summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJames Cowgill <james410@cowgill.org.uk>2017-04-23 16:56:49 +0100
committerJames Cowgill <james410@cowgill.org.uk>2017-04-23 16:56:49 +0100
commit4960f31a0d030784e60243679f1a51332b44499f (patch)
treed0189ccf895386f7de6c8cbc58aa2cfd87230c1d
parentd513e2dee6757128ab5764345019efae19372663 (diff)
New upstream version 0.25.0
-rw-r--r--.travis.yml2
-rw-r--r--Copyright11
-rw-r--r--DOCS/client-api-changes.rst3
-rw-r--r--DOCS/interface-changes.rst29
-rw-r--r--DOCS/man/af.rst50
-rw-r--r--DOCS/man/input.rst72
-rw-r--r--DOCS/man/lua.rst4
-rw-r--r--DOCS/man/mpv.rst35
-rw-r--r--DOCS/man/options.rst191
-rw-r--r--DOCS/man/osc.rst40
-rw-r--r--DOCS/man/vf.rst56
-rw-r--r--DOCS/man/vo.rst13
-rw-r--r--DOCS/mplayer-changes.rst2
-rw-r--r--DOCS/release-policy.md17
-rw-r--r--DOCS/waf-buildsystem.rst2
-rw-r--r--README.md20
-rw-r--r--RELEASE_NOTES162
-rwxr-xr-xTOOLS/gen-osd-font.sh9
-rw-r--r--TOOLS/lua/acompressor.lua126
-rw-r--r--TOOLS/lua/drc-control.lua99
-rw-r--r--TOOLS/mpv-osd-symbols.sfdir/.notdef.glyph6
-rw-r--r--TOOLS/mpv-osd-symbols.sfdir/font.props77
-rw-r--r--TOOLS/mpv-osd-symbols.sfdir/uniE001.glyph16
-rw-r--r--TOOLS/mpv-osd-symbols.sfdir/uniE002.glyph22
-rw-r--r--TOOLS/mpv-osd-symbols.sfdir/uniE003.glyph17
-rw-r--r--TOOLS/mpv-osd-symbols.sfdir/uniE004.glyph20
-rw-r--r--TOOLS/mpv-osd-symbols.sfdir/uniE005.glyph20
-rw-r--r--TOOLS/mpv-osd-symbols.sfdir/uniE006.glyph47
-rw-r--r--TOOLS/mpv-osd-symbols.sfdir/uniE007.glyph21
-rw-r--r--TOOLS/mpv-osd-symbols.sfdir/uniE008.glyph37
-rw-r--r--TOOLS/mpv-osd-symbols.sfdir/uniE009.glyph17
-rw-r--r--TOOLS/mpv-osd-symbols.sfdir/uniE00A.glyph50
-rw-r--r--TOOLS/mpv-osd-symbols.sfdir/uniE00B.glyph27
-rw-r--r--TOOLS/mpv-osd-symbols.sfdir/uniE010.glyph21
-rw-r--r--TOOLS/mpv-osd-symbols.sfdir/uniE011.glyph17
-rw-r--r--TOOLS/mpv-osd-symbols.sfdir/uniE012.glyph21
-rw-r--r--TOOLS/mpv-osd-symbols.sfdir/uniE013.glyph17
-rw-r--r--TOOLS/mpv-osd-symbols.sfdir/uniE101.glyph16
-rw-r--r--TOOLS/mpv-osd-symbols.sfdir/uniE104.glyph25
-rw-r--r--TOOLS/mpv-osd-symbols.sfdir/uniE105.glyph25
-rw-r--r--TOOLS/mpv-osd-symbols.sfdir/uniE106.glyph22
-rw-r--r--TOOLS/mpv-osd-symbols.sfdir/uniE107.glyph56
-rw-r--r--TOOLS/mpv-osd-symbols.sfdir/uniE108.glyph39
-rw-r--r--TOOLS/mpv-osd-symbols.sfdir/uniE109.glyph39
-rw-r--r--TOOLS/mpv-osd-symbols.sfdir/uniE10A.glyph36
-rw-r--r--TOOLS/mpv-osd-symbols.sfdir/uniE10B.glyph26
-rw-r--r--TOOLS/mpv-osd-symbols.sfdir/uniE10C.glyph33
-rw-r--r--TOOLS/mpv-osd-symbols.sfdir/uniE10D.glyph40
-rw-r--r--TOOLS/mpv-osd-symbols.sfdir/uniE10E.glyph53
-rw-r--r--TOOLS/mpv-osd-symbols.sfdir/uniE110.glyph16
-rw-r--r--TOOLS/mpv-osd-symbols.sfdir/uniE111.glyph37
-rw-r--r--TOOLS/osxbundle/mpv.app/Contents/Info.plist2
-rwxr-xr-xTOOLS/osxbundle/mpv.app/Contents/MacOS/mpv-wrapper.sh3
-rw-r--r--TOOLS/osxbundle/mpv.app/Contents/Resources/mpv.conf1
-rwxr-xr-xTOOLS/zsh.pl3
-rw-r--r--VERSION2
-rw-r--r--audio/decode/dec_audio.c3
-rw-r--r--audio/filter/af.c32
-rw-r--r--audio/filter/af_channels.c2
-rw-r--r--audio/filter/af_drc.c334
-rw-r--r--audio/filter/af_equalizer.c1
-rw-r--r--audio/filter/af_lavfi.c106
-rw-r--r--audio/filter/af_pan.c1
-rw-r--r--audio/filter/af_volume.c4
-rw-r--r--audio/out/ao.c11
-rw-r--r--audio/out/ao_alsa.c18
-rw-r--r--audio/out/ao_jack.c41
-rw-r--r--audio/out/ao_pcm.c2
-rw-r--r--audio/out/ao_wasapi_changenotify.c24
-rw-r--r--audio/out/ao_wasapi_utils.c28
-rwxr-xr-xbootstrap.py4
-rw-r--r--common/av_common.c30
-rw-r--r--common/common.h14
-rw-r--r--common/encode_lavc.c6
-rw-r--r--demux/codec_tags.c29
-rw-r--r--demux/demux.c9
-rw-r--r--demux/demux_cue.c2
-rw-r--r--demux/demux_lavf.c47
-rw-r--r--demux/demux_mkv.c18
-rw-r--r--demux/ebml.c14
-rw-r--r--demux/ebml.h14
-rw-r--r--demux/matroska.h14
-rw-r--r--demux/packet.c41
-rw-r--r--demux/packet.h15
-rw-r--r--etc/builtin.conf1
-rw-r--r--etc/encoding-profiles.conf80
-rw-r--r--etc/input.conf6
-rw-r--r--etc/mpv.conf25
-rw-r--r--etc/mpv.desktop15
-rw-r--r--input/cmd_list.c1
-rw-r--r--input/cmd_list.h1
-rw-r--r--input/cmd_parse.c1
-rw-r--r--input/input.c8
-rw-r--r--input/input.h1
-rw-r--r--libmpv/client.h79
-rw-r--r--libmpv/opengl_cb.h53
-rw-r--r--libmpv/qthelper.hpp8
-rw-r--r--libmpv/stream_cb.h19
-rw-r--r--misc/thread_pool.c125
-rw-r--r--misc/thread_pool.h10
-rw-r--r--options/m_config.c14
-rw-r--r--options/m_option.c106
-rw-r--r--options/m_option.h11
-rw-r--r--options/options.c36
-rw-r--r--options/options.h5
-rw-r--r--options/parse_commandline.c2
-rw-r--r--osdep/atomic.h37
-rw-r--r--osdep/io.c27
-rw-r--r--osdep/io.h2
-rw-r--r--osdep/macosx_application.m122
-rw-r--r--osdep/macosx_application_objc.h5
-rw-r--r--osdep/macosx_compat.h30
-rw-r--r--osdep/macosx_events.h4
-rw-r--r--osdep/macosx_events.m93
-rw-r--r--osdep/macosx_events_objc.h4
-rw-r--r--osdep/macosx_touchbar.h45
-rw-r--r--osdep/macosx_touchbar.m272
-rw-r--r--osdep/macosx_versions.h4
-rw-r--r--osdep/semaphore_osx.c4
-rw-r--r--osdep/win32/include/pthread.h15
-rw-r--r--osdep/win32/include/semaphore.h15
-rw-r--r--osdep/win32/pthread.c5
-rw-r--r--player/audio.c7
-rw-r--r--player/client.c9
-rw-r--r--player/command.c92
-rw-r--r--player/command.h1
-rw-r--r--player/core.h16
-rw-r--r--player/external_files.c28
-rw-r--r--player/lavfi.c131
-rw-r--r--player/lavfi.h1
-rw-r--r--player/loadfile.c22
-rw-r--r--player/lua/osc.lua253
-rw-r--r--player/main.c9
-rw-r--r--player/playloop.c129
-rw-r--r--player/screenshot.c135
-rw-r--r--player/screenshot.h4
-rw-r--r--player/video.c14
-rw-r--r--stream/dvb_tune.c554
-rw-r--r--stream/dvb_tune.h15
-rw-r--r--stream/dvbin.h90
-rw-r--r--stream/stream.c2
-rw-r--r--stream/stream_dvb.c751
-rw-r--r--stream/stream_dvd.c6
-rw-r--r--stream/stream_dvdnav.c9
-rw-r--r--sub/ass_mp.c17
-rw-r--r--sub/ass_mp.h14
-rw-r--r--sub/filter_sdh.c453
-rw-r--r--sub/lavc_conv.c3
-rw-r--r--sub/osd.c15
-rw-r--r--sub/osd.h14
-rw-r--r--sub/osd_font.otfbin3032 -> 4196 bytes
-rw-r--r--sub/sd.h2
-rw-r--r--sub/sd_ass.c42
-rw-r--r--sub/sd_lavc.c16
-rw-r--r--ta/ta.c4
-rw-r--r--ta/ta.h4
-rw-r--r--ta/ta_talloc.c4
-rw-r--r--ta/ta_talloc.h6
-rw-r--r--ta/ta_utils.c4
-rwxr-xr-xversion.sh12
-rw-r--r--video/decode/dec_video.c3
-rw-r--r--video/decode/hw_cuda.c2
-rw-r--r--video/decode/hw_d3d11va.c2
-rw-r--r--video/decode/hw_dxva2.c2
-rw-r--r--video/decode/hw_vaapi.c171
-rw-r--r--video/decode/hw_vaapi_old.c12
-rw-r--r--video/decode/hw_vdpau.c11
-rw-r--r--video/decode/hw_videotoolbox.c79
-rw-r--r--video/decode/lavc.h29
-rw-r--r--video/decode/vd_lavc.c244
-rw-r--r--video/filter/vf.c73
-rw-r--r--video/filter/vf_buffer.c1
-rw-r--r--video/filter/vf_crop.c1
-rw-r--r--video/filter/vf_dlopen.c2
-rw-r--r--video/filter/vf_dsize.c2
-rw-r--r--video/filter/vf_eq.c3
-rw-r--r--video/filter/vf_expand.c2
-rw-r--r--video/filter/vf_flip.c2
-rw-r--r--video/filter/vf_gradfun.c2
-rw-r--r--video/filter/vf_lavfi.c95
-rw-r--r--video/filter/vf_lavfi.h4
-rw-r--r--video/filter/vf_mirror.c1
-rw-r--r--video/filter/vf_noformat.c1
-rw-r--r--video/filter/vf_pullup.c2
-rw-r--r--video/filter/vf_rotate.c5
-rw-r--r--video/filter/vf_scale.c7
-rw-r--r--video/filter/vf_stereo3d.c6
-rw-r--r--video/filter/vf_sub.c1
-rw-r--r--video/filter/vf_vapoursynth.c2
-rw-r--r--video/filter/vf_vavpp.c48
-rw-r--r--video/filter/vf_yadif.c6
-rw-r--r--video/hwdec.c2
-rw-r--r--video/hwdec.h13
-rw-r--r--video/image_writer.c135
-rw-r--r--video/image_writer.h5
-rw-r--r--video/img_format.c13
-rw-r--r--video/mp_image.c34
-rw-r--r--video/mp_image.h1
-rw-r--r--video/out/cocoa/events_view.h1
-rw-r--r--video/out/cocoa/events_view.m40
-rw-r--r--video/out/cocoa/mpvadapter.h3
-rw-r--r--video/out/cocoa/video_view.m2
-rw-r--r--video/out/cocoa/window.m90
-rw-r--r--video/out/cocoa_common.m231
-rw-r--r--video/out/filter_kernels.c26
-rw-r--r--video/out/filter_kernels.h5
-rw-r--r--video/out/opengl/common.c10
-rw-r--r--video/out/opengl/common.h25
-rw-r--r--video/out/opengl/context.c17
-rw-r--r--video/out/opengl/context.h13
-rw-r--r--video/out/opengl/context_angle.c37
-rw-r--r--video/out/opengl/context_cocoa.c49
-rw-r--r--video/out/opengl/context_drm_egl.c4
-rw-r--r--video/out/opengl/context_dxinterop.c4
-rw-r--r--video/out/opengl/context_mali_fbdev.c13
-rw-r--r--video/out/opengl/context_rpi.c13
-rw-r--r--video/out/opengl/context_vdpau.c389
-rw-r--r--video/out/opengl/context_w32.c13
-rw-r--r--video/out/opengl/context_wayland.c6
-rw-r--r--video/out/opengl/context_x11.c20
-rw-r--r--video/out/opengl/context_x11egl.c5
-rw-r--r--video/out/opengl/cuda_dynamic.h5
-rw-r--r--video/out/opengl/egl_helpers.c35
-rw-r--r--video/out/opengl/egl_helpers.h3
-rw-r--r--video/out/opengl/formats.c175
-rw-r--r--video/out/opengl/formats.h25
-rw-r--r--video/out/opengl/gl_headers.h747
-rw-r--r--video/out/opengl/header_fixes.h159
-rw-r--r--video/out/opengl/hwdec.c3
-rw-r--r--video/out/opengl/hwdec.h5
-rw-r--r--video/out/opengl/hwdec_cuda.c42
-rw-r--r--video/out/opengl/hwdec_d3d11egl.c8
-rw-r--r--video/out/opengl/hwdec_dxva2.c68
-rw-r--r--video/out/opengl/hwdec_ios.m214
-rw-r--r--video/out/opengl/hwdec_osx.c163
-rw-r--r--video/out/opengl/hwdec_vaegl.c36
-rw-r--r--video/out/opengl/lcms.c4
-rw-r--r--video/out/opengl/user_shaders.c4
-rw-r--r--video/out/opengl/utils.c194
-rw-r--r--video/out/opengl/utils.h9
-rw-r--r--video/out/opengl/video.c274
-rw-r--r--video/out/opengl/video.h1
-rw-r--r--video/out/opengl/video_shaders.c4
-rw-r--r--video/out/vo.c26
-rw-r--r--video/out/vo_image.c2
-rw-r--r--video/out/vo_opengl.c17
-rw-r--r--video/out/vo_opengl_cb.c1
-rw-r--r--video/out/vo_tct.c12
-rw-r--r--video/out/vo_x11.c24
-rw-r--r--video/out/vo_xv.c14
-rw-r--r--video/out/w32_common.c726
-rw-r--r--video/out/wayland_common.c12
-rw-r--r--video/out/win32/droptarget.c216
-rw-r--r--video/out/win32/droptarget.h33
-rw-r--r--video/out/x11_common.c68
-rw-r--r--video/vaapi.c13
-rw-r--r--video/vaapi.h4
-rw-r--r--video/vdpau.c28
-rw-r--r--video/vdpau.h4
-rw-r--r--video/vt.c74
-rw-r--r--video/vt.h16
-rw-r--r--waftools/fragments/touchbar.m7
-rw-r--r--waftools/generators/headers.py1
-rw-r--r--waftools/waf_customizations.py6
-rw-r--r--wscript121
-rw-r--r--wscript_build.py16
266 files changed, 8535 insertions, 4037 deletions
diff --git a/.travis.yml b/.travis.yml
index 2cd4acf..557a526 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,7 +2,7 @@ language: c
os:
- linux
- - osx
+ #- osx
env:
matrix:
- LIBAV=libav-stable
diff --git a/Copyright b/Copyright
index 51a1a16..65e8f87 100644
--- a/Copyright
+++ b/Copyright
@@ -4,7 +4,9 @@ mpv as a whole is licensed as GPL version 2 or later (see LICENSE). Most source
files are GPLv2+, but some files are available under a more liberal license,
such as LGPLv2.1+, BSD, MIT, ISC, and possibly others. Look at the copyright
header of each source file, and grep the sources for "Copyright" if you need
-to know details. Files without Copyright notice are licensed as LGPLv2.1+.
+to know details. C source files without Copyright notice are licensed as
+LGPLv2.1+. Also see the list of files with specific licenses below (not all
+files can have a standard license header).
All new contributions must be LGPLv2.1+ licensed, or if the changes are done on
GPL code, must come with the implicit agreement that the project can relicense
@@ -25,3 +27,10 @@ In particular, this affects the file libmpdemux/demux_ty_osd.c. It is disabled
under mplayer2, and has been removed from mpv.
"v2.1+" in this context means "version 2.1 or later".
+
+Source files with specific licenses:
+- everything under etc/ is unknown
+- everything under DOCS/man/ is GPLv2+
+- sub/osd_font.otf is LGPLv2.1+
+- some tests under waftools/fragments/ might be GPLv2+
+- version.sh is GPLv2+
diff --git a/DOCS/client-api-changes.rst b/DOCS/client-api-changes.rst
index 5e6e1d3..f2a84a6 100644
--- a/DOCS/client-api-changes.rst
+++ b/DOCS/client-api-changes.rst
@@ -32,6 +32,9 @@ API changes
::
+ --- mpv 0.25.0 ---
+ 1.24 - add a MPV_ENABLE_DEPRECATED preprocessor symbol, which can be defined
+ by the user to exclude deprecated API symbols from the C headers
--- mpv 0.23.0 ---
1.24 - the deprecated mpv_suspend() and mpv_resume() APIs now do nothing.
--- mpv 0.22.0 ---
diff --git a/DOCS/interface-changes.rst b/DOCS/interface-changes.rst
index 3f21bf8..bdf5791 100644
--- a/DOCS/interface-changes.rst
+++ b/DOCS/interface-changes.rst
@@ -19,6 +19,35 @@ Interface changes
::
+ --- mpv 0.25.0 ---
+ - remove opengl-cb dxva2 dummy hwdec interop
+ (see git "vo_opengl: remove dxva2 dummy hwdec backend")
+ - remove ppm, pgm, pgmyuv, tga choices from the --screenshot-format and
+ --vo-image-format options
+ - the "jpeg" choice in the option above now leads to a ".jpg" file extension
+ - --af=drc is gone (you can use e.g. lavfi/acompressor instead)
+ - remove image_size predefined uniform from OpenGL user shaders. Use
+ input_size instead
+ - add --sub-filter-sdh
+ - add --sub-filter-sdh-harder
+ - remove --input-app-events option (macOS)
+ - deprecate most --vf and --af filters. Only some filters not in libavfilter
+ will be kept.
+ Also, you can use libavfilter filters directly (e.g. you can use
+ --vf=name=opts instead of --vf=lavfi=[name=opts]), as long as the
+ libavfilter filter's name doesn't clash with a mpv builtin filter.
+ In the long term, --vf/--af syntax might change again, but if it does, it
+ will switch to libavfilter's native syntax. (The above mentioned direct
+ support for lavfi filters still has some differences, such as how strings
+ are escaped.) If this happens, the non-deprecated builtin filters might be
+ moved to "somewhere else" syntax-wise.
+ - deprecate --loop - after a deprecation period, it will be undeprecated,
+ but changed to alias --loop-file
+ - add --keep-open-pause=no
+ - deprecate --demuxer-max-packets
+ - change --audio-file-auto default from "exact" to "no" (mpv won't load
+ files with the same filename as the video, but different extension, as
+ audio track anymore)
--- mpv 0.24.0 ---
- deprecate --hwdec-api and replace it with --opengl-hwdec-interop.
The new option accepts both --hwdec values, as well as named backends.
diff --git a/DOCS/man/af.rst b/DOCS/man/af.rst
index 4c91b5a..9dff18e 100644
--- a/DOCS/man/af.rst
+++ b/DOCS/man/af.rst
@@ -4,8 +4,8 @@ AUDIO FILTERS
Audio filters allow you to modify the audio stream and its properties. The
syntax is:
-``--af=<filter1[=parameter1:parameter2:...],filter2,...>``
- Setup a chain of audio filters.
+``--af=...``
+ Setup a chain of audio filters. See ``--vf`` for the syntax.
.. note::
@@ -15,27 +15,8 @@ syntax is:
wrapper, which gives you access to most of libavfilter's filters. This
includes all filters that have been ported from MPlayer to libavfilter.
-You can also set defaults for each filter. The defaults are applied before the
-normal filter parameters.
-
-``--af-defaults=<filter1[=parameter1:parameter2:...],filter2,...>``
- Set defaults for each filter.
-
-Audio filters are managed in lists. There are a few commands to manage the
-filter list:
-
-``--af-add=<filter1[,filter2,...]>``
- Appends the filters given as arguments to the filter list.
-
-``--af-pre=<filter1[,filter2,...]>``
- Prepends the filters given as arguments to the filter list.
-
-``--af-del=<index1[,index2,...]>``
- Deletes the filters at the given indexes. Index numbers start at 0,
- negative numbers address the end of the list (-1 is the last).
-
-``--af-clr``
- Completely empties the filter list.
+See ``--vf`` group of options for info on how ``--af-defaults``, ``--af-add``,
+``--af-pre``, ``--af-del``, ``--af-clr``, and possibly others work.
Available filters are:
@@ -319,29 +300,6 @@ Available filters are:
the mixing matrix at runtime, without reinitializing the entire filter
chain.
-``drc[=method:target]``
- Applies dynamic range compression. This maximizes the volume by compressing
- the audio signal's dynamic range. (Formerly called ``volnorm``.)
-
- ``<method>``
- Sets the used method.
-
- 1
- Use a single sample to smooth the variations via the standard
- weighted mean over past samples (default).
- 2
- Use several samples to smooth the variations via the standard
- weighted mean over past samples.
-
- ``<target>``
- Sets the target amplitude as a fraction of the maximum for the sample
- type (default: 0.25).
-
- .. note::
-
- This filter can cause distortion with audio signals that have a very
- large dynamic range.
-
``scaletempo[=option1:option2:...]``
Scales audio tempo without altering pitch, optionally synced to playback
speed (default).
diff --git a/DOCS/man/input.rst b/DOCS/man/input.rst
index 1399ef7..6759226 100644
--- a/DOCS/man/input.rst
+++ b/DOCS/man/input.rst
@@ -159,10 +159,10 @@ List of Input Commands
``multiply <property> <factor>``
Multiplies the value of a property with the numeric factor.
-``screenshot [subtitles|video|window|- [single|each-frame]]``
+``screenshot [subtitles|video|window|single|each-frame]``
Take a screenshot.
- First argument:
+ Multiple flags are available (some can be combined with ``+``):
<subtitles> (default)
Save the video image, in its original resolution, and with subtitles.
@@ -182,6 +182,16 @@ List of Input Commands
frame was dropped. This flag can be combined with the other flags,
e.g. ``video+each-frame``.
+ Older mpv versions required passing ``single`` and ``each-frame`` as
+ second argument (and did not have flags). This syntax is still understood,
+ but deprecated and might be removed in the future.
+
+ Setting the ``async`` flag will make encoding and writing the actual image
+ file asynchronous in most cases. (``each-frame`` mode ignores this flag
+ currently.) Requesting async screenshots too early or too often could lead
+ to the same filenames being chosen, and overwriting each others in undefined
+ order.
+
``screenshot-to-file "<filename>" [subtitles|video|window]``
Take a screenshot and save it to a given file. The format of the file will
be guessed by the extension (and ``--screenshot-format`` is ignored - the
@@ -194,6 +204,9 @@ List of Input Commands
Like all input command parameters, the filename is subject to property
expansion as described in `Property Expansion`_.
+ The ``async`` flag has an effect on this command (see ``screenshot``
+ command).
+
``playlist-next [weak|force]``
Go to the next entry on the playlist.
@@ -353,6 +366,11 @@ List of Input Commands
<level>
The minimum OSD level to show the text at (see ``--osd-level``).
+``expand-text "<string>"``
+ Property-expand the argument and return the expanded string. This can be
+ used only through the client API or from a script using
+ ``mp.command_native``. (see `Property Expansion`_).
+
``show-progress``
Show the progress bar, the elapsed time and the total duration of the file
on the OSD.
@@ -448,6 +466,10 @@ Input Commands that are Possibly Subject to Change
(If several filters are passed to the command, this is done for
each filter.)
+ A special variant is combining this with labels, and using ``@name``
+ without filter name and parameters as filter entry. This toggles the
+ enable/disable flag.
+
del
Remove the given filters from the video chain. Unlike in the other
cases, the second parameter is a comma separated list of filter names
@@ -483,6 +505,16 @@ Input Commands that are Possibly Subject to Change
- ``b vf set ""`` remove all video filters on ``b``
- ``c vf toggle lavfi=gradfun`` toggle debanding on ``c``
+ .. admonition:: Example how to toggle disabled filters at runtime
+
+ - Add something ``vf-add=@deband:!lavfi=[gradfun]`` to ``mpv.conf``. The
+ ``@deband:`` is the label, and ``deband`` is an arbitrary, user-given
+ name for this filter entry. The ``!`` before the filter name disables
+ the filter by default. Everything after this is the normal filter name
+ and the filter parameters.
+ - Add ``a vf toggle @deband`` to ``input.conf``. This toggles the
+ "disabled" flag for the filter identified with ``deband``.
+
``cycle-values ["!reverse"] <property> "<value1>" "<value2>" ...``
Cycle through a list of values. Each invocation of the command will set the
given property to the next value in the list. The command maintains an
@@ -673,11 +705,12 @@ Input Commands that are Possibly Subject to Change
``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
+ ``stride`` fields set to obvious contents. The ``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``
+ is the LSB). The contents of the padding ``X`` are 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.
+ is freed as soon as the result mpv_node is freed. As usual with client API
+ semantics, you are not allowed to write to the image data.
``vf-command "<label>" "<cmd>" "<args>"``
Send a command to the filter with the given ``<label>``. Use ``all`` to send
@@ -782,8 +815,10 @@ Input Command Prefixes
These prefixes are placed between key name and the actual command. Multiple
prefixes can be specified. They are separated by whitespace.
-``osd-auto`` (default)
- Use the default behavior for this command.
+``osd-auto``
+ Use the default behavior for this command. This is the default for
+ ``input.conf`` commands. Some libmpv/scripting/IPC APIs do not use this as
+ default, but use ``no-osd`` instead.
``no-osd``
Do not use any OSD for this command.
``osd-bar``
@@ -797,11 +832,20 @@ prefixes can be specified. They are separated by whitespace.
Combine osd-bar and osd-msg.
``raw``
Do not expand properties in string arguments. (Like ``"${property-name}"``.)
-``expand-properties`` (default)
+ This is the default for some libmpv/scripting/IPC APIs.
+``expand-properties``
All string arguments are expanded as described in `Property Expansion`_.
+ This is the default for ``input.conf`` commands.
``repeatable``
For some commands, keeping a key pressed doesn't run the command repeatedly.
This prefix forces enabling key repeat in any case.
+``async``
+ Allow asynchronous execution (if possible). Note that only a few commands
+ will support this (usually this is explicitly documented). Some commands
+ are asynchronous by default (or rather, their effects might manifest
+ after completion of the command). The semantics of this flag might change
+ in the future. Set it only if you don't rely on the effects of this command
+ being fully realized when it returns.
All of the osd prefixes are still overridden by the global ``--osd-level``
settings.
@@ -1219,6 +1263,17 @@ Property list
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.
+``demuxer-via-network``
+ Returns ``yes`` if the stream demuxed via the main demuxer is most likely
+ played via network. What constitutes "network" is not always clear, might
+ be used for other types of untrusted streams, could be wrong in certain
+ cases, and its definition might be changing. Also, external files (like
+ separate audio files or streams) do not influence the value of this
+ property (currently).
+
+``demuxer-start-time`` (R)
+ Returns the start time reported by the demuxer in fractional seconds.
+
``paused-for-cache``
Returns ``yes`` when playback is paused because of waiting for the cache.
@@ -1771,6 +1826,7 @@ Property list
MPV_FORMAT_NODE_MAP (for each filter entry)
"name" MPV_FORMAT_STRING
"label" MPV_FORMAT_STRING [optional]
+ "enabled" MPV_FORMAT_FLAG [optional]
"params" MPV_FORMAT_NODE_MAP [optional]
"key" MPV_FORMAT_STRING
"value" MPV_FORMAT_STRING
diff --git a/DOCS/man/lua.rst b/DOCS/man/lua.rst
index e2f47a8..cfd622b 100644
--- a/DOCS/man/lua.rst
+++ b/DOCS/man/lua.rst
@@ -240,7 +240,7 @@ The ``mp`` module is preloaded, although it can be loaded manually with
This will print the message ``the key was pressed`` when ``x`` was pressed.
The user can remap these key bindings. Then the user has to put the
- following into his input.conf to remap the command to the ``y`` key:
+ following into their input.conf to remap the command to the ``y`` key:
::
@@ -259,7 +259,7 @@ The ``mp`` module is preloaded, although it can be loaded manually with
``mp.add_forced_key_binding(...)``
This works almost the same as ``mp.add_key_binding``, but registers the
- key binding in a way that will overwrite the user's custom bindings in his
+ key binding in a way that will overwrite the user's custom bindings in their
input.conf. (``mp.add_key_binding`` overwrites default key bindings only,
but not those by the user's input.conf.)
diff --git a/DOCS/man/mpv.rst b/DOCS/man/mpv.rst
index db03828..ff51920 100644
--- a/DOCS/man/mpv.rst
+++ b/DOCS/man/mpv.rst
@@ -439,7 +439,7 @@ specifying the path to the local copy: ``--dvd-device=PATH``.
Alternatively, running ``mpv PATH`` should auto-detect a DVD directory
tree and play the longest title.
-.. note::
+.. note:: DVD library choices
mpv uses a different default DVD library than MPlayer. MPlayer
uses libdvdread by default, and mpv uses libdvdnav by default.
@@ -452,8 +452,12 @@ tree and play the longest title.
when using libdvdnav, in which playback gets stuck in a DVD menu
stream. These problems are reported to go away when auto-selecting
the title (``dvd://`` rather than ``dvd://1``) or when using
- libdvdread (e.g. ``dvdread://0``).
+ libdvdread (e.g. ``dvdread://0``). There are also outstanding bugs
+ in libdvdnav with seeking backwards and forwards in a video
+ stream. Specify ``dvdread://...`` to fix such problems.
+.. note:: DVD subtitles
+
DVDs use image-based subtitles. Image subtitles are implemented as
a bitmap video stream which can be superimposed over the main
movie. mpv's subtitle styling and positioning options and keyboard
@@ -665,6 +669,7 @@ PROTOCOLS
=========
``http://...``, ``https://``, ...
+
Many network protocols are supported, but the protocol prefix must always
be specified. mpv will never attempt to guess whether a filename is
actually a network address. A protocol prefix is always required.
@@ -678,6 +683,7 @@ PROTOCOLS
can also prefix it with ``lavf://`` or ``ffmpeg://``.
``ytdl://...``
+
By default, the youtube-dl hook script (enabled by default for mpv CLI)
only looks at http URLs. Prefixing an URL with ``ytdl://`` forces it to
be always processed by the script. This can also be used to invoke special
@@ -687,17 +693,21 @@ PROTOCOLS
and you have to use ``--ytdl-raw-options`` instead.
``-``
+
Play data from stdin.
``smb://PATH``
+
Play a path from Samba share.
``bd://[title][/device]`` ``--bluray-device=PATH``
+
Play a Blu-ray disc. Currently, this does not accept ISO files. Instead,
you must mount the ISO file as filesystem, and point ``--bluray-device``
to the mounted directory directly.
``dvd://[title|[starttitle]-endtitle][/device]`` ``--dvd-device=PATH``
+
Play a DVD. DVD menus are not supported. If no title is given, the longest
title is auto-selected.
@@ -705,30 +715,39 @@ PROTOCOLS
thing.
``dvdread://...:``
- Play a DVD using the old libdvdread code. This is what MPlayer and older
- mpv versions use for ``dvd://``. Use is discouraged. It's provided only
- for compatibility and for transition.
+
+ Play a DVD using the old libdvdread code. This is what MPlayer and
+ older mpv versions use for ``dvd://``. Use is discouraged. It's
+ provided only for compatibility and for transition, and to work
+ around outstanding dvdnav bugs (see "DVD library choices" above).
``tv://[channel][/input_id]`` ``--tv-...``
+
Analogue TV via V4L. Also useful for webcams. (Linux only.)
``pvr://`` ``--pvr-...``
+
PVR. (Linux only.)
``dvb://[cardnumber@]channel`` ``--dvbin-...``
+
Digital TV via DVB. (Linux only.)
``mf://[filemask|@listfile]`` ``--mf-...``
+
Play a series of images as video.
``cdda://[device]`` ``--cdrom-device=PATH`` ``--cdda-...``
+
Play CD.
``lavf://...``
+
Access any FFmpeg/Libav libavformat protocol. Basically, this passed the
string after the ``//`` directly to libavformat.
``av://type:options``
+
This is intended for using libavdevice inputs. ``type`` is the libavdevice
demuxer name, and ``options`` is the (pseudo-)filename passed to the
demuxer.
@@ -740,26 +759,32 @@ PROTOCOLS
``avdevice://`` is an alias.
``file://PATH``
+
A local path as URL. Might be useful in some special use-cases. Note that
``PATH`` itself should start with a third ``/`` to make the path an
absolute path.
``fd://123``
+
Read data from the given file descriptor (for example 123). This is similar
to piping data to stdin via ``-``, but can use an arbitrary file descriptor.
``edl://[edl specification as in edl-mpv.rst]``
+
Stitch together parts of multiple files and play them.
``null://``
+
Simulate an empty file. If opened for writing, it will discard all data.
The ``null`` demuxer will specifically pass autoprobing if this protocol
is used (while it's not automatically invoked for empty files).
``memory://data``
+
Use the ``data`` part as source data.
``hex://data``
+
Like ``memory://``, but the string is interpreted as hexdump.
PSEUDO GUI MODE
diff --git a/DOCS/man/options.rst b/DOCS/man/options.rst
index af9a3b9..7d3070f 100644
--- a/DOCS/man/options.rst
+++ b/DOCS/man/options.rst
@@ -134,17 +134,6 @@ Playback Control
speed higher than normal automatically inserts the ``scaletempo`` audio
filter.
-``--loop=<N|inf|force|no>``, ``--loop``
- Loops playback ``N`` times. A value of ``1`` plays it one time (default),
- ``2`` two times, etc. ``inf`` means forever. ``no`` is the same as ``1`` and
- disables looping. If several files are specified on command line, the
- entire playlist is looped. ``--loop`` is the same as ``--loop=inf``.
-
- The ``force`` mode is like ``inf``, but does not skip playlist entries
- which have been marked as failing. This means the player might waste CPU
- time trying to loop a file that doesn't exist. But it might be useful for
- playing webradios under very bad network conditions.
-
``--pause``
Start the player in paused state.
@@ -287,15 +276,31 @@ Playback Control
directory). Prefixing the filename with ``./`` if it doesn't start with
a ``/`` will avoid this.
+``--loop-playlist=<N|inf|force|no>``, ``--loop-playlist``
+ Loops playback ``N`` times. A value of ``1`` plays it one time (default),
+ ``2`` two times, etc. ``inf`` means forever. ``no`` is the same as ``1`` and
+ disables looping. If several files are specified on command line, the
+ entire playlist is looped. ``--loop-playlist`` is the same as
+ ``--loop-playlist=inf``.
+
+ The ``force`` mode is like ``inf``, but does not skip playlist entries
+ which have been marked as failing. This means the player might waste CPU
+ time trying to loop a file that doesn't exist. But it might be useful for
+ playing webradios under very bad network conditions.
+
+``--loop``
+ Currently a deprecated alias to ``--loop-playlist``. After a deprecation
+ period, it will be undeprecated, but changed to alias ``--loop-file``.
+
``--loop-file=<N|inf|no>``
Loop a single file N times. ``inf`` means forever, ``no`` means normal
playback. For compatibility, ``--loop-file`` and ``--loop-file=yes`` are
also accepted, and are the same as ``--loop-file=inf``.
- The difference to ``--loop`` is that this doesn't loop the playlist, just
- the file itself. If the playlist contains only a single file, the difference
- between the two option is that this option performs a seek on loop, instead
- of reloading the file.
+ The difference to ``--loop-playlist`` is that this doesn't loop the playlist,
+ just the file itself. If the playlist contains only a single file, the
+ difference between the two option is that this option performs a seek on
+ loop, instead of reloading the file.
``--ab-loop-a=<time>``, ``--ab-loop-b=<time>``
Set loop points. If playback passes the ``b`` timestamp, it will seek to
@@ -634,8 +639,9 @@ Video
:vdpau-copy: copies video back into system RAM (Linux with some GPUs only)
:vaapi: requires ``--vo=opengl`` or ``--vo=vaapi`` (Linux only)
:vaapi-copy: copies video back into system RAM (Linux with Intel GPUs only)
- :videotoolbox: requires ``--vo=opengl`` (OS X 10.8 and up only)
- :videotoolbox-copy: copies video back into system RAM (OS X 10.8 and up only)
+ :videotoolbox: requires ``--vo=opengl`` (OS X 10.8 and up),
+ or ``--vo=opengl-cb`` (iOS 9.0 and up)
+ :videotoolbox-copy: copies video back into system RAM (OS X 10.8 or iOS 9.0 and up)
:dxva2: requires ``--vo=opengl`` with ``--opengl-backend=angle`` or
``--opengl-backend=dxinterop`` (Windows only)
:dxva2-copy: copies video back to system RAM (Windows only)
@@ -776,8 +782,10 @@ Video
choice of the format can influence performance considerably. On the other
hand, there doesn't appear to be a good way to detect the best format for
the given hardware. ``nv12``, the default, works better on modern hardware,
- while ``uyvy422`` appears to be better for old hardware. ``rgb0`` and
- ``yuv420p`` also work.
+ while ``uyvy422`` appears to be better for old hardware. ``yuv420p`` also
+ works.
+ Since mpv 0.25.0, ``no`` is an accepted value, which lets the decoder pick
+ the format on newer FFmpeg versions (will use ``nv12`` on older versions).
``--panscan=<0.0-1.0>``
Enables pan-and-scan functionality (cropping the sides of e.g. a 16:9
@@ -1410,18 +1418,6 @@ Audio
if you want to force a different audio profile (e.g. with PulseAudio),
or to set your own application name when using libmpv.
-``--volume-restore-data=<string>``
- Used internally for use by playback resume (e.g. with ``quit-watch-later``).
- Restoring value has to be done carefully, because different AOs as well as
- softvol can have different value ranges, and we don't want to restore
- volume if setting the volume changes it system wide. The normal options
- (like ``--volume``) would always set the volume. This option was added for
- restoring volume in a safer way (by storing the method used to set the
- volume), and is not generally useful. Its semantics are considered private
- to mpv.
-
- Do not use.
-
``--audio-buffer=<seconds>``
Set the audio output minimum buffer. The audio device might actually create
a larger buffer if it pleases. If the device creates a smaller buffer,
@@ -1833,6 +1829,8 @@ Subtitles
Multiple directories can be separated by ":" (";" on Windows).
Paths can be relative or absolute. Relative paths are interpreted relative
to video file directory.
+ If the file is a URL, only absolute paths and ``sub`` configuration
+ subdirectory will be scanned.
.. admonition:: Example
@@ -2000,6 +1998,23 @@ Subtitles
Default: 0.
+``--sub-filter-sdh=<yes|no>``
+ Applies filter removing subtitle additions for the deaf or hard-of-hearing (SDH).
+ This is intended for English, but may in part work for other languages too.
+ The intention is that it can be always enabled so may not remove
+ all parts added.
+ It removes speaker labels (like MAN:), upper case text in parentheses and
+ any text in brackets.
+
+ Default: ``no``.
+
+``--sub-filter-sdh-harder=<yes|no>``
+ Do harder SDH filtering (if enabled by ``--sub-filter-sdh``).
+ Will also remove speaker labels and text within parentheses using both
+ lower and upper case letters.
+
+ Default: ``no``.
+
Window
------
@@ -2055,6 +2070,9 @@ Window
Instead, pause the player. When trying to seek beyond end of the file, the
player will attempt to seek to the last frame.
+ Normally, this will act like ``set pause yes`` on EOF, unless the
+ ``--keep-open-pause=no`` option is set.
+
The following arguments can be given:
:no: If the current file ends, go to the next file or terminate.
@@ -2081,6 +2099,11 @@ Window
file.mkv normally, then fail to open ``/dev/null``, then exit). (In
mpv 0.8.0, ``always`` was introduced, which restores the old behavior.)
+``--keep-open-pause=<yes|no>``
+ If set to ``no``, instead of pausing when ``--keep-open`` is active, just
+ stop at end of file and continue playing forward when you seek backwards
+ until end where it stops again. Default: ``yes``.
+
``--image-display-duration=<seconds|inf>``
If the current file is an image, play the image for the given amount of
seconds (default: 1). ``inf`` means the file is kept open forever (until
@@ -2130,6 +2153,14 @@ Window
treated as exclusive fullscreen window that bypasses the Desktop Window
Manager.
+``--ontop-level=<window|system|level>``
+ (OS X only)
+ Sets the level of an ontop window (default: window).
+
+ :window: On top of all other windows.
+ :system: On top of system elements like Taskbar, Menubar and Dock.
+ :level: A level as integer.
+
``--border``, ``--no-border``
Play video with window border and decorations. Since this is on by
default, use ``--no-border`` to disable the standard window decorations.
@@ -2555,6 +2586,15 @@ Demuxer
``--demuxer-lavf-analyzeduration=<value>``
Maximum length in seconds to analyze the stream properties.
+``--demuxer-lavf-probe-info=<yes|no|auto>``
+ Whether to probe stream information (default: auto). Technically, this
+ controls whether libavformat's ``avformat_find_stream_info()`` function
+ is called. Usually it's safer to call it, but it can also make startup
+ slower.
+
+ The ``auto`` choice (the default) tries to skip this for a few know-safe
+ whitelisted formats, while calling it for everything else.
+
``--demuxer-lavf-probescore=<1-100>``
Minimum required libavformat probe score. Lower values will require
less data to be loaded (makes streams start faster), but makes file
@@ -2714,20 +2754,26 @@ Demuxer
``--demuxer-rawvideo-size=<value>``
Frame size in bytes when using ``--demuxer=rawvideo``.
-``--demuxer-max-packets=<packets>``, ``--demuxer-max-bytes=<bytes>``
+``--demuxer-max-bytes=<bytes>``
This controls how much the demuxer is allowed to buffer ahead. The demuxer
will normally try to read ahead as much as necessary, or as much is
- requested with ``--demuxer-readahead-secs``. The ``--demuxer-max-...``
- options can be used to restrict the maximum readahead. This limits excessive
- readahead in case of broken files or desynced playback. The demuxer will
- stop reading additional packets as soon as one of the limits is reached.
- (The limits still can be slightly overstepped due to technical reasons.)
+ requested with ``--demuxer-readahead-secs``. The option can be used to
+ restrict the maximum readahead. This limits excessive readahead in case of
+ broken files or desynced playback. The demuxer will stop reading additional
+ packets as soon as one of the limits is reached. (The limits still can be
+ slightly overstepped due to technical reasons.)
Set these limits higher if you get a packet queue overflow warning, and
you think normal playback would be possible with a larger packet queue.
See ``--list-options`` for defaults and value range.
+``--demuxer-max-packets=<packets>``
+ Quite similar ``--demuxer-max-bytes=<bytes>``. Deprecated, because the
+ other option does basically the same job. Since mpv 0.25.0, the code
+ tries to account for per-packet overhead, which is why this option becomes
+ rather pointless.
+
``--demuxer-thread=<yes|no>``
Run the demuxer in a separate thread, and let it prefetch a certain amount
of packets (default: yes). Having this enabled may lead to smoother
@@ -2898,11 +2944,6 @@ Input
(This option was renamed from ``--input-x11-keyboard``.)
-``--input-app-events=<yes|no>``
- (OS X only)
- Enable/disable application wide keyboard events so that keyboard shortcuts
- can be processed without a window. Enabled by default (except for libmpv).
-
OSD
---
@@ -3099,12 +3140,8 @@ Screenshot
Available choices:
:png: PNG
- :ppm: PPM
- :pgm: PGM
- :pgmyuv: PGM with YV12 pixel format
- :tga: TARGA
:jpg: JPEG (default)
- :jpeg: JPEG (same as jpg, but with .jpeg file ending)
+ :jpeg: JPEG (alias for jpg)
``--screenshot-tag-colorspace=<yes|no>``
Tag screenshots with the appropriate colorspace.
@@ -4137,9 +4174,11 @@ The following video options are currently all specific to ``--vo=opengl`` and
Specifies the size of the resulting texture for this pass. ``szexpr``
refers to an expression in RPN (reverse polish notation), using the
operators + - * / > < !, floating point literals, and references to
- sizes of existing texture and OUTPUT (such as MAIN.width or
- CHROMA.height). By default, these are set to HOOKED.w and HOOKED.h,
- respectively.
+ sizes of existing texture (such as MAIN.width or CHROMA.height),
+ OUTPUT, or NATIVE_CROPPED (size of an input texture cropped after
+ pan-and-scan, video-align-x/y, video-pan-x/y, etc. and possibly
+ prescaled). By default, these are set to HOOKED.w and HOOKED.h,
+ espectively.
WHEN <szexpr>
Specifies a condition that needs to be true (non-zero) for the shader
@@ -4193,11 +4232,13 @@ The following video options are currently all specific to ``--vo=opengl`` and
int frame
A simple count of frames rendered, increases by one per frame and never
resets (regardless of seeks).
- vec2 image_size
- The size in pixels of the input image.
+ vec2 input_size
+ The size in pixels of the input image (possibly cropped and prescaled).
vec2 target_size
The size in pixels of the visible part of the scaled (and possibly
cropped) image.
+ vec2 tex_offset
+ Texture offset introduced by user shaders or options like panscan, video-align-x/y, video-pan-x/y.
Internally, vo_opengl may generate any number of the following textures.
Whenever a texture is rendered and saved by vo_opengl, all of the passes
@@ -4359,8 +4400,21 @@ The following video options are currently all specific to ``--vo=opengl`` and
chain will be used for D3D9. This option is mainly for debugging purposes,
in case the custom swap chain has poor performance or does not work.
- If set to ``yes``, the ``--angle-max-frame-latency`` and
- ``--angle-swapchain-length`` options will have no effect.
+ If set to ``yes``, the ``--angle-max-frame-latency``,
+ ``--angle-swapchain-length`` and ``--angle-flip`` options will have no
+ effect.
+
+ Windows with ANGLE only.
+
+``--angle-flip=<yes|no>``
+ Enable flip-model presentation, which avoids unnecessarily copying the
+ backbuffer by sharing surfaces with the DWM (default: yes). This may cause
+ performance issues with older drivers. If flip-model presentation is not
+ supported (for example, on Windows 7 without the platform update), mpv will
+ automatically fall back to the older bitblt presentation model.
+
+ If set to ``no``, the ``--angle-swapchain-length`` option will have no
+ effect.
Windows with ANGLE only.
@@ -4395,6 +4449,12 @@ The following video options are currently all specific to ``--vo=opengl`` and
Windows 8+ with ANGLE only.
+``--cocoa-force-dedicated-gpu=<yes|no>``
+ Deactivates the automatic graphics switching and forces the dedicated GPU.
+ (default: no)
+
+ OS X only.
+
``--opengl-sw``
Continue even if a software renderer is detected.
@@ -4430,12 +4490,19 @@ The following video options are currently all specific to ``--vo=opengl`` and
X11/EGL
mali-fbdev
Direct fbdev/EGL support on some ARM/MALI devices.
+ vdpauglx
+ Use vdpau presentation with GLX as backing. Experimental use only.
+ Using this will have no advantage (other than additional bugs or
+ performance problems), and is for doing experiments only. Will not
+ be used automatically.
``--opengl-es=<mode>``
Select whether to use GLES:
yes
Try to prefer ES over Desktop GL
+ force2
+ Try to request a ES 2.0 context (the driver might ignore this)
no
Try to prefer desktop GL over ES
auto
@@ -4702,6 +4769,18 @@ The following video options are currently all specific to ``--vo=opengl`` and
This option might be silently removed in the future.
+``--opengl-shader-cache-dir=<dirname>``
+ Store and load compiled GL shaders in this directory. Normally, shader
+ compilation is very fast, so this is usually not needed. But some GL
+ implementations (notably ANGLE, the default on Windows) have relatively
+ slow shader compilation, and can cause startup delays.
+
+ NOTE: This is not cleaned automatically, so old, unused cache files may
+ stick around indefinitely.
+
+ This option might be silently removed in the future, if ANGLE fixes shader
+ compilation speed.
+
Miscellaneous
-------------
@@ -4734,6 +4813,10 @@ Miscellaneous
``--video-sync=<audio|...>``
How the player synchronizes audio and video.
+ If you use this option, you usually want to set it to ``display-resample``
+ to enable a timing mode that tries to not skip or repeat frames when for
+ example playing 24fps video on a 24Hz screen.
+
The modes starting with ``display-`` try to output video frames completely
synchronously to the display, using the detected display vertical refresh
rate as a hint how fast frames will be displayed on average. These modes
diff --git a/DOCS/man/osc.rst b/DOCS/man/osc.rst
index ecb4d99..eb15056 100644
--- a/DOCS/man/osc.rst
+++ b/DOCS/man/osc.rst
@@ -20,12 +20,12 @@ The Interface
::
- +---------+----------+----------------------------+-------------+
- | pl prev | pl next | title | cache |
- +------+--+---+------+---------+-----------+------+-------+-----+
- | play | skip | skip | time | seekbar | time | audio | sub |
- | | back | frwd | elapsed | | left | | |
- +------+------+------+---------+-----------+------+-------+-----+
+ +---------+----------+------------------------------------------+----------+
+ | pl prev | pl next | title | cache |
+ +------+--+---+------+---------+-----------+------+-------+-----+-----+----+
+ | play | skip | skip | time | seekbar | time | audio | sub | vol | fs |
+ | | back | frwd | elapsed | | left | | | | |
+ +------+------+------+---------+-----------+------+-------+-----+-----+----+
pl prev
@@ -43,7 +43,7 @@ pl next
============= ================================================
title
- | Displays current media-title or filename
+ | Displays current media-title, filename, or custom title
============= ================================================
left-click show playlist position and length and full title
@@ -102,6 +102,17 @@ audio and sub
shift+L-click show available audio/sub tracks
============= ================================================
+vol
+ ============= ================================================
+ left-click toggle mute
+ mouse wheel volume up/down
+ ============= ================================================
+
+fs
+ ============= ================================================
+ left-click toggle fullscreen
+ ============= ================================================
+
Key Bindings
~~~~~~~~~~~~
@@ -239,6 +250,13 @@ Configurable Options
Duration of fade out in ms, 0 = no fade
+``title``
+ Default: ${media-title}
+
+ String that supports property expansion that will be displayed as
+ OSC title.
+ ASS tags are escaped, and newlines and trailing slashes are stripped.
+
``tooltipborder``
Default: 1
@@ -259,6 +277,14 @@ Configurable Options
Also supports ``never`` and ``always``
+``boxmaxchars``
+ Default: 80
+
+ Max chars for the osc title at the box layout. mpv does not measure the
+ text width on screen and so it needs to limit it by number of chars. The
+ default is conservative to allow wide fonts to be used without overflow.
+ However, with many common fonts a bigger number can be used. YMMV.
+
Script Commands
~~~~~~~~~~~~~~~
diff --git a/DOCS/man/vf.rst b/DOCS/man/vf.rst
index 2242351..2d53fc4 100644
--- a/DOCS/man/vf.rst
+++ b/DOCS/man/vf.rst
@@ -5,7 +5,41 @@ Video filters allow you to modify the video stream and its properties. The
syntax is:
``--vf=<filter1[=parameter1:parameter2:...],filter2,...>``
- Setup a chain of video filters.
+ Setup a chain of video filters. This consists on the filter name, and an
+ option list of parameters after ``=``. The parameters are separated by
+ ``:`` (not ``,``, as that starts a new filter entry).
+
+ Before the filter name, a label can be specified with ``@name:``, where
+ name is an arbitrary user-given name, which identifies the filter. This
+ is only needed if you want to toggle the filter at runtime.
+
+ A ``!`` before the filter name means the filter is enabled by default. It
+ will be skipped on filter creation. This is also useful for runtime filter
+ toggling.
+
+ See the ``vf`` command (and ``toggle`` sub-command) for further explanations
+ and examples.
+
+ The general filter entry syntax is:
+
+ ``["@"<label-name>":"] ["!"] <filter-name> [ "=" <filter-parameter-list> ]``
+
+ or for the special "toggle" syntax (see ``vf`` command):
+
+ ``"@"<label-name>``
+
+ and the ``filter-parameter-list``:
+
+ ``<filter-parameter> | <filter-parameter> "," <filter-parameter-list>``
+
+ and ``filter-parameter``:
+
+ ``( <param-name> "=" <param-value> ) | <param-value>``
+
+ ``param-value`` can further be quoted in ``[`` / ``]`` in case the value
+ contains characters like ``,`` or ``=``. This is used in particular with
+ the ``lavfi`` filter, which uses a very similar syntax as mpv (MPlayer
+ historically) to specify filters and their parameters.
You can also set defaults for each filter. The defaults are applied before the
normal filter parameters.
@@ -21,6 +55,17 @@ normal filter parameters.
wrapper, which gives you access to most of libavfilter's filters. This
includes all filters that have been ported from MPlayer to libavfilter.
+ Most filters are deprecated in some ways, unless they're only available
+ in mpv (such as filters which deal with mpv specifics, or which are
+ implemented in mpv only).
+
+ If a filter is not builtin, the ``lavfi-bridge`` will be automatically
+ tried. This bridge does not support help output, and does not verify
+ parameters before the filter is actually used. Although the mpv syntax
+ is rather similar to libavfilter's, it's not the same. (Which means not
+ everything accepted by vf_lavfi's ``graph`` option will be accepted by
+ ``--vf``.)
+
Video filters are managed in lists. There are a few commands to manage the
filter list.
@@ -754,6 +799,15 @@ Available filters are:
:no: Deinterlace all frames.
:yes: Only deinterlace frames marked as interlaced (default).
+ ``reversal-bug=<yes|no>``
+ :no: Use the API as it was interpreted by older Mesa drivers. While
+ this interpretation was more obvious and inuitive, it was
+ apparently wrong, and not shared by Intel driver developers.
+ :yes: Use Intel interpretation of surface forward and backwards
+ references (default). This is what Intel drivers and newer Mesa
+ drivers expect. Matters only for the advanced deinterlacing
+ algorithms.
+
``vdpaupp``
VDPAU video post processing. Works with ``--vo=vdpau`` and ``--vo=opengl``
only. This filter is automatically inserted if deinterlacing is requested
diff --git a/DOCS/man/vo.rst b/DOCS/man/vo.rst
index 6ff4b42..a282541 100644
--- a/DOCS/man/vo.rst
+++ b/DOCS/man/vo.rst
@@ -323,9 +323,8 @@ Available video output drivers are:
``vaapi``
Intel VA API video output driver with support for hardware decoding. Note
- that there is absolutely no reason to use this, other than wanting to use
- hardware decoding to save power on laptops, or possibly preventing video
- tearing with some setups.
+ that there is absolutely no reason to use this, other than compatibility.
+ This is low quality, and has issues with OSD.
.. note:: This driver is for compatibility with crappy systems. You can
use vaapi hardware decoding with ``--vo=opengl`` too.
@@ -419,14 +418,6 @@ Available video output drivers are:
JPEG files, extension .jpeg.
png
PNG files.
- ppm
- Portable bitmap format.
- pgm
- Portable graymap format.
- pgmyuv
- Portable graymap format, using the YV12 pixel format.
- tga
- Truevision TGA.
``--vo-image-png-compression=<0-9>``
PNG compression factor (speed vs. file size tradeoff) (default: 7)
diff --git a/DOCS/mplayer-changes.rst b/DOCS/mplayer-changes.rst
index df33f66..7c8ec50 100644
--- a/DOCS/mplayer-changes.rst
+++ b/DOCS/mplayer-changes.rst
@@ -212,7 +212,7 @@ Command Line Switches
``-no<opt>`` ``--no-<opt>`` (add a dash)
``-a52drc level`` ``--ad-lavc-ac3drc=level``
``-ac spdifac3`` ``--ad=spdif:ac3`` (see ``--ad=help``)
- ``-af volnorm`` ``--af=drc`` (renamed)
+ ``-af volnorm`` (removed; use acompressor ffmpeg filter instead)
``-afm hwac3`` ``--ad=spdif:ac3,spdif:dts``
``-ao alsa:device=hw=0.3`` ``--ao=alsa:device=[hw:0,3]``
``-aspect`` ``--video-aspect``
diff --git a/DOCS/release-policy.md b/DOCS/release-policy.md
index e68bc35..507f4c4 100644
--- a/DOCS/release-policy.md
+++ b/DOCS/release-policy.md
@@ -7,8 +7,7 @@ contains breaking changes, such as changed options or added/removed features,
and Y is incremented if a release contains only bugfixes and other minor
changes.
-There is only one release branch that keeps track of the latest version and
-will not be maintained separately.
+Releases are tagged on the master branch and will not be maintained separately.
The goal of releases is to provide Linux distributions with something to
package. If you want the newest features, just use the master branch.
@@ -19,18 +18,22 @@ Releases other than the latest release are unsupported and unmaintained.
Release procedure
-----------------
-- Merge master into branch release/current.
+While on master:
-- Create and/or update the `RELEASE_NOTES` file.
+- Update the `RELEASE_NOTES` file.
-- Create and/or update the `VERSION` file.
+- Update the `VERSION` file.
- Update `DOCS/client-api-changes.rst` and `DOCS/interface-changes.rst`
(in particular, update the last version numbers if necessary)
-- Create tag v0.X.Y.
+- Commit changes.
-- Push branch and tag to GitHub.
+- Create signed tag v0.X.Y.
+
+- Add -UNKNOWN suffix to version in `VERSION` file.
+
+- Commit changes, push branch and tag to GitHub.
- Create a new GitHub release using the content of `RELEASE_NOTES` related to
the new version.
diff --git a/DOCS/waf-buildsystem.rst b/DOCS/waf-buildsystem.rst
index adb1bc2..6222c5d 100644
--- a/DOCS/waf-buildsystem.rst
+++ b/DOCS/waf-buildsystem.rst
@@ -146,7 +146,7 @@ mpv's custom build step on top of waf
Build step is pretty much vanilla waf. The only difference being that the list
of source files can contain both strings or tuples. If a tuple is found,
-the second element in the tuple will the used to match the features detected
+the second element in the tuple will be used to match the features detected
in the configure step (the ``name`` field described above). If this feature
was not enabled during configure, the source file will not be compiled in.
diff --git a/README.md b/README.md
index d2b678c..3724239 100644
--- a/README.md
+++ b/README.md
@@ -33,14 +33,12 @@ Releases can be found on the [release list][releases].
- A not too ancient Linux, or Windows Vista or later, or OSX 10.8 or later.
- A somewhat capable CPU. Hardware decoding might sometimes help if the CPU
is too slow to decode video realtime, but must be explicitly enabled with
- the `--hwdec` option. On Windows, a CPU with SSE4 instruction set is required
- to get decent hardware decoding performance.
+ the `--hwdec` option.
- A not too crappy GPU. mpv is not intended to be used with bad GPUs. There are
many caveats with drivers or system compositors causing tearing, stutter,
etc. On Windows, you might want to make sure the graphics drivers are
- current, especially OpenGL. In some cases, ancient fallback video output
- methods can help (such as `--vo=xv` on Linux), but this use is not
- recommended or supported.
+ current. In some cases, ancient fallback video output methods can help
+ (such as `--vo=xv` on Linux), but this use is not recommended or supported.
## Downloads
@@ -91,7 +89,8 @@ to the *prefix* after it is compiled.
Essential dependencies (incomplete list):
- gcc or clang
-- X development headers (xlib, X extensions, libvdpau, libGL, libXv, ...)
+- X development headers (xlib, xrandr, xext, xscrnsaver, xinerama, libvdpau,
+ libGL, libXv, ...)
- Audio output development headers (libasound/ALSA, pulseaudio)
- FFmpeg libraries (libavutil libavcodec libavformat libswscale libavfilter
and either libswresample or libavresample)
@@ -123,12 +122,13 @@ FFmpeg dependencies:
Most of the above libraries are available in suitable versions on normal
Linux distributions. However FFmpeg is an exception (distro versions may be
too old to work at all or work well). For that reason you may want to use
-the separately available build wrapper ([mpv-build][mpv-build]) that first compiles FFmpeg
-libraries and libass, and then compiles the player statically linked against
-those.
+the separately available build wrapper ([mpv-build][mpv-build]) that first
+compiles FFmpeg libraries and libass, and then compiles the player statically
+linked against those.
If you want to build a Windows binary, you either have to use MSYS2 and MinGW,
-or cross-compile from Linux with MinGW. See [Windows compilation][windows_compilation].
+or cross-compile from Linux with MinGW. See
+[Windows compilation][windows_compilation].
## FFmpeg vs. Libav
diff --git a/RELEASE_NOTES b/RELEASE_NOTES
index 5730b39..b78b82e 100644
--- a/RELEASE_NOTES
+++ b/RELEASE_NOTES
@@ -1,3 +1,165 @@
+Release 0.25.0
+==============
+
+This release drops support for OS X 10.7 and earlier.
+Support for some optical media functionality (DVD/CD) is now disabled by default.
+
+More parts of the player are now licensed under LGPL. In particular:
+
+ * OSD symbol font
+ * ass_mp, sd_ass
+ * common.h
+ * demux/packet
+ * demux_mkv (with minor exceptions), ebml, matroska.h
+ * sd_lavc
+ * sub/osd
+
+Thanks to all contributors who have agreed to relicensing of their changes!
+See #2033 for details.
+
+Starting with this release, releases will be tagged on the master branch.
+The release/current branch is thus abandoned.
+
+
+Features
+--------
+
+Added
+~~~~~
+
+- TOOLS: add acompressor.lua script for runtime acompressor ffmpeg filter control
+- dvb: add support for DVB-T2
+- lavfi: support hwdec filters for --lavfi-complex
+- macOS: initial Touch Bar support
+- osc: add volume button (mouse wheel to change volume)
+- sub: add SDH subtitle filter
+- vo_opengl: add experimental vdpauglx backend
+- vo_opengl: implement videotoolbox hwdec on iOS
+
+
+Removed
+~~~~~~~
+
+- image_writer: remove useless formats (PPM, PGM, TGA)
+- af_drc: remove (use --af=acompressor instead; higher quality)
+
+
+Options and Commands
+--------------------
+
+Added
+~~~~~
+
+- --demuxer-lavf-probe-info
+- --sub-filter-sdh, --sub-filter-sdh-harder
+- add automatic libavfilter bridges to option parsing
+- command: add better runtime filter toggling method
+- command: add demux-start-time property
+- command: add property notifications for hwdec properties (#4289)
+- input: add "async" flag
+- osc: add user_opts.boxmaxchars for box layout title limit
+- player: add --keep-open-pause=no option
+- va_vavpp: reversal-bug=no
+- vo_opengl: --opengl-es=force2
+- vo_opengl: add --opengl-shader-cache-dir option for caching shaders on disk (mostly for ANGLE)
+- vo_opengl: angle: add --angle-flip to set the ANGLE present model
+
+
+Changed
+~~~~~~~
+
+- command: update sub-fps etc. options on runtime changes
+- vo_opengl: prefer X11 backends over Wayland backends
+- options: change --audio-file-auto default to not to load any files
+
+
+Deprecated
+~~~~~~~~~~
+
+- audio: deprecate most non-lavfi audio filters
+ (channels, equalizer, pan, volume)
+- video: deprecate almost all non-lavfi video filters
+ (buffer, crop, dlopen, dsize, eq, expand, flip, gradfun, mirror, noformat,
+ pullup, rotate, scale, stereo3d, sub, yadif)
+- options: deprecate --loop semantics (planned alias to --loop-file)
+
+
+Fixes and Minor Enhancements
+----------------------------
+
+- Windows: demux_cue: fix UTF-8 paths
+- Windows: fix mismatched free/talloc_free (#4315)
+- Windows: fix undefined behaviour when toggling fullscreen
+- ao_alsa: close lost audio devices (#4189)
+- ao_alsa: filter fewer devices
+- ao_alsa: fix an error check (#4188)
+- ao_jack: update latency on buffer_size/graph change
+- ao_wasapi: do not pass nonsense to drivers with double precision formats
+- ass_mp: reallocate cached subtitle image data on format changes (#4325)
+- build: decouple dvdnav check from dvdread (#4290)
+- build: encode_lavc: fix build failure after libavcodec major bump
+- build: fix build with HAVE_GL==0
+- build: replace android-gl check with a standard GLES3 check
+- build: update bundled waf to 1.9.8
+- build: vd_lavc: fix potential build failure with vaapi
+- demux_lavf: disable half-working mp4 edit list support in libavcodec
+- demux_lavf: skip avformat_find_stream_info() for some formats (hls, mp4, mkv by default)
+- etc/encoding_profiles.conf: update and remove deprecated stuff
+- etc/mpv.conf: remove deprecated options
+- external_files: actually try to autoload from fallback paths
+- external_files: enable autoloading with URLs (#3264)
+- image_writer: make it work with libavcodec's jpg encoder
+- macOS: add --ontop-level option for modifying ontop window level (#2376, #3974)
+- macOS: add key mappings for previous and next Media Keys (#4204)
+- macOS: add option to force dedicated GPU (#3242)
+- macOS: fix autofit options on HiDPI resolutions without HiDPI scaling (#4194)
+- macOS: fix black edges on live resize
+- macOS: fix cursor hiding in the Dock area and top of the screen
+- macOS: fix event propagation of menu bar item key shortcuts
+- macOS: fix first responder when borderless window is used
+- macOS: fix key input in certain circumstances
+- macOS: fix retrieval of non-fullscreen window size, also while animating (#4323)
+- macOS: fix scroll wheel input with Shift modifier (#3506)
+- macOS: improve bundle environment variable handling (#2061)
+- macOS: make window draggable on init
+- macOS: only move window into screen bounds when changing screens (#4178)
+- macOS: only report mouse movements when window is not being dragged (might have triggered OSC and other scripts)
+- macOS: properly restore shell state when quitting from the Dock's context menu
+- macOS: refactor mouse events and cursor visibility (#1817, #3856, #4147)
+- macOS: set background of the title bar from black to white
+- osc: bottom/topbar: add fullscreen button
+- osc: bottom/topbar: don't clip title vertically
+- osc: bottom/topbar: increase timecodes width a bit (#3952)
+- osc: fix PlayResX undefined warning when aspect is 0
+- osc: fix window dragging with showwindowed=no (#1819)
+- osc: make title configurable and use property expansion on it (#4221)
+- osc: refactor osc message scaling (#4081, #4083, #4102)
+- player: don't block playback stop when seeking
+- player: enable "buffering" pausing for DASH streams too
+- player: fix core-idle and eof-reached update notifcations
+- player: make screenshot commands honor the async flag (#4250)
+- player: reduce blocking on VO when switching pause (#4152)
+- sd_ass: disable --sub-fix-timing if sub style override is fully disabled
+- stream/stream_dvdnav: don't ignore setting title (#4283)
+- stream_dvd: fix subs/audio detection on DVDs containing multi-PGC titles
+- vf_vavpp: add advanced deint bug compatibility for Intel vaapi drivers
+- vf_vavpp: fix first-field mode
+- vo_opengl: fix crash with temporal dithering in dumb mode
+- vo_opengl: hwdec_d3d11egl: make it work with some ANGLE DLL versions
+- vo_x11: reduce flickering on playlist navigation
+- wayland: correctly map mouse buttons
+
+
+This listing is not complete. Check DOCS/client-api-changes.rst for a history
+of changes to the client API, and DOCS/interface-changes.rst for a history
+of changes to other user-visible interfaces.
+
+A complete changelog can be seen by running `git log v0.24.0..v0.25.0`
+in the git repository or by visiting either
+https://github.com/mpv-player/mpv/compare/v0.24.0...v0.25.0 or
+https://git.srsfckn.biz/mpv/log/?qt=range&q=v0.24.0..v0.25.0
+
+
Release 0.24.0
==============
diff --git a/TOOLS/gen-osd-font.sh b/TOOLS/gen-osd-font.sh
new file mode 100755
index 0000000..6838692
--- /dev/null
+++ b/TOOLS/gen-osd-font.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+# This script is expected to be called as TOOLS/gen-osd-font.sh (it will access
+# TOOLS/mpv-osd-symbols.sfdir), and it will write sub/osd_font.otf.
+
+# Needs fontforge with python scripting
+
+fontforge -lang=py -c 'f=open(argv[1]); f.generate(argv[2])' \
+ TOOLS/mpv-osd-symbols.sfdir sub/osd_font.otf
diff --git a/TOOLS/lua/acompressor.lua b/TOOLS/lua/acompressor.lua
new file mode 100644
index 0000000..9ce4ebf
--- /dev/null
+++ b/TOOLS/lua/acompressor.lua
@@ -0,0 +1,126 @@
+-- This script adds control to the dynamic range compression ffmpeg
+-- filter including key bindings for adjusting parameters.
+--
+-- See https://ffmpeg.org/ffmpeg-filters.html#acompressor for explanation
+-- of the parameteres.
+--
+-- Default key bindings:
+-- n: Toggle dynamic range compression on or off
+-- F1/Shift+F1: Increase/Decrease threshold parameter
+-- F2/Shift+F2: Increase/Decrease ratio parameter
+-- F3/Shift+F3: Increase/Decrease knee parameter
+-- F4/Shift+F4: Increase/Decrease makeup gain parameter
+-- F5/Shift+F5: Increase/Decrease attack parameter
+-- F6/Shift+F6: Increase/Decrease release parameter
+--
+-- To change key bindings in input.conf use:
+-- BINDING script-message-to acompressor toggle-acompressor
+-- BINDING script-message-to acompressor update-param PARAM INCREMENT
+-- BINDING is the key binding to use, PARAM is either 'attack', 'release',
+-- 'threshold', 'ratio', 'knee' or 'makeup' and INCREMENT is a signed floating
+-- point value to add to the current parameter value.
+--
+-- You may also just adjust default parameters to your liking in this table.
+local params = {
+ -- 'hide' defines wether the parameter should be hidden from OSD display (it will still be included in ffmpeg filter graph).
+ -- 'input_format' is used to parse the value back from a obtained filter graph.
+ -- 'output_format' defines how the value shall be formatted for creating the filter graph.
+ { name = 'Attack', value= 20, min=0.01, max=2000, hide= 20, input_format='attack=(%d+[.%d+]*)', output_format='%g' },
+ { name = 'Release', value=250, min=0.01, max=9000, hide=250, input_format='release=(%d+[.%d+]*)', output_format='%g' },
+ { name = 'Threshold', value=-25, min= -30, max= 0, hide=nil, input_format='threshold=(-%d+[.%d+]*)dB', output_format='%gdB' },
+ { name = 'Ratio', value= 3, min= 1, max= 20, hide=nil, input_format='ratio=(%d+[.%d+]*)', output_format='%g' },
+ { name = 'Knee', value= 2, min= 1, max= 10, hide= 2, input_format='knee=(%d+[.%d+]*)dB', output_format='%gdB' },
+ { name = 'Makeup', value= 8, min= 0, max= 24, hide=nil, input_format='makeup=(%d+[.%d+]*)dB', output_format='%gdB' }
+}
+
+-- Defines the mpv filter label to be used. This allows us to easily add/replace/remove it, so
+-- it should be left so something meaningful and unique.
+local filter_label = 'acompressor'
+
+local function update_filter()
+ local graph = {}
+ local pretty = {}
+
+ for _,param in pairs(params) do
+ graph[#graph+1] = string.format('%s=' .. param.output_format, string.lower(param.name), param.value)
+ if param.hide ~= param.value then
+ pretty[#pretty+1] = string.format('%s: ' .. param.output_format, param.name, param.value)
+ end
+ end
+
+ if #pretty == 0 then
+ pretty = ''
+ else
+ pretty = '\n(' .. table.concat(pretty, ', ') .. ')'
+ end
+
+ mp.command(string.format('no-osd af add @%s:lavfi=[acompressor=%s]; show-text "Dynamic range compressor: enabled%s" 4000', filter_label, table.concat(graph, ':'), pretty))
+end
+
+local function read_filter()
+ local graph = nil
+ local af = mp.get_property_native('af', {})
+
+ for i = 1, #af do
+ if af[i]['name'] == 'lavfi' and af[i]['label'] == 'acompressor' then
+ graph = af[i]['params']['graph']
+ break
+ end
+ end
+
+ if graph == nil then
+ return false
+ end
+
+ for _,param in pairs(params) do
+ local value = tonumber(string.match(graph, param.input_format))
+ if value ~= nil and value >= param.min and value <= param.max then
+ param.value = value
+ end
+ end
+ return true
+end
+
+local function toggle_acompressor()
+ if read_filter() then
+ mp.command(string.format('no-osd af del @%s; show-text "Dynamic range compressor: disabled"', filter_label))
+ else
+ update_filter()
+ end
+end
+
+local function update_param(name, increment)
+ -- The current filter values could come from a watch_later config.
+ -- read_filter() will parse them, so that we don't clobber those with our default values.
+ read_filter()
+ for _,param in pairs(params) do
+ if string.lower(param.name) == string.lower(name) then
+ param.value = math.max(param.min, math.min(param.value + increment, param.max))
+ update_filter()
+ return
+ end
+ end
+
+ mp.msg.error('Unknown parameter "' .. name .. '"')
+end
+
+mp.add_key_binding("n", "toggle-acompressor", toggle_acompressor)
+mp.register_script_message('update-param', update_param)
+
+mp.add_key_binding("F1", 'acompressor-inc-threshold', function() update_param('threshold', -5); end, { repeatable = true })
+mp.add_key_binding("Shift+F1", 'acompressor-dec-threshold', function() update_param('Threshold', 5); end, { repeatable = true })
+
+mp.add_key_binding("F2", 'acompressor-inc-ratio', function() update_param('Ratio', 1); end, { repeatable = true })
+mp.add_key_binding("Shift+F2", 'acompressor-dec-ratio', function() update_param('Ratio', -1); end, { repeatable = true })
+
+mp.add_key_binding("F3", 'acompressor-inc-knee', function() update_param('Knee', 1); end, { repeatable = true })
+mp.add_key_binding("Shift+F3", 'acompressor-dec-knee', function() update_param('Knee', -1); end, { repeatable = true })
+
+mp.add_key_binding("F4", 'acompressor-inc-makeup', function() update_param('Makeup', 1); end, { repeatable = true })
+mp.add_key_binding("Shift+F4", 'acompressor-dec-makeup', function() update_param('Makeup', -1); end, { repeatable = true })
+
+mp.add_key_binding("F5", 'acompressor-inc-attack', function() update_param('Attack', 10); end, { repeatable = true })
+mp.add_key_binding("Shift+F5", 'acompressor-dec-attack', function() update_param('Attack', -10); end, { repeatable = true })
+
+mp.add_key_binding("F6", 'acompressor-inc-release', function() update_param('Release', 10); end, { repeatable = true })
+mp.add_key_binding("Shift+F6", 'acompressor-dec-release', function() update_param('Release', -10); end, { repeatable = true })
diff --git a/TOOLS/lua/drc-control.lua b/TOOLS/lua/drc-control.lua
deleted file mode 100644
index 83cdb9c..0000000
--- a/TOOLS/lua/drc-control.lua
+++ /dev/null
@@ -1,99 +0,0 @@
--- This script enables live control of the dynamic range compression
--- (drc) audio filter while the video is playing back. This can be
--- useful to avoid having to stop and restart mpv to adjust filter
--- parameters. See the entry for "drc" under the "AUDIO FILTERS"
--- section of the man page for a complete description of the filter.
---
--- This script registers the key-binding "\" to toggle the filter between
---
--- * off
--- * method=1 (single-sample smoothing)
--- * method=2 (multi-sample smoothing)
---
--- It registers the keybindings ctrl+9/ctrl+0 to decrease/increase the
--- target ampltiude. These keys will insert the filter at the default
--- target amplitude of 0.25 if it was not previously present.
---
--- OSD feedback of the current filter state is displayed on pressing
--- each bound key.
-
-script_name = mp.get_script_name()
-
-function print_state(params)
- if params then
- mp.osd_message(script_name..":\n"
- .."method = "..params["method"].."\n"
- .."target = "..params["target"])
- else
- mp.osd_message(script_name..":\noff")
- end
-end
-
-function get_index_of_drc(afs)
- for i,af in pairs(afs) do
- if af["label"] == script_name then
- return i
- end
- end
-end
-
-function append_drc(afs)
- afs[#afs+1] = {
- name = "drc",
- label = script_name,
- params = {
- method = "1",
- target = "0.25"
- }
- }
- print_state(afs[#afs]["params"])
-end
-
-function modify_or_create_af(fun)
- afs = mp.get_property_native("af")
- i = get_index_of_drc(afs)
- if not i then
- append_drc(afs)
- else
- fun(afs, i)
- end
- mp.set_property_native("af", afs)
-end
-
-function drc_toggle_method_handler()
- modify_or_create_af(
- function (afs, i)
- new_method=(afs[i]["params"]["method"]+1)%3
- if new_method == 0 then
- table.remove(afs, i)
- print_state(nil)
- else
- afs[i]["params"]["method"] = tostring((afs[i]["params"]["method"])%2+1)
- print_state(afs[i]["params"])
- end
- end
- )
-end
-
-function drc_scale_target(factor)
- modify_or_create_af(
- function (afs)
- afs[i]["params"]["target"] = tostring(afs[i]["params"]["target"]*factor)
- print_state(afs[i]["params"])
- end
- )
-end
-
-function drc_louder_handler()
- drc_scale_target(2.0)
-end
-
-function drc_quieter_handler()
- drc_scale_target(0.5)
-end
-
--- toggle between off, method 1 and method 2
-mp.add_key_binding("\\", "drc_toggle_method", drc_toggle_method_handler)
--- increase or decrease target volume
-mp.add_key_binding("ctrl+9", "drc_quieter", drc_quieter_handler)
-mp.add_key_binding("ctrl+0", "drc_louder", drc_louder_handler)
diff --git a/TOOLS/mpv-osd-symbols.sfdir/.notdef.glyph b/TOOLS/mpv-osd-symbols.sfdir/.notdef.glyph
new file mode 100644
index 0000000..99cdece
--- /dev/null
+++ b/TOOLS/mpv-osd-symbols.sfdir/.notdef.glyph
@@ -0,0 +1,6 @@
+StartChar: .notdef
+Encoding: 65536 -1 0
+Width: 400
+Flags: W
+LayerCount: 2
+EndChar
diff --git a/TOOLS/mpv-osd-symbols.sfdir/font.props b/TOOLS/mpv-osd-symbols.sfdir/font.props
new file mode 100644
index 0000000..17668f9
--- /dev/null
+++ b/TOOLS/mpv-osd-symbols.sfdir/font.props
@@ -0,0 +1,77 @@
+SplineFontDB: 3.0
+FontName: mpv-osd-symbols-Regular
+FullName: mpv-osd-symbols Regular
+FamilyName: mpv-osd-symbols
+Weight: Normal
+Copyright: This is generated file.
+Version: 001.000
+ItalicAngle: 0
+UnderlinePosition: -133
+UnderlineWidth: 50
+Ascent: 800
+Descent: 200
+InvalidEm: 0
+sfntRevision: 0x00010000
+LayerCount: 2
+Layer: 0 0 "Back" 1
+Layer: 1 0 "Fore" 0
+XUID: [1021 879 -1597228462 15927]
+StyleMap: 0x0040
+FSType: 8
+OS2Version: 3
+OS2_WeightWidthSlopeOnly: 0
+OS2_UseTypoMetrics: 0
+CreationTime: 1408646554
+ModificationTime: 1492879730
+PfmFamily: 81
+TTFWeight: 400
+TTFWidth: 5
+LineGap: 0
+VLineGap: 0
+Panose: 0 0 5 0 0 0 0 0 0 0
+OS2TypoAscent: 800
+OS2TypoAOffset: 0
+OS2TypoDescent: -200
+OS2TypoDOffset: 0
+OS2TypoLinegap: 90
+OS2WinAscent: 1000
+OS2WinAOffset: 0
+OS2WinDescent: 200
+OS2WinDOffset: 0
+HheadAscent: 1000
+HheadAOffset: 0
+HheadDescent: -200
+HheadDOffset: 0
+OS2SubXSize: 650
+OS2SubYSize: 600
+OS2SubXOff: 0
+OS2SubYOff: 75
+OS2SupXSize: 650
+OS2SupYSize: 600
+OS2SupXOff: 0
+OS2SupYOff: 350
+OS2StrikeYSize: 50
+OS2StrikeYPos: 220
+OS2Vendor: 'PfEd'
+OS2CodePages: 00000001.00000000
+OS2UnicodeRanges: 00000000.00000000.00000000.00000000
+DEI: 91125
+LangName: 1033 "" "" "Regular" "1.000;PfEd;mpv-osd-symbols-Regular" "mpv-osd-symbols" "Version 1.000;PS 001.000;hotconv 1.0.70;makeotf.lib2.5.58329"
+Encoding: UnicodeBmp
+UnicodeInterp: none
+NameList: AGL For New Fonts
+DisplaySize: -72
+AntiAlias: 1
+FitToEm: 0
+WinInfo: 57600 8 2
+BeginPrivate: 8
+BlueValues 31 [-10 0 640 650 720 730 800 810]
+BlueScale 5 0.037
+BlueShift 1 0
+BlueFuzz 1 0
+StdHW 4 [65]
+StdVW 4 [65]
+StemSnapH 8 [65 800]
+StemSnapV 8 [65 150]
+EndPrivate
+EndSplineFont
diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE001.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE001.glyph
new file mode 100644
index 0000000..ebfb715
--- /dev/null
+++ b/TOOLS/mpv-osd-symbols.sfdir/uniE001.glyph
@@ -0,0 +1,16 @@
+StartChar: uniE001
+Encoding: 57345 57345 1
+Width: 880
+GlyphClass: 2
+Flags: MW
+HStem: 780 20G<200 200>
+VStem: 200 375<400 400 400 800>
+LayerCount: 2
+Fore
+SplineSet
+575 400 m 1
+ 200 0 l 1
+ 200 800 l 1
+ 575 400 l 1
+EndSplineSet
+EndChar
diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE002.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE002.glyph
new file mode 100644
index 0000000..f47c153
--- /dev/null
+++ b/TOOLS/mpv-osd-symbols.sfdir/uniE002.glyph
@@ -0,0 +1,22 @@
+StartChar: uniE002
+Encoding: 57346 57346 2
+Width: 880
+GlyphClass: 2
+Flags: MW
+HStem: 0 800<200 350 200 350 200 500 500 650>
+VStem: 200 150<0 800 0 800> 500 150<0 800 0 800>
+LayerCount: 2
+Fore
+SplineSet
+350 800 m 1
+ 350 0 l 1
+ 200 0 l 1
+ 200 800 l 1
+ 350 800 l 1
+650 800 m 1
+ 650 0 l 1
+ 500 0 l 1
+ 500 800 l 1
+ 650 800 l 1
+EndSplineSet
+EndChar
diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE003.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE003.glyph
new file mode 100644
index 0000000..ba45630
--- /dev/null
+++ b/TOOLS/mpv-osd-symbols.sfdir/uniE003.glyph
@@ -0,0 +1,17 @@
+StartChar: uniE003
+Encoding: 57347 57347 3
+Width: 880
+GlyphClass: 2
+Flags: MW
+HStem: 0 720<110 775 110 775>
+VStem: 110 665<0 720 0 720>
+LayerCount: 2
+Fore
+SplineSet
+775 720 m 1
+ 775 0 l 1
+ 110 0 l 1
+ 110 720 l 1
+ 775 720 l 1
+EndSplineSet
+EndChar
diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE004.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE004.glyph
new file mode 100644
index 0000000..62ce5f7
--- /dev/null
+++ b/TOOLS/mpv-osd-symbols.sfdir/uniE004.glyph
@@ -0,0 +1,20 @@
+StartChar: uniE004
+Encoding: 57348 57348 4
+Width: 880
+GlyphClass: 2
+Flags: MW
+HStem: 780 20G<423 423 813 813>
+VStem: 48 765<400 400>
+LayerCount: 2
+Fore
+SplineSet
+423 800 m 1
+ 423 0 l 1
+ 48 400 l 1
+ 423 800 l 1
+813 800 m 1
+ 813 0 l 1
+ 438 400 l 1
+ 813 800 l 1
+EndSplineSet
+EndChar
diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE005.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE005.glyph
new file mode 100644
index 0000000..26db474
--- /dev/null
+++ b/TOOLS/mpv-osd-symbols.sfdir/uniE005.glyph
@@ -0,0 +1,20 @@
+StartChar: uniE005
+Encoding: 57349 57349 5
+Width: 880
+GlyphClass: 2
+Flags: MW
+HStem: 780 20G<95 95 485 485>
+VStem: 95 765<400 400 400 800 400 800>
+LayerCount: 2
+Fore
+SplineSet
+470 400 m 1
+ 95 0 l 1
+ 95 800 l 1
+ 470 400 l 1
+860 400 m 1
+ 485 0 l 1
+ 485 800 l 1
+ 860 400 l 1
+EndSplineSet
+EndChar
diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE006.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE006.glyph
new file mode 100644
index 0000000..474d3c1
--- /dev/null
+++ b/TOOLS/mpv-osd-symbols.sfdir/uniE006.glyph
@@ -0,0 +1,47 @@
+StartChar: uniE006
+Encoding: 57350 57350 6
+Width: 880
+GlyphClass: 2
+Flags: MW
+HStem: -10 11<396 461 396 543> 196 171<397 461 396 462> 401 31 801 9<397 462 397 397>
+VStem: 20 9<369 434 369 513> 224 173<369 408 367 434 367 434> 363 34 397 65<367 404 367 408 367 404 606 801> 462 173<368 404 404 404> 830 10<368 433 433 433>
+LayerCount: 2
+Fore
+SplineSet
+430 810 m 0x9940
+ 656 810 840 626 840 400 c 0
+ 840 174 656 -10 430 -10 c 0
+ 204 -10 20 174 20 400 c 0
+ 20 626 204 810 430 810 c 0x9940
+397 606 m 1x5280
+ 462 606 l 1
+ 462 801 l 1
+ 397 801 l 1
+ 397 606 l 1x5280
+462 367 m 1
+ 462 404 l 1
+ 710 627 l 1
+ 683 657 l 1
+ 439 437 l 1
+ 363 592 l 1
+ 318 570 l 1x4280
+ 397 408 l 1
+ 397 367 l 1x44
+ 462 367 l 1
+224 434 m 1
+ 29 434 l 1
+ 29 369 l 1
+ 224 369 l 1
+ 224 434 l 1
+830 368 m 1
+ 830 433 l 1
+ 635 433 l 1
+ 635 368 l 1
+ 830 368 l 1
+461 196 m 1
+ 396 196 l 1
+ 396 1 l 1
+ 461 1 l 1
+ 461 196 l 1
+EndSplineSet
+EndChar
diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE007.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE007.glyph
new file mode 100644
index 0000000..719d9fa
--- /dev/null
+++ b/TOOLS/mpv-osd-symbols.sfdir/uniE007.glyph
@@ -0,0 +1,21 @@
+StartChar: uniE007
+Encoding: 57351 57351 7
+Width: 880
+GlyphClass: 2
+Flags: MW
+HStem: -10 80<339 430 339 543> 730 80<339 430>
+VStem: 20 80<309 491 309 513>
+LayerCount: 2
+Fore
+SplineSet
+430 -10 m 0
+ 204 -10 20 174 20 400 c 0
+ 20 626 204 810 430 810 c 0
+ 656 810 840 626 840 400 c 0
+ 840 174 656 -10 430 -10 c 0
+430 70 m 1
+ 430 730 l 1
+ 248 730 100 582 100 400 c 0
+ 100 218 248 70 430 70 c 1
+EndSplineSet
+EndChar
diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE008.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE008.glyph
new file mode 100644
index 0000000..b36acfb
--- /dev/null
+++ b/TOOLS/mpv-osd-symbols.sfdir/uniE008.glyph
@@ -0,0 +1,37 @@
+StartChar: uniE008
+Encoding: 57352 57352 8
+Width: 880
+GlyphClass: 2
+Flags: MW
+HStem: -10 80<339 521 339 543> 240 160<269 313 546 590> 480 160<408 452> 730 80<339 521>
+VStem: 20 80<309 491 309 513> 211 160<298 342> 350 160<538 582> 488 160<298 342> 760 80<309 491>
+LayerCount: 2
+Fore
+SplineSet
+430 -10 m 0xfa80
+ 204 -10 20 174 20 400 c 0
+ 20 626 204 810 430 810 c 0
+ 656 810 840 626 840 400 c 0
+ 840 174 656 -10 430 -10 c 0xfa80
+430 70 m 0
+ 612 70 760 218 760 400 c 0
+ 760 582 612 730 430 730 c 0
+ 248 730 100 582 100 400 c 0
+ 100 218 248 70 430 70 c 0
+430 480 m 0
+ 386 480 350 516 350 560 c 0
+ 350 604 386 640 430 640 c 0
+ 474 640 510 604 510 560 c 0
+ 510 516 474 480 430 480 c 0
+291 240 m 0xfd80
+ 247 240 211 276 211 320 c 0
+ 211 364 247 400 291 400 c 0
+ 335 400 371 364 371 320 c 0
+ 371 276 335 240 291 240 c 0xfd80
+568 240 m 0
+ 524 240 488 276 488 320 c 0
+ 488 364 524 400 568 400 c 0
+ 612 400 648 364 648 320 c 0
+ 648 276 612 240 568 240 c 0
+EndSplineSet
+EndChar
diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE009.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE009.glyph
new file mode 100644
index 0000000..26a2f37
--- /dev/null
+++ b/TOOLS/mpv-osd-symbols.sfdir/uniE009.glyph
@@ -0,0 +1,17 @@
+StartChar: uniE009
+Encoding: 57353 57353 9
+Width: 880
+Flags: HMW
+LayerCount: 2
+Fore
+SplineSet
+716 10 m 1
+ 378 244 l 1
+ 165 244 l 1
+ 165 559 l 1
+ 379 559 l 1
+ 716 793 l 1
+ 716 10 l 1
+EndSplineSet
+Validated: 1
+EndChar
diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE00A.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE00A.glyph
new file mode 100644
index 0000000..634249f
--- /dev/null
+++ b/TOOLS/mpv-osd-symbols.sfdir/uniE00A.glyph
@@ -0,0 +1,50 @@
+StartChar: uniE00A
+Encoding: 57354 57354 10
+Width: 880
+GlyphClass: 2
+Flags: MW
+HStem: 252 48<406 406 456 456> 375 50<57 282 57 282 578 805> 500 48<406 406> 627 20G<149 149 713 713>
+VStem: 282 48<375 375 425 425> 406 50<26 252 26 252 548 774> 530 48<375 375>
+LayerCount: 2
+Fore
+SplineSet
+805 375 m 1
+ 578 375 l 1
+ 574 352 565 331 553 313 c 1
+ 713 153 l 1
+ 678 118 l 1
+ 518 278 l 1
+ 500 265 479 256 456 252 c 1
+ 456 26 l 1
+ 406 26 l 1
+ 406 252 l 1
+ 383 256 362 265 343 277 c 1
+ 184 118 l 1
+ 149 153 l 1
+ 308 312 l 1
+ 295 330 286 352 282 375 c 1
+ 57 375 l 1
+ 57 425 l 1
+ 282 425 l 1
+ 286 448 295 470 308 488 c 1
+ 149 647 l 1
+ 184 682 l 1
+ 343 523 l 1
+ 362 535 383 544 406 548 c 1
+ 406 774 l 1
+ 456 774 l 1
+ 456 548 l 1
+ 479 544 500 535 518 522 c 1
+ 678 682 l 1
+ 713 647 l 1
+ 553 487 l 1
+ 565 469 574 448 578 425 c 1
+ 805 425 l 1
+ 805 375 l 1
+430 300 m 0
+ 485 300 530 345 530 400 c 0
+ 530 455 485 500 430 500 c 0
+ 375 500 330 455 330 400 c 0
+ 330 345 375 300 430 300 c 0
+EndSplineSet
+EndChar
diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE00B.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE00B.glyph
new file mode 100644
index 0000000..21e0966
--- /dev/null
+++ b/TOOLS/mpv-osd-symbols.sfdir/uniE00B.glyph
@@ -0,0 +1,27 @@
+StartChar: uniE00B
+Encoding: 57355 57355 11
+Width: 880
+GlyphClass: 2
+Flags: MW
+HStem: -10 244<341 519 341 543> 566 54<413.5 446.5 413.5 519> 740 70<413.5 446.5>
+VStem: 20 88<354 446 354 513> 752 88<354 446>
+LayerCount: 2
+Fore
+SplineSet
+430 -10 m 0
+ 204 -10 20 174 20 400 c 0
+ 20 626 204 810 430 810 c 0
+ 656 810 840 626 840 400 c 0
+ 840 174 656 -10 430 -10 c 0
+430 620 m 0
+ 463 620 490 647 490 680 c 0
+ 490 713 463 740 430 740 c 0
+ 397 740 370 713 370 680 c 0
+ 370 647 397 620 430 620 c 0
+430 234 m 0
+ 608 234 752 308 752 400 c 0
+ 752 492 608 566 430 566 c 0
+ 252 566 108 492 108 400 c 0
+ 108 308 252 234 430 234 c 0
+EndSplineSet
+EndChar
diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE010.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE010.glyph
new file mode 100644
index 0000000..519e34a
--- /dev/null
+++ b/TOOLS/mpv-osd-symbols.sfdir/uniE010.glyph
@@ -0,0 +1,21 @@
+StartChar: uniE010
+Encoding: 57360 57360 12
+Width: 334
+GlyphClass: 2
+Flags: MW
+HStem: 0 90<221 258 221 258> 550 90<221 258 221 221>
+VStem: 76 145<90 550 90 640 90 640>
+LayerCount: 2
+Fore
+SplineSet
+258 640 m 1
+ 258 550 l 1
+ 221 550 l 1
+ 221 90 l 1
+ 258 90 l 1
+ 258 0 l 1
+ 76 0 l 1
+ 76 640 l 1
+ 258 640 l 1
+EndSplineSet
+EndChar
diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE011.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE011.glyph
new file mode 100644
index 0000000..b3c7b56
--- /dev/null
+++ b/TOOLS/mpv-osd-symbols.sfdir/uniE011.glyph
@@ -0,0 +1,17 @@
+StartChar: uniE011
+Encoding: 57361 57361 13
+Width: 334
+GlyphClass: 2
+Flags: MW
+HStem: 0 640<84 250 84 250>
+VStem: 84 166<0 640 0 640>
+LayerCount: 2
+Fore
+SplineSet
+250 640 m 1
+ 250 0 l 1
+ 84 0 l 1
+ 84 640 l 1
+ 250 640 l 1
+EndSplineSet
+EndChar
diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE012.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE012.glyph
new file mode 100644
index 0000000..86c07cd
--- /dev/null
+++ b/TOOLS/mpv-osd-symbols.sfdir/uniE012.glyph
@@ -0,0 +1,21 @@
+StartChar: uniE012
+Encoding: 57362 57362 14
+Width: 334
+GlyphClass: 2
+Flags: MW
+HStem: 0 90<76 113 76 258 76 113> 550 90<76 113 76 258>
+VStem: 113 145<90 550 550 550>
+LayerCount: 2
+Fore
+SplineSet
+113 90 m 1
+ 113 550 l 1
+ 76 550 l 1
+ 76 640 l 1
+ 258 640 l 1
+ 258 0 l 1
+ 76 0 l 1
+ 76 90 l 1
+ 113 90 l 1
+EndSplineSet
+EndChar
diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE013.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE013.glyph
new file mode 100644
index 0000000..b456777
--- /dev/null
+++ b/TOOLS/mpv-osd-symbols.sfdir/uniE013.glyph
@@ -0,0 +1,17 @@
+StartChar: uniE013
+Encoding: 57363 57363 15
+Width: 334
+GlyphClass: 2
+Flags: MW
+HStem: 255 130<102 232 102 232>
+VStem: 102 130<255 385 255 385>
+LayerCount: 2
+Fore
+SplineSet
+232 385 m 1
+ 232 255 l 1
+ 102 255 l 1
+ 102 385 l 1
+ 232 385 l 1
+EndSplineSet
+EndChar
diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE101.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE101.glyph
new file mode 100644
index 0000000..8e0d4b6
--- /dev/null
+++ b/TOOLS/mpv-osd-symbols.sfdir/uniE101.glyph
@@ -0,0 +1,16 @@
+StartChar: uniE101
+Encoding: 57601 57601 16
+Width: 880
+GlyphClass: 2
+Flags: MW
+HStem: 780 20G<222 222>
+VStem: 222 600<400 400 400 800 400 800>
+LayerCount: 2
+Fore
+SplineSet
+822 400 m 1
+ 222 0 l 1
+ 222 800 l 1
+ 822 400 l 1
+EndSplineSet
+EndChar
diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE104.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE104.glyph
new file mode 100644
index 0000000..7c7ba13
--- /dev/null
+++ b/TOOLS/mpv-osd-symbols.sfdir/uniE104.glyph
@@ -0,0 +1,25 @@
+StartChar: uniE104
+Encoding: 57604 57604 17
+Width: 880
+GlyphClass: 2
+Flags: MW
+HStem: 0 800<3 126 3 126 3 502 3 878> 780 20G<3 126 126 126 502 502 878 878>
+VStem: 3 123<0 800 0 800>
+LayerCount: 2
+Fore
+SplineSet
+126 800 m 1x60
+ 126 0 l 1
+ 3 0 l 1xa0
+ 3 800 l 1
+ 126 800 l 1x60
+502 800 m 1
+ 502 0 l 1
+ 127 400 l 1
+ 502 800 l 1
+878 800 m 1
+ 878 0 l 1
+ 503 400 l 1
+ 878 800 l 1
+EndSplineSet
+EndChar
diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE105.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE105.glyph
new file mode 100644
index 0000000..bb0f87f
--- /dev/null
+++ b/TOOLS/mpv-osd-symbols.sfdir/uniE105.glyph
@@ -0,0 +1,25 @@
+StartChar: uniE105
+Encoding: 57605 57605 18
+Width: 880
+GlyphClass: 2
+Flags: MW
+HStem: 0 800<3 3 3 379 3 755 755 878> 780 20G<3 3 379 379 755 878 878 878>
+VStem: 755 123<0 800>
+LayerCount: 2
+Fore
+SplineSet
+3 0 m 1x60
+ 3 800 l 1
+ 378 400 l 1
+ 3 0 l 1x60
+379 0 m 1xa0
+ 379 800 l 1
+ 754 400 l 1
+ 379 0 l 1xa0
+878 0 m 1
+ 755 0 l 1
+ 755 800 l 1
+ 878 800 l 1
+ 878 0 l 1
+EndSplineSet
+EndChar
diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE106.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE106.glyph
new file mode 100644
index 0000000..b7d6ca2
--- /dev/null
+++ b/TOOLS/mpv-osd-symbols.sfdir/uniE106.glyph
@@ -0,0 +1,22 @@
+StartChar: uniE106
+Encoding: 57606 57606 19
+Width: 880
+GlyphClass: 2
+Flags: MW
+HStem: 0 649<68.5 556.5>
+VStem: 0 880
+LayerCount: 2
+Fore
+SplineSet
+880 380 m 0
+ 880 230 685 101 449 101 c 0
+ 390 101 336 107 283 118 c 1
+ 278 49 189 0 85 0 c 0
+ 52 0 24 14 0 40 c 1
+ 82 48 137 93 137 158 c 0
+ 137 163 137 168 135 172 c 0
+ 58 217 19 273 19 380 c 0
+ 19 528 258 649 449 649 c 0
+ 664 649 880 508 880 380 c 0
+EndSplineSet
+EndChar
diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE107.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE107.glyph
new file mode 100644
index 0000000..01065b6
--- /dev/null
+++ b/TOOLS/mpv-osd-symbols.sfdir/uniE107.glyph
@@ -0,0 +1,56 @@
+StartChar: uniE107
+Encoding: 57607 57607 20
+Width: 880
+GlyphClass: 2
+Flags: MW
+HStem: 0 60<0 60 60 820> 106 50<146 343 146 343 146 390 390 641 680 735> 207 50<99 317 99 317 99 363 363 488 529 781> 580 60<60 820 60 820>
+VStem: 0 60<60 580> 99 218<207 257 207 257> 146 197<106 156 106 156> 363 125<207 257 207 257> 529 112<106 257 106 257> 680 55<106 156 106 156> 820 60<0 60 60 580>
+LayerCount: 2
+Fore
+SplineSet
+0 60 m 2xfde0
+ 0 580 l 2
+ 0 610 30 640 60 640 c 2
+ 820 640 l 2
+ 850 640 880 610 880 580 c 2
+ 880 60 l 2
+ 880 30 850 0 820 0 c 2
+ 60 0 l 2
+ 30 0 0 30 0 60 c 2xfde0
+60 60 m 1
+ 820 60 l 1
+ 820 580 l 1
+ 60 580 l 1
+ 60 60 l 1
+317 257 m 1
+ 317 207 l 1
+ 99 207 l 1
+ 99 257 l 1
+ 317 257 l 1
+488 257 m 1
+ 488 207 l 1
+ 363 207 l 1
+ 363 257 l 1
+ 488 257 l 1
+781 257 m 1
+ 781 207 l 1
+ 529 207 l 1
+ 529 257 l 1
+ 781 257 l 1
+343 156 m 1x4280
+ 343 106 l 1
+ 146 106 l 1
+ 146 156 l 1
+ 343 156 l 1x4280
+641 156 m 1
+ 641 106 l 1
+ 390 106 l 1
+ 390 156 l 1x41c0
+ 641 156 l 1
+735 156 m 1
+ 735 106 l 1
+ 680 106 l 1
+ 680 156 l 1
+ 735 156 l 1
+EndSplineSet
+EndChar
diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE108.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE108.glyph
new file mode 100644
index 0000000..83659e3
--- /dev/null
+++ b/TOOLS/mpv-osd-symbols.sfdir/uniE108.glyph
@@ -0,0 +1,39 @@
+StartChar: uniE108
+Encoding: 57608 57608 21
+Width: 880
+GlyphClass: 2
+Flags: MW
+HStem: 80 60<0 60 60 820> 321 159<274 606 274 606> 660 60<60 820 60 820>
+VStem: 0 60<140 660> 274 332<321 480 321 480> 820 60<80 140 140 660>
+LayerCount: 2
+Fore
+SplineSet
+0 140 m 2
+ 0 660 l 2
+ 0 690 30 720 60 720 c 2
+ 820 720 l 2
+ 850 720 880 690 880 660 c 2
+ 880 140 l 2
+ 880 110 850 80 820 80 c 2
+ 60 80 l 2
+ 30 80 0 110 0 140 c 2
+60 140 m 1
+ 820 140 l 1
+ 820 660 l 1
+ 60 660 l 1
+ 60 140 l 1
+580 600 m 1
+ 760 600 l 1
+ 760 420 l 1
+ 580 600 l 1
+606 480 m 1
+ 606 321 l 1
+ 274 321 l 1
+ 274 480 l 1
+ 606 480 l 1
+300 200 m 1
+ 120 200 l 1
+ 120 380 l 1
+ 300 200 l 1
+EndSplineSet
+EndChar
diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE109.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE109.glyph
new file mode 100644
index 0000000..4038f3d
--- /dev/null
+++ b/TOOLS/mpv-osd-symbols.sfdir/uniE109.glyph
@@ -0,0 +1,39 @@
+StartChar: uniE109
+Encoding: 57609 57609 22
+Width: 880
+GlyphClass: 2
+Flags: MW
+HStem: 80 60<0 60 60 820> 296 209<285 595 285 595> 660 60<60 820 60 820>
+VStem: 0 60<140 660> 120 107<400 400 400 510> 285 310<296 505 296 505> 653 107<400 400> 820 60<80 140 140 660>
+LayerCount: 2
+Fore
+SplineSet
+0 140 m 2
+ 0 660 l 2
+ 0 690 30 720 60 720 c 2
+ 820 720 l 2
+ 850 720 880 690 880 660 c 2
+ 880 140 l 2
+ 880 110 850 80 820 80 c 2
+ 60 80 l 2
+ 30 80 0 110 0 140 c 2
+60 140 m 1
+ 820 140 l 1
+ 820 660 l 1
+ 60 660 l 1
+ 60 140 l 1
+227 400 m 1
+ 120 290 l 1
+ 120 510 l 1
+ 227 400 l 1
+760 290 m 1
+ 653 400 l 1
+ 760 510 l 1
+ 760 290 l 1
+595 505 m 1
+ 595 296 l 1
+ 285 296 l 1
+ 285 505 l 1
+ 595 505 l 1
+EndSplineSet
+EndChar
diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE10A.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE10A.glyph
new file mode 100644
index 0000000..029bf74
--- /dev/null
+++ b/TOOLS/mpv-osd-symbols.sfdir/uniE10A.glyph
@@ -0,0 +1,36 @@
+StartChar: uniE10A
+Encoding: 57610 57610 23
+Width: 1977
+GlyphClass: 2
+Flags: HMWO
+HStem: 242 248<2 155 2 156>
+LayerCount: 2
+Fore
+SplineSet
+398 58 m 1
+ 155 242 l 1
+ 2 242 l 1
+ 2 490 l 1
+ 156 490 l 1
+ 398 674 l 1
+ 398 58 l 1
+809 524 m 0
+ 814 524 819 523 823 519 c 0
+ 831 511 831 499 823 491 c 2
+ 697 365 l 1
+ 823 239 l 2
+ 830 232 830 221 823 213 c 0
+ 816 206 804 206 797 213 c 2
+ 671 339 l 1
+ 545 213 l 2
+ 538 206 525 206 518 213 c 0
+ 510 221 510 233 518 241 c 2
+ 643 367 l 1
+ 517 493 l 2
+ 510 500 510 511 517 519 c 0
+ 525 526 536 526 544 519 c 2
+ 670 393 l 1
+ 796 519 l 2
+ 799 523 804 524 809 524 c 0
+EndSplineSet
+EndChar
diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE10B.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE10B.glyph
new file mode 100644
index 0000000..780b4e4
--- /dev/null
+++ b/TOOLS/mpv-osd-symbols.sfdir/uniE10B.glyph
@@ -0,0 +1,26 @@
+StartChar: uniE10B
+Encoding: 57611 57611 24
+Width: 1977
+GlyphClass: 2
+Flags: MW
+HStem: 242 248<2 155 2 156>
+VStem: 603 47<326.5 405>
+LayerCount: 2
+Fore
+SplineSet
+650 366 m 0
+ 650 281 615 198 545 119 c 1
+ 507 142 l 1
+ 571 213 603 287 603 366 c 0
+ 603 444 571 518 507 590 c 1
+ 545 613 l 1
+ 615 534 650 452 650 366 c 0
+398 58 m 1
+ 155 242 l 1
+ 2 242 l 1
+ 2 490 l 1
+ 156 490 l 1
+ 398 674 l 1
+ 398 58 l 1
+EndSplineSet
+EndChar
diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE10C.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE10C.glyph
new file mode 100644
index 0000000..b17f57c
--- /dev/null
+++ b/TOOLS/mpv-osd-symbols.sfdir/uniE10C.glyph
@@ -0,0 +1,33 @@
+StartChar: uniE10C
+Encoding: 57612 57612 25
+Width: 1977
+GlyphClass: 2
+Flags: MW
+HStem: 242 248<2 155 2 156>
+VStem: 603 47<326.5 405> 717 47<316.5 416>
+LayerCount: 2
+Fore
+SplineSet
+764 366 m 0
+ 764 258 720 156 631 61 c 1
+ 593 84 l 1
+ 675 173 717 267 717 366 c 0
+ 717 466 675 559 593 647 c 1
+ 631 671 l 1
+ 720 578 764 476 764 366 c 0
+650 366 m 0
+ 650 281 615 198 545 119 c 1
+ 507 142 l 1
+ 571 213 603 287 603 366 c 0
+ 603 444 571 518 507 590 c 1
+ 545 613 l 1
+ 615 534 650 452 650 366 c 0
+398 58 m 1
+ 155 242 l 1
+ 2 242 l 1
+ 2 490 l 1
+ 156 490 l 1
+ 398 674 l 1
+ 398 58 l 1
+EndSplineSet
+EndChar
diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE10D.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE10D.glyph
new file mode 100644
index 0000000..24e0acf
--- /dev/null
+++ b/TOOLS/mpv-osd-symbols.sfdir/uniE10D.glyph
@@ -0,0 +1,40 @@
+StartChar: uniE10D
+Encoding: 57613 57613 26
+Width: 1977
+GlyphClass: 2
+Flags: MW
+HStem: 242 248<2 155 2 156> 709 20G<717 717>
+VStem: 603 47<326.5 405> 717 47<316.5 416> 830 47<304 426.5>
+LayerCount: 2
+Fore
+SplineSet
+877 366 m 0
+ 877 236 824 115 717 3 c 1
+ 679 27 l 1
+ 780 130 830 242 830 366 c 0
+ 830 487 780 600 679 705 c 1
+ 717 729 l 1
+ 824 617 877 496 877 366 c 0
+764 366 m 0
+ 764 258 720 156 631 61 c 1
+ 593 84 l 1
+ 675 173 717 267 717 366 c 0
+ 717 466 675 559 593 647 c 1
+ 631 671 l 1
+ 720 578 764 476 764 366 c 0
+650 366 m 0
+ 650 281 615 198 545 119 c 1
+ 507 142 l 1
+ 571 213 603 287 603 366 c 0
+ 603 444 571 518 507 590 c 1
+ 545 613 l 1
+ 615 534 650 452 650 366 c 0
+398 58 m 1
+ 155 242 l 1
+ 2 242 l 1
+ 2 490 l 1
+ 156 490 l 1
+ 398 674 l 1
+ 398 58 l 1
+EndSplineSet
+EndChar
diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE10E.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE10E.glyph
new file mode 100644
index 0000000..17b4af6
--- /dev/null
+++ b/TOOLS/mpv-osd-symbols.sfdir/uniE10E.glyph
@@ -0,0 +1,53 @@
+StartChar: uniE10E
+Encoding: 57614 57614 27
+Width: 1977
+GlyphClass: 2
+Flags: MW
+HStem: 49 95<806 818.5> 242 248<2 155 2 156>
+VStem: 559 47<326.5 405> 673 47<316.5 416> 767 91<90 103.5 648 652>
+LayerCount: 2
+Fore
+SplineSet
+720 366 m 0
+ 720 258 676 156 587 61 c 1
+ 549 84 l 1
+ 631 173 673 267 673 366 c 0
+ 673 466 631 559 549 647 c 1
+ 587 671 l 1
+ 676 578 720 476 720 366 c 0
+606 366 m 0
+ 606 281 571 198 501 119 c 1
+ 463 142 l 1
+ 527 213 559 287 559 366 c 0
+ 559 444 527 518 463 590 c 1
+ 501 613 l 1
+ 571 534 606 452 606 366 c 0
+398 58 m 1
+ 155 242 l 1
+ 2 242 l 1
+ 2 490 l 1
+ 156 490 l 1
+ 398 674 l 1
+ 398 58 l 1
+858 648 m 1
+ 824 238 l 2
+ 823 224 819 217 812 217 c 0
+ 805 217 801 224 800 238 c 2
+ 767 648 l 1
+ 767 652 l 2
+ 767 665 771 675 780 682 c 0
+ 789 690 800 694 812 694 c 0
+ 825 694 835 690 844 682 c 0
+ 853 675 858 665 858 652 c 2
+ 858 648 l 1
+858 97 m 0
+ 858 83 853 72 845 63 c 0
+ 836 53 825 49 812 49 c 0
+ 800 49 789 53 780 63 c 0
+ 771 72 767 83 767 97 c 0
+ 767 110 771 121 780 130 c 0
+ 789 140 800 144 812 144 c 0
+ 825 144 836 140 845 130 c 0
+ 853 121 858 110 858 97 c 0
+EndSplineSet
+EndChar
diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE110.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE110.glyph
new file mode 100644
index 0000000..f4d22f5
--- /dev/null
+++ b/TOOLS/mpv-osd-symbols.sfdir/uniE110.glyph
@@ -0,0 +1,16 @@
+StartChar: uniE110
+Encoding: 57616 57616 28
+Width: 880
+GlyphClass: 2
+Flags: MW
+HStem: 780 20G<656 656>
+VStem: 56 600<400 400>
+LayerCount: 2
+Fore
+SplineSet
+656 0 m 1
+ 56 400 l 1
+ 656 800 l 1
+ 656 0 l 1
+EndSplineSet
+EndChar
diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE111.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE111.glyph
new file mode 100644
index 0000000..370f803
--- /dev/null
+++ b/TOOLS/mpv-osd-symbols.sfdir/uniE111.glyph
@@ -0,0 +1,37 @@
+StartChar: uniE111
+Encoding: 57617 57617 29
+Width: 880
+GlyphClass: 2
+Flags: W
+LayerCount: 2
+Fore
+SplineSet
+0 140 m 2
+ 0 660 l 2
+ 0 690 30 720 60 720 c 2
+ 820 720 l 2
+ 850 720 880 690 880 660 c 2
+ 880 140 l 2
+ 880 110 850 80 820 80 c 2
+ 60 80 l 2
+ 30 80 0 110 0 140 c 2
+60 140 m 1
+ 820 140 l 1
+ 820 660 l 1
+ 60 660 l 1
+ 60 140 l 1
+227 400 m 1
+ 120 290 l 1
+ 120 510 l 1
+ 227 400 l 1
+760 290 m 1
+ 653 400 l 1
+ 760 510 l 1
+ 760 290 l 1
+595 505 m 1
+ 595 296 l 1
+ 285 296 l 1
+ 285 505 l 1
+ 595 505 l 1
+EndSplineSet
+EndChar
diff --git a/TOOLS/osxbundle/mpv.app/Contents/Info.plist b/TOOLS/osxbundle/mpv.app/Contents/Info.plist
index c134abb..11e34c4 100644
--- a/TOOLS/osxbundle/mpv.app/Contents/Info.plist
+++ b/TOOLS/osxbundle/mpv.app/Contents/Info.plist
@@ -173,7 +173,7 @@
</dict>
</array>
<key>CFBundleExecutable</key>
- <string>mpv</string>
+ <string>mpv-wrapper.sh</string>
<key>CFBundleIconFile</key>
<string>icon</string>
<key>CFBundleIdentifier</key>
diff --git a/TOOLS/osxbundle/mpv.app/Contents/MacOS/mpv-wrapper.sh b/TOOLS/osxbundle/mpv.app/Contents/MacOS/mpv-wrapper.sh
new file mode 100755
index 0000000..1a16975
--- /dev/null
+++ b/TOOLS/osxbundle/mpv.app/Contents/MacOS/mpv-wrapper.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+export MPVBUNDLE="true"
+$SHELL -l -c "$(dirname "$0")/mpv --player-operation-mode=pseudo-gui"
diff --git a/TOOLS/osxbundle/mpv.app/Contents/Resources/mpv.conf b/TOOLS/osxbundle/mpv.app/Contents/Resources/mpv.conf
deleted file mode 100644
index bdffa7a..0000000
--- a/TOOLS/osxbundle/mpv.app/Contents/Resources/mpv.conf
+++ /dev/null
@@ -1 +0,0 @@
-player-operation-mode=pseudo-gui
diff --git a/TOOLS/zsh.pl b/TOOLS/zsh.pl
index f4491e3..96ef17a 100755
--- a/TOOLS/zsh.pl
+++ b/TOOLS/zsh.pl
@@ -96,6 +96,9 @@ my $tmpl = <<"EOS";
local curcontext="\$curcontext" state state_descr line
typeset -A opt_args
+local -a match mbegin mend
+local MATCH MBEGIN MEND
+
# By default, don't complete URLs unless no files match
local -a tag_order
zstyle -a ":completion:*:*:\$service:*" tag-order tag_order || \
diff --git a/VERSION b/VERSION
index 2094a10..d21d277 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.24.0
+0.25.0
diff --git a/audio/decode/dec_audio.c b/audio/decode/dec_audio.c
index 5a2735e..fce9ef9 100644
--- a/audio/decode/dec_audio.c
+++ b/audio/decode/dec_audio.c
@@ -260,8 +260,7 @@ void audio_work(struct dec_audio *da)
audio_reset_decoding(da);
} else {
da->codec = new_segment->codec;
- if (da->ad_driver)
- da->ad_driver->uninit(da);
+ da->ad_driver->uninit(da);
da->ad_driver = NULL;
audio_init_best_codec(da);
}
diff --git a/audio/filter/af.c b/audio/filter/af.c
index e6f19b3..189c12b 100644
--- a/audio/filter/af.c
+++ b/audio/filter/af.c
@@ -36,12 +36,12 @@ extern const struct af_info af_info_format;
extern const struct af_info af_info_volume;
extern const struct af_info af_info_equalizer;
extern const struct af_info af_info_pan;
-extern const struct af_info af_info_drc;
extern const struct af_info af_info_lavcac3enc;
extern const struct af_info af_info_lavrresample;
extern const struct af_info af_info_scaletempo;
extern const struct af_info af_info_bs2b;
extern const struct af_info af_info_lavfi;
+extern const struct af_info af_info_lavfi_bridge;
extern const struct af_info af_info_rubberband;
static const struct af_info *const filter_list[] = {
@@ -50,7 +50,6 @@ static const struct af_info *const filter_list[] = {
&af_info_volume,
&af_info_equalizer,
&af_info_pan,
- &af_info_drc,
&af_info_lavcac3enc,
&af_info_lavrresample,
#if HAVE_RUBBERBAND
@@ -58,6 +57,7 @@ static const struct af_info *const filter_list[] = {
#endif
&af_info_scaletempo,
&af_info_lavfi,
+ &af_info_lavfi_bridge,
NULL
};
@@ -80,6 +80,8 @@ static bool get_desc(struct m_obj_desc *dst, int index)
const struct m_obj_list af_obj_list = {
.get_desc = get_desc,
.description = "audio filters",
+ .allow_disable_entries = true,
+ .allow_unknown_entries = true,
.aliases = {
{"force", "format"},
{0}
@@ -150,10 +152,17 @@ contain the commandline parameters for the filter */
static struct af_instance *af_create(struct af_stream *s, char *name,
char **args)
{
+ const char *lavfi_name = NULL;
+ char **lavfi_args = NULL;
struct m_obj_desc desc;
if (!m_obj_list_find(&desc, &af_obj_list, bstr0(name))) {
- MP_ERR(s, "Couldn't find audio filter '%s'.\n", name);
- return NULL;
+ if (!m_obj_list_find(&desc, &af_obj_list, bstr0("lavfi-bridge"))) {
+ MP_ERR(s, "Couldn't find audio filter '%s'.\n", name);
+ return NULL;
+ }
+ lavfi_name = name;
+ lavfi_args = args;
+ args = NULL;
}
MP_VERBOSE(s, "Adding filter %s \n", name);
@@ -171,6 +180,19 @@ static struct af_instance *af_create(struct af_stream *s, char *name,
name, s->opts->af_defs, args);
if (!config)
goto error;
+ if (lavfi_name) {
+ // Pass the filter arguments as proper sub-options to the bridge filter.
+ struct m_config_option *name_opt = m_config_get_co(config, bstr0("name"));
+ assert(name_opt);
+ assert(name_opt->opt->type == &m_option_type_string);
+ if (m_config_set_option_raw(config, name_opt, &lavfi_name, 0) < 0)
+ goto error;
+ struct m_config_option *opts = m_config_get_co(config, bstr0("opts"));
+ assert(opts);
+ assert(opts->opt->type == &m_option_type_keyvalue_list);
+ if (m_config_set_option_raw(config, opts, &lavfi_args, 0) < 0)
+ goto error;
+ }
af->priv = config->optstruct;
// Initialize the new filter
@@ -547,6 +569,8 @@ int af_init(struct af_stream *s)
// Add all filters in the list (if there are any)
struct m_obj_settings *list = s->opts->af_settings;
for (int i = 0; list && list[i].name; i++) {
+ if (!list[i].enabled)
+ continue;
struct af_instance *af =
af_prepend(s, s->last, list[i].name, list[i].attribs);
if (!af) {
diff --git a/audio/filter/af_channels.c b/audio/filter/af_channels.c
index 57fe487..7cd7810 100644
--- a/audio/filter/af_channels.c
+++ b/audio/filter/af_channels.c
@@ -214,6 +214,8 @@ static int af_open(struct af_instance* af){
af->filter_frame = filter_frame;
af_channels_t *s = af->priv;
+ MP_WARN(af, "This filter is deprecated (no replacement).\n");
+
// If router scan commandline for routing pairs
if(s->routes && s->routes[0]){
char* cp = s->routes;
diff --git a/audio/filter/af_drc.c b/audio/filter/af_drc.c
deleted file mode 100644
index 7b375fe..0000000
--- a/audio/filter/af_drc.c
+++ /dev/null
@@ -1,334 +0,0 @@
-/*
- * Copyright (C) 2004 Alex Beregszaszi & Pierre Lombard
- *
- * 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 <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <inttypes.h>
-#include <math.h>
-#include <limits.h>
-
-#include "common/common.h"
-#include "af.h"
-
-// Methods:
-// 1: uses a 1 value memory and coefficients new=a*old+b*cur (with a+b=1)
-// 2: uses several samples to smooth the variations (standard weighted mean
-// on past samples)
-
-// Size of the memory array
-// FIXME: should depend on the frequency of the data (should be a few seconds)
-#define NSAMPLES 128
-
-// If summing all the mem[].len is lower than MIN_SAMPLE_SIZE bytes, then we
-// choose to ignore the computed value as it's not significant enough
-// FIXME: should depend on the frequency of the data (0.5s maybe)
-#define MIN_SAMPLE_SIZE 32000
-
-// mul is the value by which the samples are scaled
-// and has to be in [MUL_MIN, MUL_MAX]
-#define MUL_INIT 1.0
-#define MUL_MIN 0.1
-#define MUL_MAX 5.0
-
-// Silence level
-// FIXME: should be relative to the level of the samples
-#define SIL_S16 (SHRT_MAX * 0.01)
-#define SIL_FLOAT 0.01
-
-// smooth must be in ]0.0, 1.0[
-#define SMOOTH_MUL 0.06
-#define SMOOTH_LASTAVG 0.06
-
-#define DEFAULT_TARGET 0.25
-
-// Data for specific instances of this filter
-typedef struct af_volume_s
-{
- int method; // method used
- float mul;
- // method 1
- float lastavg; // history value of the filter
- // method 2
- int idx;
- struct {
- float avg; // average level of the sample
- int len; // sample size (weight)
- } mem[NSAMPLES];
- // "Ideal" level
- float mid_s16;
- float mid_float;
-}af_drc_t;
-
-// Initialization and runtime control
-static int control(struct af_instance* af, int cmd, void* arg)
-{
- switch(cmd){
- case AF_CONTROL_REINIT:
- // Sanity check
- if(!arg) return AF_ERROR;
-
- mp_audio_force_interleaved_format((struct mp_audio*)arg);
- mp_audio_copy_config(af->data, (struct mp_audio*)arg);
-
- if(((struct mp_audio*)arg)->format != (AF_FORMAT_S16)){
- mp_audio_set_format(af->data, AF_FORMAT_FLOAT);
- }
- return af_test_output(af,(struct mp_audio*)arg);
- }
- return AF_UNKNOWN;
-}
-
-static void method1_int16(af_drc_t *s, struct mp_audio *c)
-{
- register int i = 0;
- int16_t *data = (int16_t*)c->planes[0]; // Audio data
- int len = c->samples*c->nch; // Number of samples
- float curavg = 0.0, newavg, neededmul;
- int tmp;
-
- for (i = 0; i < len; i++)
- {
- tmp = data[i];
- curavg += tmp * tmp;
- }
- curavg = sqrt(curavg / (float) len);
-
- // Evaluate an adequate 'mul' coefficient based on previous state, current
- // samples level, etc
-
- if (curavg > SIL_S16)
- {
- neededmul = s->mid_s16 / (curavg * s->mul);
- s->mul = (1.0 - SMOOTH_MUL) * s->mul + SMOOTH_MUL * neededmul;
-
- // clamp the mul coefficient
- s->mul = MPCLAMP(s->mul, MUL_MIN, MUL_MAX);
- }
-
- // Scale & clamp the samples
- for (i = 0; i < len; i++)
- {
- tmp = s->mul * data[i];
- tmp = MPCLAMP(tmp, SHRT_MIN, SHRT_MAX);
- data[i] = tmp;
- }
-
- // Evaluation of newavg (not 100% accurate because of values clamping)
- newavg = s->mul * curavg;
-
- // Stores computed values for future smoothing
- s->lastavg = (1.0 - SMOOTH_LASTAVG) * s->lastavg + SMOOTH_LASTAVG * newavg;
-}
-
-static void method1_float(af_drc_t *s, struct mp_audio *c)
-{
- register int i = 0;
- float *data = (float*)c->planes[0]; // Audio data
- int len = c->samples*c->nch; // Number of samples
- float curavg = 0.0, newavg, neededmul, tmp;
-
- for (i = 0; i < len; i++)
- {
- tmp = data[i];
- curavg += tmp * tmp;
- }
- curavg = sqrt(curavg / (float) len);
-
- // Evaluate an adequate 'mul' coefficient based on previous state, current
- // samples level, etc
-
- if (curavg > SIL_FLOAT) // FIXME
- {
- neededmul = s->mid_float / (curavg * s->mul);
- s->mul = (1.0 - SMOOTH_MUL) * s->mul + SMOOTH_MUL * neededmul;
-
- // clamp the mul coefficient
- s->mul = MPCLAMP(s->mul, MUL_MIN, MUL_MAX);
- }
-
- // Scale & clamp the samples
- for (i = 0; i < len; i++)
- data[i] *= s->mul;
-
- // Evaluation of newavg (not 100% accurate because of values clamping)
- newavg = s->mul * curavg;
-
- // Stores computed values for future smoothing
- s->lastavg = (1.0 - SMOOTH_LASTAVG) * s->lastavg + SMOOTH_LASTAVG * newavg;
-}
-
-static void method2_int16(af_drc_t *s, struct mp_audio *c)
-{
- register int i = 0;
- int16_t *data = (int16_t*)c->planes[0]; // Audio data
- int len = c->samples*c->nch; // Number of samples
- float curavg = 0.0, newavg, avg = 0.0;
- int tmp, totallen = 0;
-
- for (i = 0; i < len; i++)
- {
- tmp = data[i];
- curavg += tmp * tmp;
- }
- curavg = sqrt(curavg / (float) len);
-
- // Evaluate an adequate 'mul' coefficient based on previous state, current
- // samples level, etc
- for (i = 0; i < NSAMPLES; i++)
- {
- avg += s->mem[i].avg * (float)s->mem[i].len;
- totallen += s->mem[i].len;
- }
-
- if (totallen > MIN_SAMPLE_SIZE)
- {
- avg /= (float)totallen;
- if (avg >= SIL_S16)
- {
- s->mul = s->mid_s16 / avg;
- s->mul = MPCLAMP(s->mul, MUL_MIN, MUL_MAX);
- }
- }
-
- // Scale & clamp the samples
- for (i = 0; i < len; i++)
- {
- tmp = s->mul * data[i];
- tmp = MPCLAMP(tmp, SHRT_MIN, SHRT_MAX);
- data[i] = tmp;
- }
-
- // Evaluation of newavg (not 100% accurate because of values clamping)
- newavg = s->mul * curavg;
-
- // Stores computed values for future smoothing
- s->mem[s->idx].len = len;
- s->mem[s->idx].avg = newavg;
- s->idx = (s->idx + 1) % NSAMPLES;
-}
-
-static void method2_float(af_drc_t *s, struct mp_audio *c)
-{
- register int i = 0;
- float *data = (float*)c->planes[0]; // Audio data
- int len = c->samples*c->nch; // Number of samples
- float curavg = 0.0, newavg, avg = 0.0, tmp;
- int totallen = 0;
-
- for (i = 0; i < len; i++)
- {
- tmp = data[i];
- curavg += tmp * tmp;
- }
- curavg = sqrt(curavg / (float) len);
-
- // Evaluate an adequate 'mul' coefficient based on previous state, current
- // samples level, etc
- for (i = 0; i < NSAMPLES; i++)
- {
- avg += s->mem[i].avg * (float)s->mem[i].len;
- totallen += s->mem[i].len;
- }
-
- if (totallen > MIN_SAMPLE_SIZE)
- {
- avg /= (float)totallen;
- if (avg >= SIL_FLOAT)
- {
- s->mul = s->mid_float / avg;
- s->mul = MPCLAMP(s->mul, MUL_MIN, MUL_MAX);
- }
- }
-
- // Scale & clamp the samples
- for (i = 0; i < len; i++)
- data[i] *= s->mul;
-
- // Evaluation of newavg (not 100% accurate because of values clamping)
- newavg = s->mul * curavg;
-
- // Stores computed values for future smoothing
- s->mem[s->idx].len = len;
- s->mem[s->idx].avg = newavg;
- s->idx = (s->idx + 1) % NSAMPLES;
-}
-
-static int filter(struct af_instance *af, struct mp_audio *data)
-{
- af_drc_t *s = af->priv;
-
- if (!data)
- return 0;
-
- if (af_make_writeable(af, data) < 0) {
- talloc_free(data);
- return -1;
- }
-
- if(af->data->format == (AF_FORMAT_S16))
- {
- if (s->method == 2)
- method2_int16(s, data);
- else
- method1_int16(s, data);
- }
- else if(af->data->format == (AF_FORMAT_FLOAT))
- {
- if (s->method == 2)
- method2_float(s, data);
- else
- method1_float(s, data);
- }
- af_add_output_frame(af, data);
- return 0;
-}
-
-// Allocate memory and set function pointers
-static int af_open(struct af_instance* af){
- int i = 0;
- af->control=control;
- af->filter_frame = filter;
- af_drc_t *priv = af->priv;
-
- priv->mul = MUL_INIT;
- priv->lastavg = ((float)SHRT_MAX) * DEFAULT_TARGET;
- priv->idx = 0;
- for (i = 0; i < NSAMPLES; i++)
- {
- priv->mem[i].len = 0;
- priv->mem[i].avg = 0;
- }
- priv->mid_s16 = ((float)SHRT_MAX) * priv->mid_float;
- return AF_OK;
-}
-
-#define OPT_BASE_STRUCT af_drc_t
-const struct af_info af_info_drc = {
- .info = "Dynamic range compression filter",
- .name = "drc",
- .open = af_open,
- .priv_size = sizeof(af_drc_t),
- .options = (const struct m_option[]) {
- OPT_INTRANGE("method", method, 0, 1, 2, OPTDEF_INT(1)),
- OPT_FLOAT("target", mid_float, 0, OPTDEF_FLOAT(DEFAULT_TARGET)),
- {0}
- },
-};
diff --git a/audio/filter/af_equalizer.c b/audio/filter/af_equalizer.c
index cb4ecb2..3f132fd 100644
--- a/audio/filter/af_equalizer.c
+++ b/audio/filter/af_equalizer.c
@@ -188,6 +188,7 @@ static int filter(struct af_instance* af, struct mp_audio* data)
// Allocate memory and set function pointers
static int af_open(struct af_instance* af){
+ MP_WARN(af, "This filter is deprecated. Use 'anequalizer' or 'firequalizer' instead.\n");
af->control=control;
af->filter_frame = filter;
af_equalizer_t *priv = af->priv;
diff --git a/audio/filter/af_lavfi.c b/audio/filter/af_lavfi.c
index 55fb7cb..47edf20 100644
--- a/audio/filter/af_lavfi.c
+++ b/audio/filter/af_lavfi.c
@@ -30,7 +30,6 @@
#include <libavutil/time.h>
#include <libavutil/opt.h>
#include <libavfilter/avfilter.h>
-#include <libavfilter/avfiltergraph.h>
#include <libavfilter/buffersink.h>
#include <libavfilter/buffersrc.h>
@@ -57,6 +56,9 @@
#endif
struct priv {
+ // Single filter bridge, instead of a graph.
+ bool is_bridge;
+
AVFilterGraph *graph;
AVFilterContext *in;
AVFilterContext *out;
@@ -72,6 +74,8 @@ struct priv {
// options
char *cfg_graph;
char **cfg_avopts;
+ char *cfg_filter_name;
+ char **cfg_filter_opts;
};
static void destroy_graph(struct af_instance *af)
@@ -87,15 +91,14 @@ static bool recreate_graph(struct af_instance *af, struct mp_audio *config)
{
void *tmp = talloc_new(NULL);
struct priv *p = af->priv;
- AVFilterContext *in = NULL, *out = NULL, *f_format = NULL;
+ AVFilterContext *in = NULL, *out = NULL;
- if (bstr0(p->cfg_graph).len == 0) {
+ if (!p->is_bridge && bstr0(p->cfg_graph).len == 0) {
MP_FATAL(af, "lavfi: no filter graph set\n");
return false;
}
destroy_graph(af);
- MP_VERBOSE(af, "lavfi: create graph: '%s'\n", p->cfg_graph);
AVFilterGraph *graph = avfilter_graph_alloc();
if (!graph)
@@ -109,24 +112,6 @@ static bool recreate_graph(struct af_instance *af, struct mp_audio *config)
if (!outputs || !inputs)
goto error;
- // Build list of acceptable output sample formats. libavfilter will insert
- // conversion filters if needed.
- static const enum AVSampleFormat sample_fmts[] = {
- AV_SAMPLE_FMT_U8, AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_S32,
- AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_DBL,
- AV_SAMPLE_FMT_U8P, AV_SAMPLE_FMT_S16P, AV_SAMPLE_FMT_S32P,
- AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_DBLP,
- AV_SAMPLE_FMT_NONE
- };
- char *fmtstr = talloc_strdup(tmp, "");
- for (int n = 0; sample_fmts[n] != AV_SAMPLE_FMT_NONE; n++) {
- const char *name = av_get_sample_fmt_name(sample_fmts[n]);
- if (name) {
- const char *s = fmtstr[0] ? "|" : "";
- fmtstr = talloc_asprintf_append_buffer(fmtstr, "%s%s", s, name);
- }
- }
-
char *src_args = talloc_asprintf(tmp,
"sample_rate=%d:sample_fmt=%s:time_base=%d/%d:"
"channel_layout=0x%"PRIx64, config->rate,
@@ -141,21 +126,46 @@ static bool recreate_graph(struct af_instance *af, struct mp_audio *config)
"out", NULL, NULL, graph) < 0)
goto error;
- if (avfilter_graph_create_filter(&f_format, avfilter_get_by_name("aformat"),
- "format", fmtstr, NULL, graph) < 0)
- goto error;
+ if (p->is_bridge) {
+ AVFilterContext *filter = avfilter_graph_alloc_filter(graph,
+ avfilter_get_by_name(p->cfg_filter_name), "filter");
+ if (!filter)
+ goto error;
- if (avfilter_link(f_format, 0, out, 0) < 0)
- goto error;
+ if (mp_set_avopts(af->log, filter->priv, p->cfg_filter_opts) < 0)
+ goto error;
+
+ if (avfilter_init_str(filter, NULL) < 0)
+ goto error;
- outputs->name = av_strdup("in");
- outputs->filter_ctx = in;
+ // Yep, we have to manually link those filters.
+ if (filter->nb_inputs != 1 ||
+ avfilter_pad_get_type(filter->input_pads, 0) != AVMEDIA_TYPE_AUDIO ||
+ filter->nb_outputs != 1 ||
+ avfilter_pad_get_type(filter->output_pads, 0) != AVMEDIA_TYPE_AUDIO)
+ {
+ MP_ERR(af, "The filter is required to have 1 audio input pad and "
+ "1 audio output pad.\n");
+ goto error;
+ }
+ if (avfilter_link(in, 0, filter, 0) < 0 ||
+ avfilter_link(filter, 0, out, 0) < 0)
+ {
+ MP_ERR(af, "Failed to link filter.\n");
+ goto error;
+ }
+ } else {
+ MP_VERBOSE(af, "lavfi: create graph: '%s'\n", p->cfg_graph);
- inputs->name = av_strdup("out");
- inputs->filter_ctx = f_format;
+ outputs->name = av_strdup("in");
+ outputs->filter_ctx = in;
- if (graph_parse(graph, p->cfg_graph, inputs, outputs, NULL) < 0)
- goto error;
+ inputs->name = av_strdup("out");
+ inputs->filter_ctx = out;
+
+ if (graph_parse(graph, p->cfg_graph, inputs, outputs, NULL) < 0)
+ goto error;
+ }
if (avfilter_graph_config(graph, NULL) < 0)
goto error;
@@ -347,10 +357,23 @@ static void uninit(struct af_instance *af)
static int af_open(struct af_instance *af)
{
+ struct priv *p = af->priv;
+
af->control = control;
af->uninit = uninit;
af->filter_frame = filter_frame;
af->filter_out = filter_out;
+
+ if (p->is_bridge) {
+ if (!p->cfg_filter_name) {
+ MP_ERR(af, "Filter name not set!\n");
+ return 0;
+ }
+ if (!avfilter_get_by_name(p->cfg_filter_name)) {
+ MP_ERR(af, "libavfilter filter '%s' not found!\n", p->cfg_filter_name);
+ return 0;
+ }
+ }
return AF_OK;
}
@@ -367,3 +390,20 @@ const struct af_info af_info_lavfi = {
{0}
},
};
+
+const struct af_info af_info_lavfi_bridge = {
+ .info = "libavfilter bridge (explicit options)",
+ .name = "lavfi-bridge",
+ .open = af_open,
+ .priv_size = sizeof(struct priv),
+ .priv_defaults = &(const struct priv){
+ .is_bridge = true,
+ },
+ .options = (const struct m_option[]) {
+ OPT_STRING("name", cfg_filter_name, M_OPT_MIN, .min = 1),
+ OPT_KEYVALUELIST("opts", cfg_filter_opts, 0),
+ OPT_STRING("graph", cfg_graph, 0),
+ OPT_KEYVALUELIST("o", cfg_avopts, 0),
+ {0}
+ },
+};
diff --git a/audio/filter/af_pan.c b/audio/filter/af_pan.c
index de2adf7..b2233a7 100644
--- a/audio/filter/af_pan.c
+++ b/audio/filter/af_pan.c
@@ -180,6 +180,7 @@ static int af_open(struct af_instance *af)
{
af->control = control;
af->filter_frame = filter_frame;
+ MP_WARN(af, "This filter is deprecated. Use lavfi pan instead.\n");
af_pan_t *s = af->priv;
int nch = s->nch;
if (nch && AF_OK != control(af, AF_CONTROL_SET_PAN_NOUT, &nch))
diff --git a/audio/filter/af_volume.c b/audio/filter/af_volume.c
index e1d5d45..fb7a87f 100644
--- a/audio/filter/af_volume.c
+++ b/audio/filter/af_volume.c
@@ -42,6 +42,7 @@ struct priv {
int fast; // Use fix-point volume control
int detach; // Detach if gain volume is neutral
float cfg_volume;
+ int warn;
};
// Convert to gain value from dB. input <= -200dB will become 0 gain.
@@ -160,6 +161,8 @@ static int filter(struct af_instance *af, struct mp_audio *data)
static int af_open(struct af_instance *af)
{
struct priv *s = af->priv;
+ if (s->warn)
+ MP_WARN(af, "This filter is deprecated. Use --volume directly.\n");
af->control = control;
af->filter_frame = filter;
s->level = 1.0;
@@ -184,6 +187,7 @@ const struct af_info af_info_volume = {
OPT_FLAG("softclip", soft, 0),
OPT_FLAG("s16", fast, 0),
OPT_FLAG("detach", detach, 0),
+ OPT_FLAG("warn", warn, 0, OPTDEF_INT(1)),
{0}
},
};
diff --git a/audio/out/ao.c b/audio/out/ao.c
index 6cf8de2..1c51a07 100644
--- a/audio/out/ao.c
+++ b/audio/out/ao.c
@@ -245,9 +245,10 @@ static void split_ao_device(void *tmp, char *opt, char **out_ao, char **out_dev)
return;
if (!opt[0] || strcmp(opt, "auto") == 0)
return;
- // Split on "/". If there's no "/", leave out_device NULL.
+ // Split on "/". If "/" is the final character, or absent, out_dev is NULL.
bstr b_dev, b_ao;
- if (bstr_split_tok(bstr0(opt), "/", &b_ao, &b_dev))
+ bstr_split_tok(bstr0(opt), "/", &b_ao, &b_dev);
+ if (b_dev.len > 0)
*out_dev = bstrto0(tmp, b_dev);
*out_ao = bstrto0(tmp, b_ao);
}
@@ -454,7 +455,7 @@ bool ao_chmap_sel_adjust2(struct ao *ao, const struct mp_chmap_sel *s,
if (!mp_chmap_equals(&res, &(struct mp_chmap)MP_CHMAP_INIT_MONO) &&
!mp_chmap_equals(&res, &(struct mp_chmap)MP_CHMAP_INIT_STEREO))
{
- MP_WARN(ao, "Disabling multichannel output.\n");
+ MP_VERBOSE(ao, "Disabling multichannel output.\n");
*map = (struct mp_chmap)MP_CHMAP_INIT_STEREO;
}
}
@@ -605,8 +606,8 @@ void ao_device_list_add(struct ao_device_list *list, struct ao *ao,
c.desc = "Default";
}
}
- c.name = c.name[0] ? talloc_asprintf(list, "%s/%s", dname, c.name)
- : talloc_strdup(list, dname);
+ c.name = (c.name && c.name[0]) ? talloc_asprintf(list, "%s/%s", dname, c.name)
+ : talloc_strdup(list, dname);
c.desc = talloc_strdup(list, c.desc);
MP_TARRAY_APPEND(list, list->devices, list->num_devices, c);
}
diff --git a/audio/out/ao_alsa.c b/audio/out/ao_alsa.c
index ac29905..f7f9a1a 100644
--- a/audio/out/ao_alsa.c
+++ b/audio/out/ao_alsa.c
@@ -1121,11 +1121,17 @@ static int audio_wait(struct ao *ao, pthread_mutex_t *lock)
return r;
unsigned short revents;
- snd_pcm_poll_descriptors_revents(p->alsa, fds, num_fds, &revents);
+ err = snd_pcm_poll_descriptors_revents(p->alsa, fds, num_fds, &revents);
CHECK_ALSA_ERROR("cannot read poll events");
- if (revents & POLLERR)
+ if (revents & POLLERR) {
+ snd_pcm_status_t *status;
+ snd_pcm_status_alloca(&status);
+
+ err = snd_pcm_status(p->alsa, status);
+ check_device_present(ao, err);
return -1;
+ }
if (revents & POLLOUT)
return 0;
}
@@ -1137,16 +1143,16 @@ alsa_error:
static bool is_useless_device(char *name)
{
- char *crap[] = {"front", "rear", "center_lfe", "side", "surround21",
- "surround40", "surround41", "surround50", "surround51", "surround71",
- "sysdefault", "pulse", "null", "dsnoop", "dmix", "hw", "iec958",
- "default"};
+ char *crap[] = {"rear", "center_lfe", "side", "pulse", "null", "dsnoop", "hw"};
for (int i = 0; i < MP_ARRAY_SIZE(crap); i++) {
int l = strlen(crap[i]);
if (name && strncmp(name, crap[i], l) == 0 &&
(!name[l] || name[l] == ':'))
return true;
}
+ // The standard default entry will achieve exactly the same.
+ if (name && strcmp(name, "default") == 0)
+ return true;
return false;
}
diff --git a/audio/out/ao_jack.c b/audio/out/ao_jack.c
index 2ad3cad..597835c 100644
--- a/audio/out/ao_jack.c
+++ b/audio/out/ao_jack.c
@@ -33,6 +33,7 @@
#include "ao.h"
#include "internal.h"
#include "audio/format.h"
+#include "osdep/atomic.h"
#include "osdep/timer.h"
#include "options/m_config.h"
#include "options/m_option.h"
@@ -67,7 +68,10 @@ static const struct m_sub_options ao_jack_conf = {
struct priv {
jack_client_t *client;
- float jack_latency;
+
+ atomic_uint graph_latency_max;
+ atomic_uint buffer_size;
+
int last_chunk;
int num_ports;
@@ -78,6 +82,29 @@ struct priv {
struct jack_opts *opts;
};
+static int graph_order_cb(void *arg)
+{
+ struct ao *ao = arg;
+ struct priv *p = ao->priv;
+
+ jack_latency_range_t jack_latency_range;
+ jack_port_get_latency_range(p->ports[0], JackPlaybackLatency,
+ &jack_latency_range);
+ atomic_store(&p->graph_latency_max, jack_latency_range.max);
+
+ return 0;
+}
+
+static int buffer_size_cb(jack_nframes_t nframes, void *arg)
+{
+ struct ao *ao = arg;
+ struct priv *p = ao->priv;
+
+ atomic_store(&p->buffer_size, nframes);
+
+ return 0;
+}
+
static int process(jack_nframes_t nframes, void *arg)
{
struct ao *ao = arg;
@@ -88,8 +115,11 @@ static int process(jack_nframes_t nframes, void *arg)
for (int i = 0; i < p->num_ports; i++)
buffers[i] = jack_port_get_buffer(p->ports[i], nframes);
+ jack_nframes_t jack_latency =
+ atomic_load(&p->graph_latency_max) + atomic_load(&p->buffer_size);
+
int64_t end_time = mp_time_us();
- end_time += (p->jack_latency + nframes / (double)ao->samplerate) * 1000000.0;
+ end_time += (jack_latency + nframes) / (double)ao->samplerate * 1000000.0;
ao_read_data(ao, buffers, nframes, end_time);
@@ -212,11 +242,8 @@ static int init(struct ao *ao)
ao->samplerate = jack_get_sample_rate(p->client);
- jack_latency_range_t jack_latency_range;
- jack_port_get_latency_range(p->ports[0], JackPlaybackLatency,
- &jack_latency_range);
- p->jack_latency = (float)(jack_latency_range.max + jack_get_buffer_size(p->client))
- / (float)ao->samplerate;
+ jack_set_buffer_size_callback(p->client, buffer_size_cb, ao);
+ jack_set_graph_order_callback(p->client, graph_order_cb, ao);
if (!ao_chmap_sel_get_def(ao, &sel, &ao->channels, p->num_ports))
goto err_chmap_sel_get_def;
diff --git a/audio/out/ao_pcm.c b/audio/out/ao_pcm.c
index 4e5ec0a..0ef99bc 100644
--- a/audio/out/ao_pcm.c
+++ b/audio/out/ao_pcm.c
@@ -219,7 +219,7 @@ const struct ao_driver audio_out_pcm = {
.priv_size = sizeof(struct priv),
.priv_defaults = &(const struct priv) { .waveheader = 1 },
.options = (const struct m_option[]) {
- OPT_STRING("file", outputfilename, 0),
+ OPT_STRING("file", outputfilename, M_OPT_FILE),
OPT_FLAG("waveheader", waveheader, 0),
OPT_FLAG("append", append, 0),
{0}
diff --git a/audio/out/ao_wasapi_changenotify.c b/audio/out/ao_wasapi_changenotify.c
index b9806e0..f0e1895 100644
--- a/audio/out/ao_wasapi_changenotify.c
+++ b/audio/out/ao_wasapi_changenotify.c
@@ -67,7 +67,7 @@ static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_OnDeviceStateChanged(
case DEVICE_STATE_DISABLED:
case DEVICE_STATE_NOTPRESENT:
case DEVICE_STATE_UNPLUGGED:
- MP_VERBOSE(ao, "OnDeviceStateChanged triggered on device %S: "
+ MP_VERBOSE(ao, "OnDeviceStateChanged triggered on device %ls: "
"requesting ao reload\n", pwstrDeviceId);
ao_request_reload(ao);
break;
@@ -106,7 +106,7 @@ static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_OnDeviceRemoved(
MP_VERBOSE(ao, "OnDeviceRemoved triggered: sending hotplug event\n");
ao_hotplug_event(ao);
} else if (pwstrDeviceId && !wcscmp(pwstrDeviceId, change->monitored)) {
- MP_VERBOSE(ao, "OnDeviceRemoved triggered for device %S: "
+ MP_VERBOSE(ao, "OnDeviceRemoved triggered for device %ls: "
"requesting ao reload\n", pwstrDeviceId);
ao_request_reload(ao);
}
@@ -166,7 +166,7 @@ static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_OnPropertyValueChanged(
if (!change->is_hotplug && pwstrDeviceId &&
!wcscmp(pwstrDeviceId, change->monitored))
{
- MP_VERBOSE(ao, "OnPropertyValueChanged triggered on device %S\n",
+ MP_VERBOSE(ao, "OnPropertyValueChanged triggered on device %ls\n",
pwstrDeviceId);
if (IsEqualPropertyKey(PKEY_AudioEngine_DeviceFormat, key)) {
MP_VERBOSE(change->ao,
@@ -202,14 +202,6 @@ HRESULT wasapi_change_init(struct ao *ao, bool is_hotplug)
(void **)&change->pEnumerator);
EXIT_ON_ERROR(hr);
- // COM voodoo to emulate c++ class
- change->client.lpVtbl = &sIMMNotificationClientVtbl;
-
- // register the change notification client
- hr = IMMDeviceEnumerator_RegisterEndpointNotificationCallback(
- change->pEnumerator, (IMMNotificationClient *)change);
- EXIT_ON_ERROR(hr);
-
// so the callbacks can access the ao
change->ao = ao;
@@ -221,9 +213,17 @@ HRESULT wasapi_change_init(struct ao *ao, bool is_hotplug)
} else {
// Get the device string to compare with the pwstrDeviceId
change->monitored = state->deviceID;
- MP_VERBOSE(ao, "Monitoring changes in device %S\n", change->monitored);
+ MP_VERBOSE(ao, "Monitoring changes in device %ls\n", change->monitored);
}
+ // COM voodoo to emulate c++ class
+ change->client.lpVtbl = &sIMMNotificationClientVtbl;
+
+ // register the change notification client
+ hr = IMMDeviceEnumerator_RegisterEndpointNotificationCallback(
+ change->pEnumerator, (IMMNotificationClient *)change);
+ EXIT_ON_ERROR(hr);
+
return hr;
exit_label:
MP_ERR(state, "Error setting up device change monitoring: %s\n",
diff --git a/audio/out/ao_wasapi_utils.c b/audio/out/ao_wasapi_utils.c
index a449dbe..f1895eb 100644
--- a/audio/out/ao_wasapi_utils.c
+++ b/audio/out/ao_wasapi_utils.c
@@ -88,10 +88,12 @@ static const GUID *format_to_subtype(int format)
return wasapi_fmt_table[i].subtype;
}
return &KSDATAFORMAT_SPECIFIER_NONE;
- } else if (af_fmt_is_float(format)) {
+ } else if (format == AF_FORMAT_FLOAT) {
return &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
+ } else if (af_fmt_is_int(format)) {
+ return &KSDATAFORMAT_SUBTYPE_PCM;
}
- return &KSDATAFORMAT_SUBTYPE_PCM;
+ return NULL;
}
// "solve" the under-determined inverse of format_to_subtype by assuming the
@@ -124,13 +126,27 @@ static void set_waveformat(WAVEFORMATEXTENSIBLE *wformat,
int format, WORD valid_bits,
DWORD samplerate, struct mp_chmap *channels)
{
+ // First find a format that is actually representable.
+ // (Notably excludes AF_FORMAT_DOUBLE.)
+ const GUID *sub_format = &KSDATAFORMAT_SPECIFIER_NONE;
+ int alt_formats[AF_FORMAT_COUNT];
+ af_get_best_sample_formats(format, alt_formats);
+ for (int n = 0; alt_formats[n]; n++) {
+ const GUID *guid = format_to_subtype(alt_formats[n]);
+ if (guid) {
+ format = alt_formats[n];
+ sub_format = guid;
+ break;
+ }
+ }
+
wformat->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
wformat->Format.nChannels = channels->num;
wformat->Format.nSamplesPerSec = samplerate;
wformat->Format.wBitsPerSample = af_fmt_to_bytes(format) * 8;
wformat->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
- wformat->SubFormat = *format_to_subtype(format);
+ wformat->SubFormat = *sub_format;
wformat->Samples.wValidBitsPerSample =
valid_bits ? valid_bits : wformat->Format.wBitsPerSample;
wformat->dwChannelMask = mp_chmap_to_waveext(channels);
@@ -318,10 +334,10 @@ static bool search_sample_formats(struct ao *ao, WAVEFORMATEXTENSIBLE *wformat,
int samplerate, struct mp_chmap *channels)
{
// some common bit depths / container sizes (requests welcome)
- int try[] = {AF_FORMAT_DOUBLE, AF_FORMAT_FLOAT, AF_FORMAT_S32,
+ int try[] = {AF_FORMAT_FLOAT , AF_FORMAT_S32 ,
AF_FORMAT_S24 , AF_FORMAT_S32 , AF_FORMAT_S16,
AF_FORMAT_U8 , 0};
- unsigned valid[] = {0 , 0, 0,
+ unsigned valid[] = {0 , 0,
0 , 24, 0,
0 };
for (int i = 0; try[i]; i++) {
@@ -596,6 +612,8 @@ static HRESULT fix_format(struct ao *ao)
bufferPeriod = bufferDuration = devicePeriod;
}
+ ao->format = af_fmt_from_planar(ao->format);
+
// handle unsupported buffer size hopefully this shouldn't happen because of
// the above integer device period
// http://msdn.microsoft.com/en-us/library/windows/desktop/dd370875%28v=vs.85%29.aspx
diff --git a/bootstrap.py b/bootstrap.py
index e526ff2..fbcd0c9 100755
--- a/bootstrap.py
+++ b/bootstrap.py
@@ -5,10 +5,10 @@
from __future__ import print_function
import os, sys, stat, hashlib, subprocess
-WAFRELEASE = "waf-1.8.12"
+WAFRELEASE = "waf-1.9.8"
WAFURLS = ["https://waf.io/" + WAFRELEASE,
"http://www.freehackers.org/~tnagy/release/" + WAFRELEASE]
-SHA256HASH = "01bf2beab2106d1558800c8709bc2c8e496d3da4a2ca343fe091f22fca60c98b"
+SHA256HASH = "167dc42bab6d5bd823b798af195420319cb5c9b571e00db7d83df2a0fe1f4dbf"
if os.path.exists("waf"):
wafver = subprocess.check_output([sys.executable, './waf', '--version']).decode()
diff --git a/common/av_common.c b/common/av_common.c
index 5c58f3f..d7ad8e1 100644
--- a/common/av_common.c
+++ b/common/av_common.c
@@ -302,6 +302,35 @@ void mp_avdict_print_unset(struct mp_log *log, int msgl, AVDictionary *dict)
mp_msg(log, msgl, "Could not set AVOption %s='%s'\n", t->key, t->value);
}
+// If the name starts with "@", try to interpret it as a number, and set *name
+// to the name of the n-th parameter.
+static void resolve_positional_arg(void *avobj, char **name)
+{
+ if (!*name || (*name)[0] != '@')
+ return;
+
+ char *end = NULL;
+ int pos = strtol(*name + 1, &end, 10);
+ if (!end || *end)
+ return;
+
+ const AVOption *opt = NULL;
+ int offset = -1;
+ while (1) {
+ opt = av_opt_next(avobj, opt);
+ if (!opt)
+ return;
+ // This is what libavfilter's parser does to skip aliases.
+ if (opt->offset != offset && opt->type != AV_OPT_TYPE_CONST)
+ pos--;
+ if (pos < 0) {
+ *name = (char *)opt->name;
+ return;
+ }
+ offset = opt->offset;
+ }
+}
+
// kv is in the format as by OPT_KEYVALUELIST(): kv[0]=key0, kv[1]=val0, ...
// Set these options on given avobj (using av_opt_set..., meaning avobj must
// point to a struct that has AVClass as first member).
@@ -313,6 +342,7 @@ int mp_set_avopts(struct mp_log *log, void *avobj, char **kv)
for (int n = 0; kv && kv[n * 2]; n++) {
char *k = kv[n * 2 + 0];
char *v = kv[n * 2 + 1];
+ resolve_positional_arg(avobj, &k);
int r = av_opt_set(avobj, k, v, AV_OPT_SEARCH_CHILDREN);
if (r == AVERROR_OPTION_NOT_FOUND) {
mp_err(log, "AVOption '%s' not found.\n", k);
diff --git a/common/common.h b/common/common.h
index c78409b..4f944c9 100644
--- a/common/common.h
+++ b/common/common.h
@@ -1,18 +1,18 @@
/*
* 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 free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
*
* mpv is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * GNU Lesser 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/>.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_MPCOMMON_H
diff --git a/common/encode_lavc.c b/common/encode_lavc.c
index ee870a0..3563def 100644
--- a/common/encode_lavc.c
+++ b/common/encode_lavc.c
@@ -36,7 +36,7 @@
#define OPT_BASE_STRUCT struct encode_opts
const struct m_sub_options encode_config = {
.opts = (const m_option_t[]) {
- OPT_STRING("o", file, M_OPT_FIXED | CONF_NOCFG | CONF_PRE_PARSE),
+ OPT_STRING("o", file, M_OPT_FIXED | CONF_NOCFG | CONF_PRE_PARSE | M_OPT_FILE),
OPT_STRING("of", format, M_OPT_FIXED),
OPT_STRINGLIST("ofopts*", fopts, M_OPT_FIXED),
OPT_FLOATRANGE("ofps", fps, M_OPT_FIXED, 0.0, 1000000.0),
@@ -676,7 +676,7 @@ int encode_lavc_open_codec(struct encode_lavc_context *ctx,
MP_INFO(ctx, "Opening video encoder: %s [%s]\n",
ctx->vc->long_name, ctx->vc->name);
- if (ctx->vc->capabilities & CODEC_CAP_EXPERIMENTAL) {
+ if (ctx->vc->capabilities & AV_CODEC_CAP_EXPERIMENTAL) {
codec->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL;
MP_WARN(ctx, "\n\n"
" ********************************************\n"
@@ -713,7 +713,7 @@ int encode_lavc_open_codec(struct encode_lavc_context *ctx,
MP_INFO(ctx, "Opening audio encoder: %s [%s]\n",
ctx->ac->long_name, ctx->ac->name);
- if (ctx->ac->capabilities & CODEC_CAP_EXPERIMENTAL) {
+ if (ctx->ac->capabilities & AV_CODEC_CAP_EXPERIMENTAL) {
codec->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL;
MP_WARN(ctx, "\n\n"
" ********************************************\n"
diff --git a/demux/codec_tags.c b/demux/codec_tags.c
index 5f4f569..e520161 100644
--- a/demux/codec_tags.c
+++ b/demux/codec_tags.c
@@ -48,25 +48,20 @@ static const char *lookup_tag(int type, uint32_t tag)
return id == AV_CODEC_ID_NONE ? NULL : mp_codec_from_av_codec_id(id);
}
-static const char *const pcm_le[] = {"pcm_u8", "pcm_s16le", "pcm_s24le", "pcm_s32le"};
-static const char *const pcm_be[] = {"pcm_s8", "pcm_s16be", "pcm_s24be", "pcm_s32be"};
-
-static const char *map_audio_pcm_tag(uint32_t tag, int bits)
+static void map_audio_pcm_tag(struct mp_codec_params *c)
{
+ int bits = c->bits_per_coded_sample;
int bytes = (bits + 7) / 8;
- switch (tag) {
+ switch (c->codec_tag) {
case 0x0: // Microsoft PCM
case 0x1:
case 0xfffe: // MS PCM, Extended
- return bytes >= 1 && bytes <= 4 ? pcm_le[bytes - 1] : NULL;
+ if (bytes >= 1 && bytes <= 4)
+ mp_set_pcm_codec(c, bytes > 1, false, bytes * 8, false);
+ break;
case 0x3: // IEEE float
- return bits == 64 ? "pcm_f64le" : "pcm_f32le";
- case 0x20776172:// 'raw '
- return bits == 8 ? "pcm_u8" : "pcm_s16be";
- case MKTAG('t', 'w', 'o', 's'): // demux_mkv.c internal
- return bytes >= 1 && bytes <= 4 ? pcm_be[bytes - 1] : NULL;
- default:
- return NULL;
+ mp_set_pcm_codec(c, true, true, bits == 64 ? 64 : 32, false);
+ break;
}
}
@@ -74,12 +69,8 @@ void mp_set_codec_from_tag(struct mp_codec_params *c)
{
c->codec = lookup_tag(c->type, c->codec_tag);
- if (c->type == STREAM_AUDIO && c->bits_per_coded_sample) {
- const char *codec =
- map_audio_pcm_tag(c->codec_tag, c->bits_per_coded_sample);
- if (codec)
- c->codec = codec;
- }
+ if (c->type == STREAM_AUDIO && c->bits_per_coded_sample)
+ map_audio_pcm_tag(c);
}
void mp_set_pcm_codec(struct mp_codec_params *c, bool sign, bool is_float,
diff --git a/demux/demux.c b/demux/demux.c
index 0c45083..acccdf5 100644
--- a/demux/demux.c
+++ b/demux/demux.c
@@ -97,7 +97,8 @@ struct demux_opts {
const struct m_sub_options demux_conf = {
.opts = (const struct m_option[]){
OPT_DOUBLE("demuxer-readahead-secs", min_secs, M_OPT_MIN, .min = 0),
- OPT_INTRANGE("demuxer-max-packets", max_packs, 0, 0, INT_MAX),
+ OPT_INTRANGE("demuxer-max-packets", max_packs, 0, 0, INT_MAX,
+ .deprecation_message = "use --demuxer-max-bytes"),
OPT_INTRANGE("demuxer-max-bytes", max_bytes, 0, 0, INT_MAX),
OPT_FLAG("force-seekable", force_seekable, 0),
OPT_DOUBLE("cache-secs", min_secs_cache, M_OPT_MIN, .min = 0),
@@ -106,7 +107,7 @@ const struct m_sub_options demux_conf = {
},
.size = sizeof(struct demux_opts),
.defaults = &(const struct demux_opts){
- .max_packs = 16000,
+ .max_packs = INT_MAX,
.max_bytes = 400 * 1024 * 1024,
.min_secs = 1.0,
.min_secs_cache = 10.0,
@@ -558,7 +559,7 @@ void demux_add_packet(struct sh_stream *stream, demux_packet_t *dp)
dp->next = NULL;
ds->packs++;
- ds->bytes += dp->len;
+ ds->bytes += demux_packet_estimate_total_size(dp);
if (ds->tail) {
// next packet in stream
ds->tail->next = dp;
@@ -777,7 +778,7 @@ static struct demux_packet *dequeue_packet(struct demux_stream *ds)
pkt->next = NULL;
if (!ds->head)
ds->tail = NULL;
- ds->bytes -= pkt->len;
+ ds->bytes -= demux_packet_estimate_total_size(pkt);
ds->packs--;
double ts = pkt->dts == MP_NOPTS_VALUE ? pkt->pts : pkt->dts;
diff --git a/demux/demux_cue.c b/demux/demux_cue.c
index ba97ca0..a8d1176 100644
--- a/demux/demux_cue.c
+++ b/demux/demux_cue.c
@@ -23,6 +23,8 @@
#include <dirent.h>
#include <inttypes.h>
+#include "osdep/io.h"
+
#include "mpv_talloc.h"
#include "misc/bstr.h"
diff --git a/demux/demux_lavf.c b/demux/demux_lavf.c
index 46a2f55..f853a77 100644
--- a/demux/demux_lavf.c
+++ b/demux/demux_lavf.c
@@ -61,6 +61,7 @@
#define OPT_BASE_STRUCT struct demux_lavf_opts
struct demux_lavf_opts {
int probesize;
+ int probeinfo;
int probescore;
float analyzeduration;
int buffersize;
@@ -77,6 +78,8 @@ struct demux_lavf_opts {
const struct m_sub_options demux_lavf_conf = {
.opts = (const m_option_t[]) {
OPT_INTRANGE("demuxer-lavf-probesize", probesize, 0, 32, INT_MAX),
+ OPT_CHOICE("demuxer-lavf-probe-info", probeinfo, 0,
+ ({"no", 0}, {"yes", 1}, {"auto", -1})),
OPT_STRING("demuxer-lavf-format", format, 0),
OPT_FLOATRANGE("demuxer-lavf-analyzeduration", analyzeduration, 0,
0, 3600),
@@ -100,6 +103,7 @@ const struct m_sub_options demux_lavf_conf = {
},
.size = sizeof(struct demux_lavf_opts),
.defaults = &(const struct demux_lavf_opts){
+ .probeinfo = -1,
.allow_mimetype = 1,
.hacks = 1,
// AVPROBE_SCORE_MAX/4 + 1 is the "recommended" limit. Below that, the
@@ -116,6 +120,7 @@ struct format_hack {
const char *mime_type;
int probescore;
float analyzeduration;
+ bool skipinfo : 1; // skip avformat_find_stream_info()
unsigned int if_flags; // additional AVInputFormat.flags flags
bool max_probe : 1; // use probescore only if max. probe size reached
bool ignore : 1; // blacklisted
@@ -128,6 +133,7 @@ struct format_hack {
// segment, with e.g. HLS, player knows about the playlist main file only).
bool clear_filepos : 1;
bool ignore_start : 1;
+ bool fix_editlists : 1;
};
#define BLACKLIST(fmt) {fmt, .ignore = true}
@@ -143,10 +149,13 @@ static const struct format_hack format_hacks[] = {
{"mp3", "audio/mpeg", 24, 0.5},
{"mp3", NULL, 24, .max_probe = true},
- {"hls", .no_stream = true, .clear_filepos = true},
+ {"hls", .no_stream = true, .clear_filepos = true, .skipinfo = true},
{"mpeg", .use_stream_ids = true},
{"mpegts", .use_stream_ids = true},
+ {"mp4", .skipinfo = true, .fix_editlists = true},
+ {"matroska", .skipinfo = true},
+
// In theory, such streams might contain timestamps, but virtually none do.
{"h264", .if_flags = AVFMT_NOTIMESTAMPS },
{"hevc", .if_flags = AVFMT_NOTIMESTAMPS },
@@ -867,6 +876,9 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
guess_and_set_vobsub_name(demuxer, &dopts);
+ if (priv->format_hack.fix_editlists)
+ av_dict_set(&dopts, "advanced_editlist", "0", 0);
+
avfc->interrupt_callback = (AVIOInterruptCB){
.callback = interrupt_cb,
.opaque = demuxer,
@@ -889,15 +901,19 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
priv->avfc = avfc;
- if (!demuxer->params || !demuxer->params->skip_lavf_probing) {
+ bool probeinfo = lavfdopts->probeinfo < 0 ?
+ !priv->format_hack.skipinfo : lavfdopts->probeinfo;
+ if (demuxer->params && demuxer->params->skip_lavf_probing)
+ probeinfo = false;
+ if (probeinfo) {
if (avformat_find_stream_info(avfc, NULL) < 0) {
MP_ERR(demuxer, "av_find_stream_info() failed\n");
return -1;
}
- }
- MP_VERBOSE(demuxer, "avformat_find_stream_info() finished after %"PRId64
- " bytes.\n", stream_tell(priv->stream));
+ MP_VERBOSE(demuxer, "avformat_find_stream_info() finished after %"PRId64
+ " bytes.\n", stream_tell(priv->stream));
+ }
for (int i = 0; i < avfc->nb_chapters; i++) {
AVChapter *c = avfc->chapters[i];
@@ -1027,8 +1043,25 @@ static int demux_lavf_control(demuxer_t *demuxer, int cmd, void *arg)
switch (cmd) {
case DEMUXER_CTRL_GET_TIME_LENGTH:
- if (priv->avfc->duration == 0 || priv->avfc->duration == AV_NOPTS_VALUE)
- return DEMUXER_CTRL_DONTKNOW;
+ if (priv->avfc->duration <= 0) {
+ double total_duration = 0;
+ double av_duration = 0;
+ for (int n = 0; n < priv->avfc->nb_streams; n++) {
+ AVStream *st = priv->avfc->streams[n];
+ if (st->duration <= 0)
+ continue;
+ double f_duration = st->duration * av_q2d(st->time_base);
+ total_duration = MPMAX(total_duration, f_duration);
+ if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO ||
+ st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
+ av_duration = MPMAX(av_duration, f_duration);
+ }
+ double duration = av_duration > 0 ? av_duration : total_duration;
+ if (duration <= 0)
+ return DEMUXER_CTRL_DONTKNOW;
+ *(double *)arg = duration;
+ return DEMUXER_CTRL_OK;
+ }
*((double *)arg) = (double)priv->avfc->duration / AV_TIME_BASE;
return DEMUXER_CTRL_OK;
diff --git a/demux/demux_mkv.c b/demux/demux_mkv.c
index d564999..8fd4f42 100644
--- a/demux/demux_mkv.c
+++ b/demux/demux_mkv.c
@@ -6,18 +6,20 @@
*
* 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 free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
*
* mpv is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * GNU Lesser 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/>.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Parts under HAVE_GPL are licensed under GNU General Public License.
*/
#include <stdlib.h>
@@ -647,7 +649,9 @@ static void parse_trackentry(struct demuxer *demuxer,
if (entry->name) {
track->name = talloc_strdup(track, entry->name);
+#if HAVE_GPL
MP_VERBOSE(demuxer, "| + Name: %s\n", track->name);
+#endif
}
track->type = entry->track_type;
diff --git a/demux/ebml.c b/demux/ebml.c
index d62dd40..e05f724 100644
--- a/demux/ebml.c
+++ b/demux/ebml.c
@@ -6,18 +6,18 @@
*
* 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 free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
*
* mpv is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * GNU Lesser 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/>.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
diff --git a/demux/ebml.h b/demux/ebml.h
index 6821c1b..8b67ea7 100644
--- a/demux/ebml.h
+++ b/demux/ebml.h
@@ -1,18 +1,18 @@
/*
* 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 free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
*
* mpv is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * GNU Lesser 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/>.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_EBML_H
diff --git a/demux/matroska.h b/demux/matroska.h
index 169ee7f..939792c 100644
--- a/demux/matroska.h
+++ b/demux/matroska.h
@@ -1,18 +1,18 @@
/*
* 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 free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
*
* mpv is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * GNU Lesser 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/>.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_MATROSKA_H
diff --git a/demux/packet.c b/demux/packet.c
index 47f0cb8..fd1754b 100644
--- a/demux/packet.c
+++ b/demux/packet.c
@@ -1,19 +1,20 @@
/*
* 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 free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
*
* mpv is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * GNU Lesser 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/>.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
+
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
@@ -130,6 +131,32 @@ struct demux_packet *demux_copy_packet(struct demux_packet *dp)
return new;
}
+#define ROUND_ALLOC(s) MP_ALIGN_UP(s, 64)
+
+// Attempt to estimate the total memory consumption of the given packet.
+// This is important if we store thousands of packets and not to exceed
+// user-provided limits. Of course we can't know how much memory internal
+// fragmentation of the libc memory allocator will waste.
+// Note that this should return a "stable" value - e.g. if a new packet ref
+// is created, this should return the same value with the new ref. (This
+// implies the value is not exact and does not return the actual size of
+// memory wasted due to internal fragmentation.)
+size_t demux_packet_estimate_total_size(struct demux_packet *dp)
+{
+ size_t size = ROUND_ALLOC(sizeof(struct demux_packet));
+ size += ROUND_ALLOC(dp->len);
+ if (dp->avpacket) {
+ size += ROUND_ALLOC(sizeof(AVPacket));
+ size += ROUND_ALLOC(sizeof(AVBufferRef));
+ size += 64; // upper bound estimate on sizeof(AVBuffer)
+ size += ROUND_ALLOC(dp->avpacket->side_data_elems *
+ sizeof(dp->avpacket->side_data[0]));
+ for (int n = 0; n < dp->avpacket->side_data_elems; n++)
+ size += ROUND_ALLOC(dp->avpacket->side_data[n].size);
+ }
+ return size;
+}
+
int demux_packet_set_padding(struct demux_packet *dp, int start, int end)
{
#if LIBAVCODEC_VERSION_MICRO >= 100
diff --git a/demux/packet.h b/demux/packet.h
index d669d02..884a7d8 100644
--- a/demux/packet.h
+++ b/demux/packet.h
@@ -1,18 +1,18 @@
/*
* 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 free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
*
* mpv is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * GNU Lesser 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/>.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_DEMUX_PACKET_H
@@ -51,6 +51,7 @@ struct demux_packet *new_demux_packet_from(void *data, size_t len);
void demux_packet_shorten(struct demux_packet *dp, size_t len);
void free_demux_packet(struct demux_packet *dp);
struct demux_packet *demux_copy_packet(struct demux_packet *dp);
+size_t demux_packet_estimate_total_size(struct demux_packet *dp);
void demux_packet_copy_attribs(struct demux_packet *dst, struct demux_packet *src);
diff --git a/etc/builtin.conf b/etc/builtin.conf
index 533dce7..1d93df9 100644
--- a/etc/builtin.conf
+++ b/etc/builtin.conf
@@ -24,7 +24,6 @@ stop-playback-on-init-failure=yes
# OSX/Cocoa global input hooks
input-appleremote=no
input-media-keys=no
-input-app-events=no
[encoding]
vo=lavc
diff --git a/etc/encoding-profiles.conf b/etc/encoding-profiles.conf
index 132110b..3c8f766 100644
--- a/etc/encoding-profiles.conf
+++ b/etc/encoding-profiles.conf
@@ -12,7 +12,7 @@
# will be loaded instead.
#
# Then, list all profiles by
-# mpv -profile help | grep enc-
+# mpv -profile help -o - | grep enc-
#
# The following kinds of encoding profiles exist:
# enc-a-*: initialize an audio codec including good defaults
@@ -27,7 +27,7 @@
# options, or even switch to another codec.
#
# You can view the exact options a profile sets by
-# mpv -show-profile enc-to-bb-9000
+# mpv -show-profile enc-to-hp-slate-7
#
# Examples:
# mpv -profile enc-to-dvdpal -o outfile.mpg infile.mkv
@@ -38,8 +38,8 @@
# audio codecs #
################
[enc-a-aac]
-profile-desc = "AAC (libfaac or FFmpeg)"
-oac = libfdk_aac,libfaac,libvo_aacenc,aac
+profile-desc = "AAC (libfdk-aac or FFmpeg)"
+oac = libfdk_aac,aac
oacopts = b=96k
[enc-a-ac3]
@@ -53,10 +53,16 @@ oac = libmp3lame
oacopts = b=128k
[enc-a-vorbis]
-profile-desc = "Vorbis (libvorbis)"
+profile-desc = "Vorbis (libvorbis or FFmpeg)"
oac = libvorbis,vorbis
oacopts = qscale=3
+[enc-a-opus]
+profile-desc = "Opus (libopus or FFmpeg)"
+oac = libopus,opus
+audio-samplerate = 48000
+oacopts = b=96k
+
################
# video codecs #
################
@@ -84,7 +90,12 @@ ovcopts = qscale=4
[enc-v-vp8]
profile-desc = "VP8 (libvpx)"
ovc = libvpx
-ovcopts = qmin=4,b=10000000k # ought to be enough for anyone; for CBR use, set b=; for VBR use, set qmin= to quality
+ovcopts = speed=0,lag-in-frames=8,slices=2,threads=0,b=2M,crf=10,qmin=0,qmax=36
+
+[enc-v-vp9]
+profile-desc = "VP9 (libvpx)"
+ovc = libvpx-vp9
+ovcopts = speed=6,lag-in-frames=8,slices=2,threads=0,crf=18,qmin=0,qmax=36
###########
# formats #
@@ -115,11 +126,11 @@ profile = enc-a-aac
ofopts-clr = yes
[enc-f-webm]
-profile-desc = "VP8 + Vorbis (for WebM)"
+profile-desc = "VP9 + Opus (for WebM)"
of = webm
ocopyts = yes
-profile = enc-v-vp8
-profile = enc-a-vorbis
+profile = enc-v-vp9
+profile = enc-a-opus
ofopts-clr = yes
##################
@@ -149,13 +160,6 @@ ovfirst = yes # dvdauthor needs this
audio-samplerate = 48000
ovcopts-add = g=18,b=6000000,maxrate=9000000,minrate=0,bufsize=1835008
-[enc-to-bb-9000]
-profile-desc = "MP4 for Blackberry Bold 9000"
-profile = enc-f-mp4
-vf-add = dsize=480:360:0:2,scale=w=0:h=0,dsize=-1:-1 # native screen res, letterbox
-ovcopts-add = maxrate=1500k,bufsize=1000k,rc_init_occupancy=900k,refs=1,profile=baseline
-omaxfps = 30
-
[enc-to-nok-n900]
profile-desc = "MP4 for Nokia N900"
profile = enc-f-mp4
@@ -168,59 +172,17 @@ omaxfps = 30
profile-desc = "3GP for Nokia 6300"
profile = enc-f-3gp
ofps = 25
-vf-add = scale=w=176:h=144
+vf-add = lavfi=graph="scale=176:144"
audio-samplerate = 16000
audio-channels = 1
oacopts-add = b=32k
-[enc-to-psp]
-profile-desc = "MP4 for PlayStation Portable"
-profile = enc-f-mp4
-ofps = 30000/1001
-vf-add = scale=w=480:h=272,dsize=480:270
-audio-samplerate = 48000
-audio-channels = 2
-ovcopts-add = b=512k,profile=baseline
-
-[enc-to-iphone-noscale]
-profile-desc = "MP4 for iPhone (no scaling)"
-profile = enc-f-mp4
-oautofps = yes # iphone supports 30fps max
-ovcopts-add = maxrate=2500k,bufsize=1000k,rc_init_occupancy=900k,level=30,profile=baseline
-omaxfps = 30
-
-[enc-to-iphone]
-profile-desc = "MP4 for iPhone (480x320)"
-profile = enc-to-iphone-noscale
-vf-add = dsize=480:320:1:2,scale=w=0:h=0,dsize=-1:-1 # panscan
-omaxfps = 30
-
-[enc-to-iphone-4]
-profile-desc = "MP4 for iPhone 4 (960x640)"
-profile = enc-to-iphone-noscale
-vf-add = dsize=960:480:1:2,scale=w=0:h=0,dsize=-1:-1 # panscan
-omaxfps = 30
-
-[enc-to-iphone-5]
-profile-desc = "MP4 for iPhone 5 (1136x640)"
-profile = enc-to-iphone-noscale
-vf-add = dsize=1136:480:1:2,scale=w=0:h=0,dsize=-1:-1 # panscan
-omaxfps = 30
-
[enc-to-hp-slate-7]
profile-desc = "MP4 for HP Slate 7 (1024x600, crazy aspect)"
profile = enc-f-mp4
omaxfps = 30
ovcopts-add = profile=high
# DW = 1024, DH = 600, DAR = 97:54 (=> SAR = 2425:2304)
-vf-add = lavfi=graph="scale=floor(min(1024*min(1\,dar/(97/54))\,in_w*max(1\,sar/(2425/2304)))/2+0.5)*2:floor(min(600*min((97/54)/dar\,1)\,in_h*max((2425/2304)/sar\,1))/2+0.5)*2,setsar=sar=1"
-
-[enc-to-hp-slate-7-git]
-profile-desc = "MP4 for HP Slate 7 (1024x600, crazy aspect), FFmpeg-git"
-profile = enc-f-mp4
-omaxfps = 30
-ovcopts-add = profile=high
-# DW = 1024, DH = 600, DAR = 97:54 (=> SAR = 2425:2304)
vf-add = lavfi=graph="scale=floor(min(1024*min(1\,dar/(97/54))\,in_w)/2+0.5)*2:floor(min(600*min((97/54)/dar\,1)\,in_h)/2+0.5)*2,setsar=sar=sar/(2425/2304)"
# Advanced scaling for specific output devices - how it works:
diff --git a/etc/input.conf b/etc/input.conf
index fe39b6c..0e07f33 100644
--- a/etc/input.conf
+++ b/etc/input.conf
@@ -119,9 +119,9 @@
#_ cycle video
#T cycle ontop # toggle video window ontop of other windows
#f cycle fullscreen # toggle fullscreen
-#s screenshot # take a screenshot
-#S screenshot video # ...without subtitles
-#Ctrl+s screenshot window # ...with subtitles and OSD, and scaled
+#s async screenshot # take a screenshot
+#S async screenshot video # ...without subtitles
+#Ctrl+s async screenshot window # ...with subtitles and OSD, and scaled
#Alt+s screenshot each-frame # automatically screenshot every frame
#w add panscan -0.1 # zoom out with -panscan 0 -fs
#e add panscan +0.1 # in
diff --git a/etc/mpv.conf b/etc/mpv.conf
index c6080c6..d72c9ee 100644
--- a/etc/mpv.conf
+++ b/etc/mpv.conf
@@ -52,9 +52,6 @@
# Keep the player window on top of all other windows.
#ontop=yes
-# Specify default video driver (see --vo=help for a list).
-#vo=opengl
-
# Specify high quality video rendering preset (for OpenGL VO only)
# Can cause performance problems with some drivers and GPUs.
#profile=opengl-hq
@@ -74,11 +71,9 @@
# audio settings #
##################
-# Specify default audio driver (see --ao=help for a list).
-#ao=alsa
-
-# Disable softvol usage, and always use the system mixer if available.
-#softvol=no
+# Specify default audio device. You can list devices with: --audio-device=help
+# The option takes the device string (the stuff between the '...').
+#audio-device=alsa/default
# Do not filter audio to keep pitch when changing playback speed.
#audio-pitch-correction=no
@@ -87,7 +82,7 @@
#audio-channels=5.1
# Disable any automatic remix, _if_ the audio output accepts the audio format.
# of the currently played file. See caveats mentioned in the manpage.
-# (This is the default.)
+# (The default is "auto-safe", see manpage.)
#audio-channels=auto
##################
@@ -124,13 +119,12 @@
# Change subtitle encoding. For Arabic subtitles use 'cp1256'.
# If the file seems to be valid UTF-8, prefer UTF-8.
-#sub-codepage=utf8:cp1256
-
+# (You can add '+' in front of the codepage to force it.)
+#sub-codepage=cp1256
# You can also include other configuration files.
#include=/path/to/the/file/you/want/to/include
-
############
# Profiles #
############
@@ -138,8 +132,7 @@
# The options declared as part of profiles override global default settings,
# but only take effect when the profile is active.
-# The following profile can be enabled on the command line with: --profile=invert
+# The following profile can be enabled on the command line with: --profile=eye-cancer
-#[invert]
-# The profile forces this video filter:
-#vf-add=flip
+#[eye-cancer]
+#sharpen=5
diff --git a/etc/mpv.desktop b/etc/mpv.desktop
index 3ff6388..c246855 100644
--- a/etc/mpv.desktop
+++ b/etc/mpv.desktop
@@ -3,25 +3,28 @@ Type=Application
Name=mpv Media Player
Name[ca]=Reproductor multimèdia mpv
Name[cs]=mpv přehrávač
+Name[da]=mpv-medieafspiller
Name[pl]=Odtwarzacz mpv
Name[ru]=Проигрыватель mpv
-Name[zh-CN]=mpv 媒体播放器
-Name[zh-TW]=mpv 媒體播放器
+Name[zh_CN]=mpv 媒体播放器
+Name[zh_TW]=mpv 媒體播放器
GenericName=Multimedia player
GenericName[cs]=Multimediální přehrávač
-GenericName[zh-CN]=多媒体播放器
-GenericName[zh-TW]=多媒體播放器
+GenericName[da]=Multimedieafspiller
+GenericName[zh_CN]=多媒体播放器
+GenericName[zh_TW]=多媒體播放器
Comment=Play movies and songs
Comment[ca]=Reproduïu vídeos i cançons
Comment[cs]=Přehrává filmy a hudbu
+Comment[da]=Afspil film og sange
Comment[de]=Filme und Musik abspielen
Comment[es]=Reproduzca vídeos y canciones
Comment[fr]=Lecteur multimédia
Comment[it]=Lettore multimediale
Comment[pl]=Odtwarzaj filmy i muzykę
Comment[ru]=Воспроизвести фильмы и музыку
-Comment[zh-CN]=播放电影和歌曲
-Comment[zh-TW]=播放電影和歌曲
+Comment[zh_CN]=播放电影和歌曲
+Comment[zh_TW]=播放電影和歌曲
Icon=mpv
TryExec=mpv
Exec=mpv --player-operation-mode=pseudo-gui -- %U
diff --git a/input/cmd_list.c b/input/cmd_list.c
index 53cc375..33cf40f 100644
--- a/input/cmd_list.c
+++ b/input/cmd_list.c
@@ -97,6 +97,7 @@ const struct mp_cmd_def mp_cmds[] = {
{ MP_CMD_PRINT_TEXT, "print-text", { ARG_STRING }, .allow_auto_repeat = true },
{ MP_CMD_SHOW_TEXT, "show-text", { ARG_STRING, OARG_INT(-1), OARG_INT(0) },
.allow_auto_repeat = true},
+ { MP_CMD_EXPAND_TEXT, "expand-text", { ARG_STRING } },
{ MP_CMD_SHOW_PROGRESS, "show-progress", .allow_auto_repeat = true},
{ MP_CMD_SUB_ADD, "sub-add", { ARG_STRING,
OARG_CHOICE(0, ({"select", 0}, {"auto", 1}, {"cached", 2})),
diff --git a/input/cmd_list.h b/input/cmd_list.h
index 1c2330b..404df46 100644
--- a/input/cmd_list.h
+++ b/input/cmd_list.h
@@ -67,6 +67,7 @@ enum mp_command_type {
MP_CMD_SET,
MP_CMD_PRINT_TEXT,
MP_CMD_SHOW_TEXT,
+ MP_CMD_EXPAND_TEXT,
MP_CMD_SHOW_PROGRESS,
MP_CMD_ADD,
MP_CMD_CYCLE,
diff --git a/input/cmd_parse.c b/input/cmd_parse.c
index 01e4bb6..b0c6c50 100644
--- a/input/cmd_parse.c
+++ b/input/cmd_parse.c
@@ -49,6 +49,7 @@ static const struct flag cmd_flags[] = {
{"expand-properties", 0, MP_EXPAND_PROPERTIES},
{"raw", MP_EXPAND_PROPERTIES, 0},
{"repeatable", 0, MP_ALLOW_REPEAT},
+ {"async", 0, MP_ASYNC_CMD},
{0}
};
diff --git a/input/input.c b/input/input.c
index f0f9f64..b8cc142 100644
--- a/input/input.c
+++ b/input/input.c
@@ -168,7 +168,6 @@ struct input_opts {
int use_alt_gr;
int use_appleremote;
int use_media_keys;
- int use_app_events;
int default_bindings;
int enable_mouse_movements;
int vo_key_input;
@@ -193,7 +192,6 @@ const struct m_sub_options input_config = {
#if HAVE_COCOA
OPT_FLAG("input-appleremote", use_appleremote, 0),
OPT_FLAG("input-media-keys", use_media_keys, 0),
- OPT_FLAG("input-app-events", use_app_events, M_OPT_FIXED),
#endif
OPT_FLAG("window-dragging", allow_win_drag, 0),
OPT_REPLACED("input-x11-keyboard", "input-vo-keyboard"),
@@ -210,7 +208,6 @@ const struct m_sub_options input_config = {
#if HAVE_COCOA
.use_appleremote = 1,
.use_media_keys = 1,
- .use_app_events = 1,
#endif
.default_bindings = 1,
.vo_key_input = 1,
@@ -1298,11 +1295,6 @@ void mp_input_load_config(struct input_ctx *ictx)
talloc_free(tmp);
}
-#if HAVE_COCOA
- if (ictx->opts->use_app_events)
- cocoa_start_event_monitor();
-#endif
-
#if defined(__MINGW32__)
if (ictx->global->opts->input_file && *ictx->global->opts->input_file)
mp_input_pipe_add(ictx, ictx->global->opts->input_file);
diff --git a/input/input.h b/input/input.h
index fb928e0..dfc1407 100644
--- a/input/input.h
+++ b/input/input.h
@@ -34,6 +34,7 @@ enum mp_cmd_flags {
MP_ON_OSD_MSG = 4, // force a message, if applicable
MP_EXPAND_PROPERTIES = 8, // expand strings as properties
MP_ALLOW_REPEAT = 16, // if used as keybinding, allow key repeat
+ MP_ASYNC_CMD = 32,
MP_ON_OSD_FLAGS = MP_ON_OSD_NO | MP_ON_OSD_AUTO |
MP_ON_OSD_BAR | MP_ON_OSD_MSG,
diff --git a/libmpv/client.h b/libmpv/client.h
index 14777b4..c7405d8 100644
--- a/libmpv/client.h
+++ b/libmpv/client.h
@@ -1,4 +1,6 @@
-/* Permission to use, copy, modify, and/or distribute this software for any
+/* Copyright (C) 2017 the mpv developers
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
@@ -12,13 +14,9 @@
*/
/*
- * Note: the client API is licensed under ISC (see above) to ease
- * interoperability with other licenses. But keep in mind that the
- * mpv core is still mostly GPLv2+. It's up to lawyers to decide
- * whether applications using this API are affected by the GPL.
- * One argument against this is that proprietary applications
- * using mplayer in slave mode is apparently tolerated, and this
- * API is basically equivalent to slave mode.
+ * Note: the client API is licensed under ISC (see above) to enable
+ * other wrappers outside of mpv. But keep in mind that the
+ * mpv core is still mostly GPLv2+.
*/
#ifndef MPV_CLIENT_API_H_
@@ -161,23 +159,19 @@ extern "C" {
* Embedding the video window
* --------------------------
*
- * Currently you have to get the raw window handle, and set it as "wid" option.
- * This works on X11, win32, and OSX only. In addition, it works with a few VOs
- * only, and VOs which do not support this will just create a freestanding
- * window.
- *
- * Both on X11 and win32, the player will fill the window referenced by the
- * "wid" option fully and letterbox the video (i.e. add black bars if the
- * aspect ratio of the window and the video mismatch).
- *
- * Setting the "input-vo-keyboard" may be required to get keyboard input
- * through the embedded window, if this is desired.
+ * Using the opengl-cb API (in opengl_cb.h) is recommended. This API requires
+ * you to create and maintain an OpenGL context, to which you can render
+ * video using a specific API call. This API does not include keyboard or mouse
+ * input directly.
*
- * For OpenGL integration (e.g. rendering video to a texture), a separate API
- * is available. Look at opengl_cb.h. This API does not include keyboard or
- * mouse input directly.
+ * There is an older way to embed the native mpv window into your own. You have
+ * to get the raw window handle, and set it as "wid" option. This works on X11,
+ * win32, and OSX only. It's much easier to use than the opengl-cb API, but
+ * also has various problems.
*
- * Also see client API examples and the mpv manpage.
+ * Also see client API examples and the mpv manpage. There is an extensive
+ * discussion here:
+ * https://github.com/mpv-player/mpv-examples/tree/master/libmpv#methods-of-embedding-the-video-window
*
* Compatibility
* -------------
@@ -214,6 +208,16 @@ extern "C" {
#define MPV_CLIENT_API_VERSION MPV_MAKE_VERSION(1, 24)
/**
+ * The API user is allowed to "#define MPV_ENABLE_DEPRECATED 0" before
+ * including any libmpv headers. Then deprecated symbols will be excluded
+ * from the headers. (Of course, deprecated properties and commands and
+ * other functionality will still work.)
+ */
+#ifndef MPV_ENABLE_DEPRECATED
+#define MPV_ENABLE_DEPRECATED 1
+#endif
+
+/**
* Return the MPV_CLIENT_API_VERSION the mpv source has been compiled with.
*/
unsigned long mpv_client_api_version(void);
@@ -505,6 +509,8 @@ mpv_handle *mpv_create_client(mpv_handle *ctx, const char *name);
*/
int mpv_load_config_file(mpv_handle *ctx, const char *filename);
+#if MPV_ENABLE_DEPRECATED
+
/**
* This does nothing since mpv 0.23.0 (API version 1.24). Below is the
* description of the old behavior.
@@ -538,6 +544,8 @@ void mpv_suspend(mpv_handle *ctx);
*/
void mpv_resume(mpv_handle *ctx);
+#endif
+
/**
* Return the internal time in microseconds. This has an arbitrary start offset,
* but will never wrap or go backwards.
@@ -818,7 +826,7 @@ void mpv_free_node_contents(mpv_node *node);
* - deprecated options shadowed by properties:
* - chapter (option deprecated in 0.21.0)
* - playlist-pos (option deprecated in 0.21.0)
- * The deprecated properties will be removed in mpv 0.23.0.
+ * The deprecated properties were removed in mpv 0.23.0.
*
* @param name Option name. This is the same as on the mpv command line, but
* without the leading "--".
@@ -844,6 +852,9 @@ int mpv_set_option_string(mpv_handle *ctx, const char *name, const char *data);
*
* The commands and their parameters are documented in input.rst.
*
+ * Does not use OSD and string expansion by default (unlike mpv_command_string()
+ * and input.conf).
+ *
* @param[in] args NULL-terminated list of strings. Usually, the first item
* is the command, and the following items are arguments.
* @return error code
@@ -856,6 +867,8 @@ int mpv_command(mpv_handle *ctx, const char **args);
* mpv_command_node() with the format set to MPV_FORMAT_NODE_ARRAY, and
* every arg passed in order as MPV_FORMAT_STRING.
*
+ * Does not use OSD and string expansion by default.
+ *
* @param[in] args mpv_node with format set to MPV_FORMAT_NODE_ARRAY; each entry
* is an argument using an arbitrary format (the format must be
* compatible to the used command). Usually, the first item is
@@ -873,6 +886,8 @@ int mpv_command_node(mpv_handle *ctx, mpv_node *args, mpv_node *result);
* Same as mpv_command, but use input.conf parsing for splitting arguments.
* This is slightly simpler, but also more error prone, since arguments may
* need quoting/escaping.
+ *
+ * This also has OSD and string expansion enabled by default.
*/
int mpv_command_string(mpv_handle *ctx, const char *args);
@@ -883,6 +898,10 @@ int mpv_command_string(mpv_handle *ctx, const char *args);
* MPV_EVENT_COMMAND_REPLY event. (This event will also have an
* error code set if running the command failed.)
*
+ * This has nothing to do with the "async" command prefix, although they might
+ * be unified in the future. For now, calling this API means that the command
+ * will be synchronously executed on the core, without blocking the API user.
+ *
* @param reply_userdata the value mpv_event.reply_userdata of the reply will
* be set to (see section about asynchronous calls)
* @param args NULL-terminated list of strings (see mpv_command())
@@ -1127,6 +1146,7 @@ typedef enum mpv_event_id {
* decoding starts.
*/
MPV_EVENT_FILE_LOADED = 8,
+#if MPV_ENABLE_DEPRECATED
/**
* The list of video/audio/subtitle tracks was changed. (E.g. a new track
* was found. This doesn't necessarily indicate a track switch; for this,
@@ -1145,6 +1165,7 @@ typedef enum mpv_event_id {
* and might be removed in the far future.
*/
MPV_EVENT_TRACK_SWITCHED = 10,
+#endif
/**
* Idle mode was entered. In this mode, no file is played, and the playback
* core waits for new commands. (The command line player normally quits
@@ -1152,6 +1173,7 @@ typedef enum mpv_event_id {
* was started with mpv_create(), idle mode is enabled by default.)
*/
MPV_EVENT_IDLE = 11,
+#if MPV_ENABLE_DEPRECATED
/**
* Playback was paused. This indicates the user pause state.
*
@@ -1181,6 +1203,7 @@ typedef enum mpv_event_id {
* removed in the far future.
*/
MPV_EVENT_UNPAUSE = 13,
+#endif
/**
* Sent every time after a video frame is displayed. Note that currently,
* this will be sent in lower frequency if there is no video, or playback
@@ -1188,6 +1211,7 @@ typedef enum mpv_event_id {
* restricted to video frames only.
*/
MPV_EVENT_TICK = 14,
+#if MPV_ENABLE_DEPRECATED
/**
* @deprecated This was used internally with the internal "script_dispatch"
* command to dispatch keyboard and mouse input for the OSC.
@@ -1197,6 +1221,7 @@ typedef enum mpv_event_id {
* header only for compatibility.
*/
MPV_EVENT_SCRIPT_INPUT_DISPATCH = 15,
+#endif
/**
* Triggered by the script-message input command. The command uses the
* first argument of the command as client name (see mpv_client_name()) to
@@ -1221,6 +1246,7 @@ typedef enum mpv_event_id {
* because there is no such thing as audio output embedding.
*/
MPV_EVENT_AUDIO_RECONFIG = 18,
+#if MPV_ENABLE_DEPRECATED
/**
* Happens when metadata (like file tags) is possibly updated. (It's left
* unspecified whether this happens on file start or only when it changes
@@ -1231,6 +1257,7 @@ typedef enum mpv_event_id {
* be removed in the far future.
*/
MPV_EVENT_METADATA_UPDATE = 19,
+#endif
/**
* Happens when a seek was initiated. Playback stops. Usually it will
* resume with MPV_EVENT_PLAYBACK_RESTART as soon as the seek is finished.
@@ -1248,6 +1275,7 @@ typedef enum mpv_event_id {
* See also mpv_event and mpv_event_property.
*/
MPV_EVENT_PROPERTY_CHANGE = 22,
+#if MPV_ENABLE_DEPRECATED
/**
* Happens when the current chapter changes.
*
@@ -1256,6 +1284,7 @@ typedef enum mpv_event_id {
* be removed in the far future.
*/
MPV_EVENT_CHAPTER_CHANGE = 23,
+#endif
/**
* Happens if the internal per-mpv_handle ringbuffer overflows, and at
* least 1 event had to be dropped. This can happen if the client doesn't
@@ -1409,12 +1438,14 @@ typedef struct mpv_event_end_file {
int error;
} mpv_event_end_file;
+#if MPV_ENABLE_DEPRECATED
/** @deprecated see MPV_EVENT_SCRIPT_INPUT_DISPATCH for remarks
*/
typedef struct mpv_event_script_input_dispatch {
int arg0;
const char *type;
} mpv_event_script_input_dispatch;
+#endif
typedef struct mpv_event_client_message {
/**
diff --git a/libmpv/opengl_cb.h b/libmpv/opengl_cb.h
index 0c14f0f..2c6219a 100644
--- a/libmpv/opengl_cb.h
+++ b/libmpv/opengl_cb.h
@@ -1,4 +1,6 @@
-/* Permission to use, copy, modify, and/or distribute this software for any
+/* Copyright (C) 2017 the mpv developers
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
@@ -11,16 +13,6 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-/*
- * Note: the client API is licensed under ISC (see above) to ease
- * interoperability with other licenses. But keep in mind that the
- * mpv core is still mostly GPLv2+. It's up to lawyers to decide
- * whether applications using this API are affected by the GPL.
- * One argument against this is that proprietary applications
- * using mplayer in slave mode is apparently tolerated, and this
- * API is basically equivalent to slave mode.
- */
-
#ifndef MPV_CLIENT_API_OPENGL_CB_H_
#define MPV_CLIENT_API_OPENGL_CB_H_
@@ -31,22 +23,23 @@ extern "C" {
#endif
/**
- * Warning: this API is not stable yet.
*
* Overview
* --------
*
* This API can be used to make mpv render into a foreign OpenGL context. It
- * can be used to handle video display. Be aware that using this API is not
- * required: you can embed the mpv window by setting the mpv "wid" option to
- * a native window handle (see "Embedding the video window" section in the
- * client.h header). In general, using the "wid" option is recommended over
- * the OpenGL API, because it's simpler and more flexible on the mpv side.
+ * can be used to handle video display.
*
* The renderer needs to be explicitly initialized with mpv_opengl_cb_init_gl(),
* and then video can be drawn with mpv_opengl_cb_draw(). The user thread can
* be notified by new frames with mpv_opengl_cb_set_update_callback().
*
+ * You can output and embed video without this API by setting the mpv "wid"
+ * option to a native window handle (see "Embedding the video window" section
+ * in the client.h header). In general, using the opengl-cb API is recommended,
+ * because window embedding can cause various issues, especially with GUI
+ * toolkits and certain platforms.
+ *
* OpenGL interop
* --------------
*
@@ -129,6 +122,7 @@ extern "C" {
* as well, if you have recent enough drivers and the
* "hwaccel" option is set to "cuda" as well)
* - OSX: CGL is required (CGLGetCurrentContext() returning non-NULL)
+ * - iOS: EAGL is required (EAGLContext.currentContext returning non-nil)
*
* Once these things are setup, hardware decoding can be enabled/disabled at
* any time by setting the "hwdec" property.
@@ -173,26 +167,9 @@ extern "C" {
* Windowing system interop on MS win32
* ------------------------------------
*
- * Warning: the following is only required if native OpenGL instead of ANGLE
- * is used. ANGLE is recommended, because it also allows direct
- * hardware decoding interop without further setup by the libmpv
- * API user, while the same with native OpenGL is either very hard
- * to do (via GL/DX interop with D3D9), or not implemented.
- *
- * If OpenGL switches to fullscreen, most players give it access GPU access,
- * which means DXVA2 hardware decoding in mpv won't work. This can be worked
- * around by giving mpv access to Direct3D device, which it will then use to
- * create a decoder. The device can be either the real device used for display,
- * or a "blank" device created before switching to fullscreen.
- *
- * You can provide glMPGetNativeDisplay as described in the previous section.
- * If it is called with name set to "IDirect3DDevice9", it should return a
- * IDirect3DDevice9 pointer (or NULL if not available). libmpv will release
- * this interface when it is done with it.
- *
- * In previous libmpv releases, this used "GL_MP_D3D_interfaces" and
- * "glMPGetD3DInterface". This is deprecated; use glMPGetNativeDisplay instead
- * (the semantics are 100% compatible).
+ * You should use ANGLE, and make sure your application and libmpv are linked
+ * to the same ANGLE DLLs. libmpv will pick the device context (needed for
+ * hardware decoding) from the current ANGLE EGL context.
*
* Windowing system interop on RPI
* -------------------------------
@@ -309,6 +286,7 @@ int mpv_opengl_cb_init_gl(mpv_opengl_cb_context *ctx, const char *exts,
*/
int mpv_opengl_cb_draw(mpv_opengl_cb_context *ctx, int fbo, int w, int h);
+#if MPV_ENABLE_DEPRECATED
/**
* Deprecated. Use mpv_opengl_cb_draw(). This function is equivalent to:
*
@@ -321,6 +299,7 @@ int mpv_opengl_cb_draw(mpv_opengl_cb_context *ctx, int fbo, int w, int h);
* was never marked as stable).
*/
int mpv_opengl_cb_render(mpv_opengl_cb_context *ctx, int fbo, int vp[4]);
+#endif
/**
* Tell the renderer that a frame was flipped at the given time. This is
diff --git a/libmpv/qthelper.hpp b/libmpv/qthelper.hpp
index 8615d0f..bc30dec 100644
--- a/libmpv/qthelper.hpp
+++ b/libmpv/qthelper.hpp
@@ -1,4 +1,6 @@
-/* Permission to use, copy, modify, and/or distribute this software for any
+/* Copyright (C) 2017 the mpv developers
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
@@ -224,6 +226,8 @@ struct node_autofree {
~node_autofree() { mpv_free_node_contents(ptr); }
};
+#if MPV_ENABLE_DEPRECATED
+
/**
* Return the given property as mpv_node converted to QVariant, or QVariant()
* on error.
@@ -281,6 +285,8 @@ static inline QVariant command_variant(mpv_handle *ctx, const QVariant &args)
return node_to_variant(&res);
}
+#endif
+
/**
* This is used to return error codes wrapped in QVariant for functions which
* return QVariant.
diff --git a/libmpv/stream_cb.h b/libmpv/stream_cb.h
index a9a9e05..01de470 100644
--- a/libmpv/stream_cb.h
+++ b/libmpv/stream_cb.h
@@ -1,4 +1,6 @@
-/* Permission to use, copy, modify, and/or distribute this software for any
+/* Copyright (C) 2017 the mpv developers
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
@@ -11,16 +13,6 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-/*
- * Note: the client API is licensed under ISC (see above) to ease
- * interoperability with other licenses. But keep in mind that the
- * mpv core is still mostly GPLv2+. It's up to lawyers to decide
- * whether applications using this API are affected by the GPL.
- * One argument against this is that proprietary applications
- * using mplayer in slave mode is apparently tolerated, and this
- * API is basically equivalent to slave mode.
- */
-
#ifndef MPV_CLIENT_API_STREAM_CB_H_
#define MPV_CLIENT_API_STREAM_CB_H_
@@ -54,7 +46,9 @@ extern "C" {
* stream callbacks.
*
* Note that your custom callbacks must not invoke libmpv APIs as that would
- * cause a deadlock.
+ * cause a deadlock. (Unless you call a different mpv_handle than the one the
+ * callback was registered for, and the mpv_handles refer to different mpv
+ * instances.)
*
* Stream lifetime
* ---------------
@@ -196,7 +190,6 @@ typedef struct mpv_stream_cb_info {
* @param user_data opaque user data provided via mpv_stream_cb_add()
* @param uri name of the stream to be opened (with protocol prefix)
* @param info fields which the user should fill
- * @return opaque cookie identifing the newly opened stream
* @return 0 on success, MPV_ERROR_LOADING_FAILED if the URI cannot be opened.
*/
typedef int (*mpv_stream_cb_open_ro_fn)(void *user_data, char *uri,
diff --git a/misc/thread_pool.c b/misc/thread_pool.c
new file mode 100644
index 0000000..dddfad6
--- /dev/null
+++ b/misc/thread_pool.c
@@ -0,0 +1,125 @@
+/*
+ * This file is part of mpv.
+ *
+ * mpv is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * mpv is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <pthread.h>
+
+#include "common/common.h"
+
+#include "thread_pool.h"
+
+struct work {
+ void (*fn)(void *ctx);
+ void *fn_ctx;
+};
+
+struct mp_thread_pool {
+ pthread_t *threads;
+ int num_threads;
+
+ pthread_mutex_t lock;
+ pthread_cond_t wakeup;
+
+ // --- the following fields are protected by lock
+ bool terminate;
+ struct work *work;
+ int num_work;
+};
+
+static void *worker_thread(void *arg)
+{
+ struct mp_thread_pool *pool = arg;
+
+ pthread_mutex_lock(&pool->lock);
+ while (1) {
+ while (!pool->num_work && !pool->terminate)
+ pthread_cond_wait(&pool->wakeup, &pool->lock);
+
+ if (!pool->num_work && pool->terminate)
+ break;
+
+ assert(pool->num_work > 0);
+ struct work work = pool->work[pool->num_work - 1];
+ pool->num_work -= 1;
+
+ pthread_mutex_unlock(&pool->lock);
+ work.fn(work.fn_ctx);
+ pthread_mutex_lock(&pool->lock);
+ }
+ assert(pool->num_work == 0);
+ pthread_mutex_unlock(&pool->lock);
+
+ return NULL;
+}
+
+static void thread_pool_dtor(void *ctx)
+{
+ struct mp_thread_pool *pool = ctx;
+
+ pthread_mutex_lock(&pool->lock);
+ pool->terminate = true;
+ pthread_cond_broadcast(&pool->wakeup);
+ pthread_mutex_unlock(&pool->lock);
+
+ for (int n = 0; n < pool->num_threads; n++)
+ pthread_join(pool->threads[n], NULL);
+
+ assert(pool->num_work == 0);
+ pthread_cond_destroy(&pool->wakeup);
+ pthread_mutex_destroy(&pool->lock);
+}
+
+// Create a thread pool with the given number of worker threads. This can return
+// NULL if the worker threads could not be created. The thread pool can be
+// destroyed with talloc_free(pool), or indirectly with talloc_free(ta_parent).
+// If there are still work items on freeing, it will block until all work items
+// are done, and the threads terminate.
+struct mp_thread_pool *mp_thread_pool_create(void *ta_parent, int threads)
+{
+ assert(threads > 0);
+
+ struct mp_thread_pool *pool = talloc_zero(ta_parent, struct mp_thread_pool);
+ talloc_set_destructor(pool, thread_pool_dtor);
+
+ pthread_mutex_init(&pool->lock, NULL);
+ pthread_cond_init(&pool->wakeup, NULL);
+
+ for (int n = 0; n < threads; n++) {
+ pthread_t thread;
+ if (pthread_create(&thread, NULL, worker_thread, pool)) {
+ talloc_free(pool);
+ return NULL;
+ }
+ MP_TARRAY_APPEND(pool, pool->threads, pool->num_threads, thread);
+ }
+
+ return pool;
+}
+
+// Queue a function to be run on a worker thread: fn(fn_ctx)
+// If no worker thread is currently available, it's appended to a list in memory
+// with unbounded size. This function always returns immediately.
+// Concurrent queue calls are allowed, as long as it does not overlap with
+// pool destruction.
+void mp_thread_pool_queue(struct mp_thread_pool *pool, void (*fn)(void *ctx),
+ void *fn_ctx)
+{
+ pthread_mutex_lock(&pool->lock);
+ struct work work = {fn, fn_ctx};
+ MP_TARRAY_INSERT_AT(pool, pool->work, pool->num_work, 0, work);
+ pthread_cond_signal(&pool->wakeup);
+ pthread_mutex_unlock(&pool->lock);
+}
diff --git a/misc/thread_pool.h b/misc/thread_pool.h
new file mode 100644
index 0000000..c7af7b2
--- /dev/null
+++ b/misc/thread_pool.h
@@ -0,0 +1,10 @@
+#ifndef MPV_MP_THREAD_POOL_H
+#define MPV_MP_THREAD_POOL_H
+
+struct mp_thread_pool;
+
+struct mp_thread_pool *mp_thread_pool_create(void *ta_parent, int threads);
+void mp_thread_pool_queue(struct mp_thread_pool *pool, void (*fn)(void *ctx),
+ void *fn_ctx);
+
+#endif
diff --git a/options/m_config.c b/options/m_config.c
index 14aa56d..16a0b38 100644
--- a/options/m_config.c
+++ b/options/m_config.c
@@ -624,12 +624,16 @@ struct m_config_option *m_config_get_co(const struct m_config *config,
const char *prefix = config->is_toplevel ? "--" : "";
if (co->opt->type == &m_option_type_alias) {
const char *alias = (const char *)co->opt->priv;
- // deprecation_message is not used, but decides whether it's a
- // proper or deprecated alias.
if (co->opt->deprecation_message && !co->warning_was_printed) {
- MP_WARN(config, "Warning: option %s%s was replaced with "
- "%s%s and might be removed in the future.\n",
- prefix, co->name, prefix, alias);
+ if (co->opt->deprecation_message[0]) {
+ MP_WARN(config, "Warning: option %s%s was replaced with "
+ "%s%s: %s\n", prefix, co->name, prefix, alias,
+ co->opt->deprecation_message);
+ } else {
+ MP_WARN(config, "Warning: option %s%s was replaced with "
+ "%s%s and might be removed in the future.\n",
+ prefix, co->name, prefix, alias);
+ }
co->warning_was_printed = true;
}
return m_config_get_co(config, bstr0(alias));
diff --git a/options/m_option.c b/options/m_option.c
index 11bb677..fa93dd4 100644
--- a/options/m_option.c
+++ b/options/m_option.c
@@ -2047,6 +2047,8 @@ const m_option_type_t m_option_type_size_box = {
static int parse_imgfmt(struct mp_log *log, const m_option_t *opt,
struct bstr name, struct bstr param, void *dst)
{
+ bool accept_no = opt->min < 0;
+
if (param.len == 0)
return M_OPT_MISSING_PARAM;
@@ -2055,28 +2057,29 @@ static int parse_imgfmt(struct mp_log *log, const m_option_t *opt,
char **list = mp_imgfmt_name_list();
for (int i = 0; list[i]; i++)
mp_info(log, " %s", list[i]);
+ if (accept_no)
+ mp_info(log, " no");
mp_info(log, "\n");
talloc_free(list);
return M_OPT_EXIT;
}
unsigned int fmt = mp_imgfmt_from_name(param, true);
- if (!fmt) {
+ if (!fmt && !(accept_no && bstr_equals0(param, "no"))) {
mp_err(log, "Option %.*s: unknown format name: '%.*s'\n",
BSTR_P(name), BSTR_P(param));
return M_OPT_INVALID;
}
if (dst)
- *((uint32_t *)dst) = fmt;
+ *((int *)dst) = fmt;
return 1;
}
const m_option_type_t m_option_type_imgfmt = {
- // Please report any missing colorspaces
.name = "Image format",
- .size = sizeof(uint32_t),
+ .size = sizeof(int),
.parse = parse_imgfmt,
.copy = copy_opt,
};
@@ -2587,6 +2590,7 @@ static void copy_obj_settings_list(const m_option_t *opt, void *dst,
for (n = 0; s[n].name; n++) {
d[n].name = talloc_strdup(NULL, s[n].name);
d[n].label = talloc_strdup(NULL, s[n].label);
+ d[n].enabled = s[n].enabled;
d[n].attribs = NULL;
copy_str_list(NULL, &(d[n].attribs), &(s[n].attribs));
}
@@ -2602,17 +2606,33 @@ static void copy_obj_settings_list(const m_option_t *opt, void *dst,
static int get_obj_param(struct mp_log *log, bstr opt_name, bstr obj_name,
struct m_config *config, bstr name, bstr val,
int flags, bool nopos,
- int *nold, bstr *out_name, bstr *out_val)
+ int *nold, bstr *out_name, bstr *out_val,
+ char *tmp, size_t tmp_size)
{
int r;
if (!config) {
- *out_name = name; // preserve args for opengl-hq legacy handling
- *out_val = val;
+ // Duplicates the logic below, but with unknown parameter types/names.
+ if (val.start || nopos) {
+ *out_name = name;
+ *out_val = val;
+ } else {
+ val = name;
+ // positional fields
+ if (val.len == 0) { // Empty field, count it and go on
+ (*nold)++;
+ return 0;
+ }
+ // Positional naming convention for/followed by mp_set_avopts().
+ snprintf(tmp, tmp_size, "@%d", *nold);
+ *out_name = bstr0(tmp);
+ *out_val = val;
+ (*nold)++;
+ }
return 1;
}
- // va.start != NULL => of the form name=val (not positional)
+ // val.start != NULL => of the form name=val (not positional)
// If it's just "name", and the associated option exists and is a flag,
// don't accept it as positional argument.
if (val.start || m_config_option_requires_param(config, name) == 0 || nopos) {
@@ -2679,6 +2699,7 @@ static int m_obj_parse_sub_config(struct mp_log *log, struct bstr opt_name,
char **args = NULL;
int num_args = 0;
int r = 1;
+ char tmp[80];
if (ret) {
args = *ret;
@@ -2704,7 +2725,7 @@ static int m_obj_parse_sub_config(struct mp_log *log, struct bstr opt_name,
if (bstr_equals0(fname, "help"))
goto print_help;
r = get_obj_param(log, opt_name, name, config, fname, fval, flags,
- nopos, &nold, &fname, &fval);
+ nopos, &nold, &fname, &fval, tmp, sizeof(tmp));
if (r < 0)
goto exit;
@@ -2751,26 +2772,50 @@ exit:
#define NAMECH "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-"
// Parse one item, e.g. -vf a=b:c:d,e=f:g => parse a=b:c:d into "a" and "b:c:d"
-static int parse_obj_settings(struct mp_log *log, struct bstr opt,
+static int parse_obj_settings(struct mp_log *log, struct bstr opt, int op,
struct bstr *pstr, const struct m_obj_list *list,
m_obj_settings_t **_ret)
{
int r;
char **plist = NULL;
struct m_obj_desc desc;
+ bstr str = {0};
bstr label = {0};
bool nopos = list->disallow_positional_parameters;
+ bool enabled = true;
if (bstr_eatstart0(pstr, "@")) {
- if (!bstr_split_tok(*pstr, ":", &label, pstr)) {
+ bstr rest;
+ if (!bstr_split_tok(*pstr, ":", &label, &rest)) {
+ // "@labelname" is the special enable/disable toggle syntax
+ if (op == OP_TOGGLE) {
+ int idx = bstrspn(*pstr, NAMECH);
+ label = bstr_splice(*pstr, 0, idx);
+ if (label.len) {
+ *pstr = bstr_cut(*pstr, idx);
+ goto done;
+ }
+ }
mp_err(log, "Option %.*s: ':' expected after label.\n", BSTR_P(opt));
return M_OPT_INVALID;
}
+ *pstr = rest;
+ if (label.len == 0) {
+ mp_err(log, "Option %.*s: label name expected.\n", BSTR_P(opt));
+ return M_OPT_INVALID;
+ }
}
+ if (list->allow_disable_entries && bstr_eatstart0(pstr, "!"))
+ enabled = false;
+
bool has_param = false;
int idx = bstrspn(*pstr, NAMECH);
- bstr str = bstr_splice(*pstr, 0, idx);
+ str = bstr_splice(*pstr, 0, idx);
+ if (!str.len) {
+ mp_err(log, "Option %.*s: filter name expected.\n", BSTR_P(opt));
+ return M_OPT_INVALID;
+ }
*pstr = bstr_cut(*pstr, idx);
// video filters use "=", VOs use ":"
if (bstr_eatstart0(pstr, "=") || bstr_eatstart0(pstr, ":"))
@@ -2814,9 +2859,11 @@ static int parse_obj_settings(struct mp_log *log, struct bstr opt,
if (!_ret)
return 1;
+done: ;
m_obj_settings_t item = {
.name = bstrto0(NULL, str),
.label = bstrdup0(NULL, label),
+ .enabled = enabled,
.attribs = plist,
};
obj_settings_list_insert_at(_ret, -1, &item);
@@ -2961,7 +3008,7 @@ static int parse_obj_settings_list(struct mp_log *log, const m_option_t *opt,
if (op == OP_DEL)
r = parse_obj_settings_del(log, name, &param, dst, mark_del);
if (r == 0) {
- r = parse_obj_settings(log, name, &param, ol, dst ? &res : NULL);
+ r = parse_obj_settings(log, name, op, &param, ol, dst ? &res : NULL);
}
if (r < 0)
return r;
@@ -3014,12 +3061,25 @@ static int parse_obj_settings_list(struct mp_log *log, const m_option_t *opt,
talloc_free(res);
} else if (op == OP_TOGGLE) {
for (int n = 0; res && res[n].name; n++) {
- int found = obj_settings_find_by_content(list, &res[n]);
- if (found < 0) {
- obj_settings_list_insert_at(&list, -1, &res[n]);
- } else {
- obj_settings_list_del_at(&list, found);
+ if (res[n].label && !res[n].name[0]) {
+ // Toggle enable/disable special case.
+ int found =
+ obj_settings_list_find_by_label0(list, res[n].label);
+ if (found < 0) {
+ mp_warn(log, "Option %.*s: Label %s not found\n",
+ BSTR_P(name), res[n].label);
+ } else {
+ list[found].enabled = !list[found].enabled;
+ }
obj_setting_free(&res[n]);
+ } else {
+ int found = obj_settings_find_by_content(list, &res[n]);
+ if (found < 0) {
+ obj_settings_list_insert_at(&list, -1, &res[n]);
+ } else {
+ obj_settings_list_del_at(&list, found);
+ obj_setting_free(&res[n]);
+ }
}
}
talloc_free(res);
@@ -3070,6 +3130,8 @@ static char *print_obj_settings_list(const m_option_t *opt, const void *val)
// Assume labels and names don't need escaping
if (entry->label && entry->label[0])
res = talloc_asprintf_append(res, "@%s:", entry->label);
+ if (!entry->enabled)
+ res = talloc_strdup_append(res, "!");
res = talloc_strdup_append(res, entry->name);
if (entry->attribs && entry->attribs[0]) {
res = talloc_strdup_append(res, "=");
@@ -3094,6 +3156,7 @@ static int set_obj_settings_list(const m_option_t *opt, void *dst,
talloc_zero_array(NULL, m_obj_settings_t, src->u.list->num + 1);
for (int n = 0; n < src->u.list->num; n++) {
m_obj_settings_t *entry = &entries[n];
+ entry->enabled = true;
if (src->u.list->values[n].format != MPV_FORMAT_NODE_MAP)
goto error;
struct mpv_node_list *src_entry = src->u.list->values[n].u.list;
@@ -3108,6 +3171,10 @@ static int set_obj_settings_list(const m_option_t *opt, void *dst,
if (val->format != MPV_FORMAT_STRING)
goto error;
entry->label = talloc_strdup(NULL, val->u.string);
+ } else if (strcmp(key, "enabled") == 0) {
+ if (val->format != MPV_FORMAT_FLAG)
+ goto error;
+ entry->enabled = val->u.flag;
} else if (strcmp(key, "params") == 0) {
if (val->format != MPV_FORMAT_NODE_MAP)
goto error;
@@ -3173,6 +3240,9 @@ static int get_obj_settings_list(const m_option_t *opt, void *ta_parent,
add_map_string(nentry, "name", entry->name);
if (entry->label && entry->label[0])
add_map_string(nentry, "label", entry->label);
+ struct mpv_node *enabled = add_map_entry(nentry, "enabled");
+ enabled->format = MPV_FORMAT_FLAG;
+ enabled->u.flag = entry->enabled;
struct mpv_node *params = add_map_entry(nentry, "params");
params->format = MPV_FORMAT_NODE_MAP;
params->u.list = talloc_zero(ta_parent, struct mpv_node_list);
diff --git a/options/m_option.h b/options/m_option.h
index 8709d20..064b7f8 100644
--- a/options/m_option.h
+++ b/options/m_option.h
@@ -146,6 +146,8 @@ struct m_obj_list {
// Allow unknown entries, for which a dummy entry is inserted, and whose
// options are skipped and ignored.
bool allow_unknown_entries;
+ // Allow syntax for disabling entries.
+ bool allow_disable_entries;
// This helps with confusing error messages if unknown flag options are used.
bool disallow_positional_parameters;
// Each sub-item is backed by global options (for AOs and VOs).
@@ -162,6 +164,8 @@ typedef struct m_obj_settings {
char *name;
// Optional user-defined name.
char *label;
+ // User enable flag.
+ bool enabled;
// NULL terminated array of parameter/value pairs.
char **attribs;
} m_obj_settings_t;
@@ -711,9 +715,12 @@ extern const char m_option_path_separator;
// If "--optname" was removed, but "--newname" has the same semantics.
// It will be redirected, and a warning will be printed on first use.
-#define OPT_REPLACED(optname, newname) \
+#define OPT_REPLACED_MSG(optname, newname, msg) \
{.name = optname, .type = &m_option_type_alias, .priv = newname, \
- .deprecation_message = "", .offset = -1}
+ .deprecation_message = (msg), .offset = -1}
+
+// Same, with a generic deprecation message.
+#define OPT_REPLACED(optname, newname) OPT_REPLACED_MSG(optname, newname, "")
// "--optname" doesn't exist, but inform the user about a replacement with msg.
#define OPT_REMOVED(optname, msg) \
diff --git a/options/options.c b/options/options.c
index b711156..229f328 100644
--- a/options/options.c
+++ b/options/options.c
@@ -40,7 +40,6 @@
#include "stream/stream.h"
#include "video/csputils.h"
#include "video/hwdec.h"
-#include "video/out/opengl/hwdec.h"
#include "video/image_writer.h"
#include "sub/osd.h"
#include "audio/filter/af.h"
@@ -53,6 +52,10 @@
#include "video/out/drm_common.h"
#endif
+#if HAVE_GL
+#include "video/out/opengl/hwdec.h"
+#endif
+
extern const char mp_help_text[];
static void print_version(struct mp_log *log)
@@ -89,6 +92,7 @@ extern const struct m_obj_list vo_obj_list;
extern const struct m_obj_list ao_obj_list;
extern const struct m_sub_options angle_conf;
+extern const struct m_sub_options cocoa_conf;
const struct m_opt_choice_alternatives mp_hwdec_names[] = {
{"no", HWDEC_NONE},
@@ -157,6 +161,8 @@ static const m_option_t mp_vo_opt_list[] = {
OPT_FLAG("taskbar-progress", taskbar_progress, 0),
OPT_FLAG("snap-window", snap_window, 0),
OPT_FLAG("ontop", ontop, 0),
+ OPT_CHOICE_OR_INT("ontop-level", ontop_level, 0, 0, INT_MAX,
+ ({"window", -1}, {"system", -2})),
OPT_FLAG("border", border, 0),
OPT_FLAG("fit-border", fit_border, 0),
OPT_FLAG("on-all-workspaces", all_workspaces, 0),
@@ -230,6 +236,7 @@ const struct m_sub_options vo_sub_opts = {
.window_scale = 1.0,
.x11_bypass_compositor = 2,
.mmcss_profile = "Playback",
+ .ontop_level = -1,
},
};
@@ -298,7 +305,7 @@ const m_option_t mp_opts[] = {
#endif
OPT_FLAG("config", load_config, M_OPT_FIXED | CONF_PRE_PARSE),
OPT_STRING("config-dir", force_configdir,
- M_OPT_FIXED | CONF_NOCFG | CONF_PRE_PARSE),
+ M_OPT_FIXED | CONF_NOCFG | CONF_PRE_PARSE | M_OPT_FILE),
OPT_STRINGLIST("reset-on-next-file", reset_options, 0),
#if HAVE_LUA
@@ -347,6 +354,7 @@ const m_option_t mp_opts[] = {
({"no", 0},
{"yes", 1},
{"always", 2})),
+ OPT_FLAG("keep-open-pause", keep_open_pause, 0),
OPT_DOUBLE("image-display-duration", image_display_duration,
M_OPT_RANGE, 0, INFINITY),
@@ -441,7 +449,7 @@ const m_option_t mp_opts[] = {
OPT_CHOICE_C("hwdec", hwdec_api, 0, mp_hwdec_names),
OPT_STRING("hwdec-codecs", hwdec_codecs, 0),
#if HAVE_VIDEOTOOLBOX_HWACCEL
- OPT_IMAGEFORMAT("videotoolbox-format", videotoolbox_format, 0),
+ OPT_IMAGEFORMAT("videotoolbox-format", videotoolbox_format, 0, .min = -1),
#endif
// -1 means auto aspect (prefer container size until aspect change)
@@ -485,6 +493,8 @@ const m_option_t mp_opts[] = {
OPT_FLOATRANGE("sub-gauss", sub_gauss, UPDATE_OSD, 0.0, 3.0),
OPT_FLAG("sub-gray", sub_gray, UPDATE_OSD),
OPT_FLAG("sub-ass", ass_enabled, 0),
+ OPT_FLAG("sub-filter-sdh", sub_filter_SDH, 0),
+ OPT_FLAG("sub-filter-sdh-harder", sub_filter_SDH_harder, 0),
OPT_FLOATRANGE("sub-scale", sub_scale, UPDATE_OSD, 0, 100),
OPT_FLOATRANGE("sub-ass-line-spacing", ass_line_spacing, UPDATE_OSD, -1000, 1000),
OPT_FLAG("sub-use-margins", sub_use_margins, UPDATE_OSD),
@@ -598,7 +608,7 @@ const m_option_t mp_opts[] = {
OPT_FLAG("stop-playback-on-init-failure", stop_playback_on_init_failure, 0),
- OPT_CHOICE_OR_INT("loop", loop_times, 0, 1, 10000,
+ OPT_CHOICE_OR_INT("loop-playlist", loop_times, 0, 1, 10000,
({"no", 1},
{"inf", -1}, {"yes", -1},
{"force", -2})),
@@ -611,7 +621,7 @@ const m_option_t mp_opts[] = {
OPT_FLAG("save-position-on-quit", position_save_on_quit, 0),
OPT_FLAG("write-filename-in-watch-later-config", write_filename_in_watch_later_config, 0),
OPT_FLAG("ignore-path-in-watch-later-config", ignore_path_in_watch_later_config, 0),
- OPT_STRING("watch-later-directory", watch_later_directory, 0),
+ OPT_STRING("watch-later-directory", watch_later_directory, M_OPT_FILE),
OPT_FLAG("ordered-chapters", ordered_chapters, 0),
OPT_STRING("ordered-chapters-files", ordered_chapters_files, M_OPT_FILE),
@@ -679,7 +689,7 @@ const m_option_t mp_opts[] = {
OPT_SUBSTRUCT("screenshot", screenshot_image_opts, screenshot_conf, 0),
OPT_STRING("screenshot-template", screenshot_template, 0),
- OPT_STRING("screenshot-directory", screenshot_directory, 0),
+ OPT_STRING("screenshot-directory", screenshot_directory, M_OPT_FILE),
OPT_STRING("record-file", record_file, M_OPT_FILE),
@@ -702,6 +712,10 @@ const m_option_t mp_opts[] = {
OPT_SUBSTRUCT("", angle_opts, angle_conf, 0),
#endif
+#if HAVE_GL_COCOA
+ OPT_SUBSTRUCT("", cocoa_opts, cocoa_conf, 0),
+#endif
+
#if HAVE_GL_WIN32
OPT_CHOICE("opengl-dwmflush", wingl_dwm_flush, 0,
({"no", -1}, {"auto", 0}, {"windowed", 1}, {"yes", 2})),
@@ -819,6 +833,8 @@ const m_option_t mp_opts[] = {
OPT_REPLACED("ass-style-override", "sub-ass-style-override"),
OPT_REPLACED("ass-scale-with-window", "sub-ass-scale-with-window"),
OPT_REMOVED("fs-black-out-screens", NULL),
+ OPT_REPLACED_MSG("loop", "loop-playlist", "--loop will be changed to map to"
+ " --loop-file in future releases."),
{0}
};
@@ -896,6 +912,7 @@ const struct MPOpts mp_default_opts = {
.play_frames = -1,
.rebase_start_time = 1,
.keep_open = 0,
+ .keep_open_pause = 1,
.image_display_duration = 1.0,
.stream_id = { { [STREAM_AUDIO] = -1,
[STREAM_VIDEO] = -1,
@@ -916,7 +933,7 @@ const struct MPOpts mp_default_opts = {
.movie_aspect = -1.,
.field_dominance = -1,
.sub_auto = 0,
- .audiofile_auto = 0,
+ .audiofile_auto = -1,
.osd_bar_visible = 1,
#if HAVE_LIBASS
.ass_enabled = 1,
@@ -935,6 +952,11 @@ const struct MPOpts mp_default_opts = {
.hwdec_codecs = "h264,vc1,wmv3,hevc,mpeg2video,vp9",
.videotoolbox_format = IMGFMT_NV12,
+ .audio_output_channels = {
+ .set = 1,
+ .auto_safe = 1,
+ },
+
.index_mode = 1,
.mf_fps = 1.0,
diff --git a/options/options.h b/options/options.h
index f465c0f..82f0c15 100644
--- a/options/options.h
+++ b/options/options.h
@@ -12,6 +12,7 @@ typedef struct mp_vo_opts {
int taskbar_progress;
int snap_window;
int ontop;
+ int ontop_level;
int fullscreen;
int border;
int fit_border;
@@ -199,6 +200,7 @@ typedef struct MPOpts {
char *watch_later_directory;
int pause;
int keep_open;
+ int keep_open_pause;
double image_display_duration;
char *lavfi_complex;
int stream_id[2][STREAM_TYPE_COUNT];
@@ -269,6 +271,8 @@ typedef struct MPOpts {
float sub_scale;
float sub_gauss;
int sub_gray;
+ int sub_filter_SDH;
+ int sub_filter_SDH_harder;
int ass_enabled;
float ass_line_spacing;
int ass_use_margins;
@@ -326,6 +330,7 @@ typedef struct MPOpts {
struct gl_video_opts *gl_video_opts;
struct angle_opts *angle_opts;
+ struct cocoa_opts *cocoa_opts;
struct dvd_opts *dvd_opts;
} MPOpts;
diff --git a/options/parse_commandline.c b/options/parse_commandline.c
index b90912e..f51d19d 100644
--- a/options/parse_commandline.c
+++ b/options/parse_commandline.c
@@ -107,7 +107,7 @@ static bool split_opt(struct parse_state *p)
return false;
}
-#ifdef __MINGW32__
+#if defined(__MINGW32__) && (HAVE_GLOB || HAVE_GLOB_WIN32_REPLACEMENT)
static void process_non_option(struct playlist *files, const char *arg)
{
glob_t gg;
diff --git a/osdep/atomic.h b/osdep/atomic.h
index 50f4f40..1d3e158 100644
--- a/osdep/atomic.h
+++ b/osdep/atomic.h
@@ -27,15 +27,14 @@
#else
// Emulate the parts of C11 stdatomic.h needed by mpv.
-// Still relies on gcc/clang atomic builtins.
-typedef struct { volatile unsigned long v; } atomic_ulong;
-typedef struct { volatile int v; } atomic_int;
-typedef struct { volatile unsigned int v; } atomic_uint;
-typedef struct { volatile _Bool v; } atomic_bool;
-typedef struct { volatile long long v; } atomic_llong;
-typedef struct { volatile uint_least32_t v; } atomic_uint_least32_t;
-typedef struct { volatile unsigned long long v; } atomic_ullong;
+typedef struct { unsigned long v; } atomic_ulong;
+typedef struct { int v; } atomic_int;
+typedef struct { unsigned int v; } atomic_uint;
+typedef struct { _Bool v; } atomic_bool;
+typedef struct { long long v; } atomic_llong;
+typedef struct { uint_least32_t v; } atomic_uint_least32_t;
+typedef struct { unsigned long long v; } atomic_ullong;
#define ATOMIC_VAR_INIT(x) \
{.v = (x)}
@@ -45,24 +44,6 @@ typedef struct { volatile unsigned long long v; } atomic_ullong;
#define atomic_load_explicit(p, e) atomic_load(p)
-#if HAVE_ATOMIC_BUILTINS
-
-#define atomic_load(p) \
- __atomic_load_n(&(p)->v, __ATOMIC_SEQ_CST)
-#define atomic_store(p, val) \
- __atomic_store_n(&(p)->v, val, __ATOMIC_SEQ_CST)
-#define atomic_fetch_add(a, b) \
- __atomic_fetch_add(&(a)->v, b, __ATOMIC_SEQ_CST)
-#define atomic_fetch_and(a, b) \
- __atomic_fetch_and(&(a)->v, b, __ATOMIC_SEQ_CST)
-#define atomic_fetch_or(a, b) \
- __atomic_fetch_or(&(a)->v, b, __ATOMIC_SEQ_CST)
-#define atomic_compare_exchange_strong(a, b, c) \
- __atomic_compare_exchange_n(&(a)->v, b, c, 0, __ATOMIC_SEQ_CST, \
- __ATOMIC_SEQ_CST)
-
-#elif defined(__GNUC__)
-
#include <pthread.h>
extern pthread_mutex_t mp_atomic_mutex;
@@ -104,10 +85,6 @@ extern pthread_mutex_t mp_atomic_mutex;
pthread_mutex_unlock(&mp_atomic_mutex); \
res; })
-#else
-# error "this should have been a configuration error, report a bug please"
-#endif /* no atomics */
-
#endif /* else HAVE_STDATOMIC */
#endif
diff --git a/osdep/io.c b/osdep/io.c
index 2688918..260a998 100644
--- a/osdep/io.c
+++ b/osdep/io.c
@@ -335,6 +335,33 @@ int mp_mkdir(const char *path, int mode)
return res;
}
+char *mp_win32_getcwd(char *buf, size_t size)
+{
+ if (size >= SIZE_MAX / 3 - 1) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ size_t wbuffer = size * 3 + 1;
+ wchar_t *wres = talloc_array(NULL, wchar_t, wbuffer);
+ DWORD wlen = GetFullPathNameW(L".", wbuffer, wres, NULL);
+ if (wlen >= wbuffer || wlen == 0) {
+ talloc_free(wres);
+ errno = wlen ? ERANGE : ENOENT;
+ return NULL;
+ }
+ char *t = mp_to_utf8(NULL, wres);
+ talloc_free(wres);
+ size_t st = strlen(t);
+ if (st >= size) {
+ talloc_free(t);
+ errno = ERANGE;
+ return NULL;
+ }
+ memcpy(buf, t, st + 1);
+ talloc_free(t);
+ return buf;
+}
+
FILE *mp_tmpfile(void)
{
// Reserve a file name in the format %TMP%\mpvXXXX.TMP
diff --git a/osdep/io.h b/osdep/io.h
index 333ed4f..01c7c8b 100644
--- a/osdep/io.h
+++ b/osdep/io.h
@@ -75,6 +75,7 @@ DIR *mp_opendir(const char *path);
struct dirent *mp_readdir(DIR *dir);
int mp_closedir(DIR *dir);
int mp_mkdir(const char *path, int mode);
+char *mp_win32_getcwd(char *buf, size_t size);
FILE *mp_tmpfile(void);
char *mp_getenv(const char *name);
off_t mp_lseek(int fd, off_t offset, int whence);
@@ -121,6 +122,7 @@ void mp_globfree(mp_glob_t *pglob);
#define readdir(...) mp_readdir(__VA_ARGS__)
#define closedir(...) mp_closedir(__VA_ARGS__)
#define mkdir(...) mp_mkdir(__VA_ARGS__)
+#define getcwd(...) mp_win32_getcwd(__VA_ARGS__)
#define tmpfile(...) mp_tmpfile(__VA_ARGS__)
#define getenv(...) mp_getenv(__VA_ARGS__)
diff --git a/osdep/macosx_application.m b/osdep/macosx_application.m
index d312692..52dacb0 100644
--- a/osdep/macosx_application.m
+++ b/osdep/macosx_application.m
@@ -17,10 +17,12 @@
#include <stdio.h>
#include <pthread.h>
+#include "config.h"
#include "mpv_talloc.h"
#include "common/msg.h"
#include "input/input.h"
+#include "player/client.h"
#import "osdep/macosx_application_objc.h"
#include "osdep/macosx_compat.h"
@@ -28,6 +30,10 @@
#include "osdep/threads.h"
#include "osdep/main-fn.h"
+#if HAVE_MACOS_TOUCHBAR
+#import "osdep/macosx_touchbar.h"
+#endif
+
#define MPV_PROTOCOL @"mpv://"
// Whether the NSApplication singleton was created. If this is false, we are
@@ -75,8 +81,8 @@ static void terminate_cocoa_application(void)
- (void)sendEvent:(NSEvent *)event
{
- [super sendEvent:event];
-
+ if (![_eventsResponder processKeyEvent:event])
+ [super sendEvent:event];
[_eventsResponder wakeup];
}
@@ -101,9 +107,44 @@ static void terminate_cocoa_application(void)
NSAppleEventManager *em = [NSAppleEventManager sharedAppleEventManager];
[em removeEventHandlerForEventClass:kInternetEventClass
andEventID:kAEGetURL];
+ [em removeEventHandlerForEventClass:kCoreEventClass
+ andEventID:kAEQuitApplication];
[super dealloc];
}
+#if HAVE_MACOS_TOUCHBAR
+- (NSTouchBar *)makeTouchBar
+{
+ TouchBar *tBar = [[TouchBar alloc] init];
+ [tBar setApp:self];
+ tBar.delegate = tBar;
+ tBar.customizationIdentifier = customID;
+ tBar.defaultItemIdentifiers = @[play, previousItem, nextItem, seekBar];
+ tBar.customizationAllowedItemIdentifiers = @[play, seekBar, previousItem,
+ nextItem, previousChapter, nextChapter, cycleAudio, cycleSubtitle,
+ currentPosition, timeLeft];
+ return tBar;
+}
+
+- (void)toggleTouchBarMenu
+{
+ [NSApp toggleTouchBarCustomizationPalette:self];
+}
+#endif
+
+- (void)processEvent:(struct mpv_event *)event
+{
+#if HAVE_MACOS_TOUCHBAR
+ if ([self respondsToSelector:@selector(touchBar)])
+ [(TouchBar *)self.touchBar processEvent:event];
+#endif
+}
+
+- (void)queueCommand:(char *)cmd
+{
+ [_eventsResponder queueCommand:cmd];
+}
+
#define _R(P, T, E, K) \
{ \
NSMenuItem *tmp = [self menuItemWithParent:(P) title:(T) \
@@ -137,6 +178,15 @@ static void terminate_cocoa_application(void)
NSMenu *menu = [[NSMenu alloc] initWithTitle:@"Window"];
_R(menu, @"Minimize", @"m", MPM_MINIMIZE)
_R(menu, @"Zoom", @"z", MPM_ZOOM)
+
+#if HAVE_MACOS_TOUCHBAR
+ if ([self respondsToSelector:@selector(touchBar)]) {
+ [menu addItem:[NSMenuItem separatorItem]];
+ [self menuItemWithParent:menu title:@"Customize Touch Bar…"
+ action:@selector(toggleTouchBarMenu) keyEquivalent: @""];
+ }
+#endif
+
return [menu autorelease];
}
@@ -206,13 +256,17 @@ static void terminate_cocoa_application(void)
return [item autorelease];
}
-- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)theApp
+- (void)applicationWillFinishLaunching:(NSNotification *)notification
{
- return NSTerminateNow;
+ NSAppleEventManager *em = [NSAppleEventManager sharedAppleEventManager];
+ [em setEventHandler:self
+ andSelector:@selector(handleQuitEvent:withReplyEvent:)
+ forEventClass:kCoreEventClass
+ andEventID:kAEQuitApplication];
}
-- (void)handleQuitEvent:(NSAppleEventDescriptor*)e
- withReplyEvent:(NSAppleEventDescriptor*)r
+- (void)handleQuitEvent:(NSAppleEventDescriptor *)event
+ withReplyEvent:(NSAppleEventDescriptor *)replyEvent
{
[self stopPlayback];
}
@@ -304,69 +358,25 @@ static void macosx_redirect_output_to_logfile(const char *filename)
[pool release];
}
-static void get_system_version(int* major, int* minor, int* bugfix)
-{
- static dispatch_once_t once_token;
- static int s_major = 0;
- static int s_minor = 0;
- static int s_bugfix = 0;
- dispatch_once(&once_token, ^{
- NSString *version_plist =
- @"/System/Library/CoreServices/SystemVersion.plist";
- NSString *version_string =
- [NSDictionary dictionaryWithContentsOfFile:version_plist]
- [@"ProductVersion"];
- NSArray* versions = [version_string componentsSeparatedByString:@"."];
- int count = [versions count];
- if (count >= 1)
- s_major = [versions[0] intValue];
- if (count >= 2)
- s_minor = [versions[1] intValue];
- if (count >= 3)
- s_bugfix = [versions[2] intValue];
- });
- *major = s_major;
- *minor = s_minor;
- *bugfix = s_bugfix;
-}
-
-static bool is_psn_argument(char *psn_arg_to_check)
-{
- NSString *psn_arg = [NSString stringWithUTF8String:psn_arg_to_check];
- return [psn_arg hasPrefix:@"-psn_"];
-}
-
-static bool bundle_started_from_finder(int argc, char **argv)
+static bool bundle_started_from_finder()
{
- bool bundle_detected = [[NSBundle mainBundle] bundleIdentifier];
- int major, minor, bugfix;
- get_system_version(&major, &minor, &bugfix);
- bool without_psn = bundle_detected && argc==1;
- bool with_psn = bundle_detected && argc==2 && is_psn_argument(argv[1]);
+ NSDictionary *env = [[NSProcessInfo processInfo] environment];
+ NSString *is_bundle = [env objectForKey:@"MPVBUNDLE"];
- if ((major == 10) && (minor >= 9)) {
- // Looks like opening quarantined files from the finder inserts the
- // -psn argument while normal files do not. Hurr.
- return with_psn || without_psn;
- } else {
- return with_psn;
- }
+ return is_bundle ? [is_bundle boolValue] : false;
}
int cocoa_main(int argc, char *argv[])
{
@autoreleasepool {
application_instantiated = true;
+ [[EventsResponder sharedInstance] setIsApplication:YES];
struct playback_thread_ctx ctx = {0};
ctx.argc = &argc;
ctx.argv = &argv;
- if (bundle_started_from_finder(argc, argv)) {
- if (argc > 1) {
- argc = 1; // clears out -psn argument if present
- argv[1] = NULL;
- }
+ if (bundle_started_from_finder()) {
macosx_redirect_output_to_logfile("mpv");
init_cocoa_application(true);
} else {
diff --git a/osdep/macosx_application_objc.h b/osdep/macosx_application_objc.h
index 4741a14..8bbe26d 100644
--- a/osdep/macosx_application_objc.h
+++ b/osdep/macosx_application_objc.h
@@ -18,10 +18,15 @@
#import <Cocoa/Cocoa.h>
#include "osdep/macosx_application.h"
+struct mpv_event;
+
@interface Application : NSApplication
+
- (void)initialize_menu;
- (void)registerSelector:(SEL)selector forKey:(MPMenuKey)key;
- (void)stopPlayback;
+- (void)processEvent:(struct mpv_event *)event;
+- (void)queueCommand:(char *)cmd;
@property(nonatomic, retain) NSMutableDictionary *menuItems;
@property(nonatomic, retain) NSArray *files;
diff --git a/osdep/macosx_compat.h b/osdep/macosx_compat.h
index 9ef422b..1cb5018 100644
--- a/osdep/macosx_compat.h
+++ b/osdep/macosx_compat.h
@@ -37,8 +37,7 @@ static const NSEventType NSEventTypeSystemDefined = NSSystemDefined;
static const NSEventType NSEventTypeKeyDown = NSKeyDown;
static const NSEventType NSEventTypeKeyUp = NSKeyUp;
-static const NSEventMask NSEventMaskKeyDown = NSKeyDownMask;
-static const NSEventMask NSEventMaskKeyUp = NSKeyUpMask;
+static const NSEventMask NSEventMaskLeftMouseUp = NSLeftMouseUpMask;
#if (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_10)
typedef NSUInteger NSEventModifierFlags;
@@ -50,31 +49,4 @@ static const NSEventModifierFlags NSEventModifierFlagCommand = NSCommandKeyMask
static const NSEventModifierFlags NSEventModifierFlagOption = NSAlternateKeyMask;
#endif
-#if (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_8)
-@interface NSArray (SubscriptingAdditions)
-- (id)objectAtIndexedSubscript:(NSUInteger)index;
-@end
-
-@interface NSMutableArray (SubscriptingAdditions)
-- (void)setObject: (id)object atIndexedSubscript:(NSUInteger)index;
-@end
-
-@interface NSDictionary (SubscriptingAdditions)
-- (id)objectForKeyedSubscript:(id)key;
-@end
-
-@interface NSMutableDictionary (SubscriptingAdditions)
-- (void)setObject: (id)object forKeyedSubscript:(id)key;
-@end
-
-#if __has_feature(objc_bool)
- #define YES __objc_yes
- #define NO __objc_no
-#else
- #define YES ((BOOL)1)
- #define NO ((BOOL)0)
-#endif
-
-#endif
-
#endif /* MPV_MACOSX_COMPAT */
diff --git a/osdep/macosx_events.h b/osdep/macosx_events.h
index a6bfbfe..9da3cb2 100644
--- a/osdep/macosx_events.h
+++ b/osdep/macosx_events.h
@@ -22,13 +22,12 @@
#include "input/keycodes.h"
struct input_ctx;
+struct mpv_handle;
void cocoa_put_key(int keycode);
void cocoa_put_key_with_modifiers(int keycode, int modifiers);
void cocoa_put_key_event(void *event);
-void cocoa_start_event_monitor(void);
-
void cocoa_init_apple_remote(void);
void cocoa_uninit_apple_remote(void);
@@ -36,5 +35,6 @@ void cocoa_init_media_keys(void);
void cocoa_uninit_media_keys(void);
void cocoa_set_input_context(struct input_ctx *input_context);
+void cocoa_set_mpv_handle(struct mpv_handle *ctx);
#endif
diff --git a/osdep/macosx_events.m b/osdep/macosx_events.m
index 494c9aa..dbdef2f 100644
--- a/osdep/macosx_events.m
+++ b/osdep/macosx_events.m
@@ -28,18 +28,22 @@
#include "mpv_talloc.h"
#include "input/event.h"
#include "input/input.h"
+#include "player/client.h"
#include "input/keycodes.h"
// doesn't make much sense, but needed to access keymap functionality
#include "video/out/vo.h"
#include "osdep/macosx_compat.h"
#import "osdep/macosx_events_objc.h"
+#import "osdep/macosx_application_objc.h"
#include "config.h"
@interface EventsResponder ()
{
struct input_ctx *_inputContext;
+ struct mpv_handle *_ctx;
+ BOOL _is_application;
NSCondition *_input_lock;
CFMachPortRef _mk_tap_port;
#if HAVE_APPLE_REMOTE
@@ -49,7 +53,8 @@
- (BOOL)handleMediaKey:(NSEvent *)event;
- (NSEvent *)handleKey:(NSEvent *)event;
-- (void)startEventMonitor;
+- (BOOL)setMpvHandle:(struct mpv_handle *)ctx;
+- (void)readEvents;
- (void)startAppleRemote;
- (void)stopAppleRemote;
- (void)startMediaKeys;
@@ -117,11 +122,6 @@ static int convert_key(unsigned key, unsigned charcode)
return charcode;
}
-void cocoa_start_event_monitor(void)
-{
- [[EventsResponder sharedInstance] startEventMonitor];
-}
-
void cocoa_init_apple_remote(void)
{
[[EventsResponder sharedInstance] startAppleRemote];
@@ -210,6 +210,21 @@ void cocoa_set_input_context(struct input_ctx *input_context)
[[EventsResponder sharedInstance] setInputContext:input_context];
}
+static void wakeup(void *context)
+{
+ [[EventsResponder sharedInstance] readEvents];
+}
+
+void cocoa_set_mpv_handle(struct mpv_handle *ctx)
+{
+ if ([[EventsResponder sharedInstance] setMpvHandle:ctx]) {
+ mpv_observe_property(ctx, 0, "duration", MPV_FORMAT_DOUBLE);
+ mpv_observe_property(ctx, 0, "time-pos", MPV_FORMAT_DOUBLE);
+ mpv_observe_property(ctx, 0, "pause", MPV_FORMAT_FLAG);
+ mpv_set_wakeup_callback(ctx, wakeup, NULL);
+ }
+}
+
@implementation EventsResponder
+ (EventsResponder *)sharedInstance
@@ -286,17 +301,47 @@ void cocoa_set_input_context(struct input_ctx *input_context)
return r;
}
-- (void)startEventMonitor
+- (void)setIsApplication:(BOOL)isApplication
+{
+ _is_application = isApplication;
+}
+
+- (BOOL)setMpvHandle:(struct mpv_handle *)ctx
+{
+ if (_is_application) {
+ dispatch_sync(dispatch_get_main_queue(), ^{ _ctx = ctx; });
+ return YES;
+ } else {
+ mpv_detach_destroy(ctx);
+ return NO;
+ }
+}
+
+- (void)readEvents
{
- [NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskKeyDown|NSEventMaskKeyUp
- handler:^(NSEvent *event) {
- BOOL equivalent = [[NSApp mainMenu] performKeyEquivalent:event];
- if (equivalent) {
- return (NSEvent *)nil;
- } else {
- return [self handleKey:event];
+ dispatch_async(dispatch_get_main_queue(), ^{
+ while (_ctx) {
+ mpv_event *event = mpv_wait_event(_ctx, 0);
+ if (event->event_id == MPV_EVENT_NONE)
+ break;
+ [self processEvent:event];
}
- }];
+ });
+}
+
+-(void)processEvent:(struct mpv_event *)event
+{
+ switch (event->event_id) {
+ case MPV_EVENT_SHUTDOWN: {
+ mpv_detach_destroy(_ctx);
+ _ctx = nil;
+ break;
+ }
+ }
+
+ if(_is_application) {
+ [NSApp processEvent:event];
+ }
}
- (void)startAppleRemote
@@ -358,9 +403,11 @@ void cocoa_set_input_context(struct input_ctx *input_context)
- (BOOL)handleMediaKey:(NSEvent *)event
{
NSDictionary *keymapd = @{
- @(NX_KEYTYPE_PLAY): @(MP_KEY_PLAY),
- @(NX_KEYTYPE_REWIND): @(MP_KEY_PREV),
- @(NX_KEYTYPE_FAST): @(MP_KEY_NEXT),
+ @(NX_KEYTYPE_PLAY): @(MP_KEY_PLAY),
+ @(NX_KEYTYPE_REWIND): @(MP_KEY_PREV),
+ @(NX_KEYTYPE_FAST): @(MP_KEY_NEXT),
+ @(NX_KEYTYPE_PREVIOUS): @(MP_KEY_REWIND),
+ @(NX_KEYTYPE_NEXT): @(MP_KEY_FORWARD),
};
return [self handleKey:mk_code(event)
@@ -464,6 +511,16 @@ void cocoa_set_input_context(struct input_ctx *input_context)
return nil;
}
+- (bool)processKeyEvent:(NSEvent *)event
+{
+ if (event.type == NSEventTypeKeyDown || event.type == NSEventTypeKeyUp){
+ if (![[NSApp mainMenu] performKeyEquivalent:event])
+ [self handleKey:event];
+ return true;
+ }
+ return false;
+}
+
- (void)handleFilesArray:(NSArray *)files
{
enum mp_dnd_action action = [NSEvent modifierFlags] &
diff --git a/osdep/macosx_events_objc.h b/osdep/macosx_events_objc.h
index 70a058e..ff5db93 100644
--- a/osdep/macosx_events_objc.h
+++ b/osdep/macosx_events_objc.h
@@ -29,6 +29,8 @@ struct input_ctx;
- (void)setInputContext:(struct input_ctx *)ctx;
+- (void)setIsApplication:(BOOL)isApplication;
+
/// Blocks until inputContext is present.
- (void)waitForInputContext;
@@ -40,4 +42,6 @@ struct input_ctx;
- (void)handleFilesArray:(NSArray *)files;
+- (bool)processKeyEvent:(NSEvent *)event;
+
@end
diff --git a/osdep/macosx_touchbar.h b/osdep/macosx_touchbar.h
new file mode 100644
index 0000000..9a56118
--- /dev/null
+++ b/osdep/macosx_touchbar.h
@@ -0,0 +1,45 @@
+/*
+ * 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/>.
+ */
+
+#import <Cocoa/Cocoa.h>
+#import "osdep/macosx_application_objc.h"
+
+#define BASE_ID @"io.mpv.touchbar"
+static NSTouchBarCustomizationIdentifier customID = BASE_ID;
+static NSTouchBarItemIdentifier seekBar = BASE_ID ".seekbar";
+static NSTouchBarItemIdentifier play = BASE_ID ".play";
+static NSTouchBarItemIdentifier nextItem = BASE_ID ".nextItem";
+static NSTouchBarItemIdentifier previousItem = BASE_ID ".previousItem";
+static NSTouchBarItemIdentifier nextChapter = BASE_ID ".nextChapter";
+static NSTouchBarItemIdentifier previousChapter = BASE_ID ".previousChapter";
+static NSTouchBarItemIdentifier cycleAudio = BASE_ID ".cycleAudio";
+static NSTouchBarItemIdentifier cycleSubtitle = BASE_ID ".cycleSubtitle";
+static NSTouchBarItemIdentifier currentPosition = BASE_ID ".currentPosition";
+static NSTouchBarItemIdentifier timeLeft = BASE_ID ".timeLeft";
+
+struct mpv_event;
+
+@interface TouchBar : NSTouchBar <NSTouchBarDelegate>
+
+-(void)processEvent:(struct mpv_event *)event;
+
+@property(nonatomic, retain) Application *app;
+@property(nonatomic, retain) NSDictionary *touchbarItems;
+@property(nonatomic, assign) double duration;
+@property(nonatomic, assign) double position;
+
+@end
diff --git a/osdep/macosx_touchbar.m b/osdep/macosx_touchbar.m
new file mode 100644
index 0000000..846ce6a
--- /dev/null
+++ b/osdep/macosx_touchbar.m
@@ -0,0 +1,272 @@
+/*
+ * 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 "player/client.h"
+#import "macosx_touchbar.h"
+
+@implementation TouchBar
+
+@synthesize app = _app;
+@synthesize touchbarItems = _touchbar_items;
+@synthesize duration = _duration;
+@synthesize position = _position;
+
+- (id)init
+{
+ if (self = [super init]) {
+ self.touchbarItems = @{
+ seekBar: [NSMutableDictionary dictionaryWithDictionary:@{
+ @"type": @"slider",
+ @"name": @"Seek Bar",
+ @"cmd": @"seek %f absolute-percent"
+ }],
+ play: [NSMutableDictionary dictionaryWithDictionary:@{
+ @"type": @"button",
+ @"name": @"Play Button",
+ @"cmd": @"cycle pause",
+ @"image": [NSImage imageNamed:NSImageNameTouchBarPauseTemplate],
+ @"imageAlt": [NSImage imageNamed:NSImageNameTouchBarPlayTemplate]
+ }],
+ previousItem: [NSMutableDictionary dictionaryWithDictionary:@{
+ @"type": @"button",
+ @"name": @"Previous Playlist Item",
+ @"cmd": @"playlist-prev",
+ @"image": [NSImage imageNamed:NSImageNameTouchBarGoBackTemplate]
+ }],
+ nextItem: [NSMutableDictionary dictionaryWithDictionary:@{
+ @"type": @"button",
+ @"name": @"Next Playlist Item",
+ @"cmd": @"playlist-next",
+ @"image": [NSImage imageNamed:NSImageNameTouchBarGoForwardTemplate]
+ }],
+ previousChapter: [NSMutableDictionary dictionaryWithDictionary:@{
+ @"type": @"button",
+ @"name": @"Previous Chapter",
+ @"cmd": @"add chapter -1",
+ @"image": [NSImage imageNamed:NSImageNameTouchBarSkipBackTemplate]
+ }],
+ nextChapter: [NSMutableDictionary dictionaryWithDictionary:@{
+ @"type": @"button",
+ @"name": @"Next Chapter",
+ @"cmd": @"add chapter 1",
+ @"image": [NSImage imageNamed:NSImageNameTouchBarSkipAheadTemplate]
+ }],
+ cycleAudio: [NSMutableDictionary dictionaryWithDictionary:@{
+ @"type": @"button",
+ @"name": @"Cycle Audio",
+ @"cmd": @"cycle audio",
+ @"image": [NSImage imageNamed:NSImageNameTouchBarAudioInputTemplate]
+ }],
+ cycleSubtitle: [NSMutableDictionary dictionaryWithDictionary:@{
+ @"type": @"button",
+ @"name": @"Cycle Subtitle",
+ @"cmd": @"cycle sub",
+ @"image": [NSImage imageNamed:NSImageNameTouchBarComposeTemplate]
+ }],
+ currentPosition: [NSMutableDictionary dictionaryWithDictionary:@{
+ @"type": @"text",
+ @"name": @"Current Position"
+ }],
+ timeLeft: [NSMutableDictionary dictionaryWithDictionary:@{
+ @"type": @"text",
+ @"name": @"Time Left"
+ }]
+ };
+ }
+ return self;
+}
+
+-(void)processEvent:(struct mpv_event *)event
+{
+ switch (event->event_id) {
+ case MPV_EVENT_END_FILE: {
+ self.position = 0;
+ self.duration = 0;
+ break;
+ }
+ case MPV_EVENT_PROPERTY_CHANGE: {
+ [self handlePropertyChange:(mpv_event_property *)event->data];
+ break;
+ }
+ }
+}
+
+-(void)handlePropertyChange:(struct mpv_event_property *)property
+{
+ NSString *name = [NSString stringWithUTF8String:property->name];
+ mpv_format format = property->format;
+
+ if ([name isEqualToString:@"time-pos"] && format == MPV_FORMAT_DOUBLE) {
+ self.position = *(double *)property->data;
+ self.position = self.position < 0 ? 0 : self.position;
+ [self updateTouchBarTimeItems];
+ } else if ([name isEqualToString:@"duration"] && format == MPV_FORMAT_DOUBLE) {
+ self.duration = *(double *)property->data;
+ [self updateTouchBarTimeItems];
+ } else if ([name isEqualToString:@"pause"] && format == MPV_FORMAT_FLAG) {
+ NSButton *playButton = self.touchbarItems[play][@"view"];
+ if (*(int *)property->data) {
+ playButton.image = self.touchbarItems[play][@"imageAlt"];
+ } else {
+ playButton.image = self.touchbarItems[play][@"image"];
+ }
+ }
+}
+
+- (nullable NSTouchBarItem *)touchBar:(NSTouchBar *)touchBar
+ makeItemForIdentifier:(NSTouchBarItemIdentifier)identifier
+{
+ if ([self.touchbarItems[identifier][@"type"] isEqualToString:@"slider"]) {
+ NSSliderTouchBarItem *tbItem = [[NSSliderTouchBarItem alloc] initWithIdentifier:identifier];
+ tbItem.slider.minValue = 0.0f;
+ tbItem.slider.maxValue = 100.0f;
+ tbItem.target = self;
+ tbItem.action = @selector(seekbarChanged:);
+ tbItem.customizationLabel = self.touchbarItems[identifier][@"name"];
+ [self.touchbarItems[identifier] setObject:tbItem.slider forKey:@"view"];
+ return tbItem;
+ } else if ([self.touchbarItems[identifier][@"type"] isEqualToString:@"button"]) {
+ NSCustomTouchBarItem *tbItem = [[NSCustomTouchBarItem alloc] initWithIdentifier:identifier];
+ NSImage *tbImage = self.touchbarItems[identifier][@"image"];
+ NSButton *tbButton = [NSButton buttonWithImage:tbImage target:self action:@selector(buttonAction:)];
+ tbItem.view = tbButton;
+ tbItem.customizationLabel = self.touchbarItems[identifier][@"name"];
+ [self.touchbarItems[identifier] setObject:tbButton forKey:@"view"];
+ return tbItem;
+ } else if ([self.touchbarItems[identifier][@"type"] isEqualToString:@"text"]) {
+ NSCustomTouchBarItem *tbItem = [[NSCustomTouchBarItem alloc] initWithIdentifier:identifier];
+ NSTextField *tbText = [NSTextField labelWithString:@"0:00"];
+ tbText.alignment = NSTextAlignmentCenter;
+ tbItem.view = tbText;
+ tbItem.customizationLabel = self.touchbarItems[identifier][@"name"];
+ [self.touchbarItems[identifier] setObject:tbText forKey:@"view"];
+ return tbItem;
+ }
+
+ return nil;
+}
+
+- (NSString *)formatTime:(int)time
+{
+ int seconds = time % 60;
+ int minutes = (time / 60) % 60;
+ int hours = time / (60 * 60);
+
+ NSString *stime = hours > 0 ? [NSString stringWithFormat:@"%d:", hours] : @"";
+ stime = (stime.length > 0 || minutes > 9) ?
+ [NSString stringWithFormat:@"%@%02d:", stime, minutes] :
+ [NSString stringWithFormat:@"%d:", minutes];
+ stime = [NSString stringWithFormat:@"%@%02d", stime, seconds];
+
+ return stime;
+}
+
+- (void)removeConstraintForIdentifier:(NSTouchBarItemIdentifier)identifier
+{
+ NSTextField *field = self.touchbarItems[identifier][@"view"];
+ [field removeConstraint:self.touchbarItems[identifier][@"constrain"]];
+}
+
+- (void)applyConstraintFromString:(NSString *)string
+ forIdentifier:(NSTouchBarItemIdentifier)identifier
+{
+ NSTextField *field = self.touchbarItems[identifier][@"view"];
+ if (field) {
+ NSString *fString = [[string componentsSeparatedByCharactersInSet:
+ [NSCharacterSet decimalDigitCharacterSet]] componentsJoinedByString:@"0"];
+ NSTextField *textField = [NSTextField labelWithString:fString];
+ NSSize size = [textField frame].size;
+
+ NSLayoutConstraint *con =
+ [NSLayoutConstraint constraintWithItem:field
+ attribute:NSLayoutAttributeWidth
+ relatedBy:NSLayoutRelationEqual
+ toItem:nil
+ attribute:NSLayoutAttributeNotAnAttribute
+ multiplier:1.0
+ constant:(int)ceil(size.width*1.1)];
+ [field addConstraint:con];
+ [self.touchbarItems[identifier] setObject:con forKey:@"constrain"];
+ }
+}
+
+- (void)updateTouchBarTimeItemConstrains
+{
+ [self removeConstraintForIdentifier:currentPosition];
+ [self removeConstraintForIdentifier:timeLeft];
+
+ if (self.duration <= 0) {
+ [self applyConstraintFromString:[self formatTime:self.position]
+ forIdentifier:currentPosition];
+ } else {
+ NSString *durFormat = [self formatTime:self.duration];
+
+ [self applyConstraintFromString:durFormat forIdentifier:currentPosition];
+ [self applyConstraintFromString:[NSString stringWithFormat:@"-%@", durFormat]
+ forIdentifier:timeLeft];
+ }
+}
+
+- (void)updateTouchBarTimeItems
+{
+ NSSlider *seekSlider = self.touchbarItems[seekBar][@"view"];
+ NSTextField *curPosItem = self.touchbarItems[currentPosition][@"view"];
+ NSTextField *timeLeftItem = self.touchbarItems[timeLeft][@"view"];
+
+ if (self.duration <= 0) {
+ seekSlider.enabled = NO;
+ seekSlider.doubleValue = 0;
+ timeLeftItem.stringValue = @"";
+ }
+ else {
+ seekSlider.enabled = YES;
+ if (!seekSlider.highlighted)
+ seekSlider.doubleValue = (self.position/self.duration)*100;
+ int left = (int)(floor(self.duration)-floor(self.position));
+ NSString *leftFormat = [self formatTime:left];
+ timeLeftItem.stringValue = [NSString stringWithFormat:@"-%@", leftFormat];
+ }
+ NSString *posFormat = [self formatTime:(int)floor(self.position)];
+ curPosItem.stringValue = posFormat;
+
+ [self updateTouchBarTimeItemConstrains];
+}
+
+- (NSString *)getIdentifierFromView:(id)view
+{
+ NSString *identifier;
+ for (identifier in self.touchbarItems)
+ if([self.touchbarItems[identifier][@"view"] isEqual:view])
+ break;
+ return identifier;
+}
+
+- (void)buttonAction:(NSButton *)sender
+{
+ NSString *identifier = [self getIdentifierFromView:sender];
+ [self.app queueCommand:(char *)[self.touchbarItems[identifier][@"cmd"] UTF8String]];
+}
+
+- (void)seekbarChanged:(NSSliderTouchBarItem *)sender
+{
+ NSString *identifier = [self getIdentifierFromView:sender.slider];
+ NSString *seek = [NSString stringWithFormat:
+ self.touchbarItems[identifier][@"cmd"], sender.slider.doubleValue];
+ [self.app queueCommand:(char *)[seek UTF8String]];
+}
+
+@end
diff --git a/osdep/macosx_versions.h b/osdep/macosx_versions.h
index f4c49ab..0a03a35 100644
--- a/osdep/macosx_versions.h
+++ b/osdep/macosx_versions.h
@@ -18,10 +18,6 @@
#ifndef MPV_MACOSX_VERSIONS
#define MPV_MACOSX_VERSIONS
-#if !defined(MAC_OS_X_VERSION_10_8)
-# define MAC_OS_X_VERSION_10_8 1080
-#endif
-
#if !defined(MAC_OS_X_VERSION_10_9)
# define MAC_OS_X_VERSION_10_9 1090
#endif
diff --git a/osdep/semaphore_osx.c b/osdep/semaphore_osx.c
index 1bff233..1e2015b 100644
--- a/osdep/semaphore_osx.c
+++ b/osdep/semaphore_osx.c
@@ -1,4 +1,6 @@
-/* Permission to use, copy, modify, and/or distribute this software for any
+/* Copyright (C) 2017 the mpv developers
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
diff --git a/osdep/win32/include/pthread.h b/osdep/win32/include/pthread.h
index e1324a0..870e9d7 100644
--- a/osdep/win32/include/pthread.h
+++ b/osdep/win32/include/pthread.h
@@ -1,3 +1,18 @@
+/* Copyright (C) 2017 the mpv developers
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
#ifndef MP_WRAP_PTHREAD_H_
#define MP_WRAP_PTHREAD_H_
diff --git a/osdep/win32/include/semaphore.h b/osdep/win32/include/semaphore.h
index 49670fc..bc0ed2e 100644
--- a/osdep/win32/include/semaphore.h
+++ b/osdep/win32/include/semaphore.h
@@ -1,3 +1,18 @@
+/* Copyright (C) 2017 the mpv developers
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
#ifndef MP_WRAP_SEMAPHORE_H_
#define MP_WRAP_SEMAPHORE_H_
diff --git a/osdep/win32/pthread.c b/osdep/win32/pthread.c
index b215e46..4f42cc3 100644
--- a/osdep/win32/pthread.c
+++ b/osdep/win32/pthread.c
@@ -1,4 +1,6 @@
-/* Permission to use, copy, modify, and/or distribute this software for any
+/* Copyright (C) 2017 the mpv developers
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
@@ -10,6 +12,7 @@
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+
#include <pthread.h>
#include <semaphore.h>
diff --git a/player/audio.c b/player/audio.c
index e176614..d05cae8 100644
--- a/player/audio.c
+++ b/player/audio.c
@@ -135,7 +135,8 @@ void audio_update_volume(struct MPContext *mpctx)
if (gain == 1.0)
return;
MP_VERBOSE(mpctx, "Inserting volume filter.\n");
- if (!(af_add(ao_c->af, "volume", "softvol", NULL)
+ char *args[] = {"warn", "no", NULL};
+ if (!(af_add(ao_c->af, "volume", "softvol", args)
&& af_control_any_rev(ao_c->af, AF_CONTROL_SET_VOLUME, &gain)))
MP_ERR(mpctx, "No volume control available.\n");
}
@@ -1105,8 +1106,10 @@ void fill_audio_out_buffers(struct MPContext *mpctx)
// we trigger EOF immediately, and let it play asynchronously.
if (ao_eof_reached(mpctx->ao) || opts->gapless_audio) {
mpctx->audio_status = STATUS_EOF;
- if (!was_eof)
+ if (!was_eof) {
+ MP_VERBOSE(mpctx, "audio EOF reached\n");
mp_wakeup_core(mpctx);
+ }
}
}
}
diff --git a/player/client.c b/player/client.c
index 31a5533..e01f121 100644
--- a/player/client.c
+++ b/player/client.c
@@ -1,4 +1,6 @@
-/* Permission to use, copy, modify, and/or distribute this software for any
+/* Copyright (C) 2017 the mpv developers
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
@@ -366,6 +368,9 @@ void mpv_detach_destroy(mpv_handle *ctx)
// causes a crash, block until all asynchronous requests were served.
mpv_wait_async_requests(ctx);
+ osd_set_external(ctx->mpctx->osd, ctx, 0, 0, NULL);
+ mp_input_remove_sections_by_owner(ctx->mpctx->input, ctx->name);
+
struct mp_client_api *clients = ctx->clients;
pthread_mutex_lock(&clients->lock);
@@ -378,8 +383,6 @@ void mpv_detach_destroy(mpv_handle *ctx)
ctx->num_events--;
}
mp_msg_log_buffer_destroy(ctx->messages);
- osd_set_external(ctx->mpctx->osd, ctx, 0, 0, NULL);
- mp_input_remove_sections_by_owner(ctx->mpctx->input, ctx->name);
pthread_cond_destroy(&ctx->wakeup);
pthread_mutex_destroy(&ctx->wakeup_lock);
pthread_mutex_destroy(&ctx->lock);
diff --git a/player/command.c b/player/command.c
index cde67eb..73081e8 100644
--- a/player/command.c
+++ b/player/command.c
@@ -354,7 +354,7 @@ int mp_on_set_option(void *ctx, struct m_config_option *co, void *data, int flag
bstr bname = bstr0(name);
char tmp[50];
if (bstr_eatend0(&bname, "*")) {
- snprintf(tmp, sizeof(name), "%.*s", BSTR_P(bname));
+ snprintf(tmp, sizeof(tmp), "%.*s", BSTR_P(bname));
name = tmp;
}
@@ -1493,11 +1493,7 @@ static int mp_property_pause(void *ctx, struct m_property *prop,
MPContext *mpctx = ctx;
if (mpctx->playback_initialized && action == M_PROPERTY_SET) {
- if (*(int *)arg) {
- pause_player(mpctx);
- } else {
- unpause_player(mpctx);
- }
+ set_pause_state(mpctx, *(int *)arg);
return M_PROPERTY_OK;
}
return mp_property_generic_option(mpctx, prop, action, arg);
@@ -1507,8 +1503,7 @@ static int mp_property_core_idle(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
- bool idle = mpctx->paused || !mpctx->restart_complete || !mpctx->playing;
- return m_property_flag_ro(action, arg, idle);
+ return m_property_flag_ro(action, arg, !mpctx->playback_active);
}
static int mp_property_idle(void *ctx, struct m_property *prop,
@@ -1723,6 +1718,16 @@ static int mp_property_demuxer_cache_idle(void *ctx, struct m_property *prop,
return m_property_flag_ro(action, arg, s.idle);
}
+static int mp_property_demuxer_start_time(void *ctx, struct m_property *prop,
+ int action, void *arg)
+{
+ MPContext *mpctx = ctx;
+ if (!mpctx->demuxer)
+ return M_PROPERTY_UNAVAILABLE;
+
+ return m_property_double_ro(action, arg, mpctx->demuxer->start_time);
+}
+
static int mp_property_paused_for_cache(void *ctx, struct m_property *prop,
int action, void *arg)
{
@@ -1742,6 +1747,17 @@ static int mp_property_cache_buffering(void *ctx, struct m_property *prop,
return m_property_int_ro(action, arg, state);
}
+static int mp_property_demuxer_is_network(void *ctx, struct m_property *prop,
+ int action, void *arg)
+{
+ MPContext *mpctx = ctx;
+ if (!mpctx->demuxer)
+ return M_PROPERTY_UNAVAILABLE;
+
+ return m_property_flag_ro(action, arg, mpctx->demuxer->is_network);
+}
+
+
static int mp_property_clock(void *ctx, struct m_property *prop,
int action, void *arg)
{
@@ -2214,7 +2230,7 @@ static int get_track_entry(int item, int action, void *arg, void *ctx)
if (track->d_audio)
decoder_desc = track->d_audio->decoder_desc;
- bool has_rg = track->stream->codec->replaygain_data;
+ bool has_rg = track->stream && track->stream->codec->replaygain_data;
struct replaygain_data rg = has_rg ? *track->stream->codec->replaygain_data
: (struct replaygain_data){0};
@@ -3081,17 +3097,7 @@ static int mp_property_sub_speed(void *ctx, struct m_property *prop,
{
MPContext *mpctx = ctx;
struct MPOpts *opts = mpctx->opts;
- switch (action) {
- case M_PROPERTY_SET: {
- opts->sub_speed = *(float *)arg;
- struct track *track = mpctx->current_track[0][STREAM_SUB];
- struct dec_sub *sub = track ? track->d_sub : NULL;
- if (sub) {
- sub_control(track->d_sub, SD_CTRL_UPDATE_SPEED, NULL);
- }
- return M_PROPERTY_OK;
- }
- case M_PROPERTY_PRINT:
+ if (action == M_PROPERTY_PRINT) {
*(char **)arg = talloc_asprintf(NULL, "%4.1f%%", 100 * opts->sub_speed);
return M_PROPERTY_OK;
}
@@ -3428,7 +3434,10 @@ static char *print_obj_osd_list(struct m_obj_settings *list)
list[n].attribs[i],
list[n].attribs[i + 1]);
}
- res = talloc_asprintf_append(res, "]\n");
+ res = talloc_asprintf_append(res, "]");
+ if (!list[n].enabled)
+ res = talloc_strdup_append(res, " (disabled)");
+ res = talloc_strdup_append(res, "\n");
}
if (!res)
res = talloc_strdup(NULL, "(empty)");
@@ -3904,8 +3913,10 @@ static const struct m_property mp_properties_base[] = {
{"demuxer-cache-duration", mp_property_demuxer_cache_duration},
{"demuxer-cache-time", mp_property_demuxer_cache_time},
{"demuxer-cache-idle", mp_property_demuxer_cache_idle},
+ {"demuxer-start-time", mp_property_demuxer_start_time},
{"cache-buffering-state", mp_property_cache_buffering},
{"paused-for-cache", mp_property_paused_for_cache},
+ {"demuxer-via-network", mp_property_demuxer_is_network},
{"clock", mp_property_clock},
{"seekable", mp_property_seekable},
{"partially-seekable", mp_property_partially_seekable},
@@ -4081,8 +4092,8 @@ static const char *const *const mp_event_property_change[] = {
E(MPV_EVENT_TRACK_SWITCHED, "vid", "video", "aid", "audio", "sid", "sub",
"secondary-sid"),
E(MPV_EVENT_IDLE, "*"),
- E(MPV_EVENT_PAUSE, "pause", "paused-on-cache", "core-idle", "eof-reached"),
- E(MPV_EVENT_UNPAUSE, "pause", "paused-on-cache", "core-idle", "eof-reached"),
+ E(MPV_EVENT_PAUSE, "pause"),
+ E(MPV_EVENT_UNPAUSE, "pause"),
E(MPV_EVENT_TICK, "time-pos", "audio-pts", "stream-pos", "avsync",
"percent-pos", "time-remaining", "playtime-remaining", "playback-time",
"estimated-vf-fps", "drop-frame-count", "vo-drop-frame-count",
@@ -4094,9 +4105,9 @@ static const char *const *const mp_event_property_change[] = {
E(MPV_EVENT_VIDEO_RECONFIG, "video-out-params", "video-params",
"video-format", "video-codec", "video-bitrate", "dwidth", "dheight",
"width", "height", "fps", "aspect", "vo-configured", "current-vo",
- "detected-hwdec", "colormatrix", "colormatrix-input-range",
- "colormatrix-output-range", "colormatrix-primaries", "video-aspect",
- "video-dec-params"),
+ "colormatrix", "colormatrix-input-range", "colormatrix-output-range",
+ "colormatrix-primaries", "video-aspect", "video-dec-params",
+ "hwdec", "hwdec-current", "hwdec-interop"),
E(MPV_EVENT_AUDIO_RECONFIG, "audio-format", "audio-codec", "audio-bitrate",
"samplerate", "channels", "audio", "volume", "mute", "balance",
"current-ao", "audio-codec-name", "audio-params",
@@ -4114,6 +4125,7 @@ static const char *const *const mp_event_property_change[] = {
"fullscreen"),
E(MP_EVENT_CHANGE_PLAYLIST, "playlist", "playlist-pos", "playlist-pos-1",
"playlist-count", "playlist/count"),
+ E(MP_EVENT_CORE_IDLE, "core-idle", "eof-reached"),
};
#undef E
@@ -4271,7 +4283,7 @@ static const struct property_osd_display {
const char *msg;
} property_osd_display[] = {
// general
- { "loop", "Loop" },
+ { "loop-playlist", "Loop" },
{ "loop-file", "Loop current file" },
{ "chapter", .seek_msg = OSD_SEEK_INFO_CHAPTER_TEXT,
.seek_bar = OSD_SEEK_INFO_BAR },
@@ -4801,6 +4813,7 @@ int run_command(struct MPContext *mpctx, struct mp_cmd *cmd, struct mpv_node *re
bool bar_osd = auto_osd || (on_osd & MP_ON_OSD_BAR);
bool msg_or_nobar_osd = msg_osd && !(auto_osd && opts->osd_bar_visible);
int osdl = msg_osd ? 1 : OSD_LEVEL_INVISIBLE;
+ bool async = cmd->flags & MP_ASYNC_CMD;
mp_cmd_dump(mpctx->log, cmd->id == MP_CMD_IGNORE ? MSGL_DEBUG : MSGL_V,
"Run command:", cmd);
@@ -5001,10 +5014,10 @@ int run_command(struct MPContext *mpctx, struct mp_cmd *cmd, struct mpv_node *re
if (cmd->is_up_down) {
if (cmd->is_up) {
if (mpctx->step_frames < 1)
- pause_player(mpctx);
+ set_pause_state(mpctx, true);
} else {
if (cmd->repeated) {
- unpause_player(mpctx);
+ set_pause_state(mpctx, false);
} else {
add_step_frame(mpctx, 1);
}
@@ -5110,6 +5123,16 @@ int run_command(struct MPContext *mpctx, struct mp_cmd *cmd, struct mpv_node *re
break;
}
+ case MP_CMD_EXPAND_TEXT: {
+ if (!res)
+ return -1;
+ *res = (mpv_node){
+ .format = MPV_FORMAT_STRING,
+ .u.string = mp_property_expand_string(mpctx, cmd->args[0].v.s)
+ };
+ break;
+ }
+
case MP_CMD_LOADFILE: {
char *filename = cmd->args[0].v.s;
int append = cmd->args[1].v.i;
@@ -5330,12 +5353,13 @@ int run_command(struct MPContext *mpctx, struct mp_cmd *cmd, struct mpv_node *re
case MP_CMD_SCREENSHOT: {
int mode = cmd->args[0].v.i & 3;
int freq = (cmd->args[0].v.i | cmd->args[1].v.i) >> 3;
- screenshot_request(mpctx, mode, freq, msg_osd);
+ screenshot_request(mpctx, mode, freq, msg_osd, async);
break;
}
case MP_CMD_SCREENSHOT_TO_FILE:
- screenshot_to_file(mpctx, cmd->args[0].v.s, cmd->args[1].v.i, msg_osd);
+ screenshot_to_file(mpctx, cmd->args[0].v.s, cmd->args[1].v.i, msg_osd,
+ async);
break;
case MP_CMD_SCREENSHOT_RAW: {
@@ -5729,6 +5753,12 @@ void mp_option_change_callback(void *ctx, struct m_config_option *co, int flags)
if (flags & UPDATE_OSD) {
osd_changed(mpctx->osd);
+ for (int n = 0; n < NUM_PTRACKS; n++) {
+ struct track *track = mpctx->current_track[n][STREAM_SUB];
+ struct dec_sub *sub = track ? track->d_sub : NULL;
+ if (sub)
+ sub_control(track->d_sub, SD_CTRL_UPDATE_SPEED, NULL);
+ }
mp_wakeup_core(mpctx);
}
diff --git a/player/command.h b/player/command.h
index 27a4d39..75ac687 100644
--- a/player/command.h
+++ b/player/command.h
@@ -56,6 +56,7 @@ enum {
MP_EVENT_WIN_RESIZE,
MP_EVENT_WIN_STATE,
MP_EVENT_CHANGE_PLAYLIST,
+ MP_EVENT_CORE_IDLE,
};
bool mp_hook_test_completion(struct MPContext *mpctx, char *type);
diff --git a/player/core.h b/player/core.h
index 79120de..7945080 100644
--- a/player/core.h
+++ b/player/core.h
@@ -239,6 +239,12 @@ typedef struct MPContext {
struct mp_dispatch_queue *dispatch;
struct mp_cancel *playback_abort;
bool in_dispatch;
+ // Number of asynchronous tasks that still need to finish until MPContext
+ // destruction is ok. It's implied that the async tasks call
+ // mp_wakeup_core() each time this is decremented.
+ // As using an atomic+wakeup would be racy, this is a normal integer, and
+ // mp_dispatch_lock must be called to change it.
+ int64_t outstanding_async;
struct mp_log *statusline;
struct osd_state *osd;
@@ -409,7 +415,10 @@ typedef struct MPContext {
int last_chapter_seek;
double last_chapter_pts;
- bool paused;
+ bool paused; // internal pause state
+ bool playback_active; // not paused, restarting, loading, unloading
+ bool in_playloop;
+
// step this many frames, then pause
int step_frames;
// Counted down each frame, stop playback if 0 is reached. (-1 = disable)
@@ -552,8 +561,9 @@ void mp_wakeup_core_cb(void *ctx);
void mp_process_input(struct MPContext *mpctx);
double get_relative_time(struct MPContext *mpctx);
void reset_playback_state(struct MPContext *mpctx);
-void pause_player(struct MPContext *mpctx);
-void unpause_player(struct MPContext *mpctx);
+void set_pause_state(struct MPContext *mpctx, bool user_pause);
+void update_internal_pause_state(struct MPContext *mpctx);
+void update_core_idle_state(struct MPContext *mpctx);
void add_step_frame(struct MPContext *mpctx, int dir);
void queue_seek(struct MPContext *mpctx, enum seek_type type, double amount,
enum seek_precision exact, int flags);
diff --git a/player/external_files.c b/player/external_files.c
index 5eb3a1d..dc58838 100644
--- a/player/external_files.c
+++ b/player/external_files.c
@@ -1,3 +1,20 @@
+/*
+ * 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 <dirent.h>
#include <string.h>
#include <strings.h>
@@ -96,9 +113,6 @@ static void append_dir_subtitles(struct mpv_global *global,
struct MPOpts *opts = global->opts;
struct mp_log *log = mp_log_new(tmpmem, global->log, "find_files");
- if (mp_is_url(bstr0(fname)))
- goto out;
-
struct bstr f_fbname = bstr0(mp_basename(fname));
struct bstr f_fname = mp_iconv_to_utf8(log, f_fbname,
"UTF-8-MAC", MP_NO_LATIN1_FALLBACK);
@@ -114,6 +128,10 @@ static void append_dir_subtitles(struct mpv_global *global,
// 2 = any sub file containing movie name
// 3 = sub file containing movie name and the lang extension
char *path0 = bstrdup0(tmpmem, path);
+
+ if (mp_is_url(bstr0(path0)))
+ goto out;
+
DIR *d = opendir(path0);
if (!d)
goto out;
@@ -270,12 +288,12 @@ struct subfn *find_external_files(struct mpv_global *global, const char *fname)
// Load subtitles in dirs specified by sub-paths option
if (opts->sub_auto >= 0) {
- load_paths(global, &slist, &n, fname, opts->sub_paths, "sub/",
+ load_paths(global, &slist, &n, fname, opts->sub_paths, "sub",
STREAM_SUB);
}
if (opts->audiofile_auto >= 0) {
- load_paths(global, &slist, &n, fname, opts->audiofile_paths, "audio/",
+ load_paths(global, &slist, &n, fname, opts->audiofile_paths, "audio",
STREAM_AUDIO);
}
diff --git a/player/lavfi.c b/player/lavfi.c
index 2a41aa8..5183b0e 100644
--- a/player/lavfi.c
+++ b/player/lavfi.c
@@ -28,6 +28,7 @@
#include <libavutil/mathematics.h>
#include <libavutil/rational.h>
#include <libavutil/error.h>
+#include <libavutil/opt.h>
#include <libavfilter/avfilter.h>
#include <libavfilter/buffersink.h>
#include <libavfilter/buffersrc.h>
@@ -40,6 +41,7 @@
#include "video/mp_image.h"
#include "audio/fmt-conversion.h"
#include "video/fmt-conversion.h"
+#include "video/hwdec.h"
#include "lavfi.h"
@@ -52,6 +54,8 @@ struct lavfi {
struct mp_log *log;
char *graph_string;
+ struct mp_hwdec_devices *hwdec_devs;
+
AVFilterGraph *graph;
// Set to true once all inputs have been initialized, and the graph is
// linked.
@@ -71,6 +75,8 @@ struct lavfi {
struct lavfi_pad **pads;
int num_pads;
+
+ AVFrame *tmp_frame;
};
struct lavfi_pad {
@@ -78,7 +84,6 @@ struct lavfi_pad {
enum stream_type type;
enum lavfi_direction dir;
char *name; // user-given pad name
- AVFrame *tmp_frame;
bool connected; // if false, inputs/otuputs are considered always EOF
@@ -101,7 +106,7 @@ struct lavfi_pad {
bool input_eof; // caller notified us that no input will come anymore
// used to check for format changes manually
- struct mp_image_params in_fmt_v;
+ struct mp_image *in_fmt_v;
struct mp_audio in_fmt_a;
// -- dir==LAVFI_OUT
@@ -144,9 +149,6 @@ static void add_pad(struct lavfi *c, enum lavfi_direction dir, AVFilterInOut *it
p->main = c;
p->dir = dir;
p->name = talloc_strdup(p, item->name);
- p->tmp_frame = av_frame_alloc();
- if (!p->tmp_frame)
- abort();
p->type = type;
MP_TARRAY_APPEND(c, c->pads, c->num_pads, p);
}
@@ -196,7 +198,7 @@ static void free_graph(struct lavfi *c)
pad->filter = NULL;
pad->filter_pad = -1;
pad->buffer = NULL;
- pad->in_fmt_v = (struct mp_image_params){0};
+ TA_FREEP(&pad->in_fmt_v);
pad->in_fmt_a = (struct mp_audio){0};
pad->buffer_is_eof = false;
pad->input_needed = false;
@@ -238,6 +240,9 @@ struct lavfi *lavfi_create(struct mp_log *log, char *graph_string)
struct lavfi *c = talloc_zero(NULL, struct lavfi);
c->log = log;
c->graph_string = graph_string;
+ c->tmp_frame = av_frame_alloc();
+ if (!c->tmp_frame)
+ abort();
precreate_graph(c);
return c;
}
@@ -246,6 +251,7 @@ void lavfi_destroy(struct lavfi *c)
{
free_graph(c);
clear_data(c);
+ av_frame_free(&c->tmp_frame);
talloc_free(c);
}
@@ -303,11 +309,11 @@ static bool is_aformat_ok(struct mp_audio *a, struct mp_audio *b)
{
return mp_audio_config_equals(a, b);
}
-static bool is_vformat_ok(struct mp_image_params *a, struct mp_image_params *b)
+static bool is_vformat_ok(struct mp_image *a, struct mp_image *b)
{
return a->imgfmt == b->imgfmt &&
a->w == b->w && a->h && b->h &&
- a->p_w == b->p_w && a->p_h == b->p_h;
+ a->params.p_w == b->params.p_w && a->params.p_h == b->params.p_h;
}
static void check_format_changes(struct lavfi *c)
@@ -322,9 +328,9 @@ static void check_format_changes(struct lavfi *c)
c->draining_new_format |= !is_aformat_ok(pad->pending_a,
&pad->in_fmt_a);
}
- if (pad->type == STREAM_VIDEO && pad->pending_v && pad->in_fmt_v.imgfmt) {
- c->draining_new_format |= !is_vformat_ok(&pad->pending_v->params,
- &pad->in_fmt_v);
+ if (pad->type == STREAM_VIDEO && pad->pending_v && pad->in_fmt_v) {
+ c->draining_new_format |= !is_vformat_ok(pad->pending_v,
+ pad->in_fmt_v);
}
}
@@ -370,8 +376,7 @@ static bool init_pads(struct lavfi *c)
if (avfilter_link(pad->filter, pad->filter_pad, pad->buffer, 0) < 0)
goto error;
} else {
- char src_args[256];
- AVFilter *src_filter = NULL;
+ TA_FREEP(&pad->in_fmt_v); // potentially cleanup previous error state
pad->input_eof |= !pad->connected;
@@ -380,7 +385,10 @@ static bool init_pads(struct lavfi *c)
mp_audio_copy_config(&pad->in_fmt_a, pad->pending_a);
} else if (pad->pending_v) {
assert(pad->type == STREAM_VIDEO);
- pad->in_fmt_v = pad->pending_v->params;
+ pad->in_fmt_v = mp_image_new_ref(pad->pending_v);
+ if (!pad->in_fmt_v)
+ goto error;
+ mp_image_unref_data(pad->in_fmt_v);
} else if (pad->input_eof) {
// libavfilter makes this painful. Init it with a dummy config,
// just so we can tell it the stream is EOF.
@@ -389,11 +397,9 @@ static bool init_pads(struct lavfi *c)
mp_audio_set_num_channels(&pad->in_fmt_a, 2);
pad->in_fmt_a.rate = 48000;
} else if (pad->type == STREAM_VIDEO) {
- pad->in_fmt_v = (struct mp_image_params){
- .imgfmt = IMGFMT_420P,
- .w = 64, .h = 64,
- .p_w = 1, .p_h = 1,
- };
+ pad->in_fmt_v = talloc_zero(NULL, struct mp_image);
+ mp_image_setfmt(pad->in_fmt_v, IMGFMT_420P);
+ mp_image_set_size(pad->in_fmt_v, 64, 64);
}
} else {
// no input data, format unknown, can't init, wait longer.
@@ -401,35 +407,58 @@ static bool init_pads(struct lavfi *c)
return false;
}
+ AVBufferSrcParameters *params = av_buffersrc_parameters_alloc();
+ if (!params)
+ goto error;
+
+ char *filter_name = NULL;
if (pad->type == STREAM_AUDIO) {
- pad->timebase = (AVRational){1, pad->in_fmt_a.rate};
- snprintf(src_args, sizeof(src_args),
- "sample_rate=%d:sample_fmt=%s:time_base=%d/%d:"
- "channel_layout=0x%"PRIx64, pad->in_fmt_a.rate,
- av_get_sample_fmt_name(af_to_avformat(pad->in_fmt_a.format)),
- pad->timebase.num, pad->timebase.den,
- mp_chmap_to_lavc(&pad->in_fmt_a.channels));
- src_filter = avfilter_get_by_name("abuffer");
+ params->time_base = pad->timebase =
+ (AVRational){1, pad->in_fmt_a.rate};
+ params->format = af_to_avformat(pad->in_fmt_a.format);
+ params->sample_rate = pad->in_fmt_a.rate;
+ params->channel_layout =
+ mp_chmap_to_lavc(&pad->in_fmt_a.channels);
+ filter_name = "abuffer";
} else if (pad->type == STREAM_VIDEO) {
- pad->timebase = AV_TIME_BASE_Q;
- snprintf(src_args, sizeof(src_args), "%d:%d:%d:%d:%d:%d:%d",
- pad->in_fmt_v.w, pad->in_fmt_v.h,
- imgfmt2pixfmt(pad->in_fmt_v.imgfmt),
- pad->timebase.num, pad->timebase.den,
- pad->in_fmt_v.p_w, pad->in_fmt_v.p_h);
- src_filter = avfilter_get_by_name("buffer");
+ params->time_base = pad->timebase = AV_TIME_BASE_Q;
+ params->format = imgfmt2pixfmt(pad->in_fmt_v->imgfmt);
+ params->width = pad->in_fmt_v->w;
+ params->height = pad->in_fmt_v->h;
+ params->sample_aspect_ratio.num = pad->in_fmt_v->params.p_w;
+ params->sample_aspect_ratio.den = pad->in_fmt_v->params.p_h;
+ params->hw_frames_ctx = pad->in_fmt_v->hwctx;
+ filter_name = "buffer";
} else {
assert(0);
}
- if (!src_filter)
+ AVFilter *filter = avfilter_get_by_name(filter_name);
+ if (filter) {
+ char name[256];
+ snprintf(name, sizeof(name), "mpv_src_%s", pad->name);
+
+ pad->buffer = avfilter_graph_alloc_filter(c->graph, filter, name);
+ }
+ if (!pad->buffer) {
+ av_free(params);
goto error;
+ }
- char name[256];
- snprintf(name, sizeof(name), "mpv_src_%s", pad->name);
+ if (pad->type == STREAM_AUDIO) {
+ char layout[80];
+ snprintf(layout, sizeof(layout), "%lld",
+ (long long)params->channel_layout);
+ av_opt_set(pad->buffer, "channel_layout", layout,
+ AV_OPT_SEARCH_CHILDREN);
+ }
- if (avfilter_graph_create_filter(&pad->buffer, src_filter,
- name, src_args, NULL, c->graph) < 0)
+ int ret = av_buffersrc_parameters_set(pad->buffer, params);
+ av_free(params);
+ if (ret < 0)
+ goto error;
+
+ if (avfilter_init_str(pad->buffer, NULL) < 0)
goto error;
if (avfilter_link(pad->buffer, 0, pad->filter, pad->filter_pad) < 0)
@@ -455,6 +484,11 @@ static void dump_graph(struct lavfi *c)
#endif
}
+void lavfi_set_hwdec_devs(struct lavfi *c, struct mp_hwdec_devices *hwdevs)
+{
+ c->hwdec_devs = hwdevs;
+}
+
// Initialize the graph if all inputs have formats set. If it's already
// initialized, or can't be initialized yet, do nothing.
static void init_graph(struct lavfi *c)
@@ -462,6 +496,15 @@ static void init_graph(struct lavfi *c)
assert(!c->initialized);
if (init_pads(c)) {
+ if (c->hwdec_devs) {
+ struct mp_hwdec_ctx *hwdec = hwdec_devices_get_first(c->hwdec_devs);
+ for (int n = 0; n < c->graph->nb_filters; n++) {
+ AVFilterContext *filter = c->graph->filters[n];
+ if (hwdec && hwdec->av_device_ref)
+ filter->hw_device_ctx = av_buffer_ref(hwdec->av_device_ref);
+ }
+ }
+
// And here the actual libavfilter initialization happens.
if (avfilter_graph_config(c->graph, NULL) < 0) {
MP_FATAL(c, "failed to configure the filter graph\n");
@@ -564,22 +607,22 @@ static void read_output_pads(struct lavfi *c)
int flags = pad->output_needed ? 0 : AV_BUFFERSINK_FLAG_NO_REQUEST;
int r = AVERROR_EOF;
if (!pad->buffer_is_eof)
- r = av_buffersink_get_frame_flags(pad->buffer, pad->tmp_frame, flags);
+ r = av_buffersink_get_frame_flags(pad->buffer, c->tmp_frame, flags);
if (r >= 0) {
pad->output_needed = false;
- double pts = mp_pts_from_av(pad->tmp_frame->pts, &pad->timebase);
+ double pts = mp_pts_from_av(c->tmp_frame->pts, &pad->timebase);
if (pad->type == STREAM_AUDIO) {
- pad->pending_a = mp_audio_from_avframe(pad->tmp_frame);
+ pad->pending_a = mp_audio_from_avframe(c->tmp_frame);
if (pad->pending_a)
pad->pending_a->pts = pts;
} else if (pad->type == STREAM_VIDEO) {
- pad->pending_v = mp_image_from_av_frame(pad->tmp_frame);
+ pad->pending_v = mp_image_from_av_frame(c->tmp_frame);
if (pad->pending_v)
pad->pending_v->pts = pts;
} else {
assert(0);
}
- av_frame_unref(pad->tmp_frame);
+ av_frame_unref(c->tmp_frame);
if (!pad->pending_v && !pad->pending_a)
MP_ERR(c, "could not use filter output\n");
pad->output_eof = false;
diff --git a/player/lavfi.h b/player/lavfi.h
index d39ecb0..0f2ae77 100644
--- a/player/lavfi.h
+++ b/player/lavfi.h
@@ -22,6 +22,7 @@ bool lavfi_get_connected(struct lavfi_pad *pad);
bool lavfi_process(struct lavfi *c);
bool lavfi_has_failed(struct lavfi *c);
void lavfi_seek_reset(struct lavfi *c);
+void lavfi_set_hwdec_devs(struct lavfi *c, struct mp_hwdec_devices *hwdevs);
int lavfi_request_frame_a(struct lavfi_pad *pad, struct mp_audio **out_aframe);
int lavfi_request_frame_v(struct lavfi_pad *pad, struct mp_image **out_vframe);
bool lavfi_needs_input(struct lavfi_pad *pad);
diff --git a/player/loadfile.c b/player/loadfile.c
index dba02ee..e0a796d 100644
--- a/player/loadfile.c
+++ b/player/loadfile.c
@@ -132,7 +132,19 @@ static void print_stream(struct MPContext *mpctx, struct track *t)
if (t->title)
APPEND(b, " '%s'", t->title);
const char *codec = s ? s->codec->codec : NULL;
- APPEND(b, " (%s)", codec ? codec : "<unknown>");
+ APPEND(b, " (%s", codec ? codec : "<unknown>");
+ if (t->type == STREAM_VIDEO) {
+ if (s && s->codec->disp_w)
+ APPEND(b, " %dx%d", s->codec->disp_w, s->codec->disp_h);
+ if (s && s->codec->fps)
+ APPEND(b, " %.3ffps", s->codec->fps);
+ } else if (t->type == STREAM_AUDIO) {
+ if (s && s->codec->channels.num)
+ APPEND(b, " %dch", s->codec->channels.num);
+ if (s && s->codec->samplerate)
+ APPEND(b, " %dHz", s->codec->samplerate);
+ }
+ APPEND(b, ")");
if (t->is_external)
APPEND(b, " (external)");
MP_INFO(mpctx, "%s\n", b);
@@ -1256,20 +1268,23 @@ reopen_file:
execute_queued_seek(mpctx);
}
- if (mpctx->opts->pause)
- pause_player(mpctx);
+ update_internal_pause_state(mpctx);
open_recorder(mpctx, true);
playback_start = mp_time_sec();
mpctx->error_playing = 0;
+ mpctx->in_playloop = true;
while (!mpctx->stop_play)
run_playloop(mpctx);
+ mpctx->in_playloop = false;
MP_VERBOSE(mpctx, "EOF code: %d \n", mpctx->stop_play);
terminate_playback:
+ update_core_idle_state(mpctx);
+
process_unload_hooks(mpctx);
if (mpctx->stop_play == KEEP_PLAYING)
@@ -1295,7 +1310,6 @@ terminate_playback:
uninit_audio_out(mpctx);
mpctx->playback_initialized = false;
- update_screensaver_state(mpctx);
if (mpctx->stop_play == PT_RELOAD_FILE) {
mpctx->stop_play = KEEP_PLAYING;
diff --git a/player/lua/osc.lua b/player/lua/osc.lua
index 1604574..276e34f 100644
--- a/player/lua/osc.lua
+++ b/player/lua/osc.lua
@@ -34,10 +34,13 @@ local user_opts = {
layout = "bottombar",
seekbarstyle = "bar", -- slider (diamond marker), knob (circle
-- marker with guide), or bar (fill)
+ title = "${media-title}", -- string compatible with property-expansion
+ -- to be shown as OSC title
tooltipborder = 1, -- border of tooltip in bottom/topbar
timetotal = false, -- display total time instead of remaining time?
timems = false, -- display timecodes with milliseconds?
visibility = "auto", -- only used at init to set visibility_mode(...)
+ boxmaxchars = 80, -- title crop threshold for box layout
}
-- read_options may modify hidetimeout, so save the original default value in
@@ -54,6 +57,7 @@ local osc_param = { -- calculated by osc_init()
playresy = 0, -- canvas size Y
playresx = 0, -- canvas size X
display_aspect = 1,
+ unscaled_y = 0,
areas = {},
}
@@ -659,8 +663,10 @@ function render_elements(master_ass)
local maxchars = element.layout.button.maxchars
if not (maxchars == nil) and (#buttontext > maxchars) then
- if (#buttontext > maxchars+20) then
- while (#buttontext > maxchars+20) do
+ local max_ratio = 1.25 -- up to 25% more chars while shrinking
+ local limit = math.max(0, math.floor(maxchars * max_ratio) - 3)
+ if (#buttontext > limit) then
+ while (#buttontext > limit) do
buttontext = buttontext:gsub(".[\128-\191]*$", "")
end
buttontext = buttontext .. "..."
@@ -691,7 +697,7 @@ function limited_list(prop, pos)
end
local fs = tonumber(mp.get_property('options/osd-font-size'))
- local max = math.ceil(720 / fs)
+ local max = math.ceil(osc_param.unscaled_y*0.75 / fs)
if max % 2 == 0 then
max = max - 1
end
@@ -762,47 +768,30 @@ function show_message(text, duration)
text = string.sub(text, 0, 4000)
-- replace actual linebreaks with ASS linebreaks
- -- and get the amount of lines along the way
- local lines
- text, lines = string.gsub(text, "\n", "\\N")
+ text = string.gsub(text, "\n", "\\N")
- -- append a Zero-Width-Space to . and _ to enable
- -- linebreaking of long filenames
- text = string.gsub(text, "%.", ".\226\128\139")
- text = string.gsub(text, "_", "_\226\128\139")
-
- local scale = 1
- if (mp.get_property("video") == "no") then
- scale = user_opts.scaleforcedwindow
- elseif state.fullscreen then
- scale = user_opts.scalefullscreen
- else
- scale = user_opts.scalewindowed
- end
-
- -- scale the fontsize for longer multi-line output
- local fontsize = tonumber(mp.get_property("options/osd-font-size")) / scale
- local outline = tonumber(mp.get_property("options/osd-border-size")) / scale
-
-
- if lines > 12 then
- fontsize, outline = fontsize / 1.5, outline / 1.25
- elseif lines > 8 then
- fontsize, outline = fontsize / 1.25, outline / 1.125
- end
-
- local style = "{\\bord" .. outline .. "\\fs" .. fontsize .. "}"
-
- state.message_text = style .. text
+ state.message_text = text
state.message_timeout = mp.get_time() + duration
end
function render_message(ass)
if not(state.message_timeout == nil) and not(state.message_text == nil)
and state.message_timeout > mp.get_time() then
+ local _, lines = string.gsub(state.message_text, "\\N", "")
+
+ local fontsize = tonumber(mp.get_property("options/osd-font-size"))
+ local outline = tonumber(mp.get_property("options/osd-border-size"))
+ local maxlines = math.ceil(osc_param.unscaled_y*0.75 / fontsize)
+ local counterscale = osc_param.playresy / osc_param.unscaled_y
+
+ fontsize = fontsize * counterscale / math.max(0.65 + math.min(lines/maxlines, 1), 1)
+ outline = outline * counterscale / math.max(0.75 + math.min(lines/maxlines, 1)/2, 1)
+
+ local style = "{\\bord" .. outline .. "\\fs" .. fontsize .. "}"
+
ass:new_event()
- ass:append(state.message_text)
+ ass:append(style .. state.message_text)
else
state.message_text = nil
state.message_timeout = nil
@@ -951,7 +940,7 @@ layouts["box"] = function ()
lo = add_layout("title")
lo.geometry = {x = posX, y = titlerowY, an = 8, w = 496, h = 12}
lo.style = osc_styles.vidtitle
- lo.button.maxchars = 80
+ lo.button.maxchars = user_opts.boxmaxchars
lo = add_layout("pl_prev")
lo.geometry =
@@ -1007,7 +996,13 @@ layouts["box"] = function ()
lo = add_layout("tog_fs")
lo.geometry =
- {x = posX+pos_offsetX, y = bigbtnrowY, an = 6, w = 25, h = 25}
+ {x = posX+pos_offsetX - 25, y = bigbtnrowY, an = 4, w = 25, h = 25}
+ lo.style = osc_styles.smallButtonsR
+
+ lo = add_layout("volume")
+ lo.geometry =
+ {x = posX+pos_offsetX - (25 * 2) - osc_geo.p,
+ y = bigbtnrowY, an = 4, w = 25, h = 25}
lo.style = osc_styles.smallButtonsR
--
@@ -1166,9 +1161,9 @@ layouts["bottombar"] = function()
local padX = 9
local padY = 3
local buttonW = 27
- local tcW = (state.tc_ms) and 150 or 105
+ local tcW = (state.tc_ms) and 170 or 110
local tsW = 90
- local minW = (buttonW + padX)*3 + (tcW + padX)*4 + (tsW + padX)*2
+ local minW = (buttonW + padX)*5 + (tcW + padX)*4 + (tsW + padX)*2
if ((osc_param.display_aspect > 0) and (osc_param.playresx < minW)) then
osc_param.playresy = minW / osc_param.display_aspect
@@ -1233,7 +1228,7 @@ layouts["bottombar"] = function()
lo.geometry = geo
lo.style = string.format("%s{\\clip(%f,%f,%f,%f)}",
osc_styles.vidtitleBar,
- geo.x, geo.y-geo.h/2, geo.w, geo.y+geo.h/2)
+ geo.x, geo.y-geo.h, geo.w, geo.y+geo.h)
-- Playback control buttons
@@ -1262,10 +1257,21 @@ layouts["bottombar"] = function()
local sb_l = geo.x + padX
+ -- Fullscreen button
+ geo = { x = osc_geo.x + osc_geo.w - buttonW - padX, y = geo.y, an = 4,
+ w = buttonW, h = geo.h }
+ lo = add_layout("tog_fs")
+ lo.geometry = geo
+ lo.style = osc_styles.smallButtonsBar
+
+ -- Volume
+ geo = { x = geo.x - geo.w - padX, y = geo.y, an = geo.an, w = geo.w, h = geo.h }
+ lo = add_layout("volume")
+ lo.geometry = geo
+ lo.style = osc_styles.smallButtonsBar
-- Track selection buttons
- geo = { x = osc_geo.x + osc_geo.w - padX, y = geo.y, an = geo.an,
- w = tsW, h = geo.h }
+ geo = { x = geo.x - tsW - padX, y = geo.y, an = geo.an, w = tsW, h = geo.h }
lo = add_layout("cy_sub")
lo.geometry = geo
lo.style = osc_styles.smallButtonsBar
@@ -1277,7 +1283,7 @@ layouts["bottombar"] = function()
-- Right timecode
- geo = { x = geo.x - geo.w - padX - tcW, y = geo.y, an = 4,
+ geo = { x = geo.x - padX - tcW - 10, y = geo.y, an = geo.an,
w = tcW, h = geo.h }
lo = add_layout("tc_right")
lo.geometry = geo
@@ -1320,9 +1326,9 @@ layouts["topbar"] = function()
local padX = 9
local padY = 3
local buttonW = 27
- local tcW = (state.tc_ms) and 150 or 105
+ local tcW = (state.tc_ms) and 170 or 110
local tsW = 90
- local minW = (buttonW + padX)*3 + (tcW + padX)*4 + (tsW + padX)*2
+ local minW = (buttonW + padX)*5 + (tcW + padX)*4 + (tsW + padX)*2
if ((osc_param.display_aspect > 0) and (osc_param.playresx < minW)) then
osc_param.playresy = minW / osc_param.display_aspect
@@ -1385,10 +1391,21 @@ layouts["topbar"] = function()
local sb_l = geo.x + padX
+ -- Fullscreen button
+ geo = { x = osc_geo.x + osc_geo.w - buttonW - padX, y = geo.y, an = 4,
+ w = buttonW, h = geo.h }
+ lo = add_layout("tog_fs")
+ lo.geometry = geo
+ lo.style = osc_styles.smallButtonsBar
+
+ -- Volume
+ geo = { x = geo.x - geo.w - padX, y = geo.y, an = geo.an, w = geo.w, h = geo.h }
+ lo = add_layout("volume")
+ lo.geometry = geo
+ lo.style = osc_styles.smallButtonsBar
-- Track selection buttons
- geo = { x = osc_geo.x + osc_geo.w - padX, y = geo.y, an = geo.an,
- w = tsW, h = geo.h }
+ geo = { x = geo.x - tsW - padX, y = geo.y, an = geo.an, w = tsW, h = geo.h }
lo = add_layout("cy_sub")
lo.geometry = geo
lo.style = osc_styles.smallButtonsBar
@@ -1400,7 +1417,7 @@ layouts["topbar"] = function()
-- Right timecode
- geo = { x = geo.x - geo.w - padX - tcW, y = geo.y, an = 4,
+ geo = { x = geo.x - geo.w - padX - tcW - 10, y = geo.y, an = 4,
w = tcW, h = geo.h }
lo = add_layout("tc_right")
lo.geometry = geo
@@ -1460,7 +1477,7 @@ layouts["topbar"] = function()
lo.geometry = geo
lo.style = string.format("%s{\\clip(%f,%f,%f,%f)}",
osc_styles.vidtitleBar,
- geo.x, geo.y-geo.h/2, geo.w, geo.y+geo.h/2)
+ geo.x, geo.y-geo.h, geo.w, geo.y+geo.h)
end
-- Validate string type user options
@@ -1498,12 +1515,15 @@ function osc_init()
end
if user_opts.vidscale then
- osc_param.playresy = baseResY / scale
+ osc_param.unscaled_y = baseResY
else
- osc_param.playresy = display_h / scale
+ osc_param.unscaled_y = display_h
end
- osc_param.playresx = osc_param.playresy * display_aspect
- osc_param.display_aspect = display_aspect
+ osc_param.playresy = osc_param.unscaled_y / scale
+ if (display_aspect > 0) then
+ osc_param.display_aspect = display_aspect
+ end
+ osc_param.playresx = osc_param.playresy * osc_param.display_aspect
@@ -1516,7 +1536,7 @@ function osc_init()
local have_pl = (pl_count > 1)
local pl_pos = mp.get_property_number("playlist-pos", 0) + 1
local have_ch = (mp.get_property_number("chapters", 0) > 0)
- local loop = mp.get_property("loop", "no")
+ local loop = mp.get_property("loop-playlist", "no")
local ne
@@ -1524,12 +1544,10 @@ function osc_init()
ne = new_element("title", "button")
ne.content = function ()
- local title = mp.get_property_osd("media-title")
- if not (title == nil) then
- return (title)
- else
- return ("mpv")
- end
+ local title = mp.command_native({"expand-text", user_opts.title})
+ -- escape ASS, and strip newlines and trailing slashes
+ title = title:gsub("\\n", " "):gsub("\\$", ""):gsub("{","\\{")
+ return not (title == "") and title or "mpv"
end
ne.eventresponder["mouse_btn0_up"] = function ()
@@ -1791,24 +1809,52 @@ function osc_init()
ne.content = function ()
local dmx_cache = mp.get_property_number("demuxer-cache-duration")
- if not (dmx_cache == nil) then
- dmx_cache = math.floor(dmx_cache + 0.5) .. "s + "
- else
- dmx_cache = ""
- end
local cache_used = mp.get_property_number("cache-used")
- if not (cache_used == nil) then
- if (cache_used < 1024) then
- cache_used = cache_used .. " KB"
- else
- cache_used = math.floor((cache_used/102.4)+0.5)/10 .. " MB"
+ local is_network = mp.get_property_native("demuxer-via-network")
+ if dmx_cache then
+ dmx_cache = string.format("%3.0fs", dmx_cache)
+ end
+ if cache_used then
+ local suffix = " KiB"
+ if (cache_used >= 1024) then
+ cache_used = cache_used/1024
+ suffix = " MiB"
end
- return ("Cache: " .. dmx_cache .. cache_used)
+ cache_used = string.format("%5.1f%s", cache_used, suffix)
+ end
+ if (is_network and dmx_cache) or cache_used then
+ -- Only show dmx-cache-duration by itself if it's a network file.
+ -- Cache can be forced even for local files, so always show that.
+ return string.format("Cache: %s%s%s",
+ (dmx_cache and dmx_cache or ""),
+ ((dmx_cache and cache_used) and " + " or ""),
+ (cache_used or ""))
else
return ""
end
end
+ -- volume
+ ne = new_element("volume", "button")
+
+ ne.content = function()
+ local volume = mp.get_property_number("volume", 0)
+ local mute = mp.get_property_native("mute")
+ local volicon = {"\238\132\139", "\238\132\140",
+ "\238\132\141", "\238\132\142"}
+ if volume == 0 or mute then
+ return "\238\132\138"
+ else
+ return volicon[math.min(4,math.ceil(volume / (100/3)))]
+ end
+ end
+ ne.eventresponder["mouse_btn0_up"] =
+ function () mp.commandv("cycle", "mute") end
+
+ ne.eventresponder["mouse_btn3_press"] =
+ function () mp.commandv("osd-auto", "add", "volume", 5) end
+ ne.eventresponder["mouse_btn4_press"] =
+ function () mp.commandv("osd-auto", "add", "volume", -5) end
-- load layout
@@ -2039,7 +2085,8 @@ function render()
end
-- submit
- mp.set_osd_ass(osc_param.playresy * aspect, osc_param.playresy, ass.text)
+ mp.set_osd_ass(osc_param.playresy * osc_param.display_aspect,
+ osc_param.playresy, ass.text)
@@ -2050,24 +2097,31 @@ end
-- Eventhandling
--
+local function element_has_action(element, action)
+ return element and element.eventresponder and
+ element.eventresponder[action]
+end
+
function process_event(source, what)
+ local action = string.format("%s%s", source,
+ what and ("_" .. what) or "")
- if what == "down" then
+ if what == "down" or what == "press" then
for n = 1, #elements do
- if not (elements[n].eventresponder == nil) then
- if not (elements[n].eventresponder[source .. "_up"] == nil)
- or not (elements[n].eventresponder[source .. "_down"] == nil) then
+ if mouse_hit(elements[n]) and
+ elements[n].eventresponder and
+ (elements[n].eventresponder[source .. "_up"] or
+ elements[n].eventresponder[action]) then
- if mouse_hit(elements[n]) then
- state.active_element = n
- state.active_event_source = source
- -- fire the down event if the element has one
- if not (elements[n].eventresponder[source .. "_" .. what] == nil) then
- elements[n].eventresponder[source .. "_" .. what](elements[n])
- end
- end
+ if what == "down" then
+ state.active_element = n
+ state.active_event_source = source
+ end
+ -- fire the down or press event if the element has one
+ if element_has_action(elements[n], action) then
+ elements[n].eventresponder[action](elements[n])
end
end
@@ -2075,23 +2129,19 @@ function process_event(source, what)
elseif what == "up" then
- if not (state.active_element == nil) then
-
+ if elements[state.active_element] then
local n = state.active_element
if n == 0 then
--click on background (does not work)
- elseif n > 0 and not (n > #elements) and
- not (elements[n].eventresponder == nil) and
- not (elements[n].eventresponder[source .. "_" .. what] == nil) then
+ elseif element_has_action(elements[n], action) and
+ mouse_hit(elements[n]) then
- if mouse_hit(elements[n]) then
- elements[n].eventresponder[source .. "_" .. what](elements[n])
- end
+ elements[n].eventresponder[action](elements[n])
end
--reset active element
- if not (elements[n].eventresponder["reset"] == nil) then
+ if element_has_action(elements[n], "reset") then
elements[n].eventresponder["reset"](elements[n])
end
@@ -2100,6 +2150,7 @@ function process_event(source, what)
state.mouse_down_counter = 0
elseif source == "mouse_move" then
+
local mouseX, mouseY = get_virt_mouse_pos()
if (user_opts.minmousemove == 0) or
(not ((state.last_mouseX == nil) or (state.last_mouseY == nil)) and
@@ -2111,15 +2162,9 @@ function process_event(source, what)
end
state.last_mouseX, state.last_mouseY = mouseX, mouseY
- if not (state.active_element == nil) then
-
- local n = state.active_element
-
- if not (n > #elements) and not (elements[n].eventresponder == nil) then
- if not (elements[n].eventresponder[source] == nil) then
- elements[n].eventresponder[source](elements[n])
- end
- end
+ local n = state.active_element
+ if element_has_action(elements[n], action) then
+ elements[n].eventresponder[action](elements[n])
end
tick()
end
@@ -2257,6 +2302,8 @@ mp.set_key_bindings({
function(e) process_event("shift+mouse_btn0", "down") end},
{"mouse_btn2", function(e) process_event("mouse_btn2", "up") end,
function(e) process_event("mouse_btn2", "down") end},
+ {"mouse_btn3", function(e) process_event("mouse_btn3", "press") end},
+ {"mouse_btn4", function(e) process_event("mouse_btn4", "press") end},
{"mouse_btn0_dbl", "ignore"},
{"shift+mouse_btn0_dbl", "ignore"},
{"mouse_btn2_dbl", "ignore"},
@@ -2310,3 +2357,5 @@ end
visibility_mode(user_opts.visibility, true)
mp.register_script_message("osc-visibility", visibility_mode)
mp.add_key_binding("del", function() visibility_mode("cycle") end)
+
+set_virt_mouse_area(0, 0, 0, 0, "input")
diff --git a/player/main.c b/player/main.c
index 87ec1ab..2045e3b 100644
--- a/player/main.c
+++ b/player/main.c
@@ -74,7 +74,7 @@ static const char def_config[] =
#define FULLCONFIG "(missing)\n"
#endif
-#if !(HAVE_STDATOMIC || HAVE_ATOMIC_BUILTINS || HAVE_SYNC_BUILTINS)
+#if !HAVE_STDATOMIC
pthread_mutex_t mp_atomic_mutex = PTHREAD_MUTEX_INITIALIZER;
#endif
@@ -150,7 +150,7 @@ void mp_print_version(struct mp_log *log, int always)
static void shutdown_clients(struct MPContext *mpctx)
{
mp_client_enter_shutdown(mpctx);
- while (mp_clients_num(mpctx)) {
+ while (mp_clients_num(mpctx) || mpctx->outstanding_async) {
mp_client_broadcast_event(mpctx, MPV_EVENT_SHUTDOWN, NULL);
mp_wait_events(mpctx);
}
@@ -455,6 +455,11 @@ int mp_initialize(struct MPContext *mpctx, char **options)
MP_STATS(mpctx, "start init");
+#if HAVE_COCOA
+ mpv_handle *ctx = mp_new_client(mpctx->clients, "osx");
+ cocoa_set_mpv_handle(ctx);
+#endif
+
#if HAVE_ENCODING
if (opts->encode_opts->file && opts->encode_opts->file[0]) {
mpctx->encode_lavc_ctx = encode_lavc_init(opts->encode_opts,
diff --git a/player/playloop.c b/player/playloop.c
index e73ad61..2e18025 100644
--- a/player/playloop.c
+++ b/player/playloop.c
@@ -56,7 +56,8 @@
// mp_wait_events() was called.
void mp_wait_events(struct MPContext *mpctx)
{
- if (mpctx->sleeptime > 0)
+ bool sleeping = mpctx->sleeptime > 0;
+ if (sleeping)
MP_STATS(mpctx, "start sleep");
mpctx->in_dispatch = true;
@@ -66,7 +67,7 @@ void mp_wait_events(struct MPContext *mpctx)
mpctx->in_dispatch = false;
mpctx->sleeptime = INFINITY;
- if (mpctx->sleeptime > 0)
+ if (sleeping)
MP_STATS(mpctx, "end sleep");
}
@@ -119,58 +120,70 @@ double get_relative_time(struct MPContext *mpctx)
return delta * 0.000001;
}
-void pause_player(struct MPContext *mpctx)
+void update_core_idle_state(struct MPContext *mpctx)
{
- mpctx->opts->pause = 1;
+ bool eof = mpctx->video_status == STATUS_EOF &&
+ mpctx->audio_status == STATUS_EOF;
+ bool active = !mpctx->paused && mpctx->restart_complete && mpctx->playing &&
+ mpctx->in_playloop && !eof;
- update_screensaver_state(mpctx);
+ if (mpctx->playback_active != active) {
+ mpctx->playback_active = active;
- if (mpctx->paused)
- goto end;
- mpctx->paused = true;
- mpctx->step_frames = 0;
- mpctx->time_frame -= get_relative_time(mpctx);
- mpctx->osd_function = 0;
- mpctx->osd_force_update = true;
- mpctx->paused_for_cache = false;
-
- if (mpctx->ao && mpctx->ao_chain)
- ao_pause(mpctx->ao);
- if (mpctx->video_out)
- vo_set_paused(mpctx->video_out, true);
-
- mp_wakeup_core(mpctx);
+ update_screensaver_state(mpctx);
-end:
- mp_notify(mpctx, mpctx->opts->pause ? MPV_EVENT_PAUSE : MPV_EVENT_UNPAUSE, 0);
+ mp_notify(mpctx, MP_EVENT_CORE_IDLE, NULL);
+ }
}
-void unpause_player(struct MPContext *mpctx)
+// The value passed here is the new value for mpctx->opts->pause
+void set_pause_state(struct MPContext *mpctx, bool user_pause)
{
- mpctx->opts->pause = 0;
+ struct MPOpts *opts = mpctx->opts;
+ bool send_update = false;
+
+ if (opts->pause != user_pause)
+ send_update = true;
+ opts->pause = user_pause;
- update_screensaver_state(mpctx);
+ bool internal_paused = opts->pause || mpctx->paused_for_cache;
+ if (internal_paused != mpctx->paused) {
+ mpctx->paused = internal_paused;
+ send_update = true;
- if (!mpctx->paused)
- goto end;
- // Don't actually unpause while cache is loading.
- if (mpctx->paused_for_cache)
- goto end;
- mpctx->paused = false;
- mpctx->osd_function = 0;
- mpctx->osd_force_update = true;
+ if (mpctx->ao && mpctx->ao_chain) {
+ if (internal_paused) {
+ ao_pause(mpctx->ao);
+ } else {
+ ao_resume(mpctx->ao);
+ }
+ }
- if (mpctx->ao && mpctx->ao_chain)
- ao_resume(mpctx->ao);
- if (mpctx->video_out)
- vo_set_paused(mpctx->video_out, false);
+ if (mpctx->video_out)
+ vo_set_paused(mpctx->video_out, internal_paused);
- mp_wakeup_core(mpctx);
+ mpctx->osd_function = 0;
+ mpctx->osd_force_update = true;
- (void)get_relative_time(mpctx); // ignore time that passed during pause
+ mp_wakeup_core(mpctx);
+
+ if (internal_paused) {
+ mpctx->step_frames = 0;
+ mpctx->time_frame -= get_relative_time(mpctx);
+ } else {
+ (void)get_relative_time(mpctx); // ignore time that passed during pause
+ }
+ }
+
+ update_core_idle_state(mpctx);
-end:
- mp_notify(mpctx, mpctx->opts->pause ? MPV_EVENT_PAUSE : MPV_EVENT_UNPAUSE, 0);
+ if (send_update)
+ mp_notify(mpctx, opts->pause ? MPV_EVENT_PAUSE : MPV_EVENT_UNPAUSE, 0);
+}
+
+void update_internal_pause_state(struct MPContext *mpctx)
+{
+ set_pause_state(mpctx, mpctx->opts->pause);
}
void update_screensaver_state(struct MPContext *mpctx)
@@ -178,10 +191,9 @@ void update_screensaver_state(struct MPContext *mpctx)
if (!mpctx->video_out)
return;
- bool saver_state = mpctx->opts->pause || !mpctx->opts->stop_screensaver ||
- !mpctx->playback_initialized;
- vo_control(mpctx->video_out, saver_state ? VOCTRL_RESTORE_SCREENSAVER
- : VOCTRL_KILL_SCREENSAVER, NULL);
+ bool saver_state = !mpctx->playback_active || !mpctx->opts->stop_screensaver;
+ vo_control_async(mpctx->video_out, saver_state ? VOCTRL_RESTORE_SCREENSAVER
+ : VOCTRL_KILL_SCREENSAVER, NULL);
}
void add_step_frame(struct MPContext *mpctx, int dir)
@@ -190,11 +202,11 @@ void add_step_frame(struct MPContext *mpctx, int dir)
return;
if (dir > 0) {
mpctx->step_frames += 1;
- unpause_player(mpctx);
+ set_pause_state(mpctx, false);
} else if (dir < 0) {
if (!mpctx->hrseek_active) {
queue_seek(mpctx, MPSEEK_BACKSTEP, 0, MPSEEK_VERY_EXACT, 0);
- pause_player(mpctx);
+ set_pause_state(mpctx, true);
}
}
}
@@ -230,6 +242,8 @@ void reset_playback_state(struct MPContext *mpctx)
#if HAVE_ENCODING
encode_lavc_discontinuity(mpctx->encode_lavc_ctx);
#endif
+
+ update_core_idle_state(mpctx);
}
static void mp_seek(MPContext *mpctx, struct seek_params seek)
@@ -586,7 +600,6 @@ static void handle_osd_redraw(struct MPContext *mpctx)
if (!want_redraw)
return;
vo_redraw(mpctx->video_out);
- mp_wakeup_core(mpctx);
}
static void handle_pause_on_low_cache(struct MPContext *mpctx)
@@ -605,8 +618,9 @@ static void handle_pause_on_low_cache(struct MPContext *mpctx)
demux_control(mpctx->demuxer, DEMUXER_CTRL_GET_READER_STATE, &s);
int cache_buffer = 100;
+ bool use_pause_on_low_cache = c.size > 0 || mpctx->demuxer->is_network;
- if (mpctx->restart_complete && c.size > 0) {
+ if (mpctx->restart_complete && use_pause_on_low_cache) {
if (mpctx->paused && mpctx->paused_for_cache) {
if (!s.underrun && (!opts->cache_pausing || s.idle ||
s.ts_duration >= mpctx->cache_wait_time))
@@ -618,17 +632,14 @@ static void handle_pause_on_low_cache(struct MPContext *mpctx)
mpctx->cache_wait_time /= 1.5 - 0.1;
}
mpctx->paused_for_cache = false;
- if (!opts->pause)
- unpause_player(mpctx);
+ update_internal_pause_state(mpctx);
force_update = true;
}
mp_set_timeout(mpctx, 0.2);
} else {
if (opts->cache_pausing && s.underrun) {
- bool prev_paused_user = opts->pause;
- pause_player(mpctx);
mpctx->paused_for_cache = true;
- opts->pause = prev_paused_user;
+ update_internal_pause_state(mpctx);
mpctx->cache_stop_time = now;
force_update = true;
}
@@ -762,7 +773,7 @@ static void handle_sstep(struct MPContext *mpctx)
if (mpctx->max_frames >= 0 && !mpctx->stop_play)
mpctx->stop_play = AT_END_OF_FILE; // force EOF even if audio left
if (mpctx->step_frames > 0 && !mpctx->paused)
- pause_player(mpctx);
+ set_pause_state(mpctx, true);
}
}
@@ -830,8 +841,8 @@ static void handle_keep_open(struct MPContext *mpctx)
seek_to_last_frame(mpctx);
mpctx->playback_pts = mpctx->last_vo_pts;
}
- if (!mpctx->opts->pause)
- pause_player(mpctx);
+ if (opts->keep_open_pause)
+ set_pause_state(mpctx, true);
}
}
@@ -986,6 +997,7 @@ static void handle_playback_restart(struct MPContext *mpctx)
mpctx->audio_allow_second_chance_seek = false;
handle_playback_time(mpctx);
mp_notify(mpctx, MPV_EVENT_PLAYBACK_RESTART, NULL);
+ update_core_idle_state(mpctx);
if (!mpctx->playing_msg_shown) {
if (opts->playing_msg && opts->playing_msg[0]) {
char *msg =
@@ -1109,8 +1121,7 @@ void run_playloop(struct MPContext *mpctx)
handle_sstep(mpctx);
- if (mpctx->stop_play == AT_END_OF_FILE && mpctx->seek.type)
- mpctx->stop_play = KEEP_PLAYING;
+ update_core_idle_state(mpctx);
if (mpctx->stop_play)
return;
diff --git a/player/screenshot.c b/player/screenshot.c
index 4c043ab..7f79b2c 100644
--- a/player/screenshot.c
+++ b/player/screenshot.c
@@ -28,6 +28,8 @@
#include "core.h"
#include "command.h"
#include "misc/bstr.h"
+#include "misc/dispatch.h"
+#include "misc/thread_pool.h"
#include "common/msg.h"
#include "options/path.h"
#include "video/mp_image.h"
@@ -50,6 +52,8 @@ typedef struct screenshot_ctx {
bool osd;
int frameno;
+
+ struct mp_thread_pool *thread_pool;
} screenshot_ctx;
void screenshot_init(struct MPContext *mpctx)
@@ -61,9 +65,6 @@ void screenshot_init(struct MPContext *mpctx)
};
}
-#define SMSG_OK 0
-#define SMSG_ERR 1
-
static void screenshot_msg(screenshot_ctx *ctx, int status, const char *msg,
...) PRINTF_ATTRIBUTE(3,4);
@@ -77,8 +78,8 @@ static void screenshot_msg(screenshot_ctx *ctx, int status, const char *msg,
s = talloc_vasprintf(NULL, msg, ap);
va_end(ap);
- MP_MSG(ctx->mpctx, status == SMSG_ERR ? MSGL_ERR : MSGL_INFO, "%s\n", s);
- if (ctx->osd)
+ MP_MSG(ctx->mpctx, status, "%s\n", s);
+ if (ctx->osd && status <= MSGL_INFO)
set_osd_msg(ctx->mpctx, 1, ctx->mpctx->opts->osd_duration, "%s", s);
talloc_free(s);
@@ -92,6 +93,75 @@ static char *stripext(void *talloc_ctx, const char *s)
return talloc_asprintf(talloc_ctx, "%.*s", (int)(end - s), s);
}
+struct screenshot_item {
+ bool on_thread;
+ struct MPContext *mpctx;
+ const char *filename;
+ struct mp_image *img;
+ struct image_writer_opts opts;
+};
+
+#define LOCK(item) if (item->on_thread) mp_dispatch_lock(item->mpctx->dispatch);
+#define UNLOCK(item) if (item->on_thread) mp_dispatch_unlock(item->mpctx->dispatch);
+
+static void write_screenshot_thread(void *arg)
+{
+ struct screenshot_item *item = arg;
+ screenshot_ctx *ctx = item->mpctx->screenshot_ctx;
+
+ LOCK(item)
+ screenshot_msg(ctx, MSGL_INFO, "Screenshot: '%s'", item->filename);
+ UNLOCK(item)
+
+ if (!item->img || !write_image(item->img, &item->opts, item->filename,
+ item->mpctx->log))
+ {
+ LOCK(item)
+ screenshot_msg(ctx, MSGL_ERR, "Error writing screenshot!");
+ UNLOCK(item)
+ }
+
+ if (item->on_thread) {
+ mp_dispatch_lock(item->mpctx->dispatch);
+ screenshot_msg(ctx, MSGL_V, "Screenshot writing done.");
+ item->mpctx->outstanding_async -= 1;
+ mp_wakeup_core(item->mpctx);
+ mp_dispatch_unlock(item->mpctx->dispatch);
+ }
+
+ talloc_free(item);
+}
+
+static void write_screenshot(struct MPContext *mpctx, struct mp_image *img,
+ const char *filename, struct image_writer_opts *opts,
+ bool async)
+{
+ screenshot_ctx *ctx = mpctx->screenshot_ctx;
+ struct image_writer_opts *gopts = mpctx->opts->screenshot_image_opts;
+
+ struct screenshot_item *item = talloc_zero(NULL, struct screenshot_item);
+ *item = (struct screenshot_item){
+ .mpctx = mpctx,
+ .filename = talloc_strdup(item, filename),
+ .img = talloc_steal(item, mp_image_new_ref(img)),
+ .opts = opts ? *opts : *gopts,
+ };
+
+ if (async) {
+ if (!ctx->thread_pool)
+ ctx->thread_pool = mp_thread_pool_create(ctx, 1);
+ if (ctx->thread_pool) {
+ item->on_thread = true;
+ mpctx->outstanding_async += 1;
+ mp_thread_pool_queue(ctx->thread_pool, write_screenshot_thread, item);
+ item = NULL;
+ }
+ }
+
+ if (item)
+ write_screenshot_thread(item);
+}
+
#ifdef _WIN32
#define ILLEGAL_FILENAME_CHARS "?\"/\\<>*|:"
#else
@@ -277,7 +347,7 @@ static char *gen_fname(screenshot_ctx *ctx, const char *file_ext)
&ctx->frameno);
if (!fname) {
- screenshot_msg(ctx, SMSG_ERR, "Invalid screenshot filename "
+ screenshot_msg(ctx, MSGL_ERR, "Invalid screenshot filename "
"template! Fix or remove the --screenshot-template "
"option.");
return NULL;
@@ -298,7 +368,7 @@ static char *gen_fname(screenshot_ctx *ctx, const char *file_ext)
return fname;
if (sequence == prev_sequence) {
- screenshot_msg(ctx, SMSG_ERR, "Can't save screenshot, file '%s' "
+ screenshot_msg(ctx, MSGL_ERR, "Can't save screenshot, file '%s' "
"already exists!", fname);
talloc_free(fname);
return NULL;
@@ -315,21 +385,6 @@ static void add_subs(struct MPContext *mpctx, struct mp_image *image)
OSD_DRAW_SUB_ONLY, image);
}
-static void screenshot_save(struct MPContext *mpctx, struct mp_image *image)
-{
- screenshot_ctx *ctx = mpctx->screenshot_ctx;
-
- struct image_writer_opts *opts = mpctx->opts->screenshot_image_opts;
-
- char *filename = gen_fname(ctx, image_writer_file_ext(opts));
- if (filename) {
- screenshot_msg(ctx, SMSG_OK, "Screenshot: '%s'", filename);
- if (!write_image(image, opts, filename, mpctx->log))
- screenshot_msg(ctx, SMSG_ERR, "Error writing screenshot!");
- talloc_free(filename);
- }
-}
-
static struct mp_image *screenshot_get(struct MPContext *mpctx, int mode)
{
struct mp_image *image = NULL;
@@ -347,19 +402,16 @@ static struct mp_image *screenshot_get(struct MPContext *mpctx, int mode)
}
}
- bool hwimage = image && (image->fmt.flags & MP_IMGFLAG_HWACCEL);
- if (hwimage) {
+ if (image && (image->fmt.flags & MP_IMGFLAG_HWACCEL)) {
struct mp_image *nimage = mp_image_hw_download(image, NULL);
if (!nimage && mpctx->vo_chain && mpctx->vo_chain->hwdec_devs) {
struct mp_hwdec_ctx *ctx =
hwdec_devices_get_first(mpctx->vo_chain->hwdec_devs);
- if (ctx && ctx->download_image && hwimage)
+ if (ctx && ctx->download_image)
nimage = ctx->download_image(ctx, image, NULL);
}
- if (nimage) {
- talloc_free(image);
- image = nimage;
- }
+ talloc_free(image);
+ image = nimage;
}
if (image && mode == MODE_SUBTITLES)
@@ -379,7 +431,7 @@ struct mp_image *screenshot_get_rgb(struct MPContext *mpctx, int mode)
}
void screenshot_to_file(struct MPContext *mpctx, const char *filename, int mode,
- bool osd)
+ bool osd, bool async)
{
screenshot_ctx *ctx = mpctx->screenshot_ctx;
struct image_writer_opts opts = *mpctx->opts->screenshot_image_opts;
@@ -387,16 +439,15 @@ void screenshot_to_file(struct MPContext *mpctx, const char *filename, int mode,
ctx->osd = osd;
char *ext = mp_splitext(filename, NULL);
- if (ext)
- opts.format = ext;
+ int format = image_writer_format_from_ext(ext);
+ if (format)
+ opts.format = format;
struct mp_image *image = screenshot_get(mpctx, mode);
if (!image) {
- screenshot_msg(ctx, SMSG_ERR, "Taking screenshot failed.");
+ screenshot_msg(ctx, MSGL_ERR, "Taking screenshot failed.");
goto end;
}
- screenshot_msg(ctx, SMSG_OK, "Screenshot: '%s'", filename);
- if (!write_image(image, &opts, filename, mpctx->log))
- screenshot_msg(ctx, SMSG_ERR, "Error writing screenshot!");
+ write_screenshot(mpctx, image, filename, &opts, async);
talloc_free(image);
end:
@@ -404,7 +455,7 @@ end:
}
void screenshot_request(struct MPContext *mpctx, int mode, bool each_frame,
- bool osd)
+ bool osd, bool async)
{
screenshot_ctx *ctx = mpctx->screenshot_ctx;
@@ -425,9 +476,13 @@ void screenshot_request(struct MPContext *mpctx, int mode, bool each_frame,
struct mp_image *image = screenshot_get(mpctx, mode);
if (image) {
- screenshot_save(mpctx, image);
+ struct image_writer_opts *opts = mpctx->opts->screenshot_image_opts;
+ char *filename = gen_fname(ctx, image_writer_file_ext(opts));
+ if (filename)
+ write_screenshot(mpctx, image, filename, NULL, async);
+ talloc_free(filename);
} else {
- screenshot_msg(ctx, SMSG_ERR, "Taking screenshot failed.");
+ screenshot_msg(ctx, MSGL_ERR, "Taking screenshot failed.");
}
talloc_free(image);
@@ -441,5 +496,5 @@ void screenshot_flip(struct MPContext *mpctx)
return;
ctx->each_frame = false;
- screenshot_request(mpctx, ctx->mode, true, ctx->osd);
+ screenshot_request(mpctx, ctx->mode, true, ctx->osd, false);
}
diff --git a/player/screenshot.h b/player/screenshot.h
index aa5dfac..69f6ac8 100644
--- a/player/screenshot.h
+++ b/player/screenshot.h
@@ -31,13 +31,13 @@ void screenshot_init(struct MPContext *mpctx);
// screenshot slave command (MP_CMD_SCREENSHOT).
// osd: show status on OSD
void screenshot_request(struct MPContext *mpctx, int mode, bool each_frame,
- bool osd);
+ bool osd, bool async);
// filename: where to store the screenshot; doesn't try to find an alternate
// name if the file already exists
// mode, osd: same as in screenshot_request()
void screenshot_to_file(struct MPContext *mpctx, const char *filename, int mode,
- bool osd);
+ bool osd, bool async);
// mode is the same as in screenshot_request()
struct mp_image *screenshot_get_rgb(struct MPContext *mpctx, int mode);
diff --git a/player/video.c b/player/video.c
index 6ea3e4c..3bbb2dc 100644
--- a/player/video.c
+++ b/player/video.c
@@ -189,7 +189,8 @@ static int probe_deint_filters(struct vo_chain *vo_c)
if (check_output_format(vo_c, IMGFMT_D3D11VA) ||
check_output_format(vo_c, IMGFMT_D3D11NV12))
return try_filter(vo_c, "d3d11vpp", VF_DEINTERLACE_LABEL, NULL);
- return try_filter(vo_c, "yadif", VF_DEINTERLACE_LABEL, NULL);
+ char *args[] = {"warn", "no", NULL};
+ return try_filter(vo_c, "yadif", VF_DEINTERLACE_LABEL, args);
}
// Reconfigure the filter chain according to the new input format.
@@ -220,7 +221,7 @@ static void filter_reconfig(struct MPContext *mpctx, struct vo_chain *vo_c)
if (params.rotate) {
if (!(vo_c->vo->driver->caps & VO_CAP_ROTATE90) || params.rotate % 90) {
// Try to insert a rotation filter.
- char *args[] = {"angle", "auto", NULL};
+ char *args[] = {"angle", "auto", "warn", "no", NULL};
if (try_filter(vo_c, "rotate", "autorotate", args) < 0)
MP_ERR(vo_c, "Can't insert rotation filter.\n");
}
@@ -231,7 +232,7 @@ static void filter_reconfig(struct MPContext *mpctx, struct vo_chain *vo_c)
{
char *to = (char *)MP_STEREO3D_NAME(params.stereo_out);
if (to) {
- char *args[] = {"in", "auto", "out", to, NULL, NULL};
+ char *args[] = {"in", "auto", "out", to, "warn", "no", NULL, NULL};
if (try_filter(vo_c, "stereo3d", "autostereo3d", args) < 0)
MP_ERR(vo_c, "Can't insert 3D conversion filter.\n");
}
@@ -496,6 +497,9 @@ int reinit_video_chain_src(struct MPContext *mpctx, struct lavfi_pad *src)
vo_c->hwdec_devs = vo_c->vo->hwdec_devs;
+ if (mpctx->lavfi)
+ lavfi_set_hwdec_devs(mpctx->lavfi, vo_c->hwdec_devs);
+
vo_c->filter_src = src;
if (!vo_c->filter_src) {
vo_c->track = track;
@@ -1539,8 +1543,8 @@ void write_video(struct MPContext *mpctx)
if (mpctx->video_status != STATUS_EOF) {
if (mpctx->step_frames > 0) {
mpctx->step_frames--;
- if (!mpctx->step_frames && !opts->pause)
- pause_player(mpctx);
+ if (!mpctx->step_frames)
+ set_pause_state(mpctx, true);
}
if (mpctx->max_frames == 0 && !mpctx->stop_play)
mpctx->stop_play = AT_END_OF_FILE;
diff --git a/stream/dvb_tune.c b/stream/dvb_tune.c
index b83da04..09a2027 100644
--- a/stream/dvb_tune.c
+++ b/stream/dvb_tune.c
@@ -1,6 +1,7 @@
/* dvbtune - tune.c
Copyright (C) Dave Chapman 2001,2002
+ Copyright (C) Rozhuk Ivan <rozhuk.im@gmail.com> 2016
Modified for use with MPlayer, for details see the changelog at
http://svn.mplayerhq.hu/mplayer/trunk/
@@ -40,111 +41,148 @@
#include "dvb_tune.h"
#include "common/msg.h"
-int dvb_get_tuner_types(int fe_fd, struct mp_log *log, int** tuner_types)
+/* Keep in sync with enum fe_delivery_system. */
+static const char *dvb_delsys_str[] = {
+ "UNDEFINED",
+ "DVB-C ANNEX A",
+ "DVB-C ANNEX B",
+ "DVB-T",
+ "DSS",
+ "DVB-S",
+ "DVB-S2",
+ "DVB-H",
+ "ISDBT",
+ "ISDBS",
+ "ISDBC",
+ "ATSC",
+ "ATSCMH",
+ "DTMB",
+ "CMMB",
+ "DAB",
+ "DVB-T2",
+ "TURBO",
+ "DVB-C ANNEX C",
+ NULL
+};
+
+const char *get_dvb_delsys(unsigned int delsys)
+{
+ if (SYS_DVB__COUNT__ <= delsys)
+ return dvb_delsys_str[0];
+ return dvb_delsys_str[delsys];
+}
+
+unsigned int dvb_get_tuner_delsys_mask(int fe_fd, struct mp_log *log)
{
+ unsigned int ret_mask = 0, delsys;
+ struct dtv_property prop[1];
+ struct dtv_properties cmdseq = {.num = 1, .props = prop};
+ struct dvb_frontend_info fe_info;
+
#ifdef DVB_USE_S2API
/* S2API is the DVB API new since 2.6.28.
It allows to query frontends with multiple delivery systems. */
- struct dtv_property p[] = {{ .cmd = DTV_ENUM_DELSYS }};
- struct dtv_properties cmdseq = {.num = 1, .props = p};
mp_verbose(log, "Querying tuner type via DVBv5 API for frontend FD %d\n",
fe_fd);
- if ((ioctl(fe_fd, FE_GET_PROPERTY, &cmdseq)) < -0) {
- mp_err(log, "FE_GET_PROPERTY error: %d, FD: %d\n\n", errno, fe_fd);
- return 0;
+ prop[0].cmd = DTV_ENUM_DELSYS;
+ if (ioctl(fe_fd, FE_GET_PROPERTY, &cmdseq) < 0) {
+ mp_err(log, "DVBv5: FE_GET_PROPERTY(DTV_ENUM_DELSYS) error: %d, FD: %d\n\n", errno, fe_fd);
+ goto old_api;
}
- int num_tuner_types = p[0].u.buffer.len;
- mp_verbose(log, "Number of supported delivery systems: %d\n", num_tuner_types);
- if (num_tuner_types == 0) {
- mp_err(log, "Frontend FD %d returned no delivery systems!", fe_fd);
- return 0;
+ unsigned int i, delsys_count = prop[0].u.buffer.len;
+ mp_verbose(log, "DVBv5: Number of supported delivery systems: %d\n", delsys_count);
+ if (delsys_count == 0) {
+ mp_err(log, "DVBv5: Frontend FD %d returned no delivery systems!\n", fe_fd);
+ goto old_api;
}
- (*tuner_types) = talloc_array(NULL, int, num_tuner_types);
- int supported_tuners = 0;
- for(;p[0].u.buffer.len > 0; p[0].u.buffer.len--) {
- fe_delivery_system_t delsys = p[0].u.buffer.data[p[0].u.buffer.len - 1];
- /* Second level standards like like DVB-T2, DVB-S2 not treated here -
- Cards can usually either only do S/T/C or both levels.
- DVB-T2 probably needs more implementation details,
- DVB-S2 is treated in the DVB-S branch already. */
- switch (delsys) {
- case SYS_DVBT:
- mp_verbose(log, "Tuner type seems to be DVB-T\n");
- (*tuner_types)[supported_tuners++] = TUNER_TER;
- break;
- case SYS_DVBC_ANNEX_AC:
- mp_verbose(log, "Tuner type seems to be DVB-C\n");
- (*tuner_types)[supported_tuners++] = TUNER_CBL;
- break;
- case SYS_DVBS:
- mp_verbose(log, "Tuner type seems to be DVB-S\n");
- (*tuner_types)[supported_tuners++] = TUNER_SAT;
- break;
-#ifdef DVB_ATSC
- case SYS_ATSC:
- mp_verbose(log, "Tuner type seems to be DVB-ATSC\n");
- (*tuner_types)[supported_tuners++] = TUNER_ATSC;
- break;
+ for (i = 0; i < delsys_count; i++) {
+ delsys = (unsigned int)prop[0].u.buffer.data[i];
+ DELSYS_SET(ret_mask, delsys);
+ mp_verbose(log, "DVBv5: Tuner type seems to be %s\n", get_dvb_delsys(delsys));
+ }
+
+ return ret_mask;
+
+old_api:
#endif
- case SYS_DVBS2:
- // We actually handle that in the DVB-S branch, ok to ignore here.
- mp_verbose(log, "Tuner supports DVB-S2\n");
- break;
- default:
- mp_err(log, "Unhandled tuner type: %d\n", delsys);
- }
+ mp_verbose(log, "Querying tuner type via pre-DVBv5 API for frontend FD %d\n",
+ fe_fd);
+
+ memset(&fe_info, 0x00, sizeof(struct dvb_frontend_info));
+ if (ioctl(fe_fd, FE_GET_INFO, &fe_info) < 0) {
+ mp_err(log, "DVBv3: FE_GET_INFO error: %d, FD: %d\n\n", errno, fe_fd);
+ return ret_mask;
}
- return supported_tuners;
-#else
- struct dvb_frontend_info fe_info;
- int res = ioctl(fe_fd, FE_GET_INFO, &fe_info);
- if (res < 0) {
- mp_err(log, "FE_GET_INFO error: %d, FD: %d\n\n", errno, fe_fd);
- return 0;
+ /* Try to get kernel DVB API version. */
+ prop[0].cmd = DTV_API_VERSION;
+ if (ioctl(fe_fd, FE_GET_PROPERTY, &cmdseq) < 0) {
+ prop[0].u.data = 0x0300; /* Fail, assume 3.0 */
}
- mp_verbose(log, "Queried tuner type of device named '%s', FD: %d\n",
+ mp_verbose(log, "DVBv3: Queried tuner type of device named '%s', FD: %d\n",
fe_info.name, fe_fd);
switch (fe_info.type) {
case FE_OFDM:
- mp_verbose(log, "Tuner type seems to be DVB-T\n");
- *tuner_types = talloc_array(NULL, int, 1);
- (*tuner_types)[0] = TUNER_TER;
- return 1;
+ DELSYS_SET(ret_mask, SYS_DVBT);
+ if (prop[0].u.data < 0x0500)
+ break;
+ if (FE_CAN_2G_MODULATION & fe_info.caps) {
+ DELSYS_SET(ret_mask, SYS_DVBT2);
+ }
+ break;
case FE_QPSK:
- mp_verbose(log, "Tuner type seems to be DVB-S\n");
- *tuner_types = talloc_array(NULL, int, 1);
- (*tuner_types)[0] = TUNER_SAT;
- return 1;
+ DELSYS_SET(ret_mask, SYS_DVBS);
+ if (prop[0].u.data < 0x0500)
+ break;
+ if (FE_CAN_2G_MODULATION & fe_info.caps) {
+ DELSYS_SET(ret_mask, SYS_DVBS2);
+ }
+#if 0 /* Not used now. */
+ if (FE_CAN_TURBO_FEC & fe_info.caps) {
+ DELSYS_SET(ret_mask, SYS_TURBO);
+ }
+#endif
+ break;
case FE_QAM:
- mp_verbose(log, "Tuner type seems to be DVB-C\n");
- *tuner_types = talloc_array(NULL, int, 1);
- (*tuner_types)[0] = TUNER_CBL;
- return 1;
+ DELSYS_SET(ret_mask, SYS_DVBC_ANNEX_A);
+ DELSYS_SET(ret_mask, SYS_DVBC_ANNEX_C);
+ break;
#ifdef DVB_ATSC
case FE_ATSC:
- mp_verbose(log, "Tuner type seems to be DVB-ATSC\n");
- *tuner_types = talloc_array(NULL, int, 1);
- (*tuner_types)[0] = TUNER_ATSC;
- return 1;
+ if ((FE_CAN_8VSB | FE_CAN_16VSB) & fe_info.caps) {
+ DELSYS_SET(ret_mask, SYS_ATSC);
+ }
+#if 0 /* Not used now. */
+ if ((FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_QAM_AUTO) & fe_info.caps) {
+ DELSYS_SET(ret_mask, SYS_DVBC_ANNEX_B);
+ }
+#endif
+ break;
#endif
default:
- mp_err(log, "Unknown tuner type: %d\n", fe_info.type);
- return 0;
+ mp_err(log, "DVBv3: Unknown tuner type: %d\n", fe_info.type);
+ return ret_mask;
}
-#endif
+
+ for (delsys = 0; delsys < SYS_DVB__COUNT__; delsys ++) {
+ if (!DELSYS_IS_SET(ret_mask, delsys))
+ continue; /* Skip unsupported. */
+ mp_verbose(log, "DVBv3: Tuner type seems to be %s\n", get_dvb_delsys(delsys));
+ }
+
+ return ret_mask;
}
-int dvb_open_devices(dvb_priv_t *priv, int n, int demux_cnt)
+int dvb_open_devices(dvb_priv_t *priv, unsigned int n, unsigned int demux_cnt)
{
- int i;
- char frontend_dev[32], dvr_dev[32], demux_dev[32];
+ unsigned int i;
+ char frontend_dev[PATH_MAX], dvr_dev[PATH_MAX], demux_dev[PATH_MAX];
dvb_state_t* state = priv->state;
- sprintf(frontend_dev, "/dev/dvb/adapter%d/frontend0", n);
- sprintf(dvr_dev, "/dev/dvb/adapter%d/dvr0", n);
- sprintf(demux_dev, "/dev/dvb/adapter%d/demux0", n);
+ snprintf(frontend_dev, sizeof(frontend_dev), "/dev/dvb/adapter%u/frontend0", n);
+ snprintf(dvr_dev, sizeof(dvr_dev), "/dev/dvb/adapter%u/dvr0", n);
+ snprintf(demux_dev, sizeof(demux_dev), "/dev/dvb/adapter%u/demux0", n);
state->fe_fd = open(frontend_dev, O_RDWR | O_NONBLOCK | O_CLOEXEC);
if (state->fe_fd < 0) {
MP_ERR(priv, "ERROR OPENING FRONTEND DEVICE %s: ERRNO %d\n",
@@ -165,7 +203,6 @@ int dvb_open_devices(dvb_priv_t *priv, int n, int demux_cnt)
}
}
-
state->dvr_fd = open(dvr_dev, O_RDONLY | O_NONBLOCK | O_CLOEXEC);
if (state->dvr_fd < 0) {
MP_ERR(priv, "ERROR OPENING DVR DEVICE %s: %d\n", dvr_dev, errno);
@@ -176,22 +213,23 @@ int dvb_open_devices(dvb_priv_t *priv, int n, int demux_cnt)
}
-int dvb_fix_demuxes(dvb_priv_t *priv, int cnt)
+int dvb_fix_demuxes(dvb_priv_t *priv, unsigned int cnt)
{
int i;
- char demux_dev[32];
+ char demux_dev[PATH_MAX];
dvb_state_t* state = priv->state;
- sprintf(demux_dev, "/dev/dvb/adapter%d/demux0", state->card);
+ snprintf(demux_dev, sizeof(demux_dev), "/dev/dvb/adapter%d/demux0",
+ state->adapters[state->cur_adapter].devno);
MP_VERBOSE(priv, "FIX %d -> %d\n", state->demux_fds_cnt, cnt);
if (state->demux_fds_cnt >= cnt) {
- for (i = state->demux_fds_cnt - 1; i >= cnt; i--) {
+ for (i = state->demux_fds_cnt - 1; i >= (int)cnt; i--) {
MP_VERBOSE(priv, "FIX, CLOSE fd(%d): %d\n", i, state->demux_fds[i]);
close(state->demux_fds[i]);
}
state->demux_fds_cnt = cnt;
- } else if (state->demux_fds_cnt < cnt) {
+ } else {
for (i = state->demux_fds_cnt; i < cnt; i++) {
state->demux_fds[i] = open(demux_dev,
O_RDWR | O_NONBLOCK | O_CLOEXEC);
@@ -220,7 +258,7 @@ int dvb_set_ts_filt(dvb_priv_t *priv, int fd, uint16_t pid,
pesFilterParams.flags = DMX_IMMEDIATE_START;
{
- int buffersize = 64 * 1024;
+ int buffersize = 256 * 1024;
if (ioctl(fd, DMX_SET_BUFFER_SIZE, buffersize) < 0)
MP_ERR(priv, "ERROR IN DMX_SET_BUFFER_SIZE %i for fd %d: ERRNO: %d\n",
pid, fd, errno);
@@ -238,16 +276,16 @@ int dvb_set_ts_filt(dvb_priv_t *priv, int fd, uint16_t pid,
return 1;
}
-int dvb_get_pmt_pid(dvb_priv_t *priv, int card, int service_id)
+int dvb_get_pmt_pid(dvb_priv_t *priv, int devno, int service_id)
{
/* We need special filters on the demux,
so open one locally, and close also here. */
- char demux_dev[32];
- sprintf(demux_dev, "/dev/dvb/adapter%d/demux0", card);
+ char demux_dev[PATH_MAX];
+ snprintf(demux_dev, sizeof(demux_dev), "/dev/dvb/adapter%d/demux0", devno);
struct dmx_sct_filter_params fparams;
- memset(&fparams, 0, sizeof(fparams));
+ memset(&fparams, 0x00, sizeof(fparams));
fparams.pid = 0;
fparams.filter.filter[0] = 0x00;
fparams.filter.mask[0] = 0xff;
@@ -260,7 +298,7 @@ int dvb_get_pmt_pid(dvb_priv_t *priv, int card, int service_id)
return -1;
}
- if (ioctl(pat_fd, DMX_SET_FILTER, &fparams) == -1) {
+ if (ioctl(pat_fd, DMX_SET_FILTER, &fparams) < 0) {
MP_ERR(priv, "ioctl DMX_SET_FILTER failed, error: %d", errno);
close(pat_fd);
return -1;
@@ -309,16 +347,6 @@ int dvb_get_pmt_pid(dvb_priv_t *priv, int card, int service_id)
return pmt_pid;
}
-int dvb_demux_stop(int fd)
-{
- return ioctl(fd, DMX_STOP) == 0;
-}
-
-int dvb_demux_start(int fd)
-{
- return ioctl(fd, DMX_START) == 0;
-}
-
static void print_status(dvb_priv_t *priv, fe_status_t festatus)
{
MP_VERBOSE(priv, "FE_STATUS:");
@@ -400,20 +428,21 @@ struct diseqc_cmd {
static int diseqc_send_msg(int fd, fe_sec_voltage_t v, struct diseqc_cmd *cmd,
fe_sec_tone_mode_t t, fe_sec_mini_cmd_t b)
{
- if (ioctl(fd, FE_SET_TONE, SEC_TONE_OFF) == -1)
+ if (ioctl(fd, FE_SET_TONE, SEC_TONE_OFF) < 0)
return -1;
- if (ioctl(fd, FE_SET_VOLTAGE, v) == -1)
+ if (ioctl(fd, FE_SET_VOLTAGE, v) < 0)
return -1;
usleep(15 * 1000);
- if (ioctl(fd, FE_DISEQC_SEND_MASTER_CMD, &cmd->cmd) == -1)
+ if (ioctl(fd, FE_DISEQC_SEND_MASTER_CMD, &cmd->cmd) < 0)
return -1;
usleep(cmd->wait * 1000);
usleep(15 * 1000);
- if (ioctl(fd, FE_DISEQC_SEND_BURST, b) == -1)
+ if (ioctl(fd, FE_DISEQC_SEND_BURST, b) < 0)
return -1;
usleep(15 * 1000);
- if (ioctl(fd, FE_SET_TONE, t) == -1)
+ if (ioctl(fd, FE_SET_TONE, t) < 0)
return -1;
+ usleep(100000);
return 0;
}
@@ -432,12 +461,12 @@ static int do_diseqc(int secfd, int sat_no, int polv, int hi_lo)
return diseqc_send_msg(secfd, polv ? SEC_VOLTAGE_13 : SEC_VOLTAGE_18,
&cmd, hi_lo ? SEC_TONE_ON : SEC_TONE_OFF,
- (sat_no / 4) % 2 ? SEC_MINI_B : SEC_MINI_A);
+ ((sat_no / 4) % 2) ? SEC_MINI_B : SEC_MINI_A);
}
-static int tune_it(dvb_priv_t *priv, int fd_frontend,
- unsigned int freq, unsigned int srate, char pol, int tone,
- bool is_dvb_s2, int stream_id,
+static int tune_it(dvb_priv_t *priv, int fd_frontend, unsigned int delsys,
+ unsigned int freq, unsigned int srate, char pol,
+ int stream_id,
fe_spectral_inversion_t specInv, unsigned int diseqc,
fe_modulation_t modulation,
fe_code_rate_t HP_CodeRate,
@@ -447,205 +476,228 @@ static int tune_it(dvb_priv_t *priv, int fd_frontend,
fe_code_rate_t LP_CodeRate, fe_hierarchy_t hier,
int timeout)
{
- int hi_lo = 0, dfd;
-
+ int hi_lo = 0, bandwidth_hz = 0;
dvb_state_t* state = priv->state;
-
struct dvb_frontend_parameters feparams;
- MP_VERBOSE(priv, "TUNE_IT, fd_frontend %d, freq %lu, srate %lu, "
- "pol %c, tone %i, diseqc %u\n", fd_frontend,
- (long unsigned int)freq, (long unsigned int)srate, pol,
- tone, diseqc);
- memset(&feparams, 0, sizeof(feparams));
+ MP_VERBOSE(priv, "TUNE_IT, fd_frontend %d, %s freq %lu, srate %lu, "
+ "pol %c, diseqc %u\n", fd_frontend,
+ get_dvb_delsys(delsys),
+ (long unsigned int)freq, (long unsigned int)srate,
+ (pol > ' ' ? pol : '-'), diseqc);
- MP_VERBOSE(priv, "Using DVB card \"%s\"\n", state->cards[state->card].name);
+ MP_VERBOSE(priv, "Using %s adapter %d\n",
+ get_dvb_delsys(delsys),
+ state->adapters[state->cur_adapter].devno);
{
/* discard stale QPSK events */
struct dvb_frontend_event ev;
while (true) {
- if (ioctl(fd_frontend, FE_GET_EVENT, &ev) == -1)
+ if (ioctl(fd_frontend, FE_GET_EVENT, &ev) < 0)
break;
}
}
- switch (state->tuner_type) {
- case TUNER_TER: {
+ /* Prepare params, be verbose. */
+ switch (delsys) {
+ case SYS_DVBT2:
+#ifndef DVB_USE_S2API
+ MP_ERR(priv, "ERROR: Can not tune to T2 channel, S2-API not "
+ "available, will tune to DVB-T!\n");
+#endif
+ /* PASSTROUTH. */
+ case SYS_DVBT:
if (freq < 1000000)
freq *= 1000UL;
- feparams.frequency = freq;
- feparams.inversion = specInv;
- feparams.u.ofdm.bandwidth = bandwidth;
- feparams.u.ofdm.code_rate_HP = HP_CodeRate;
- feparams.u.ofdm.code_rate_LP = LP_CodeRate;
- feparams.u.ofdm.constellation = modulation;
- feparams.u.ofdm.transmission_mode = TransmissionMode;
- feparams.u.ofdm.guard_interval = guardInterval;
- feparams.u.ofdm.hierarchy_information = hier;
- MP_VERBOSE(priv, "tuning DVB-T to %d Hz, bandwidth: %d\n",
- freq, bandwidth);
- if (ioctl(fd_frontend, FE_SET_FRONTEND, &feparams) < 0) {
- MP_ERR(priv, "ERROR tuning channel\n");
- return -1;
+ switch (bandwidth) {
+ case BANDWIDTH_5_MHZ:
+ bandwidth_hz = 5000000;
+ break;
+ case BANDWIDTH_6_MHZ:
+ bandwidth_hz = 6000000;
+ break;
+ case BANDWIDTH_7_MHZ:
+ bandwidth_hz = 7000000;
+ break;
+ case BANDWIDTH_8_MHZ:
+ bandwidth_hz = 8000000;
+ break;
+ case BANDWIDTH_10_MHZ:
+ bandwidth_hz = 10000000;
+ break;
+ case BANDWIDTH_AUTO:
+ if (freq < 474000000) {
+ bandwidth_hz = 7000000;
+ } else {
+ bandwidth_hz = 8000000;
+ }
+ break;
+ default:
+ bandwidth_hz = 0;
+ break;
}
- }
- break;
- case TUNER_SAT: {
- // DVB-S
+
+ MP_VERBOSE(priv, "tuning %s to %d Hz, bandwidth: %d\n",
+ get_dvb_delsys(delsys), freq, bandwidth_hz);
+ break;
+ case SYS_DVBS2:
+#ifndef DVB_USE_S2API
+ MP_ERR(priv, "ERROR: Can not tune to S2 channel, S2-API not "
+ "available, will tune to DVB-S!\n");
+#endif
+ /* PASSTROUTH. */
+ case SYS_DVBS:
if (freq > 2200000) {
// this must be an absolute frequency
if (freq < SLOF) {
- freq = feparams.frequency = (freq - LOF1);
+ freq -= LOF1;
hi_lo = 0;
} else {
- freq = feparams.frequency = (freq - LOF2);
+ freq -= LOF2;
hi_lo = 1;
}
- } else {
- // this is an L-Band frequency
- feparams.frequency = freq;
}
-
- feparams.inversion = specInv;
- feparams.u.qpsk.symbol_rate = srate;
- feparams.u.qpsk.fec_inner = HP_CodeRate;
- dfd = fd_frontend;
-
- MP_VERBOSE(priv, "tuning DVB-S%sto Freq: %u, Pol: %c Srate: %d, "
- "22kHz: %s, LNB: %d\n", is_dvb_s2 ? "2 " : " ", freq,
+ MP_VERBOSE(priv, "tuning %s to Freq: %u, Pol: %c Srate: %d, "
+ "22kHz: %s, LNB: %d\n", get_dvb_delsys(delsys), freq,
pol, srate, hi_lo ? "on" : "off", diseqc);
- if (do_diseqc(dfd, diseqc, (pol == 'V' ? 1 : 0), hi_lo) == 0) {
+ if (do_diseqc(fd_frontend, diseqc, (pol == 'V' ? 1 : 0), hi_lo) == 0) {
MP_VERBOSE(priv, "DISEQC setting succeeded\n");
} else {
MP_ERR(priv, "DISEQC setting failed\n");
return -1;
}
- usleep(100000);
-#ifdef DVB_USE_S2API
- /* S2API is the DVB API new since 2.6.28.
- * It is needed to tune to new delivery systems, e.g. DVB-S2.
- * It takes a struct with a list of pairs of command + parameter.
- */
-
- fe_delivery_system_t delsys = SYS_DVBS;
- if (is_dvb_s2)
- delsys = SYS_DVBS2;
- fe_rolloff_t rolloff = ROLLOFF_AUTO;
-
- struct dtv_property p[] = {
- { .cmd = DTV_DELIVERY_SYSTEM, .u.data = delsys },
- { .cmd = DTV_FREQUENCY, .u.data = freq },
- { .cmd = DTV_MODULATION, .u.data = modulation },
- { .cmd = DTV_SYMBOL_RATE, .u.data = srate },
- { .cmd = DTV_INNER_FEC, .u.data = HP_CodeRate },
- { .cmd = DTV_INVERSION, .u.data = specInv },
- { .cmd = DTV_ROLLOFF, .u.data = rolloff },
- { .cmd = DTV_PILOT, .u.data = PILOT_AUTO },
- { .cmd = DTV_STREAM_ID, .u.data = stream_id },
- { .cmd = DTV_TUNE },
- };
- struct dtv_properties cmdseq = {
- .num = sizeof(p) / sizeof(p[0]),
- .props = p
- };
- MP_VERBOSE(priv, "Tuning via S2API, channel is DVB-S%s.\n",
- is_dvb_s2 ? "2" : "");
- if ((ioctl(fd_frontend, FE_SET_PROPERTY, &cmdseq)) == -1) {
- MP_ERR(priv, "ERROR tuning channel\n");
- return -1;
- }
-#else
- MP_VERBOSE(priv, "Tuning via DVB-API version 3.\n");
- if (is_dvb_s2) {
- MP_ERR(priv, "ERROR: Can not tune to S2 channel, S2-API not "
- "available, will tune to DVB-S!\n");
- }
- if (ioctl(fd_frontend, FE_SET_FRONTEND, &feparams) < 0) {
- MP_ERR(priv, "ERROR tuning channel\n");
- return -1;
- }
+ break;
+ case SYS_DVBC_ANNEX_A:
+ case SYS_DVBC_ANNEX_C:
+ MP_VERBOSE(priv, "tuning %s to %d, srate=%d\n",
+ get_dvb_delsys(delsys), freq, srate);
+ break;
+#ifdef DVB_ATSC
+ case SYS_ATSC:
+ MP_VERBOSE(priv, "tuning %s to %d, modulation=%d\n",
+ get_dvb_delsys(delsys), freq, modulation);
+ break;
#endif
+ default:
+ MP_VERBOSE(priv, "Unknown FE type. Aborting\n");
+ return 0;
}
- break;
- case TUNER_CBL: {
+
#ifdef DVB_USE_S2API
- /* S2API is the DVB API new since 2.6.28.
- * It is also needed for devices supporting multiple delivery systems,
- * commonly DVB-C + DVB-T are supported here.
- */
- fe_delivery_system_t delsys = SYS_DVBC_ANNEX_AC;
- struct dtv_property p[] = {
- { .cmd = DTV_DELIVERY_SYSTEM, .u.data = delsys },
- { .cmd = DTV_FREQUENCY, .u.data = freq },
- { .cmd = DTV_INVERSION, .u.data = specInv },
- { .cmd = DTV_MODULATION, .u.data = modulation },
- { .cmd = DTV_SYMBOL_RATE, .u.data = srate },
- { .cmd = DTV_INNER_FEC, .u.data = HP_CodeRate },
- { .cmd = DTV_TUNE },
- };
- struct dtv_properties cmdseq = {
- .num = sizeof(p) / sizeof(p[0]),
- .props = p
- };
- MP_VERBOSE(priv, "tuning DVB-C to %d, srate=%d using DVBv5 API...\n",
- freq, srate);
- if ((ioctl(fd_frontend, FE_SET_PROPERTY, &cmdseq)) == -1) {
- MP_ERR(priv, "ERROR tuning channel\n");
- return -1;
- }
-#else
- feparams.frequency = freq;
- feparams.inversion = specInv;
+ /* S2API is the DVB API new since 2.6.28.
+ * It is needed to tune to new delivery systems, e.g. DVB-S2.
+ * It takes a struct with a list of pairs of command + parameter.
+ */
+
+ /* Reset before tune. */
+ struct dtv_property p_clear[] = {
+ { .cmd = DTV_CLEAR },
+ };
+ struct dtv_properties cmdseq_clear = {
+ .num = 1,
+ .props = p_clear
+ };
+ if (ioctl(fd_frontend, FE_SET_PROPERTY, &cmdseq_clear) < 0) {
+ MP_ERR(priv, "FE_SET_PROPERTY DTV_CLEAR failed\n");
+ }
+
+ /* Tune. */
+ struct dtv_property p[] = {
+ { .cmd = DTV_DELIVERY_SYSTEM, .u.data = delsys },
+ { .cmd = DTV_FREQUENCY, .u.data = freq },
+ { .cmd = DTV_MODULATION, .u.data = modulation },
+ { .cmd = DTV_SYMBOL_RATE, .u.data = srate },
+ { .cmd = DTV_INNER_FEC, .u.data = HP_CodeRate },
+ { .cmd = DTV_INVERSION, .u.data = specInv },
+ { .cmd = DTV_ROLLOFF, .u.data = ROLLOFF_AUTO },
+ { .cmd = DTV_BANDWIDTH_HZ, .u.data = bandwidth_hz },
+ { .cmd = DTV_PILOT, .u.data = PILOT_AUTO },
+ { .cmd = DTV_STREAM_ID, .u.data = stream_id },
+ { .cmd = DTV_TUNE },
+ };
+ struct dtv_properties cmdseq = {
+ .num = sizeof(p) / sizeof(p[0]),
+ .props = p
+ };
+ MP_VERBOSE(priv, "Tuning via S2API, channel is %s.\n",
+ get_dvb_delsys(delsys));
+ if (ioctl(fd_frontend, FE_SET_PROPERTY, &cmdseq) < 0) {
+ MP_ERR(priv, "ERROR tuning channel\n");
+ goto old_api;
+ }
+
+ return check_status(priv, fd_frontend, timeout);
+
+old_api:
+#endif
+
+ MP_VERBOSE(priv, "Tuning via DVB-API version 3.\n");
+
+ if (stream_id != NO_STREAM_ID_FILTER && stream_id != 0) {
+ MP_ERR(priv, "DVB-API version 3 does not support stream_id (PLP).\n");
+ return -1;
+ }
+ memset(&feparams, 0x00, sizeof(feparams));
+ feparams.frequency = freq;
+ feparams.inversion = specInv;
+
+ switch (delsys) {
+ case SYS_DVBT:
+ case SYS_DVBT2:
+ feparams.u.ofdm.bandwidth = bandwidth;
+ feparams.u.ofdm.code_rate_HP = HP_CodeRate;
+ feparams.u.ofdm.code_rate_LP = LP_CodeRate;
+ feparams.u.ofdm.constellation = modulation;
+ feparams.u.ofdm.transmission_mode = TransmissionMode;
+ feparams.u.ofdm.guard_interval = guardInterval;
+ feparams.u.ofdm.hierarchy_information = hier;
+ break;
+ case SYS_DVBS:
+ case SYS_DVBS2:
+ feparams.u.qpsk.symbol_rate = srate;
+ feparams.u.qpsk.fec_inner = HP_CodeRate;
+ break;
+ case SYS_DVBC_ANNEX_A:
+ case SYS_DVBC_ANNEX_C:
feparams.u.qam.symbol_rate = srate;
feparams.u.qam.fec_inner = HP_CodeRate;
feparams.u.qam.modulation = modulation;
- MP_VERBOSE(priv, "tuning DVB-C to %d, srate=%d\n", freq, srate);
- if (ioctl(fd_frontend, FE_SET_FRONTEND, &feparams) < 0) {
- MP_ERR(priv, "ERROR tuning channel\n");
- return -1;
- }
-#endif
- }
- break;
+ break;
#ifdef DVB_ATSC
- case TUNER_ATSC: {
- feparams.frequency = freq;
+ case SYS_ATSC:
feparams.u.vsb.modulation = modulation;
- MP_VERBOSE(priv, "tuning ATSC to %d, modulation=%d\n", freq, modulation);
- if (ioctl(fd_frontend, FE_SET_FRONTEND, &feparams) < 0) {
- MP_ERR(priv, "ERROR tuning channel\n");
- return -1;
- }
- }
- break;
+ break;
#endif
- default:
- MP_VERBOSE(priv, "Unknown FE type. Aborting\n");
- return 0;
+ }
+
+ if (ioctl(fd_frontend, FE_SET_FRONTEND, &feparams) < 0) {
+ MP_ERR(priv, "ERROR tuning channel\n");
+ return -1;
}
return check_status(priv, fd_frontend, timeout);
}
-int dvb_tune(dvb_priv_t *priv, int freq, char pol, int srate, int diseqc,
- int tone,
- bool is_dvb_s2, int stream_id, fe_spectral_inversion_t specInv,
+int dvb_tune(dvb_priv_t *priv, unsigned int delsys,
+ int freq, char pol, int srate, int diseqc,
+ int stream_id, fe_spectral_inversion_t specInv,
fe_modulation_t modulation, fe_guard_interval_t guardInterval,
fe_transmit_mode_t TransmissionMode, fe_bandwidth_t bandWidth,
fe_code_rate_t HP_CodeRate,
fe_code_rate_t LP_CodeRate, fe_hierarchy_t hier,
int timeout)
{
- MP_INFO(priv, "dvb_tune Freq: %lu\n", (long unsigned int) freq);
+ MP_INFO(priv, "dvb_tune %s Freq: %lu\n",
+ get_dvb_delsys(delsys), (long unsigned int) freq);
dvb_state_t* state = priv->state;
- int ris = tune_it(priv, state->fe_fd, freq, srate, pol, tone,
- is_dvb_s2, stream_id, specInv, diseqc, modulation,
+ int ris = tune_it(priv, state->fe_fd, delsys, freq, srate, pol,
+ stream_id, specInv, diseqc, modulation,
HP_CodeRate, TransmissionMode, guardInterval,
bandWidth, LP_CodeRate, hier, timeout);
diff --git a/stream/dvb_tune.h b/stream/dvb_tune.h
index dafa117..756f730 100644
--- a/stream/dvb_tune.h
+++ b/stream/dvb_tune.h
@@ -23,15 +23,16 @@
struct mp_log;
-int dvb_get_tuner_types(int fe_fd, struct mp_log *log, int** tuner_types);
-int dvb_open_devices(dvb_priv_t *priv, int n, int demux_cnt);
-int dvb_fix_demuxes(dvb_priv_t *priv, int cnt);
+
+const char *get_dvb_delsys(unsigned int delsys);
+unsigned int dvb_get_tuner_delsys_mask(int fe_fd, struct mp_log *log);
+int dvb_open_devices(dvb_priv_t *priv, unsigned int n, unsigned int demux_cnt);
+int dvb_fix_demuxes(dvb_priv_t *priv, unsigned int cnt);
int dvb_set_ts_filt(dvb_priv_t *priv, int fd, uint16_t pid, dmx_pes_type_t pestype);
int dvb_get_pmt_pid(dvb_priv_t *priv, int card, int service_id);
-int dvb_demux_stop(int fd);
-int dvb_demux_start(int fd);
-int dvb_tune(dvb_priv_t *priv, int freq, char pol, int srate, int diseqc,
- int tone, bool is_dvb_s2, int stream_id, fe_spectral_inversion_t specInv,
+int dvb_tune(dvb_priv_t *priv, unsigned int delsys,
+ int freq, char pol, int srate, int diseqc,
+ int stream_id, fe_spectral_inversion_t specInv,
fe_modulation_t modulation, fe_guard_interval_t guardInterval,
fe_transmit_mode_t TransmissionMode, fe_bandwidth_t bandWidth,
fe_code_rate_t HP_CodeRate, fe_code_rate_t LP_CodeRate,
diff --git a/stream/dvbin.h b/stream/dvbin.h
index e461364..02b6a72 100644
--- a/stream/dvbin.h
+++ b/stream/dvbin.h
@@ -62,12 +62,13 @@
typedef struct {
char *name;
- int freq, srate, diseqc, tone;
+ unsigned int freq, srate, diseqc;
char pol;
- int tpid, dpid1, dpid2, progid, ca, pids[DMX_FILTER_SIZE], pids_cnt;
- bool is_dvb_s2;
- int stream_id;
- int service_id;
+ unsigned int tpid, dpid1, dpid2, progid, ca, pids[DMX_FILTER_SIZE], pids_cnt;
+ bool is_dvb_x2;
+ unsigned int delsys;
+ unsigned int stream_id;
+ unsigned int service_id;
fe_spectral_inversion_t inv;
fe_modulation_t mod;
fe_transmit_mode_t trans;
@@ -78,57 +79,96 @@ typedef struct {
} dvb_channel_t;
typedef struct {
- uint16_t NUM_CHANNELS;
- uint16_t current;
+ unsigned int NUM_CHANNELS;
+ unsigned int current;
dvb_channel_t *channels;
-} dvb_channels_list;
+} dvb_channels_list_t;
typedef struct {
- int type;
- dvb_channels_list *list;
- char *name;
int devno;
-} dvb_card_config_t;
+ unsigned int delsys_mask;
+ dvb_channels_list_t *list;
+} dvb_adapter_config_t;
typedef struct {
- int count;
- dvb_card_config_t *cards;
+ unsigned int adapters_count;
+ dvb_adapter_config_t *adapters;
+ unsigned int cur_adapter;
- int card;
int fe_fd;
int dvr_fd;
int demux_fd[3], demux_fds[DMX_FILTER_SIZE], demux_fds_cnt;
- dvb_channels_list *list;
- int tuner_type;
int is_on;
int retry;
int timeout;
- int last_freq;
+ unsigned int last_freq;
bool switching_channel;
bool stream_used;
} dvb_state_t;
-typedef struct dvb_params {
+typedef struct {
struct mp_log *log;
dvb_state_t *state;
char *cfg_prog;
- int cfg_card;
+ int cfg_devno;
int cfg_timeout;
char *cfg_file;
int cfg_full_transponder;
} dvb_priv_t;
-#define TUNER_SAT 1
-#define TUNER_TER 2
-#define TUNER_CBL 3
-#define TUNER_ATSC 4
+
+/* Keep in sync with enum fe_delivery_system. */
+#ifndef DVB_USE_S2API
+# define SYS_DVBC_ANNEX_A 1
+# define SYS_DVBT 3
+# define SYS_DVBS 5
+# define SYS_DVBS2 6
+# define SYS_ATSC 11
+# define SYS_DVBT2 16
+# define SYS_DVBC_ANNEX_C 18
+#endif
+#define SYS_DVB__COUNT__ (SYS_DVBC_ANNEX_C + 1)
+
+
+#define DELSYS_BIT(__bit) (((unsigned int)1) << (__bit))
+
+#define DELSYS_SET(__mask, __bit) \
+ (__mask) |= DELSYS_BIT((__bit))
+
+#define DELSYS_IS_SET(__mask, __bit) \
+ (0 != ((__mask) & DELSYS_BIT((__bit))))
+
+
+#ifdef DVB_ATSC
+#define DELSYS_SUPP_MASK \
+ ( \
+ DELSYS_BIT(SYS_DVBC_ANNEX_A) | \
+ DELSYS_BIT(SYS_DVBT) | \
+ DELSYS_BIT(SYS_DVBS) | \
+ DELSYS_BIT(SYS_DVBS2) | \
+ DELSYS_BIT(SYS_ATSC) | \
+ DELSYS_BIT(SYS_DVBT2) | \
+ DELSYS_BIT(SYS_DVBC_ANNEX_C) \
+ )
+#else
+#define DELSYS_SUPP_MASK \
+ ( \
+ DELSYS_BIT(SYS_DVBC_ANNEX_A) | \
+ DELSYS_BIT(SYS_DVBT) | \
+ DELSYS_BIT(SYS_DVBS) | \
+ DELSYS_BIT(SYS_DVBS2) | \
+ DELSYS_BIT(SYS_DVBT2) | \
+ DELSYS_BIT(SYS_DVBC_ANNEX_C) \
+ )
+#endif
+
int dvb_step_channel(stream_t *, int);
-int dvb_set_channel(stream_t *, int, int);
+int dvb_set_channel(stream_t *, unsigned int, unsigned int);
dvb_state_t *dvb_get_state(stream_t *);
void dvb_free_state(dvb_state_t *);
diff --git a/stream/stream.c b/stream/stream.c
index 94b9c44..469e370 100644
--- a/stream/stream.c
+++ b/stream/stream.c
@@ -93,7 +93,7 @@ static const stream_info_t *const stream_list[] = {
#if HAVE_LIBSMBCLIENT
&stream_info_smb,
#endif
-#if HAVE_DVDREAD
+#if HAVE_DVDREAD || HAVE_DVDNAV
&stream_info_ifo,
&stream_info_dvd,
#endif
diff --git a/stream/stream_dvb.c b/stream/stream_dvb.c
index 01ec6c2..0638b17 100644
--- a/stream/stream_dvb.c
+++ b/stream/stream_dvb.c
@@ -2,6 +2,7 @@
dvbstream
(C) Dave Chapman <dave@dchapman.com> 2001, 2002.
+ (C) Rozhuk Ivan <rozhuk.im@gmail.com> 2016
Original authors: Nico, probably Arpi
@@ -57,32 +58,35 @@
#include "dvbin.h"
#include "dvb_tune.h"
-#define MAX_CHANNELS 8
+#define MAX_ADAPTERS 16
#define CHANNEL_LINE_LEN 256
#define min(a, b) ((a) <= (b) ? (a) : (b))
-#define OPT_BASE_STRUCT struct dvb_params
+#define OPT_BASE_STRUCT dvb_priv_t
-static dvb_state_t* global_dvb_state = NULL;
+static dvb_state_t *global_dvb_state = NULL;
static pthread_mutex_t global_dvb_state_lock = PTHREAD_MUTEX_INITIALIZER;
const struct m_sub_options stream_dvb_conf = {
.opts = (const m_option_t[]) {
OPT_STRING("prog", cfg_prog, 0),
- OPT_INTRANGE("card", cfg_card, 0, 1, 4),
+ OPT_INTRANGE("card", cfg_devno, 0, 1, 4),
OPT_INTRANGE("timeout", cfg_timeout, 0, 1, 30),
OPT_STRING("file", cfg_file, M_OPT_FILE),
OPT_FLAG("full-transponder", cfg_full_transponder, 0),
{0}
},
- .size = sizeof(struct dvb_params),
- .defaults = &(const struct dvb_params){
- .cfg_prog = "",
- .cfg_card = 1,
+ .size = sizeof(dvb_priv_t),
+ .defaults = &(const dvb_priv_t){
+ .cfg_prog = NULL,
+ .cfg_devno = 0,
.cfg_timeout = 30,
},
};
+void dvbin_close(stream_t *stream);
+
+
static void parse_vdr_par_string(const char *vdr_par_str, dvb_channel_t *ptr)
{
//FIXME: There is more information in this parameter string, especially related
@@ -102,9 +106,9 @@ static void parse_vdr_par_string(const char *vdr_par_str, dvb_channel_t *ptr)
case 'S':
vdr_par++;
if (*vdr_par == '1') {
- ptr->is_dvb_s2 = true;
+ ptr->is_dvb_x2 = true;
} else {
- ptr->is_dvb_s2 = false;
+ ptr->is_dvb_x2 = false;
}
vdr_par++;
break;
@@ -193,12 +197,13 @@ static bool parse_pid_string(struct mp_log *log, char *pid_string,
return false;
}
-static dvb_channels_list *dvb_get_channels(struct mp_log *log,
+static dvb_channels_list_t *dvb_get_channels(struct mp_log *log,
+ dvb_channels_list_t *list_add,
int cfg_full_transponder,
char *filename,
- int type)
+ int delsys, unsigned int delsys_mask)
{
- dvb_channels_list *list;
+ dvb_channels_list_t *list = list_add;
FILE *f;
char line[CHANNEL_LINE_LEN], *colon;
@@ -216,27 +221,30 @@ static dvb_channels_list *dvb_get_channels(struct mp_log *log,
const char *sat_conf = "%d:%c:%d:%d:%255[^:]:%255[^:]\n";
const char *ter_conf =
"%d:%255[^:]:%255[^:]:%255[^:]:%255[^:]:%255[^:]:%255[^:]:%255[^:]:%255[^:]:%255[^:]:%255[^:]\n";
+#ifdef DVB_ATSC
const char *atsc_conf = "%d:%255[^:]:%255[^:]:%255[^:]\n";
-
+#endif
const char *vdr_conf =
"%d:%255[^:]:%255[^:]:%d:%255[^:]:%255[^:]:%255[^:]:%*255[^:]:%d:%*d:%*d:%*d\n%n";
- mp_verbose(log, "CONFIG_READ FILE: %s, type: %d\n", filename, type);
+ mp_verbose(log, "CONFIG_READ FILE: %s, type: %s\n",
+ filename, get_dvb_delsys(delsys));
if ((f = fopen(filename, "r")) == NULL) {
mp_fatal(log, "CAN'T READ CONFIG FILE %s\n", filename);
return NULL;
}
- list = malloc(sizeof(dvb_channels_list));
if (list == NULL) {
- fclose(f);
- mp_verbose(log, "DVB_GET_CHANNELS: couldn't malloc enough memory\n");
- return NULL;
+ list = malloc(sizeof(dvb_channels_list_t));
+ if (list == NULL) {
+ fclose(f);
+ mp_verbose(log, "DVB_GET_CHANNELS: couldn't malloc enough memory\n");
+ return NULL;
+ }
+ memset(list, 0x00, sizeof(dvb_channels_list_t));
}
ptr = &chn;
- list->NUM_CHANNELS = 0;
- list->channels = NULL;
while (!feof(f)) {
if (fgets(line, CHANNEL_LINE_LEN, f) == NULL)
continue;
@@ -244,6 +252,10 @@ static dvb_channels_list *dvb_get_channels(struct mp_log *log,
if ((line[0] == '#') || (strlen(line) == 0))
continue;
+ memset(ptr, 0x00, sizeof(dvb_channel_t));
+ vpid_str[0] = apid_str[0] = tpid_str[0] = 0;
+ vdr_loc_str[0] = vdr_par_str[0] = 0;
+
colon = strchr(line, ':');
if (colon) {
k = colon - line;
@@ -254,23 +266,30 @@ static dvb_channels_list *dvb_get_channels(struct mp_log *log,
char *bouquet_sep = strchr(line, ';');
int channel_name_length = k;
if (bouquet_sep && bouquet_sep < colon)
- channel_name_length = bouquet_sep - line;
- ptr->name = malloc(channel_name_length + 1);
+ channel_name_length = (bouquet_sep - line);
+ ptr->name = malloc((channel_name_length + 1));
if (!ptr->name)
continue;
- av_strlcpy(ptr->name, line, channel_name_length + 1);
+ av_strlcpy(ptr->name, line, (channel_name_length + 1));
} else {
continue;
}
k++;
- vpid_str[0] = apid_str[0] = tpid_str[0] = 0;
- vdr_loc_str[0] = vdr_par_str[0] = 0;
ptr->pids_cnt = 0;
ptr->freq = 0;
- ptr->is_dvb_s2 = false;
ptr->service_id = -1;
+ ptr->is_dvb_x2 = false;
+ ptr->delsys = delsys;
+ ptr->diseqc = 0;
ptr->stream_id = NO_STREAM_ID_FILTER;
ptr->inv = INVERSION_AUTO;
+ ptr->bw = BANDWIDTH_AUTO;
+ ptr->cr = FEC_AUTO;
+ ptr->cr_lp = FEC_AUTO;
+ ptr->mod = QAM_AUTO;
+ ptr->hier = HIERARCHY_AUTO;
+ ptr->gi = GUARD_INTERVAL_AUTO;
+ ptr->trans = TRANSMISSION_MODE_AUTO;
// Check if VDR-type channels.conf-line - then full line is consumed by the scan.
int num_chars = 0;
@@ -283,12 +302,40 @@ static dvb_channels_list *dvb_get_channels(struct mp_log *log,
// It's a VDR-style config line.
parse_vdr_par_string(vdr_par_str, ptr);
// We still need the special SAT-handling here.
- if (type != TUNER_TER && type != TUNER_CBL && type != TUNER_ATSC) {
+ switch (delsys) {
+ case SYS_DVBT:
+ case SYS_DVBT2:
+ /* Fix delsys value. */
+ if (ptr->is_dvb_x2) {
+ ptr->delsys = delsys = SYS_DVBT2;
+ } else {
+ ptr->delsys = delsys = SYS_DVBT;
+ }
+ if (!DELSYS_IS_SET(delsys_mask, delsys))
+ continue; /* Skip channel. */
+ /* PASSTROUTH */
+ case SYS_DVBC_ANNEX_A:
+ case SYS_DVBC_ANNEX_C:
+ case SYS_ATSC:
+ mp_verbose(log, "VDR, %s, NUM: %d, NUM_FIELDS: %d, NAME: %s, "
+ "FREQ: %d, SRATE: %d",
+ get_dvb_delsys(delsys),
+ list->NUM_CHANNELS, fields,
+ ptr->name, ptr->freq, ptr->srate);
+ break;
+ case SYS_DVBS:
+ case SYS_DVBS2:
+ /* Fix delsys value. */
+ if (ptr->is_dvb_x2) {
+ ptr->delsys = delsys = SYS_DVBS2;
+ } else {
+ ptr->delsys = delsys = SYS_DVBS;
+ }
+ if (!DELSYS_IS_SET(delsys_mask, delsys))
+ continue; /* Skip channel. */
+
ptr->freq *= 1000UL;
ptr->srate *= 1000UL;
- ptr->tone = -1;
- ptr->inv = INVERSION_AUTO;
- ptr->cr = FEC_AUTO;
if (vdr_loc_str[0]) {
// In older vdr config format, this field contained the DISEQc information.
@@ -299,8 +346,8 @@ static dvb_channels_list *dvb_get_channels(struct mp_log *log,
&valid_digits) == 1)
{
if (valid_digits == strlen(vdr_loc_str)) {
- ptr->diseqc = diseqc_info;
- if ((ptr->diseqc > 4) || (ptr->diseqc < 0))
+ ptr->diseqc = (unsigned int)diseqc_info;
+ if (ptr->diseqc > 4)
continue;
if (ptr->diseqc > 0)
ptr->diseqc--;
@@ -308,58 +355,71 @@ static dvb_channels_list *dvb_get_channels(struct mp_log *log,
}
}
- mp_verbose(log, "SAT, NUM: %d, NUM_FIELDS: %d, NAME: %s, "
+ mp_verbose(log, "%s NUM: %d, NUM_FIELDS: %d, NAME: %s, "
"FREQ: %d, SRATE: %d, POL: %c, DISEQC: %d, S2: %s, "
- "StreamID: %d, SID: %d", list->NUM_CHANNELS,
+ "StreamID: %d, SID: %d",
+ get_dvb_delsys(delsys),
+ list->NUM_CHANNELS,
fields, ptr->name, ptr->freq, ptr->srate, ptr->pol,
- ptr->diseqc, ptr->is_dvb_s2 ? "yes" : "no",
+ ptr->diseqc, (delsys == SYS_DVBS2) ? "yes" : "no",
ptr->stream_id, ptr->service_id);
- } else {
- mp_verbose(log, "VDR, NUM: %d, NUM_FIELDS: %d, NAME: %s, "
- "FREQ: %d, SRATE: %d", list->NUM_CHANNELS, fields,
- ptr->name, ptr->freq, ptr->srate);
+ break;
+ default:
+ break;
}
- } else if (type == TUNER_TER) {
- fields = sscanf(&line[k], ter_conf,
- &ptr->freq, inv, bw, cr, tmp_lcr, mod,
- transm, gi, tmp_hier, vpid_str, apid_str);
- mp_verbose(log, "TER, NUM: %d, NUM_FIELDS: %d, NAME: %s, FREQ: %d",
- list->NUM_CHANNELS, fields, ptr->name, ptr->freq);
- } else if (type == TUNER_CBL) {
- fields = sscanf(&line[k], cbl_conf,
- &ptr->freq, inv, &ptr->srate,
- cr, mod, vpid_str, apid_str);
- mp_verbose(log, "CBL, NUM: %d, NUM_FIELDS: %d, NAME: %s, FREQ: %d, "
- "SRATE: %d", list->NUM_CHANNELS, fields, ptr->name,
- ptr->freq, ptr->srate);
- }
+ } else {
+ switch (delsys) {
+ case SYS_DVBT:
+ case SYS_DVBT2:
+ fields = sscanf(&line[k], ter_conf,
+ &ptr->freq, inv, bw, cr, tmp_lcr, mod,
+ transm, gi, tmp_hier, vpid_str, apid_str);
+ mp_verbose(log, "%s, NUM: %d, NUM_FIELDS: %d, NAME: %s, FREQ: %d",
+ get_dvb_delsys(delsys), list->NUM_CHANNELS,
+ fields, ptr->name, ptr->freq);
+ break;
+ case SYS_DVBC_ANNEX_A:
+ case SYS_DVBC_ANNEX_C:
+ fields = sscanf(&line[k], cbl_conf,
+ &ptr->freq, inv, &ptr->srate,
+ cr, mod, vpid_str, apid_str);
+ mp_verbose(log, "%s, NUM: %d, NUM_FIELDS: %d, NAME: %s, FREQ: %d, "
+ "SRATE: %d",
+ get_dvb_delsys(delsys),
+ list->NUM_CHANNELS, fields, ptr->name,
+ ptr->freq, ptr->srate);
+ break;
#ifdef DVB_ATSC
- else if (type == TUNER_ATSC) {
- fields = sscanf(&line[k], atsc_conf,
- &ptr->freq, mod, vpid_str, apid_str);
- mp_verbose(log, "ATSC, NUM: %d, NUM_FIELDS: %d, NAME: %s, FREQ: %d\n",
- list->NUM_CHANNELS, fields, ptr->name, ptr->freq);
- }
+ case SYS_ATSC:
+ fields = sscanf(&line[k], atsc_conf,
+ &ptr->freq, mod, vpid_str, apid_str);
+ mp_verbose(log, "%s, NUM: %d, NUM_FIELDS: %d, NAME: %s, FREQ: %d\n",
+ get_dvb_delsys(delsys), list->NUM_CHANNELS,
+ fields, ptr->name, ptr->freq);
+ break;
#endif
- else { //SATELLITE
- fields = sscanf(&line[k], sat_conf,
- &ptr->freq, &ptr->pol, &ptr->diseqc, &ptr->srate,
- vpid_str,
- apid_str);
- ptr->pol = mp_toupper(ptr->pol);
- ptr->freq *= 1000UL;
- ptr->srate *= 1000UL;
- ptr->tone = -1;
- ptr->inv = INVERSION_AUTO;
- ptr->cr = FEC_AUTO;
- if ((ptr->diseqc > 4) || (ptr->diseqc < 0))
- continue;
- if (ptr->diseqc > 0)
- ptr->diseqc--;
- mp_verbose(log, "SAT, NUM: %d, NUM_FIELDS: %d, NAME: %s, FREQ: %d, "
- "SRATE: %d, POL: %c, DISEQC: %d",
- list->NUM_CHANNELS, fields, ptr->name, ptr->freq,
- ptr->srate, ptr->pol, ptr->diseqc);
+ case SYS_DVBS:
+ case SYS_DVBS2:
+ fields = sscanf(&line[k], sat_conf,
+ &ptr->freq, &ptr->pol, &ptr->diseqc, &ptr->srate,
+ vpid_str,
+ apid_str);
+ ptr->pol = mp_toupper(ptr->pol);
+ ptr->freq *= 1000UL;
+ ptr->srate *= 1000UL;
+ if (ptr->diseqc > 4)
+ continue;
+ if (ptr->diseqc > 0)
+ ptr->diseqc--;
+ mp_verbose(log, "%s, NUM: %d, NUM_FIELDS: %d, NAME: %s, FREQ: %d, "
+ "SRATE: %d, POL: %c, DISEQC: %d",
+ get_dvb_delsys(delsys),
+ list->NUM_CHANNELS, fields, ptr->name, ptr->freq,
+ ptr->srate, ptr->pol, ptr->diseqc);
+ break;
+ default:
+ break;
+ }
}
if (parse_pid_string(log, vpid_str, ptr))
@@ -374,8 +434,8 @@ static dvb_channels_list *dvb_get_channels(struct mp_log *log,
}
- if ((fields < 2) || (ptr->pids_cnt <= 0) || (ptr->freq == 0) ||
- (strlen(ptr->name) == 0))
+ if (fields < 2 || ptr->pids_cnt == 0 || ptr->freq == 0 ||
+ strlen(ptr->name) == 0)
continue;
/* Add some PIDs which are mandatory in DVB,
@@ -425,13 +485,15 @@ static dvb_channels_list *dvb_get_channels(struct mp_log *log,
mp_verbose(log, " %d ", ptr->pids[cnt]);
mp_verbose(log, "\n");
- if ((type == TUNER_TER) || (type == TUNER_CBL)) {
+ switch (delsys) {
+ case SYS_DVBT:
+ case SYS_DVBT2:
+ case SYS_DVBC_ANNEX_A:
+ case SYS_DVBC_ANNEX_C:
if (!strcmp(inv, "INVERSION_ON")) {
ptr->inv = INVERSION_ON;
} else if (!strcmp(inv, "INVERSION_OFF")) {
ptr->inv = INVERSION_OFF;
- } else {
- ptr->inv = INVERSION_AUTO;
}
@@ -453,13 +515,15 @@ static dvb_channels_list *dvb_get_channels(struct mp_log *log,
ptr->cr = FEC_7_8;
} else if (!strcmp(cr, "FEC_NONE")) {
ptr->cr = FEC_NONE;
- } else {
- ptr->cr = FEC_AUTO;
}
}
-
- if (type == TUNER_TER || type == TUNER_CBL || type == TUNER_ATSC) {
+ switch (delsys) {
+ case SYS_DVBT:
+ case SYS_DVBT2:
+ case SYS_DVBC_ANNEX_A:
+ case SYS_DVBC_ANNEX_C:
+ case SYS_ATSC:
if (!strcmp(mod, "QAM_128")) {
ptr->mod = QAM_128;
} else if (!strcmp(mod, "QAM_256")) {
@@ -475,20 +539,23 @@ static dvb_channels_list *dvb_get_channels(struct mp_log *log,
ptr->mod = VSB_8;
} else if (!strcmp(mod, "VSB_16") || !strcmp(mod, "16VSB")) {
ptr->mod = VSB_16;
- } else if (!strcmp(mod, "QAM_AUTO")) {
- ptr->mod = QAM_AUTO;
- }
-
#endif
+ }
}
- if (type == TUNER_TER) {
- if (!strcmp(bw, "BANDWIDTH_6_MHZ")) {
+ switch (delsys) {
+ case SYS_DVBT:
+ case SYS_DVBT2:
+ if (!strcmp(bw, "BANDWIDTH_5_MHZ")) {
+ ptr->bw = BANDWIDTH_5_MHZ;
+ } else if (!strcmp(bw, "BANDWIDTH_6_MHZ")) {
ptr->bw = BANDWIDTH_6_MHZ;
} else if (!strcmp(bw, "BANDWIDTH_7_MHZ")) {
ptr->bw = BANDWIDTH_7_MHZ;
} else if (!strcmp(bw, "BANDWIDTH_8_MHZ")) {
ptr->bw = BANDWIDTH_8_MHZ;
+ } else if (!strcmp(bw, "BANDWIDTH_10_MHZ")) {
+ ptr->bw = BANDWIDTH_10_MHZ;
}
@@ -508,8 +575,6 @@ static dvb_channels_list *dvb_get_channels(struct mp_log *log,
ptr->gi = GUARD_INTERVAL_1_8;
} else if (!strcmp(gi, "GUARD_INTERVAL_1_4")) {
ptr->gi = GUARD_INTERVAL_1_4;
- } else {
- ptr->gi = GUARD_INTERVAL_AUTO;
}
if (!strcmp(tmp_lcr, "FEC_1_2")) {
@@ -530,8 +595,6 @@ static dvb_channels_list *dvb_get_channels(struct mp_log *log,
ptr->cr_lp = FEC_7_8;
} else if (!strcmp(tmp_lcr, "FEC_NONE")) {
ptr->cr_lp = FEC_NONE;
- } else {
- ptr->cr_lp = FEC_AUTO;
}
@@ -541,9 +604,7 @@ static dvb_channels_list *dvb_get_channels(struct mp_log *log,
ptr->hier = HIERARCHY_2;
} else if (!strcmp(tmp_hier, "HIERARCHY_4")) {
ptr->hier = HIERARCHY_4;
- } else if (!strcmp(tmp_hier, "HIERARCHY_AUTO")) {
- ptr->hier = HIERARCHY_AUTO;
- } else {
+ } else if (!strcmp(tmp_hier, "HIERARCHY_NONE")) {
ptr->hier = HIERARCHY_NONE;
}
}
@@ -570,7 +631,6 @@ static dvb_channels_list *dvb_get_channels(struct mp_log *log,
return NULL;
}
- list->current = 0;
return list;
}
@@ -578,16 +638,15 @@ void dvb_free_state(dvb_state_t *state)
{
int i, j;
- for (i = 0; i < state->count; i++) {
- free(state->cards[i].name);
- if (!state->cards[i].list)
+ for (i = 0; i < state->adapters_count; i++) {
+ if (!state->adapters[i].list)
continue;
- if (state->cards[i].list->channels) {
- for (j = 0; j < state->cards[i].list->NUM_CHANNELS; j++)
- free(state->cards[i].list->channels[j].name);
- free(state->cards[i].list->channels);
+ if (state->adapters[i].list->channels) {
+ for (j = 0; j < state->adapters[i].list->NUM_CHANNELS; j++)
+ free(state->adapters[i].list->channels[j].name);
+ free(state->adapters[i].list->channels);
}
- free(state->cards[i].list);
+ free(state->adapters[i].list);
}
free(state);
}
@@ -597,47 +656,41 @@ static int dvb_streaming_read(stream_t *stream, char *buffer, int size)
struct pollfd pfds[1];
int pos = 0, tries, rk, fd;
dvb_priv_t *priv = (dvb_priv_t *) stream->priv;
- dvb_state_t* state = priv->state;
+ dvb_state_t *state = priv->state;
MP_TRACE(stream, "dvb_streaming_read(%d)\n", size);
- tries = state->retry + 1;
-
+ tries = state->retry;
fd = state->dvr_fd;
while (pos < size) {
- pfds[0].fd = fd;
- pfds[0].events = POLLIN | POLLPRI;
-
- rk = size - pos;
- if (poll(pfds, 1, 500) <= 0) {
- MP_ERR(stream, "dvb_streaming_read, attempt N. %d failed with "
- "errno %d when reading %d bytes\n", tries, errno, size - pos);
- errno = 0;
- if (--tries > 0)
- continue;
- break;
- }
- if ((rk = read(fd, &buffer[pos], rk)) > 0) {
- pos += rk;
- MP_TRACE(stream, "ret (%d) bytes\n", pos);
- } else {
- MP_ERR(stream, "dvb_streaming_read, poll ok but read failed with "
- "errno %d when reading %d bytes, size: %d, pos: %d\n",
- errno, size - pos, size, pos);
+ rk = read(fd, &buffer[pos], (size - pos));
+ if (rk <= 0) {
+ if (pos || tries == 0)
+ break;
+ tries --;
+ pfds[0].fd = fd;
+ pfds[0].events = POLLIN | POLLPRI;
+ if (poll(pfds, 1, 500) <= 0) {
+ MP_ERR(stream, "dvb_streaming_read, failed with "
+ "errno %d when reading %d bytes\n", errno, size - pos);
+ errno = 0;
+ break;
+ }
+ continue;
}
+ pos += rk;
+ MP_TRACE(stream, "ret (%d) bytes\n", pos);
}
if (!pos)
- MP_ERR(stream, "dvb_streaming_read, return %d bytes\n", pos);
+ MP_ERR(stream, "dvb_streaming_read, return 0 bytes\n");
return pos;
}
-static void dvbin_close(stream_t *stream);
-
-int dvb_set_channel(stream_t *stream, int card, int n)
+int dvb_set_channel(stream_t *stream, unsigned int adapter, unsigned int n)
{
- dvb_channels_list *new_list;
+ dvb_channels_list_t *new_list;
dvb_channel_t *channel;
dvb_priv_t *priv = stream->priv;
char buf[4096];
@@ -645,33 +698,33 @@ int dvb_set_channel(stream_t *stream, int card, int n)
int devno;
int i;
- if ((card < 0) || (card > state->count)) {
- MP_ERR(stream, "dvb_set_channel: INVALID CARD NUMBER: %d vs %d, abort\n",
- card, state->count);
+ if (adapter >= state->adapters_count) {
+ MP_ERR(stream, "dvb_set_channel: INVALID internal ADAPTER NUMBER: %d vs %d, abort\n",
+ adapter, state->adapters_count);
return 0;
}
- devno = state->cards[card].devno;
- new_list = state->cards[card].list;
- if ((n > new_list->NUM_CHANNELS) || (n < 0)) {
+ devno = state->adapters[adapter].devno;
+ new_list = state->adapters[adapter].list;
+ if (n > new_list->NUM_CHANNELS) {
MP_ERR(stream, "dvb_set_channel: INVALID CHANNEL NUMBER: %d, for "
- "card %d, abort\n", n, card);
+ "adapter %d, abort\n", n, devno);
return 0;
}
channel = &(new_list->channels[n]);
if (state->is_on) { //the fds are already open and we have to stop the demuxers
- for (i = 0; i < state->demux_fds_cnt; i++)
- dvb_demux_stop(state->demux_fds[i]);
+ /* Remove all demuxes. */
+ dvb_fix_demuxes(priv, 0);
state->retry = 0;
//empty both the stream's and driver's buffer
- while (dvb_streaming_read(stream, buf, 4096) > 0) {}
- if (state->card != card) {
+ while (dvb_streaming_read(stream, buf, sizeof(buf)) > 0) {}
+ if (state->cur_adapter != adapter) {
dvbin_close(stream);
if (!dvb_open_devices(priv, devno, channel->pids_cnt)) {
MP_ERR(stream, "DVB_SET_CHANNEL, COULDN'T OPEN DEVICES OF "
- "CARD: %d, EXIT\n", card);
+ "ADAPTER: %d, EXIT\n", devno);
return 0;
}
} else {
@@ -683,24 +736,23 @@ int dvb_set_channel(stream_t *stream, int card, int n)
} else {
if (!dvb_open_devices(priv, devno, channel->pids_cnt)) {
MP_ERR(stream, "DVB_SET_CHANNEL2, COULDN'T OPEN DEVICES OF "
- "CARD: %d, EXIT\n", card);
+ "ADAPTER: %d, EXIT\n", devno);
return 0;
}
}
- state->card = card;
- state->list = new_list;
+ state->cur_adapter = adapter;
state->retry = 5;
new_list->current = n;
- MP_VERBOSE(stream, "DVB_SET_CHANNEL: new channel name=%s, card: %d, "
- "channel %d\n", channel->name, card, n);
+ MP_VERBOSE(stream, "DVB_SET_CHANNEL: new channel name=%s, adapter: %d, "
+ "channel %d\n", channel->name, devno, n);
stream_drop_buffers(stream);
if (channel->freq != state->last_freq) {
- if (!dvb_tune(priv, channel->freq, channel->pol, channel->srate,
- channel->diseqc, channel->tone,
- channel->is_dvb_s2, channel->stream_id, channel->inv,
+ if (!dvb_tune(priv, channel->delsys, channel->freq,
+ channel->pol, channel->srate, channel->diseqc,
+ channel->stream_id, channel->inv,
channel->mod, channel->gi,
channel->trans, channel->bw, channel->cr, channel->cr_lp,
channel->hier, priv->cfg_timeout))
@@ -718,7 +770,7 @@ int dvb_set_channel(stream_t *stream, int card, int n)
MP_VERBOSE(stream, "DVB_SET_CHANNEL: PMT-PID for service %d "
"not resolved yet, parsing PAT...\n",
channel->service_id);
- int pmt_pid = dvb_get_pmt_pid(priv, card, channel->service_id);
+ int pmt_pid = dvb_get_pmt_pid(priv, adapter, channel->service_id);
MP_VERBOSE(stream, "DVB_SET_CHANNEL: Found PMT-PID: %d\n",
pmt_pid);
channel->pids[i] = pmt_pid;
@@ -744,14 +796,13 @@ int dvb_set_channel(stream_t *stream, int card, int n)
int dvb_step_channel(stream_t *stream, int dir)
{
- int new_current;
- dvb_channels_list *list;
+ unsigned int new_current;
dvb_priv_t *priv = stream->priv;
- dvb_state_t* state = priv->state;
+ dvb_state_t *state = priv->state;
+ dvb_channels_list_t *list = state->adapters[state->cur_adapter].list;
MP_VERBOSE(stream, "DVB_STEP_CHANNEL dir %d\n", dir);
- list = state->list;
if (list == NULL) {
MP_ERR(stream, "dvb_step_channel: NULL list_ptr, quit\n");
return 0;
@@ -760,43 +811,33 @@ int dvb_step_channel(stream_t *stream, int dir)
new_current = (list->NUM_CHANNELS + list->current +
(dir >= 0 ? 1 : -1)) % list->NUM_CHANNELS;
- return dvb_set_channel(stream, state->card, new_current);
+ return dvb_set_channel(stream, state->cur_adapter, new_current);
}
static int dvbin_stream_control(struct stream *s, int cmd, void *arg)
{
int r;
+ dvb_priv_t *priv = (dvb_priv_t *) s->priv;
+ dvb_state_t *state = priv->state;
+ dvb_channels_list_t *list = NULL;
+
+
switch (cmd) {
case STREAM_CTRL_DVB_SET_CHANNEL: {
- int *iarg = arg;
+ unsigned int *iarg = arg;
+ MP_VERBOSE(s, "dvbin_stream_control: cmd STREAM_CTRL_DVB_SET_CHANNEL: %i, %i\n", iarg[1], iarg[0]);
r = dvb_set_channel(s, iarg[1], iarg[0]);
if (r) {
// Stream will be pulled down after channel switch,
// persist state.
- dvb_priv_t *priv = (dvb_priv_t *) s->priv;
- dvb_state_t* state = priv->state;
state->switching_channel = true;
return STREAM_OK;
}
return STREAM_ERROR;
}
- case STREAM_CTRL_DVB_SET_CHANNEL_NAME: {
- char *progname = *((char**)arg);
- dvb_priv_t *priv = (dvb_priv_t *) s->priv;
- dvb_state_t* state = priv->state;
- int new_channel = -1;
- for (int i=0; i < state->list->NUM_CHANNELS; ++i) {
- if (!strcmp(state->list->channels[i].name, progname)) {
- new_channel = i;
- break;
- }
- }
- if (new_channel == -1) {
- MP_ERR(s, "Program '%s' not found for card %d!\n",
- progname, state->card);
- return STREAM_ERROR;
- }
- r = dvb_set_channel(s, state->card, new_channel);
+ case STREAM_CTRL_DVB_STEP_CHANNEL: {
+ MP_VERBOSE(s, "dvbin_stream_control: cmd STREAM_CTRL_DVB_STEP_CHANNEL: %i\n", (*(int *)arg));
+ r = dvb_step_channel(s, (*(int *)arg));
if (r) {
// Stream will be pulled down after channel switch,
// persist state.
@@ -805,44 +846,62 @@ static int dvbin_stream_control(struct stream *s, int cmd, void *arg)
}
return STREAM_ERROR;
}
- case STREAM_CTRL_DVB_STEP_CHANNEL: {
- r = dvb_step_channel(s, *(int *)arg);
+ }
+
+
+ if (state->cur_adapter >= state->adapters_count)
+ return STREAM_ERROR;
+ list = state->adapters[state->cur_adapter].list;
+
+ switch (cmd) {
+ case STREAM_CTRL_GET_TV_FREQ:
+ (*(unsigned int*)arg) = list->channels[list->current].freq;
+ return STREAM_ERROR;
+ case STREAM_CTRL_DVB_SET_CHANNEL_NAME: {
+ char *progname = *((char**)arg);
+ unsigned int new_channel = (~(unsigned int)0);
+ MP_VERBOSE(s, "dvbin_stream_control: cmd STREAM_CTRL_DVB_SET_CHANNEL_NAME: %s\n", progname);
+ for (unsigned int i=0; i < list->NUM_CHANNELS; ++i) {
+ if (!strcmp(list->channels[i].name, progname)) {
+ new_channel = i;
+ break;
+ }
+ }
+ if (new_channel == -1) {
+ MP_ERR(s, "Program '%s' not found for adapter %d!\n",
+ progname, state->adapters[state->cur_adapter].devno);
+ return STREAM_ERROR;
+ }
+ r = dvb_set_channel(s, state->cur_adapter, new_channel);
if (r) {
// Stream will be pulled down after channel switch,
// persist state.
- dvb_priv_t *priv = (dvb_priv_t *) s->priv;
- dvb_state_t* state = priv->state;
state->switching_channel = true;
return STREAM_OK;
}
return STREAM_ERROR;
}
case STREAM_CTRL_DVB_GET_CHANNEL_NAME: {
- dvb_priv_t *priv = (dvb_priv_t *) s->priv;
- dvb_state_t* state = priv->state;
- int current_channel = state->list->current;
- char* progname = state->list->channels[current_channel].name;
- *(char **)arg = talloc_strdup(NULL, progname);
- return STREAM_OK;
+ MP_VERBOSE(s, "dvbin_stream_control: cmd STREAM_CTRL_DVB_GET_CHANNEL_NAME\n");
+ char *progname = list->channels[list->current].name;
+ *(char **)arg = talloc_strdup(NULL, progname);
+ return STREAM_OK;
}
case STREAM_CTRL_GET_METADATA: {
- struct mp_tags* metadata = talloc_zero(NULL, struct mp_tags);
- dvb_priv_t *priv = (dvb_priv_t *) s->priv;
- dvb_state_t* state = priv->state;
- int current_channel = state->list->current;
- char* progname = state->list->channels[current_channel].name;
- mp_tags_set_str(metadata, "title", progname);
- *(struct mp_tags **)arg = metadata;
- return STREAM_OK;
+ struct mp_tags *metadata = talloc_zero(NULL, struct mp_tags);
+ char *progname = list->channels[list->current].name;
+ mp_tags_set_str(metadata, "title", progname);
+ *(struct mp_tags **)arg = metadata;
+ return STREAM_OK;
}
}
return STREAM_UNSUPPORTED;
}
-static void dvbin_close(stream_t *stream)
+void dvbin_close(stream_t *stream)
{
dvb_priv_t *priv = (dvb_priv_t *) stream->priv;
- dvb_state_t* state = priv->state;
+ dvb_state_t *state = priv->state;
if (state->switching_channel && state->is_on) {
// Prevent state destruction, reset channel-switch.
@@ -860,7 +919,6 @@ static void dvbin_close(stream_t *stream)
close(state->demux_fds[i]);
}
close(state->dvr_fd);
-
close(state->fe_fd);
state->fe_fd = state->dvr_fd = -1;
@@ -872,39 +930,39 @@ static void dvbin_close(stream_t *stream)
pthread_mutex_unlock(&global_dvb_state_lock);
}
-static int dvb_streaming_start(stream_t *stream, int tuner_type, char *progname)
+static int dvb_streaming_start(stream_t *stream, char *progname)
{
int i;
dvb_channel_t *channel = NULL;
dvb_priv_t *priv = stream->priv;
- dvb_state_t* state = priv->state;
- dvb_priv_t *opts = priv;
+ dvb_state_t *state = priv->state;
+ dvb_channels_list_t *list;
- MP_VERBOSE(stream, "\r\ndvb_streaming_start(PROG: %s, CARD: %d)\n",
- opts->cfg_prog, opts->cfg_card);
+ if (progname == NULL)
+ return 0;
+ MP_VERBOSE(stream, "\r\ndvb_streaming_start(PROG: %s, ADAPTER: %d)\n",
+ progname, priv->cfg_devno);
state->is_on = 0;
-
- i = 0;
- while ((channel == NULL) && i < state->list->NUM_CHANNELS) {
- if (!strcmp(state->list->channels[i].name, progname))
- channel = &(state->list->channels[i]);
-
- i++;
+ list = state->adapters[state->cur_adapter].list;
+ for (i = 0; i < list->NUM_CHANNELS; i ++) {
+ if (!strcmp(list->channels[i].name, progname)) {
+ channel = &(list->channels[i]);
+ break;
+ }
}
- if (channel != NULL) {
- state->list->current = i - 1;
- MP_VERBOSE(stream, "PROGRAM NUMBER %d: name=%s, freq=%u\n", i - 1,
- channel->name, channel->freq);
- } else {
+ if (channel == NULL) {
MP_ERR(stream, "\n\nDVBIN: no such channel \"%s\"\n\n", progname);
return 0;
}
+ list->current = i;
+ MP_VERBOSE(stream, "PROGRAM NUMBER %d: name=%s, freq=%u\n", i,
+ channel->name, channel->freq);
- if (!dvb_set_channel(stream, state->card, state->list->current)) {
- MP_ERR(stream, "ERROR, COULDN'T SET CHANNEL %i: ", state->list->current);
+ if (!dvb_set_channel(stream, state->cur_adapter, list->current)) {
+ MP_ERR(stream, "ERROR, COULDN'T SET CHANNEL %i: \"%s\"\n", list->current, progname);
dvbin_close(stream);
return 0;
}
@@ -921,27 +979,28 @@ static int dvb_open(stream_t *stream)
{
// I don't force the file format because, although it's almost always TS,
// there are some providers that stream an IP multicast with M$ Mpeg4 inside
+ dvb_priv_t *priv = NULL;
char *progname;
- int tuner_type = 0, i;
+ int i;
pthread_mutex_lock(&global_dvb_state_lock);
if (global_dvb_state && global_dvb_state->stream_used) {
- MP_ERR(stream, "DVB stream already in use, only one DVB stream can exist at a time!");
+ MP_ERR(stream, "DVB stream already in use, only one DVB stream can exist at a time!\n");
pthread_mutex_unlock(&global_dvb_state_lock);
- return STREAM_ERROR;
+ goto err_out;
}
+ // Need to re-get config in any case, not part of global state.
stream->priv = mp_get_config_group(stream, stream->global, &stream_dvb_conf);
- dvb_state_t* state = dvb_get_state(stream);
-
- dvb_priv_t *p = stream->priv;
- p->log = stream->log;
+ dvb_state_t *state = dvb_get_state(stream);
- p->state = state;
+ priv = stream->priv;
+ priv->state = state;
+ priv->log = stream->log;
if (state == NULL) {
MP_ERR(stream, "DVB CONFIGURATION IS EMPTY, exit\n");
pthread_mutex_unlock(&global_dvb_state_lock);
- return STREAM_ERROR;
+ goto err_out;
}
state->stream_used = true;
pthread_mutex_unlock(&global_dvb_state_lock);
@@ -950,59 +1009,51 @@ static int dvb_open(stream_t *stream)
// State could be already initialized, for example, we just did a channel switch.
// The following setup only has to be done once.
- state->card = -1;
- for (i = 0; i < state->count; i++) {
- if (state->cards[i].devno + 1 == p->cfg_card) {
- state->card = i;
+ state->cur_adapter = -1;
+ for (i = 0; i < state->adapters_count; i++) {
+ if (state->adapters[i].devno == priv->cfg_devno) {
+ state->cur_adapter = i;
break;
}
}
- if (state->card == -1) {
- MP_ERR(stream, "NO CONFIGURATION FOUND FOR CARD N. %d, exit\n",
- p->cfg_card);
- return STREAM_ERROR;
- }
- state->timeout = p->cfg_timeout;
-
- tuner_type = state->cards[state->card].type;
-
- if (tuner_type == 0) {
- MP_VERBOSE(stream,
- "OPEN_DVB: UNKNOWN OR UNDETECTABLE TUNER TYPE, EXIT\n");
- return STREAM_ERROR;
+ if (state->cur_adapter == -1) {
+ MP_ERR(stream, "NO CONFIGURATION FOUND FOR ADAPTER N. %d, exit\n",
+ priv->cfg_devno);
+ goto err_out;
}
+ state->timeout = priv->cfg_timeout;
- state->tuner_type = tuner_type;
+ MP_VERBOSE(stream, "OPEN_DVB: prog=%s, devno=%d\n",
+ priv->cfg_prog, state->adapters[state->cur_adapter].devno);
- MP_VERBOSE(stream, "OPEN_DVB: prog=%s, card=%d, type=%d\n",
- p->cfg_prog, state->card + 1, state->tuner_type);
-
- state->list = state->cards[state->card].list;
-
- if ((!strcmp(p->cfg_prog, "")) && (state->list != NULL)) {
- progname = state->list->channels[0].name;
+ if ((priv->cfg_prog == NULL || priv->cfg_prog[0] == 0) &&
+ state->adapters[state->cur_adapter].list != NULL) {
+ progname = state->adapters[state->cur_adapter].list->channels[0].name;
} else {
- progname = p->cfg_prog;
+ progname = priv->cfg_prog;
}
-
- if (!dvb_streaming_start(stream, tuner_type, progname))
- return STREAM_ERROR;
+ if (!dvb_streaming_start(stream, progname))
+ goto err_out;
}
stream->fill_buffer = dvb_streaming_read;
stream->close = dvbin_close;
stream->control = dvbin_stream_control;
stream->streaming = true;
-
+ stream->allow_caching = true;
stream->demuxer = "lavf";
stream->lavf_type = "mpegts";
return STREAM_OK;
+
+err_out:
+ talloc_free(priv);
+ stream->priv = NULL;
+ return STREAM_ERROR;
}
-#define MAX_CARDS 4
dvb_state_t *dvb_get_state(stream_t *stream)
{
if (global_dvb_state != NULL) {
@@ -1011,40 +1062,43 @@ dvb_state_t *dvb_get_state(stream_t *stream)
struct mp_log *log = stream->log;
struct mpv_global *global = stream->global;
dvb_priv_t *priv = stream->priv;
- int type, size;
- char filename[30], *name;
- dvb_channels_list *list;
- dvb_card_config_t *cards = NULL, *tmp;
+ unsigned int delsys, delsys_mask, size;
+ char filename[PATH_MAX], *conf_file;
+ const char *conf_file_name;
+ void *talloc_ctx;
+ dvb_channels_list_t *list;
+ dvb_adapter_config_t *adapters = NULL, *tmp;
dvb_state_t *state = NULL;
+ bstr prog, devno;
- bstr prog, card;
- if (!bstr_split_tok(bstr0(stream->path), "@", &card, &prog)) {
- prog = card;
- card.len = 0;
+ if (!bstr_split_tok(bstr0(stream->path), "@", &devno, &prog)) {
+ prog = devno;
+ devno.len = 0;
}
if (prog.len) {
talloc_free(priv->cfg_prog);
priv->cfg_prog = bstrto0(NULL, prog);
}
- if (card.len) {
+ if (devno.len) {
bstr r;
- priv->cfg_card = bstrtoll(card, &r, 0);
- if (r.len || priv->cfg_card < 1 || priv->cfg_card > 4) {
- MP_ERR(stream, "invalid card: '%.*s'\n", BSTR_P(card));
+ priv->cfg_devno = bstrtoll(devno, &r, 0);
+ if (r.len || priv->cfg_devno < 0 || priv->cfg_devno > MAX_ADAPTERS) {
+ MP_ERR(stream, "invalid devno: '%.*s'\n", BSTR_P(devno));
return NULL;
}
}
+ MP_VERBOSE(stream, "DVB_CONFIG: prog=%s, devno=%d\n",
+ priv->cfg_prog, priv->cfg_devno);
+
state = malloc(sizeof(dvb_state_t));
if (state == NULL)
return NULL;
-
- state->count = 0;
+ memset(state, 0x00, sizeof(dvb_state_t));
state->switching_channel = false;
state->stream_used = true;
- state->cards = NULL;
state->fe_fd = state->dvr_fd = -1;
- for (int i = 0; i < MAX_CARDS; i++) {
+ for (int i = 0; i < MAX_ADAPTERS; i++) {
snprintf(filename, sizeof(filename), "/dev/dvb/adapter%d/frontend0", i);
int fd = open(filename, O_RDONLY | O_NONBLOCK | O_CLOEXEC);
if (fd < 0) {
@@ -1054,91 +1108,89 @@ dvb_state_t *dvb_get_state(stream_t *stream)
}
mp_verbose(log, "Opened device %s, FD: %d\n", filename, fd);
- int* tuner_types = NULL;
- int num_tuner_types = dvb_get_tuner_types(fd, log, &tuner_types);
+ delsys_mask = dvb_get_tuner_delsys_mask(fd, log);
+ delsys_mask &= DELSYS_SUPP_MASK; /* Filter unsupported delivery systems. */
close(fd);
- mp_verbose(log, "Frontend device %s offers %d supported delivery systems.\n",
- filename, num_tuner_types);
- for (int num_tuner_type=0; num_tuner_type<num_tuner_types; num_tuner_type++) {
- type = tuner_types[num_tuner_type];
- if (type != TUNER_SAT && type != TUNER_TER && type != TUNER_CBL &&
- type != TUNER_ATSC) {
- mp_verbose(log, "DVB_CONFIG, can't detect tuner type of "
- "card %d, skipping\n", i);
- continue;
- }
-
- void *talloc_ctx = talloc_new(NULL);
- char *conf_file = NULL;
- if (priv->cfg_file && priv->cfg_file[0])
- conf_file = priv->cfg_file;
- else {
- switch (type) {
- case TUNER_TER:
- conf_file = mp_find_config_file(talloc_ctx, global,
- "channels.conf.ter");
- break;
- case TUNER_CBL:
- conf_file = mp_find_config_file(talloc_ctx, global,
- "channels.conf.cbl");
- break;
- case TUNER_SAT:
- conf_file = mp_find_config_file(talloc_ctx, global,
- "channels.conf.sat");
- break;
- case TUNER_ATSC:
- conf_file = mp_find_config_file(talloc_ctx, global,
- "channels.conf.atsc");
- break;
+ if (delsys_mask == 0) {
+ mp_verbose(log, "Frontend device %s has no supported delivery systems.\n",
+ filename);
+ continue; /* Skip tuner. */
+ }
+ mp_verbose(log, "Frontend device %s offers some supported delivery systems.\n",
+ filename);
+ /* Create channel list for adapter. */
+ list = NULL;
+ for (delsys = 0; delsys < SYS_DVB__COUNT__; delsys++) {
+ if (!DELSYS_IS_SET(delsys_mask, delsys))
+ continue; /* Skip unsupported. */
+
+ switch (delsys) {
+ case SYS_DVBC_ANNEX_A:
+ case SYS_DVBC_ANNEX_C:
+ conf_file_name = "channels.conf.cbl";
+ break;
+ case SYS_ATSC:
+ conf_file_name = "channels.conf.atsc";
+ break;
+ case SYS_DVBT:
+ if (DELSYS_IS_SET(delsys_mask, SYS_DVBT2))
+ continue; /* Add all channels later with T2. */
+ /* PASSTOUTH */
+ case SYS_DVBT2:
+ conf_file_name = "channels.conf.ter";
+ break;
+ case SYS_DVBS:
+ if (DELSYS_IS_SET(delsys_mask, SYS_DVBS2))
+ continue; /* Add all channels later with S2. */
+ /* PASSTOUTH */
+ case SYS_DVBS2:
+ conf_file_name = "channels.conf.sat";
+ break;
+ default:
+ continue;
}
- if (conf_file) {
- mp_verbose(log, "Ignoring other channels.conf files.\n");
+
+ if (priv->cfg_file && priv->cfg_file[0]) {
+ talloc_ctx = NULL;
+ conf_file = priv->cfg_file;
} else {
- conf_file = mp_find_config_file(talloc_ctx, global,
- "channels.conf");
+ talloc_ctx = talloc_new(NULL);
+ conf_file = mp_find_config_file(talloc_ctx, global, conf_file_name);
+ if (conf_file) {
+ mp_verbose(log, "Ignoring other channels.conf files.\n");
+ } else {
+ conf_file = mp_find_config_file(talloc_ctx, global,
+ "channels.conf");
+ }
}
- }
- list = dvb_get_channels(log, priv->cfg_full_transponder, conf_file,
- type);
- talloc_free(talloc_ctx);
-
- if (list == NULL)
+ list = dvb_get_channels(log, list, priv->cfg_full_transponder, conf_file,
+ delsys, delsys_mask);
+ talloc_free(talloc_ctx);
+ }
+ /* Add adapter with non zero channel list. */
+ if (list == NULL)
continue;
- size = sizeof(dvb_card_config_t) * (state->count + 1);
- tmp = realloc(state->cards, size);
+ size = sizeof(dvb_adapter_config_t) * (state->adapters_count + 1);
+ tmp = realloc(state->adapters, size);
- if (tmp == NULL) {
+ if (tmp == NULL) {
mp_err(log, "DVB_CONFIG, can't realloc %d bytes, skipping\n",
size);
free(list);
continue;
- }
- cards = tmp;
-
- name = malloc(20);
- if (name == NULL) {
- mp_err(log, "DVB_CONFIG, can't realloc 20 bytes, skipping\n");
- free(list);
- free(tmp);
- continue;
- }
-
- state->cards = cards;
- state->cards[state->count].devno = i;
- state->cards[state->count].list = list;
- state->cards[state->count].type = type;
- snprintf(name, 20, "DVB-%c card n. %d",
- type == TUNER_TER ? 'T' : (type == TUNER_CBL ? 'C' : 'S'),
- state->count + 1);
- state->cards[state->count].name = name;
- state->count++;
}
- talloc_free(tuner_types);
+ adapters = tmp;
+
+ state->adapters = adapters;
+ state->adapters[state->adapters_count].devno = i;
+ state->adapters[state->adapters_count].delsys_mask = delsys_mask;
+ state->adapters[state->adapters_count].list = list;
+ state->adapters_count++;
}
- if (state->count == 0) {
+ if (state->adapters_count == 0) {
free(state);
state = NULL;
}
@@ -1151,5 +1203,4 @@ const stream_info_t stream_info_dvb = {
.name = "dvbin",
.open = dvb_open,
.protocols = (const char *const[]){ "dvb", NULL },
-
};
diff --git a/stream/stream_dvd.c b/stream/stream_dvd.c
index fe5796d..4fe3e3e 100644
--- a/stream/stream_dvd.c
+++ b/stream/stream_dvd.c
@@ -781,7 +781,9 @@ static int open_s_internal(stream_t *stream)
d->vts_file=vts_file;
d->cur_title = d->dvd_title;
- pgc = vts_file->vts_pgcit ? vts_file->vts_pgcit->pgci_srp[ttn].pgc : NULL;
+ pgc_id = vts_file->vts_ptt_srpt->title[ttn].ptt[0].pgcn; // local
+ pgn = vts_file->vts_ptt_srpt->title[ttn].ptt[0].pgn; // local
+ pgc = vts_file->vts_pgcit ? vts_file->vts_pgcit->pgci_srp[pgc_id-1].pgc : NULL;
/**
* Check number of audio channels and types
*/
@@ -879,8 +881,6 @@ static int open_s_internal(stream_t *stream)
* Determine which program chain we want to watch. This is based on the
* chapter number.
*/
- pgc_id = vts_file->vts_ptt_srpt->title[ttn].ptt[0].pgcn; // local
- pgn = vts_file->vts_ptt_srpt->title[ttn].ptt[0].pgn; // local
d->cur_pgc_idx = pgc_id-1;
d->cur_pgc = vts_file->vts_pgcit->pgci_srp[pgc_id-1].pgc;
d->cur_cell = d->cur_pgc->program_map[pgn-1] - 1; // start playback here
diff --git a/stream/stream_dvdnav.c b/stream/stream_dvdnav.c
index 21827b6..fc7ddfd 100644
--- a/stream/stream_dvdnav.c
+++ b/stream/stream_dvdnav.c
@@ -473,6 +473,7 @@ static int open_s_internal(stream_t *stream)
int best_title = -1;
int32_t num_titles;
if (dvdnav_get_number_of_titles(dvdnav, &num_titles) == DVDNAV_STATUS_OK) {
+ MP_VERBOSE(stream, "List of available titles:\n");
for (int n = 1; n <= num_titles; n++) {
uint64_t *parts = NULL, duration = 0;
dvdnav_describe_title_chapters(dvdnav, n, &parts, &duration);
@@ -481,6 +482,12 @@ static int open_s_internal(stream_t *stream)
best_length = duration;
best_title = n;
}
+ if (duration > 90000) { // arbitrarily ignore <1s titles
+ char *time = mp_format_time(duration / 90000, false);
+ MP_VERBOSE(stream, "title: %3d duration: %s\n",
+ n - 1, time);
+ talloc_free(time);
+ }
free(parts);
}
}
@@ -530,7 +537,7 @@ static int open_s(stream_t *stream)
priv->track = TITLE_MENU;
} else if (title.len) {
bstr rest;
- priv->title = bstrtoll(title, &rest, 10);
+ priv->track = bstrtoll(title, &rest, 10);
if (rest.len) {
MP_ERR(stream, "number expected: '%.*s'\n", BSTR_P(rest));
return STREAM_ERROR;
diff --git a/sub/ass_mp.c b/sub/ass_mp.c
index 6d85ac1..7006d53 100644
--- a/sub/ass_mp.c
+++ b/sub/ass_mp.c
@@ -3,18 +3,18 @@
*
* 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 free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
*
* mpv is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * GNU Lesser 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/>.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <inttypes.h>
@@ -220,7 +220,8 @@ static bool pack(struct mp_ass_packer *p, struct sub_bitmaps *res, int imgfmt)
res->packed_h = bb[1].y;
if (!p->cached_img || p->cached_img->w < res->packed_w ||
- p->cached_img->h < res->packed_h)
+ p->cached_img->h < res->packed_h ||
+ p->cached_img->imgfmt != imgfmt)
{
talloc_free(p->cached_img);
p->cached_img = mp_image_alloc(imgfmt, p->packer->w, p->packer->h);
diff --git a/sub/ass_mp.h b/sub/ass_mp.h
index 50397bd..4ebb7f3 100644
--- a/sub/ass_mp.h
+++ b/sub/ass_mp.h
@@ -3,18 +3,18 @@
*
* 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 free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
*
* mpv is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * GNU Lesser 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/>.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_ASS_MP_H
diff --git a/sub/filter_sdh.c b/sub/filter_sdh.c
new file mode 100644
index 0000000..ce8c28e
--- /dev/null
+++ b/sub/filter_sdh.c
@@ -0,0 +1,453 @@
+/*
+ * This file is part of mpv.
+ *
+ * mpv is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * mpv is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include "misc/ctype.h"
+#include "common/common.h"
+#include "common/msg.h"
+#include "options/options.h"
+#include "sd.h"
+
+// Filter for removing subtitle additions for deaf or hard-of-hearing (SDH)
+// This is for English, but may in part work for others too.
+// The intention is that it can always be active so may not remove
+// all SDH parts.
+// It is for filtering ASS encoded subtitles
+
+struct buffer {
+ char *string;
+ int length;
+ int pos;
+};
+
+static void init_buf(struct buffer *buf, int length)
+{
+ buf->string = talloc_size(NULL, length);
+ buf->pos = 0;
+ buf->length = length;
+}
+
+static inline int append(struct sd *sd, struct buffer *buf, char c)
+{
+ if (buf->pos >= 0 && buf->pos < buf->length) {
+ buf->string[buf->pos++] = c;
+ } else {
+ // ensure that terminating \0 is always written
+ if (c == '\0')
+ buf->string[buf->length - 1] = c;
+ }
+ return c;
+}
+
+
+// copy ass override tags, if they exist att current position,
+// from source string to destination buffer stopping at first
+// character following last sequence of '{text}'
+//
+// Parameters:
+// rpp read pointer pointer to source string, updated on return
+// buf write buffer
+//
+// on return the read pointer is updated to the position after
+// the tags.
+static void copy_ass(struct sd *sd, char **rpp, struct buffer *buf)
+{
+ char *rp = *rpp;
+
+ while (rp[0] == '{') {
+ while (*rp) {
+ char tmp = append(sd, buf, rp[0]);
+ rp++;
+ if (tmp == '}')
+ break;
+ }
+ }
+ *rpp = rp;
+
+ return;
+}
+
+// check for speaker label, like MAN:
+// normal subtitles may include mixed case text with : after so
+// only upper case is accepted and lower case l which for some
+// looks like upper case I unless filter_harder - then
+// lower case is also acceptable
+//
+// Parameters:
+// rpp read pointer pointer to source string, updated on return
+// buf write buffer
+//
+// scan in source string and copy ass tags to destination string
+// skipping speaker label if it exists
+//
+// if no label was found read pointer and write position in buffer
+// will be unchanged
+// otherwise they point to next position after label and next write position
+static void skip_speaker_label(struct sd *sd, char **rpp, struct buffer *buf)
+{
+ int filter_harder = sd->opts->sub_filter_SDH_harder;
+ char *rp = *rpp;
+ int old_pos = buf->pos;
+
+ copy_ass(sd, &rp, buf);
+ // copy any leading "- "
+ if (rp[0] == '-') {
+ append(sd, buf, rp[0]);
+ rp++;
+ }
+ copy_ass(sd, &rp, buf);
+ while (rp[0] == ' ') {
+ append(sd, buf, rp[0]);
+ rp++;
+ copy_ass(sd, &rp, buf);
+ }
+ // skip past valid data searching for :
+ while (*rp && rp[0] != ':') {
+ if (rp[0] == '{') {
+ copy_ass(sd, &rp, buf);
+ } else if ((mp_isalpha(rp[0]) &&
+ (filter_harder || mp_isupper(rp[0]) || rp[0] == 'l')) ||
+ mp_isdigit(rp[0]) ||
+ rp[0] == ' ' || rp[0] == '\'' ||
+ (filter_harder && (rp[0] == '(' || rp[0] == ')')) ||
+ rp[0] == '#' || rp[0] == '.' || rp[0] == ',') {
+ rp++;
+ } else {
+ buf->pos = old_pos;
+ return;
+ }
+ }
+ if (!*rp) {
+ // : was not found
+ buf->pos = old_pos;
+ return;
+ }
+ rp++; // skip :
+ copy_ass(sd, &rp, buf);
+ if (!*rp) {
+ // end of data
+ } else if (rp[0] == '\\' && rp[1] == 'N') {
+ // line end follows - skip it as line is empty
+ rp += 2;
+ } else if (rp[0] == ' ') {
+ while (rp[0] == ' ') {
+ rp++;
+ }
+ if (rp[0] == '\\' && rp[1] == 'N') {
+ // line end follows - skip it as line is empty
+ rp += 2;
+ }
+ } else {
+ // non space follows - no speaker label
+ buf->pos = old_pos;
+ return;
+ }
+ *rpp = rp;
+
+ return;
+}
+
+// check for bracketed text, like [SOUND]
+// and skip it while preserving ass tags
+// any characters are allowed, brackets are seldom used in normal text
+//
+// Parameters:
+// rpp read pointer pointer to source string, updated on return
+// buf write buffer
+//
+// scan in source string
+// the first character in source string must by the starting '['
+// and copy ass tags to destination string but
+// skipping bracketed text if it looks like SDH
+//
+// return true if bracketed text was removed.
+// if not valid SDH read pointer and write buffer position will be unchanged
+// otherwise they point to next position after text and next write position
+static bool skip_bracketed(struct sd *sd, char **rpp, struct buffer *buf)
+{
+ char *rp = *rpp;
+ int old_pos = buf->pos;
+
+ rp++; // skip past '['
+ // skip past valid data searching for ]
+ while (*rp && rp[0] != ']') {
+ if (rp[0] == '{') {
+ copy_ass(sd, &rp, buf);
+ } else {
+ rp++;
+ }
+ }
+ if (!*rp) {
+ // ] was not found
+ buf->pos = old_pos;
+ return false;
+ }
+ rp++; // skip ]
+ // skip trailing spaces
+ while (rp[0] == ' ') {
+ rp++;
+ }
+ *rpp = rp;
+
+ return true;
+}
+
+// check for paranthesed text, like (SOUND)
+// and skip it while preserving ass tags
+// normal subtitles may include mixed case text in parentheses so
+// only upper case is accepted and lower case l which for some
+// looks like upper case I but if requested harder filtering
+// both upper and lower case is accepted
+//
+// Parameters:
+// rpp read pointer pointer to source string, updated on return
+// buf write buffer
+//
+// scan in source string
+// the first character in source string must be the starting '('
+// and copy ass tags to destination string but
+// skipping paranthesed text if it looks like SDH
+//
+// return true if paranthesed text was removed.
+// if not valid SDH read pointer and write buffer position will be unchanged
+// otherwise they point to next position after text and next write position
+static bool skip_parenthesed(struct sd *sd, char **rpp, struct buffer *buf)
+{
+ int filter_harder = sd->opts->sub_filter_SDH_harder;
+ char *rp = *rpp;
+ int old_pos = buf->pos;
+
+ rp++; // skip past '('
+ // skip past valid data searching for )
+ bool only_digits = true;
+ while (*rp && rp[0] != ')') {
+ if (rp[0] == '{') {
+ copy_ass(sd, &rp, buf);
+ } else if ((mp_isalpha(rp[0]) &&
+ (filter_harder || mp_isupper(rp[0]) || rp[0] == 'l')) ||
+ mp_isdigit(rp[0]) ||
+ rp[0] == ' ' || rp[0] == '\'' || rp[0] == '#' ||
+ rp[0] == '.' || rp[0] == ',' ||
+ rp[0] == '-' || rp[0] == '"' || rp[0] == '\\') {
+ if (!mp_isdigit(rp[0]))
+ only_digits = false;
+ rp++;
+ } else {
+ buf->pos = old_pos;
+ return false;
+ }
+ }
+ if (!*rp) {
+ // ) was not found
+ buf->pos = old_pos;
+ return false;
+ }
+ if (only_digits) {
+ // number within parentheses is probably not SDH
+ buf->pos = old_pos;
+ return false;
+ }
+ rp++; // skip )
+ // skip trailing spaces
+ while (rp[0] == ' ') {
+ rp++;
+ }
+ *rpp = rp;
+
+ return true;
+}
+
+// remove leading hyphen and following spaces in write buffer
+//
+// Parameters:
+// start_pos start position i buffer
+// buf buffer to remove in
+//
+// when removing characters the following are moved back
+//
+static void remove_leading_hyphen_space(struct sd *sd, int start_pos, struct buffer *buf)
+{
+ int old_pos = buf->pos;
+ if (start_pos < 0 || start_pos >= old_pos)
+ return;
+ append(sd, buf, '\0'); // \0 terminate for reading
+
+ // move past leading ass tags
+ while (buf->string[start_pos] == '{') {
+ while (buf->string[start_pos] && buf->string[start_pos] != '}') {
+ start_pos++;
+ }
+ if (buf->string[start_pos])
+ start_pos++; // skip past '}'
+ }
+
+ // if there is not a leading '-' no removing will be done
+ if (buf->string[start_pos] != '-') {
+ buf->pos = old_pos;
+ return;
+ }
+
+ char *rp = &buf->string[start_pos]; // read from here
+ buf->pos = start_pos; // start writing here
+ rp++; // skip '-'
+ copy_ass(sd, &rp, buf);
+ while (rp[0] == ' ') {
+ rp++; // skip ' '
+ copy_ass(sd, &rp, buf);
+ }
+ while (*rp) {
+ // copy the rest
+ append(sd, buf, rp[0]);
+ rp++;
+ }
+}
+
+// Filter ASS formatted string for SDH
+//
+// Parameters:
+// format format line from ASS configuration
+// n_ignored number of comma to skip as preprocessing have removed them
+// data ASS line. null terminated string if length == 0
+// length length of ASS input if not null terminated, 0 otherwise
+//
+// Returns a talloc allocated string with filtered ASS data (may be the same
+// content as original if no SDH was found) which must be released
+// by caller using talloc_free.
+//
+// Returns NULL if filtering resulted in all of ASS data being removed so no
+// subtitle should be output
+char *filter_SDH(struct sd *sd, char *format, int n_ignored, char *data, int length)
+{
+ if (!format) {
+ MP_VERBOSE(sd, "SDH filtering not possible - format missing\n");
+ return length ? talloc_strndup(NULL, data, length) : talloc_strdup(NULL, data);
+ }
+
+ // need null terminated string
+ char *ass = length ? talloc_strndup(NULL, data, length) : data;
+
+ int comma = 0;
+ // scan format line to find the number of the field where the text is
+ for (char *c = format; *c; c++) {
+ if (*c == ',') {
+ comma++;
+ if (strncasecmp(c + 1, "Text", 4) == 0)
+ break;
+ }
+ }
+ // if preprocessed line some fields are skipped
+ comma -= n_ignored;
+
+ struct buffer writebuf;
+ struct buffer *buf = &writebuf;
+
+ init_buf(buf, strlen(ass) + 1); // with room for terminating '\0'
+
+ char *rp = ass;
+
+ // locate text field in ASS line
+ for (int k = 0; k < comma; k++) {
+ while (*rp) {
+ char tmp = append(sd, buf, rp[0]);
+ rp++;
+ if (tmp == ',')
+ break;
+ }
+ }
+ if (!*rp) {
+ talloc_free(buf->string);
+ MP_VERBOSE(sd, "SDH filtering not possible - cannot find text field\n");
+ return length ? ass : talloc_strdup(NULL, ass);
+ }
+
+ bool contains_text = false; // true if non SDH text was found
+ bool line_with_text = false; // if last line contained text
+ int wp_line_start = buf->pos; // write pos to start of last line
+ int wp_line_end = buf->pos; // write pos to end of previous line with text (\N)
+
+ // go through the lines in the text
+ // they are separated by \N
+ while (*rp) {
+ line_with_text = false;
+ wp_line_start = buf->pos;
+
+ // skip any speaker label
+ skip_speaker_label(sd, &rp, buf);
+
+ // go through the rest of the line looking for SDH in () or []
+ while (*rp && !(rp[0] == '\\' && rp[1] == 'N')) {
+ copy_ass(sd, &rp, buf);
+ if (rp[0] == '[') {
+ if (!skip_bracketed(sd, &rp, buf)) {
+ append(sd, buf, rp[0]);
+ rp++;
+ line_with_text = true;
+ }
+ } else if (rp[0] == '(') {
+ if (!skip_parenthesed(sd, &rp, buf)) {
+ append(sd, buf, rp[0]);
+ rp++;
+ line_with_text = true;
+ }
+ } else if (*rp && rp[0] != '\\') {
+ if (rp[0] > 32 && rp[0] < 127 && rp[0] != '-')
+ line_with_text = true;
+ append(sd, buf, rp[0]);
+ rp++;
+ } else if (rp[0] == '\\' && rp[1] != 'N') {
+ append(sd, buf, rp[0]);
+ rp++;
+ }
+ }
+ // either end of data or ASS line end defined by separating \N
+ if (*rp) {
+ // ASS line end
+ if (line_with_text) {
+ contains_text = true;
+ wp_line_end = buf->pos;
+ append(sd, buf, rp[0]); // copy backslash
+ append(sd, buf, rp[1]); // copy N
+ rp += 2; // move read pointer past \N
+ } else {
+ // no text in line, remove leading hyphen and spaces
+ remove_leading_hyphen_space(sd, wp_line_start, buf);
+ // and join with next line
+ rp += 2; // move read pointer past \N
+ }
+ }
+ }
+ // if no normal text i last line - remove last line
+ // by moving write pointer to start of last line
+ if (!line_with_text) {
+ buf->pos = wp_line_end;
+ } else {
+ contains_text = true;
+ }
+ if (length)
+ talloc_free(ass);
+ if (contains_text) {
+ // the ASS data contained normal text after filtering
+ append(sd, buf, '\0'); // '\0' terminate
+ return buf->string;
+ } else {
+ // all data removed by filtering
+ talloc_free(buf->string);
+ return NULL;
+ }
+}
diff --git a/sub/lavc_conv.c b/sub/lavc_conv.c
index 3ff350c..0f3b18e 100644
--- a/sub/lavc_conv.c
+++ b/sub/lavc_conv.c
@@ -91,6 +91,9 @@ struct lavc_conv *lavc_conv_create(struct mp_log *log, const char *codec_name,
av_dict_free(&opts);
// Documented as "set by libavcodec", but there is no other way
avctx->time_base = (AVRational) {1, 1000};
+#if LIBAVCODEC_VERSION_MICRO >= 100
+ avctx->pkt_timebase = avctx->time_base;
+#endif
priv->avctx = avctx;
priv->extradata = talloc_strndup(priv, avctx->subtitle_header,
avctx->subtitle_header_size);
diff --git a/sub/osd.c b/sub/osd.c
index a35380d..9517595 100644
--- a/sub/osd.c
+++ b/sub/osd.c
@@ -1,23 +1,22 @@
/*
* 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 free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
*
* mpv is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * GNU Lesser 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/>.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
-#include <string.h>
#include <assert.h>
#include <libavutil/common.h>
diff --git a/sub/osd.h b/sub/osd.h
index 7572ec0..d139647 100644
--- a/sub/osd.h
+++ b/sub/osd.h
@@ -1,18 +1,18 @@
/*
* 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 free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
*
* mpv is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * GNU Lesser 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/>.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_SUB_H
diff --git a/sub/osd_font.otf b/sub/osd_font.otf
index 26c8299..7c21c36 100644
--- a/sub/osd_font.otf
+++ b/sub/osd_font.otf
Binary files differ
diff --git a/sub/sd.h b/sub/sd.h
index 178a6d5..dd1f26d 100644
--- a/sub/sd.h
+++ b/sub/sd.h
@@ -50,4 +50,6 @@ char **lavc_conv_decode(struct lavc_conv *priv, struct demux_packet *packet);
void lavc_conv_reset(struct lavc_conv *priv);
void lavc_conv_uninit(struct lavc_conv *priv);
+char *filter_SDH(struct sd *sd, char *format, int n_ignored, char *data, int length);
+
#endif
diff --git a/sub/sd_ass.c b/sub/sd_ass.c
index 9b4d376..6405133 100644
--- a/sub/sd_ass.c
+++ b/sub/sd_ass.c
@@ -1,18 +1,18 @@
/*
* 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 free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
*
* mpv is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * GNU Lesser 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/>.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
@@ -259,8 +259,15 @@ static void decode(struct sd *sd, struct demux_packet *packet)
packet->duration = UNKNOWN_DURATION;
}
char **r = lavc_conv_decode(ctx->converter, packet);
- for (int n = 0; r && r[n]; n++)
- ass_process_data(track, r[n], strlen(r[n]));
+ for (int n = 0; r && r[n]; n++) {
+ char *ass_line = r[n];
+ if (sd->opts->sub_filter_SDH)
+ ass_line = filter_SDH(sd, track->event_format, 0, ass_line, 0);
+ if (ass_line)
+ ass_process_data(track, ass_line, strlen(ass_line));
+ if (sd->opts->sub_filter_SDH)
+ talloc_free(ass_line);
+ }
if (ctx->duration_unknown) {
for (int n = 0; n < track->n_events - 1; n++) {
if (track->events[n].Duration == UNKNOWN_DURATION * 1000) {
@@ -272,9 +279,18 @@ static void decode(struct sd *sd, struct demux_packet *packet)
} else {
// Note that for this packet format, libass has an internal mechanism
// for discarding duplicate (already seen) packets.
- ass_process_chunk(track, packet->buffer, packet->len,
- llrint(packet->pts * 1000),
- llrint(packet->duration * 1000));
+ char *ass_line = packet->buffer;
+ int ass_len = packet->len;
+ if (sd->opts->sub_filter_SDH) {
+ ass_line = filter_SDH(sd, track->event_format, 1, ass_line, ass_len);
+ ass_len = strlen(ass_line);
+ }
+ if (ass_line)
+ ass_process_chunk(track, ass_line, ass_len,
+ llrint(packet->pts * 1000),
+ llrint(packet->duration * 1000));
+ if (sd->opts->sub_filter_SDH)
+ talloc_free(ass_line);
}
}
@@ -371,7 +387,7 @@ static long long find_timestamp(struct sd *sd, double pts)
long long ts = llrint(pts * 1000);
- if (!sd->opts->sub_fix_timing)
+ if (!sd->opts->sub_fix_timing || sd->opts->ass_style_override == 0)
return ts;
// Try to fix small gaps and overlaps.
diff --git a/sub/sd_lavc.c b/sub/sd_lavc.c
index b660912..59e10ab 100644
--- a/sub/sd_lavc.c
+++ b/sub/sd_lavc.c
@@ -1,18 +1,18 @@
/*
* 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 free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
*
* mpv is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * GNU Lesser 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/>.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
@@ -113,6 +113,8 @@ static int init(struct sd *sd)
error:
MP_FATAL(sd, "Could not open libavcodec subtitle decoder\n");
+ if (ctx)
+ av_free(ctx->extradata);
av_free(ctx);
talloc_free(priv);
return -1;
diff --git a/ta/ta.c b/ta/ta.c
index 0045024..f8966bd 100644
--- a/ta/ta.c
+++ b/ta/ta.c
@@ -1,4 +1,6 @@
-/* Permission to use, copy, modify, and/or distribute this software for any
+/* Copyright (C) 2017 the mpv developers
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
diff --git a/ta/ta.h b/ta/ta.h
index 05564be..4945aa2 100644
--- a/ta/ta.h
+++ b/ta/ta.h
@@ -1,4 +1,6 @@
-/* Permission to use, copy, modify, and/or distribute this software for any
+/* Copyright (C) 2017 the mpv developers
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
diff --git a/ta/ta_talloc.c b/ta/ta_talloc.c
index 9c52bba..27dca22 100644
--- a/ta/ta_talloc.c
+++ b/ta/ta_talloc.c
@@ -1,4 +1,6 @@
-/* Permission to use, copy, modify, and/or distribute this software for any
+/* Copyright (C) 2017 the mpv developers
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
diff --git a/ta/ta_talloc.h b/ta/ta_talloc.h
index f5eb35e..693fd09 100644
--- a/ta/ta_talloc.h
+++ b/ta/ta_talloc.h
@@ -1,4 +1,6 @@
-/* Permission to use, copy, modify, and/or distribute this software for any
+/* Copyright (C) 2017 the mpv developers
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
@@ -14,6 +16,8 @@
#ifndef TA_TALLOC_H_
#define TA_TALLOC_H_
+#include <string.h>
+
#include "ta.h"
// Note: all talloc wrappers are wired to the "x" functions, which abort on OOM.
diff --git a/ta/ta_utils.c b/ta/ta_utils.c
index 6a7455c..cc122b5 100644
--- a/ta/ta_utils.c
+++ b/ta/ta_utils.c
@@ -1,4 +1,6 @@
-/* Permission to use, copy, modify, and/or distribute this software for any
+/* Copyright (C) 2017 the mpv developers
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
diff --git a/version.sh b/version.sh
index 674c6a1..e79ad0c 100755
--- a/version.sh
+++ b/version.sh
@@ -33,15 +33,13 @@ fi
# Extract revision number from file used by daily tarball snapshots
# or from "git describe" output
git_revision=$(cat snapshot_version 2> /dev/null)
-test "$git_revision" || test ! -e .git || git_revision="$(git rev-parse --short HEAD)"
-test "$git_revision" && git_revision="git-$git_revision"
+test "$git_revision" || test ! -e .git || git_revision="$(git describe \
+ --match "v[0-9]*" --always --tags | sed 's/^v//')"
version="$git_revision"
-# releases extract the version number from the VERSION file
-releaseversion="$(cat VERSION 2> /dev/null)"
-if test "$releaseversion" ; then
- test "$version" && version="-$version"
- version="$releaseversion$version"
+# other tarballs extract the version number from the VERSION file
+if test ! "$version"; then
+ version="$(cat VERSION 2> /dev/null)"
fi
test "$version" || version=UNKNOWN
diff --git a/video/decode/dec_video.c b/video/decode/dec_video.c
index 23aba81..5231fad 100644
--- a/video/decode/dec_video.c
+++ b/video/decode/dec_video.c
@@ -469,8 +469,7 @@ void video_work(struct dec_video *d_video)
video_reset(d_video);
} else {
d_video->codec = new_segment->codec;
- if (d_video->vd_driver)
- d_video->vd_driver->uninit(d_video);
+ d_video->vd_driver->uninit(d_video);
d_video->vd_driver = NULL;
video_init_best_codec(d_video);
}
diff --git a/video/decode/hw_cuda.c b/video/decode/hw_cuda.c
index 92ba077..d0c7669 100644
--- a/video/decode/hw_cuda.c
+++ b/video/decode/hw_cuda.c
@@ -63,7 +63,7 @@ static int init_decoder(struct lavc_ctx *ctx, int w, int h)
// This is proper use of the hw_frames_ctx API, but it does not work
// (appaears to work but fails e.g. with 10 bit). The cuvid wrapper
- // does non-standard things, and it's a meesy situation.
+ // does non-standard things, and it's a messy situation.
/*
hwframe_ctx->width = w;
hwframe_ctx->height = h;
diff --git a/video/decode/hw_d3d11va.c b/video/decode/hw_d3d11va.c
index a69a389..1eab4b5 100644
--- a/video/decode/hw_d3d11va.c
+++ b/video/decode/hw_d3d11va.c
@@ -27,7 +27,7 @@
#include "d3d.h"
-#define ADDITIONAL_SURFACES (HWDEC_EXTRA_SURFACES + HWDEC_DELAY_QUEUE_COUNT)
+#define ADDITIONAL_SURFACES HWDEC_EXTRA_SURFACES
struct d3d11va_decoder {
ID3D11VideoDecoder *decoder;
diff --git a/video/decode/hw_dxva2.c b/video/decode/hw_dxva2.c
index 7b1a6b4..7be0d9b 100644
--- a/video/decode/hw_dxva2.c
+++ b/video/decode/hw_dxva2.c
@@ -32,7 +32,7 @@
#include "d3d.h"
-#define ADDITIONAL_SURFACES (HWDEC_EXTRA_SURFACES + HWDEC_DELAY_QUEUE_COUNT)
+#define ADDITIONAL_SURFACES HWDEC_EXTRA_SURFACES
struct priv {
struct mp_log *log;
diff --git a/video/decode/hw_vaapi.c b/video/decode/hw_vaapi.c
deleted file mode 100644
index 99c23f4..0000000
--- a/video/decode/hw_vaapi.c
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * This file is part of mpv.
- *
- * mpv is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * mpv is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <stddef.h>
-#include <assert.h>
-
-#include <libavcodec/avcodec.h>
-#include <libavutil/common.h>
-#include <libavutil/hwcontext.h>
-#include <libavutil/hwcontext_vaapi.h>
-
-#include "config.h"
-
-#include "lavc.h"
-#include "common/common.h"
-#include "common/av_common.h"
-#include "video/fmt-conversion.h"
-#include "video/vaapi.h"
-#include "video/mp_image_pool.h"
-#include "video/hwdec.h"
-#include "video/filter/vf.h"
-
-#define ADDITIONAL_SURFACES (HWDEC_EXTRA_SURFACES + HWDEC_DELAY_QUEUE_COUNT)
-
-struct priv {
- struct mp_log *log;
- struct mp_vaapi_ctx *ctx;
- struct mp_hwdec_ctx *hwdev;
-};
-
-static int init_decoder(struct lavc_ctx *ctx, int w, int h)
-{
- struct priv *p = ctx->hwdec_priv;
- // libavcodec has no way yet to communicate the exact surface format needed
- // for the frame pool, or the required minimum size of the frame pool.
- // Hopefully, this weakness in the libavcodec API will be fixed in the
- // future.
- // For the pixel format, we try to second-guess from what the libavcodec
- // software decoder would require (sw_pix_fmt). It could break and require
- // adjustment if new VAAPI surface formats are added.
- int sw_format = ctx->avctx->sw_pix_fmt == AV_PIX_FMT_YUV420P10 ?
- AV_PIX_FMT_P010 : AV_PIX_FMT_NV12;
-
- // The video output might not support all formats.
- // Note that supported_formats==NULL means any are accepted.
- if (p->hwdev && p->hwdev->supported_formats) {
- int mp_format = pixfmt2imgfmt(sw_format);
- bool found = false;
- for (int n = 0; p->hwdev->supported_formats[n]; n++) {
- if (p->hwdev->supported_formats[n] == mp_format) {
- found = true;
- break;
- }
- }
- if (!found) {
- MP_WARN(ctx, "Surface format %s not supported for direct rendering.\n",
- mp_imgfmt_to_name(mp_format));
- return -1;
- }
- }
-
- return hwdec_setup_hw_frames_ctx(ctx, p->ctx->av_device_ref, sw_format,
- hwdec_get_max_refs(ctx) + ADDITIONAL_SURFACES);
-}
-
-static void uninit(struct lavc_ctx *ctx)
-{
- struct priv *p = ctx->hwdec_priv;
-
- if (!p)
- return;
-
- if (!p->hwdev)
- va_destroy(p->ctx);
-
- talloc_free(p);
- ctx->hwdec_priv = NULL;
-}
-
-static int init(struct lavc_ctx *ctx, bool direct)
-{
- struct priv *p = talloc_ptrtype(NULL, p);
- *p = (struct priv) {
- .log = mp_log_new(p, ctx->log, "vaapi"),
- };
-
- if (direct) {
- p->hwdev = hwdec_devices_get(ctx->hwdec_devs, HWDEC_VAAPI);
- p->ctx = p->hwdev->ctx;
- } else {
- p->ctx = va_create_standalone(ctx->log, false);
- if (!p->ctx) {
- talloc_free(p);
- return -1;
- }
- }
-
- ctx->hwdec_priv = p;
-
- if (!p->ctx->av_device_ref)
- return -1;
-
- return 0;
-}
-
-static int init_direct(struct lavc_ctx *ctx)
-{
- return init(ctx, true);
-}
-
-static int probe(struct lavc_ctx *ctx, struct vd_lavc_hwdec *hwdec,
- const char *codec)
-{
- if (!hwdec_devices_load(ctx->hwdec_devs, HWDEC_VAAPI))
- return HWDEC_ERR_NO_CTX;
- return 0;
-}
-
-static int probe_copy(struct lavc_ctx *ctx, struct vd_lavc_hwdec *hwdec,
- const char *codec)
-{
- struct mp_vaapi_ctx *dummy = va_create_standalone(ctx->log, true);
- if (!dummy)
- return HWDEC_ERR_NO_CTX;
- bool emulated = va_guess_if_emulated(dummy);
- va_destroy(dummy);
- if (emulated)
- return HWDEC_ERR_EMULATED;
- return 0;
-}
-
-static int init_copy(struct lavc_ctx *ctx)
-{
- return init(ctx, false);
-}
-
-const struct vd_lavc_hwdec mp_vd_lavc_vaapi = {
- .type = HWDEC_VAAPI,
- .image_format = IMGFMT_VAAPI,
- .volatile_context = true,
- .probe = probe,
- .init = init_direct,
- .uninit = uninit,
- .init_decoder = init_decoder,
-};
-
-const struct vd_lavc_hwdec mp_vd_lavc_vaapi_copy = {
- .type = HWDEC_VAAPI_COPY,
- .copying = true,
- .image_format = IMGFMT_VAAPI,
- .volatile_context = true,
- .probe = probe_copy,
- .init = init_copy,
- .uninit = uninit,
- .init_decoder = init_decoder,
- .delay_queue = HWDEC_DELAY_QUEUE_COUNT,
-};
diff --git a/video/decode/hw_vaapi_old.c b/video/decode/hw_vaapi_old.c
index 88379df..8ba4d44 100644
--- a/video/decode/hw_vaapi_old.c
+++ b/video/decode/hw_vaapi_old.c
@@ -44,7 +44,7 @@
* Note that redundant additional surfaces also might allow for some
* buffering (i.e. not trying to reuse a surface while it's busy).
*/
-#define ADDTIONAL_SURFACES MPMAX(6, HWDEC_DELAY_QUEUE_COUNT)
+#define ADDTIONAL_SURFACES HWDEC_EXTRA_SURFACES
// Some upper bound.
#define MAX_SURFACES 25
@@ -327,11 +327,12 @@ static int init(struct lavc_ctx *ctx, bool direct)
if (direct) {
p->ctx = hwdec_devices_get(ctx->hwdec_devs, HWDEC_VAAPI)->ctx;
} else {
- p->ctx = va_create_standalone(ctx->log, false);
- if (!p->ctx) {
+ struct mp_hwdec_ctx *hwctx = va_create_standalone(NULL, ctx->log, false);
+ if (!hwctx) {
talloc_free(p);
return -1;
}
+ p->ctx = hwctx->ctx;
p->own_ctx = true;
}
@@ -368,9 +369,10 @@ static int probe(struct lavc_ctx *ctx, struct vd_lavc_hwdec *hwdec,
static int probe_copy(struct lavc_ctx *ctx, struct vd_lavc_hwdec *hwdec,
const char *codec)
{
- struct mp_vaapi_ctx *dummy = va_create_standalone(ctx->log, true);
- if (!dummy)
+ struct mp_hwdec_ctx *hwctx = va_create_standalone(NULL, ctx->log, true);
+ if (!hwctx)
return HWDEC_ERR_NO_CTX;
+ struct mp_vaapi_ctx *dummy = hwctx->ctx;
bool emulated = va_guess_if_emulated(dummy);
va_destroy(dummy);
if (!hwdec_check_codec_support(codec, profiles))
diff --git a/video/decode/hw_vdpau.c b/video/decode/hw_vdpau.c
index 6047ef1..e47893f 100644
--- a/video/decode/hw_vdpau.c
+++ b/video/decode/hw_vdpau.c
@@ -51,10 +51,13 @@ static int init_decoder(struct lavc_ctx *ctx, int w, int h)
if (mp_vdpau_handle_preemption(p->mpvdp, &p->preemption_counter) < 0)
return 0;
- return av_vdpau_bind_context(ctx->avctx, p->mpvdp->vdp_device,
- p->mpvdp->get_proc_address,
- AV_HWACCEL_FLAG_IGNORE_LEVEL |
- AV_HWACCEL_FLAG_ALLOW_HIGH_DEPTH);
+ int r = av_vdpau_bind_context(ctx->avctx, p->mpvdp->vdp_device,
+ p->mpvdp->get_proc_address,
+ AV_HWACCEL_FLAG_IGNORE_LEVEL |
+ AV_HWACCEL_FLAG_ALLOW_HIGH_DEPTH);
+ if (r >= 0 && ctx->avctx->codec_id == AV_CODEC_ID_HEVC)
+ MP_WARN(ctx, "HEVC video output may be broken due to nVidia bugs.\n");
+ return r;
}
static void uninit(struct lavc_ctx *ctx)
diff --git a/video/decode/hw_videotoolbox.c b/video/decode/hw_videotoolbox.c
index c6f1a47..b343b1d 100644
--- a/video/decode/hw_videotoolbox.c
+++ b/video/decode/hw_videotoolbox.c
@@ -22,9 +22,11 @@
#include "common/av_common.h"
#include "common/msg.h"
+#include "options/options.h"
#include "video/mp_image.h"
#include "video/decode/lavc.h"
#include "video/mp_image_pool.h"
+#include "video/vt.h"
#include "config.h"
struct priv {
@@ -95,10 +97,23 @@ static void print_videotoolbox_error(struct mp_log *log, int lev, char *message,
mp_msg(log, lev, "%s: %d\n", message, error_code);
}
-static int init_decoder_common(struct lavc_ctx *ctx, int w, int h, AVVideotoolboxContext *vtctx)
+static int init_decoder(struct lavc_ctx *ctx, int w, int h)
{
av_videotoolbox_default_free(ctx->avctx);
+ AVVideotoolboxContext *vtctx = av_videotoolbox_alloc_context();
+ if (!vtctx)
+ return -1;
+
+ int imgfmt = ctx->opts->videotoolbox_format;
+#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(57, 81, 103)
+ if (!imgfmt)
+ imgfmt = IMGFMT_NV12;
+#endif
+ vtctx->cv_pix_fmt_type = mp_imgfmt_to_cvpixelformat(imgfmt);
+ MP_VERBOSE(ctx, "Requesting cv_pix_fmt_type=0x%x\n",
+ (unsigned)vtctx->cv_pix_fmt_type);
+
int err = av_videotoolbox_default_init2(ctx->avctx, vtctx);
if (err < 0) {
print_videotoolbox_error(ctx->log, MSGL_ERR, "failed to init videotoolbox decoder", err);
@@ -108,20 +123,6 @@ static int init_decoder_common(struct lavc_ctx *ctx, int w, int h, AVVideotoolbo
return 0;
}
-static int init_decoder(struct lavc_ctx *ctx, int w, int h)
-{
- AVVideotoolboxContext *vtctx = av_videotoolbox_alloc_context();
- struct mp_vt_ctx *vt = hwdec_devices_load(ctx->hwdec_devs, HWDEC_VIDEOTOOLBOX);
- vtctx->cv_pix_fmt_type = vt->get_vt_fmt(vt);
-
- return init_decoder_common(ctx, w, h, vtctx);
-}
-
-static int init_decoder_copy(struct lavc_ctx *ctx, int w, int h)
-{
- return init_decoder_common(ctx, w, h, NULL);
-}
-
static void uninit(struct lavc_ctx *ctx)
{
if (ctx->avctx)
@@ -138,55 +139,11 @@ static void uninit(struct lavc_ctx *ctx)
ctx->hwdec_priv = NULL;
}
-static int mp_imgfmt_from_cvpixelformat(uint32_t cvpixfmt)
-{
- switch (cvpixfmt) {
- case kCVPixelFormatType_420YpCbCr8Planar: return IMGFMT_420P;
- case kCVPixelFormatType_422YpCbCr8: return IMGFMT_UYVY;
- case kCVPixelFormatType_32BGRA: return IMGFMT_RGB0;
- case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange: return IMGFMT_NV12;
- }
- return 0;
-}
-
static struct mp_image *copy_image(struct lavc_ctx *ctx, struct mp_image *hw_image)
{
- if (hw_image->imgfmt != IMGFMT_VIDEOTOOLBOX)
- return hw_image;
-
struct priv *p = ctx->hwdec_priv;
- struct mp_image *image = NULL;
- CVPixelBufferRef pbuf = (CVPixelBufferRef)hw_image->planes[3];
- CVPixelBufferLockBaseAddress(pbuf, kCVPixelBufferLock_ReadOnly);
- size_t width = CVPixelBufferGetWidth(pbuf);
- size_t height = CVPixelBufferGetHeight(pbuf);
- uint32_t cvpixfmt = CVPixelBufferGetPixelFormatType(pbuf);
- int pixfmt = mp_imgfmt_from_cvpixelformat(cvpixfmt);
- if (!pixfmt)
- goto unlock;
-
- struct mp_image img = {0};
- mp_image_setfmt(&img, pixfmt);
- mp_image_set_size(&img, width, height);
-
- if (CVPixelBufferIsPlanar(pbuf)) {
- int planes = CVPixelBufferGetPlaneCount(pbuf);
- for (int i = 0; i < planes; i++) {
- img.planes[i] = CVPixelBufferGetBaseAddressOfPlane(pbuf, i);
- img.stride[i] = CVPixelBufferGetBytesPerRowOfPlane(pbuf, i);
- }
- } else {
- img.planes[0] = CVPixelBufferGetBaseAddress(pbuf);
- img.stride[0] = CVPixelBufferGetBytesPerRow(pbuf);
- }
-
- mp_image_copy_attributes(&img, hw_image);
-
- image = mp_image_pool_new_copy(p->sw_pool, &img);
-
-unlock:
- CVPixelBufferUnlockBaseAddress(pbuf, kCVPixelBufferLock_ReadOnly);
+ struct mp_image *image = mp_vt_download_image(NULL, hw_image, p->sw_pool);
if (image) {
talloc_free(hw_image);
return image;
@@ -222,7 +179,7 @@ const struct vd_lavc_hwdec mp_vd_lavc_videotoolbox_copy = {
.probe = probe_copy,
.init = init,
.uninit = uninit,
- .init_decoder = init_decoder_copy,
+ .init_decoder = init_decoder,
.process_image = copy_image,
.delay_queue = HWDEC_DELAY_QUEUE_COUNT,
};
diff --git a/video/decode/lavc.h b/video/decode/lavc.h
index fd1c7fe..4df92e4 100644
--- a/video/decode/lavc.h
+++ b/video/decode/lavc.h
@@ -16,8 +16,9 @@
// This number might require adjustment depending on whatever the player does;
// for example, if vo_opengl increases the number of reference surfaces for
// interpolation, this value has to be increased too.
-// This value does not yet include HWDEC_DELAY_QUEUE_COUNT.
-#define HWDEC_EXTRA_SURFACES 4
+#define HWDEC_EXTRA_SURFACES 6
+
+struct mpv_global;
typedef struct lavc_ctx {
struct mp_log *log;
@@ -55,6 +56,9 @@ typedef struct lavc_ctx {
// For free use by hwdec implementation
void *hwdec_priv;
+ // Set by generic hwaccels.
+ struct mp_hwdec_ctx *hwdec_dev;
+
int hwdec_fmt;
int hwdec_w;
int hwdec_h;
@@ -92,15 +96,24 @@ struct vd_lavc_hwdec {
struct mp_image *(*allocate_image)(struct lavc_ctx *ctx, int w, int h);
// Process the image returned by the libavcodec decoder.
struct mp_image *(*process_image)(struct lavc_ctx *ctx, struct mp_image *img);
- // For horrible Intel shit-drivers only
- void (*lock)(struct lavc_ctx *ctx);
- void (*unlock)(struct lavc_ctx *ctx);
- // Optional; if a special hardware decoder is needed (instead of "hwaccel").
- const char *(*get_codec)(struct lavc_ctx *ctx, const char *codec);
- // Suffix for libavcodec decoder. If non-NULL, get_codec() is overridden
+ // For copy hwdecs. If probing is true, don't log errors if unavailable.
+ // The returned device must be freed with mp_hwdec_ctx->destroy.
+ struct mp_hwdec_ctx *(*create_dev)(struct mpv_global *global,
+ struct mp_log *log, bool probing);
+ // Suffix for libavcodec decoder. If non-NULL, the codec is overridden
// with hwdec_find_decoder.
// Intuitively, this will force the corresponding wrapper decoder.
const char *lavc_suffix;
+ // Generic hwaccels set AVCodecContext.hw_frames_ctx in get_format().
+ // pixfmt_map must be non-NULL.
+ // struct lavc_ctx.hwdec_dev must be set at runtime (in init).
+ bool generic_hwaccel;
+ // Array of pixfmt pairs. The first pixfmt is the AVCodecContext.sw_pix_fmt,
+ // the second the required AVHWFramesContext.sw_format.
+ const enum AVPixelFormat (*pixfmt_map)[2];
+ // The generic hwaccel has a fixed pool size. Enough surfaces need to be
+ // preallocated before decoding begins. If false, pool size is left to 0.
+ bool static_pool;
};
enum {
diff --git a/video/decode/vd_lavc.c b/video/decode/vd_lavc.c
index 54d8278..e78b581 100644
--- a/video/decode/vd_lavc.c
+++ b/video/decode/vd_lavc.c
@@ -125,12 +125,8 @@ const struct m_sub_options vd_lavc_conf = {
},
};
-extern const struct vd_lavc_hwdec mp_vd_lavc_vdpau;
-extern const struct vd_lavc_hwdec mp_vd_lavc_vdpau_copy;
extern const struct vd_lavc_hwdec mp_vd_lavc_videotoolbox;
extern const struct vd_lavc_hwdec mp_vd_lavc_videotoolbox_copy;
-extern const struct vd_lavc_hwdec mp_vd_lavc_vaapi;
-extern const struct vd_lavc_hwdec mp_vd_lavc_vaapi_copy;
extern const struct vd_lavc_hwdec mp_vd_lavc_dxva2;
extern const struct vd_lavc_hwdec mp_vd_lavc_dxva2_copy;
extern const struct vd_lavc_hwdec mp_vd_lavc_d3d11va;
@@ -172,12 +168,78 @@ static const struct vd_lavc_hwdec mp_vd_lavc_crystalhd = {
.copying = true,
};
+#if HAVE_VAAPI_HWACCEL
+#if HAVE_VAAPI_HWACCEL_NEW
+const struct vd_lavc_hwdec mp_vd_lavc_vaapi = {
+ .type = HWDEC_VAAPI,
+ .image_format = IMGFMT_VAAPI,
+ .generic_hwaccel = true,
+ .static_pool = true,
+ .pixfmt_map = (const enum AVPixelFormat[][2]) {
+ {AV_PIX_FMT_YUV420P10, AV_PIX_FMT_P010},
+ {AV_PIX_FMT_YUV420P, AV_PIX_FMT_NV12},
+ {AV_PIX_FMT_NONE}
+ },
+};
+
+#include "video/vaapi.h"
+
+const struct vd_lavc_hwdec mp_vd_lavc_vaapi_copy = {
+ .type = HWDEC_VAAPI_COPY,
+ .copying = true,
+ .image_format = IMGFMT_VAAPI,
+ .generic_hwaccel = true,
+ .static_pool = true,
+ .create_dev = va_create_standalone,
+ .pixfmt_map = (const enum AVPixelFormat[][2]) {
+ {AV_PIX_FMT_YUV420P10, AV_PIX_FMT_P010},
+ {AV_PIX_FMT_YUV420P, AV_PIX_FMT_NV12},
+ {AV_PIX_FMT_NONE}
+ },
+};
+#else
+extern const struct vd_lavc_hwdec mp_vd_lavc_vaapi;
+extern const struct vd_lavc_hwdec mp_vd_lavc_vaapi_copy;
+#endif
+#endif
+
+#if HAVE_VDPAU_HWACCEL
+#if HAVE_VDPAU_HWACCEL_NEW
+const struct vd_lavc_hwdec mp_vd_lavc_vdpau = {
+ .type = HWDEC_VDPAU,
+ .image_format = IMGFMT_VDPAU,
+ .generic_hwaccel = true,
+ .pixfmt_map = (const enum AVPixelFormat[][2]) {
+ {AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV420P},
+ {AV_PIX_FMT_NONE}
+ },
+};
+
+#include "video/vdpau.h"
+
+const struct vd_lavc_hwdec mp_vd_lavc_vdpau_copy = {
+ .type = HWDEC_VDPAU_COPY,
+ .copying = true,
+ .image_format = IMGFMT_VDPAU,
+ .generic_hwaccel = true,
+ .create_dev = vdpau_create_standalone,
+ .pixfmt_map = (const enum AVPixelFormat[][2]) {
+ {AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV420P},
+ {AV_PIX_FMT_NONE}
+ },
+};
+#else
+extern const struct vd_lavc_hwdec mp_vd_lavc_vdpau;
+extern const struct vd_lavc_hwdec mp_vd_lavc_vdpau_copy;
+#endif
+#endif
+
static const struct vd_lavc_hwdec *const hwdec_list[] = {
#if HAVE_RPI
&mp_vd_lavc_rpi,
&mp_vd_lavc_rpi_copy,
#endif
-#if HAVE_VDPAU_HWACCEL
+#if HAVE_VDPAU_HWACCEL_OLD || HAVE_VDPAU_HWACCEL_NEW
&mp_vd_lavc_vdpau,
&mp_vd_lavc_vdpau_copy,
#endif
@@ -185,7 +247,7 @@ static const struct vd_lavc_hwdec *const hwdec_list[] = {
&mp_vd_lavc_videotoolbox,
&mp_vd_lavc_videotoolbox_copy,
#endif
-#if HAVE_VAAPI_HWACCEL
+#if HAVE_VAAPI_HWACCEL_OLD || HAVE_VAAPI_HWACCEL_NEW
&mp_vd_lavc_vaapi,
&mp_vd_lavc_vaapi_copy,
#endif
@@ -227,17 +289,6 @@ static bool hwdec_codec_allowed(struct dec_video *vd, const char *codec)
return false;
}
-static void hwdec_lock(struct lavc_ctx *ctx)
-{
- if (ctx->hwdec && ctx->hwdec->lock)
- ctx->hwdec->lock(ctx);
-}
-static void hwdec_unlock(struct lavc_ctx *ctx)
-{
- if (ctx->hwdec && ctx->hwdec->unlock)
- ctx->hwdec->unlock(ctx);
-}
-
// Find the correct profile entry for the current codec and profile.
// Assumes the table has higher profiles first (for each codec).
const struct hwdec_profile_entry *hwdec_find_profile(
@@ -320,13 +371,37 @@ static bool hwdec_is_wrapper(struct vd_lavc_hwdec *hwdec, const char *decoder)
return bstr_endswith0(bstr0(decoder), hwdec->lavc_suffix);
}
+static struct mp_hwdec_ctx *hwdec_create_dev(struct dec_video *vd,
+ struct vd_lavc_hwdec *hwdec,
+ bool autoprobe)
+{
+ if (hwdec->create_dev)
+ return hwdec->create_dev(vd->global, vd->log, autoprobe);
+ if (vd->hwdec_devs) {
+ hwdec_devices_request(vd->hwdec_devs, hwdec->type);
+ return hwdec_devices_get(vd->hwdec_devs, hwdec->type);
+ }
+ return NULL;
+}
+
static int hwdec_probe(struct dec_video *vd, struct vd_lavc_hwdec *hwdec,
- const char *codec)
+ const char *codec, bool autoprobe)
{
vd_ffmpeg_ctx *ctx = vd->priv;
int r = 0;
if (hwdec->probe)
r = hwdec->probe(ctx, hwdec, codec);
+ if (hwdec->generic_hwaccel) {
+ assert(!hwdec->probe && !hwdec->init && !hwdec->init_decoder &&
+ !hwdec->uninit && !hwdec->allocate_image && !hwdec->process_image);
+ struct mp_hwdec_ctx *dev = hwdec_create_dev(vd, hwdec, autoprobe);
+ if (!dev)
+ return hwdec->copying ? -1 : HWDEC_ERR_NO_CTX;
+ if (dev->emulated)
+ r = HWDEC_ERR_EMULATED;
+ if (hwdec->create_dev && dev->destroy)
+ dev->destroy(dev);
+ }
if (r >= 0) {
if (hwdec->lavc_suffix && !hwdec_find_decoder(codec, hwdec->lavc_suffix))
return HWDEC_ERR_NO_CODEC;
@@ -341,10 +416,11 @@ static struct vd_lavc_hwdec *probe_hwdec(struct dec_video *vd, bool autoprobe,
MP_VERBOSE(vd, "Probing '%s'...\n", m_opt_choice_str(mp_hwdec_names, api));
struct vd_lavc_hwdec *hwdec = find_hwcodec(api);
if (!hwdec) {
- MP_VERBOSE(vd, "Requested hardware decoder not compiled.\n");
+ int level = autoprobe ? MSGL_V : MSGL_WARN;
+ MP_MSG(vd, level, "Requested hardware decoder not compiled.\n");
return NULL;
}
- int r = hwdec_probe(vd, hwdec, codec);
+ int r = hwdec_probe(vd, hwdec, codec, autoprobe);
if (r == HWDEC_ERR_EMULATED) {
if (autoprobe)
return NULL;
@@ -432,8 +508,6 @@ static void reinit(struct dec_video *vd)
if (hwdec) {
const char *orig_decoder = decoder;
- if (hwdec->get_codec)
- decoder = hwdec->get_codec(ctx, decoder);
if (hwdec->lavc_suffix)
decoder = hwdec_find_decoder(codec, hwdec->lavc_suffix);
MP_VERBOSE(vd, "Trying hardware decoding.\n");
@@ -510,12 +584,20 @@ static void init_avctx(struct dec_video *vd, const char *decoder,
if (ctx->hwdec) {
avctx->thread_count = 1;
+#if HAVE_VDPAU_HWACCEL_NEW
+ avctx->hwaccel_flags |= AV_HWACCEL_FLAG_IGNORE_LEVEL;
+#endif
if (ctx->hwdec->image_format)
avctx->get_format = get_format_hwdec;
if (ctx->hwdec->allocate_image)
avctx->get_buffer2 = get_buffer2_hwdec;
if (ctx->hwdec->init && ctx->hwdec->init(ctx) < 0)
goto error;
+ if (ctx->hwdec->generic_hwaccel) {
+ ctx->hwdec_dev = hwdec_create_dev(vd, ctx->hwdec, false);
+ if (!ctx->hwdec_dev)
+ goto error;
+ }
ctx->max_delay_queue = ctx->hwdec->delay_queue;
ctx->hw_probing = true;
} else {
@@ -595,6 +677,11 @@ static void uninit_avctx(struct dec_video *vd)
av_freep(&ctx->avctx->extradata);
}
+ if (ctx->hwdec_dev && ctx->hwdec && ctx->hwdec->generic_hwaccel &&
+ ctx->hwdec_dev->destroy)
+ ctx->hwdec_dev->destroy(ctx->hwdec_dev);
+ ctx->hwdec_dev = NULL;
+
if (ctx->hwdec && ctx->hwdec->uninit)
ctx->hwdec->uninit(ctx);
ctx->hwdec = NULL;
@@ -675,11 +762,8 @@ int hwdec_setup_hw_frames_ctx(struct lavc_ctx *ctx, AVBufferRef *device_ctx,
fctx->initial_pool_size = initial_pool_size;
- hwdec_lock(ctx);
int res = av_hwframe_ctx_init(ctx->cached_hw_frames_ctx);
- hwdec_unlock(ctx);
-
- if (res > 0) {
+ if (res < 0) {
MP_ERR(ctx, "Failed to allocate hw frames.\n");
av_buffer_unref(&ctx->cached_hw_frames_ctx);
return -1;
@@ -691,6 +775,70 @@ int hwdec_setup_hw_frames_ctx(struct lavc_ctx *ctx, AVBufferRef *device_ctx,
return ctx->avctx->hw_frames_ctx ? 0 : -1;
}
+static int init_generic_hwaccel(struct dec_video *vd)
+{
+ struct lavc_ctx *ctx = vd->priv;
+ struct vd_lavc_hwdec *hwdec = ctx->hwdec;
+
+ if (!ctx->hwdec_dev)
+ return -1;
+
+ // libavcodec has no way yet to communicate the exact surface format needed
+ // for the frame pool, or the required minimum size of the frame pool.
+ // Hopefully, this weakness in the libavcodec API will be fixed in the
+ // future.
+ // For the pixel format, we try to second-guess from what the libavcodec
+ // software decoder would require (sw_pix_fmt). It could break and require
+ // adjustment if new hwaccel surface formats are added.
+ enum AVPixelFormat av_sw_format = AV_PIX_FMT_NONE;
+ for (int n = 0; hwdec->pixfmt_map[n][0] != AV_PIX_FMT_NONE; n++) {
+ if (ctx->avctx->sw_pix_fmt == hwdec->pixfmt_map[n][0]) {
+ av_sw_format = hwdec->pixfmt_map[n][1];
+ break;
+ }
+ }
+
+ if (av_sw_format == AV_PIX_FMT_NONE) {
+ MP_VERBOSE(ctx, "Unsupported hw decoding format: %s\n",
+ mp_imgfmt_to_name(pixfmt2imgfmt(ctx->avctx->sw_pix_fmt)));
+ return -1;
+ }
+
+ // The video output might not support all formats.
+ // Note that supported_formats==NULL means any are accepted.
+ int *render_formats = ctx->hwdec_dev->supported_formats;
+ if (render_formats) {
+ int mp_format = pixfmt2imgfmt(av_sw_format);
+ bool found = false;
+ for (int n = 0; render_formats[n]; n++) {
+ if (render_formats[n] == mp_format) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ MP_WARN(ctx, "Surface format %s not supported for direct rendering.\n",
+ mp_imgfmt_to_name(mp_format));
+ return -1;
+ }
+ }
+
+ int pool_size = 0;
+ if (hwdec->static_pool)
+ pool_size = hwdec_get_max_refs(ctx) + HWDEC_EXTRA_SURFACES;
+
+ ctx->hwdec_fmt = hwdec->image_format;
+
+ if (hwdec->image_format == IMGFMT_VDPAU &&
+ ctx->avctx->codec_id == AV_CODEC_ID_HEVC)
+ {
+ MP_WARN(ctx, "HEVC video output may be broken due to nVidia bugs.\n");
+ }
+
+ return hwdec_setup_hw_frames_ctx(ctx, ctx->hwdec_dev->av_device_ref,
+ av_sw_format, pool_size);
+}
+
static enum AVPixelFormat get_format_hwdec(struct AVCodecContext *avctx,
const enum AVPixelFormat *fmt)
{
@@ -711,8 +859,15 @@ static enum AVPixelFormat get_format_hwdec(struct AVCodecContext *avctx,
ctx->hwdec_request_reinit |= ctx->hwdec_failed;
ctx->hwdec_failed = false;
+ enum AVPixelFormat select = AV_PIX_FMT_NONE;
for (int i = 0; fmt[i] != AV_PIX_FMT_NONE; i++) {
if (ctx->hwdec->image_format == pixfmt2imgfmt(fmt[i])) {
+ if (ctx->hwdec->generic_hwaccel) {
+ if (init_generic_hwaccel(vd) < 0)
+ break;
+ select = fmt[i];
+ break;
+ }
// There could be more reasons for a change, and it's possible
// that we miss some. (Might also depend on the hwaccel type.)
bool change =
@@ -734,17 +889,25 @@ static enum AVPixelFormat get_format_hwdec(struct AVCodecContext *avctx,
break;
}
}
- return fmt[i];
+ select = fmt[i];
+ break;
}
}
- ctx->hwdec_failed = true;
- for (int i = 0; fmt[i] != AV_PIX_FMT_NONE; i++) {
- const AVPixFmtDescriptor *d = av_pix_fmt_desc_get(fmt[i]);
- if (d && !(d->flags & AV_PIX_FMT_FLAG_HWACCEL))
- return fmt[i];
+ if (select == AV_PIX_FMT_NONE) {
+ ctx->hwdec_failed = true;
+ for (int i = 0; fmt[i] != AV_PIX_FMT_NONE; i++) {
+ const AVPixFmtDescriptor *d = av_pix_fmt_desc_get(fmt[i]);
+ if (d && !(d->flags & AV_PIX_FMT_FLAG_HWACCEL)) {
+ select = fmt[i];
+ break;
+ }
+ }
}
- return AV_PIX_FMT_NONE;
+
+ const char *name = av_get_pix_fmt_name(select);
+ MP_VERBOSE(vd, "Requesting pixfmt '%s' from decoder.\n", name ? name : "-");
+ return select;
}
static int get_buffer2_hwdec(AVCodecContext *avctx, AVFrame *pic, int flags)
@@ -820,8 +983,11 @@ static void handle_err(struct dec_video *vd)
if (ctx->hwdec) {
ctx->hwdec_fail_count += 1;
// The FFmpeg VT hwaccel is buggy and can crash after 1 broken frame.
- bool vt = ctx->hwdec && ctx->hwdec->type == HWDEC_VIDEOTOOLBOX;
- if (ctx->hwdec_fail_count >= opts->software_fallback || vt)
+ bool force = false;
+#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(57, 82, 101)
+ force |= ctx->hwdec && ctx->hwdec->type == HWDEC_VIDEOTOOLBOX;
+#endif
+ if (ctx->hwdec_fail_count >= opts->software_fallback || force)
ctx->hwdec_failed = true;
}
}
@@ -837,10 +1003,7 @@ static bool do_send_packet(struct dec_video *vd, struct demux_packet *pkt)
AVPacket avpkt;
mp_set_av_packet(&avpkt, pkt, &ctx->codec_timebase);
- hwdec_lock(ctx);
int ret = avcodec_send_packet(avctx, pkt ? &avpkt : NULL);
- hwdec_unlock(ctx);
-
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
return false;
@@ -878,10 +1041,7 @@ static bool decode_frame(struct dec_video *vd)
if (!prepare_decoding(vd))
return true;
- hwdec_lock(ctx);
int ret = avcodec_receive_frame(avctx, ctx->pic);
- hwdec_unlock(ctx);
-
if (ret == AVERROR_EOF) {
// If flushing was initialized earlier and has ended now, make it start
// over in case we get new packets at some point in the future.
@@ -973,7 +1133,7 @@ static bool receive_frame(struct dec_video *vd, struct mp_image **out_image)
MP_ERR(vd, "Could not copy back hardware decoded frame.\n");
ctx->hwdec_fail_count = INT_MAX - 1; // force fallback
handle_err(vd);
- return NULL;
+ return false;
}
}
diff --git a/video/filter/vf.c b/video/filter/vf.c
index 94e6760..004df75 100644
--- a/video/filter/vf.c
+++ b/video/filter/vf.c
@@ -56,6 +56,7 @@ extern const vf_info_t vf_info_yadif;
extern const vf_info_t vf_info_stereo3d;
extern const vf_info_t vf_info_dlopen;
extern const vf_info_t vf_info_lavfi;
+extern const vf_info_t vf_info_lavfi_bridge;
extern const vf_info_t vf_info_vaapi;
extern const vf_info_t vf_info_vapoursynth;
extern const vf_info_t vf_info_vapoursynth_lazy;
@@ -74,6 +75,7 @@ static const vf_info_t *const filter_list[] = {
&vf_info_mirror,
&vf_info_lavfi,
+ &vf_info_lavfi_bridge,
&vf_info_rotate,
&vf_info_gradfun,
&vf_info_pullup,
@@ -128,6 +130,8 @@ static bool get_desc(struct m_obj_desc *dst, int index)
const struct m_obj_list vf_obj_list = {
.get_desc = get_desc,
.description = "video filters",
+ .allow_disable_entries = true,
+ .allow_unknown_entries = true,
};
// Try the cmd on each filter (starting with the first), and stop at the first
@@ -240,10 +244,17 @@ void vf_print_filter_chain(struct vf_chain *c, int msglevel,
static struct vf_instance *vf_open(struct vf_chain *c, const char *name,
char **args)
{
+ const char *lavfi_name = NULL;
+ char **lavfi_args = NULL;
struct m_obj_desc desc;
if (!m_obj_list_find(&desc, &vf_obj_list, bstr0(name))) {
- MP_ERR(c, "Couldn't find video filter '%s'.\n", name);
- return NULL;
+ if (!m_obj_list_find(&desc, &vf_obj_list, bstr0("lavfi-bridge"))) {
+ MP_ERR(c, "Couldn't find video filter '%s'.\n", name);
+ return NULL;
+ }
+ lavfi_name = name;
+ lavfi_args = args;
+ args = NULL;
}
vf_instance_t *vf = talloc_zero(NULL, struct vf_instance);
*vf = (vf_instance_t) {
@@ -259,6 +270,19 @@ static struct vf_instance *vf_open(struct vf_chain *c, const char *name,
name, c->opts->vf_defs, args);
if (!config)
goto error;
+ if (lavfi_name) {
+ // Pass the filter arguments as proper sub-options to the bridge filter.
+ struct m_config_option *name_opt = m_config_get_co(config, bstr0("name"));
+ assert(name_opt);
+ assert(name_opt->opt->type == &m_option_type_string);
+ if (m_config_set_option_raw(config, name_opt, &lavfi_name, 0) < 0)
+ goto error;
+ struct m_config_option *opts = m_config_get_co(config, bstr0("opts"));
+ assert(opts);
+ assert(opts->opt->type == &m_option_type_keyvalue_list);
+ if (m_config_set_option_raw(config, opts, &lavfi_args, 0) < 0)
+ goto error;
+ }
vf->priv = config->optstruct;
int retcode = vf->info->open(vf);
if (retcode < 1)
@@ -322,6 +346,8 @@ struct vf_instance *vf_append_filter(struct vf_chain *c, const char *name,
int vf_append_filter_list(struct vf_chain *c, struct m_obj_settings *list)
{
for (int n = 0; list && list[n].name; n++) {
+ if (!list[n].enabled)
+ continue;
struct vf_instance *vf =
vf_append_filter(c, list[n].name, list[n].attribs);
if (vf) {
@@ -576,7 +602,11 @@ static void update_formats(struct vf_chain *c, struct vf_instance *vf,
}
query_formats(fmts, vf);
const char *filter = find_conv_filter(fmts, out_formats);
- struct vf_instance *conv = vf_open(c, filter, NULL);
+ char **args = NULL;
+ char *args_no_warn[] = {"warn", "no", NULL};
+ if (strcmp(filter, "scale") == 0)
+ args = args_no_warn;
+ struct vf_instance *conv = vf_open(c, filter, args);
if (conv) {
conv->autoinserted = true;
conv->next = vf->next;
@@ -597,6 +627,39 @@ static void update_formats(struct vf_chain *c, struct vf_instance *vf,
}
}
+// Insert a conversion filter _after_ vf.
+// vf needs to have been successfully configured, vf->next unconfigured but
+// with formats negotiated.
+static void auto_insert_conversion_filter_if_needed(struct vf_chain *c,
+ struct vf_instance *vf)
+{
+ if (!vf->next || vf->next->query_format(vf->next, vf->fmt_out.imgfmt) ||
+ is_conv_filter(vf) || is_conv_filter(vf->next))
+ return;
+
+ MP_INFO(c, "Using conversion filter.\n");
+
+ uint8_t fmts[IMGFMT_END - IMGFMT_START];
+ query_formats(fmts, vf->next);
+
+ uint8_t out_formats[IMGFMT_END - IMGFMT_START];
+ for (int n = IMGFMT_START; n < IMGFMT_END; n++)
+ out_formats[n - IMGFMT_START] = n == vf->fmt_out.imgfmt;
+
+ const char *filter = find_conv_filter(out_formats, fmts);
+ char **args = NULL;
+ char *args_no_warn[] = {"warn", "no", NULL};
+ if (strcmp(filter, "scale") == 0)
+ args = args_no_warn;
+ struct vf_instance *conv = vf_open(c, filter, args);
+ if (conv) {
+ conv->autoinserted = true;
+ conv->next = vf->next;
+ vf->next = conv;
+ update_formats(c, conv, vf->last_outfmts);
+ }
+}
+
static int vf_reconfig_wrapper(struct vf_instance *vf,
const struct mp_image_params *p)
{
@@ -659,6 +722,8 @@ int vf_reconfig(struct vf_chain *c, const struct mp_image_params *params)
}
cur = vf->fmt_out;
hwfctx = vf->out_hwframes_ref;
+ // Recheck if the new output format works with the following filters.
+ auto_insert_conversion_filter_if_needed(c, vf);
}
c->output_params = cur;
c->initialized = r < 0 ? -1 : 1;
@@ -727,12 +792,14 @@ struct vf_chain *vf_new(struct mpv_global *global)
static const struct vf_info in = { .name = "in" };
c->first = talloc(c, struct vf_instance);
*c->first = (struct vf_instance) {
+ .log = c->log,
.info = &in,
.query_format = input_query_format,
};
static const struct vf_info out = { .name = "out" };
c->last = talloc(c, struct vf_instance);
*c->last = (struct vf_instance) {
+ .log = c->log,
.info = &out,
.query_format = output_query_format,
.priv = (void *)c,
diff --git a/video/filter/vf_buffer.c b/video/filter/vf_buffer.c
index 772f7cb..f08dfec 100644
--- a/video/filter/vf_buffer.c
+++ b/video/filter/vf_buffer.c
@@ -65,6 +65,7 @@ static void uninit(vf_instance_t *vf)
static int vf_open(vf_instance_t *vf)
{
+ MP_WARN(vf, "This filter is deprecated. No replacement.\n");
vf->filter_ext = filter_ext;
vf->control = control;
vf->uninit = uninit;
diff --git a/video/filter/vf_crop.c b/video/filter/vf_crop.c
index 6f9a788..79a2fce 100644
--- a/video/filter/vf_crop.c
+++ b/video/filter/vf_crop.c
@@ -100,6 +100,7 @@ static int query_format(struct vf_instance *vf, unsigned int fmt)
}
static int vf_open(vf_instance_t *vf){
+ MP_WARN(vf, "This filter is deprecated. Use lavfi crop instead.\n");
vf->reconfig=reconfig;
vf->filter=filter;
vf->query_format=query_format;
diff --git a/video/filter/vf_dlopen.c b/video/filter/vf_dlopen.c
index 1ef37e3..a53b0d1 100644
--- a/video/filter/vf_dlopen.c
+++ b/video/filter/vf_dlopen.c
@@ -263,6 +263,8 @@ static int vf_open(vf_instance_t *vf)
return 0;
}
+ MP_WARN(vf, "This filter is deprecated. No replacement.\n");
+
vf->priv->dll = DLLOpen(vf->priv->cfg_dllname);
if (!vf->priv->dll) {
MP_ERR(vf, "library not found: %s\n",
diff --git a/video/filter/vf_dsize.c b/video/filter/vf_dsize.c
index b498b31..27d21c0 100644
--- a/video/filter/vf_dsize.c
+++ b/video/filter/vf_dsize.c
@@ -83,6 +83,8 @@ static int reconfig(struct vf_instance *vf, struct mp_image_params *in,
static int vf_open(vf_instance_t *vf)
{
+ MP_WARN(vf, "This filter is deprecated. No replacement.\n");
+
vf->reconfig = reconfig;
return 1;
}
diff --git a/video/filter/vf_eq.c b/video/filter/vf_eq.c
index 33b340e..9be749a 100644
--- a/video/filter/vf_eq.c
+++ b/video/filter/vf_eq.c
@@ -390,6 +390,9 @@ int vf_open(vf_instance_t *vf)
vf_eq2_t *eq2;
double *par = vf->priv->par;
+ MP_WARN(vf, "This filter is deprecated. Use lavfi eq instead.\n"
+ "For interactive eq, there is no replacement.\n");
+
vf->control = control;
vf->query_format = query_format;
vf->filter = filter;
diff --git a/video/filter/vf_expand.c b/video/filter/vf_expand.c
index a788568..216c47a 100644
--- a/video/filter/vf_expand.c
+++ b/video/filter/vf_expand.c
@@ -142,6 +142,8 @@ static int query_format(struct vf_instance *vf, unsigned int fmt)
}
static int vf_open(vf_instance_t *vf){
+ MP_WARN(vf, "This filter is deprecated. Use lavfi pad instead.\n");
+
vf->reconfig=reconfig;
vf->query_format=query_format;
vf->filter=filter;
diff --git a/video/filter/vf_flip.c b/video/filter/vf_flip.c
index bec7e9b..30658e4 100644
--- a/video/filter/vf_flip.c
+++ b/video/filter/vf_flip.c
@@ -41,6 +41,8 @@ static int query_format(struct vf_instance *vf, unsigned int fmt)
}
static int vf_open(vf_instance_t *vf){
+ MP_WARN(vf, "This filter is deprecated. Use lavfi vflip instead.\n");
+
vf->filter=filter;
vf->query_format = query_format;
return 1;
diff --git a/video/filter/vf_gradfun.c b/video/filter/vf_gradfun.c
index 78e1f4e..c34b77f 100644
--- a/video/filter/vf_gradfun.c
+++ b/video/filter/vf_gradfun.c
@@ -59,6 +59,8 @@ static int lavfi_reconfig(struct vf_instance *vf,
static int vf_open(vf_instance_t *vf)
{
+ MP_WARN(vf, "%s", VF_LW_REPLACE);
+
bool have_radius = vf->priv->cfg_radius > -1;
bool have_size = vf->priv->cfg_size > -1;
diff --git a/video/filter/vf_lavfi.c b/video/filter/vf_lavfi.c
index d79bfd4..eefa051 100644
--- a/video/filter/vf_lavfi.c
+++ b/video/filter/vf_lavfi.c
@@ -35,7 +35,6 @@
#include <libavutil/error.h>
#include <libswscale/swscale.h>
#include <libavfilter/avfilter.h>
-#include <libavfilter/avfiltergraph.h>
#include <libavfilter/buffersink.h>
#include <libavfilter/buffersrc.h>
@@ -65,6 +64,9 @@
#endif
struct vf_priv_s {
+ // Single filter bridge, instead of a graph.
+ bool is_bridge;
+
AVFilterGraph *graph;
AVFilterContext *in;
AVFilterContext *out;
@@ -86,10 +88,9 @@ struct vf_priv_s {
char *cfg_graph;
int64_t cfg_sws_flags;
char **cfg_avopts;
-};
-static const struct vf_priv_s vf_priv_dflt = {
- .cfg_sws_flags = SWS_BICUBIC,
+ char *cfg_filter_name;
+ char **cfg_filter_opts;
};
static void destroy_graph(struct vf_instance *vf)
@@ -113,13 +114,12 @@ static bool recreate_graph(struct vf_instance *vf, struct mp_image_params *fmt)
AVFilterContext *in = NULL, *out = NULL;
int ret;
- if (bstr0(p->cfg_graph).len == 0) {
+ if (!p->is_bridge && bstr0(p->cfg_graph).len == 0) {
MP_FATAL(vf, "lavfi: no filter graph set\n");
return false;
}
destroy_graph(vf);
- MP_VERBOSE(vf, "lavfi: create graph: '%s'\n", p->cfg_graph);
AVFilterGraph *graph = avfilter_graph_alloc();
if (!graph)
@@ -165,14 +165,46 @@ static bool recreate_graph(struct vf_instance *vf, struct mp_image_params *fmt)
"out", NULL, NULL, graph) < 0)
goto error;
- outputs->name = av_strdup("in");
- outputs->filter_ctx = in;
+ if (p->is_bridge) {
+ AVFilterContext *filter = avfilter_graph_alloc_filter(graph,
+ avfilter_get_by_name(p->cfg_filter_name), "filter");
+ if (!filter)
+ goto error;
+
+ if (mp_set_avopts(vf->log, filter->priv, p->cfg_filter_opts) < 0)
+ goto error;
+
+ if (avfilter_init_str(filter, NULL) < 0)
+ goto error;
+
+ // Yep, we have to manually link those filters.
+ if (filter->nb_inputs != 1 ||
+ avfilter_pad_get_type(filter->input_pads, 0) != AVMEDIA_TYPE_VIDEO ||
+ filter->nb_outputs != 1 ||
+ avfilter_pad_get_type(filter->output_pads, 0) != AVMEDIA_TYPE_VIDEO)
+ {
+ MP_ERR(vf, "The filter is required to have 1 video input pad and "
+ "1 video output pad.\n");
+ goto error;
+ }
+ if (avfilter_link(in, 0, filter, 0) < 0 ||
+ avfilter_link(filter, 0, out, 0) < 0)
+ {
+ MP_ERR(vf, "Failed to link filter.\n");
+ goto error;
+ }
+ } else {
+ MP_VERBOSE(vf, "lavfi: create graph: '%s'\n", p->cfg_graph);
- inputs->name = av_strdup("out");
- inputs->filter_ctx = out;
+ outputs->name = av_strdup("in");
+ outputs->filter_ctx = in;
- if (graph_parse(graph, p->cfg_graph, inputs, outputs, NULL) < 0)
- goto error;
+ inputs->name = av_strdup("out");
+ inputs->filter_ctx = out;
+
+ if (graph_parse(graph, p->cfg_graph, inputs, outputs, NULL) < 0)
+ goto error;
+ }
if (vf->hwdec_devs) {
struct mp_hwdec_ctx *hwdec = hwdec_devices_get_first(vf->hwdec_devs);
@@ -384,6 +416,8 @@ static void uninit(struct vf_instance *vf)
static int vf_open(vf_instance_t *vf)
{
+ struct vf_priv_s *p = vf->priv;
+
vf->reconfig = reconfig;
vf->filter_ext = filter_ext;
vf->filter_out = filter_out;
@@ -391,6 +425,18 @@ static int vf_open(vf_instance_t *vf)
vf->query_format = query_format;
vf->control = control;
vf->uninit = uninit;
+
+ if (p->is_bridge) {
+ if (!p->cfg_filter_name) {
+ MP_ERR(vf, "Filter name not set!\n");
+ return 0;
+ }
+ if (!avfilter_get_by_name(p->cfg_filter_name)) {
+ MP_ERR(vf, "libavfilter filter '%s' not found!\n", p->cfg_filter_name);
+ return 0;
+ }
+ }
+
return 1;
}
@@ -445,11 +491,32 @@ const vf_info_t vf_info_lavfi = {
.name = "lavfi",
.open = vf_open,
.priv_size = sizeof(struct vf_priv_s),
- .priv_defaults = &vf_priv_dflt,
+ .priv_defaults = &(const struct vf_priv_s){
+ .cfg_sws_flags = SWS_BICUBIC,
+ },
.options = vf_opts_fields,
.print_help = print_help,
};
+const vf_info_t vf_info_lavfi_bridge = {
+ .description = "libavfilter bridge (explicit options)",
+ .name = "lavfi-bridge",
+ .open = vf_open,
+ .priv_size = sizeof(struct vf_priv_s),
+ .options = (const m_option_t[]) {
+ OPT_STRING("name", cfg_filter_name, M_OPT_MIN, .min = 1),
+ OPT_KEYVALUELIST("opts", cfg_filter_opts, 0),
+ OPT_INT64("sws-flags", cfg_sws_flags, 0),
+ OPT_KEYVALUELIST("o", cfg_avopts, 0),
+ {0}
+ },
+ .priv_defaults = &(const struct vf_priv_s){
+ .is_bridge = true,
+ .cfg_sws_flags = SWS_BICUBIC,
+ },
+ .print_help = print_help,
+};
+
// The following code is for the old filters wrapper code.
struct vf_lw_opts {
@@ -497,7 +564,7 @@ int vf_lw_set_graph(struct vf_instance *vf, struct vf_lw_opts *lavfi_opts,
void *old_priv = vf->priv;
struct vf_priv_s *p = talloc(vf, struct vf_priv_s);
vf->priv = p;
- *p = vf_priv_dflt;
+ *p = *(const struct vf_priv_s *)vf_info_lavfi.priv_defaults;
p->cfg_sws_flags = lavfi_opts->sws_flags;
p->cfg_avopts = lavfi_opts->avopts;
va_list ap;
diff --git a/video/filter/vf_lavfi.h b/video/filter/vf_lavfi.h
index 0b1f493..9196532 100644
--- a/video/filter/vf_lavfi.h
+++ b/video/filter/vf_lavfi.h
@@ -18,4 +18,8 @@ void vf_lw_set_reconfig_cb(struct vf_instance *vf,
struct mp_image_params *in,
struct mp_image_params *out));
+#define VF_LW_REPLACE "This filter will be replaced by using libavfilter " \
+ "option syntax directly. Parts of the old syntax will stop working, " \
+ "and some defaults may change.\n"
+
#endif
diff --git a/video/filter/vf_mirror.c b/video/filter/vf_mirror.c
index 342c6ab..f835d38 100644
--- a/video/filter/vf_mirror.c
+++ b/video/filter/vf_mirror.c
@@ -22,6 +22,7 @@
static int vf_open(vf_instance_t *vf)
{
+ MP_WARN(vf, "This filter is deprecated. Use lavfi hflip instead.\n");
return vf_lw_set_graph(vf, NULL, NULL, "hflip") >= 0;
}
diff --git a/video/filter/vf_noformat.c b/video/filter/vf_noformat.c
index 7fcc3de..2d3985c 100644
--- a/video/filter/vf_noformat.c
+++ b/video/filter/vf_noformat.c
@@ -44,6 +44,7 @@ static int query_format(struct vf_instance *vf, unsigned int fmt){
}
static int vf_open(vf_instance_t *vf){
+ MP_WARN(vf, "This filter is deprecated and will be removed (no replacement)\n");
vf->query_format=query_format;
return 1;
}
diff --git a/video/filter/vf_pullup.c b/video/filter/vf_pullup.c
index 39907da..eee6f43 100644
--- a/video/filter/vf_pullup.c
+++ b/video/filter/vf_pullup.c
@@ -38,6 +38,8 @@ struct vf_priv_s {
static int vf_open(vf_instance_t *vf)
{
+ MP_WARN(vf, "%s", VF_LW_REPLACE);
+
struct vf_priv_s *p = vf->priv;
const char *pname[3] = {"y", "u", "v"};
if (vf_lw_set_graph(vf, p->lw_opts, "pullup", "%d:%d:%d:%d:%d:%s",
diff --git a/video/filter/vf_rotate.c b/video/filter/vf_rotate.c
index dcaba53..be1247f 100644
--- a/video/filter/vf_rotate.c
+++ b/video/filter/vf_rotate.c
@@ -29,6 +29,7 @@
struct vf_priv_s {
int angle;
+ int warn;
struct vf_lw_opts *lw_opts;
};
@@ -67,6 +68,9 @@ static int vf_open(vf_instance_t *vf)
{
struct vf_priv_s *p = vf->priv;
+ if (p->warn)
+ MP_WARN(vf, "%s", VF_LW_REPLACE);
+
if (vf_lw_set_graph(vf, p->lw_opts, NULL, "%s", rot[p->angle]) >= 0) {
vf_lw_set_reconfig_cb(vf, lavfi_reconfig);
return 1;
@@ -88,6 +92,7 @@ const vf_info_t vf_info_rotate = {
{"180", 2},
{"270", 3},
{"auto", 4})),
+ OPT_FLAG("warn", warn, 0, OPTDEF_INT(1)),
OPT_SUBSTRUCT("", lw_opts, vf_lw_conf, 0),
{0}
},
diff --git a/video/filter/vf_scale.c b/video/filter/vf_scale.c
index 8b1f0a1..0759d28 100644
--- a/video/filter/vf_scale.c
+++ b/video/filter/vf_scale.c
@@ -39,6 +39,8 @@
#include "options/m_option.h"
+#include "vf_lavfi.h"
+
static struct vf_priv_s {
int w, h;
int cfg_w, cfg_h;
@@ -47,11 +49,13 @@ static struct vf_priv_s {
struct mp_sws_context *sws;
int noup;
int accurate_rnd;
+ int warn;
} const vf_priv_dflt = {
0, 0,
-1, -1,
0,
{SWS_PARAM_DEFAULT, SWS_PARAM_DEFAULT},
+ .warn = 1,
};
static int find_best_out(vf_instance_t *vf, int in_format)
@@ -238,6 +242,8 @@ static int vf_open(vf_instance_t *vf)
vf->priv->sws->log = vf->log;
vf->priv->sws->params[0] = vf->priv->param[0];
vf->priv->sws->params[1] = vf->priv->param[1];
+ if (vf->priv->warn)
+ MP_WARN(vf, "%s", VF_LW_REPLACE);
return 1;
}
@@ -250,6 +256,7 @@ static const m_option_t vf_opts_fields[] = {
OPT_INTRANGE("chr-drop", v_chr_drop, 0, 0, 3),
OPT_INTRANGE("noup", noup, 0, 0, 2),
OPT_FLAG("arnd", accurate_rnd, 0),
+ OPT_FLAG("warn", warn, 0),
{0}
};
diff --git a/video/filter/vf_stereo3d.c b/video/filter/vf_stereo3d.c
index bde2617..51cfff8 100644
--- a/video/filter/vf_stereo3d.c
+++ b/video/filter/vf_stereo3d.c
@@ -72,10 +72,12 @@ struct vf_priv_s {
int in_fmt;
int out_fmt;
bool auto_in;
+ int warn;
struct vf_lw_opts *lw_opts;
} const vf_priv_default = {
SIDE_BY_SIDE_LR,
ANAGLYPH_RC_DUBOIS,
+ .warn = 1,
};
const struct m_opt_choice_alternatives stereo_code_names[] = {
@@ -185,6 +187,9 @@ static void lavfi_init(vf_instance_t *vf)
static int vf_open(vf_instance_t *vf)
{
+ if (vf->priv->warn)
+ MP_WARN(vf, "%s", VF_LW_REPLACE);
+
if (vf->priv->out_fmt == STEREO_AUTO) {
MP_FATAL(vf, "No autodetection for stereo output.\n");
return 0;
@@ -200,6 +205,7 @@ static int vf_open(vf_instance_t *vf)
static const m_option_t vf_opts_fields[] = {
OPT_CHOICE_C("in", in_fmt, 0, stereo_code_names),
OPT_CHOICE_C("out", out_fmt, 0, stereo_code_names),
+ OPT_FLAG("warn", warn, 0),
OPT_SUBSTRUCT("", lw_opts, vf_lw_conf, 0),
{0}
};
diff --git a/video/filter/vf_sub.c b/video/filter/vf_sub.c
index a3680a3..184ab8d 100644
--- a/video/filter/vf_sub.c
+++ b/video/filter/vf_sub.c
@@ -127,6 +127,7 @@ static int control(vf_instance_t *vf, int request, void *data)
static int vf_open(vf_instance_t *vf)
{
+ MP_WARN(vf, "This filter is deprecated and will be removed (no replacement)\n");
vf->reconfig = reconfig;
vf->query_format = query_format;
vf->control = control;
diff --git a/video/filter/vf_vapoursynth.c b/video/filter/vf_vapoursynth.c
index 625d539..33a586c 100644
--- a/video/filter/vf_vapoursynth.c
+++ b/video/filter/vf_vapoursynth.c
@@ -736,7 +736,7 @@ static int vf_open(vf_instance_t *vf)
#define OPT_BASE_STRUCT struct vf_priv_s
static const m_option_t vf_opts_fields[] = {
- OPT_STRING("file", cfg_file, 0),
+ OPT_STRING("file", cfg_file, M_OPT_FILE),
OPT_INTRANGE("buffered-frames", cfg_maxbuffer, 0, 1, 9999, OPTDEF_INT(4)),
OPT_CHOICE_OR_INT("concurrent-frames", cfg_maxrequests, 0, 1, 99,
({"auto", -1}), OPTDEF_INT(-1)),
diff --git a/video/filter/vf_vavpp.c b/video/filter/vf_vavpp.c
index ad669ac..e3c107c 100644
--- a/video/filter/vf_vavpp.c
+++ b/video/filter/vf_vavpp.c
@@ -39,6 +39,7 @@ static bool check_error(struct vf_instance *vf, VAStatus status, const char *msg
struct surface_refs {
VASurfaceID *surfaces;
int num_surfaces;
+ int max_surfaces;
};
struct pipeline {
@@ -51,8 +52,9 @@ struct pipeline {
};
struct vf_priv_s {
- int deint_type; // 0: none, 1: discard, 2: double fps
+ int deint_type;
int interlaced_only;
+ int reversal_bug;
bool do_deint;
VABufferID buffers[VAProcFilterCount];
int num_buffers;
@@ -73,24 +75,26 @@ static const struct vf_priv_s vf_priv_default = {
.context = VA_INVALID_ID,
.deint_type = 2,
.interlaced_only = 1,
+ .reversal_bug = 1,
};
static void add_surfaces(struct vf_priv_s *p, struct surface_refs *refs, int dir)
{
- for (int n = 0; ; n++) {
+ for (int n = 0; n < refs->max_surfaces; n++) {
struct mp_image *s = mp_refqueue_get(p->queue, (1 + n) * dir);
if (!s)
break;
VASurfaceID id = va_surface_id(s);
- if (id != VA_INVALID_ID)
- MP_TARRAY_APPEND(p, refs->surfaces, refs->num_surfaces, id);
+ if (id == VA_INVALID_ID)
+ break;
+ MP_TARRAY_APPEND(p, refs->surfaces, refs->num_surfaces, id);
}
}
// The array items must match with the "deint" suboption values.
static const int deint_algorithm[] = {
[0] = VAProcDeinterlacingNone,
- [1] = VAProcDeinterlacingNone, // first-field, special-cased
+ [1] = VAProcDeinterlacingBob, // first-field, special-cased
[2] = VAProcDeinterlacingBob,
[3] = VAProcDeinterlacingWeave,
[4] = VAProcDeinterlacingMotionAdaptive,
@@ -112,8 +116,6 @@ static void update_pipeline(struct vf_instance *vf)
filters++;
num_filters--;
}
- if (filters == p->pipe.filters && num_filters == p->pipe.num_filters)
- return; /* cached state is correct */
p->pipe.forward.num_surfaces = p->pipe.backward.num_surfaces = 0;
p->pipe.num_input_colors = p->pipe.num_output_colors = 0;
p->pipe.num_filters = 0;
@@ -134,8 +136,15 @@ static void update_pipeline(struct vf_instance *vf)
p->pipe.num_filters = num_filters;
p->pipe.num_input_colors = caps.num_input_color_standards;
p->pipe.num_output_colors = caps.num_output_color_standards;
- mp_refqueue_set_refs(p->queue, caps.num_backward_references,
- caps.num_forward_references);
+ p->pipe.forward.max_surfaces = caps.num_forward_references;
+ p->pipe.backward.max_surfaces = caps.num_backward_references;
+ if (p->reversal_bug) {
+ int max = MPMAX(caps.num_forward_references, caps.num_backward_references);
+ mp_refqueue_set_refs(p->queue, max, max);
+ } else {
+ mp_refqueue_set_refs(p->queue, p->pipe.backward.max_surfaces,
+ p->pipe.forward.max_surfaces);
+ }
mp_refqueue_set_mode(p->queue,
(p->do_deint ? MP_MODE_DEINT : 0) |
(p->deint_type >= 2 ? MP_MODE_OUTPUT_FIELDS : 0) |
@@ -155,6 +164,7 @@ static struct mp_image *render(struct vf_instance *vf)
struct mp_image *img = NULL;
bool need_end_picture = false;
bool success = false;
+ VABufferID buffer = VA_INVALID_ID;
VASurfaceID in_id = va_surface_id(in);
if (!p->pipe.filters || in_id == VA_INVALID_ID)
@@ -187,7 +197,6 @@ static struct mp_image *render(struct vf_instance *vf)
need_end_picture = true;
- VABufferID buffer = VA_INVALID_ID;
VAProcPipelineParameterBuffer *param = NULL;
status = vaCreateBuffer(p->display, p->context,
VAProcPipelineParameterBufferType,
@@ -210,6 +219,7 @@ static struct mp_image *render(struct vf_instance *vf)
if (!check_error(vf, status, "vaMapBuffer()"))
goto cleanup;
+ *param = (VAProcPipelineParameterBuffer){0};
param->surface = in_id;
param->surface_region = &(VARectangle){0, 0, in->w, in->h};
param->output_region = &(VARectangle){0, 0, img->w, img->h};
@@ -218,14 +228,22 @@ static struct mp_image *render(struct vf_instance *vf)
param->filters = p->pipe.filters;
param->num_filters = p->pipe.num_filters;
- add_surfaces(p, &p->pipe.forward, 1);
+ int dir = p->reversal_bug ? -1 : 1;
+
+ add_surfaces(p, &p->pipe.forward, 1 * dir);
param->forward_references = p->pipe.forward.surfaces;
param->num_forward_references = p->pipe.forward.num_surfaces;
- add_surfaces(p, &p->pipe.backward, -1);
+ add_surfaces(p, &p->pipe.backward, -1 * dir);
param->backward_references = p->pipe.backward.surfaces;
param->num_backward_references = p->pipe.backward.num_surfaces;
+ MP_TRACE(vf, "in=0x%x\n", (unsigned)in_id);
+ for (int n = 0; n < param->num_backward_references; n++)
+ MP_TRACE(vf, " b%d=0x%x\n", n, (unsigned)param->backward_references[n]);
+ for (int n = 0; n < param->num_forward_references; n++)
+ MP_TRACE(vf, " f%d=0x%x\n", n, (unsigned)param->forward_references[n]);
+
vaUnmapBuffer(p->display, buffer);
status = vaRenderPicture(p->display, p->context, &buffer, 1);
@@ -237,6 +255,7 @@ static struct mp_image *render(struct vf_instance *vf)
cleanup:
if (need_end_picture)
vaEndPicture(p->display, p->context);
+ vaDestroyBuffer(p->display, buffer);
if (success)
return img;
talloc_free(img);
@@ -414,7 +433,7 @@ static bool initialize(struct vf_instance *vf)
buffers[i] = VA_INVALID_ID;
for (int i = 0; i < num_filters; i++) {
if (filters[i] == VAProcFilterDeinterlacing) {
- if (p->deint_type < 2)
+ if (p->deint_type < 1)
continue;
VAProcFilterCapDeinterlacing caps[VAProcDeinterlacingCount];
int num = va_query_filter_caps(vf, VAProcFilterDeinterlacing, caps,
@@ -425,7 +444,7 @@ static bool initialize(struct vf_instance *vf)
for (int n=0; n < num; n++) { // find the algorithm
if (caps[n].type != algorithm)
continue;
- VAProcFilterParameterBufferDeinterlacing param;
+ VAProcFilterParameterBufferDeinterlacing param = {0};
param.type = VAProcFilterDeinterlacing;
param.algorithm = algorithm;
buffers[VAProcFilterDeinterlacing] =
@@ -477,6 +496,7 @@ static const m_option_t vf_opts_fields[] = {
{"motion-adaptive", 4},
{"motion-compensated", 5})),
OPT_FLAG("interlaced-only", interlaced_only, 0),
+ OPT_FLAG("reversal-bug", reversal_bug, 0),
{0}
};
diff --git a/video/filter/vf_yadif.c b/video/filter/vf_yadif.c
index d1e1ffb..e044dcc 100644
--- a/video/filter/vf_yadif.c
+++ b/video/filter/vf_yadif.c
@@ -32,12 +32,16 @@ struct vf_priv_s {
int mode;
int interlaced_only;
struct vf_lw_opts *lw_opts;
+ int warn;
};
static int vf_open(vf_instance_t *vf)
{
struct vf_priv_s *p = vf->priv;
+ if (p->warn)
+ MP_WARN(vf, "%s", VF_LW_REPLACE);
+
#if LIBAVFILTER_VERSION_MICRO >= 100
const char *mode[] = {"send_frame", "send_field", "send_frame_nospatial",
"send_field_nospatial"};
@@ -69,6 +73,7 @@ static const m_option_t vf_opts_fields[] = {
{"frame-nospatial", 2},
{"field-nospatial", 3})),
OPT_FLAG("interlaced-only", interlaced_only, 0),
+ OPT_FLAG("warn", warn, 0),
OPT_SUBSTRUCT("", lw_opts, vf_lw_conf, 0),
{0}
};
@@ -81,6 +86,7 @@ const vf_info_t vf_info_yadif = {
.priv_defaults = &(const struct vf_priv_s){
.mode = 1,
.interlaced_only = 1,
+ .warn = 1,
},
.options = vf_opts_fields,
};
diff --git a/video/hwdec.c b/video/hwdec.c
index 6db8d57..371c368 100644
--- a/video/hwdec.c
+++ b/video/hwdec.c
@@ -72,8 +72,6 @@ void hwdec_devices_set_loader(struct mp_hwdec_devices *devs,
devs->load_api_ctx = load_api_ctx;
}
-// Cause VO to lazily load the requested device, and will block until this is
-// done (even if not available).
void hwdec_devices_request(struct mp_hwdec_devices *devs, enum hwdec_type type)
{
if (devs->load_api && !hwdec_devices_get_first(devs))
diff --git a/video/hwdec.h b/video/hwdec.h
index 9d1035c..49fd989 100644
--- a/video/hwdec.h
+++ b/video/hwdec.h
@@ -38,8 +38,8 @@ struct mp_hwdec_ctx {
const char *driver_name; // NULL if unknown/not loaded
// This is never NULL. Its meaning depends on the .type field:
- // HWDEC_VDPAU: struct mp_vaapi_ctx*
- // HWDEC_VIDEOTOOLBOX: struct mp_vt_ctx*
+ // HWDEC_VDPAU: struct mp_vdpau_ctx*
+ // HWDEC_VIDEOTOOLBOX: non-NULL dummy pointer
// HWDEC_VAAPI: struct mp_vaapi_ctx*
// HWDEC_D3D11VA: ID3D11Device*
// HWDEC_DXVA2: IDirect3DDevice9*
@@ -53,6 +53,9 @@ struct mp_hwdec_ctx {
// List of IMGFMT_s, terminated with 0. NULL if N/A.
int *supported_formats;
+ // Hint to generic code: it's using a wrapper API
+ bool emulated;
+
// Optional. Legacy. (New code should use AVHWFramesContext and
// mp_image_hw_download().)
// Allocates a software image from the pool, downloads the hw image from
@@ -62,11 +65,9 @@ struct mp_hwdec_ctx {
struct mp_image *(*download_image)(struct mp_hwdec_ctx *ctx,
struct mp_image *mpi,
struct mp_image_pool *swpool);
-};
-struct mp_vt_ctx {
- void *priv;
- uint32_t (*get_vt_fmt)(struct mp_vt_ctx *ctx);
+ // Optional. Do not set for VO-bound devices.
+ void (*destroy)(struct mp_hwdec_ctx *ctx);
};
// Used to communicate hardware decoder device handles from VO to video decoder.
diff --git a/video/image_writer.c b/video/image_writer.c
index 59d986f..9855839 100644
--- a/video/image_writer.c
+++ b/video/image_writer.c
@@ -42,7 +42,7 @@
#include "options/m_option.h"
const struct image_writer_opts image_writer_opts_defaults = {
- .format = "jpg",
+ .format = AV_CODEC_ID_MJPEG,
.high_bit_depth = 1,
.png_compression = 7,
.png_filter = 5,
@@ -52,15 +52,22 @@ const struct image_writer_opts image_writer_opts_defaults = {
.tag_csp = 0,
};
+const struct m_opt_choice_alternatives mp_image_writer_formats[] = {
+ {"jpg", AV_CODEC_ID_MJPEG},
+ {"jpeg", AV_CODEC_ID_MJPEG},
+ {"png", AV_CODEC_ID_PNG},
+ {0}
+};
+
#define OPT_BASE_STRUCT struct image_writer_opts
const struct m_option image_writer_opts[] = {
+ OPT_CHOICE_C("format", format, 0, mp_image_writer_formats),
OPT_INTRANGE("jpeg-quality", jpeg_quality, 0, 0, 100),
OPT_INTRANGE("jpeg-smooth", jpeg_smooth, 0, 0, 100),
OPT_FLAG("jpeg-source-chroma", jpeg_source_chroma, 0),
OPT_INTRANGE("png-compression", png_compression, 0, 0, 9),
OPT_INTRANGE("png-filter", png_filter, 0, 0, 5),
- OPT_STRING("format", format, 0),
OPT_FLAG("high-bit-depth", high_bit_depth, 0),
OPT_FLAG("tag-colorspace", tag_csp, 0),
{0},
@@ -69,16 +76,18 @@ const struct m_option image_writer_opts[] = {
struct image_writer_ctx {
struct mp_log *log;
const struct image_writer_opts *opts;
- const struct img_writer *writer;
struct mp_imgfmt_desc original_format;
};
-struct img_writer {
- const char *file_ext;
- bool (*write)(struct image_writer_ctx *ctx, mp_image_t *image, FILE *fp);
- const int *pixfmts;
- int lavc_codec;
-};
+static enum AVPixelFormat replace_j_format(enum AVPixelFormat fmt)
+{
+ switch (fmt) {
+ case AV_PIX_FMT_YUV420P: return AV_PIX_FMT_YUVJ420P;
+ case AV_PIX_FMT_YUV422P: return AV_PIX_FMT_YUVJ422P;
+ case AV_PIX_FMT_YUV444P: return AV_PIX_FMT_YUVJ444P;
+ }
+ return fmt;
+}
static bool write_lavc(struct image_writer_ctx *ctx, mp_image_t *image, FILE *fp)
{
@@ -89,7 +98,7 @@ static bool write_lavc(struct image_writer_ctx *ctx, mp_image_t *image, FILE *fp
av_init_packet(&pkt);
- struct AVCodec *codec = avcodec_find_encoder(ctx->writer->lavc_codec);
+ struct AVCodec *codec = avcodec_find_encoder(ctx->opts->format);
AVCodecContext *avctx = NULL;
if (!codec)
goto print_open_fail;
@@ -100,13 +109,17 @@ static bool write_lavc(struct image_writer_ctx *ctx, mp_image_t *image, FILE *fp
avctx->time_base = AV_TIME_BASE_Q;
avctx->width = image->w;
avctx->height = image->h;
+ avctx->color_range = mp_csp_levels_to_avcol_range(image->params.color.levels);
avctx->pix_fmt = imgfmt2pixfmt(image->imgfmt);
+ // Annoying deprecated garbage for the jpg encoder.
+ if (image->params.color.levels == MP_CSP_LEVELS_PC)
+ avctx->pix_fmt = replace_j_format(avctx->pix_fmt);
if (avctx->pix_fmt == AV_PIX_FMT_NONE) {
MP_ERR(ctx, "Image format %s not supported by lavc.\n",
mp_imgfmt_to_name(image->imgfmt));
goto error_exit;
}
- if (ctx->writer->lavc_codec == AV_CODEC_ID_PNG) {
+ if (codec->id == AV_CODEC_ID_PNG) {
avctx->compression_level = ctx->opts->png_compression;
av_opt_set_int(avctx, "pred", ctx->opts->png_filter,
AV_OPT_SEARCH_CHILDREN);
@@ -128,6 +141,7 @@ static bool write_lavc(struct image_writer_ctx *ctx, mp_image_t *image, FILE *fp
pic->format = avctx->pix_fmt;
pic->width = avctx->width;
pic->height = avctx->height;
+ pic->color_range = avctx->color_range;
if (ctx->opts->tag_csp) {
pic->color_primaries = mp_csp_prim_to_avcol_pri(image->params.color.primaries);
pic->color_trc = mp_csp_trc_to_avcol_trc(image->params.color.gamma);
@@ -136,7 +150,9 @@ static bool write_lavc(struct image_writer_ctx *ctx, mp_image_t *image, FILE *fp
int ret = avcodec_send_frame(avctx, pic);
if (ret < 0)
goto error_exit;
- avcodec_send_frame(avctx, NULL); // send EOF
+ ret = avcodec_send_frame(avctx, NULL); // send EOF
+ if (ret < 0)
+ goto error_exit;
ret = avcodec_receive_packet(avctx, &pkt);
if (ret < 0)
goto error_exit;
@@ -235,15 +251,14 @@ static int get_encoder_format(struct AVCodec *codec, int srcfmt, bool highdepth)
return current;
}
-static int get_target_format(struct image_writer_ctx *ctx, int srcfmt)
+static int get_target_format(struct image_writer_ctx *ctx)
{
- if (!ctx->writer->lavc_codec)
- goto unknown;
-
- struct AVCodec *codec = avcodec_find_encoder(ctx->writer->lavc_codec);
+ struct AVCodec *codec = avcodec_find_encoder(ctx->opts->format);
if (!codec)
goto unknown;
+ int srcfmt = ctx->original_format.id;
+
int target = get_encoder_format(codec, srcfmt, ctx->opts->high_bit_depth);
if (!target)
target = get_encoder_format(codec, srcfmt, true);
@@ -254,32 +269,7 @@ static int get_target_format(struct image_writer_ctx *ctx, int srcfmt)
return target;
unknown:
- return IMGFMT_RGB24;
-}
-
-static const struct img_writer img_writers[] = {
- { "png", write_lavc, .lavc_codec = AV_CODEC_ID_PNG },
- { "ppm", write_lavc, .lavc_codec = AV_CODEC_ID_PPM },
- { "pgm", write_lavc, .lavc_codec = AV_CODEC_ID_PGM },
- { "pgmyuv", write_lavc, .lavc_codec = AV_CODEC_ID_PGMYUV },
- { "tga", write_lavc, .lavc_codec = AV_CODEC_ID_TARGA },
-#if HAVE_JPEG
- { "jpg", write_jpeg },
- { "jpeg", write_jpeg },
-#endif
-};
-
-static const struct img_writer *get_writer(const struct image_writer_opts *opts)
-{
- const char *type = opts->format;
-
- for (size_t n = 0; n < sizeof(img_writers) / sizeof(img_writers[0]); n++) {
- const struct img_writer *writer = &img_writers[n];
- if (type && strcmp(type, writer->file_ext) == 0)
- return writer;
- }
-
- return &img_writers[0];
+ return IMGFMT_RGB0;
}
const char *image_writer_file_ext(const struct image_writer_opts *opts)
@@ -289,7 +279,16 @@ const char *image_writer_file_ext(const struct image_writer_opts *opts)
if (!opts)
opts = &defs;
- return get_writer(opts)->file_ext;
+ return m_opt_choice_str(mp_image_writer_formats, opts->format);
+}
+
+int image_writer_format_from_ext(const char *ext)
+{
+ for (int n = 0; mp_image_writer_formats[n].name; n++) {
+ if (ext && strcmp(mp_image_writer_formats[n].name, ext) == 0)
+ return mp_image_writer_formats[n].value;
+ }
+ return 0;
}
struct mp_image *convert_image(struct mp_image *image, int destfmt,
@@ -297,20 +296,38 @@ struct mp_image *convert_image(struct mp_image *image, int destfmt,
{
int d_w, d_h;
mp_image_params_get_dsize(&image->params, &d_w, &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)
+ struct mp_image_params p = {
+ .imgfmt = destfmt,
+ .w = d_w,
+ .h = d_h,
+ .p_w = 1,
+ .p_h = 1,
+ };
+ mp_image_params_guess_csp(&p);
+
+ // If RGB, just assume everything is correct.
+ if (p.color.space != MP_CSP_RGB) {
+ // Currently, assume what FFmpeg's jpg encoder needs.
+ // Of course this works only for non-HDR (no HDR support in libswscale).
+ p.color.levels = MP_CSP_LEVELS_PC;
+ p.color.space = MP_CSP_BT_601;
+ p.chroma_location = MP_CHROMA_CENTER;
+ mp_image_params_guess_csp(&p);
+ }
+
+ if (mp_image_params_equal(&p, &image->params))
return mp_image_new_ref(image);
- struct mp_image *dst = mp_image_alloc(destfmt, d_w, d_h);
+ struct mp_image *dst = mp_image_alloc(p.imgfmt, p.w, p.h);
if (!dst) {
mp_err(log, "Out of memory.\n");
return NULL;
}
mp_image_copy_attributes(dst, image);
+ dst->params = p;
+
if (mp_image_swscale(dst, image, mp_sws_hq_flags) < 0) {
mp_err(log, "Error when converting image.\n");
talloc_free(dst);
@@ -327,9 +344,19 @@ bool write_image(struct mp_image *image, const struct image_writer_opts *opts,
if (!opts)
opts = &defs;
- const struct img_writer *writer = get_writer(opts);
- struct image_writer_ctx ctx = { log, opts, writer, image->fmt };
- int destfmt = get_target_format(&ctx, image->imgfmt);
+ struct image_writer_ctx ctx = { log, opts, image->fmt };
+ bool (*write)(struct image_writer_ctx *, mp_image_t *, FILE *) = write_lavc;
+ int destfmt = 0;
+
+#if HAVE_JPEG
+ if (opts->format == AV_CODEC_ID_MJPEG) {
+ write = write_jpeg;
+ destfmt = IMGFMT_RGB24;
+ }
+#endif
+
+ if (!destfmt)
+ destfmt = get_target_format(&ctx);
struct mp_image *dst = convert_image(image, destfmt, log);
if (!dst)
@@ -340,7 +367,7 @@ bool write_image(struct mp_image *image, const struct image_writer_opts *opts,
if (fp == NULL) {
mp_err(log, "Error opening '%s' for writing!\n", filename);
} else {
- success = writer->write(&ctx, dst, fp);
+ success = write(&ctx, dst, fp);
success = !fclose(fp) && success;
if (!success)
mp_err(log, "Error writing file '%s'!\n", filename);
@@ -353,6 +380,6 @@ bool write_image(struct mp_image *image, const struct image_writer_opts *opts,
void dump_png(struct mp_image *image, const char *filename, struct mp_log *log)
{
struct image_writer_opts opts = image_writer_opts_defaults;
- opts.format = "png";
+ opts.format = AV_CODEC_ID_PNG;
write_image(image, &opts, filename, log);
}
diff --git a/video/image_writer.h b/video/image_writer.h
index 8b7414b..723dffc 100644
--- a/video/image_writer.h
+++ b/video/image_writer.h
@@ -21,7 +21,7 @@ struct mp_image;
struct mp_log;
struct image_writer_opts {
- char *format;
+ int format;
int high_bit_depth;
int png_compression;
int png_filter;
@@ -42,6 +42,9 @@ extern const struct m_option image_writer_opts[];
// Return the file extension that will be used, e.g. "png".
const char *image_writer_file_ext(const struct image_writer_opts *opts);
+// Map file extension to format ID - return 0 (which is invalid) if unknown.
+int image_writer_format_from_ext(const char *ext);
+
/*
* Save the given image under the given filename. The parameters csp and opts
* are optional. All pixel formats supported by swscale are supported.
diff --git a/video/img_format.c b/video/img_format.c
index 0232f53..395ba7e 100644
--- a/video/img_format.c
+++ b/video/img_format.c
@@ -211,7 +211,9 @@ struct mp_imgfmt_desc mp_imgfmt_get_desc(int mpfmt)
? MP_IMGFLAG_BE : MP_IMGFLAG_LE;
}
- if (fmt == AV_PIX_FMT_XYZ12LE || fmt == AV_PIX_FMT_XYZ12BE) {
+ if ((pd->flags & AV_PIX_FMT_FLAG_HWACCEL)) {
+ desc.flags |= MP_IMGFLAG_HWACCEL;
+ } else if (fmt == AV_PIX_FMT_XYZ12LE || fmt == AV_PIX_FMT_XYZ12BE) {
desc.flags |= MP_IMGFLAG_XYZ;
} else if (!(pd->flags & AV_PIX_FMT_FLAG_RGB) &&
fmt != AV_PIX_FMT_MONOBLACK &&
@@ -290,11 +292,10 @@ struct mp_imgfmt_desc mp_imgfmt_get_desc(int mpfmt)
if ((desc.bpp[0] % 8) != 0)
desc.align_x = 8 / desc.bpp[0]; // expect power of 2
- if (pd->flags & AV_PIX_FMT_FLAG_HWACCEL) {
- desc.flags |= MP_IMGFLAG_HWACCEL;
- desc.component_bits = 8; // usually restricted to 8 bit; may change
- desc.component_full_bits = desc.component_bits;
- desc.plane_bits = desc.component_bits;
+ if (desc.flags & MP_IMGFLAG_HWACCEL) {
+ desc.component_bits = 0;
+ desc.component_full_bits = 0;
+ desc.plane_bits = 0;
}
if (desc.chroma_xs || desc.chroma_ys)
diff --git a/video/mp_image.c b/video/mp_image.c
index 37d8f67..5e2726f 100644
--- a/video/mp_image.c
+++ b/video/mp_image.c
@@ -41,6 +41,9 @@
#include "video/filter/vf.h"
+#define HAVE_OPAQUE_REF (LIBAVUTIL_VERSION_MICRO >= 100 && \
+ LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(55, 47, 100))
+
static bool mp_image_alloc_planes(struct mp_image *mpi)
{
assert(!mpi->planes[0]);
@@ -174,6 +177,17 @@ void mp_image_steal_data(struct mp_image *dst, struct mp_image *src)
talloc_free(src);
}
+// Unref most data buffer (and clear the data array), but leave other fields
+// allocated. In particular, mp_image.hwctx is preserved.
+void mp_image_unref_data(struct mp_image *img)
+{
+ for (int n = 0; n < MP_MAX_PLANES; n++) {
+ img->planes[n] = NULL;
+ img->stride[n] = 0;
+ av_buffer_unref(&img->bufs[n]);
+ }
+}
+
// Return a new reference to img. The returned reference is owned by the caller,
// while img is left untouched.
struct mp_image *mp_image_new_ref(struct mp_image *img)
@@ -600,7 +614,8 @@ void mp_image_set_attributes(struct mp_image *image,
// the colorspace as implied by the pixel format.
void mp_image_params_guess_csp(struct mp_image_params *params)
{
- struct mp_imgfmt_desc fmt = mp_imgfmt_get_desc(params->imgfmt);
+ int imgfmt = params->hw_subfmt ? params->hw_subfmt : params->imgfmt;
+ struct mp_imgfmt_desc fmt = mp_imgfmt_get_desc(imgfmt);
if (!fmt.id)
return;
if (fmt.flags & MP_IMGFLAG_YUV) {
@@ -721,6 +736,15 @@ static void mp_image_copy_fields_from_av_frame(struct mp_image *dst,
};
dst->params.chroma_location = avchroma_location_to_mp(src->chroma_location);
+
+#if HAVE_OPAQUE_REF
+ if (src->opaque_ref) {
+ struct mp_image_params *p = (void *)src->opaque_ref->data;
+ dst->params.rotate = p->rotate;
+ dst->params.stereo_in = p->stereo_in;
+ dst->params.stereo_out = p->stereo_out;
+ }
+#endif
}
// Copy properties and data of the mp_image into the AVFrame, without taking
@@ -756,6 +780,14 @@ static void mp_image_copy_fields_to_av_frame(struct AVFrame *dst,
dst->color_trc = mp_csp_trc_to_avcol_trc(src->params.color.gamma);
dst->chroma_location = mp_chroma_location_to_av(src->params.chroma_location);
+
+#if HAVE_OPAQUE_REF
+ av_buffer_unref(&dst->opaque_ref);
+ dst->opaque_ref = av_buffer_alloc(sizeof(struct mp_image_params));
+ if (!dst->opaque_ref)
+ abort();
+ *(struct mp_image_params *)dst->opaque_ref->data = src->params;
+#endif
}
// Create a new mp_image reference to av_frame.
diff --git a/video/mp_image.h b/video/mp_image.h
index 6606f19..e3ffd55 100644
--- a/video/mp_image.h
+++ b/video/mp_image.h
@@ -124,6 +124,7 @@ int mp_image_plane_h(struct mp_image *mpi, int plane);
void mp_image_setfmt(mp_image_t* mpi, int out_fmt);
void mp_image_steal_data(struct mp_image *dst, struct mp_image *src);
+void mp_image_unref_data(struct mp_image *img);
struct mp_image *mp_image_new_dummy_ref(struct mp_image *img);
struct mp_image *mp_image_new_custom_ref(struct mp_image *img, void *arg,
diff --git a/video/out/cocoa/events_view.h b/video/out/cocoa/events_view.h
index 6ad51cc..91a6289 100644
--- a/video/out/cocoa/events_view.h
+++ b/video/out/cocoa/events_view.h
@@ -21,5 +21,4 @@
@interface MpvEventsView : NSView <NSDraggingDestination>
@property(nonatomic, retain) MpvCocoaAdapter *adapter;
- (BOOL)canHideCursor;
-- (void)signalMousePosition;
@end
diff --git a/video/out/cocoa/events_view.m b/video/out/cocoa/events_view.m
index f76ca0d..3184947 100644
--- a/video/out/cocoa/events_view.m
+++ b/video/out/cocoa/events_view.m
@@ -74,6 +74,9 @@
userInfo:nil] autorelease];
[self addTrackingArea:self.tracker];
+
+ if (![self containsMouseLocation])
+ [self.adapter putKey:MP_KEY_MOUSE_LEAVE withModifiers:0];
}
- (NSPoint)mouseLocation
@@ -83,7 +86,19 @@
- (BOOL)containsMouseLocation
{
- NSRect vF = [[self.window screen] visibleFrame];
+ CGFloat topMargin = 0.0;
+ CGFloat menuBarHeight = [[NSApp mainMenu] menuBarHeight];
+
+ // menuBarHeight is 0 when menu bar is hidden in fullscreen
+ // 1pt to compensate of the black line beneath the menu bar
+ if ([self.adapter isInFullScreenMode] && menuBarHeight > 0) {
+ NSRect tr = [NSWindow frameRectForContentRect:CGRectZero
+ styleMask:NSWindowStyleMaskTitled];
+ topMargin = tr.size.height + 1 + menuBarHeight;
+ }
+
+ NSRect vF = [[self.window screen] frame];
+ vF.size.height -= topMargin;
NSRect vFW = [self.window convertRectFromScreen:vF];
NSRect vFV = [self convertRect:vFW fromView:nil];
NSPoint pt = [self convertPoint:[self mouseLocation] fromView:nil];
@@ -109,16 +124,6 @@
- (BOOL)resignFirstResponder { return YES; }
-- (void)keyDown:(NSEvent *)event
-{
- [self.adapter putKeyEvent:event];
-}
-
-- (void)keyUp:(NSEvent *)event
-{
- [self.adapter putKeyEvent:event];
-}
-
- (BOOL)canHideCursor
{
return !self.hasMouseDown && [self containsMouseLocation]
@@ -148,8 +153,6 @@
if (self.clearing)
return;
-
- [self signalMousePosition];
}
- (NSPoint)convertPointToPixels:(NSPoint)point
@@ -162,14 +165,6 @@
return point;
}
-- (void)signalMousePosition
-{
- NSPoint p = [self convertPointToPixels:[self mouseLocation]];
- p.x = MIN(MAX(p.x, 0), self.bounds.size.width-1);
- p.y = MIN(MAX(p.y, 0), self.bounds.size.height-1);
- [self.adapter signalMouseMovement:p];
-}
-
- (void)signalMouseMovement:(NSEvent *)event
{
NSPoint p = [self convertPointToPixels:[event locationInWindow]];
@@ -273,7 +268,8 @@
[self preciseScroll:event];
} else {
const int modifiers = [event modifierFlags];
- const int mpkey = [event deltaY] > 0 ? MP_MOUSE_BTN3 : MP_MOUSE_BTN4;
+ const int mpkey = ([event deltaX] + [event deltaY]) > 0 ?
+ MP_MOUSE_BTN3 : MP_MOUSE_BTN4;
[self.adapter putKey:mpkey withModifiers:modifiers];
}
}
diff --git a/video/out/cocoa/mpvadapter.h b/video/out/cocoa/mpvadapter.h
index e547708..eb40df0 100644
--- a/video/out/cocoa/mpvadapter.h
+++ b/video/out/cocoa/mpvadapter.h
@@ -21,14 +21,13 @@
@interface MpvCocoaAdapter : NSObject<NSWindowDelegate>
- (void)setNeedsResize;
- (void)signalMouseMovement:(NSPoint)point;
-- (void)putKeyEvent:(NSEvent*)event;
- (void)putKey:(int)mpkey withModifiers:(int)modifiers;
- (void)putAxis:(int)mpkey delta:(float)delta;
- (void)putCommand:(char*)cmd;
- (void)handleFilesArray:(NSArray *)files;
- (void)didChangeWindowedScreenProfile:(NSNotification *)notification;
- (void)performAsyncResize:(NSSize)size;
-- (void)didChangeMousePosition;
+- (void)windowDidChangePhysicalScreen;
- (BOOL)isInFullScreenMode;
- (BOOL)keyboardEnabled;
diff --git a/video/out/cocoa/video_view.m b/video/out/cocoa/video_view.m
index cb09dc8..45c21cd 100644
--- a/video/out/cocoa/video_view.m
+++ b/video/out/cocoa/video_view.m
@@ -45,6 +45,8 @@
- (void)drawRect:(NSRect)rect
{
+ [[NSColor blackColor] setFill];
+ NSRectFill(rect);
[self.adapter performAsyncResize:[self frameInPixels].size];
}
@end
diff --git a/video/out/cocoa/window.m b/video/out/cocoa/window.m
index 1ff57ac..d26d727 100644
--- a/video/out/cocoa/window.m
+++ b/video/out/cocoa/window.m
@@ -29,6 +29,7 @@
@property(nonatomic, retain) NSScreen *targetScreen;
@property(nonatomic, retain) NSScreen *previousScreen;
@property(nonatomic, retain) NSScreen *currentScreen;
+@property(nonatomic, retain) NSScreen *unfScreen;
- (NSRect)frameRect:(NSRect)frameRect forCenteredContentSize:(NSSize)newSize;
- (void)setCenteredContentSize:(NSSize)newSize;
@@ -37,7 +38,6 @@
@implementation MpvVideoWindow {
NSSize _queued_video_size;
NSRect _unfs_content_frame;
- NSRect _unfs_screen_frame;
int _is_animating;
}
@@ -45,6 +45,7 @@
@synthesize targetScreen = _target_screen;
@synthesize previousScreen = _previous_screen;
@synthesize currentScreen = _current_screen;
+@synthesize unfScreen = _unf_Screen;
- (id)initWithContentRect:(NSRect)content_rect
styleMask:(NSUInteger)style_mask
backing:(NSBackingStoreType)buffering_type
@@ -56,19 +57,26 @@
backing:buffering_type
defer:flag
screen:screen]) {
- [self setBackgroundColor:[NSColor blackColor]];
+ [self setBackgroundColor:[NSColor whiteColor]];
[self setMinSize:NSMakeSize(50,50)];
[self setCollectionBehavior: NSWindowCollectionBehaviorFullScreenPrimary];
self.targetScreen = screen;
self.currentScreen = screen;
+ self.unfScreen = screen;
_is_animating = 0;
_unfs_content_frame = [self convertRectToScreen:[[self contentView] frame]];
- _unfs_screen_frame = [screen frame];
}
return self;
}
+- (void)setStyleMask:(NSWindowStyleMask)style
+{
+ NSResponder *nR = [self firstResponder];
+ [super setStyleMask:style];
+ [self makeFirstResponder:nR];
+}
+
- (void)toggleFullScreen:(id)sender
{
if (_is_animating)
@@ -88,12 +96,14 @@
if (![self.adapter isInFullScreenMode]) {
_unfs_content_frame = [self convertRectToScreen:[[self contentView] frame]];
- _unfs_screen_frame = [[self screen] frame];
+ self.unfScreen = [self screen];
}
//move window to target screen when going to fullscreen
if (![self.adapter isInFullScreenMode] && ![[self targetScreen] isEqual:[self screen]]) {
- [self setFrame:[self calculateWindowPositionForScreen:[self targetScreen]] display:YES];
+ NSRect frame = [self calculateWindowPositionForScreen:[self targetScreen]
+ withoutBounds:NO];
+ [self setFrame:frame display:YES];
}
[super toggleFullScreen:sender];
@@ -115,7 +125,8 @@
- (void)setToWindow
{
[self setStyleMask:([self styleMask] & ~NSWindowStyleMaskFullScreen)];
- NSRect frame = [self calculateWindowPositionForScreen:[self targetScreen]];
+ NSRect frame = [self calculateWindowPositionForScreen:[self targetScreen]
+ withoutBounds:[[self targetScreen] isEqual:[self screen]]];
[self setFrame:frame display:YES];
[self setContentAspectRatio:_unfs_content_frame.size];
[self setCenteredContentSize:_unfs_content_frame.size];
@@ -168,14 +179,15 @@
- (void)windowDidChangeScreen:(NSNotification *)notification
{
- //this event doesn't exclusively trigger on screen change
- //examples: screen reconfigure, toggling fullscreen
+ [self.adapter windowDidChangeScreen:notification];
+
if (!_is_animating && ![[self currentScreen] isEqual:[self screen]]) {
self.previousScreen = [self screen];
}
if (![[self currentScreen] isEqual:[self screen]]) {
- [self.adapter windowDidChangeScreen:notification];
+ [self.adapter windowDidChangePhysicalScreen];
}
+
self.currentScreen = [self screen];
}
@@ -204,6 +216,11 @@
[self.adapter windowDidDeminiaturize:notification];
}
+- (void)windowWillMove:(NSNotification *)notification
+{
+ [self.adapter windowWillMove:notification];
+}
+
- (BOOL)canBecomeMainWindow { return YES; }
- (BOOL)canBecomeKeyWindow { return YES; }
- (BOOL)windowShouldClose:(id)sender
@@ -271,20 +288,53 @@
animate:NO];
}
-- (NSRect)calculateWindowPositionForScreen:(NSScreen *)screen
+- (NSRect)calculateWindowPositionForScreen:(NSScreen *)screen withoutBounds:(BOOL)withoutBounds
{
NSRect frame = [self frameRectForContentRect:_unfs_content_frame];
NSRect targetFrame = [screen frame];
-
- CGFloat x_per = (_unfs_screen_frame.size.width - frame.size.width);
- CGFloat y_per = (_unfs_screen_frame.size.height - frame.size.height);
- if (x_per > 0) x_per = (frame.origin.x - _unfs_screen_frame.origin.x)/x_per;
- if (y_per > 0) y_per = (frame.origin.y - _unfs_screen_frame.origin.y)/y_per;
-
- frame.origin.x = targetFrame.origin.x +
- (targetFrame.size.width - frame.size.width)*x_per;
- frame.origin.y = targetFrame.origin.y +
- (targetFrame.size.height - frame.size.height)*y_per;
+ NSRect targetVisibleFrame = [screen visibleFrame];
+ NSRect unfsScreenFrame = [self.unfScreen frame];
+ NSRect visibleWindow = NSIntersectionRect(unfsScreenFrame, frame);
+
+ // calculate visible area of every side
+ CGFloat left = frame.origin.x - unfsScreenFrame.origin.x;
+ CGFloat right = unfsScreenFrame.size.width -
+ (frame.origin.x - unfsScreenFrame.origin.x + frame.size.width);
+ CGFloat bottom = frame.origin.y - unfsScreenFrame.origin.y;
+ CGFloat top = unfsScreenFrame.size.height -
+ (frame.origin.y - unfsScreenFrame.origin.y + frame.size.height);
+
+ // normalize visible areas, decide which one to take horizontal/vertical
+ CGFloat x_per = (unfsScreenFrame.size.width - visibleWindow.size.width);
+ CGFloat y_per = (unfsScreenFrame.size.height - visibleWindow.size.height);
+ if (x_per != 0) x_per = (left >= 0 || right < 0 ? left : right)/x_per;
+ if (y_per != 0) y_per = (bottom >= 0 || top < 0 ? bottom : top)/y_per;
+
+ // calculate visible area for every side for target screen
+ CGFloat x_new_left = targetFrame.origin.x +
+ (targetFrame.size.width - visibleWindow.size.width)*x_per;
+ CGFloat x_new_right = targetFrame.origin.x + targetFrame.size.width -
+ (targetFrame.size.width - visibleWindow.size.width)*x_per - frame.size.width;
+ CGFloat y_new_bottom = targetFrame.origin.y +
+ (targetFrame.size.height - visibleWindow.size.height)*y_per;
+ CGFloat y_new_top = targetFrame.origin.y + targetFrame.size.height -
+ (targetFrame.size.height - visibleWindow.size.height)*y_per - frame.size.height;
+
+ // calculate new coordinates, decide which one to take horizontal/vertical
+ frame.origin.x = left >= 0 || right < 0 ? x_new_left : x_new_right;
+ frame.origin.y = bottom >= 0 || top < 0 ? y_new_bottom : y_new_top;
+
+ // don't place new window on top of a visible menubar
+ CGFloat top_mar = targetFrame.size.height -
+ (frame.origin.y - targetFrame.origin.y + frame.size.height);
+ CGFloat menuBarHeight = targetFrame.size.height -
+ (targetVisibleFrame.size.height + targetVisibleFrame.origin.y);
+
+ if (top_mar < menuBarHeight)
+ frame.origin.y -= top-menuBarHeight;
+
+ if (withoutBounds)
+ return frame;
//screen bounds right and left
if (frame.origin.x + frame.size.width > targetFrame.origin.x + targetFrame.size.width)
diff --git a/video/out/cocoa_common.m b/video/out/cocoa_common.m
index 0420b0d..14a96c4 100644
--- a/video/out/cocoa_common.m
+++ b/video/out/cocoa_common.m
@@ -69,9 +69,16 @@ struct vo_cocoa_state {
NSOpenGLContext *nsgl_ctx;
NSScreen *current_screen;
+ CGDirectDisplayID display_id;
NSInteger window_level;
int fullscreen;
+ NSRect unfs_window;
+
+ bool cursor_visibility;
+ bool cursor_visibility_wanted;
+ bool window_is_dragged;
+ id event_monitor_mouseup;
bool embedded; // wether we are embedding in another GUI
@@ -107,8 +114,6 @@ struct vo_cocoa_state {
// render frames
int frame_w, frame_h; // dimensions of the frame rendered
- NSCursor *blankCursor;
-
char *window_title;
};
@@ -117,37 +122,14 @@ static void run_on_main_thread(struct vo *vo, void(^block)(void))
dispatch_sync(dispatch_get_main_queue(), block);
}
-static NSRect calculate_window_geometry(struct vo *vo, NSRect rect)
-{
- struct vo_cocoa_state *s = vo->cocoa;
- struct mp_vo_opts *opts = vo->opts;
-
- NSRect screenFrame = [s->current_screen frame];
- rect.origin.y = screenFrame.size.height - (rect.origin.y + rect.size.height);
-
- if(!opts->hidpi_window_scale) {
- NSRect oldRect = rect;
- rect = [s->current_screen convertRectFromBacking:rect];
-
- CGFloat x_per = screenFrame.size.width - oldRect.size.width;
- CGFloat y_per = screenFrame.size.height - oldRect.size.height;
- if (x_per > 0) x_per = oldRect.origin.x/x_per;
- if (y_per > 0) y_per = oldRect.origin.y/y_per;
-
- rect.origin.x = (screenFrame.size.width - rect.size.width)*x_per;
- rect.origin.y = (screenFrame.size.height - rect.size.height)*y_per;
- }
-
- return rect;
-}
-
static void queue_new_video_size(struct vo *vo, int w, int h)
{
struct vo_cocoa_state *s = vo->cocoa;
struct mp_vo_opts *opts = vo->opts;
id<MpvWindowUpdate> win = (id<MpvWindowUpdate>) s->window;
- NSRect r = calculate_window_geometry(vo, NSMakeRect(0, 0, w, h));
- [win queueNewVideoSize:NSMakeSize(r.size.width, r.size.height)];
+ NSRect r = NSMakeRect(0, 0, w, h);
+ r = [s->current_screen convertRectFromBacking:r];
+ [win queueNewVideoSize:r.size];
}
static void flag_events(struct vo *vo, int events)
@@ -307,28 +289,65 @@ static void vo_cocoa_update_screen_info(struct vo *vo)
if (!s->current_screen)
s->current_screen = [NSScreen mainScreen];
}
+
+ NSDictionary* sinfo = [s->current_screen deviceDescription];
+ s->display_id = [[sinfo objectForKey:@"NSScreenNumber"] longValue];
+}
+
+static void vo_cocoa_signal_swap(struct vo_cocoa_state *s)
+{
+ pthread_mutex_lock(&s->sync_lock);
+ s->sync_counter += 1;
+ pthread_cond_signal(&s->sync_wakeup);
+ pthread_mutex_unlock(&s->sync_lock);
+}
+
+static void vo_cocoa_start_displaylink(struct vo_cocoa_state *s)
+{
+ if (!CVDisplayLinkIsRunning(s->link))
+ CVDisplayLinkStart(s->link);
+}
+
+static void vo_cocoa_stop_displaylink(struct vo_cocoa_state *s)
+{
+ if (CVDisplayLinkIsRunning(s->link)) {
+ CVDisplayLinkStop(s->link);
+ vo_cocoa_signal_swap(s);
+ }
}
static void vo_cocoa_init_displaylink(struct vo *vo)
{
struct vo_cocoa_state *s = vo->cocoa;
- NSDictionary* sinfo = [s->current_screen deviceDescription];
- NSNumber* sid = [sinfo objectForKey:@"NSScreenNumber"];
- CGDirectDisplayID did = [sid longValue];
-
- CVDisplayLinkCreateWithCGDisplay(did, &s->link);
+ CVDisplayLinkCreateWithCGDisplay(s->display_id, &s->link);
CVDisplayLinkSetOutputCallback(s->link, &displayLinkCallback, vo);
CVDisplayLinkStart(s->link);
}
static void vo_cocoa_uninit_displaylink(struct vo_cocoa_state *s)
{
- if (CVDisplayLinkIsRunning(s->link))
- CVDisplayLinkStop(s->link);
+ vo_cocoa_stop_displaylink(s);
CVDisplayLinkRelease(s->link);
}
+static void cocoa_add_event_monitor(struct vo *vo)
+{
+ struct vo_cocoa_state *s = vo->cocoa;
+
+ s->event_monitor_mouseup = [NSEvent
+ addLocalMonitorForEventsMatchingMask: NSEventMaskLeftMouseUp
+ handler:^NSEvent*(NSEvent* event) {
+ s->window_is_dragged = false;
+ return event;
+ }];
+}
+
+static void cocoa_rm_event_monitor(struct vo *vo)
+{
+ [NSEvent removeMonitor:vo->cocoa->event_monitor_mouseup];
+}
+
void vo_cocoa_init(struct vo *vo)
{
struct vo_cocoa_state *s = talloc_zero(NULL, struct vo_cocoa_state);
@@ -336,13 +355,10 @@ void vo_cocoa_init(struct vo *vo)
.power_mgmt_assertion = kIOPMNullAssertionID,
.log = mp_log_new(s, vo->log, "cocoa"),
.embedded = vo->opts->WinID >= 0,
+ .cursor_visibility = true,
+ .cursor_visibility_wanted = true,
.fullscreen = 0,
};
- if (!s->embedded) {
- NSImage* blankImage = [[NSImage alloc] initWithSize:NSMakeSize(1, 1)];
- s->blankCursor = [[NSCursor alloc] initWithImage:blankImage hotSpot:NSZeroPoint];
- [blankImage release];
- }
pthread_mutex_init(&s->lock, NULL);
pthread_cond_init(&s->wakeup, NULL);
pthread_mutex_init(&s->sync_lock, NULL);
@@ -352,29 +368,33 @@ void vo_cocoa_init(struct vo *vo)
vo_cocoa_init_displaylink(vo);
cocoa_init_light_sensor(vo);
cocoa_add_screen_reconfiguration_observer(vo);
+ cocoa_add_event_monitor(vo);
+
if (!s->embedded) {
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
set_application_icon(NSApp);
}
}
-static int vo_cocoa_set_cursor_visibility(struct vo *vo, bool *visible)
+static int vo_cocoa_update_cursor_visibility(struct vo *vo, bool forceVisible)
{
struct vo_cocoa_state *s = vo->cocoa;
if (s->embedded)
return VO_NOTIMPL;
- MpvEventsView *v = (MpvEventsView *) s->view;
+ if (s->view) {
+ MpvEventsView *v = (MpvEventsView *) s->view;
+ bool visibility = !(!s->cursor_visibility_wanted && [v canHideCursor]);
- if (*visible) {
- [[NSCursor arrowCursor] set];
- } else if ([v canHideCursor] && s->blankCursor) {
- [s->blankCursor set];
- } else {
- *visible = true;
+ if ((forceVisible || visibility) && !s->cursor_visibility) {
+ [NSCursor unhide];
+ s->cursor_visibility = YES;
+ } else if (!visibility && s->cursor_visibility) {
+ [NSCursor hide];
+ s->cursor_visibility = NO;
+ }
}
-
return VO_TRUE;
}
@@ -392,6 +412,7 @@ void vo_cocoa_uninit(struct vo *vo)
run_on_main_thread(vo, ^{
// if using --wid + libmpv there's no window to release
if (s->window) {
+ vo_cocoa_update_cursor_visibility(vo, true);
[s->window setDelegate:nil];
[s->window close];
}
@@ -400,11 +421,10 @@ void vo_cocoa_uninit(struct vo *vo)
run_on_main_thread(vo, ^{
enable_power_management(s);
vo_cocoa_uninit_displaylink(s);
- pthread_mutex_lock(&s->sync_lock);
- pthread_cond_signal(&s->sync_wakeup);
- pthread_mutex_unlock(&s->sync_lock);
+ vo_cocoa_signal_swap(s);
cocoa_uninit_light_sensor(s);
cocoa_rm_screen_reconfiguration_observer(vo);
+ cocoa_rm_event_monitor(vo);
[s->nsgl_ctx release];
CGLReleaseContext(s->cgl_ctx);
@@ -416,9 +436,6 @@ void vo_cocoa_uninit(struct vo *vo)
[s->view removeFromSuperview];
[s->view release];
- if (!s->embedded)
- [s->blankCursor release];
-
pthread_cond_destroy(&s->sync_wakeup);
pthread_mutex_destroy(&s->sync_lock);
pthread_cond_destroy(&s->wakeup);
@@ -467,21 +484,25 @@ static CVReturn displayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt
struct vo *vo = displayLinkContext;
struct vo_cocoa_state *s = vo->cocoa;
- pthread_mutex_lock(&s->sync_lock);
- s->sync_counter += 1;
- pthread_cond_signal(&s->sync_wakeup);
- pthread_mutex_unlock(&s->sync_lock);
+ vo_cocoa_signal_swap(s);
return kCVReturnSuccess;
}
-static void vo_set_level(struct vo *vo, int ontop)
+static void vo_set_level(struct vo *vo, int ontop, int ontop_level)
{
struct vo_cocoa_state *s = vo->cocoa;
if (ontop) {
- // +1 is not enough as that will show the icon layer on top of the
- // menubar when the application is not frontmost. so use +2
- s->window_level = NSMainMenuWindowLevel + 2;
+ switch (ontop_level) {
+ case -1:
+ s->window_level = NSFloatingWindowLevel;
+ break;
+ case -2:
+ s->window_level = NSStatusWindowLevel;
+ break;
+ default:
+ s->window_level = ontop_level;
+ }
} else {
s->window_level = NSNormalWindowLevel;
}
@@ -499,7 +520,7 @@ static int vo_cocoa_ontop(struct vo *vo)
return VO_NOTIMPL;
struct mp_vo_opts *opts = vo->opts;
- vo_set_level(vo, opts->ontop);
+ vo_set_level(vo, opts->ontop, opts->ontop_level);
return VO_TRUE;
}
@@ -538,8 +559,8 @@ static void create_ui(struct vo *vo, struct mp_rect *win, int geo_flags)
if (s->embedded) {
parent = (NSView *) (intptr_t) opts->WinID;
} else {
- NSRect wr = calculate_window_geometry(vo,
- NSMakeRect(win->x0, win->y0, win->x1 - win->x0, win->y1 - win->y0));
+ NSRect wr = NSMakeRect(win->x0, win->y1, win->x1 - win->x0, win->y0 - win->y1);
+ wr = [s->current_screen convertRectFromBacking:wr];
s->window = create_window(wr, s->current_screen, opts->border, adapter);
parent = [s->window contentView];
}
@@ -548,8 +569,6 @@ static void create_ui(struct vo *vo, struct mp_rect *win, int geo_flags)
view.adapter = adapter;
s->view = view;
[parent addSubview:s->view];
- // update the cursor position now that the view has been added.
- [view signalMousePosition];
s->adapter = adapter;
cocoa_register_menu_item_action(MPM_H_SIZE, @selector(halfSize));
@@ -572,6 +591,8 @@ static void create_ui(struct vo *vo, struct mp_rect *win, int geo_flags)
[s->window setRestorable:NO];
[s->window makeMainWindow];
[s->window makeKeyAndOrderFront:nil];
+ if (!opts->fullscreen)
+ [s->window setMovableByWindowBackground:YES];
[NSApp activateIgnoringOtherApps:YES];
}
}
@@ -618,11 +639,7 @@ static void cocoa_screen_reconfiguration_observer(
struct vo *vo = ctx;
struct vo_cocoa_state *s = vo->cocoa;
- NSDictionary* sinfo = [s->current_screen deviceDescription];
- NSNumber* sid = [sinfo objectForKey:@"NSScreenNumber"];
- CGDirectDisplayID did = [sid longValue];
-
- if (did == display) {
+ if (s->display_id == display) {
MP_VERBOSE(vo, "detected display mode change, updating screen refresh rate\n");
flag_events(vo, VO_EVENT_WIN_STATE);
}
@@ -657,11 +674,16 @@ int vo_cocoa_config_window(struct vo *vo)
run_on_main_thread(vo, ^{
NSRect r = [s->current_screen frame];
+ r = [s->current_screen convertRectToBacking:r];
struct mp_rect screenrc = {0, 0, r.size.width, r.size.height};
struct vo_win_geometry geo;
- vo_calc_window_geometry(vo, &screenrc, &geo);
+ vo_calc_window_geometry2(vo, &screenrc, [s->current_screen backingScaleFactor], &geo);
vo_apply_window_geometry(vo, &geo);
+ //flip y coordinates
+ geo.win.y1 = r.size.height - geo.win.y1;
+ geo.win.y0 = r.size.height - geo.win.y0;
+
uint32_t width = vo->dwidth;
uint32_t height = vo->dheight;
@@ -673,13 +695,15 @@ int vo_cocoa_config_window(struct vo *vo)
create_ui(vo, &geo.win, geo.flags);
}
+ s->unfs_window = NSMakeRect(0, 0, width, height);
+
if (!s->embedded && s->window) {
if (reset_size)
queue_new_video_size(vo, width, height);
if (opts->fullscreen && !s->fullscreen)
vo_cocoa_fullscreen(vo);
cocoa_set_window_title(vo);
- vo_set_level(vo, opts->ontop);
+ vo_set_level(vo, opts->ontop, opts->ontop_level);
GLint o;
if (!CGLGetParameter(s->cgl_ctx, kCGLCPSurfaceOpacity, &o) && !o) {
@@ -753,7 +777,7 @@ void vo_cocoa_swap_buffers(struct vo *vo)
pthread_mutex_lock(&s->sync_lock);
uint64_t old_counter = s->sync_counter;
- while(old_counter == s->sync_counter) {
+ while(CVDisplayLinkIsRunning(s->link) && old_counter == s->sync_counter) {
pthread_cond_wait(&s->sync_wakeup, &s->sync_lock);
}
pthread_mutex_unlock(&s->sync_lock);
@@ -788,10 +812,10 @@ static int vo_cocoa_fullscreen(struct vo *vo)
if (s->embedded)
return VO_NOTIMPL;
+ if (!s->fullscreen)
+ s->unfs_window = [s->view frame];
+
[s->window toggleFullScreen:nil];
- // for whatever reason sometimes cocoa doesn't create an up event on
- // the fullscreen input key
- cocoa_put_key(MP_INPUT_RELEASE_ALL);
return VO_TRUE;
}
@@ -823,17 +847,20 @@ static int vo_cocoa_control_on_main_thread(struct vo *vo, int request, void *arg
return vo_cocoa_window_border(vo);
case VOCTRL_GET_UNFS_WINDOW_SIZE: {
int *sz = arg;
- NSSize size = [s->view frame].size;
- sz[0] = size.width;
- sz[1] = size.height;
+ NSRect rect = (s->fullscreen || vo->opts->fullscreen) ?
+ s->unfs_window : [s->view frame];
+ if(!vo->opts->hidpi_window_scale)
+ rect = [s->current_screen convertRectToBacking:rect];
+ sz[0] = rect.size.width;
+ sz[1] = rect.size.height;
return VO_TRUE;
}
case VOCTRL_SET_UNFS_WINDOW_SIZE: {
int *sz = arg;
- int w, h;
- w = sz[0];
- h = sz[1];
- queue_new_video_size(vo, w, h);
+ NSRect r = NSMakeRect(0, 0, sz[0], sz[1]);
+ if(vo->opts->hidpi_window_scale)
+ r = [s->current_screen convertRectToBacking:r];
+ queue_new_video_size(vo, r.size.width, r.size.height);
return VO_TRUE;
}
case VOCTRL_GET_WIN_STATE: {
@@ -842,7 +869,8 @@ static int vo_cocoa_control_on_main_thread(struct vo *vo, int request, void *arg
return VO_TRUE;
}
case VOCTRL_SET_CURSOR_VISIBILITY:
- return vo_cocoa_set_cursor_visibility(vo, arg);
+ s->cursor_visibility_wanted = *(bool *)arg;
+ return vo_cocoa_update_cursor_visibility(vo, false);
case VOCTRL_UPDATE_WINDOW_TITLE: {
talloc_free(s->window_title);
s->window_title = talloc_strdup(s, (char *) arg);
@@ -928,13 +956,9 @@ int vo_cocoa_control(struct vo *vo, int *events, int request, void *arg)
- (void)signalMouseMovement:(NSPoint)point
{
- mp_input_set_mouse_pos(self.vout->input_ctx, point.x, point.y);
[self recalcMovableByWindowBackground:point];
-}
-
-- (void)putKeyEvent:(NSEvent*)event
-{
- cocoa_put_key_event(event);
+ if (!self.vout->cocoa->window_is_dragged)
+ mp_input_set_mouse_pos(self.vout->input_ctx, point.x, point.y);
}
- (void)putKey:(int)mpkey withModifiers:(int)modifiers
@@ -977,6 +1001,10 @@ int vo_cocoa_control(struct vo *vo, int *events, int request, void *arg)
- (void)windowDidChangeScreen:(NSNotification *)notification
{
vo_cocoa_update_screen_info(self.vout);
+}
+
+- (void)windowDidChangePhysicalScreen
+{
vo_cocoa_update_displaylink(self.vout);
flag_events(self.vout, VO_EVENT_WIN_STATE);
}
@@ -999,11 +1027,13 @@ int vo_cocoa_control(struct vo *vo, int *events, int request, void *arg)
{
// Make vo.c not do video timing, which would slow down resizing.
vo_event(self.vout, VO_EVENT_LIVE_RESIZING);
+ vo_cocoa_stop_displaylink(self.vout->cocoa);
}
- (void)windowDidEndLiveResize:(NSNotification *)notification
{
vo_query_and_reset_events(self.vout, VO_EVENT_LIVE_RESIZING);
+ vo_cocoa_start_displaylink(self.vout->cocoa);
}
- (void)didChangeWindowedScreenProfile:(NSNotification *)notification
@@ -1012,20 +1042,14 @@ int vo_cocoa_control(struct vo *vo, int *events, int request, void *arg)
flag_events(self.vout, VO_EVENT_ICC_PROFILE_CHANGED);
}
-- (void)didChangeMousePosition
-{
- struct vo_cocoa_state *s = self.vout->cocoa;
- [(MpvEventsView *)s->view signalMousePosition];
-}
-
- (void)windowDidResignKey:(NSNotification *)notification
{
- [self didChangeMousePosition];
+ vo_cocoa_update_cursor_visibility(self.vout, true);
}
- (void)windowDidBecomeKey:(NSNotification *)notification
{
- [self didChangeMousePosition];
+ vo_cocoa_update_cursor_visibility(self.vout, false);
}
- (void)windowDidMiniaturize:(NSNotification *)notification
@@ -1038,4 +1062,9 @@ int vo_cocoa_control(struct vo *vo, int *events, int request, void *arg)
flag_events(self.vout, VO_EVENT_WIN_STATE);
}
+- (void)windowWillMove:(NSNotification *)notification
+{
+ self.vout->cocoa->window_is_dragged = true;
+}
+
@end
diff --git a/video/out/filter_kernels.c b/video/out/filter_kernels.c
index c5a1229..11680a0 100644
--- a/video/out/filter_kernels.c
+++ b/video/out/filter_kernels.c
@@ -28,6 +28,7 @@
#include <assert.h>
#include "filter_kernels.h"
+#include "common/common.h"
// NOTE: all filters are designed for discrete convolution
@@ -60,19 +61,20 @@ bool mp_init_filter(struct filter_kernel *filter, const int *sizes,
{
assert(filter->f.radius > 0);
// Only downscaling requires widening the filter
- filter->inv_scale = inv_scale >= 1.0 ? inv_scale : 1.0;
- filter->f.radius *= filter->inv_scale;
+ filter->filter_scale = MPMAX(1.0, inv_scale);
+ double src_radius = filter->f.radius * filter->filter_scale;
// Polar filters are dependent solely on the radius
if (filter->polar) {
- filter->size = 1;
+ filter->size = 1; // Not meaningful for EWA/polar scalers.
// Safety precaution to avoid generating a gigantic shader
- if (filter->f.radius > 16.0) {
- filter->f.radius = 16.0;
+ if (src_radius > 16.0) {
+ src_radius = 16.0;
+ filter->filter_scale = src_radius / filter->f.radius;
return false;
}
return true;
}
- int size = ceil(2.0 * filter->f.radius);
+ int size = ceil(2.0 * src_radius);
// round up to smallest available size that's still large enough
if (size < sizes[0])
size = sizes[0];
@@ -87,7 +89,7 @@ bool mp_init_filter(struct filter_kernel *filter, const int *sizes,
// largest filter available. This is incorrect, but better than refusing
// to do anything.
filter->size = cursize[-1];
- filter->inv_scale *= (filter->size/2.0) / filter->f.radius;
+ filter->filter_scale = (filter->size/2.0) / filter->f.radius;
return false;
}
}
@@ -100,14 +102,14 @@ static double sample_window(struct filter_window *kernel, double x)
// All windows are symmetric, this makes life easier
x = fabs(x);
- if (x >= kernel->radius)
- return 0.0;
// Stretch and taper the window size as needed
x = kernel->blur > 0.0 ? x / kernel->blur : x;
x = x <= kernel->taper ? 0.0 : (x - kernel->taper) / (1 - kernel->taper);
- return kernel->weight(kernel, x);
+ if (x < kernel->radius)
+ return kernel->weight(kernel, x);
+ return 0.0;
}
// Evaluate a filter's kernel and window at a given absolute position
@@ -115,7 +117,7 @@ static double sample_filter(struct filter_kernel *filter, double x)
{
// The window is always stretched to the entire kernel
double w = sample_window(&filter->w, x / filter->f.radius * filter->w.radius);
- double k = sample_window(&filter->f, x / filter->inv_scale);
+ double k = sample_window(&filter->f, x);
return filter->clamp ? fmax(0.0, fmin(1.0, w * k)) : w * k;
}
@@ -130,7 +132,7 @@ static void mp_compute_weights(struct filter_kernel *filter, double f,
double sum = 0;
for (int n = 0; n < filter->size; n++) {
double x = f - (n - filter->size / 2 + 1);
- double w = sample_filter(filter, x);
+ double w = sample_filter(filter, x / filter->filter_scale);
out_w[n] = w;
sum += w;
}
diff --git a/video/out/filter_kernels.h b/video/out/filter_kernels.h
index fc90a1c..74cc3eb 100644
--- a/video/out/filter_kernels.h
+++ b/video/out/filter_kernels.h
@@ -34,7 +34,10 @@ struct filter_kernel {
bool polar; // whether or not the filter uses polar coordinates
// The following values are set by mp_init_filter() at runtime.
int size; // number of coefficients (may depend on radius)
- double inv_scale; // scale factor (<1.0 is upscale, >1.0 downscale)
+ double filter_scale; // Factor to convert the mathematical filter
+ // function radius to the possibly wider
+ // (in the case of downsampling) filter sample
+ // radius.
};
extern const struct filter_window mp_filter_windows[];
diff --git a/video/out/opengl/common.c b/video/out/opengl/common.c
index 5762f44..2712149 100644
--- a/video/out/opengl/common.c
+++ b/video/out/opengl/common.c
@@ -316,6 +316,16 @@ static const struct gl_functions gl_functions[] = {
{0}
},
},
+ {
+ .ver_core = 410,
+ .ver_es_core = 300,
+ .extension = "GL_ARB_get_program_binary",
+ .functions = (const struct gl_function[]) {
+ DEF_FN(GetProgramBinary),
+ DEF_FN(ProgramBinary),
+ {0}
+ },
+ },
// Swap control, always an OS specific extension
// The OSX code loads this manually.
{
diff --git a/video/out/opengl/common.h b/video/out/opengl/common.h
index afb5b61..3eb2a8e 100644
--- a/video/out/opengl/common.h
+++ b/video/out/opengl/common.h
@@ -31,23 +31,11 @@
#include "video/mp_image.h"
-#if HAVE_GL_COCOA
-#define GL_DO_NOT_WARN_IF_MULTI_GL_VERSION_HEADERS_INCLUDED 1
-#include <OpenGL/gl.h>
-#include <OpenGL/gl3.h>
-#include <OpenGL/glext.h>
-#elif HAVE_IOS_GL
-#include <OpenGLES/ES2/glext.h>
-#include <OpenGLES/ES3/glext.h>
-#elif HAVE_ANDROID_GL
-#include <GLES3/gl3.h>
-#else
-#include <GL/gl.h>
-#include <GL/glext.h>
-#endif
+#include "gl_headers.h"
-#define MP_GET_GL_WORKAROUNDS
-#include "header_fixes.h"
+#if HAVE_GL_WIN32
+#include <windows.h>
+#endif
struct GL;
typedef struct GL GL;
@@ -94,7 +82,6 @@ struct GL {
char *extensions; // Equivalent to GL_EXTENSIONS
int mpgl_caps; // Bitfield of MPGL_CAP_* constants
bool debug_context; // use of e.g. GLX_CONTEXT_DEBUG_BIT_ARB
- GLuint main_fb; // framebuffer to render to (normally 0)
void (GLAPIENTRY *Viewport)(GLint, GLint, GLsizei, GLsizei);
void (GLAPIENTRY *Clear)(GLbitfield);
@@ -163,6 +150,10 @@ struct GL {
void (GLAPIENTRY *GetShaderiv)(GLuint, GLenum, GLint *);
void (GLAPIENTRY *GetProgramInfoLog)(GLuint, GLsizei, GLsizei *, GLchar *);
void (GLAPIENTRY *GetProgramiv)(GLenum, GLenum, GLint *);
+ void (GLAPIENTRY *GetProgramBinary)(GLuint, GLsizei, GLsizei *, GLenum *,
+ void *);
+ void (GLAPIENTRY *ProgramBinary)(GLuint, GLenum, const void *, GLsizei);
+
const GLubyte* (GLAPIENTRY *GetStringi)(GLenum, GLuint);
void (GLAPIENTRY *BindAttribLocation)(GLuint, GLuint, const GLchar *);
void (GLAPIENTRY *BindFramebuffer)(GLenum, GLuint);
diff --git a/video/out/opengl/context.c b/video/out/opengl/context.c
index f7fce4f..117d7f8 100644
--- a/video/out/opengl/context.c
+++ b/video/out/opengl/context.c
@@ -47,6 +47,7 @@ extern const struct mpgl_driver mpgl_driver_angle_es2;
extern const struct mpgl_driver mpgl_driver_dxinterop;
extern const struct mpgl_driver mpgl_driver_rpi;
extern const struct mpgl_driver mpgl_driver_mali;
+extern const struct mpgl_driver mpgl_driver_vdpauglx;
static const struct mpgl_driver *const backends[] = {
#if HAVE_RPI
@@ -64,9 +65,6 @@ static const struct mpgl_driver *const backends[] = {
#if HAVE_GL_DXINTEROP
&mpgl_driver_dxinterop,
#endif
-#if HAVE_GL_WAYLAND
- &mpgl_driver_wayland,
-#endif
#if HAVE_GL_X11
&mpgl_driver_x11_probe,
#endif
@@ -76,6 +74,9 @@ static const struct mpgl_driver *const backends[] = {
#if HAVE_GL_X11
&mpgl_driver_x11,
#endif
+#if HAVE_GL_WAYLAND
+ &mpgl_driver_wayland,
+#endif
#if HAVE_EGL_DRM
&mpgl_driver_drm,
&mpgl_driver_drm_egl,
@@ -83,6 +84,9 @@ static const struct mpgl_driver *const backends[] = {
#if HAVE_MALI_FBDEV
&mpgl_driver_mali,
#endif
+#if HAVE_VDPAU_GL_X11
+ &mpgl_driver_vdpauglx,
+#endif
};
// 0-terminated list of desktop GL versions a backend should try to
@@ -160,6 +164,7 @@ static MPGLContext *init_backend(struct vo *vo, const struct mpgl_driver *driver
.vo = vo,
.global = vo->global,
.driver = driver,
+ .log = vo->log,
};
if (probing)
vo_flags |= VOFLAG_PROBING;
@@ -235,6 +240,12 @@ int mpgl_control(struct MPGLContext *ctx, int *events, int request, void *arg)
return ctx->driver->control(ctx, events, request, arg);
}
+void mpgl_start_frame(struct MPGLContext *ctx)
+{
+ if (ctx->driver->start_frame)
+ ctx->driver->start_frame(ctx);
+}
+
void mpgl_swap_buffers(struct MPGLContext *ctx)
{
ctx->driver->swap_buffers(ctx);
diff --git a/video/out/opengl/context.h b/video/out/opengl/context.h
index 0a02bd2..229c5ef 100644
--- a/video/out/opengl/context.h
+++ b/video/out/opengl/context.h
@@ -27,12 +27,13 @@
#include "common.h"
enum {
- VOFLAG_GLES = 1 << 0, // Hint to create a GLES2 context
+ VOFLAG_GLES = 1 << 0, // Hint to create a GLES context
VOFLAG_NO_GLES = 1 << 1, // Hint to create a desktop GL context
VOFLAG_GL_DEBUG = 1 << 2, // Hint to request debug OpenGL context
VOFLAG_ALPHA = 1 << 3, // Hint to request alpha framebuffer
VOFLAG_SW = 1 << 4, // Hint to accept a software GL renderer
VOFLAG_PROBING = 1 << 6, // The backend is being auto-probed.
+ VOFLAG_GLES2 = 1 << 7, // Hint for GLESv2 (needs VOFLAG_GLES)
};
extern const int mpgl_preferred_gl_versions[];
@@ -58,6 +59,11 @@ struct mpgl_driver {
// Return 0 on success, negative value (-1) on error.
int (*reconfig)(struct MPGLContext *ctx);
+ // Called when rendering starts. The backend can map or resize the
+ // framebuffer, or update GL.main_fb. swap_buffers() ends the frame.
+ // Optional.
+ void (*start_frame)(struct MPGLContext *ctx);
+
// Present the frame.
void (*swap_buffers)(struct MPGLContext *ctx);
@@ -78,6 +84,7 @@ typedef struct MPGLContext {
struct vo *vo;
const struct mpgl_driver *driver;
struct mpv_global *global;
+ struct mp_log *log;
// For hwdec_vaegl.c.
const char *native_display_type;
@@ -86,6 +93,9 @@ typedef struct MPGLContext {
// Flip the rendered image vertically. This is useful for dxinterop.
bool flip_v;
+ // framebuffer to render to (normally 0)
+ GLuint main_fb;
+
// For free use by the mpgl_driver.
void *priv;
} MPGLContext;
@@ -94,6 +104,7 @@ MPGLContext *mpgl_init(struct vo *vo, const char *backend_name, int vo_flags);
void mpgl_uninit(MPGLContext *ctx);
int mpgl_reconfig_window(struct MPGLContext *ctx);
int mpgl_control(struct MPGLContext *ctx, int *events, int request, void *arg);
+void mpgl_start_frame(struct MPGLContext *ctx);
void mpgl_swap_buffers(struct MPGLContext *ctx);
int mpgl_find_backend(const char *name);
diff --git a/video/out/opengl/context_angle.c b/video/out/opengl/context_angle.c
index 7a011a8..3e227f9 100644
--- a/video/out/opengl/context_angle.c
+++ b/video/out/opengl/context_angle.c
@@ -56,6 +56,7 @@ struct angle_opts {
int egl_windowing;
int swapchain_length; // Currently only works with DXGI 1.2+
int max_frame_latency;
+ int flip;
};
#define OPT_BASE_STRUCT struct angle_opts
@@ -80,6 +81,7 @@ const struct m_sub_options angle_conf = {
{"yes", 1})),
OPT_INTRANGE("angle-swapchain-length", swapchain_length, 0, 2, 16),
OPT_INTRANGE("angle-max-frame-latency", max_frame_latency, 0, 1, 16),
+ OPT_FLAG("angle-flip", flip, 0),
{0}
},
.defaults = &(const struct angle_opts) {
@@ -89,6 +91,7 @@ const struct m_sub_options angle_conf = {
.egl_windowing = -1,
.swapchain_length = 6,
.max_frame_latency = 3,
+ .flip = 1,
},
.size = sizeof(struct angle_opts),
};
@@ -383,13 +386,28 @@ static bool d3d11_swapchain_create_1_2(MPGLContext *ctx, int flags)
.SampleDesc = { .Count = 1 },
.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT |
DXGI_USAGE_SHADER_INPUT,
- .BufferCount = p->opts->swapchain_length,
- .SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL,
};
+ if (p->opts->flip) {
+ desc1.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
+ desc1.BufferCount = p->opts->swapchain_length;
+ } else {
+ desc1.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
+ desc1.BufferCount = 1;
+ }
+
hr = IDXGIFactory2_CreateSwapChainForHwnd(p->dxgi_factory2,
(IUnknown*)p->d3d11_device, vo_w32_hwnd(vo), &desc1, NULL, NULL,
&p->dxgi_swapchain1);
+ if (FAILED(hr) && p->opts->flip) {
+ // Try again without DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL
+ desc1.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
+ desc1.BufferCount = 1;
+
+ hr = IDXGIFactory2_CreateSwapChainForHwnd(p->dxgi_factory2,
+ (IUnknown*)p->d3d11_device, vo_w32_hwnd(vo), &desc1, NULL, NULL,
+ &p->dxgi_swapchain1);
+ }
if (FAILED(hr)) {
MP_FATAL(vo, "Couldn't create DXGI 1.2+ swap chain: %s\n",
mp_HRESULT_to_str(hr));
@@ -605,11 +623,6 @@ static int GLAPIENTRY angle_swap_interval(int interval)
}
}
-static void *get_proc_address(const GLubyte *proc_name)
-{
- return eglGetProcAddress(proc_name);
-}
-
static int angle_init(struct MPGLContext *ctx, int flags)
{
struct priv *p = ctx->priv;
@@ -670,6 +683,14 @@ static int angle_init(struct MPGLContext *ctx, int flags)
if (surface_ok) {
if (p->dxgi_swapchain1) {
MP_VERBOSE(vo, "Using DXGI 1.2+\n");
+
+ DXGI_SWAP_CHAIN_DESC1 scd = {0};
+ IDXGISwapChain1_GetDesc1(p->dxgi_swapchain1, &scd);
+ if (scd.SwapEffect == DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL) {
+ MP_VERBOSE(vo, "Using flip-model presentation\n");
+ } else {
+ MP_VERBOSE(vo, "Using bitblt-model presentation\n");
+ }
} else {
MP_VERBOSE(vo, "Using DXGI 1.1\n");
}
@@ -683,7 +704,7 @@ static int angle_init(struct MPGLContext *ctx, int flags)
if (!surface_ok)
goto fail;
- mpgl_load_functions(ctx->gl, get_proc_address, NULL, vo->log);
+ mpegl_load_functions(ctx->gl, vo->log);
current_ctx = ctx;
ctx->gl->SwapInterval = angle_swap_interval;
diff --git a/video/out/opengl/context_cocoa.c b/video/out/opengl/context_cocoa.c
index 941274c..c61f9e2 100644
--- a/video/out/opengl/context_cocoa.c
+++ b/video/out/opengl/context_cocoa.c
@@ -17,13 +17,29 @@
#include <OpenGL/OpenGL.h>
#include <dlfcn.h>
+#include "options/m_config.h"
#include "video/out/cocoa_common.h"
#include "osdep/macosx_versions.h"
#include "context.h"
-struct cgl_context {
+struct cocoa_opts {
+ int cocoa_force_dedicated_gpu;
+};
+
+#define OPT_BASE_STRUCT struct cocoa_opts
+const struct m_sub_options cocoa_conf = {
+ .opts = (const struct m_option[]) {
+ OPT_FLAG("cocoa-force-dedicated-gpu", cocoa_force_dedicated_gpu, 0),
+ {0}
+ },
+ .size = sizeof(struct cocoa_opts),
+};
+
+struct priv {
CGLPixelFormatObj pix;
CGLContextObj ctx;
+
+ struct cocoa_opts *opts;
};
static int set_swap_interval(int enabled)
@@ -46,40 +62,37 @@ static void *cocoa_glgetaddr(const char *s)
return ret;
}
-static CGLError test_gl_version(struct vo *vo,
- CGLContextObj *ctx,
- CGLPixelFormatObj *pix,
- CGLOpenGLProfile version)
+static CGLError test_gl_version(struct MPGLContext *ctx, CGLOpenGLProfile ver)
{
+ struct priv *p = ctx->priv;
+
CGLPixelFormatAttribute attrs[] = {
kCGLPFAOpenGLProfile,
- (CGLPixelFormatAttribute) version,
+ (CGLPixelFormatAttribute) ver,
kCGLPFAAccelerated,
- #if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_8
// leave this as the last entry of the array to not break the fallback
// code
kCGLPFASupportsAutomaticGraphicsSwitching,
- #endif
0
};
GLint npix;
CGLError err;
- err = CGLChoosePixelFormat(attrs, pix, &npix);
- if (err == kCGLBadAttribute) {
+ err = CGLChoosePixelFormat(attrs, &p->pix, &npix);
+ if (p->opts->cocoa_force_dedicated_gpu || err == kCGLBadAttribute) {
// kCGLPFASupportsAutomaticGraphicsSwitching is probably not supported
// by the current hardware. Falling back to not using it.
attrs[MP_ARRAY_SIZE(attrs) - 2] = 0;
- err = CGLChoosePixelFormat(attrs, pix, &npix);
+ err = CGLChoosePixelFormat(attrs, &p->pix, &npix);
}
if (err != kCGLNoError) {
- MP_ERR(vo, "error creating CGL pixel format: %s (%d)\n",
+ MP_ERR(ctx->vo, "error creating CGL pixel format: %s (%d)\n",
CGLErrorString(err), err);
goto error_out;
}
- err = CGLCreateContext(*pix, 0, ctx);
+ err = CGLCreateContext(p->pix, 0, &p->ctx);
error_out:
return err;
@@ -87,7 +100,7 @@ error_out:
static bool create_gl_context(struct MPGLContext *ctx, int vo_flags)
{
- struct cgl_context *p = ctx->priv;
+ struct priv *p = ctx->priv;
CGLError err;
CGLOpenGLProfile gl_versions[] = {
@@ -96,7 +109,7 @@ static bool create_gl_context(struct MPGLContext *ctx, int vo_flags)
};
for (int n = 0; n < MP_ARRAY_SIZE(gl_versions); n++) {
- err = test_gl_version(ctx->vo, &p->ctx, &p->pix, gl_versions[n]);
+ err = test_gl_version(ctx, gl_versions[n]);
if (err == kCGLNoError)
break;
}
@@ -122,13 +135,15 @@ static bool create_gl_context(struct MPGLContext *ctx, int vo_flags)
static void cocoa_uninit(MPGLContext *ctx)
{
- struct cgl_context *p = ctx->priv;
+ struct priv *p = ctx->priv;
CGLReleaseContext(p->ctx);
vo_cocoa_uninit(ctx->vo);
}
static int cocoa_init(MPGLContext *ctx, int vo_flags)
{
+ struct priv *p = ctx->priv;
+ p->opts = mp_get_config_group(ctx, ctx->global, &cocoa_conf);
vo_cocoa_init(ctx->vo);
if (!create_gl_context(ctx, vo_flags))
@@ -158,7 +173,7 @@ static void cocoa_swap_buffers(struct MPGLContext *ctx)
const struct mpgl_driver mpgl_driver_cocoa = {
.name = "cocoa",
- .priv_size = sizeof(struct cgl_context),
+ .priv_size = sizeof(struct priv),
.init = cocoa_init,
.reconfig = cocoa_reconfig,
.swap_buffers = cocoa_swap_buffers,
diff --git a/video/out/opengl/context_drm_egl.c b/video/out/opengl/context_drm_egl.c
index cf23423..1852cb7 100644
--- a/video/out/opengl/context_drm_egl.c
+++ b/video/out/opengl/context_drm_egl.c
@@ -307,9 +307,7 @@ static int drm_egl_init(struct MPGLContext *ctx, int flags)
return -1;
}
- const char *egl_exts = eglQueryString(p->egl.display, EGL_EXTENSIONS);
- void *(*gpa)(const GLubyte*) = (void *(*)(const GLubyte*))eglGetProcAddress;
- mpgl_load_functions(ctx->gl, gpa, egl_exts, ctx->vo->log);
+ mpegl_load_functions(ctx->gl, ctx->vo->log);
ctx->native_display_type = "drm";
ctx->native_display = (void *)(intptr_t)p->kms->fd;
diff --git a/video/out/opengl/context_dxinterop.c b/video/out/opengl/context_dxinterop.c
index e98b0d5..a23318a 100644
--- a/video/out/opengl/context_dxinterop.c
+++ b/video/out/opengl/context_dxinterop.c
@@ -294,7 +294,7 @@ static int d3d_size_dependent_create(MPGLContext *ctx)
return -1;
}
- gl->BindFramebuffer(GL_FRAMEBUFFER, gl->main_fb);
+ gl->BindFramebuffer(GL_FRAMEBUFFER, ctx->main_fb);
gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, p->texture, 0);
gl->BindFramebuffer(GL_FRAMEBUFFER, 0);
@@ -506,7 +506,7 @@ static int dxinterop_init(struct MPGLContext *ctx, int flags)
goto fail;
// Create the shared framebuffer
- gl->GenFramebuffers(1, &gl->main_fb);
+ gl->GenFramebuffers(1, &ctx->main_fb);
current_ctx = ctx;
gl->SwapInterval = dxinterop_swap_interval;
diff --git a/video/out/opengl/context_mali_fbdev.c b/video/out/opengl/context_mali_fbdev.c
index e76d220..66daa7f 100644
--- a/video/out/opengl/context_mali_fbdev.c
+++ b/video/out/opengl/context_mali_fbdev.c
@@ -49,16 +49,6 @@ static bool get_fbdev_size(int *w, int *h)
return ok;
}
-static void *get_proc_address(const GLubyte *name)
-{
- void *p = eglGetProcAddress(name);
- // EGL 1.4 (supported by the MALI drivers) does not necessarily return
- // function pointers for core functions.
- if (!p)
- p = dlsym(RTLD_DEFAULT, name);
- return p;
-}
-
struct priv {
struct mp_log *log;
struct GL *gl;
@@ -127,8 +117,7 @@ static int mali_init(struct MPGLContext *ctx, int flags)
ctx->gl = talloc_zero(ctx, GL);
- const char *exts = eglQueryString(p->egl_display, EGL_EXTENSIONS);
- mpgl_load_functions(ctx->gl, get_proc_address, exts, p->log);
+ mpegl_load_functions(ctx->gl, p->log);
return 0;
diff --git a/video/out/opengl/context_rpi.c b/video/out/opengl/context_rpi.c
index fa19a6c..e79622b 100644
--- a/video/out/opengl/context_rpi.c
+++ b/video/out/opengl/context_rpi.c
@@ -208,16 +208,6 @@ fail:
return -1;
}
-static void *get_proc_address(const GLubyte *name)
-{
- void *p = eglGetProcAddress(name);
- // EGL 1.4 (supported by the RPI firmware) does not necessarily return
- // function pointers for core functions.
- if (!p)
- p = dlsym(RTLD_DEFAULT, name);
- return p;
-}
-
static int rpi_init(struct MPGLContext *ctx, int flags)
{
struct priv *p = ctx->priv;
@@ -242,8 +232,7 @@ static int rpi_init(struct MPGLContext *ctx, int flags)
ctx->gl = talloc_zero(ctx, GL);
- const char *exts = eglQueryString(p->egl_display, EGL_EXTENSIONS);
- mpgl_load_functions(ctx->gl, get_proc_address, exts, p->log);
+ mpegl_load_functions(ctx->gl, p->log);
ctx->native_display_type = "MPV_RPI_WINDOW";
ctx->native_display = p->win_params;
diff --git a/video/out/opengl/context_vdpau.c b/video/out/opengl/context_vdpau.c
new file mode 100644
index 0000000..40d21ab
--- /dev/null
+++ b/video/out/opengl/context_vdpau.c
@@ -0,0 +1,389 @@
+/*
+ * This file is part of mpv.
+ *
+ * mpv is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * mpv is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <X11/Xlib.h>
+#include <GL/glx.h>
+
+#include "video/vdpau.h"
+#include "video/out/x11_common.h"
+#include "context.h"
+
+// This is a GL_NV_vdpau_interop specification bug, and headers (unfortunately)
+// follow it. I'm not sure about the original nvidia headers.
+#define BRAINDEATH(x) ((void *)(uintptr_t)(x))
+
+#define NUM_SURFACES 4
+
+struct surface {
+ int w, h;
+ VdpOutputSurface surface;
+ // This nested shitshow of handles to the same object piss me off.
+ GLvdpauSurfaceNV registered;
+ bool mapped;
+ GLuint texture;
+ GLuint fbo;
+};
+
+struct priv {
+ GLXContext context;
+ struct mp_vdpau_ctx *vdp;
+ VdpPresentationQueueTarget vdp_target;
+ VdpPresentationQueue vdp_queue;
+ int num_surfaces;
+ struct surface surfaces[NUM_SURFACES];
+ int current_surface;
+};
+
+typedef GLXContext (*glXCreateContextAttribsARBProc)
+ (Display*, GLXFBConfig, GLXContext, Bool, const int*);
+
+static bool create_context_x11(struct MPGLContext *ctx, int vo_flags)
+{
+ struct priv *glx_ctx = ctx->priv;
+ struct vo *vo = ctx->vo;
+
+ int glx_major, glx_minor;
+ if (!glXQueryVersion(vo->x11->display, &glx_major, &glx_minor)) {
+ MP_ERR(vo, "GLX not found.\n");
+ return false;
+ }
+
+ int glx_attribs[] = {
+ GLX_X_RENDERABLE, True,
+ GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR,
+ GLX_RED_SIZE, 1,
+ GLX_GREEN_SIZE, 1,
+ GLX_BLUE_SIZE, 1,
+ GLX_DOUBLEBUFFER, True,
+ None
+ };
+ int fbcount;
+ GLXFBConfig *fbcs = glXChooseFBConfig(vo->x11->display, vo->x11->screen,
+ glx_attribs, &fbcount);
+ if (!fbcs)
+ return false;
+ // The list in fbc is sorted (so that the first element is the best).
+ GLXFBConfig fbc = fbcount > 0 ? fbcs[0] : NULL;
+ XFree(fbcs);
+ if (!fbc) {
+ MP_ERR(vo, "no GLX support present\n");
+ return false;
+ }
+
+ glXCreateContextAttribsARBProc glXCreateContextAttribsARB =
+ (glXCreateContextAttribsARBProc)
+ glXGetProcAddressARB((const GLubyte *)"glXCreateContextAttribsARB");
+
+ const char *glxstr =
+ glXQueryExtensionsString(vo->x11->display, vo->x11->screen);
+ bool have_ctx_ext = glxstr && !!strstr(glxstr, "GLX_ARB_create_context");
+
+ if (!(have_ctx_ext && glXCreateContextAttribsARB)) {
+ return false;
+ }
+
+ int ctx_flags = vo_flags & VOFLAG_GL_DEBUG ? GLX_CONTEXT_DEBUG_BIT_ARB : 0;
+ int context_attribs[] = {
+ GLX_CONTEXT_MAJOR_VERSION_ARB, 4,
+ GLX_CONTEXT_MINOR_VERSION_ARB, 0,
+ GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
+ GLX_CONTEXT_FLAGS_ARB, ctx_flags,
+ None
+ };
+ GLXContext context = glXCreateContextAttribsARB(vo->x11->display, fbc, 0,
+ True, context_attribs);
+ if (!context)
+ return false;
+
+ // Pass 0 as drawable for offscreen use. This is probably (?) not valid in
+ // standard GLX, but the nVidia drivers accept it.
+ if (!glXMakeCurrent(vo->x11->display, 0, context)) {
+ MP_FATAL(vo, "Could not set GLX context!\n");
+ glXDestroyContext(vo->x11->display, context);
+ return false;
+ }
+
+ glx_ctx->context = context;
+ mpgl_load_functions(ctx->gl, (void *)glXGetProcAddressARB, glxstr, vo->log);
+ return true;
+}
+
+static int create_vdpau_objects(struct MPGLContext *ctx)
+{
+ struct priv *p = ctx->priv;
+ VdpDevice dev = p->vdp->vdp_device;
+ struct vdp_functions *vdp = &p->vdp->vdp;
+ VdpStatus vdp_st;
+
+ ctx->gl->VDPAUInitNV(BRAINDEATH(dev), p->vdp->get_proc_address);
+
+ vdp_st = vdp->presentation_queue_target_create_x11(dev, ctx->vo->x11->window,
+ &p->vdp_target);
+ CHECK_VDP_ERROR(ctx, "creating vdp target");
+
+ vdp_st = vdp->presentation_queue_create(dev, p->vdp_target, &p->vdp_queue);
+ CHECK_VDP_ERROR(ctx, "creating vdp presentation queue");
+
+ return 0;
+}
+
+static void destroy_vdpau_surface(struct MPGLContext *ctx,
+ struct surface *surface)
+{
+ struct priv *p = ctx->priv;
+ struct vdp_functions *vdp = &p->vdp->vdp;
+ VdpStatus vdp_st;
+ GL *gl = ctx->gl;
+
+ if (surface->mapped)
+ gl->VDPAUUnmapSurfacesNV(1, &surface->registered);
+
+ gl->DeleteFramebuffers(1, &surface->fbo);
+ gl->DeleteTextures(1, &surface->texture);
+
+ if (surface->registered)
+ gl->VDPAUUnregisterSurfaceNV(surface->registered);
+
+ if (surface->surface != VDP_INVALID_HANDLE) {
+ vdp_st = vdp->output_surface_destroy(surface->surface);
+ CHECK_VDP_WARNING(ctx, "destroying vdpau surface");
+ }
+
+ *surface = (struct surface){
+ .surface = VDP_INVALID_HANDLE,
+ };
+}
+
+static int recreate_vdpau_surface(struct MPGLContext *ctx,
+ struct surface *surface)
+{
+ struct priv *p = ctx->priv;
+ VdpDevice dev = p->vdp->vdp_device;
+ struct vdp_functions *vdp = &p->vdp->vdp;
+ VdpStatus vdp_st;
+ GL *gl = ctx->gl;
+
+ destroy_vdpau_surface(ctx, surface);
+
+ surface->w = ctx->vo->dwidth;
+ surface->h = ctx->vo->dheight;
+
+ vdp_st = vdp->output_surface_create(dev, VDP_RGBA_FORMAT_B8G8R8A8,
+ surface->w, surface->h,
+ &surface->surface);
+ CHECK_VDP_ERROR_NORETURN(ctx, "creating vdp output surface");
+ if (vdp_st != VDP_STATUS_OK)
+ goto error;
+
+ gl->GenTextures(1, &surface->texture);
+
+ surface->registered =
+ gl->VDPAURegisterOutputSurfaceNV(BRAINDEATH(surface->surface),
+ GL_TEXTURE_2D,
+ 1, &surface->texture);
+ if (!surface->registered) {
+ MP_ERR(ctx, "could not register vdpau surface with GL\n");
+ goto error;
+ }
+
+ gl->VDPAUSurfaceAccessNV(surface->registered, GL_WRITE_DISCARD_NV);
+ gl->VDPAUMapSurfacesNV(1, &surface->registered);
+ surface->mapped = true;
+
+ gl->GenFramebuffers(1, &surface->fbo);
+ gl->BindFramebuffer(GL_FRAMEBUFFER, surface->fbo);
+ gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ GL_TEXTURE_2D, surface->texture, 0);
+ GLenum err = gl->CheckFramebufferStatus(GL_FRAMEBUFFER);
+ if (err != GL_FRAMEBUFFER_COMPLETE) {
+ MP_ERR(ctx, "Framebuffer completeness check failed (error=%d).\n",
+ (int)err);
+ goto error;
+ }
+ gl->BindFramebuffer(GL_FRAMEBUFFER, 0);
+
+ gl->VDPAUUnmapSurfacesNV(1, &surface->registered);
+ surface->mapped = false;
+
+ return 0;
+
+error:
+ destroy_vdpau_surface(ctx, surface);
+ return -1;
+}
+
+static void glx_uninit(MPGLContext *ctx)
+{
+ struct priv *p = ctx->priv;
+
+ if (p->vdp) {
+ struct vdp_functions *vdp = &p->vdp->vdp;
+ VdpStatus vdp_st;
+
+ for (int n = 0; n < p->num_surfaces; n++)
+ destroy_vdpau_surface(ctx, &p->surfaces[n]);
+
+ if (p->vdp_queue != VDP_INVALID_HANDLE) {
+ vdp_st = vdp->presentation_queue_destroy(p->vdp_queue);
+ CHECK_VDP_WARNING(ctx, "destroying presentation queue");
+ }
+
+ if (p->vdp_target != VDP_INVALID_HANDLE) {
+ vdp_st = vdp->presentation_queue_target_destroy(p->vdp_target);
+ CHECK_VDP_WARNING(ctx, "destroying presentation target");
+ }
+
+ mp_vdpau_destroy(p->vdp);
+ }
+
+ if (p->context) {
+ Display *display = ctx->vo->x11->display;
+ glXMakeCurrent(display, None, NULL);
+ glXDestroyContext(display, p->context);
+ }
+
+ vo_x11_uninit(ctx->vo);
+}
+
+static int glx_init(struct MPGLContext *ctx, int flags)
+{
+ struct vo *vo = ctx->vo;
+ struct priv *p = ctx->priv;
+
+ p->vdp_queue = VDP_INVALID_HANDLE;
+ p->vdp_target = VDP_INVALID_HANDLE;
+
+ if (ctx->vo->probing)
+ goto uninit;
+
+ if (!vo_x11_init(ctx->vo))
+ goto uninit;
+
+ p->vdp = mp_vdpau_create_device_x11(ctx->log, ctx->vo->x11->display, false);
+ if (!p->vdp)
+ goto uninit;
+
+ if (!vo_x11_create_vo_window(vo, NULL, "vdpauglx"))
+ goto uninit;
+
+ if (!create_context_x11(ctx, flags))
+ goto uninit;
+
+ if (!(ctx->gl->mpgl_caps & MPGL_CAP_VDPAU))
+ goto uninit;
+
+ if (create_vdpau_objects(ctx) < 0)
+ goto uninit;
+
+ p->num_surfaces = NUM_SURFACES;
+ for (int n = 0; n < p->num_surfaces; n++)
+ p->surfaces[n].surface = VDP_INVALID_HANDLE;
+
+ ctx->flip_v = true;
+
+ return 0;
+
+uninit:
+ glx_uninit(ctx);
+ return -1;
+}
+
+static int glx_reconfig(struct MPGLContext *ctx)
+{
+ vo_x11_config_vo_window(ctx->vo);
+ return 0;
+}
+
+static int glx_control(struct MPGLContext *ctx, int *events, int request,
+ void *arg)
+{
+ return vo_x11_control(ctx->vo, events, request, arg);
+}
+
+static void glx_start_frame(struct MPGLContext *ctx)
+{
+ struct priv *p = ctx->priv;
+ struct vdp_functions *vdp = &p->vdp->vdp;
+ VdpStatus vdp_st;
+ GL *gl = ctx->gl;
+
+ struct surface *surface = &p->surfaces[p->current_surface];
+
+ if (surface->surface != VDP_INVALID_HANDLE) {
+ VdpTime prev_vsync_time;
+ vdp_st = vdp->presentation_queue_block_until_surface_idle(p->vdp_queue,
+ surface->surface,
+ &prev_vsync_time);
+ CHECK_VDP_WARNING(ctx, "waiting for surface failed");
+ }
+
+ if (surface->w != ctx->vo->dwidth || surface->h != ctx->vo->dheight)
+ recreate_vdpau_surface(ctx, surface);
+
+
+ ctx->main_fb = surface->fbo; // 0 if creating the surface failed
+
+ if (surface->surface != VDP_INVALID_HANDLE) {
+ gl->VDPAUMapSurfacesNV(1, &surface->registered);
+ surface->mapped = true;
+ }
+}
+
+static void glx_swap_buffers(struct MPGLContext *ctx)
+{
+ struct priv *p = ctx->priv;
+ struct vdp_functions *vdp = &p->vdp->vdp;
+ VdpStatus vdp_st;
+ GL *gl = ctx->gl;
+
+ struct surface *surface = &p->surfaces[p->current_surface];
+ if (surface->surface == VDP_INVALID_HANDLE)
+ return; // surface alloc probably failed before
+
+ if (surface->mapped)
+ gl->VDPAUUnmapSurfacesNV(1, &surface->registered);
+ surface->mapped = false;
+
+ vdp_st = vdp->presentation_queue_display(p->vdp_queue, surface->surface,
+ 0, 0, 0);
+ CHECK_VDP_WARNING(ctx, "trying to present vdp surface");
+
+ p->current_surface = (p->current_surface + 1) % p->num_surfaces;
+}
+
+static void glx_wakeup(struct MPGLContext *ctx)
+{
+ vo_x11_wakeup(ctx->vo);
+}
+
+static void glx_wait_events(struct MPGLContext *ctx, int64_t until_time_us)
+{
+ vo_x11_wait_events(ctx->vo, until_time_us);
+}
+
+const struct mpgl_driver mpgl_driver_vdpauglx = {
+ .name = "vdpauglx",
+ .priv_size = sizeof(struct priv),
+ .init = glx_init,
+ .reconfig = glx_reconfig,
+ .start_frame = glx_start_frame,
+ .swap_buffers = glx_swap_buffers,
+ .control = glx_control,
+ .wakeup = glx_wakeup,
+ .wait_events = glx_wait_events,
+ .uninit = glx_uninit,
+};
diff --git a/video/out/opengl/context_w32.c b/video/out/opengl/context_w32.c
index 4c47764..eb61239 100644
--- a/video/out/opengl/context_w32.c
+++ b/video/out/opengl/context_w32.c
@@ -24,6 +24,19 @@
#include "video/out/win32/exclusive_hack.h"
#include "context.h"
+#if !defined(WGL_CONTEXT_MAJOR_VERSION_ARB)
+/* these are supposed to be defined in wingdi.h but mingw's is too old */
+/* only the bits actually used by mplayer are defined */
+/* reference: http://www.opengl.org/registry/specs/ARB/wgl_create_context.txt */
+
+#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
+#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
+#define WGL_CONTEXT_FLAGS_ARB 0x2094
+#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126
+#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x0002
+#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
+#endif
+
struct w32_context {
int opt_swapinterval;
int current_swapinterval;
diff --git a/video/out/opengl/context_wayland.c b/video/out/opengl/context_wayland.c
index 127ddca..87e98cd 100644
--- a/video/out/opengl/context_wayland.c
+++ b/video/out/opengl/context_wayland.c
@@ -67,7 +67,6 @@ static int egl_create_context(struct vo_wayland_state *wl, MPGLContext *ctx,
int flags)
{
GL *gl = ctx->gl;
- const char *eglstr = "";
if (!(wl->egl_context.egl.dpy = eglGetDisplay(wl->display.display)))
return -1;
@@ -82,10 +81,7 @@ static int egl_create_context(struct vo_wayland_state *wl, MPGLContext *ctx,
eglMakeCurrent(wl->egl_context.egl.dpy, NULL, NULL, wl->egl_context.egl.ctx);
- eglstr = eglQueryString(wl->egl_context.egl.dpy, EGL_EXTENSIONS);
-
- mpgl_load_functions(gl, (void*(*)(const GLubyte*))eglGetProcAddress, eglstr,
- wl->log);
+ mpegl_load_functions(gl, wl->log);
ctx->native_display_type = "wl";
ctx->native_display = wl->display.display;
diff --git a/video/out/opengl/context_x11.c b/video/out/opengl/context_x11.c
index c71e0b3..4d8dac1 100644
--- a/video/out/opengl/context_x11.c
+++ b/video/out/opengl/context_x11.c
@@ -18,8 +18,24 @@
#include <X11/Xlib.h>
#include <GL/glx.h>
-#define MP_GET_GLX_WORKAROUNDS
-#include "header_fixes.h"
+// FreeBSD 10.0-CURRENT lacks the GLX_ARB_create_context extension completely
+#ifndef GLX_CONTEXT_MAJOR_VERSION_ARB
+#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091
+#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092
+#define GLX_CONTEXT_FLAGS_ARB 0x2094
+#define GLX_CONTEXT_PROFILE_MASK_ARB 0x9126
+#ifndef __APPLE__
+// These are respectively 0x00000001 and 0x00000002 on OSX
+#define GLX_CONTEXT_DEBUG_BIT_ARB 0x0001
+#define GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x0002
+#endif
+#define GLX_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
+#define GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002
+#endif
+// GLX_EXT_create_context_es2_profile
+#ifndef GLX_CONTEXT_ES2_PROFILE_BIT_EXT
+#define GLX_CONTEXT_ES2_PROFILE_BIT_EXT 0x00000004
+#endif
#include "video/out/x11_common.h"
#include "context.h"
diff --git a/video/out/opengl/context_x11egl.c b/video/out/opengl/context_x11egl.c
index 2cf249f..2b68007 100644
--- a/video/out/opengl/context_x11egl.c
+++ b/video/out/opengl/context_x11egl.c
@@ -131,10 +131,7 @@ static int mpegl_init(struct MPGLContext *ctx, int flags)
goto uninit;
}
- const char *egl_exts = eglQueryString(p->egl_display, EGL_EXTENSIONS);
-
- void *(*gpa)(const GLubyte*) = (void *(*)(const GLubyte*))eglGetProcAddress;
- mpgl_load_functions(ctx->gl, gpa, egl_exts, vo->log);
+ mpegl_load_functions(ctx->gl, vo->log);
ctx->native_display_type = "x11";
ctx->native_display = vo->x11->display;
diff --git a/video/out/opengl/cuda_dynamic.h b/video/out/opengl/cuda_dynamic.h
index ecf212a..e1ffc6e 100644
--- a/video/out/opengl/cuda_dynamic.h
+++ b/video/out/opengl/cuda_dynamic.h
@@ -25,6 +25,8 @@
#include <stdbool.h>
#include <stddef.h>
+#include "gl_headers.h"
+
#define CUDA_VERSION 7050
#if defined(_WIN32) || defined(__CYGWIN__)
@@ -85,9 +87,6 @@ typedef enum CUGLDeviceList_enum {
CU_GL_DEVICE_LIST_NEXT_FRAME = 3,
} CUGLDeviceList;
-typedef unsigned int GLenum;
-typedef unsigned int GLuint;
-
#define CU_GRAPHICS_REGISTER_FLAGS_WRITE_DISCARD 2
typedef CUresult CUDAAPI tcuInit(unsigned int Flags);
diff --git a/video/out/opengl/egl_helpers.c b/video/out/opengl/egl_helpers.c
index 2ed1fd4..ac152df 100644
--- a/video/out/opengl/egl_helpers.c
+++ b/video/out/opengl/egl_helpers.c
@@ -15,6 +15,12 @@
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
+#include "config.h"
+
+#if HAVE_LIBDL
+#include <dlfcn.h>
+#endif
+
#include "common/common.h"
#include "egl_helpers.h"
@@ -181,12 +187,14 @@ bool mpegl_create_context_opts(EGLDisplay display, struct mp_log *log,
return true;
}
- if (try_gles) {
+ if (try_gles && !(opts->vo_flags & VOFLAG_GLES2)) {
// ES 3.x
if (create_context(display, log, true, 3, opts,
out_context, out_config))
return true;
+ }
+ if (try_gles) {
// ES 2.0
if (create_context(display, log, probing, 2, opts,
out_context, out_config))
@@ -197,3 +205,28 @@ bool mpegl_create_context_opts(EGLDisplay display, struct mp_log *log,
return false;
}
+static void *mpegl_get_proc_address(void *ctx, const char *name)
+{
+ void *p = eglGetProcAddress(name);
+#if defined(__GLIBC__) && HAVE_LIBDL
+ // Some crappy ARM/Linux things do not provide EGL 1.5, so above call does
+ // not necessarily return function pointers for core functions. Try to get
+ // them from a loaded GLES lib. As POSIX leaves RTLD_DEFAULT "reserved",
+ // use it only with glibc.
+ if (!p)
+ p = dlsym(RTLD_DEFAULT, name);
+#endif
+ return p;
+}
+
+// Load gl version and function pointers into *gl.
+// Expects a current EGL context set.
+void mpegl_load_functions(struct GL *gl, struct mp_log *log)
+{
+ const char *egl_exts = "";
+ EGLDisplay display = eglGetCurrentDisplay();
+ if (display != EGL_NO_DISPLAY)
+ egl_exts = eglQueryString(display, EGL_EXTENSIONS);
+
+ mpgl_load_functions2(gl, mpegl_get_proc_address, NULL, egl_exts, log);
+}
diff --git a/video/out/opengl/egl_helpers.h b/video/out/opengl/egl_helpers.h
index ea751d4..05f9dcc 100644
--- a/video/out/opengl/egl_helpers.h
+++ b/video/out/opengl/egl_helpers.h
@@ -27,4 +27,7 @@ bool mpegl_create_context_opts(EGLDisplay display, struct mp_log *log,
struct mpegl_opts *opts,
EGLContext *out_context, EGLConfig *out_config);
+struct GL;
+void mpegl_load_functions(struct GL *gl, struct mp_log *log);
+
#endif
diff --git a/video/out/opengl/formats.c b/video/out/opengl/formats.c
index 2e3dad0..5c9a2ab 100644
--- a/video/out/opengl/formats.c
+++ b/video/out/opengl/formats.c
@@ -10,7 +10,7 @@ enum {
// List of allowed formats, and their usability for bilinear filtering and FBOs.
// This is limited to combinations that are useful for our renderer.
-const struct gl_format gl_formats[] = {
+static const struct gl_format gl_formats[] = {
// These are used for desktop GL 3+, and GLES 3+ with GL_EXT_texture_norm16.
{GL_R8, GL_RED, T_U8, F_CF | F_GL3 | F_GL2F | F_ES3},
{GL_RG8, GL_RG, T_U8, F_CF | F_GL3 | F_GL2F | F_ES3},
@@ -88,9 +88,9 @@ const struct gl_format gl_formats[] = {
// Special formats.
{GL_RGB8, GL_RGB,
GL_UNSIGNED_SHORT_5_6_5, F_TF | F_GL2 | F_GL3},
- {GL_RGB_RAW_422_APPLE, GL_RGB_422_APPLE,
+ {GL_RGB, GL_RGB_422_APPLE,
GL_UNSIGNED_SHORT_8_8_APPLE, F_TF | F_APPL},
- {GL_RGB_RAW_422_APPLE, GL_RGB_422_APPLE,
+ {GL_RGB, GL_RGB_422_APPLE,
GL_UNSIGNED_SHORT_8_8_REV_APPLE, F_TF | F_APPL},
{0}
@@ -106,6 +106,36 @@ static const int special_formats[][2] = {
{0}
};
+struct packed_fmt_entry {
+ int fmt;
+ int8_t component_size;
+ int8_t components[4]; // source component - 0 means unmapped
+};
+
+// Regular packed formats, which can be mapped to GL formats by finding a
+// texture format with same component count/size, and swizzling the result.
+static const struct packed_fmt_entry mp_packed_formats[] = {
+ // w R G B A
+ {IMGFMT_Y8, 1, {1, 0, 0, 0}},
+ {IMGFMT_Y16, 2, {1, 0, 0, 0}},
+ {IMGFMT_YA8, 1, {1, 0, 0, 2}},
+ {IMGFMT_YA16, 2, {1, 0, 0, 2}},
+ {IMGFMT_ARGB, 1, {2, 3, 4, 1}},
+ {IMGFMT_0RGB, 1, {2, 3, 4, 0}},
+ {IMGFMT_BGRA, 1, {3, 2, 1, 4}},
+ {IMGFMT_BGR0, 1, {3, 2, 1, 0}},
+ {IMGFMT_ABGR, 1, {4, 3, 2, 1}},
+ {IMGFMT_0BGR, 1, {4, 3, 2, 0}},
+ {IMGFMT_RGBA, 1, {1, 2, 3, 4}},
+ {IMGFMT_RGB0, 1, {1, 2, 3, 0}},
+ {IMGFMT_BGR24, 1, {3, 2, 1, 0}},
+ {IMGFMT_RGB24, 1, {1, 2, 3, 0}},
+ {IMGFMT_RGB48, 2, {1, 2, 3, 0}},
+ {IMGFMT_RGBA64, 2, {1, 2, 3, 4}},
+ {IMGFMT_BGRA64, 2, {3, 2, 1, 4}},
+ {0},
+};
+
// Return an or-ed combination of all F_ flags that apply.
int gl_format_feature_flags(GL *gl)
{
@@ -208,8 +238,8 @@ int gl_format_type(const struct gl_format *format)
return MPGL_TYPE_UNORM;
}
-// Return an integer pixel "format" to a base internal format.
-// Return 0 if it's not an integer format.
+// Return base internal format of an integer format, or 0 if it's not integer.
+// "format" is like in struct gl_format.
GLenum gl_integer_format_to_base(GLenum format)
{
switch (format) {
@@ -221,9 +251,17 @@ GLenum gl_integer_format_to_base(GLenum format)
return 0;
}
+// Return whether it's a non-normalized integer format.
+// "format" is like in struct gl_format.
+bool gl_is_integer_format(GLenum format)
+{
+ return !!gl_integer_format_to_base(format);
+}
+
// Return the number of bytes per component this format implies.
// Returns 0 for formats with non-byte alignments and formats which
// merge multiple components (like GL_UNSIGNED_SHORT_5_6_5).
+// "type" is like in struct gl_format.
int gl_component_size(GLenum type)
{
switch (type) {
@@ -234,7 +272,8 @@ int gl_component_size(GLenum type)
return 0;
}
-// Return the number of a pixel "format".
+// Return the number of separate color components.
+// "format" is like in struct gl_format.
int gl_format_components(GLenum format)
{
switch (format) {
@@ -256,8 +295,8 @@ int gl_format_components(GLenum format)
return 0;
}
-// return the number of bytes per pixel for the given format
-// does not handle all possible variants, just those used by mpv
+// Return the number of bytes per pixel for the given format.
+// Parameter names like in struct gl_format.
int gl_bytes_per_pixel(GLenum format, GLenum type)
{
// Formats with merged components are special.
@@ -268,5 +307,123 @@ int gl_bytes_per_pixel(GLenum format, GLenum type)
case GL_UNSIGNED_SHORT_8_8_REV_APPLE: return 2;
}
- return gl_format_components(format) * gl_component_size(type);
+ return gl_component_size(type) * gl_format_components(format);
+}
+
+// The format has cleanly separated components (on byte boundaries).
+bool gl_format_is_regular(const struct gl_format *fmt)
+{
+ int bpp = gl_component_size(fmt->type) * gl_format_components(fmt->format);
+ return bpp == gl_bytes_per_pixel(fmt->format, fmt->type);
+}
+
+// dest = src.<w> (always using 4 components)
+static void packed_fmt_swizzle(char w[5], const struct packed_fmt_entry *fmt)
+{
+ for (int c = 0; c < 4; c++)
+ w[c] = "rgba"[MPMAX(fmt->components[c] - 1, 0)];
+ w[4] = '\0';
+}
+
+// Like gl_find_unorm_format(), but takes bits (not bytes), and if no fixed
+// point format is available, return an unsigned integer format.
+static const struct gl_format *find_plane_format(GL *gl, int bits, int n_channels)
+{
+ int bytes = (bits + 7) / 8;
+ const struct gl_format *f = gl_find_unorm_format(gl, bytes, n_channels);
+ if (f)
+ return f;
+ return gl_find_uint_format(gl, bytes, n_channels);
+}
+
+// Put a mapping of imgfmt to OpenGL textures into *out. Basically it selects
+// the correct texture formats needed to represent an imgfmt in OpenGL, with
+// textures using the same memory organization as on the CPU.
+// Each plane is represented by a texture, and each texture has a RGBA
+// component order. out->color_swizzle is set to permute the components back.
+// May return integer formats for >8 bit formats, if the driver has no
+// normalized 16 bit formats.
+// Returns false (and *out is set to all-0) if no format found.
+bool gl_get_imgfmt_desc(GL *gl, int imgfmt, struct gl_imgfmt_desc *out)
+{
+ *out = (struct gl_imgfmt_desc){0};
+
+ struct mp_imgfmt_desc desc = mp_imgfmt_get_desc(imgfmt);
+ if (!desc.id)
+ return false;
+
+ if (desc.num_planes > 4 || (desc.flags & MP_IMGFLAG_HWACCEL))
+ return false;
+
+ const struct gl_format *planes[4] = {0};
+ char swizzle_tmp[5] = {0};
+ char *swizzle = "rgba";
+
+ // YUV/planar formats
+ if (desc.flags & (MP_IMGFLAG_YUV_P | MP_IMGFLAG_RGB_P)) {
+ int bits = desc.component_bits;
+ if ((desc.flags & MP_IMGFLAG_NE) && bits >= 8 && bits <= 16) {
+ planes[0] = find_plane_format(gl, bits, 1);
+ for (int n = 1; n < desc.num_planes; n++)
+ planes[n] = planes[0];
+ // RGB/planar
+ if (desc.flags & MP_IMGFLAG_RGB_P)
+ swizzle = "brga";
+ goto supported;
+ }
+ }
+
+ // YUV/half-packed
+ if (desc.flags & MP_IMGFLAG_YUV_NV) {
+ int bits = desc.component_bits;
+ if ((desc.flags & MP_IMGFLAG_NE) && bits >= 8 && bits <= 16) {
+ planes[0] = find_plane_format(gl, bits, 1);
+ planes[1] = find_plane_format(gl, bits, 2);
+ if (desc.flags & MP_IMGFLAG_YUV_NV_SWAP)
+ swizzle = "rbga";
+ goto supported;
+ }
+ }
+
+ // XYZ (same organization as RGB packed, but requires conversion matrix)
+ if (imgfmt == IMGFMT_XYZ12) {
+ planes[0] = gl_find_unorm_format(gl, 2, 3);
+ goto supported;
+ }
+
+ // Packed RGB(A) formats
+ for (const struct packed_fmt_entry *e = mp_packed_formats; e->fmt; e++) {
+ if (e->fmt == imgfmt) {
+ int n_comp = desc.bytes[0] / e->component_size;
+ planes[0] = gl_find_unorm_format(gl, e->component_size, n_comp);
+ swizzle = swizzle_tmp;
+ packed_fmt_swizzle(swizzle, e);
+ goto supported;
+ }
+ }
+
+ // Special formats for which OpenGL happens to have direct support.
+ planes[0] = gl_find_special_format(gl, imgfmt);
+ if (planes[0]) {
+ // Packed YUV Apple formats color permutation
+ if (planes[0]->format == GL_RGB_422_APPLE)
+ swizzle = "gbra";
+ goto supported;
+ }
+
+ // Unsupported format
+ return false;
+
+supported:
+
+ snprintf(out->swizzle, sizeof(out->swizzle), "%s", swizzle);
+ out->num_planes = desc.num_planes;
+ for (int n = 0; n < desc.num_planes; n++) {
+ if (!planes[n])
+ return false;
+ out->xs[n] = desc.xs[n];
+ out->ys[n] = desc.ys[n];
+ out->planes[n] = planes[n];
+ }
+ return true;
}
diff --git a/video/out/opengl/formats.h b/video/out/opengl/formats.h
index 6ced4a7..288e92f 100644
--- a/video/out/opengl/formats.h
+++ b/video/out/opengl/formats.h
@@ -7,11 +7,9 @@ struct gl_format {
GLint internal_format; // glTexImage argument
GLenum format; // glTexImage argument
GLenum type; // e.g. GL_UNSIGNED_SHORT
- int flags;
+ int flags; // F_* flags
};
-extern const struct gl_format gl_formats[];
-
enum {
// --- gl_format.flags
@@ -35,9 +33,9 @@ enum {
// the format is still GL_FLOAT (32 bit)
// --- Other constants.
- MPGL_TYPE_UNORM = 1,
- MPGL_TYPE_UINT = 2,
- MPGL_TYPE_FLOAT = 3,
+ MPGL_TYPE_UNORM = 1, // normalized integer (fixed point) formats
+ MPGL_TYPE_UINT = 2, // full integer formats
+ MPGL_TYPE_FLOAT = 3, // float formats (both full and half)
};
int gl_format_feature_flags(GL *gl);
@@ -51,9 +49,24 @@ const struct gl_format *gl_find_uint_format(GL *gl, int bytes_per_component,
int n_components);
const struct gl_format *gl_find_float16_format(GL *gl, int n_components);
int gl_format_type(const struct gl_format *format);
+bool gl_format_is_regular(const struct gl_format *format);
GLenum gl_integer_format_to_base(GLenum format);
+bool gl_is_integer_format(GLenum format);
int gl_component_size(GLenum type);
int gl_format_components(GLenum format);
int gl_bytes_per_pixel(GLenum format, GLenum type);
+struct gl_imgfmt_desc {
+ int num_planes;
+ const struct gl_format *planes[4];
+ // Chroma shift (sub-sampling) for each plane.
+ int xs[4], ys[4];
+ // Component order (e.g. "rgba"), applied after all planes are combined.
+ // This has always 4 components (the excess components have no meaning).
+ // (For GL_LUMINANCE_ALPHA, it is assumed "ra" has been assigned to "rg".)
+ char swizzle[5];
+};
+
+bool gl_get_imgfmt_desc(GL *gl, int imgfmt, struct gl_imgfmt_desc *out);
+
#endif
diff --git a/video/out/opengl/gl_headers.h b/video/out/opengl/gl_headers.h
new file mode 100644
index 0000000..bfefc3d
--- /dev/null
+++ b/video/out/opengl/gl_headers.h
@@ -0,0 +1,747 @@
+/*
+ * Parts of OpenGL(ES) needed by the OpenGL renderer.
+ *
+ * This excludes function declarations.
+ *
+ * This header is based on:
+ * - Khronos GLES headers (MIT)
+ * - mpv or MPlayer code (LGPL 2.1 or later)
+ * - probably Mesa GL headers (MIT)
+ */
+
+#ifndef MPV_GL_HEADERS_H
+#define MPV_GL_HEADERS_H
+
+#include <stdint.h>
+
+// Enable this to use system headers instead.
+#if 0
+#include <GL/gl.h>
+#include <GLES3/gl3.h>
+#endif
+
+#ifndef GLAPIENTRY
+#ifdef _WIN32
+#define GLAPIENTRY __stdcall
+#else
+#define GLAPIENTRY
+#endif
+#endif
+
+// --- GL 1.1
+
+#define GL_BACK_LEFT 0x0402
+#define GL_TEXTURE_1D 0x0DE0
+#define GL_RGB16 0x8054
+#define GL_RGB10 0x8052
+#define GL_RGBA12 0x805A
+#define GL_RGBA16 0x805B
+#define GL_TEXTURE_RED_SIZE 0x805C
+
+// --- GL 1.1 (removed from 3.0 core and not in GLES 2/3)
+
+#define GL_TEXTURE_LUMINANCE_SIZE 0x8060
+#define GL_LUMINANCE8 0x8040
+#define GL_LUMINANCE8_ALPHA8 0x8045
+#define GL_LUMINANCE16 0x8042
+#define GL_LUMINANCE16_ALPHA16 0x8048
+
+// --- GL 1.5
+
+#define GL_WRITE_ONLY 0x88B9
+
+// --- GL 3.0
+
+#define GL_R16 0x822A
+#define GL_RG16 0x822C
+
+// --- GL 3.1
+
+#define GL_TEXTURE_RECTANGLE 0x84F5
+
+// --- GL 3.3
+
+#define GL_TIME_ELAPSED 0x88BF
+
+// --- GL 4.3 or GL_ARB_debug_output
+
+#define GL_DEBUG_SEVERITY_HIGH 0x9146
+#define GL_DEBUG_SEVERITY_MEDIUM 0x9147
+#define GL_DEBUG_SEVERITY_LOW 0x9148
+#define GL_DEBUG_SEVERITY_NOTIFICATION 0x826B
+
+// --- GL_NV_vdpau_interop
+
+#define GLvdpauSurfaceNV GLintptr
+#define GL_WRITE_DISCARD_NV 0x88BE
+
+// --- GL_OES_EGL_image_external, GL_NV_EGL_stream_consumer_external
+
+#define GL_TEXTURE_EXTERNAL_OES 0x8D65
+
+// --- GL_APPLE_rgb_422
+
+#define GL_RGB_422_APPLE 0x8A1F
+#define GL_UNSIGNED_SHORT_8_8_APPLE 0x85BA
+#define GL_UNSIGNED_SHORT_8_8_REV_APPLE 0x85BB
+
+// --- GL_ANGLE_translated_shader_source
+
+#define GL_TRANSLATED_SHADER_SOURCE_LENGTH_ANGLE 0x93A0
+
+// ---- GLES 2
+
+#ifndef GL_TRUE
+typedef int8_t GLbyte;
+typedef float GLclampf;
+typedef int32_t GLfixed;
+typedef short GLshort;
+typedef unsigned short GLushort;
+typedef void GLvoid;
+typedef struct __GLsync *GLsync;
+typedef int64_t GLint64;
+typedef uint64_t GLuint64;
+typedef unsigned int GLenum;
+typedef unsigned int GLuint;
+typedef char GLchar;
+typedef float GLfloat;
+typedef ptrdiff_t GLsizeiptr;
+typedef intptr_t GLintptr;
+typedef unsigned int GLbitfield;
+typedef int GLint;
+typedef unsigned char GLboolean;
+typedef int GLsizei;
+typedef uint8_t GLubyte;
+#endif
+
+#define GL_DEPTH_BUFFER_BIT 0x00000100
+#define GL_STENCIL_BUFFER_BIT 0x00000400
+#define GL_COLOR_BUFFER_BIT 0x00004000
+#define GL_FALSE 0
+#define GL_TRUE 1
+#define GL_POINTS 0x0000
+#define GL_LINES 0x0001
+#define GL_LINE_LOOP 0x0002
+#define GL_LINE_STRIP 0x0003
+#define GL_TRIANGLES 0x0004
+#define GL_TRIANGLE_STRIP 0x0005
+#define GL_TRIANGLE_FAN 0x0006
+#define GL_ZERO 0
+#define GL_ONE 1
+#define GL_SRC_COLOR 0x0300
+#define GL_ONE_MINUS_SRC_COLOR 0x0301
+#define GL_SRC_ALPHA 0x0302
+#define GL_ONE_MINUS_SRC_ALPHA 0x0303
+#define GL_DST_ALPHA 0x0304
+#define GL_ONE_MINUS_DST_ALPHA 0x0305
+#define GL_DST_COLOR 0x0306
+#define GL_ONE_MINUS_DST_COLOR 0x0307
+#define GL_SRC_ALPHA_SATURATE 0x0308
+#define GL_FUNC_ADD 0x8006
+#define GL_BLEND_EQUATION 0x8009
+#define GL_BLEND_EQUATION_RGB 0x8009
+#define GL_BLEND_EQUATION_ALPHA 0x883D
+#define GL_FUNC_SUBTRACT 0x800A
+#define GL_FUNC_REVERSE_SUBTRACT 0x800B
+#define GL_BLEND_DST_RGB 0x80C8
+#define GL_BLEND_SRC_RGB 0x80C9
+#define GL_BLEND_DST_ALPHA 0x80CA
+#define GL_BLEND_SRC_ALPHA 0x80CB
+#define GL_CONSTANT_COLOR 0x8001
+#define GL_ONE_MINUS_CONSTANT_COLOR 0x8002
+#define GL_CONSTANT_ALPHA 0x8003
+#define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004
+#define GL_BLEND_COLOR 0x8005
+#define GL_ARRAY_BUFFER 0x8892
+#define GL_ELEMENT_ARRAY_BUFFER 0x8893
+#define GL_ARRAY_BUFFER_BINDING 0x8894
+#define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895
+#define GL_STREAM_DRAW 0x88E0
+#define GL_STATIC_DRAW 0x88E4
+#define GL_DYNAMIC_DRAW 0x88E8
+#define GL_BUFFER_SIZE 0x8764
+#define GL_BUFFER_USAGE 0x8765
+#define GL_CURRENT_VERTEX_ATTRIB 0x8626
+#define GL_FRONT 0x0404
+#define GL_BACK 0x0405
+#define GL_FRONT_AND_BACK 0x0408
+#define GL_TEXTURE_2D 0x0DE1
+#define GL_CULL_FACE 0x0B44
+#define GL_BLEND 0x0BE2
+#define GL_DITHER 0x0BD0
+#define GL_STENCIL_TEST 0x0B90
+#define GL_DEPTH_TEST 0x0B71
+#define GL_SCISSOR_TEST 0x0C11
+#define GL_POLYGON_OFFSET_FILL 0x8037
+#define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E
+#define GL_SAMPLE_COVERAGE 0x80A0
+#define GL_NO_ERROR 0
+#define GL_INVALID_ENUM 0x0500
+#define GL_INVALID_VALUE 0x0501
+#define GL_INVALID_OPERATION 0x0502
+#define GL_OUT_OF_MEMORY 0x0505
+#define GL_CW 0x0900
+#define GL_CCW 0x0901
+#define GL_LINE_WIDTH 0x0B21
+#define GL_ALIASED_POINT_SIZE_RANGE 0x846D
+#define GL_ALIASED_LINE_WIDTH_RANGE 0x846E
+#define GL_CULL_FACE_MODE 0x0B45
+#define GL_FRONT_FACE 0x0B46
+#define GL_DEPTH_RANGE 0x0B70
+#define GL_DEPTH_WRITEMASK 0x0B72
+#define GL_DEPTH_CLEAR_VALUE 0x0B73
+#define GL_DEPTH_FUNC 0x0B74
+#define GL_STENCIL_CLEAR_VALUE 0x0B91
+#define GL_STENCIL_FUNC 0x0B92
+#define GL_STENCIL_FAIL 0x0B94
+#define GL_STENCIL_PASS_DEPTH_FAIL 0x0B95
+#define GL_STENCIL_PASS_DEPTH_PASS 0x0B96
+#define GL_STENCIL_REF 0x0B97
+#define GL_STENCIL_VALUE_MASK 0x0B93
+#define GL_STENCIL_WRITEMASK 0x0B98
+#define GL_STENCIL_BACK_FUNC 0x8800
+#define GL_STENCIL_BACK_FAIL 0x8801
+#define GL_STENCIL_BACK_PASS_DEPTH_FAIL 0x8802
+#define GL_STENCIL_BACK_PASS_DEPTH_PASS 0x8803
+#define GL_STENCIL_BACK_REF 0x8CA3
+#define GL_STENCIL_BACK_VALUE_MASK 0x8CA4
+#define GL_STENCIL_BACK_WRITEMASK 0x8CA5
+#define GL_VIEWPORT 0x0BA2
+#define GL_SCISSOR_BOX 0x0C10
+#define GL_COLOR_CLEAR_VALUE 0x0C22
+#define GL_COLOR_WRITEMASK 0x0C23
+#define GL_UNPACK_ALIGNMENT 0x0CF5
+#define GL_PACK_ALIGNMENT 0x0D05
+#define GL_MAX_TEXTURE_SIZE 0x0D33
+#define GL_MAX_VIEWPORT_DIMS 0x0D3A
+#define GL_SUBPIXEL_BITS 0x0D50
+#define GL_RED_BITS 0x0D52
+#define GL_GREEN_BITS 0x0D53
+#define GL_BLUE_BITS 0x0D54
+#define GL_ALPHA_BITS 0x0D55
+#define GL_DEPTH_BITS 0x0D56
+#define GL_STENCIL_BITS 0x0D57
+#define GL_POLYGON_OFFSET_UNITS 0x2A00
+#define GL_POLYGON_OFFSET_FACTOR 0x8038
+#define GL_TEXTURE_BINDING_2D 0x8069
+#define GL_SAMPLE_BUFFERS 0x80A8
+#define GL_SAMPLES 0x80A9
+#define GL_SAMPLE_COVERAGE_VALUE 0x80AA
+#define GL_SAMPLE_COVERAGE_INVERT 0x80AB
+#define GL_NUM_COMPRESSED_TEXTURE_FORMATS 0x86A2
+#define GL_COMPRESSED_TEXTURE_FORMATS 0x86A3
+#define GL_DONT_CARE 0x1100
+#define GL_FASTEST 0x1101
+#define GL_NICEST 0x1102
+#define GL_GENERATE_MIPMAP_HINT 0x8192
+#define GL_BYTE 0x1400
+#define GL_UNSIGNED_BYTE 0x1401
+#define GL_SHORT 0x1402
+#define GL_UNSIGNED_SHORT 0x1403
+#define GL_INT 0x1404
+#define GL_UNSIGNED_INT 0x1405
+#define GL_FLOAT 0x1406
+#define GL_FIXED 0x140C
+#define GL_DEPTH_COMPONENT 0x1902
+#define GL_ALPHA 0x1906
+#define GL_RGB 0x1907
+#define GL_RGBA 0x1908
+#define GL_LUMINANCE 0x1909
+#define GL_LUMINANCE_ALPHA 0x190A
+#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033
+#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034
+#define GL_UNSIGNED_SHORT_5_6_5 0x8363
+#define GL_FRAGMENT_SHADER 0x8B30
+#define GL_VERTEX_SHADER 0x8B31
+#define GL_MAX_VERTEX_ATTRIBS 0x8869
+#define GL_MAX_VERTEX_UNIFORM_VECTORS 0x8DFB
+#define GL_MAX_VARYING_VECTORS 0x8DFC
+#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D
+#define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS 0x8B4C
+#define GL_MAX_TEXTURE_IMAGE_UNITS 0x8872
+#define GL_MAX_FRAGMENT_UNIFORM_VECTORS 0x8DFD
+#define GL_SHADER_TYPE 0x8B4F
+#define GL_DELETE_STATUS 0x8B80
+#define GL_LINK_STATUS 0x8B82
+#define GL_VALIDATE_STATUS 0x8B83
+#define GL_ATTACHED_SHADERS 0x8B85
+#define GL_ACTIVE_UNIFORMS 0x8B86
+#define GL_ACTIVE_UNIFORM_MAX_LENGTH 0x8B87
+#define GL_ACTIVE_ATTRIBUTES 0x8B89
+#define GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 0x8B8A
+#define GL_SHADING_LANGUAGE_VERSION 0x8B8C
+#define GL_CURRENT_PROGRAM 0x8B8D
+#define GL_NEVER 0x0200
+#define GL_LESS 0x0201
+#define GL_EQUAL 0x0202
+#define GL_LEQUAL 0x0203
+#define GL_GREATER 0x0204
+#define GL_NOTEQUAL 0x0205
+#define GL_GEQUAL 0x0206
+#define GL_ALWAYS 0x0207
+#define GL_KEEP 0x1E00
+#define GL_REPLACE 0x1E01
+#define GL_INCR 0x1E02
+#define GL_DECR 0x1E03
+#define GL_INVERT 0x150A
+#define GL_INCR_WRAP 0x8507
+#define GL_DECR_WRAP 0x8508
+#define GL_VENDOR 0x1F00
+#define GL_RENDERER 0x1F01
+#define GL_VERSION 0x1F02
+#define GL_EXTENSIONS 0x1F03
+#define GL_NEAREST 0x2600
+#define GL_LINEAR 0x2601
+#define GL_NEAREST_MIPMAP_NEAREST 0x2700
+#define GL_LINEAR_MIPMAP_NEAREST 0x2701
+#define GL_NEAREST_MIPMAP_LINEAR 0x2702
+#define GL_LINEAR_MIPMAP_LINEAR 0x2703
+#define GL_TEXTURE_MAG_FILTER 0x2800
+#define GL_TEXTURE_MIN_FILTER 0x2801
+#define GL_TEXTURE_WRAP_S 0x2802
+#define GL_TEXTURE_WRAP_T 0x2803
+#define GL_TEXTURE 0x1702
+#define GL_TEXTURE_CUBE_MAP 0x8513
+#define GL_TEXTURE_BINDING_CUBE_MAP 0x8514
+#define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515
+#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516
+#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517
+#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518
+#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519
+#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A
+#define GL_MAX_CUBE_MAP_TEXTURE_SIZE 0x851C
+#define GL_TEXTURE0 0x84C0
+#define GL_TEXTURE1 0x84C1
+#define GL_TEXTURE2 0x84C2
+#define GL_TEXTURE3 0x84C3
+#define GL_TEXTURE4 0x84C4
+#define GL_TEXTURE5 0x84C5
+#define GL_TEXTURE6 0x84C6
+#define GL_TEXTURE7 0x84C7
+#define GL_TEXTURE8 0x84C8
+#define GL_TEXTURE9 0x84C9
+#define GL_TEXTURE10 0x84CA
+#define GL_TEXTURE11 0x84CB
+#define GL_TEXTURE12 0x84CC
+#define GL_TEXTURE13 0x84CD
+#define GL_TEXTURE14 0x84CE
+#define GL_TEXTURE15 0x84CF
+#define GL_TEXTURE16 0x84D0
+#define GL_TEXTURE17 0x84D1
+#define GL_TEXTURE18 0x84D2
+#define GL_TEXTURE19 0x84D3
+#define GL_TEXTURE20 0x84D4
+#define GL_TEXTURE21 0x84D5
+#define GL_TEXTURE22 0x84D6
+#define GL_TEXTURE23 0x84D7
+#define GL_TEXTURE24 0x84D8
+#define GL_TEXTURE25 0x84D9
+#define GL_TEXTURE26 0x84DA
+#define GL_TEXTURE27 0x84DB
+#define GL_TEXTURE28 0x84DC
+#define GL_TEXTURE29 0x84DD
+#define GL_TEXTURE30 0x84DE
+#define GL_TEXTURE31 0x84DF
+#define GL_ACTIVE_TEXTURE 0x84E0
+#define GL_REPEAT 0x2901
+#define GL_CLAMP_TO_EDGE 0x812F
+#define GL_MIRRORED_REPEAT 0x8370
+#define GL_FLOAT_VEC2 0x8B50
+#define GL_FLOAT_VEC3 0x8B51
+#define GL_FLOAT_VEC4 0x8B52
+#define GL_INT_VEC2 0x8B53
+#define GL_INT_VEC3 0x8B54
+#define GL_INT_VEC4 0x8B55
+#define GL_BOOL 0x8B56
+#define GL_BOOL_VEC2 0x8B57
+#define GL_BOOL_VEC3 0x8B58
+#define GL_BOOL_VEC4 0x8B59
+#define GL_FLOAT_MAT2 0x8B5A
+#define GL_FLOAT_MAT3 0x8B5B
+#define GL_FLOAT_MAT4 0x8B5C
+#define GL_SAMPLER_2D 0x8B5E
+#define GL_SAMPLER_CUBE 0x8B60
+#define GL_VERTEX_ATTRIB_ARRAY_ENABLED 0x8622
+#define GL_VERTEX_ATTRIB_ARRAY_SIZE 0x8623
+#define GL_VERTEX_ATTRIB_ARRAY_STRIDE 0x8624
+#define GL_VERTEX_ATTRIB_ARRAY_TYPE 0x8625
+#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED 0x886A
+#define GL_VERTEX_ATTRIB_ARRAY_POINTER 0x8645
+#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING 0x889F
+#define GL_IMPLEMENTATION_COLOR_READ_TYPE 0x8B9A
+#define GL_IMPLEMENTATION_COLOR_READ_FORMAT 0x8B9B
+#define GL_COMPILE_STATUS 0x8B81
+#define GL_INFO_LOG_LENGTH 0x8B84
+#define GL_SHADER_SOURCE_LENGTH 0x8B88
+#define GL_SHADER_COMPILER 0x8DFA
+#define GL_SHADER_BINARY_FORMATS 0x8DF8
+#define GL_NUM_SHADER_BINARY_FORMATS 0x8DF9
+#define GL_LOW_FLOAT 0x8DF0
+#define GL_MEDIUM_FLOAT 0x8DF1
+#define GL_HIGH_FLOAT 0x8DF2
+#define GL_LOW_INT 0x8DF3
+#define GL_MEDIUM_INT 0x8DF4
+#define GL_HIGH_INT 0x8DF5
+#define GL_FRAMEBUFFER 0x8D40
+#define GL_RENDERBUFFER 0x8D41
+#define GL_RGBA4 0x8056
+#define GL_RGB5_A1 0x8057
+#define GL_RGB565 0x8D62
+#define GL_DEPTH_COMPONENT16 0x81A5
+#define GL_STENCIL_INDEX8 0x8D48
+#define GL_RENDERBUFFER_WIDTH 0x8D42
+#define GL_RENDERBUFFER_HEIGHT 0x8D43
+#define GL_RENDERBUFFER_INTERNAL_FORMAT 0x8D44
+#define GL_RENDERBUFFER_RED_SIZE 0x8D50
+#define GL_RENDERBUFFER_GREEN_SIZE 0x8D51
+#define GL_RENDERBUFFER_BLUE_SIZE 0x8D52
+#define GL_RENDERBUFFER_ALPHA_SIZE 0x8D53
+#define GL_RENDERBUFFER_DEPTH_SIZE 0x8D54
+#define GL_RENDERBUFFER_STENCIL_SIZE 0x8D55
+#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE 0x8CD0
+#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME 0x8CD1
+#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL 0x8CD2
+#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE 0x8CD3
+#define GL_COLOR_ATTACHMENT0 0x8CE0
+#define GL_DEPTH_ATTACHMENT 0x8D00
+#define GL_STENCIL_ATTACHMENT 0x8D20
+#define GL_NONE 0
+#define GL_FRAMEBUFFER_COMPLETE 0x8CD5
+#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6
+#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7
+#define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS 0x8CD9
+#define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD
+#define GL_FRAMEBUFFER_BINDING 0x8CA6
+#define GL_RENDERBUFFER_BINDING 0x8CA7
+#define GL_MAX_RENDERBUFFER_SIZE 0x84E8
+#define GL_INVALID_FRAMEBUFFER_OPERATION 0x0506
+
+// ---- GLES 3
+
+#ifndef GL_READ_BUFFER
+typedef unsigned short GLhalf;
+#endif
+
+#define GL_READ_BUFFER 0x0C02
+#define GL_UNPACK_ROW_LENGTH 0x0CF2
+#define GL_UNPACK_SKIP_ROWS 0x0CF3
+#define GL_UNPACK_SKIP_PIXELS 0x0CF4
+#define GL_PACK_ROW_LENGTH 0x0D02
+#define GL_PACK_SKIP_ROWS 0x0D03
+#define GL_PACK_SKIP_PIXELS 0x0D04
+#define GL_COLOR 0x1800
+#define GL_DEPTH 0x1801
+#define GL_STENCIL 0x1802
+#define GL_RED 0x1903
+#define GL_RGB8 0x8051
+#define GL_RGBA8 0x8058
+#define GL_RGB10_A2 0x8059
+#define GL_TEXTURE_BINDING_3D 0x806A
+#define GL_UNPACK_SKIP_IMAGES 0x806D
+#define GL_UNPACK_IMAGE_HEIGHT 0x806E
+#define GL_TEXTURE_3D 0x806F
+#define GL_TEXTURE_WRAP_R 0x8072
+#define GL_MAX_3D_TEXTURE_SIZE 0x8073
+#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368
+#define GL_MAX_ELEMENTS_VERTICES 0x80E8
+#define GL_MAX_ELEMENTS_INDICES 0x80E9
+#define GL_TEXTURE_MIN_LOD 0x813A
+#define GL_TEXTURE_MAX_LOD 0x813B
+#define GL_TEXTURE_BASE_LEVEL 0x813C
+#define GL_TEXTURE_MAX_LEVEL 0x813D
+#define GL_MIN 0x8007
+#define GL_MAX 0x8008
+#define GL_DEPTH_COMPONENT24 0x81A6
+#define GL_MAX_TEXTURE_LOD_BIAS 0x84FD
+#define GL_TEXTURE_COMPARE_MODE 0x884C
+#define GL_TEXTURE_COMPARE_FUNC 0x884D
+#define GL_CURRENT_QUERY 0x8865
+#define GL_QUERY_RESULT 0x8866
+#define GL_QUERY_RESULT_AVAILABLE 0x8867
+#define GL_BUFFER_MAPPED 0x88BC
+#define GL_BUFFER_MAP_POINTER 0x88BD
+#define GL_STREAM_READ 0x88E1
+#define GL_STREAM_COPY 0x88E2
+#define GL_STATIC_READ 0x88E5
+#define GL_STATIC_COPY 0x88E6
+#define GL_DYNAMIC_READ 0x88E9
+#define GL_DYNAMIC_COPY 0x88EA
+#define GL_MAX_DRAW_BUFFERS 0x8824
+#define GL_DRAW_BUFFER0 0x8825
+#define GL_DRAW_BUFFER1 0x8826
+#define GL_DRAW_BUFFER2 0x8827
+#define GL_DRAW_BUFFER3 0x8828
+#define GL_DRAW_BUFFER4 0x8829
+#define GL_DRAW_BUFFER5 0x882A
+#define GL_DRAW_BUFFER6 0x882B
+#define GL_DRAW_BUFFER7 0x882C
+#define GL_DRAW_BUFFER8 0x882D
+#define GL_DRAW_BUFFER9 0x882E
+#define GL_DRAW_BUFFER10 0x882F
+#define GL_DRAW_BUFFER11 0x8830
+#define GL_DRAW_BUFFER12 0x8831
+#define GL_DRAW_BUFFER13 0x8832
+#define GL_DRAW_BUFFER14 0x8833
+#define GL_DRAW_BUFFER15 0x8834
+#define GL_MAX_FRAGMENT_UNIFORM_COMPONENTS 0x8B49
+#define GL_MAX_VERTEX_UNIFORM_COMPONENTS 0x8B4A
+#define GL_SAMPLER_3D 0x8B5F
+#define GL_SAMPLER_2D_SHADOW 0x8B62
+#define GL_FRAGMENT_SHADER_DERIVATIVE_HINT 0x8B8B
+#define GL_PIXEL_PACK_BUFFER 0x88EB
+#define GL_PIXEL_UNPACK_BUFFER 0x88EC
+#define GL_PIXEL_PACK_BUFFER_BINDING 0x88ED
+#define GL_PIXEL_UNPACK_BUFFER_BINDING 0x88EF
+#define GL_FLOAT_MAT2x3 0x8B65
+#define GL_FLOAT_MAT2x4 0x8B66
+#define GL_FLOAT_MAT3x2 0x8B67
+#define GL_FLOAT_MAT3x4 0x8B68
+#define GL_FLOAT_MAT4x2 0x8B69
+#define GL_FLOAT_MAT4x3 0x8B6A
+#define GL_SRGB 0x8C40
+#define GL_SRGB8 0x8C41
+#define GL_SRGB8_ALPHA8 0x8C43
+#define GL_COMPARE_REF_TO_TEXTURE 0x884E
+#define GL_MAJOR_VERSION 0x821B
+#define GL_MINOR_VERSION 0x821C
+#define GL_NUM_EXTENSIONS 0x821D
+#define GL_RGBA32F 0x8814
+#define GL_RGB32F 0x8815
+#define GL_RGBA16F 0x881A
+#define GL_RGB16F 0x881B
+#define GL_VERTEX_ATTRIB_ARRAY_INTEGER 0x88FD
+#define GL_MAX_ARRAY_TEXTURE_LAYERS 0x88FF
+#define GL_MIN_PROGRAM_TEXEL_OFFSET 0x8904
+#define GL_MAX_PROGRAM_TEXEL_OFFSET 0x8905
+#define GL_MAX_VARYING_COMPONENTS 0x8B4B
+#define GL_TEXTURE_2D_ARRAY 0x8C1A
+#define GL_TEXTURE_BINDING_2D_ARRAY 0x8C1D
+#define GL_R11F_G11F_B10F 0x8C3A
+#define GL_UNSIGNED_INT_10F_11F_11F_REV 0x8C3B
+#define GL_RGB9_E5 0x8C3D
+#define GL_UNSIGNED_INT_5_9_9_9_REV 0x8C3E
+#define GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH 0x8C76
+#define GL_TRANSFORM_FEEDBACK_BUFFER_MODE 0x8C7F
+#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS 0x8C80
+#define GL_TRANSFORM_FEEDBACK_VARYINGS 0x8C83
+#define GL_TRANSFORM_FEEDBACK_BUFFER_START 0x8C84
+#define GL_TRANSFORM_FEEDBACK_BUFFER_SIZE 0x8C85
+#define GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN 0x8C88
+#define GL_RASTERIZER_DISCARD 0x8C89
+#define GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS 0x8C8A
+#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS 0x8C8B
+#define GL_INTERLEAVED_ATTRIBS 0x8C8C
+#define GL_SEPARATE_ATTRIBS 0x8C8D
+#define GL_TRANSFORM_FEEDBACK_BUFFER 0x8C8E
+#define GL_TRANSFORM_FEEDBACK_BUFFER_BINDING 0x8C8F
+#define GL_RGBA32UI 0x8D70
+#define GL_RGB32UI 0x8D71
+#define GL_RGBA16UI 0x8D76
+#define GL_RGB16UI 0x8D77
+#define GL_RGBA8UI 0x8D7C
+#define GL_RGB8UI 0x8D7D
+#define GL_RGBA32I 0x8D82
+#define GL_RGB32I 0x8D83
+#define GL_RGBA16I 0x8D88
+#define GL_RGB16I 0x8D89
+#define GL_RGBA8I 0x8D8E
+#define GL_RGB8I 0x8D8F
+#define GL_RED_INTEGER 0x8D94
+#define GL_RGB_INTEGER 0x8D98
+#define GL_RGBA_INTEGER 0x8D99
+#define GL_SAMPLER_2D_ARRAY 0x8DC1
+#define GL_SAMPLER_2D_ARRAY_SHADOW 0x8DC4
+#define GL_SAMPLER_CUBE_SHADOW 0x8DC5
+#define GL_UNSIGNED_INT_VEC2 0x8DC6
+#define GL_UNSIGNED_INT_VEC3 0x8DC7
+#define GL_UNSIGNED_INT_VEC4 0x8DC8
+#define GL_INT_SAMPLER_2D 0x8DCA
+#define GL_INT_SAMPLER_3D 0x8DCB
+#define GL_INT_SAMPLER_CUBE 0x8DCC
+#define GL_INT_SAMPLER_2D_ARRAY 0x8DCF
+#define GL_UNSIGNED_INT_SAMPLER_2D 0x8DD2
+#define GL_UNSIGNED_INT_SAMPLER_3D 0x8DD3
+#define GL_UNSIGNED_INT_SAMPLER_CUBE 0x8DD4
+#define GL_UNSIGNED_INT_SAMPLER_2D_ARRAY 0x8DD7
+#define GL_BUFFER_ACCESS_FLAGS 0x911F
+#define GL_BUFFER_MAP_LENGTH 0x9120
+#define GL_BUFFER_MAP_OFFSET 0x9121
+#define GL_DEPTH_COMPONENT32F 0x8CAC
+#define GL_DEPTH32F_STENCIL8 0x8CAD
+#define GL_FLOAT_32_UNSIGNED_INT_24_8_REV 0x8DAD
+#define GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING 0x8210
+#define GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE 0x8211
+#define GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE 0x8212
+#define GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE 0x8213
+#define GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE 0x8214
+#define GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE 0x8215
+#define GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE 0x8216
+#define GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE 0x8217
+#define GL_FRAMEBUFFER_DEFAULT 0x8218
+#define GL_FRAMEBUFFER_UNDEFINED 0x8219
+#define GL_DEPTH_STENCIL_ATTACHMENT 0x821A
+#define GL_DEPTH_STENCIL 0x84F9
+#define GL_UNSIGNED_INT_24_8 0x84FA
+#define GL_DEPTH24_STENCIL8 0x88F0
+#define GL_UNSIGNED_NORMALIZED 0x8C17
+#define GL_DRAW_FRAMEBUFFER_BINDING 0x8CA6
+#define GL_READ_FRAMEBUFFER 0x8CA8
+#define GL_DRAW_FRAMEBUFFER 0x8CA9
+#define GL_READ_FRAMEBUFFER_BINDING 0x8CAA
+#define GL_RENDERBUFFER_SAMPLES 0x8CAB
+#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER 0x8CD4
+#define GL_MAX_COLOR_ATTACHMENTS 0x8CDF
+#define GL_COLOR_ATTACHMENT1 0x8CE1
+#define GL_COLOR_ATTACHMENT2 0x8CE2
+#define GL_COLOR_ATTACHMENT3 0x8CE3
+#define GL_COLOR_ATTACHMENT4 0x8CE4
+#define GL_COLOR_ATTACHMENT5 0x8CE5
+#define GL_COLOR_ATTACHMENT6 0x8CE6
+#define GL_COLOR_ATTACHMENT7 0x8CE7
+#define GL_COLOR_ATTACHMENT8 0x8CE8
+#define GL_COLOR_ATTACHMENT9 0x8CE9
+#define GL_COLOR_ATTACHMENT10 0x8CEA
+#define GL_COLOR_ATTACHMENT11 0x8CEB
+#define GL_COLOR_ATTACHMENT12 0x8CEC
+#define GL_COLOR_ATTACHMENT13 0x8CED
+#define GL_COLOR_ATTACHMENT14 0x8CEE
+#define GL_COLOR_ATTACHMENT15 0x8CEF
+#define GL_COLOR_ATTACHMENT16 0x8CF0
+#define GL_COLOR_ATTACHMENT17 0x8CF1
+#define GL_COLOR_ATTACHMENT18 0x8CF2
+#define GL_COLOR_ATTACHMENT19 0x8CF3
+#define GL_COLOR_ATTACHMENT20 0x8CF4
+#define GL_COLOR_ATTACHMENT21 0x8CF5
+#define GL_COLOR_ATTACHMENT22 0x8CF6
+#define GL_COLOR_ATTACHMENT23 0x8CF7
+#define GL_COLOR_ATTACHMENT24 0x8CF8
+#define GL_COLOR_ATTACHMENT25 0x8CF9
+#define GL_COLOR_ATTACHMENT26 0x8CFA
+#define GL_COLOR_ATTACHMENT27 0x8CFB
+#define GL_COLOR_ATTACHMENT28 0x8CFC
+#define GL_COLOR_ATTACHMENT29 0x8CFD
+#define GL_COLOR_ATTACHMENT30 0x8CFE
+#define GL_COLOR_ATTACHMENT31 0x8CFF
+#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE 0x8D56
+#define GL_MAX_SAMPLES 0x8D57
+#define GL_HALF_FLOAT 0x140B
+#define GL_MAP_READ_BIT 0x0001
+#define GL_MAP_WRITE_BIT 0x0002
+#define GL_MAP_INVALIDATE_RANGE_BIT 0x0004
+#define GL_MAP_INVALIDATE_BUFFER_BIT 0x0008
+#define GL_MAP_FLUSH_EXPLICIT_BIT 0x0010
+#define GL_MAP_UNSYNCHRONIZED_BIT 0x0020
+#define GL_RG 0x8227
+#define GL_RG_INTEGER 0x8228
+#define GL_R8 0x8229
+#define GL_RG8 0x822B
+#define GL_R16F 0x822D
+#define GL_R32F 0x822E
+#define GL_RG16F 0x822F
+#define GL_RG32F 0x8230
+#define GL_R8I 0x8231
+#define GL_R8UI 0x8232
+#define GL_R16I 0x8233
+#define GL_R16UI 0x8234
+#define GL_R32I 0x8235
+#define GL_R32UI 0x8236
+#define GL_RG8I 0x8237
+#define GL_RG8UI 0x8238
+#define GL_RG16I 0x8239
+#define GL_RG16UI 0x823A
+#define GL_RG32I 0x823B
+#define GL_RG32UI 0x823C
+#define GL_VERTEX_ARRAY_BINDING 0x85B5
+#define GL_R8_SNORM 0x8F94
+#define GL_RG8_SNORM 0x8F95
+#define GL_RGB8_SNORM 0x8F96
+#define GL_RGBA8_SNORM 0x8F97
+#define GL_SIGNED_NORMALIZED 0x8F9C
+#define GL_PRIMITIVE_RESTART_FIXED_INDEX 0x8D69
+#define GL_COPY_READ_BUFFER 0x8F36
+#define GL_COPY_WRITE_BUFFER 0x8F37
+#define GL_COPY_READ_BUFFER_BINDING 0x8F36
+#define GL_COPY_WRITE_BUFFER_BINDING 0x8F37
+#define GL_UNIFORM_BUFFER 0x8A11
+#define GL_UNIFORM_BUFFER_BINDING 0x8A28
+#define GL_UNIFORM_BUFFER_START 0x8A29
+#define GL_UNIFORM_BUFFER_SIZE 0x8A2A
+#define GL_MAX_VERTEX_UNIFORM_BLOCKS 0x8A2B
+#define GL_MAX_FRAGMENT_UNIFORM_BLOCKS 0x8A2D
+#define GL_MAX_COMBINED_UNIFORM_BLOCKS 0x8A2E
+#define GL_MAX_UNIFORM_BUFFER_BINDINGS 0x8A2F
+#define GL_MAX_UNIFORM_BLOCK_SIZE 0x8A30
+#define GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS 0x8A31
+#define GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS 0x8A33
+#define GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT 0x8A34
+#define GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH 0x8A35
+#define GL_ACTIVE_UNIFORM_BLOCKS 0x8A36
+#define GL_UNIFORM_TYPE 0x8A37
+#define GL_UNIFORM_SIZE 0x8A38
+#define GL_UNIFORM_NAME_LENGTH 0x8A39
+#define GL_UNIFORM_BLOCK_INDEX 0x8A3A
+#define GL_UNIFORM_OFFSET 0x8A3B
+#define GL_UNIFORM_ARRAY_STRIDE 0x8A3C
+#define GL_UNIFORM_MATRIX_STRIDE 0x8A3D
+#define GL_UNIFORM_IS_ROW_MAJOR 0x8A3E
+#define GL_UNIFORM_BLOCK_BINDING 0x8A3F
+#define GL_UNIFORM_BLOCK_DATA_SIZE 0x8A40
+#define GL_UNIFORM_BLOCK_NAME_LENGTH 0x8A41
+#define GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS 0x8A42
+#define GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES 0x8A43
+#define GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER 0x8A44
+#define GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER 0x8A46
+#define GL_INVALID_INDEX 0xFFFFFFFFu
+#define GL_MAX_VERTEX_OUTPUT_COMPONENTS 0x9122
+#define GL_MAX_FRAGMENT_INPUT_COMPONENTS 0x9125
+#define GL_MAX_SERVER_WAIT_TIMEOUT 0x9111
+#define GL_OBJECT_TYPE 0x9112
+#define GL_SYNC_CONDITION 0x9113
+#define GL_SYNC_STATUS 0x9114
+#define GL_SYNC_FLAGS 0x9115
+#define GL_SYNC_FENCE 0x9116
+#define GL_SYNC_GPU_COMMANDS_COMPLETE 0x9117
+#define GL_UNSIGNALED 0x9118
+#define GL_SIGNALED 0x9119
+#define GL_ALREADY_SIGNALED 0x911A
+#define GL_TIMEOUT_EXPIRED 0x911B
+#define GL_CONDITION_SATISFIED 0x911C
+#define GL_WAIT_FAILED 0x911D
+#define GL_SYNC_FLUSH_COMMANDS_BIT 0x00000001
+#define GL_TIMEOUT_IGNORED 0xFFFFFFFFFFFFFFFFull
+#define GL_VERTEX_ATTRIB_ARRAY_DIVISOR 0x88FE
+#define GL_ANY_SAMPLES_PASSED 0x8C2F
+#define GL_ANY_SAMPLES_PASSED_CONSERVATIVE 0x8D6A
+#define GL_SAMPLER_BINDING 0x8919
+#define GL_RGB10_A2UI 0x906F
+#define GL_TEXTURE_SWIZZLE_R 0x8E42
+#define GL_TEXTURE_SWIZZLE_G 0x8E43
+#define GL_TEXTURE_SWIZZLE_B 0x8E44
+#define GL_TEXTURE_SWIZZLE_A 0x8E45
+#define GL_GREEN 0x1904
+#define GL_BLUE 0x1905
+#define GL_INT_2_10_10_10_REV 0x8D9F
+#define GL_TRANSFORM_FEEDBACK 0x8E22
+#define GL_TRANSFORM_FEEDBACK_PAUSED 0x8E23
+#define GL_TRANSFORM_FEEDBACK_ACTIVE 0x8E24
+#define GL_TRANSFORM_FEEDBACK_BINDING 0x8E25
+#define GL_PROGRAM_BINARY_RETRIEVABLE_HINT 0x8257
+#define GL_PROGRAM_BINARY_LENGTH 0x8741
+#define GL_NUM_PROGRAM_BINARY_FORMATS 0x87FE
+#define GL_PROGRAM_BINARY_FORMATS 0x87FF
+#define GL_COMPRESSED_R11_EAC 0x9270
+#define GL_COMPRESSED_SIGNED_R11_EAC 0x9271
+#define GL_COMPRESSED_RG11_EAC 0x9272
+#define GL_COMPRESSED_SIGNED_RG11_EAC 0x9273
+#define GL_COMPRESSED_RGB8_ETC2 0x9274
+#define GL_COMPRESSED_SRGB8_ETC2 0x9275
+#define GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9276
+#define GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9277
+#define GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278
+#define GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC 0x9279
+#define GL_TEXTURE_IMMUTABLE_FORMAT 0x912F
+#define GL_MAX_ELEMENT_INDEX 0x8D6B
+#define GL_NUM_SAMPLE_COUNTS 0x9380
+#define GL_TEXTURE_IMMUTABLE_LEVELS 0x82DF
+
+#endif
diff --git a/video/out/opengl/header_fixes.h b/video/out/opengl/header_fixes.h
deleted file mode 100644
index 9a7108d..0000000
--- a/video/out/opengl/header_fixes.h
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * This file is part of mpv.
- *
- * mpv is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * mpv is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifdef MP_GET_GL_WORKAROUNDS
-
-// workaround for some gl.h headers
-#ifndef GLAPIENTRY
-#ifdef APIENTRY
-#define GLAPIENTRY APIENTRY
-#elif HAVE_GL_WIN32
-#define GLAPIENTRY __stdcall
-#else
-#define GLAPIENTRY
-#endif
-#endif
-
-#ifndef GL_TEXTURE_RECTANGLE
-#define GL_TEXTURE_RECTANGLE 0x84F5
-#endif
-#ifndef GL_UNSIGNED_SHORT_8_8
-#define GL_UNSIGNED_SHORT_8_8 0x85BA
-#endif
-#ifndef GL_UNSIGNED_SHORT_8_8_REV
-#define GL_UNSIGNED_SHORT_8_8_REV 0x85BB
-#endif
-
-#if HAVE_GL_WIN32 && !defined(WGL_CONTEXT_MAJOR_VERSION_ARB)
-/* these are supposed to be defined in wingdi.h but mingw's is too old */
-/* only the bits actually used by mplayer are defined */
-/* reference: http://www.opengl.org/registry/specs/ARB/wgl_create_context.txt */
-
-#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
-#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
-#define WGL_CONTEXT_FLAGS_ARB 0x2094
-#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126
-#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x0002
-#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
-#endif
-
-#ifndef GL_NV_vdpau_interop
-#define GLvdpauSurfaceNV GLintptr
-#endif
-
-#ifndef GL_DEBUG_SEVERITY_HIGH
-#define GL_DEBUG_SEVERITY_HIGH 0x9146
-#define GL_DEBUG_SEVERITY_MEDIUM 0x9147
-#define GL_DEBUG_SEVERITY_LOW 0x9148
-#define GL_DEBUG_SEVERITY_NOTIFICATION 0x826B
-#endif
-
-#ifndef GL_BACK_LEFT
-#define GL_BACK_LEFT 0x0402
-#endif
-
-#if HAVE_ANDROID_GL
-#define GL_UNSIGNED_BYTE_3_3_2 0x8032
-#define GL_UNSIGNED_BYTE_2_3_3_REV 0x8362
-#define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366
-#define GL_UNSIGNED_SHORT_5_6_5_REV 0x8364
-#define GL_RGB_422_APPLE 0x8A1F
-#define GL_BGR 0x80E0
-#define GL_BGRA 0x80E1
-#define GL_TEXTURE_1D 0x0DE0
-#define GL_WRITE_ONLY 0x88B9
-#define GL_RGB16 0x8054
-#define GL_RGB10 0x8052
-#define GL_RGBA12 0x805A
-#define GL_RGBA16 0x805B
-#define GL_TEXTURE_RED_SIZE 0x805C
-#define GL_TEXTURE_LUMINANCE_SIZE 0x8060
-#define GL_R16 0x822A
-#define GL_RG16 0x822C
-#define GL_LUMINANCE8 0x8040
-#define GL_LUMINANCE8_ALPHA8 0x8045
-#define GL_LUMINANCE16 0x8042
-#define GL_LUMINANCE16_ALPHA16 0x8048
-#define GL_UNSIGNED_SHORT_8_8_APPLE 0x85BA
-#define GL_UNSIGNED_SHORT_8_8_REV_APPLE 0x85BB
-#endif
-
-#if HAVE_IOS_GL
-#define GL_WRITE_ONLY GL_WRITE_ONLY_OES
-#define GL_TEXTURE_1D 0x0DE0
-#define GL_R16 0x822A
-#define GL_RG16 0x822C
-#define GL_RGB10 0x8052
-#define GL_RGB16 0x8054
-#define GL_RGBA12 0x805A
-#define GL_RGBA16 0x805B
-#define GL_LUMINANCE8 GL_LUMINANCE8_EXT
-#define GL_LUMINANCE8_ALPHA8 GL_LUMINANCE8_ALPHA8_EXT
-#define GL_LUMINANCE16 0x8042
-#define GL_LUMINANCE16_ALPHA16 0x8048
-#define GL_TEXTURE_RED_SIZE 0x805C
-#define GL_TEXTURE_LUMINANCE_SIZE 0x8060
-#endif
-
-// GL_ARB_timer_query and EXT_disjoint_timer_query
-#ifndef GL_TIME_ELAPSED
-// Same as GL_TIME_ELAPSED_EXT
-#define GL_TIME_ELAPSED 0x88BF
-#endif
-
-// GL_OES_EGL_image_external, GL_NV_EGL_stream_consumer_external
-#ifndef GL_TEXTURE_EXTERNAL_OES
-#define GL_TEXTURE_EXTERNAL_OES 0x8D65
-#endif
-
-// GL_ANGLE_translated_shader_source
-#ifndef GL_TRANSLATED_SHADER_SOURCE_LENGTH_ANGLE
-#define GL_TRANSLATED_SHADER_SOURCE_LENGTH_ANGLE 0x93A0
-#endif
-
-#ifndef GL_RGB_RAW_422_APPLE
-#define GL_RGB_RAW_422_APPLE 0x8A51
-#endif
-
-#undef MP_GET_GL_WORKAROUNDS
-
-#endif // MP_GET_GL_WORKAROUNDS
-
-#ifdef MP_GET_GLX_WORKAROUNDS
-
-// FreeBSD 10.0-CURRENT lacks the GLX_ARB_create_context extension completely
-#ifndef GLX_CONTEXT_MAJOR_VERSION_ARB
-#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091
-#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092
-#define GLX_CONTEXT_FLAGS_ARB 0x2094
-#define GLX_CONTEXT_PROFILE_MASK_ARB 0x9126
-#ifndef __APPLE__
-// These are respectively 0x00000001 and 0x00000002 on OSX
-#define GLX_CONTEXT_DEBUG_BIT_ARB 0x0001
-#define GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x0002
-#endif
-#define GLX_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
-#define GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002
-#endif
-// GLX_EXT_create_context_es2_profile
-#ifndef GLX_CONTEXT_ES2_PROFILE_BIT_EXT
-#define GLX_CONTEXT_ES2_PROFILE_BIT_EXT 0x00000004
-#endif
-
-#undef MP_GET_GLX_WORKAROUNDS
-
-#endif
diff --git a/video/out/opengl/hwdec.c b/video/out/opengl/hwdec.c
index ab365df..60a6ee8 100644
--- a/video/out/opengl/hwdec.c
+++ b/video/out/opengl/hwdec.c
@@ -47,7 +47,7 @@ static const struct gl_hwdec_driver *const mpgl_hwdec_drivers[] = {
#if HAVE_VDPAU_GL_X11
&gl_hwdec_vdpau,
#endif
-#if HAVE_VIDEOTOOLBOX_GL
+#if HAVE_VIDEOTOOLBOX_GL || HAVE_IOS_GL
&gl_hwdec_videotoolbox,
#endif
#if HAVE_D3D_HWACCEL
@@ -59,7 +59,6 @@ static const struct gl_hwdec_driver *const mpgl_hwdec_drivers[] = {
#if HAVE_GL_DXINTEROP
&gl_hwdec_dxva2gldx,
#endif
- &gl_hwdec_dxva2,
#endif
#if HAVE_CUDA_HWACCEL
&gl_hwdec_cuda,
diff --git a/video/out/opengl/hwdec.h b/video/out/opengl/hwdec.h
index 948b42b..962bd7b 100644
--- a/video/out/opengl/hwdec.h
+++ b/video/out/opengl/hwdec.h
@@ -23,13 +23,16 @@ struct gl_hwdec {
struct gl_hwdec_plane {
GLuint gl_texture;
GLenum gl_target;
+ // Like struct gl_format.format (GL_RED etc.). Currently to detect
+ // GL_LUMINANCE_ALPHA and integer formats - can be left to 0 otherwise.
+ GLenum gl_format;
int tex_w, tex_h; // allocated texture size
- char swizzle[5]; // component order (if length is 0, use defaults)
};
struct gl_hwdec_frame {
struct gl_hwdec_plane planes[4];
bool vdpau_fields;
+ char swizzle[5]; // optional component swizzle (4 components if set)
};
struct gl_hwdec_driver {
diff --git a/video/out/opengl/hwdec_cuda.c b/video/out/opengl/hwdec_cuda.c
index 40c4e19..e64de97 100644
--- a/video/out/opengl/hwdec_cuda.c
+++ b/video/out/opengl/hwdec_cuda.c
@@ -42,8 +42,7 @@ struct priv {
GLuint gl_textures[4];
CUgraphicsResource cu_res[4];
CUarray cu_array[4];
- int sample_count[4];
- int sample_width;
+ int plane_bytes[4];
CUcontext cuda_ctx;
};
@@ -157,27 +156,8 @@ static int reinit(struct gl_hwdec *hw, struct mp_image_params *params)
mp_image_set_params(&p->layout, params);
- for (int n = 0; n < 4; n++)
- p->sample_count[n] = 0;
-
- switch (params->imgfmt) {
- case IMGFMT_NV12:
- p->sample_width = 1;
- p->sample_count[0] = 1;
- p->sample_count[1] = 2;
- break;
- case IMGFMT_P010:
- case IMGFMT_P016:
- p->sample_width = 2;
- p->sample_count[0] = 1;
- p->sample_count[1] = 2;
- break;
- case IMGFMT_420P:
- p->sample_width = 1;
- for (int n = 0; n < 3; n++)
- p->sample_count[n] = 1;
- break;
- default:
+ struct gl_imgfmt_desc desc;
+ if (!gl_get_imgfmt_desc(gl, params->imgfmt, &desc)) {
MP_ERR(hw, "Unsupported format: %s\n", mp_imgfmt_to_name(params->imgfmt));
return -1;
}
@@ -187,12 +167,10 @@ static int reinit(struct gl_hwdec *hw, struct mp_image_params *params)
return ret;
gl->GenTextures(4, p->gl_textures);
- for (int n = 0; n < 4; n++) {
- if (!p->sample_count[n])
- break;
+ for (int n = 0; n < desc.num_planes; n++) {
+ const struct gl_format *fmt = desc.planes[n];
- const struct gl_format *fmt =
- gl_find_unorm_format(gl, p->sample_width, p->sample_count[n]);
+ p->plane_bytes[n] = gl_bytes_per_pixel(fmt->format, fmt->type);
gl->BindTexture(GL_TEXTURE_2D, p->gl_textures[n]);
GLenum filter = GL_NEAREST;
@@ -270,10 +248,7 @@ static int map_frame(struct gl_hwdec *hw, struct mp_image *hw_image,
*out_frame = (struct gl_hwdec_frame) { 0, };
- for (int n = 0; n < 4; n++) {
- if (!p->sample_count[n])
- break;
-
+ for (int n = 0; n < p->layout.num_planes; n++) {
// widthInBytes must account for the chroma plane
// elements being two samples wide.
CUDA_MEMCPY2D cpy = {
@@ -283,8 +258,7 @@ static int map_frame(struct gl_hwdec *hw, struct mp_image *hw_image,
.srcPitch = hw_image->stride[n],
.srcY = 0,
.dstArray = p->cu_array[n],
- .WidthInBytes = mp_image_plane_w(&p->layout, n) *
- p->sample_count[n] * p->sample_width,
+ .WidthInBytes = mp_image_plane_w(&p->layout, n) * p->plane_bytes[n],
.Height = mp_image_plane_h(&p->layout, n),
};
ret = CHECK_CU(cuMemcpy2D(&cpy));
diff --git a/video/out/opengl/hwdec_d3d11egl.c b/video/out/opengl/hwdec_d3d11egl.c
index 0ca954b..1e0af02 100644
--- a/video/out/opengl/hwdec_d3d11egl.c
+++ b/video/out/opengl/hwdec_d3d11egl.c
@@ -290,7 +290,13 @@ static int map_frame(struct gl_hwdec *hw, struct mp_image *hw_image,
};
if (!p->StreamPostD3DTextureNV12ANGLE(p->egl_display, p->egl_stream,
(void *)d3d_tex, attrs))
- return -1;
+ {
+ // ANGLE changed the enum ID of this without warning at one point.
+ attrs[0] = attrs[0] == 0x33AB ? 0x3AAB : 0x33AB;
+ if (!p->StreamPostD3DTextureNV12ANGLE(p->egl_display, p->egl_stream,
+ (void *)d3d_tex, attrs))
+ return -1;
+ }
if (!p->StreamConsumerAcquireKHR(p->egl_display, p->egl_stream))
return -1;
diff --git a/video/out/opengl/hwdec_dxva2.c b/video/out/opengl/hwdec_dxva2.c
deleted file mode 100644
index d832bb4..0000000
--- a/video/out/opengl/hwdec_dxva2.c
+++ /dev/null
@@ -1,68 +0,0 @@
-#include <d3d9.h>
-
-#include "common/common.h"
-
-#include "hwdec.h"
-#include "utils.h"
-#include "video/hwdec.h"
-
-// This does not provide real (zero-copy) interop - it merely exists for
-// making sure the same D3D device is used for decoding and display, which
-// may help with OpenGL fullscreen mode.
-
-struct priv {
- struct mp_hwdec_ctx hwctx;
-};
-
-static void destroy(struct gl_hwdec *hw)
-{
- struct priv *p = hw->priv;
- hwdec_devices_remove(hw->devs, &p->hwctx);
- if (p->hwctx.ctx)
- IDirect3DDevice9_Release((IDirect3DDevice9 *)p->hwctx.ctx);
-}
-
-static int create(struct gl_hwdec *hw)
-{
- GL *gl = hw->gl;
- if (!gl->MPGetNativeDisplay)
- return -1;
-
- struct priv *p = talloc_zero(hw, struct priv);
- hw->priv = p;
-
- IDirect3DDevice9 *d3d = gl->MPGetNativeDisplay("IDirect3DDevice9");
- if (!d3d)
- return -1;
-
- MP_VERBOSE(hw, "Using libmpv supplied device %p.\n", d3d);
-
- p->hwctx = (struct mp_hwdec_ctx){
- .type = HWDEC_DXVA2_COPY,
- .driver_name = hw->driver->name,
- .ctx = d3d,
- };
- hwdec_devices_add(hw->devs, &p->hwctx);
- return 0;
-}
-
-static int reinit(struct gl_hwdec *hw, struct mp_image_params *params)
-{
- return -1;
-}
-
-static int map_frame(struct gl_hwdec *hw, struct mp_image *hw_image,
- struct gl_hwdec_frame *out_frame)
-{
- return -1;
-}
-
-const struct gl_hwdec_driver gl_hwdec_dxva2 = {
- .name = "dxva2-dummy",
- .api = HWDEC_DXVA2_COPY,
- .imgfmt = -1,
- .create = create,
- .reinit = reinit,
- .map_frame = map_frame,
- .destroy = destroy,
-};
diff --git a/video/out/opengl/hwdec_ios.m b/video/out/opengl/hwdec_ios.m
new file mode 100644
index 0000000..3ab532e
--- /dev/null
+++ b/video/out/opengl/hwdec_ios.m
@@ -0,0 +1,214 @@
+/*
+ * Copyright (c) 2013 Stefano Pigozzi <stefano.pigozzi@gmail.com>
+ * 2017 Aman Gupta <ffmpeg@tmm1.net>
+ *
+ * This file is part of mpv.
+ *
+ * mpv is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * mpv is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+
+#include <CoreVideo/CoreVideo.h>
+#include <OpenGLES/EAGL.h>
+
+#include "video/mp_image_pool.h"
+#include "video/vt.h"
+#include "formats.h"
+#include "hwdec.h"
+
+struct priv {
+ struct mp_hwdec_ctx hwctx;
+
+ CVPixelBufferRef pbuf;
+ CVOpenGLESTextureCacheRef gl_texture_cache;
+ CVOpenGLESTextureRef gl_planes[MP_MAX_PLANES];
+ struct gl_imgfmt_desc desc;
+};
+
+static bool check_hwdec(struct gl_hwdec *hw)
+{
+ if (hw->gl->es < 200) {
+ MP_ERR(hw, "need OpenGLES 2.0 for CVOpenGLESTextureCacheCreateTextureFromImage()\n");
+ return false;
+ }
+
+ if ([EAGLContext currentContext] == nil) {
+ MP_ERR(hw, "need a current EAGLContext set\n");
+ return false;
+ }
+
+ return true;
+}
+
+static int create_hwdec(struct gl_hwdec *hw)
+{
+ if (!check_hwdec(hw))
+ return -1;
+
+ struct priv *p = talloc_zero(hw, struct priv);
+ hw->priv = p;
+
+ CVReturn err = CVOpenGLESTextureCacheCreate(
+ kCFAllocatorDefault,
+ NULL,
+ [EAGLContext currentContext],
+ NULL,
+ &p->gl_texture_cache);
+
+ if (err != noErr) {
+ MP_ERR(hw, "Failure in CVOpenGLESTextureCacheCreate: %d\n", err);
+ return -1;
+ }
+
+ p->hwctx = (struct mp_hwdec_ctx){
+ .type = HWDEC_VIDEOTOOLBOX,
+ .download_image = mp_vt_download_image,
+ .ctx = &p->hwctx,
+ };
+ hwdec_devices_add(hw->devs, &p->hwctx);
+
+ return 0;
+}
+
+static int reinit(struct gl_hwdec *hw, struct mp_image_params *params)
+{
+ struct priv *p = hw->priv;
+ assert(params->imgfmt == hw->driver->imgfmt);
+
+ if (!params->hw_subfmt) {
+ MP_ERR(hw, "Unsupported CVPixelBuffer format.\n");
+ return -1;
+ }
+
+ if (!gl_get_imgfmt_desc(hw->gl, params->hw_subfmt, &p->desc)) {
+ MP_ERR(hw, "Unsupported texture format.\n");
+ return -1;
+ }
+
+ params->imgfmt = params->hw_subfmt;
+ params->hw_subfmt = 0;
+ return 0;
+}
+
+static void cleanup_textures(struct gl_hwdec *hw)
+{
+ struct priv *p = hw->priv;
+ int i;
+
+ for (i = 0; i < MP_MAX_PLANES; i++) {
+ if (p->gl_planes[i]) {
+ CFRelease(p->gl_planes[i]);
+ p->gl_planes[i] = NULL;
+ }
+ }
+
+ CVOpenGLESTextureCacheFlush(p->gl_texture_cache, 0);
+}
+
+static int map_frame(struct gl_hwdec *hw, struct mp_image *hw_image,
+ struct gl_hwdec_frame *out_frame)
+{
+ struct priv *p = hw->priv;
+ GL *gl = hw->gl;
+
+ CVPixelBufferRelease(p->pbuf);
+ p->pbuf = (CVPixelBufferRef)hw_image->planes[3];
+ CVPixelBufferRetain(p->pbuf);
+
+ const bool planar = CVPixelBufferIsPlanar(p->pbuf);
+ const int planes = CVPixelBufferGetPlaneCount(p->pbuf);
+ assert((planar && planes == p->desc.num_planes) || p->desc.num_planes == 1);
+
+ cleanup_textures(hw);
+
+ for (int i = 0; i < p->desc.num_planes; i++) {
+ const struct gl_format *fmt = p->desc.planes[i];
+ GLenum format = fmt->format;
+ GLenum internal_format = fmt->internal_format;
+
+ if (hw->gl->es >= 300) {
+ // In GLES3 mode, CVOpenGLESTextureCacheCreateTextureFromImage()
+ // will return error -6683 unless invoked with GL_LUMINANCE and
+ // GL_LUMINANCE_ALPHA (http://stackoverflow.com/q/36213994/332798)
+ if (format == GL_RED) {
+ format = internal_format = GL_LUMINANCE;
+ } else if (format == GL_RG) {
+ format = internal_format = GL_LUMINANCE_ALPHA;
+ }
+ }
+
+ CVReturn err = CVOpenGLESTextureCacheCreateTextureFromImage(
+ kCFAllocatorDefault,
+ p->gl_texture_cache,
+ p->pbuf,
+ NULL,
+ GL_TEXTURE_2D,
+ internal_format,
+ CVPixelBufferGetWidthOfPlane(p->pbuf, i),
+ CVPixelBufferGetHeightOfPlane(p->pbuf, i),
+ format,
+ fmt->type,
+ i,
+ &p->gl_planes[i]);
+
+ if (err != noErr) {
+ MP_ERR(hw, "error creating texture for plane %d: %d\n", i, err);
+ return -1;
+ }
+
+ gl->BindTexture(GL_TEXTURE_2D, CVOpenGLESTextureGetName(p->gl_planes[i]));
+ gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ gl->BindTexture(GL_TEXTURE_2D, 0);
+
+ out_frame->planes[i] = (struct gl_hwdec_plane){
+ .gl_texture = CVOpenGLESTextureGetName(p->gl_planes[i]),
+ .gl_target = GL_TEXTURE_2D,
+ .gl_format = format,
+ .tex_w = CVPixelBufferGetWidthOfPlane(p->pbuf, i),
+ .tex_h = CVPixelBufferGetHeightOfPlane(p->pbuf, i),
+ };
+ }
+
+ snprintf(out_frame->swizzle, sizeof(out_frame->swizzle), "%s",
+ p->desc.swizzle);
+
+ return 0;
+}
+
+static void destroy(struct gl_hwdec *hw)
+{
+ struct priv *p = hw->priv;
+
+ cleanup_textures(hw);
+
+ CVPixelBufferRelease(p->pbuf);
+ CFRelease(p->gl_texture_cache);
+ p->gl_texture_cache = NULL;
+
+ hwdec_devices_remove(hw->devs, &p->hwctx);
+}
+
+const struct gl_hwdec_driver gl_hwdec_videotoolbox = {
+ .name = "videotoolbox",
+ .api = HWDEC_VIDEOTOOLBOX,
+ .imgfmt = IMGFMT_VIDEOTOOLBOX,
+ .create = create_hwdec,
+ .reinit = reinit,
+ .map_frame = map_frame,
+ .destroy = destroy,
+};
diff --git a/video/out/opengl/hwdec_osx.c b/video/out/opengl/hwdec_osx.c
index e0fc5ab..463f9c3 100644
--- a/video/out/opengl/hwdec_osx.c
+++ b/video/out/opengl/hwdec_osx.c
@@ -25,126 +25,19 @@
#include <OpenGL/CGLIOSurface.h>
#include "video/mp_image_pool.h"
+#include "video/vt.h"
+#include "formats.h"
#include "hwdec.h"
-#include "common/global.h"
-#include "options/options.h"
-
-struct vt_gl_plane_format {
- GLenum gl_format;
- GLenum gl_type;
- GLenum gl_internal_format;
- char swizzle[5];
-};
-
-struct vt_format {
- uint32_t cvpixfmt;
- int imgfmt;
- int planes;
- struct vt_gl_plane_format gl[MP_MAX_PLANES];
-};
struct priv {
struct mp_hwdec_ctx hwctx;
- struct mp_vt_ctx vtctx;
CVPixelBufferRef pbuf;
GLuint gl_planes[MP_MAX_PLANES];
-};
-static struct vt_format vt_formats[] = {
- {
- .cvpixfmt = kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange,
- .imgfmt = IMGFMT_NV12,
- .planes = 2,
- .gl = {
- { GL_RED, GL_UNSIGNED_BYTE, GL_RED },
- { GL_RG, GL_UNSIGNED_BYTE, GL_RG } ,
- }
- },
- {
- .cvpixfmt = kCVPixelFormatType_422YpCbCr8,
- .imgfmt = IMGFMT_UYVY,
- .planes = 1,
- .gl = {
- { GL_RGB_422_APPLE, GL_UNSIGNED_SHORT_8_8_APPLE, GL_RGB, "gbra" }
- }
- },
- {
- .cvpixfmt = kCVPixelFormatType_420YpCbCr8Planar,
- .imgfmt = IMGFMT_420P,
- .planes = 3,
- .gl = {
- { GL_RED, GL_UNSIGNED_BYTE, GL_RED },
- { GL_RED, GL_UNSIGNED_BYTE, GL_RED },
- { GL_RED, GL_UNSIGNED_BYTE, GL_RED },
- }
- },
- {
- .cvpixfmt = kCVPixelFormatType_32BGRA,
- .imgfmt = IMGFMT_RGB0,
- .planes = 1,
- .gl = {
- { GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, GL_RGBA }
- }
- },
+ struct gl_imgfmt_desc desc;
};
-static struct vt_format *vt_get_gl_format(uint32_t cvpixfmt)
-{
- for (int i = 0; i < MP_ARRAY_SIZE(vt_formats); i++) {
- if (vt_formats[i].cvpixfmt == cvpixfmt)
- return &vt_formats[i];
- }
- return NULL;
-}
-
-static struct vt_format *vt_get_gl_format_from_imgfmt(uint32_t imgfmt)
-{
- for (int i = 0; i < MP_ARRAY_SIZE(vt_formats); i++) {
- if (vt_formats[i].imgfmt == imgfmt)
- return &vt_formats[i];
- }
- return NULL;
-}
-
-static struct mp_image *download_image(struct mp_hwdec_ctx *ctx,
- struct mp_image *hw_image,
- struct mp_image_pool *swpool)
-{
- if (hw_image->imgfmt != IMGFMT_VIDEOTOOLBOX)
- return NULL;
-
- struct mp_image *image = NULL;
- CVPixelBufferRef pbuf = (CVPixelBufferRef)hw_image->planes[3];
- CVPixelBufferLockBaseAddress(pbuf, kCVPixelBufferLock_ReadOnly);
- size_t width = CVPixelBufferGetWidth(pbuf);
- size_t height = CVPixelBufferGetHeight(pbuf);
- uint32_t cvpixfmt = CVPixelBufferGetPixelFormatType(pbuf);
- struct vt_format *f = vt_get_gl_format(cvpixfmt);
- if (!f)
- goto unlock;
-
- struct mp_image img = {0};
- mp_image_setfmt(&img, f->imgfmt);
- mp_image_set_size(&img, width, height);
-
- for (int i = 0; i < f->planes; i++) {
- void *base = CVPixelBufferGetBaseAddressOfPlane(pbuf, i);
- size_t stride = CVPixelBufferGetBytesPerRowOfPlane(pbuf, i);
- img.planes[i] = base;
- img.stride[i] = stride;
- }
-
- mp_image_copy_attributes(&img, hw_image);
-
- image = mp_image_pool_new_copy(swpool, &img);
-
-unlock:
- CVPixelBufferUnlockBaseAddress(pbuf, kCVPixelBufferLock_ReadOnly);
-
- return image;
-}
-
static bool check_hwdec(struct gl_hwdec *hw)
{
if (hw->gl->version < 300) {
@@ -160,14 +53,6 @@ static bool check_hwdec(struct gl_hwdec *hw)
return true;
}
-static uint32_t get_vt_fmt(struct mp_vt_ctx *vtctx)
-{
- struct gl_hwdec *hw = vtctx->priv;
- struct vt_format *f =
- vt_get_gl_format_from_imgfmt(hw->global->opts->videotoolbox_format);
- return f ? f->cvpixfmt : (uint32_t)-1;
-}
-
static int create(struct gl_hwdec *hw)
{
if (!check_hwdec(hw))
@@ -178,14 +63,10 @@ static int create(struct gl_hwdec *hw)
hw->gl->GenTextures(MP_MAX_PLANES, p->gl_planes);
- p->vtctx = (struct mp_vt_ctx){
- .priv = hw,
- .get_vt_fmt = get_vt_fmt,
- };
p->hwctx = (struct mp_hwdec_ctx){
.type = HWDEC_VIDEOTOOLBOX,
- .download_image = download_image,
- .ctx = &p->vtctx,
+ .download_image = mp_vt_download_image,
+ .ctx = &p->hwctx,
};
hwdec_devices_add(hw->devs, &p->hwctx);
@@ -194,15 +75,21 @@ static int create(struct gl_hwdec *hw)
static int reinit(struct gl_hwdec *hw, struct mp_image_params *params)
{
+ struct priv *p = hw->priv;
+
assert(params->imgfmt == hw->driver->imgfmt);
- struct vt_format *f = vt_get_gl_format_from_imgfmt(params->hw_subfmt);
- if (!f) {
+ if (!params->hw_subfmt) {
MP_ERR(hw, "Unsupported CVPixelBuffer format.\n");
return -1;
}
- params->imgfmt = f->imgfmt;
+ if (!gl_get_imgfmt_desc(hw->gl, params->hw_subfmt, &p->desc)) {
+ MP_ERR(hw, "Unsupported texture format.\n");
+ return -1;
+ }
+
+ params->imgfmt = params->hw_subfmt;
params->hw_subfmt = 0;
return 0;
}
@@ -222,28 +109,23 @@ static int map_frame(struct gl_hwdec *hw, struct mp_image *hw_image,
return -1;
}
- uint32_t cvpixfmt = CVPixelBufferGetPixelFormatType(p->pbuf);
- struct vt_format *f = vt_get_gl_format(cvpixfmt);
- if (!f) {
- MP_ERR(hw, "CVPixelBuffer has unsupported format type\n");
- return -1;
- }
-
const bool planar = CVPixelBufferIsPlanar(p->pbuf);
const int planes = CVPixelBufferGetPlaneCount(p->pbuf);
- assert(planar && planes == f->planes || f->planes == 1);
+ assert((planar && planes == p->desc.num_planes) || p->desc.num_planes == 1);
GLenum gl_target = GL_TEXTURE_RECTANGLE;
- for (int i = 0; i < f->planes; i++) {
+ for (int i = 0; i < p->desc.num_planes; i++) {
+ const struct gl_format *fmt = p->desc.planes[i];
+
gl->BindTexture(gl_target, p->gl_planes[i]);
CGLError err = CGLTexImageIOSurface2D(
CGLGetCurrentContext(), gl_target,
- f->gl[i].gl_internal_format,
+ fmt->internal_format,
IOSurfaceGetWidthOfPlane(surface, i),
IOSurfaceGetHeightOfPlane(surface, i),
- f->gl[i].gl_format, f->gl[i].gl_type, surface, i);
+ fmt->format, fmt->type, surface, i);
if (err != kCGLNoError)
MP_ERR(hw, "error creating IOSurface texture for plane %d: %s (%x)\n",
@@ -257,10 +139,11 @@ static int map_frame(struct gl_hwdec *hw, struct mp_image *hw_image,
.tex_w = IOSurfaceGetWidthOfPlane(surface, i),
.tex_h = IOSurfaceGetHeightOfPlane(surface, i),
};
- snprintf(out_frame->planes[i].swizzle, sizeof(out_frame->planes[i].swizzle),
- "%s", f->gl[i].swizzle);
}
+ snprintf(out_frame->swizzle, sizeof(out_frame->swizzle), "%s",
+ p->desc.swizzle);
+
return 0;
}
diff --git a/video/out/opengl/hwdec_vaegl.c b/video/out/opengl/hwdec_vaegl.c
index a66ad7f..899d3ad 100644
--- a/video/out/opengl/hwdec_vaegl.c
+++ b/video/out/opengl/hwdec_vaegl.c
@@ -34,6 +34,7 @@
#include "video/img_fourcc.h"
#include "video/mp_image_pool.h"
#include "common.h"
+#include "formats.h"
#ifndef GL_OES_EGL_image
typedef void* GLeglImageOES;
@@ -319,7 +320,10 @@ static int map_frame(struct gl_hwdec *hw, struct mp_image *hw_image,
struct mp_image layout = {0};
mp_image_set_params(&layout, &hw_image->params);
mp_image_setfmt(&layout, p->current_mpfmt);
- struct mp_imgfmt_desc fmt = layout.fmt;
+
+ struct gl_imgfmt_desc desc;
+ if (!gl_get_imgfmt_desc(gl, p->current_mpfmt, &desc))
+ goto err;
int drm_fmts[8] = {
// 1 bytes per component, 1-4 components
@@ -338,21 +342,21 @@ static int map_frame(struct gl_hwdec *hw, struct mp_image *hw_image,
int attribs[20] = {EGL_NONE};
int num_attribs = 0;
- int fmt_index = -1;
- int cbits = fmt.component_bits;
- if ((fmt.flags & (MP_IMGFLAG_YUV_P | MP_IMGFLAG_YUV_NV)) &&
- (fmt.flags & MP_IMGFLAG_NE) && cbits >= 8 && cbits <= 16)
- {
- // Regular planar and semi-planar formats.
- fmt_index = fmt.components[n] - 1 + 4 * ((cbits + 7) / 8 - 1);
- } else if (fmt.id == IMGFMT_RGB0 || fmt.id == IMGFMT_BGR0) {
- fmt_index = 3 + 4 * ((cbits + 7) / 8 - 1);
- }
+ const struct gl_format *fmt = desc.planes[n];
+ if (gl_format_type(fmt) != MPGL_TYPE_UNORM)
+ goto err;
- if (fmt_index < 0 || fmt_index >= 8 || !drm_fmts[fmt_index])
+ int n_comp = gl_format_components(fmt->format);
+ int comp_s = gl_component_size(fmt->type);
+ if (!gl_format_is_regular(fmt))
+ goto err;
+ if (n_comp < 1 || n_comp > 3 || comp_s < 1 || comp_s > 2)
+ goto err;
+ int drm_fmt = drm_fmts[n_comp - 1 + (comp_s - 1) * 4];
+ if (!drm_fmt)
goto err;
- ADD_ATTRIB(EGL_LINUX_DRM_FOURCC_EXT, drm_fmts[fmt_index]);
+ ADD_ATTRIB(EGL_LINUX_DRM_FOURCC_EXT, drm_fmt);
ADD_ATTRIB(EGL_WIDTH, mp_image_plane_w(&layout, n));
ADD_ATTRIB(EGL_HEIGHT, mp_image_plane_h(&layout, n));
ADD_ATTRIB(EGL_DMA_BUF_PLANE0_FD_EXT, buffer_info.handle);
@@ -379,6 +383,8 @@ static int map_frame(struct gl_hwdec *hw, struct mp_image *hw_image,
if (va_image->format.fourcc == VA_FOURCC_YV12)
MPSWAP(struct gl_hwdec_plane, out_frame->planes[1], out_frame->planes[2]);
+ snprintf(out_frame->swizzle, sizeof(out_frame->swizzle), "%s", desc.swizzle);
+
return 0;
err:
@@ -428,12 +434,12 @@ static void determine_working_formats(struct gl_hwdec *hw)
}
for (int n = 0; fc->valid_sw_formats[n] != AV_PIX_FMT_NONE; n++) {
AVBufferRef *fref = NULL;
+ struct mp_image *s = NULL;
+ AVFrame *frame = NULL;
fref = av_hwframe_ctx_alloc(p->ctx->av_device_ref);
if (!fref)
goto err;
AVHWFramesContext *fctx = (void *)fref->data;
- struct mp_image *s = NULL;
- AVFrame *frame = NULL;
fctx->format = AV_PIX_FMT_VAAPI;
fctx->sw_format = fc->valid_sw_formats[n];
fctx->width = 128;
diff --git a/video/out/opengl/lcms.c b/video/out/opengl/lcms.c
index 5bd1954..cabcb16 100644
--- a/video/out/opengl/lcms.c
+++ b/video/out/opengl/lcms.c
@@ -77,9 +77,9 @@ static int validate_3dlut_size_opt(struct mp_log *log, const m_option_t *opt,
#define OPT_BASE_STRUCT struct mp_icc_opts
const struct m_sub_options mp_icc_conf = {
.opts = (const m_option_t[]) {
- OPT_STRING("icc-profile", profile, 0),
+ OPT_STRING("icc-profile", profile, M_OPT_FILE),
OPT_FLAG("icc-profile-auto", profile_auto, 0),
- OPT_STRING("icc-cache-dir", cache_dir, 0),
+ OPT_STRING("icc-cache-dir", cache_dir, M_OPT_FILE),
OPT_INT("icc-intent", intent, 0),
OPT_INTRANGE("icc-contrast", contrast, 0, 0, 100000),
OPT_STRING_VALIDATE("icc-3dlut-size", size_str, 0, validate_3dlut_size_opt),
diff --git a/video/out/opengl/user_shaders.c b/video/out/opengl/user_shaders.c
index 112012f..7e1e5f4 100644
--- a/video/out/opengl/user_shaders.c
+++ b/video/out/opengl/user_shaders.c
@@ -15,9 +15,9 @@
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <ctype.h>
#include <assert.h>
+#include "misc/ctype.h"
#include "user_shaders.h"
static bool parse_rpn_szexpr(struct bstr line, struct szexp out[MAX_SZEXP_SIZE])
@@ -56,7 +56,7 @@ static bool parse_rpn_szexpr(struct bstr line, struct szexp out[MAX_SZEXP_SIZE])
case '<': exp->tag = SZEXP_OP2; exp->val.op = SZEXP_OP_LT; continue;
}
- if (isdigit(word.start[0])) {
+ if (mp_isdigit(word.start[0])) {
exp->tag = SZEXP_CONST;
if (bstr_sscanf(word, "%f", &exp->val.cval) != 1)
return false;
diff --git a/video/out/opengl/utils.c b/video/out/opengl/utils.c
index aa43728..7e8680f 100644
--- a/video/out/opengl/utils.c
+++ b/video/out/opengl/utils.c
@@ -23,7 +23,15 @@
#include <stdarg.h>
#include <assert.h>
+#include <libavutil/sha.h>
+#include <libavutil/intreadwrite.h>
+#include <libavutil/mem.h>
+
+#include "osdep/io.h"
+
#include "common/common.h"
+#include "options/path.h"
+#include "stream/stream.h"
#include "formats.h"
#include "utils.h"
@@ -100,15 +108,15 @@ void gl_upload_tex(GL *gl, GLenum target, GLenum format, GLenum type,
gl->PixelStorei(GL_UNPACK_ALIGNMENT, 4);
}
-mp_image_t *gl_read_window_contents(GL *gl, int w, int h)
+mp_image_t *gl_read_fbo_contents(GL *gl, int fbo, int w, int h)
{
if (gl->es)
return NULL; // ES can't read from front buffer
mp_image_t *image = mp_image_alloc(IMGFMT_RGB24, w, h);
if (!image)
return NULL;
- gl->BindFramebuffer(GL_FRAMEBUFFER, gl->main_fb);
- GLenum obj = gl->main_fb ? GL_COLOR_ATTACHMENT0 : GL_FRONT;
+ gl->BindFramebuffer(GL_FRAMEBUFFER, fbo);
+ GLenum obj = fbo ? GL_COLOR_ATTACHMENT0 : GL_FRONT;
gl->PixelStorei(GL_PACK_ALIGNMENT, 1);
gl->ReadBuffer(obj);
//flip image while reading (and also avoid stride-related trouble)
@@ -458,7 +466,6 @@ struct sc_entry {
int num_uniforms;
bstr frag;
bstr vert;
- struct gl_vao *vao;
};
struct gl_shader_cache {
@@ -489,6 +496,10 @@ struct gl_shader_cache {
// temporary buffers (avoids frequent reallocations)
bstr tmp[5];
+
+ // For the disk-cache.
+ char *cache_dir;
+ struct mpv_global *global; // can be NULL
};
struct gl_shader_cache *gl_sc_create(GL *gl, struct mp_log *log)
@@ -622,7 +633,7 @@ static struct sc_uniform *find_uniform(struct gl_shader_cache *sc,
return &sc->uniforms[sc->num_uniforms - 1];
}
-const char* mp_sampler_type(GLenum texture_target)
+const char *mp_sampler_type(GLenum texture_target)
{
switch (texture_target) {
case GL_TEXTURE_1D: return "sampler1D";
@@ -818,6 +829,14 @@ static void update_uniform(GL *gl, struct sc_entry *e, struct sc_uniform *u, int
}
}
+void gl_sc_set_cache_dir(struct gl_shader_cache *sc, struct mpv_global *global,
+ const char *dir)
+{
+ talloc_free(sc->cache_dir);
+ sc->cache_dir = talloc_strdup(sc, dir);
+ sc->global = global;
+}
+
static void compile_attach_shader(struct gl_shader_cache *sc, GLuint program,
GLenum type, const char *source)
{
@@ -883,18 +902,10 @@ static void link_shader(struct gl_shader_cache *sc, GLuint program)
sc->error_state = true;
}
-static GLuint create_program(struct gl_shader_cache *sc, const char *vertex,
- const char *frag)
+static GLuint compile_program(struct gl_shader_cache *sc, const char *vertex,
+ const char *frag)
{
GL *gl = sc->gl;
- MP_VERBOSE(sc, "recompiling a shader program:\n");
- if (sc->header_text.len) {
- MP_VERBOSE(sc, "header:\n");
- mp_log_source(sc->log, MSGL_V, sc->header_text.start);
- MP_VERBOSE(sc, "body:\n");
- }
- if (sc->text.len)
- mp_log_source(sc->log, MSGL_V, sc->text.start);
GLuint prog = gl->CreateProgram();
compile_attach_shader(sc, prog, GL_VERTEX_SHADER, vertex);
compile_attach_shader(sc, prog, GL_FRAGMENT_SHADER, frag);
@@ -907,6 +918,105 @@ static GLuint create_program(struct gl_shader_cache *sc, const char *vertex,
return prog;
}
+static GLuint load_program(struct gl_shader_cache *sc, const char *vertex,
+ const char *frag)
+{
+ GL *gl = sc->gl;
+
+ MP_VERBOSE(sc, "new shader program:\n");
+ if (sc->header_text.len) {
+ MP_VERBOSE(sc, "header:\n");
+ mp_log_source(sc->log, MSGL_V, sc->header_text.start);
+ MP_VERBOSE(sc, "body:\n");
+ }
+ if (sc->text.len)
+ mp_log_source(sc->log, MSGL_V, sc->text.start);
+
+ if (!sc->cache_dir || !sc->cache_dir[0] || !gl->ProgramBinary)
+ return compile_program(sc, vertex, frag);
+
+ // Try to load it from a disk cache, or compiling + saving it.
+
+ GLuint prog = 0;
+ void *tmp = talloc_new(NULL);
+ char *dir = mp_get_user_path(tmp, sc->global, sc->cache_dir);
+
+ struct AVSHA *sha = av_sha_alloc();
+ if (!sha)
+ abort();
+ av_sha_init(sha, 256);
+
+ av_sha_update(sha, vertex, strlen(vertex) + 1);
+ av_sha_update(sha, frag, strlen(frag) + 1);
+
+ // In theory, the array could change order, breaking old binaries.
+ for (int n = 0; sc->vao->entries[n].name; n++) {
+ av_sha_update(sha, sc->vao->entries[n].name,
+ strlen(sc->vao->entries[n].name) + 1);
+ }
+
+ uint8_t hash[256 / 8];
+ av_sha_final(sha, hash);
+ av_free(sha);
+
+ char hashstr[256 / 8 * 2 + 1];
+ for (int n = 0; n < 256 / 8; n++)
+ snprintf(hashstr + n * 2, sizeof(hashstr) - n * 2, "%02X", hash[n]);
+
+ const char *header = "mpv shader cache v1\n";
+ size_t header_size = strlen(header) + 4;
+
+ char *filename = mp_path_join(tmp, dir, hashstr);
+ if (stat(filename, &(struct stat){0}) == 0) {
+ MP_VERBOSE(sc, "Trying to load shader from disk...\n");
+ struct bstr cachedata = stream_read_file(filename, tmp, sc->global,
+ 1000000000); // 1 GB
+ if (cachedata.len > header_size) {
+ GLenum format = AV_RL32(cachedata.start + header_size - 4);
+ prog = gl->CreateProgram();
+ gl_check_error(gl, sc->log, "before loading program");
+ gl->ProgramBinary(prog, format, cachedata.start + header_size,
+ cachedata.len - header_size);
+ gl->GetError(); // discard potential useless error
+ GLint status = 0;
+ gl->GetProgramiv(prog, GL_LINK_STATUS, &status);
+ if (!status) {
+ gl->DeleteProgram(prog);
+ prog = 0;
+ }
+ }
+ MP_VERBOSE(sc, "Loading cached shader %s.\n", prog ? "ok" : "failed");
+ }
+
+ if (!prog) {
+ prog = compile_program(sc, vertex, frag);
+
+ GLint size = 0;
+ gl->GetProgramiv(prog, GL_PROGRAM_BINARY_LENGTH, &size);
+ uint8_t *buffer = talloc_size(tmp, size + header_size);
+ GLsizei actual_size = 0;
+ GLenum binary_format = 0;
+ gl->GetProgramBinary(prog, size, &actual_size, &binary_format,
+ buffer + header_size);
+ memcpy(buffer, header, header_size - 4);
+ AV_WL32(buffer + header_size - 4, binary_format);
+
+ if (actual_size) {
+ mp_mkdirp(dir);
+
+ MP_VERBOSE(sc, "Writing shader cache file: %s\n", filename);
+ FILE *out = fopen(filename, "wb");
+ if (out) {
+ fwrite(buffer, header_size + actual_size, 1, out);
+ fclose(out);
+ }
+ }
+ }
+
+ talloc_free(tmp);
+ return prog;
+}
+
#define ADD(x, ...) bstr_xappend_asprintf(sc, (x), __VA_ARGS__)
#define ADD_BSTR(x, s) bstr_xappend(sc, (x), (s))
@@ -1031,7 +1141,7 @@ void gl_sc_generate(struct gl_shader_cache *sc)
}
// build vertex shader from vao and cache the locations of the uniform variables
if (!entry->gl_shader) {
- entry->gl_shader = create_program(sc, vert->start, frag->start);
+ entry->gl_shader = load_program(sc, vert->start, frag->start);
entry->num_uniforms = 0;
for (int n = 0; n < sc->num_uniforms; n++) {
struct sc_cached_uniform un = {
@@ -1061,7 +1171,7 @@ void gl_sc_generate(struct gl_shader_cache *sc)
// How many samples to keep around, for the sake of average and peak
// calculations. This corresponds to a few seconds (exact time variable)
-#define QUERY_SAMPLE_SIZE 256
+#define QUERY_SAMPLE_SIZE 256u
struct gl_timer {
GL *gl;
@@ -1249,3 +1359,53 @@ void gl_pbo_upload_uninit(struct gl_pbo_upload *pbo)
pbo->gl->DeleteBuffers(NUM_PBO_BUFFERS, &pbo->buffers[0]);
*pbo = (struct gl_pbo_upload){0};
}
+
+// The intention is to return the actual depth of any fixed point 16 bit
+// textures. (Actually tests only 1 format - hope that is good enough.)
+int gl_determine_16bit_tex_depth(GL *gl)
+{
+ const struct gl_format *fmt = gl_find_unorm_format(gl, 2, 1);
+ if (!gl->GetTexLevelParameteriv || !fmt) {
+ // ANGLE supports ES 3.0 and the extension, but lacks the function above.
+ if (gl->mpgl_caps & MPGL_CAP_EXT16)
+ return 16;
+ return -1;
+ }
+
+ GLuint tex;
+ gl->GenTextures(1, &tex);
+ gl->BindTexture(GL_TEXTURE_2D, tex);
+ gl->TexImage2D(GL_TEXTURE_2D, 0, fmt->internal_format, 64, 64, 0,
+ fmt->format, fmt->type, NULL);
+ GLenum pname = 0;
+ switch (fmt->format) {
+ case GL_RED: pname = GL_TEXTURE_RED_SIZE; break;
+ case GL_LUMINANCE: pname = GL_TEXTURE_LUMINANCE_SIZE; break;
+ }
+ GLint param = -1;
+ if (pname)
+ gl->GetTexLevelParameteriv(GL_TEXTURE_2D, 0, pname, &param);
+ gl->DeleteTextures(1, &tex);
+ return param;
+}
+
+int gl_get_fb_depth(GL *gl, int fbo)
+{
+ if ((gl->es < 300 && !gl->version) || !(gl->mpgl_caps & MPGL_CAP_FB))
+ return -1;
+
+ gl->BindFramebuffer(GL_FRAMEBUFFER, fbo);
+
+ GLenum obj = gl->version ? GL_BACK_LEFT : GL_BACK;
+ if (fbo)
+ obj = GL_COLOR_ATTACHMENT0;
+
+ GLint depth_g = -1;
+
+ gl->GetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, obj,
+ GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE, &depth_g);
+
+ gl->BindFramebuffer(GL_FRAMEBUFFER, 0);
+
+ return depth_g > 0 ? depth_g : -1;
+}
diff --git a/video/out/opengl/utils.h b/video/out/opengl/utils.h
index f4e522c..95eb1c4 100644
--- a/video/out/opengl/utils.h
+++ b/video/out/opengl/utils.h
@@ -30,7 +30,7 @@ void gl_upload_tex(GL *gl, GLenum target, GLenum format, GLenum type,
const void *dataptr, int stride,
int x, int y, int w, int h);
-mp_image_t *gl_read_window_contents(GL *gl, int w, int h);
+mp_image_t *gl_read_fbo_contents(GL *gl, int fbo, int w, int h);
const char* mp_sampler_type(GLenum texture_target);
@@ -171,7 +171,9 @@ void gl_sc_set_vao(struct gl_shader_cache *sc, struct gl_vao *vao);
void gl_sc_enable_extension(struct gl_shader_cache *sc, char *name);
void gl_sc_generate(struct gl_shader_cache *sc);
void gl_sc_reset(struct gl_shader_cache *sc);
-void gl_sc_unbind(struct gl_shader_cache *sc);
+struct mpv_global;
+void gl_sc_set_cache_dir(struct gl_shader_cache *sc, struct mpv_global *global,
+ const char *dir);
struct gl_timer;
@@ -200,4 +202,7 @@ void gl_pbo_upload_tex(struct gl_pbo_upload *pbo, GL *gl, bool use_pbo,
int x, int y, int w, int h);
void gl_pbo_upload_uninit(struct gl_pbo_upload *pbo);
+int gl_determine_16bit_tex_depth(GL *gl);
+int gl_get_fb_depth(GL *gl, int fbo);
+
#endif
diff --git a/video/out/opengl/video.c b/video/out/opengl/video.c
index f9f1f87..3189ec1 100644
--- a/video/out/opengl/video.c
+++ b/video/out/opengl/video.c
@@ -92,11 +92,9 @@ struct texplane {
int tex_w, tex_h;
GLint gl_internal_format;
GLenum gl_target;
- bool use_integer;
GLenum gl_format;
GLenum gl_type;
GLuint gl_texture;
- char swizzle[5];
bool flipped;
struct gl_pbo_upload pbo;
};
@@ -125,11 +123,10 @@ struct img_tex {
float multiplier; // multiplier to be used when sampling
GLuint gl_tex;
GLenum gl_target;
- bool use_integer;
+ GLenum gl_format;
int tex_w, tex_h; // source texture size
int w, h; // logical size (after transformation)
struct gl_transform transform; // rendering transformation
- char swizzle[5];
};
// A named img_tex, for user scripting purposes
@@ -272,34 +269,6 @@ struct gl_video {
bool broken_frame; // temporary error state
};
-struct packed_fmt_entry {
- int fmt;
- int8_t component_size;
- int8_t components[4]; // source component - 0 means unmapped
-};
-
-static const struct packed_fmt_entry mp_packed_formats[] = {
- // w R G B A
- {IMGFMT_Y8, 1, {1, 0, 0, 0}},
- {IMGFMT_Y16, 2, {1, 0, 0, 0}},
- {IMGFMT_YA8, 1, {1, 0, 0, 2}},
- {IMGFMT_YA16, 2, {1, 0, 0, 2}},
- {IMGFMT_ARGB, 1, {2, 3, 4, 1}},
- {IMGFMT_0RGB, 1, {2, 3, 4, 0}},
- {IMGFMT_BGRA, 1, {3, 2, 1, 4}},
- {IMGFMT_BGR0, 1, {3, 2, 1, 0}},
- {IMGFMT_ABGR, 1, {4, 3, 2, 1}},
- {IMGFMT_0BGR, 1, {4, 3, 2, 0}},
- {IMGFMT_RGBA, 1, {1, 2, 3, 4}},
- {IMGFMT_RGB0, 1, {1, 2, 3, 0}},
- {IMGFMT_BGR24, 1, {3, 2, 1, 0}},
- {IMGFMT_RGB24, 1, {1, 2, 3, 0}},
- {IMGFMT_RGB48, 2, {1, 2, 3, 0}},
- {IMGFMT_RGBA64, 2, {1, 2, 3, 4}},
- {IMGFMT_BGRA64, 2, {3, 2, 1, 4}},
- {0},
-};
-
static const struct gl_video_opts gl_video_opts_def = {
.dither_algo = DITHER_FRUIT,
.dither_depth = -1,
@@ -411,7 +380,7 @@ const struct m_sub_options gl_video_conf = {
({"no", BLEND_SUBS_NO},
{"yes", BLEND_SUBS_YES},
{"video", BLEND_SUBS_VIDEO})),
- OPT_STRINGLIST("opengl-shaders", user_shaders, 0),
+ OPT_STRINGLIST("opengl-shaders", user_shaders, M_OPT_FILE),
OPT_FLAG("deband", deband, 0),
OPT_SUBSTRUCT("deband", deband_opts, deband_conf, 0),
OPT_FLOAT("sharpen", unsharp, 0),
@@ -420,7 +389,7 @@ const struct m_sub_options gl_video_conf = {
OPT_SUBSTRUCT("", icc_opts, mp_icc_conf, 0),
OPT_CHOICE("opengl-early-flush", early_flush, 0,
({"no", 0}, {"yes", 1}, {"auto", -1})),
-
+ OPT_STRING("opengl-shader-cache-dir", shader_cache_dir, 0),
{0}
},
.size = sizeof(struct gl_video_opts),
@@ -630,7 +599,6 @@ static struct img_tex img_tex_fbo(struct fbotex *fbo, enum plane_type type,
.gl_tex = fbo->texture,
.gl_target = GL_TEXTURE_2D,
.multiplier = 1.0,
- .use_integer = false,
.tex_w = fbo->rw,
.tex_h = fbo->rh,
.w = fbo->lw,
@@ -737,15 +705,14 @@ static void pass_get_img_tex(struct gl_video *p, struct video_image *vimg,
.type = type,
.gl_tex = t->gl_texture,
.gl_target = t->gl_target,
+ .gl_format = t->gl_format,
.multiplier = tex_mul,
- .use_integer = t->use_integer,
.tex_w = t->tex_w,
.tex_h = t->tex_h,
.w = t->w,
.h = t->h,
.components = p->image_desc.components[n],
};
- snprintf(tex[n].swizzle, sizeof(tex[n].swizzle), "%s", t->swizzle);
get_transform(t->w, t->h, p->image_params.rotate, t->flipped,
&tex[n].transform);
if (p->image_params.rotate % 180 == 90)
@@ -843,7 +810,8 @@ static void init_video(struct gl_video *p)
plane->tex_w, plane->tex_h, 0,
plane->gl_format, plane->gl_type, NULL);
- int filter = plane->use_integer ? GL_NEAREST : GL_LINEAR;
+ int filter = gl_is_integer_format(plane->gl_format)
+ ? GL_NEAREST : GL_LINEAR;
gl->TexParameteri(gl_target, GL_TEXTURE_MIN_FILTER, filter);
gl->TexParameteri(gl_target, GL_TEXTURE_MAG_FILTER, filter);
gl->TexParameteri(gl_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
@@ -926,7 +894,7 @@ static void pass_prepare_src_tex(struct gl_video *p)
snprintf(texture_rot, sizeof(texture_rot), "texture_rot%d", n);
snprintf(pixel_size, sizeof(pixel_size), "pixel_size%d", n);
- if (s->use_integer) {
+ if (gl_is_integer_format(s->gl_format)) {
gl_sc_uniform_tex_ui(sc, texture_name, s->gl_tex);
} else {
gl_sc_uniform_tex(sc, texture_name, s->gl_target, s->gl_tex);
@@ -1009,6 +977,11 @@ static void finish_pass_fbo(struct gl_video *p, struct fbotex *dst_fbo,
&(struct mp_rect){0, 0, w, h});
}
+static const char *get_tex_swizzle(struct img_tex *img)
+{
+ return img->gl_format == GL_LUMINANCE_ALPHA ? "raaa" : "rgba";
+}
+
// Copy a texture to the vec4 color, while increasing offset. Also applies
// the texture multiplier to the sampled color
static void copy_img_tex(struct gl_video *p, int *offset, struct img_tex img)
@@ -1019,14 +992,14 @@ static void copy_img_tex(struct gl_video *p, int *offset, struct img_tex img)
int id = pass_bind(p, img);
char src[5] = {0};
char dst[5] = {0};
- const char *tex_fmt = img.swizzle[0] ? img.swizzle : "rgba";
+ const char *tex_fmt = get_tex_swizzle(&img);
const char *dst_fmt = "rgba";
for (int i = 0; i < count; i++) {
src[i] = tex_fmt[i];
dst[i] = dst_fmt[*offset + i];
}
- if (img.use_integer) {
+ if (gl_is_integer_format(img.gl_format)) {
uint64_t tex_max = 1ull << p->image_desc.component_full_bits;
img.multiplier *= 1.0 / (tex_max - 1);
}
@@ -1064,7 +1037,7 @@ static void hook_prelude(struct gl_video *p, const char *name, int id,
// Set up the sampling functions
GLSLHF("#define %s_tex(pos) (%f * vec4(texture(%s_raw, pos)).%s)\n",
- name, tex.multiplier, name, tex.swizzle[0] ? tex.swizzle : "rgba");
+ name, tex.multiplier, name, get_tex_swizzle(&tex));
// Since the extra matrix multiplication impacts performance,
// skip it unless the texture was actually rotated
@@ -1251,11 +1224,19 @@ static void load_shader(struct gl_video *p, struct bstr body)
gl_sc_hadd_bstr(p->sc, body);
gl_sc_uniform_f(p->sc, "random", (double)av_lfg_get(&p->lfg) / UINT32_MAX);
gl_sc_uniform_f(p->sc, "frame", p->frames_uploaded);
- gl_sc_uniform_vec2(p->sc, "image_size", (GLfloat[]){p->image_params.w,
- p->image_params.h});
+ gl_sc_uniform_vec2(p->sc, "input_size",
+ (GLfloat[]){(p->src_rect.x1 - p->src_rect.x0) *
+ p->texture_offset.m[0][0],
+ (p->src_rect.y1 - p->src_rect.y0) *
+ p->texture_offset.m[1][1]});
gl_sc_uniform_vec2(p->sc, "target_size",
(GLfloat[]){p->dst_rect.x1 - p->dst_rect.x0,
p->dst_rect.y1 - p->dst_rect.y0});
+ gl_sc_uniform_vec2(p->sc, "tex_offset",
+ (GLfloat[]){p->src_rect.x0 * p->texture_offset.m[0][0] +
+ p->texture_offset.t[0],
+ p->src_rect.y0 * p->texture_offset.m[1][1] +
+ p->texture_offset.t[1]});
}
// Semantic equality
@@ -1479,13 +1460,12 @@ static bool img_tex_equiv(struct img_tex a, struct img_tex b)
a.components == b.components &&
a.multiplier == b.multiplier &&
a.gl_target == b.gl_target &&
- a.use_integer == b.use_integer &&
+ a.gl_format == b.gl_format &&
a.tex_w == b.tex_w &&
a.tex_h == b.tex_h &&
a.w == b.w &&
a.h == b.h &&
- gl_transform_eq(a.transform, b.transform) &&
- strcmp(a.swizzle, b.swizzle) == 0;
+ gl_transform_eq(a.transform, b.transform);
}
static void pass_add_hook(struct gl_video *p, struct tex_hook hook)
@@ -1537,6 +1517,12 @@ static bool szexp_lookup(void *priv, struct bstr var, float size[2])
struct szexp_ctx *ctx = priv;
struct gl_video *p = ctx->p;
+ if (bstr_equals0(var, "NATIVE_CROPPED")) {
+ size[0] = (p->src_rect.x1 - p->src_rect.x0) * p->texture_offset.m[0][0];
+ size[1] = (p->src_rect.y1 - p->src_rect.y0) * p->texture_offset.m[1][1];
+ return true;
+ }
+
// The size of OUTPUT is determined. It could be useful for certain
// user shaders to skip passes.
if (bstr_equals0(var, "OUTPUT")) {
@@ -1701,7 +1687,7 @@ static void pass_read_video(struct gl_video *p)
// If any textures are still in integer format by this point, we need
// to introduce an explicit conversion pass to avoid breaking hooks/scaling
for (int n = 0; n < 4; n++) {
- if (tex[n].use_integer) {
+ if (gl_is_integer_format(tex[n].gl_format)) {
GLSLF("// use_integer fix for plane %d\n", n);
copy_img_tex(p, &(int){0}, tex[n]);
@@ -2671,6 +2657,17 @@ void gl_video_render_frame(struct gl_video *p, struct vo_frame *frame, int fbo)
return;
}
+ if (p->fb_depth == 0) {
+ debug_check_gl(p, "before retrieving framebuffer depth");
+ p->fb_depth = gl_get_fb_depth(gl, fbo);
+ debug_check_gl(p, "retrieving framebuffer depth");
+ if (p->fb_depth > 0) {
+ MP_VERBOSE(p, "Reported display depth: %d\n", p->fb_depth);
+ } else {
+ p->fb_depth = 8;
+ }
+ }
+
p->broken_frame = false;
gl->BindFramebuffer(GL_FRAMEBUFFER, fbo);
@@ -2933,10 +2930,11 @@ static bool gl_video_upload_image(struct gl_video *p, struct mp_image *mpi,
.tex_h = plane->tex_h,
.gl_target = plane->gl_target,
.gl_texture = plane->gl_texture,
+ .gl_format = plane->gl_format,
};
- snprintf(vimg->planes[n].swizzle, sizeof(vimg->planes[n].swizzle),
- "%s", plane->swizzle);
}
+ snprintf(p->color_swizzle, sizeof(p->color_swizzle), "%s",
+ gl_frame.swizzle);
} else {
MP_FATAL(p, "Mapping hardware decoded surface failed.\n");
goto error;
@@ -3068,7 +3066,13 @@ static void check_gl_features(struct gl_video *p)
.alpha_mode = p->opts.alpha_mode,
.use_rectangle = p->opts.use_rectangle,
.background = p->opts.background,
- .dither_algo = DITHER_NONE,
+ .dither_algo = p->opts.dither_algo,
+ .dither_depth = p->opts.dither_depth,
+ .dither_size = p->opts.dither_size,
+ .temporal_dither = p->opts.temporal_dither,
+ .temporal_dither_period = p->opts.temporal_dither_period,
+ .tex_pad_x = p->opts.tex_pad_x,
+ .tex_pad_y = p->opts.tex_pad_y,
.target_brightness = p->opts.target_brightness,
.hdr_tone_mapping = p->opts.hdr_tone_mapping,
.tone_mapping_param = p->opts.tone_mapping_param,
@@ -3144,57 +3148,10 @@ static void init_gl(struct gl_video *p)
gl_video_set_gl_state(p);
- // Test whether we can use 10 bit. Hope that testing a single format/channel
- // is good enough (instead of testing all 1-4 channels variants etc.).
- const struct gl_format *fmt = gl_find_unorm_format(gl, 2, 1);
- if (gl->GetTexLevelParameteriv && fmt) {
- GLuint tex;
- gl->GenTextures(1, &tex);
- gl->BindTexture(GL_TEXTURE_2D, tex);
- gl->TexImage2D(GL_TEXTURE_2D, 0, fmt->internal_format, 64, 64, 0,
- fmt->format, fmt->type, NULL);
- GLenum pname = 0;
- switch (fmt->format) {
- case GL_RED: pname = GL_TEXTURE_RED_SIZE; break;
- case GL_LUMINANCE: pname = GL_TEXTURE_LUMINANCE_SIZE; break;
- }
- GLint param = 0;
- if (pname)
- gl->GetTexLevelParameteriv(GL_TEXTURE_2D, 0, pname, &param);
- if (param) {
- MP_VERBOSE(p, "16 bit texture depth: %d.\n", (int)param);
- p->texture_16bit_depth = param;
- }
- gl->DeleteTextures(1, &tex);
- }
-
- if ((gl->es >= 300 || gl->version) && (gl->mpgl_caps & MPGL_CAP_FB)) {
- gl->BindFramebuffer(GL_FRAMEBUFFER, gl->main_fb);
-
- debug_check_gl(p, "before retrieving framebuffer depth");
-
- GLenum obj = gl->version ? GL_BACK_LEFT : GL_BACK;
- if (gl->main_fb)
- obj = GL_COLOR_ATTACHMENT0;
-
- GLint depth_r = -1, depth_g = -1, depth_b = -1;
-
- gl->GetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, obj,
- GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE, &depth_r);
- gl->GetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, obj,
- GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE, &depth_g);
- gl->GetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, obj,
- GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE, &depth_b);
-
- debug_check_gl(p, "retrieving framebuffer depth");
-
- MP_VERBOSE(p, "Reported display depth: R=%d, G=%d, B=%d\n",
- depth_r, depth_g, depth_b);
-
- p->fb_depth = depth_g > 0 ? depth_g : 8;
-
- gl->BindFramebuffer(GL_FRAMEBUFFER, 0);
- }
+ // Test whether we can use 10 bit.
+ p->texture_16bit_depth = gl_determine_16bit_tex_depth(gl);
+ if (p->texture_16bit_depth > 0)
+ MP_VERBOSE(p, "16 bit texture depth: %d.\n", p->texture_16bit_depth);
p->upload_timer = gl_timer_create(p->gl);
p->render_timer = gl_timer_create(p->gl);
@@ -3255,25 +3212,6 @@ bool gl_video_showing_interpolated_frame(struct gl_video *p)
return p->is_interpolated;
}
-// dest = src.<w> (always using 4 components)
-static void packed_fmt_swizzle(char w[5], const struct packed_fmt_entry *fmt)
-{
- for (int c = 0; c < 4; c++)
- w[c] = "rgba"[MPMAX(fmt->components[c] - 1, 0)];
- w[4] = '\0';
-}
-
-// Like gl_find_unorm_format(), but takes bits (not bytes), and if no fixed
-// point format is available, return an unsigned integer format.
-static const struct gl_format *find_plane_format(GL *gl, int bits, int n_channels)
-{
- int bytes = (bits + 7) / 8;
- const struct gl_format *f = gl_find_unorm_format(gl, bytes, n_channels);
- if (f)
- return f;
- return gl_find_uint_format(gl, bytes, n_channels);
-}
-
static void init_image_desc(struct gl_video *p, int fmt)
{
p->image_desc = mp_imgfmt_get_desc(fmt);
@@ -3291,85 +3229,17 @@ static void init_image_desc(struct gl_video *p, int fmt)
// test_only=false also initializes some rendering parameters accordingly
static bool init_format(struct gl_video *p, int fmt, bool test_only)
{
- struct GL *gl = p->gl;
-
- struct mp_imgfmt_desc desc = mp_imgfmt_get_desc(fmt);
- if (!desc.id)
+ int cdepth = mp_imgfmt_get_desc(fmt).component_bits;
+ if (cdepth > 8 && cdepth < 16 && p->texture_16bit_depth < 16)
return false;
- if (desc.num_planes > 4)
+ struct gl_imgfmt_desc desc;
+ if (!gl_get_imgfmt_desc(p->gl, fmt, &desc))
return false;
- const struct gl_format *plane_format[4] = {0};
- char color_swizzle[5] = "";
- const struct packed_fmt_entry *packed_format = {0};
-
- // YUV/planar formats
- if (desc.flags & (MP_IMGFLAG_YUV_P | MP_IMGFLAG_RGB_P)) {
- int bits = desc.component_bits;
- if ((desc.flags & MP_IMGFLAG_NE) && bits >= 8 && bits <= 16) {
- plane_format[0] = find_plane_format(gl, bits, 1);
- for (int n = 1; n < desc.num_planes; n++)
- plane_format[n] = plane_format[0];
- // RGB/planar
- if (desc.flags & MP_IMGFLAG_RGB_P)
- snprintf(color_swizzle, sizeof(color_swizzle), "brga");
- goto supported;
- }
- }
-
- // YUV/half-packed
- if (desc.flags & MP_IMGFLAG_YUV_NV) {
- int bits = desc.component_bits;
- if ((desc.flags & MP_IMGFLAG_NE) && bits >= 8 && bits <= 16) {
- plane_format[0] = find_plane_format(gl, bits, 1);
- plane_format[1] = find_plane_format(gl, bits, 2);
- if (desc.flags & MP_IMGFLAG_YUV_NV_SWAP)
- snprintf(color_swizzle, sizeof(color_swizzle), "rbga");
- goto supported;
- }
- }
-
- // XYZ (same organization as RGB packed, but requires conversion matrix)
- if (fmt == IMGFMT_XYZ12) {
- plane_format[0] = gl_find_unorm_format(gl, 2, 3);
- goto supported;
- }
-
- // Packed RGB(A) formats
- for (const struct packed_fmt_entry *e = mp_packed_formats; e->fmt; e++) {
- if (e->fmt == fmt) {
- int n_comp = desc.bytes[0] / e->component_size;
- plane_format[0] = gl_find_unorm_format(gl, e->component_size, n_comp);
- packed_format = e;
- goto supported;
- }
- }
-
- // Special formats for which OpenGL happens to have direct support.
- plane_format[0] = gl_find_special_format(gl, fmt);
- if (plane_format[0]) {
- // Packed YUV Apple formats color permutation
- if (plane_format[0]->format == GL_RGB_422_APPLE)
- snprintf(color_swizzle, sizeof(color_swizzle), "gbra");
- goto supported;
- }
-
- // Unsupported format
- return false;
-
-supported:
-
- if (desc.component_bits > 8 && desc.component_bits < 16) {
- if (p->texture_16bit_depth < 16)
- return false;
- }
-
int use_integer = -1;
for (int n = 0; n < desc.num_planes; n++) {
- if (!plane_format[n])
- return false;
- int use_int_plane = !!gl_integer_format_to_base(plane_format[n]->format);
+ int use_int_plane = gl_is_integer_format(desc.planes[n]->format);
if (use_integer < 0)
use_integer = use_int_plane;
if (use_integer != use_int_plane)
@@ -3382,23 +3252,16 @@ supported:
if (!test_only) {
for (int n = 0; n < desc.num_planes; n++) {
struct texplane *plane = &p->image.planes[n];
- const struct gl_format *format = plane_format[n];
- assert(format);
+ const struct gl_format *format = desc.planes[n];
plane->gl_format = format->format;
plane->gl_internal_format = format->internal_format;
plane->gl_type = format->type;
- plane->use_integer = use_integer;
- snprintf(plane->swizzle, sizeof(plane->swizzle), "rgba");
- if (packed_format)
- packed_fmt_swizzle(plane->swizzle, packed_format);
- if (plane->gl_format == GL_LUMINANCE_ALPHA)
- MPSWAP(char, plane->swizzle[1], plane->swizzle[3]);
}
init_image_desc(p, fmt);
p->use_integer_conversion = use_integer;
- snprintf(p->color_swizzle, sizeof(p->color_swizzle), "%s", color_swizzle);
+ snprintf(p->color_swizzle, sizeof(p->color_swizzle), "%s", desc.swizzle);
}
return true;
@@ -3448,7 +3311,6 @@ struct gl_video *gl_video_init(GL *gl, struct mp_log *log, struct mpv_global *g)
.gl = gl,
.global = g,
.log = log,
- .texture_16bit_depth = 16,
.sc = gl_sc_create(gl, log),
.opts_cache = m_config_cache_alloc(p, g, &gl_video_conf),
};
@@ -3501,6 +3363,7 @@ static void reinit_from_options(struct gl_video *p)
check_gl_features(p);
uninit_rendering(p);
+ gl_sc_set_cache_dir(p->sc, p->global, p->opts.shader_cache_dir);
gl_video_setup_hooks(p);
reinit_osd(p);
@@ -3521,6 +3384,9 @@ void gl_video_configure_queue(struct gl_video *p, struct vo *vo)
const struct filter_kernel *kernel =
mp_find_filter_kernel(p->opts.scaler[SCALER_TSCALE].kernel.name);
if (kernel) {
+ // filter_scale wouldn't be correctly initialized were we to use it here.
+ // This is fine since we're always upsampling, but beware if downsampling
+ // is added!
double radius = kernel->f.radius;
radius = radius > 0 ? radius : p->opts.scaler[SCALER_TSCALE].radius;
queue_size += 1 + ceil(radius);
diff --git a/video/out/opengl/video.h b/video/out/opengl/video.h
index 3b5f452..236ab06 100644
--- a/video/out/opengl/video.h
+++ b/video/out/opengl/video.h
@@ -134,6 +134,7 @@ struct gl_video_opts {
int tex_pad_x, tex_pad_y;
struct mp_icc_opts *icc_opts;
int early_flush;
+ char *shader_cache_dir;
};
extern const struct m_sub_options gl_video_conf;
diff --git a/video/out/opengl/video_shaders.c b/video/out/opengl/video_shaders.c
index 7d668dc..5421589 100644
--- a/video/out/opengl/video_shaders.c
+++ b/video/out/opengl/video_shaders.c
@@ -107,8 +107,8 @@ void pass_sample_separated_gen(struct gl_shader_cache *sc, struct scaler *scaler
void pass_sample_polar(struct gl_shader_cache *sc, struct scaler *scaler)
{
- double radius = scaler->kernel->f.radius;
- int bound = (int)ceil(radius);
+ double radius = scaler->kernel->f.radius * scaler->kernel->filter_scale;
+ int bound = ceil(radius);
bool use_ar = scaler->conf.antiring > 0;
GLSL(color = vec4(0.0);)
GLSLF("{\n");
diff --git a/video/out/vo.c b/video/out/vo.c
index f0c865d..1b71212 100644
--- a/video/out/vo.c
+++ b/video/out/vo.c
@@ -581,6 +581,9 @@ void vo_control_async(struct vo *vo, int request, void *data)
case VOCTRL_UPDATE_PLAYBACK_STATE:
d[2] = ta_xdup_ptrtype(d, (struct voctrl_playback_state *)data);
break;
+ case VOCTRL_KILL_SCREENSAVER:
+ case VOCTRL_RESTORE_SCREENSAVER:
+ break;
default:
abort(); // requires explicit support
}
@@ -836,8 +839,6 @@ static bool render_frame(struct vo *vo)
if (in->dropped_frame) {
MP_STATS(vo, "drop-vo");
} else {
- vo->want_redraw = false;
- in->want_redraw = false;
in->request_redraw = false;
}
@@ -856,14 +857,11 @@ static void do_redraw(struct vo *vo)
{
struct vo_internal *in = vo->in;
- vo->want_redraw = false;
-
if (!vo->config_ok)
return;
pthread_mutex_lock(&in->lock);
in->request_redraw = false;
- in->want_redraw = false;
bool full_redraw = in->dropped_frame;
struct vo_frame *frame = NULL;
if (!vo->driver->untimed)
@@ -898,6 +896,7 @@ static void *vo_thread(void *ptr)
{
struct vo *vo = ptr;
struct vo_internal *in = vo->in;
+ bool vo_paused = false;
mpthread_set_name("vo");
@@ -917,6 +916,7 @@ static void *vo_thread(void *ptr)
bool working = render_frame(vo);
int64_t now = mp_time_us();
int64_t wait_until = now + (working ? 0 : (int64_t)1e9);
+
pthread_mutex_lock(&in->lock);
if (in->wakeup_pts) {
if (in->wakeup_pts > now) {
@@ -927,19 +927,28 @@ static void *vo_thread(void *ptr)
}
}
if (vo->want_redraw && !in->want_redraw) {
+ vo->want_redraw = false;
in->want_redraw = true;
wakeup_core(vo);
}
bool redraw = in->request_redraw;
bool send_reset = in->send_reset;
in->send_reset = false;
+ bool send_pause = in->paused != vo_paused;
+ vo_paused = in->paused;
pthread_mutex_unlock(&in->lock);
+
if (send_reset)
vo->driver->control(vo, VOCTRL_RESET, NULL);
+ if (send_pause)
+ vo->driver->control(vo, vo_paused ? VOCTRL_PAUSE : VOCTRL_RESUME, NULL);
if (wait_until > now && redraw) {
do_redraw(vo); // now is a good time
continue;
}
+ if (vo->want_redraw) // might have been set by VOCTRLs
+ wait_until = 0;
+
wait_vo(vo, wait_until);
}
forget_frames(vo); // implicitly synchronized
@@ -955,12 +964,14 @@ void vo_set_paused(struct vo *vo, bool paused)
pthread_mutex_lock(&in->lock);
if (in->paused != paused) {
in->paused = paused;
- if (in->paused && in->dropped_frame)
+ if (in->paused && in->dropped_frame) {
in->request_redraw = true;
+ wakeup_core(vo);
+ }
reset_vsync_timings(vo);
+ wakeup_locked(vo);
}
pthread_mutex_unlock(&in->lock);
- vo_control(vo, paused ? VOCTRL_PAUSE : VOCTRL_RESUME, NULL);
}
int64_t vo_get_drop_count(struct vo *vo)
@@ -985,6 +996,7 @@ void vo_redraw(struct vo *vo)
pthread_mutex_lock(&in->lock);
if (!in->request_redraw) {
in->request_redraw = true;
+ in->want_redraw = false;
wakeup_locked(vo);
}
pthread_mutex_unlock(&in->lock);
diff --git a/video/out/vo_image.c b/video/out/vo_image.c
index e1bc1aa..fd3efde 100644
--- a/video/out/vo_image.c
+++ b/video/out/vo_image.c
@@ -57,7 +57,7 @@ struct vo_image_opts {
static const struct m_sub_options vo_image_conf = {
.opts = (const struct m_option[]) {
OPT_SUBSTRUCT("vo-image", opts, image_writer_conf, 0),
- OPT_STRING("vo-image-outdir", outdir, 0),
+ OPT_STRING("vo-image-outdir", outdir, M_OPT_FILE),
{0},
},
.size = sizeof(struct vo_image_opts),
diff --git a/video/out/vo_opengl.c b/video/out/vo_opengl.c
index 4917a25..9b3f944 100644
--- a/video/out/vo_opengl.c
+++ b/video/out/vo_opengl.c
@@ -122,13 +122,15 @@ static void draw_frame(struct vo *vo, struct vo_frame *frame)
struct gl_priv *p = vo->priv;
GL *gl = p->gl;
+ mpgl_start_frame(p->glctx);
+
if (gl->FenceSync && p->num_vsync_fences < p->opts.vsync_fences) {
GLsync fence = gl->FenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);;
if (fence)
p->vsync_fences[p->num_vsync_fences++] = fence;
}
- gl_video_render_frame(p->renderer, frame, gl->main_fb);
+ gl_video_render_frame(p->renderer, frame, p->glctx->main_fb);
if (p->opts.use_glFinish)
gl->Finish();
@@ -270,8 +272,8 @@ static int control(struct vo *vo, uint32_t request, void *data)
return VO_NOTIMPL;
}
case VOCTRL_SCREENSHOT_WIN: {
- struct mp_image *screen =
- gl_read_window_contents(p->gl, vo->dwidth, vo->dheight);
+ struct mp_image *screen = gl_read_fbo_contents(p->gl, p->glctx->main_fb,
+ vo->dwidth, vo->dheight);
if (!screen)
break; // redirect to backend
// set image parameters according to the display, if possible
@@ -295,10 +297,8 @@ static int control(struct vo *vo, uint32_t request, void *data)
gl_video_reset(p->renderer);
return true;
case VOCTRL_PAUSE:
- if (gl_video_showing_interpolated_frame(p->renderer)) {
+ if (gl_video_showing_interpolated_frame(p->renderer))
vo->want_redraw = true;
- vo_wakeup(vo);
- }
return true;
case VOCTRL_PERFORMANCE_DATA:
*(struct voctrl_performance_data *)data = gl_video_perfdata(p->renderer);
@@ -375,6 +375,8 @@ static int preinit(struct vo *vo)
if (p->opts.es == 1)
vo_flags |= VOFLAG_GLES;
+ if (p->opts.es == 2)
+ vo_flags |= VOFLAG_GLES | VOFLAG_GLES2;
if (p->opts.es == -1)
vo_flags |= VOFLAG_NO_GLES;
@@ -439,7 +441,8 @@ const struct vo_driver video_out_opengl = {
OPT_STRING_VALIDATE("opengl-backend", opts.backend, 0,
mpgl_validate_backend_opt),
OPT_FLAG("opengl-sw", opts.allow_sw, 0),
- OPT_CHOICE("opengl-es", opts.es, 0, ({"no", -1}, {"auto", 0}, {"yes", 1})),
+ OPT_CHOICE("opengl-es", opts.es, 0, ({"no", -1}, {"auto", 0},
+ {"yes", 1}, {"force2", 2})),
OPT_INTPAIR("opengl-check-pattern", opts.pattern, 0),
OPT_INTRANGE("opengl-vsync-fences", opts.vsync_fences, 0,
0, NUM_VSYNC_FENCES),
diff --git a/video/out/vo_opengl_cb.c b/video/out/vo_opengl_cb.c
index 9ecdf5e..3dd8a03 100644
--- a/video/out/vo_opengl_cb.c
+++ b/video/out/vo_opengl_cb.c
@@ -441,7 +441,6 @@ static int control(struct vo *vo, uint32_t request, void *data)
return VO_TRUE;
case VOCTRL_PAUSE:
vo->want_redraw = true;
- vo_wakeup(vo);
return VO_TRUE;
case VOCTRL_GET_EQUALIZER: {
struct voctrl_get_equalizer_args *args = data;
diff --git a/video/out/vo_tct.c b/video/out/vo_tct.c
index 68b29e9..dbe5d69 100644
--- a/video/out/vo_tct.c
+++ b/video/out/vo_tct.c
@@ -177,14 +177,14 @@ static void write_half_blocks(
static void get_win_size(struct vo *vo, int *out_width, int *out_height) {
struct priv *p = vo->priv;
-#if HAVE_POSIX
- struct winsize winsize;
- ioctl(STDOUT_FILENO, TIOCGWINSZ, &winsize);
- *out_width = winsize.ws_col;
- *out_height = winsize.ws_row;
-#else
*out_width = DEFAULT_WIDTH;
*out_height = DEFAULT_HEIGHT;
+#if HAVE_POSIX
+ struct winsize winsize;
+ if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &winsize) >= 0) {
+ *out_width = winsize.ws_col;
+ *out_height = winsize.ws_row;
+ }
#endif
if (p->opts->width > 0)
diff --git a/video/out/vo_x11.c b/video/out/vo_x11.c
index 9540fd9..dd2d942 100644
--- a/video/out/vo_x11.c
+++ b/video/out/vo_x11.c
@@ -37,7 +37,7 @@
#include "x11_common.h"
-#if HAVE_SHM && HAVE_XEXT
+#if HAVE_SHM
#include <sys/ipc.h>
#include <sys/shm.h>
#include <X11/extensions/XShm.h>
@@ -77,8 +77,9 @@ struct priv {
XVisualInfo vinfo;
int current_buf;
+ bool reset_view;
-#if HAVE_SHM && HAVE_XEXT
+#if HAVE_SHM
int Shmem_Flag;
XShmSegmentInfo Shminfo[2];
int Shm_Warned_Slow;
@@ -90,7 +91,7 @@ static bool resize(struct vo *vo);
static bool getMyXImage(struct priv *p, int foo)
{
struct vo *vo = p->vo;
-#if HAVE_SHM && HAVE_XEXT
+#if HAVE_SHM
if (vo->x11->display_is_local && XShmQueryExtension(vo->x11->display)) {
p->Shmem_Flag = 1;
vo->x11->ShmCompletionEvent = XShmGetEventBase(vo->x11->display)
@@ -146,7 +147,7 @@ shmemerror:
}
p->myximage[foo]->data =
calloc(1, p->myximage[foo]->bytes_per_line * p->image_height + 32);
-#if HAVE_SHM && HAVE_XEXT
+#if HAVE_SHM
}
#endif
return true;
@@ -154,7 +155,7 @@ shmemerror:
static void freeMyXImage(struct priv *p, int foo)
{
-#if HAVE_SHM && HAVE_XEXT
+#if HAVE_SHM
struct vo *vo = p->vo;
if (p->Shmem_Flag) {
XShmDetach(vo->x11->display, &p->Shminfo[foo]);
@@ -208,7 +209,6 @@ static int reconfig(struct vo *vo, struct mp_image_params *fmt)
static bool resize(struct vo *vo)
{
struct priv *p = vo->priv;
- struct vo_x11_state *x11 = vo->x11;
for (int i = 0; i < 2; i++)
freeMyXImage(p, i);
@@ -268,8 +268,7 @@ static bool resize(struct vo *vo)
if (mp_sws_reinit(p->sws) < 0)
return false;
- XFillRectangle(x11->display, x11->window, p->gc, 0, 0, vo->dwidth, vo->dheight);
-
+ p->reset_view = true;
vo->want_redraw = true;
return true;
}
@@ -280,7 +279,12 @@ static void Display_Image(struct priv *p, XImage *myximage)
XImage *x_image = p->myximage[p->current_buf];
-#if HAVE_SHM && HAVE_XEXT
+ if (p->reset_view) {
+ XFillRectangle(vo->x11->display, vo->x11->window, p->gc, 0, 0, vo->dwidth, vo->dheight);
+ p->reset_view = false;
+ }
+
+#if HAVE_SHM
if (p->Shmem_Flag) {
XShmPutImage(vo->x11->display, vo->x11->window, p->gc, x_image,
0, 0, p->dst.x0, p->dst.y0, p->dst_w, p->dst_h,
@@ -308,7 +312,7 @@ static struct mp_image get_x_buffer(struct priv *p, int buf_index)
static void wait_for_completion(struct vo *vo, int max_outstanding)
{
-#if HAVE_SHM && HAVE_XEXT
+#if HAVE_SHM
struct priv *ctx = vo->priv;
struct vo_x11_state *x11 = vo->x11;
if (ctx->Shmem_Flag) {
diff --git a/video/out/vo_xv.c b/video/out/vo_xv.c
index cbc6443..a866266 100644
--- a/video/out/vo_xv.c
+++ b/video/out/vo_xv.c
@@ -30,7 +30,7 @@
#include "config.h"
-#if HAVE_SHM && HAVE_XEXT
+#if HAVE_SHM
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
@@ -93,7 +93,7 @@ struct xvctx {
GC f_gc; // used to paint background
GC vo_gc; // used to paint video
int Shmem_Flag;
-#if HAVE_SHM && HAVE_XEXT
+#if HAVE_SHM
XShmSegmentInfo Shminfo[MAX_BUFFERS];
int Shm_Warned_Slow;
#endif
@@ -537,7 +537,7 @@ static bool allocate_xvimage(struct vo *vo, int foo)
int aligned_w = FFALIGN(ctx->image_width, 32);
// round up the height to next chroma boundary too
int aligned_h = FFALIGN(ctx->image_height, 2);
-#if HAVE_SHM && HAVE_XEXT
+#if HAVE_SHM
if (x11->display_is_local && XShmQueryExtension(x11->display)) {
ctx->Shmem_Flag = 1;
x11->ShmCompletionEvent = XShmGetEventBase(x11->display)
@@ -599,7 +599,7 @@ static bool allocate_xvimage(struct vo *vo, int foo)
static void deallocate_xvimage(struct vo *vo, int foo)
{
struct xvctx *ctx = vo->priv;
-#if HAVE_SHM && HAVE_XEXT
+#if HAVE_SHM
if (ctx->Shmem_Flag) {
XShmDetach(vo->x11->display, &ctx->Shminfo[foo]);
shmdt(ctx->Shminfo[foo].shmaddr);
@@ -612,7 +612,7 @@ static void deallocate_xvimage(struct vo *vo, int foo)
XFree(ctx->xvimage[foo]);
ctx->xvimage[foo] = NULL;
-#if HAVE_SHM && HAVE_XEXT
+#if HAVE_SHM
ctx->Shminfo[foo] = (XShmSegmentInfo){0};
#endif
@@ -628,7 +628,7 @@ static inline void put_xvimage(struct vo *vo, XvImage *xvi)
struct mp_rect *dst = &ctx->dst_rect;
int dw = dst->x1 - dst->x0, dh = dst->y1 - dst->y0;
int sw = src->x1 - src->x0, sh = src->y1 - src->y0;
-#if HAVE_SHM && HAVE_XEXT
+#if HAVE_SHM
if (ctx->Shmem_Flag) {
XvShmPutImage(x11->display, ctx->xv_port, x11->window, ctx->vo_gc, xvi,
src->x0, src->y0, sw, sh,
@@ -672,7 +672,7 @@ static struct mp_image get_xv_buffer(struct vo *vo, int buf_index)
static void wait_for_completion(struct vo *vo, int max_outstanding)
{
-#if HAVE_SHM && HAVE_XEXT
+#if HAVE_SHM
struct xvctx *ctx = vo->priv;
struct vo_x11_state *x11 = vo->x11;
if (ctx->Shmem_Flag) {
diff --git a/video/out/w32_common.c b/video/out/w32_common.c
index 287bb24..6b90a08 100644
--- a/video/out/w32_common.c
+++ b/video/out/w32_common.c
@@ -37,6 +37,7 @@
#include "win_state.h"
#include "w32_common.h"
#include "win32/displayconfig.h"
+#include "win32/droptarget.h"
#include "osdep/io.h"
#include "osdep/threads.h"
#include "osdep/w32_keyboard.h"
@@ -148,203 +149,6 @@ struct vo_w32_state {
HANDLE avrt_handle;
};
-typedef struct tagDropTarget {
- IDropTarget iface;
- atomic_int refCnt;
- DWORD lastEffect;
- IDataObject* dataObj;
- struct vo_w32_state *w32;
-} DropTarget;
-
-static FORMATETC fmtetc_file = { CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
-static FORMATETC fmtetc_url = { 0, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
-
-static void DropTarget_Destroy(DropTarget* This)
-{
- if (This->dataObj != NULL) {
- This->dataObj->lpVtbl->Release(This->dataObj);
- This->dataObj->lpVtbl = NULL;
- }
-
- talloc_free(This);
-}
-
-static HRESULT STDMETHODCALLTYPE DropTarget_QueryInterface(IDropTarget* This,
- REFIID riid,
- void** ppvObject)
-{
- if (!IsEqualGUID(riid, &IID_IUnknown) ||
- !IsEqualGUID(riid, &IID_IDataObject)) {
- *ppvObject = NULL;
- return E_NOINTERFACE;
- }
-
- *ppvObject = This;
- This->lpVtbl->AddRef(This);
- return S_OK;
-}
-
-static ULONG STDMETHODCALLTYPE DropTarget_AddRef(IDropTarget* This)
-{
- DropTarget* t = (DropTarget*)This;
- return atomic_fetch_add(&t->refCnt, 1) + 1;
-}
-
-static ULONG STDMETHODCALLTYPE DropTarget_Release(IDropTarget* This)
-{
- DropTarget* t = (DropTarget*)This;
- ULONG cRef = atomic_fetch_add(&t->refCnt, -1) - 1;
-
- if (cRef == 0) {
- DropTarget_Destroy(t);
- }
-
- return cRef;
-}
-
-static HRESULT STDMETHODCALLTYPE DropTarget_DragEnter(IDropTarget* This,
- IDataObject* pDataObj,
- DWORD grfKeyState,
- POINTL pt,
- DWORD* pdwEffect)
-{
- DropTarget* t = (DropTarget*)This;
-
- pDataObj->lpVtbl->AddRef(pDataObj);
- if (pDataObj->lpVtbl->QueryGetData(pDataObj, &fmtetc_file) != S_OK &&
- pDataObj->lpVtbl->QueryGetData(pDataObj, &fmtetc_url) != S_OK) {
-
- *pdwEffect = DROPEFFECT_NONE;
- }
-
- if (t->dataObj != NULL) {
- t->dataObj->lpVtbl->Release(t->dataObj);
- }
-
- t->dataObj = pDataObj;
- t->lastEffect = *pdwEffect;
- return S_OK;
-}
-
-static HRESULT STDMETHODCALLTYPE DropTarget_DragOver(IDropTarget* This,
- DWORD grfKeyState,
- POINTL pt,
- DWORD* pdwEffect)
-{
- DropTarget* t = (DropTarget*)This;
-
- *pdwEffect = t->lastEffect;
- return S_OK;
-}
-
-static HRESULT STDMETHODCALLTYPE DropTarget_DragLeave(IDropTarget* This)
-{
- DropTarget* t = (DropTarget*)This;
-
- if (t->dataObj != NULL) {
- t->dataObj->lpVtbl->Release(t->dataObj);
- t->dataObj = NULL;
- }
-
- return S_OK;
-}
-
-static HRESULT STDMETHODCALLTYPE DropTarget_Drop(IDropTarget* This,
- IDataObject* pDataObj,
- DWORD grfKeyState, POINTL pt,
- DWORD* pdwEffect)
-{
- DropTarget* t = (DropTarget*)This;
-
- STGMEDIUM medium;
-
- if (t->dataObj != NULL) {
- t->dataObj->lpVtbl->Release(t->dataObj);
- t->dataObj = NULL;
- }
-
- enum mp_dnd_action action = (grfKeyState & MK_SHIFT) ? DND_APPEND : DND_REPLACE;
-
- pDataObj->lpVtbl->AddRef(pDataObj);
-
- if (pDataObj->lpVtbl->GetData(pDataObj, &fmtetc_file, &medium) == S_OK) {
- if (GlobalLock(medium.hGlobal) != NULL) {
- HDROP hDrop = (HDROP)medium.hGlobal;
-
- UINT numFiles = DragQueryFileW(hDrop, 0xFFFFFFFF, NULL, 0);
- char** files = talloc_zero_array(NULL, char*, numFiles);
-
- UINT nrecvd_files = 0;
- for (UINT i = 0; i < numFiles; i++) {
- UINT len = DragQueryFileW(hDrop, i, NULL, 0);
- wchar_t* buf = talloc_array(NULL, wchar_t, len + 1);
-
- if (DragQueryFileW(hDrop, i, buf, len + 1) == len) {
- char* fname = mp_to_utf8(files, buf);
- files[nrecvd_files++] = fname;
-
- MP_VERBOSE(t->w32, "received dropped file: %s\n",
- fname);
- } else {
- MP_ERR(t->w32, "error getting dropped file name\n");
- }
-
- talloc_free(buf);
- }
-
- GlobalUnlock(medium.hGlobal);
- mp_event_drop_files(t->w32->input_ctx, nrecvd_files, files,
- action);
-
- talloc_free(files);
- }
-
- ReleaseStgMedium(&medium);
- } else if (pDataObj->lpVtbl->GetData(pDataObj,
- &fmtetc_url, &medium) == S_OK) {
- // get the URL encoded in US-ASCII
- wchar_t* wurl = GlobalLock(medium.hGlobal);
- if (wurl != NULL) {
- char *url = mp_to_utf8(NULL, wurl);
- if (mp_event_drop_mime_data(t->w32->input_ctx, "text/uri-list",
- bstr0(url), action) > 0) {
- MP_VERBOSE(t->w32, "received dropped URL: %s\n", url);
- } else {
- MP_ERR(t->w32, "error getting dropped URL\n");
- }
-
- talloc_free(url);
- GlobalUnlock(medium.hGlobal);
- }
-
- ReleaseStgMedium(&medium);
- }
- else {
- t->lastEffect = DROPEFFECT_NONE;
- }
-
- pDataObj->lpVtbl->Release(pDataObj);
- *pdwEffect = t->lastEffect;
- return S_OK;
-}
-
-
-static void DropTarget_Init(DropTarget* This, struct vo_w32_state *w32)
-{
- IDropTargetVtbl* vtbl = talloc(This, IDropTargetVtbl);
- *vtbl = (IDropTargetVtbl){
- DropTarget_QueryInterface, DropTarget_AddRef, DropTarget_Release,
- DropTarget_DragEnter, DropTarget_DragOver, DropTarget_DragLeave,
- DropTarget_Drop
- };
-
- This->iface.lpVtbl = vtbl;
- atomic_store(&This->refCnt, 0);
- This->lastEffect = 0;
- This->dataObj = NULL;
- This->w32 = w32;
-}
-
static void add_window_borders(HWND hwnd, RECT *rc)
{
AdjustWindowRect(rc, GetWindowLongPtrW(hwnd, GWL_STYLE), 0);
@@ -595,6 +399,35 @@ static bool handle_char(struct vo_w32_state *w32, wchar_t wc)
return true;
}
+static bool handle_mouse_down(struct vo_w32_state *w32, int btn, int x, int y)
+{
+ btn |= mod_state(w32);
+ mp_input_put_key(w32->input_ctx, btn | MP_KEY_STATE_DOWN);
+
+ if (btn == MP_MOUSE_BTN0 && !w32->current_fs &&
+ !mp_input_test_dragging(w32->input_ctx, x, y))
+ {
+ // Window dragging hack
+ ReleaseCapture();
+ SendMessage(w32->window, WM_NCLBUTTONDOWN, HTCAPTION, 0);
+ mp_input_put_key(w32->input_ctx, MP_MOUSE_BTN0 | MP_KEY_STATE_UP);
+
+ // Indicate the message was handled, so DefWindowProc won't be called
+ return true;
+ }
+
+ SetCapture(w32->window);
+ return false;
+}
+
+static void handle_mouse_up(struct vo_w32_state *w32, int btn)
+{
+ btn |= mod_state(w32);
+ mp_input_put_key(w32->input_ctx, btn | MP_KEY_STATE_UP);
+
+ ReleaseCapture();
+}
+
static void signal_events(struct vo_w32_state *w32, int events)
{
atomic_fetch_or(&w32->event_flags, events);
@@ -815,10 +648,217 @@ static bool snap_to_screen_edges(struct vo_w32_state *w32, RECT *rc)
return true;
}
-static void toggle_fullscreen(struct vo_w32_state *w32)
+struct get_monitor_data {
+ int i;
+ int target;
+ HMONITOR mon;
+};
+
+static BOOL CALLBACK get_monitor_proc(HMONITOR mon, HDC dc, LPRECT r, LPARAM p)
{
- w32->toggle_fs = true;
- signal_events(w32, VO_EVENT_FULLSCREEN_STATE);
+ struct get_monitor_data *data = (struct get_monitor_data*)p;
+
+ if (data->i == data->target) {
+ data->mon = mon;
+ return FALSE;
+ }
+ data->i++;
+ return TRUE;
+}
+
+static HMONITOR get_monitor(int id)
+{
+ struct get_monitor_data data = { .target = id };
+ EnumDisplayMonitors(NULL, NULL, get_monitor_proc, (LPARAM)&data);
+ return data.mon;
+}
+
+static void update_screen_rect(struct vo_w32_state *w32)
+{
+ struct mp_vo_opts *opts = w32->opts;
+ int screen = w32->current_fs ? opts->fsscreen_id : opts->screen_id;
+
+ // Handle --fs-screen=all
+ if (w32->current_fs && screen == -2) {
+ struct mp_rect rc = {
+ GetSystemMetrics(SM_XVIRTUALSCREEN),
+ GetSystemMetrics(SM_YVIRTUALSCREEN),
+ GetSystemMetrics(SM_CXVIRTUALSCREEN),
+ GetSystemMetrics(SM_CYVIRTUALSCREEN),
+ };
+ rc.x1 += rc.x0;
+ rc.y1 += rc.y0;
+ w32->screenrc = rc;
+ return;
+ }
+
+ // When not using --fs-screen=all, mpv belongs to a specific HMONITOR
+ HMONITOR mon;
+ if (screen == -1) {
+ // Handle --fs-screen=current and --screen=default
+ mon = MonitorFromWindow(w32->window, MONITOR_DEFAULTTOPRIMARY);
+ } else {
+ mon = get_monitor(screen);
+ if (!mon) {
+ MP_INFO(w32, "Screen %d does not exist, falling back to primary\n",
+ screen);
+ mon = MonitorFromPoint((POINT){0, 0}, MONITOR_DEFAULTTOPRIMARY);
+ }
+ }
+
+ MONITORINFO mi = { .cbSize = sizeof(mi) };
+ GetMonitorInfoW(mon, &mi);
+ w32->screenrc = (struct mp_rect){
+ mi.rcMonitor.left, mi.rcMonitor.top,
+ mi.rcMonitor.right, mi.rcMonitor.bottom,
+ };
+}
+
+static DWORD update_style(struct vo_w32_state *w32, DWORD style)
+{
+ const DWORD NO_FRAME = WS_OVERLAPPED | WS_MINIMIZEBOX;
+ const DWORD FRAME = WS_OVERLAPPEDWINDOW;
+ const DWORD FULLSCREEN = NO_FRAME | WS_SYSMENU;
+ style &= ~(NO_FRAME | FRAME | FULLSCREEN);
+ if (w32->current_fs) {
+ style |= FULLSCREEN;
+ } else {
+ style |= w32->opts->border ? FRAME : NO_FRAME;
+ }
+ return style;
+}
+
+// Update the window title, position, size, and border style.
+static void reinit_window_state(struct vo_w32_state *w32)
+{
+ HWND layer = HWND_NOTOPMOST;
+ RECT r;
+
+ if (w32->parent)
+ return;
+
+ bool new_fs = w32->toggle_fs ? !w32->current_fs : w32->opts->fullscreen;
+ bool toggle_fs = w32->current_fs != new_fs;
+ w32->current_fs = new_fs;
+ w32->toggle_fs = false;
+
+ if (w32->taskbar_list) {
+ ITaskbarList2_MarkFullscreenWindow(w32->taskbar_list,
+ w32->window, w32->current_fs);
+ }
+
+ DWORD style = update_style(w32, GetWindowLongPtrW(w32->window, GWL_STYLE));
+
+ if (w32->opts->ontop)
+ layer = HWND_TOPMOST;
+
+ // xxx not sure if this can trigger any unwanted messages (WM_MOVE/WM_SIZE)
+ update_screen_rect(w32);
+
+ int screen_w = w32->screenrc.x1 - w32->screenrc.x0;
+ int screen_h = w32->screenrc.y1 - w32->screenrc.y0;
+
+ if (w32->current_fs) {
+ // Save window position and size when switching to fullscreen.
+ if (toggle_fs) {
+ w32->prev_width = w32->dw;
+ w32->prev_height = w32->dh;
+ w32->prev_x = w32->window_x;
+ w32->prev_y = w32->window_y;
+ MP_VERBOSE(w32, "save window bounds: %d:%d:%d:%d\n",
+ w32->prev_x, w32->prev_y, w32->prev_width, w32->prev_height);
+ }
+
+ w32->window_x = w32->screenrc.x0;
+ w32->window_y = w32->screenrc.y0;
+ w32->dw = screen_w;
+ w32->dh = screen_h;
+ } else {
+ if (toggle_fs) {
+ // Restore window position and size when switching from fullscreen.
+ MP_VERBOSE(w32, "restore window bounds: %d:%d:%d:%d\n",
+ w32->prev_x, w32->prev_y, w32->prev_width, w32->prev_height);
+ w32->dw = w32->prev_width;
+ w32->dh = w32->prev_height;
+ w32->window_x = w32->prev_x;
+ w32->window_y = w32->prev_y;
+ }
+ }
+
+ r.left = w32->window_x;
+ r.right = r.left + w32->dw;
+ r.top = w32->window_y;
+ r.bottom = r.top + w32->dh;
+
+ SetWindowLongPtrW(w32->window, GWL_STYLE, style);
+
+ RECT cr = r;
+ add_window_borders(w32->window, &r);
+ // Check on client area size instead of window size on --fit-border=no
+ long o_w;
+ long o_h;
+ if( w32->opts->fit_border ) {
+ o_w = r.right - r.left;
+ o_h = r.bottom - r.top;
+ } else {
+ o_w = cr.right - cr.left;
+ o_h = cr.bottom - cr.top;
+ }
+
+ if ( !w32->current_fs && ( o_w > screen_w || o_h > screen_h ) )
+ {
+ MP_VERBOSE(w32, "requested window size larger than the screen\n");
+ // Use the aspect of the client area, not the full window size.
+ // Basically, try to compute the maximum window size.
+ long n_w;
+ long n_h;
+ if( w32->opts->fit_border ) {
+ n_w = screen_w - (r.right - cr.right) - (cr.left - r.left);
+ n_h = screen_h - (r.bottom - cr.bottom) - (cr.top - r.top);
+ } else {
+ n_w = screen_w;
+ n_h = screen_h;
+ }
+ // Letterbox
+ double asp = (cr.right - cr.left) / (double)(cr.bottom - cr.top);
+ double s_asp = n_w / (double)n_h;
+ if (asp > s_asp) {
+ n_h = n_w / asp;
+ } else {
+ n_w = n_h * asp;
+ }
+ // Save new size
+ w32->dw = n_w;
+ w32->dh = n_h;
+ // Get old window center
+ long o_cx = r.left + (r.right - r.left) / 2;
+ long o_cy = r.top + (r.bottom - r.top) / 2;
+ // Add window borders to the new window size
+ r = (RECT){.right = n_w, .bottom = n_h};
+ add_window_borders(w32->window, &r);
+ // Get top and left border size for client area position calculation
+ long b_top = -r.top;
+ long b_left = -r.left;
+ // Center the final window around the old window center
+ n_w = r.right - r.left;
+ n_h = r.bottom - r.top;
+ r.left = o_cx - n_w / 2;
+ r.top = o_cy - n_h / 2;
+ r.right = r.left + n_w;
+ r.bottom = r.top + n_h;
+ // Save new client area position
+ w32->window_x = r.left + b_left;
+ w32->window_y = r.top + b_top;
+ }
+
+ MP_VERBOSE(w32, "reset window bounds: %d:%d:%d:%d\n",
+ (int) r.left, (int) r.top, (int)(r.right - r.left),
+ (int)(r.bottom - r.top));
+
+ SetWindowPos(w32->window, layer, r.left, r.top, r.right - r.left,
+ r.bottom - r.top, SWP_FRAMECHANGED | SWP_SHOWWINDOW);
+
+ signal_events(w32, VO_EVENT_RESIZE);
}
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,
@@ -829,7 +869,6 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,
if (!w32->window)
w32->window = hWnd; // can happen during CreateWindow*!
assert(w32->window == hWnd);
- int mouse_button = 0;
switch (message) {
case WM_USER:
@@ -941,7 +980,9 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,
break;
case SC_RESTORE:
if (IsMaximized(w32->window) && w32->current_fs) {
- toggle_fullscreen(w32);
+ w32->toggle_fs = true;
+ reinit_window_state(w32);
+ signal_events(w32, VO_EVENT_FULLSCREEN_STATE);
return 0;
}
break;
@@ -1015,35 +1056,42 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,
break;
}
case WM_LBUTTONDOWN:
- mouse_button = MP_MOUSE_BTN0 | MP_KEY_STATE_DOWN;
+ if (handle_mouse_down(w32, MP_MOUSE_BTN0, GET_X_LPARAM(lParam),
+ GET_Y_LPARAM(lParam)))
+ return 0;
break;
case WM_LBUTTONUP:
- mouse_button = MP_MOUSE_BTN0 | MP_KEY_STATE_UP;
+ handle_mouse_up(w32, MP_MOUSE_BTN0);
break;
case WM_MBUTTONDOWN:
- mouse_button = MP_MOUSE_BTN1 | MP_KEY_STATE_DOWN;
+ handle_mouse_down(w32, MP_MOUSE_BTN1, GET_X_LPARAM(lParam),
+ GET_Y_LPARAM(lParam));
break;
case WM_MBUTTONUP:
- mouse_button = MP_MOUSE_BTN1 | MP_KEY_STATE_UP;
+ handle_mouse_up(w32, MP_MOUSE_BTN1);
break;
case WM_RBUTTONDOWN:
- mouse_button = MP_MOUSE_BTN2 | MP_KEY_STATE_DOWN;
+ handle_mouse_down(w32, MP_MOUSE_BTN2, GET_X_LPARAM(lParam),
+ GET_Y_LPARAM(lParam));
break;
case WM_RBUTTONUP:
- mouse_button = MP_MOUSE_BTN2 | MP_KEY_STATE_UP;
+ handle_mouse_up(w32, MP_MOUSE_BTN2);
break;
case WM_MOUSEWHEEL: {
int x = GET_WHEEL_DELTA_WPARAM(wParam);
- mouse_button = x > 0 ? MP_MOUSE_BTN3 : MP_MOUSE_BTN4;
+ mp_input_put_key(w32->input_ctx,
+ (x > 0 ? MP_MOUSE_BTN3 : MP_MOUSE_BTN4) |
+ mod_state(w32));
+ ReleaseCapture();
break;
}
case WM_XBUTTONDOWN:
- mouse_button = HIWORD(wParam) == 1 ? MP_MOUSE_BTN5 : MP_MOUSE_BTN6;
- mouse_button |= MP_KEY_STATE_DOWN;
+ handle_mouse_down(w32,
+ HIWORD(wParam) == 1 ? MP_MOUSE_BTN5 : MP_MOUSE_BTN6,
+ GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
break;
case WM_XBUTTONUP:
- mouse_button = HIWORD(wParam) == 1 ? MP_MOUSE_BTN5 : MP_MOUSE_BTN6;
- mouse_button |= MP_KEY_STATE_UP;
+ handle_mouse_up(w32, HIWORD(wParam) == 1 ? MP_MOUSE_BTN5 : MP_MOUSE_BTN6);
break;
case WM_DISPLAYCHANGE:
force_update_display_info(w32);
@@ -1056,33 +1104,6 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,
return 0;
}
- if (mouse_button) {
- mouse_button |= mod_state(w32);
- mp_input_put_key(w32->input_ctx, mouse_button);
-
- if (mp_input_mouse_enabled(w32->input_ctx)) {
- int x = GET_X_LPARAM(lParam);
- int y = GET_Y_LPARAM(lParam);
-
- if (mouse_button == (MP_MOUSE_BTN0 | MP_KEY_STATE_DOWN) &&
- !w32->current_fs &&
- !mp_input_test_dragging(w32->input_ctx, x, y))
- {
- // Window dragging hack
- ReleaseCapture();
- SendMessage(hWnd, WM_NCLBUTTONDOWN, HTCAPTION, 0);
- mp_input_put_key(w32->input_ctx, MP_MOUSE_BTN0 |
- MP_KEY_STATE_UP);
- return 0;
- }
- }
-
- if (mouse_button & MP_KEY_STATE_DOWN)
- SetCapture(w32->window);
- else
- ReleaseCapture();
- }
-
return DefWindowProcW(hWnd, message, wParam, lParam);
}
@@ -1195,219 +1216,6 @@ static void run_message_loop(struct vo_w32_state *w32)
mp_dispatch_queue_process(w32->dispatch, 1000);
}
-struct get_monitor_data {
- int i;
- int target;
- HMONITOR mon;
-};
-
-static BOOL CALLBACK get_monitor_proc(HMONITOR mon, HDC dc, LPRECT r, LPARAM p)
-{
- struct get_monitor_data *data = (struct get_monitor_data*)p;
-
- if (data->i == data->target) {
- data->mon = mon;
- return FALSE;
- }
- data->i++;
- return TRUE;
-}
-
-static HMONITOR get_monitor(int id)
-{
- struct get_monitor_data data = { .target = id };
- EnumDisplayMonitors(NULL, NULL, get_monitor_proc, (LPARAM)&data);
- return data.mon;
-}
-
-static void update_screen_rect(struct vo_w32_state *w32)
-{
- struct mp_vo_opts *opts = w32->opts;
- int screen = w32->current_fs ? opts->fsscreen_id : opts->screen_id;
-
- // Handle --fs-screen=all
- if (w32->current_fs && screen == -2) {
- struct mp_rect rc = {
- GetSystemMetrics(SM_XVIRTUALSCREEN),
- GetSystemMetrics(SM_YVIRTUALSCREEN),
- GetSystemMetrics(SM_CXVIRTUALSCREEN),
- GetSystemMetrics(SM_CYVIRTUALSCREEN),
- };
- rc.x1 += rc.x0;
- rc.y1 += rc.y0;
- w32->screenrc = rc;
- return;
- }
-
- // When not using --fs-screen=all, mpv belongs to a specific HMONITOR
- HMONITOR mon;
- if (screen == -1) {
- // Handle --fs-screen=current and --screen=default
- mon = MonitorFromWindow(w32->window, MONITOR_DEFAULTTOPRIMARY);
- } else {
- mon = get_monitor(screen);
- if (!mon) {
- MP_INFO(w32, "Screen %d does not exist, falling back to primary\n",
- screen);
- mon = MonitorFromPoint((POINT){0, 0}, MONITOR_DEFAULTTOPRIMARY);
- }
- }
-
- MONITORINFO mi = { .cbSize = sizeof(mi) };
- GetMonitorInfoW(mon, &mi);
- w32->screenrc = (struct mp_rect){
- mi.rcMonitor.left, mi.rcMonitor.top,
- mi.rcMonitor.right, mi.rcMonitor.bottom,
- };
-}
-
-static DWORD update_style(struct vo_w32_state *w32, DWORD style)
-{
- const DWORD NO_FRAME = WS_OVERLAPPED | WS_MINIMIZEBOX;
- const DWORD FRAME = WS_OVERLAPPEDWINDOW;
- const DWORD FULLSCREEN = NO_FRAME | WS_SYSMENU;
- style &= ~(NO_FRAME | FRAME | FULLSCREEN);
- if (w32->current_fs) {
- style |= FULLSCREEN;
- } else {
- style |= w32->opts->border ? FRAME : NO_FRAME;
- }
- return style;
-}
-
-// Update the window title, position, size, and border style.
-static void reinit_window_state(struct vo_w32_state *w32)
-{
- HWND layer = HWND_NOTOPMOST;
- RECT r;
-
- if (w32->parent)
- return;
-
- bool new_fs = w32->toggle_fs ? !w32->current_fs : w32->opts->fullscreen;
- bool toggle_fs = w32->current_fs != new_fs;
- w32->current_fs = new_fs;
- w32->toggle_fs = false;
-
- if (w32->taskbar_list) {
- ITaskbarList2_MarkFullscreenWindow(w32->taskbar_list,
- w32->window, w32->current_fs);
- }
-
- DWORD style = update_style(w32, GetWindowLongPtrW(w32->window, GWL_STYLE));
-
- if (w32->opts->ontop)
- layer = HWND_TOPMOST;
-
- // xxx not sure if this can trigger any unwanted messages (WM_MOVE/WM_SIZE)
- update_screen_rect(w32);
-
- int screen_w = w32->screenrc.x1 - w32->screenrc.x0;
- int screen_h = w32->screenrc.y1 - w32->screenrc.y0;
-
- if (w32->current_fs) {
- // Save window position and size when switching to fullscreen.
- if (toggle_fs) {
- w32->prev_width = w32->dw;
- w32->prev_height = w32->dh;
- w32->prev_x = w32->window_x;
- w32->prev_y = w32->window_y;
- MP_VERBOSE(w32, "save window bounds: %d:%d:%d:%d\n",
- w32->prev_x, w32->prev_y, w32->prev_width, w32->prev_height);
- }
-
- w32->window_x = w32->screenrc.x0;
- w32->window_y = w32->screenrc.y0;
- w32->dw = screen_w;
- w32->dh = screen_h;
- } else {
- if (toggle_fs) {
- // Restore window position and size when switching from fullscreen.
- MP_VERBOSE(w32, "restore window bounds: %d:%d:%d:%d\n",
- w32->prev_x, w32->prev_y, w32->prev_width, w32->prev_height);
- w32->dw = w32->prev_width;
- w32->dh = w32->prev_height;
- w32->window_x = w32->prev_x;
- w32->window_y = w32->prev_y;
- }
- }
-
- r.left = w32->window_x;
- r.right = r.left + w32->dw;
- r.top = w32->window_y;
- r.bottom = r.top + w32->dh;
-
- SetWindowLongPtrW(w32->window, GWL_STYLE, style);
-
- RECT cr = r;
- add_window_borders(w32->window, &r);
- // Check on client area size instead of window size on --fit-border=no
- long o_w;
- long o_h;
- if( w32->opts->fit_border ) {
- o_w = r.right - r.left;
- o_h = r.bottom - r.top;
- } else {
- o_w = cr.right - cr.left;
- o_h = cr.bottom - cr.top;
- }
-
- if ( !w32->current_fs && ( o_w > screen_w || o_h > screen_h ) )
- {
- MP_VERBOSE(w32, "requested window size larger than the screen\n");
- // Use the aspect of the client area, not the full window size.
- // Basically, try to compute the maximum window size.
- long n_w;
- long n_h;
- if( w32->opts->fit_border ) {
- n_w = screen_w - (r.right - cr.right) - (cr.left - r.left);
- n_h = screen_h - (r.bottom - cr.bottom) - (cr.top - r.top);
- } else {
- n_w = screen_w;
- n_h = screen_h;
- }
- // Letterbox
- double asp = (cr.right - cr.left) / (double)(cr.bottom - cr.top);
- double s_asp = n_w / (double)n_h;
- if (asp > s_asp) {
- n_h = n_w / asp;
- } else {
- n_w = n_h * asp;
- }
- // Save new size
- w32->dw = n_w;
- w32->dh = n_h;
- // Get old window center
- long o_cx = r.left + (r.right - r.left) / 2;
- long o_cy = r.top + (r.bottom - r.top) / 2;
- // Add window borders to the new window size
- r = (RECT){.right = n_w, .bottom = n_h};
- add_window_borders(w32->window, &r);
- // Get top and left border size for client area position calculation
- long b_top = -r.top;
- long b_left = -r.left;
- // Center the final window around the old window center
- n_w = r.right - r.left;
- n_h = r.bottom - r.top;
- r.left = o_cx - n_w / 2;
- r.top = o_cy - n_h / 2;
- r.right = r.left + n_w;
- r.bottom = r.top + n_h;
- // Save new client area position
- w32->window_x = r.left + b_left;
- w32->window_y = r.top + b_top;
- }
-
- MP_VERBOSE(w32, "reset window bounds: %d:%d:%d:%d\n",
- (int) r.left, (int) r.top, (int)(r.right - r.left),
- (int)(r.bottom - r.top));
-
- SetWindowPos(w32->window, layer, r.left, r.top, r.right - r.left,
- r.bottom - r.top, SWP_FRAMECHANGED | SWP_SHOWWINDOW);
-
- signal_events(w32, VO_EVENT_RESIZE);
-}
-
static void gui_thread_reconfig(void *ptr)
{
struct vo_w32_state *w32 = ptr;
@@ -1536,10 +1344,8 @@ static void *gui_thread(void *ptr)
if (SUCCEEDED(OleInitialize(NULL))) {
ole_ok = true;
- fmtetc_url.cfFormat = (CLIPFORMAT)RegisterClipboardFormat(TEXT("UniformResourceLocatorW"));
- DropTarget* dropTarget = talloc(NULL, DropTarget);
- DropTarget_Init(dropTarget, w32);
- RegisterDragDrop(w32->window, &dropTarget->iface);
+ IDropTarget *dt = mp_w32_droptarget_create(w32->log, w32->input_ctx);
+ RegisterDragDrop(w32->window, dt);
// ITaskbarList2 has the MarkFullscreenWindow method, which is used to
// make sure the taskbar is hidden when mpv goes fullscreen
@@ -1710,7 +1516,7 @@ static int gui_thread_control(struct vo_w32_state *w32, int request, void *arg)
reinit_window_state(w32);
return VO_TRUE;
case VOCTRL_GET_FULLSCREEN:
- *(bool *)arg = w32->toggle_fs != w32->current_fs;
+ *(bool *)arg = w32->current_fs;
return VO_TRUE;
case VOCTRL_GET_UNFS_WINDOW_SIZE: {
int *s = arg;
@@ -1811,8 +1617,6 @@ static void do_control(void *ptr)
w32->vo->dwidth = w32->dw;
w32->vo->dheight = w32->dh;
}
- if (*events & VO_EVENT_FULLSCREEN_STATE)
- reinit_window_state(w32);
}
int vo_w32_control(struct vo *vo, int *events, int request, void *arg)
@@ -1826,8 +1630,6 @@ int vo_w32_control(struct vo *vo, int *events, int request, void *arg)
vo->dheight = w32->dh;
mp_dispatch_unlock(w32->dispatch);
}
- if (*events & VO_EVENT_FULLSCREEN_STATE)
- reinit_window_state(w32);
return VO_TRUE;
} else {
int r;
diff --git a/video/out/wayland_common.c b/video/out/wayland_common.c
index 35d0dfe..63f7ac2 100644
--- a/video/out/wayland_common.c
+++ b/video/out/wayland_common.c
@@ -445,12 +445,16 @@ static void pointer_handle_button(void *data,
{
struct vo_wayland_state *wl = data;
- mp_input_put_key(wl->vo->input_ctx, (MP_MOUSE_BTN0 + (button - BTN_LEFT)) |
- ((state == WL_POINTER_BUTTON_STATE_PRESSED)
- ? MP_KEY_STATE_DOWN : MP_KEY_STATE_UP));
+ state = state == WL_POINTER_BUTTON_STATE_PRESSED ? MP_KEY_STATE_DOWN
+ : MP_KEY_STATE_UP;
+
+ button = button == BTN_LEFT ? MP_MOUSE_BTN0 :
+ button == BTN_MIDDLE ? MP_MOUSE_BTN1 : MP_MOUSE_BTN2;
+
+ mp_input_put_key(wl->vo->input_ctx, button | state);
if (!mp_input_test_dragging(wl->vo->input_ctx, wl->window.mouse_x, wl->window.mouse_y) &&
- (button == BTN_LEFT) && (state == WL_POINTER_BUTTON_STATE_PRESSED))
+ (button == MP_MOUSE_BTN0) && (state == MP_KEY_STATE_DOWN))
window_move(wl, serial);
}
diff --git a/video/out/win32/droptarget.c b/video/out/win32/droptarget.c
new file mode 100644
index 0000000..7040035
--- /dev/null
+++ b/video/out/win32/droptarget.c
@@ -0,0 +1,216 @@
+/*
+ * This file is part of mpv.
+ *
+ * mpv is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * mpv is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <windows.h>
+#include <ole2.h>
+#include <shobjidl.h>
+
+#include "common/msg.h"
+#include "common/common.h"
+#include "input/input.h"
+#include "input/event.h"
+#include "osdep/atomic.h"
+#include "osdep/io.h"
+#include "osdep/windows_utils.h"
+#include "mpv_talloc.h"
+
+#include "droptarget.h"
+
+struct droptarget {
+ IDropTarget iface;
+ atomic_int ref_cnt;
+ struct mp_log *log;
+ struct input_ctx *input_ctx;
+ DWORD last_effect;
+ IDataObject *data_obj;
+};
+
+static FORMATETC fmtetc_file = {
+ .cfFormat = CF_HDROP,
+ .dwAspect = DVASPECT_CONTENT,
+ .lindex = -1,
+ .tymed = TYMED_HGLOBAL,
+};
+
+static FORMATETC fmtetc_url = {
+ .dwAspect = DVASPECT_CONTENT,
+ .lindex = -1,
+ .tymed = TYMED_HGLOBAL,
+};
+
+static void DropTarget_Destroy(struct droptarget *t)
+{
+ SAFE_RELEASE(t->data_obj);
+ talloc_free(t);
+}
+
+static STDMETHODIMP DropTarget_QueryInterface(IDropTarget *self, REFIID riid,
+ void **ppvObject)
+{
+ if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IDropTarget)) {
+ *ppvObject = self;
+ IDropTarget_AddRef(self);
+ return S_OK;
+ }
+
+ *ppvObject = NULL;
+ return E_NOINTERFACE;
+}
+
+static STDMETHODIMP_(ULONG) DropTarget_AddRef(IDropTarget *self)
+{
+ struct droptarget *t = (struct droptarget *)self;
+ return atomic_fetch_add(&t->ref_cnt, 1) + 1;
+}
+
+static STDMETHODIMP_(ULONG) DropTarget_Release(IDropTarget *self)
+{
+ struct droptarget *t = (struct droptarget *)self;
+
+ ULONG ref_cnt = atomic_fetch_add(&t->ref_cnt, -1) - 1;
+ if (ref_cnt == 0)
+ DropTarget_Destroy(t);
+ return ref_cnt;
+}
+
+static STDMETHODIMP DropTarget_DragEnter(IDropTarget *self,
+ IDataObject *pDataObj,
+ DWORD grfKeyState, POINTL pt,
+ DWORD *pdwEffect)
+{
+ struct droptarget *t = (struct droptarget *)self;
+
+ IDataObject_AddRef(pDataObj);
+ if (FAILED(IDataObject_QueryGetData(pDataObj, &fmtetc_file)) &&
+ FAILED(IDataObject_QueryGetData(pDataObj, &fmtetc_url)))
+ {
+ *pdwEffect = DROPEFFECT_NONE;
+ }
+
+ SAFE_RELEASE(t->data_obj);
+ t->data_obj = pDataObj;
+ t->last_effect = *pdwEffect;
+ return S_OK;
+}
+
+static STDMETHODIMP DropTarget_DragOver(IDropTarget *self, DWORD grfKeyState,
+ POINTL pt, DWORD *pdwEffect)
+{
+ struct droptarget *t = (struct droptarget *)self;
+
+ *pdwEffect = t->last_effect;
+ return S_OK;
+}
+
+static STDMETHODIMP DropTarget_DragLeave(IDropTarget *self)
+{
+ struct droptarget *t = (struct droptarget *)self;
+
+ SAFE_RELEASE(t->data_obj);
+ return S_OK;
+}
+
+static STDMETHODIMP DropTarget_Drop(IDropTarget *self, IDataObject *pDataObj,
+ DWORD grfKeyState, POINTL pt,
+ DWORD *pdwEffect)
+{
+ struct droptarget *t = (struct droptarget *)self;
+ enum mp_dnd_action action = (grfKeyState & MK_SHIFT) ? DND_APPEND : DND_REPLACE;
+
+ SAFE_RELEASE(t->data_obj);
+
+ STGMEDIUM medium;
+ if (SUCCEEDED(IDataObject_GetData(pDataObj, &fmtetc_file, &medium))) {
+ if (GlobalLock(medium.hGlobal)) {
+ HDROP drop = medium.hGlobal;
+
+ UINT files_num = DragQueryFileW(drop, 0xFFFFFFFF, NULL, 0);
+ char **files = talloc_zero_array(NULL, char*, files_num);
+
+ UINT recvd_files = 0;
+ for (UINT i = 0; i < files_num; i++) {
+ UINT len = DragQueryFileW(drop, i, NULL, 0);
+ wchar_t *buf = talloc_array(NULL, wchar_t, len + 1);
+
+ if (DragQueryFileW(drop, i, buf, len + 1) == len) {
+ char *fname = mp_to_utf8(files, buf);
+ files[recvd_files++] = fname;
+
+ MP_VERBOSE(t, "received dropped file: %s\n", fname);
+ } else {
+ MP_ERR(t, "error getting dropped file name\n");
+ }
+
+ talloc_free(buf);
+ }
+
+ GlobalUnlock(medium.hGlobal);
+ mp_event_drop_files(t->input_ctx, recvd_files, files, action);
+ talloc_free(files);
+ }
+
+ ReleaseStgMedium(&medium);
+ } else if (SUCCEEDED(IDataObject_GetData(pDataObj, &fmtetc_url, &medium))) {
+ wchar_t *wurl = GlobalLock(medium.hGlobal);
+ if (wurl) {
+ char *url = mp_to_utf8(NULL, wurl);
+ if (mp_event_drop_mime_data(t->input_ctx, "text/uri-list",
+ bstr0(url), action) > 0)
+ {
+ MP_VERBOSE(t, "received dropped URL: %s\n", url);
+ } else {
+ MP_ERR(t, "error getting dropped URL\n");
+ }
+
+ talloc_free(url);
+ GlobalUnlock(medium.hGlobal);
+ }
+
+ ReleaseStgMedium(&medium);
+ } else {
+ t->last_effect = DROPEFFECT_NONE;
+ }
+
+ *pdwEffect = t->last_effect;
+ return S_OK;
+}
+
+static IDropTargetVtbl idroptarget_vtbl = {
+ .QueryInterface = DropTarget_QueryInterface,
+ .AddRef = DropTarget_AddRef,
+ .Release = DropTarget_Release,
+ .DragEnter = DropTarget_DragEnter,
+ .DragOver = DropTarget_DragOver,
+ .DragLeave = DropTarget_DragLeave,
+ .Drop = DropTarget_Drop,
+};
+
+IDropTarget *mp_w32_droptarget_create(struct mp_log *log,
+ struct input_ctx *input_ctx)
+{
+ fmtetc_url.cfFormat = RegisterClipboardFormatW(L"UniformResourceLocatorW");
+
+ struct droptarget *dt = talloc(NULL, struct droptarget);
+ dt->iface.lpVtbl = &idroptarget_vtbl;
+ atomic_store(&dt->ref_cnt, 0);
+ dt->last_effect = 0;
+ dt->data_obj = NULL;
+ dt->log = mp_log_new(dt, log, "droptarget");
+ dt->input_ctx = input_ctx;
+
+ return &dt->iface;
+}
diff --git a/video/out/win32/droptarget.h b/video/out/win32/droptarget.h
new file mode 100644
index 0000000..53eb5ac
--- /dev/null
+++ b/video/out/win32/droptarget.h
@@ -0,0 +1,33 @@
+/*
+ * This file is part of mpv.
+ *
+ * mpv is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * mpv is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef MP_WIN32_DROPTARGET_H_
+#define MP_WIN32_DROPTARGET_H_
+
+#include <windows.h>
+#include <ole2.h>
+#include <shobjidl.h>
+
+#include "input/input.h"
+#include "common/msg.h"
+#include "common/common.h"
+
+// Create a IDropTarget implementation that sends dropped files to input_ctx
+IDropTarget *mp_w32_droptarget_create(struct mp_log *log,
+ struct input_ctx *input_ctx);
+
+#endif
diff --git a/video/out/x11_common.c b/video/out/x11_common.c
index 6f3cc41..26f861b 100644
--- a/video/out/x11_common.c
+++ b/video/out/x11_common.c
@@ -22,6 +22,23 @@
#include <limits.h>
#include <unistd.h>
#include <poll.h>
+#include <string.h>
+#include <assert.h>
+
+#include <X11/Xmd.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+#include <X11/keysym.h>
+#include <X11/XKBlib.h>
+#include <X11/XF86keysym.h>
+
+#include <X11/extensions/scrnsaver.h>
+#include <X11/extensions/dpms.h>
+#include <X11/extensions/Xinerama.h>
+#include <X11/extensions/Xrandr.h>
+
+#include <zlib.h>
#include "config.h"
#include "misc/bstr.h"
@@ -34,10 +51,6 @@
#include "x11_common.h"
#include "mpv_talloc.h"
-#include <string.h>
-#include <unistd.h>
-#include <assert.h>
-
#include "vo.h"
#include "win_state.h"
#include "osdep/io.h"
@@ -47,34 +60,6 @@
// Specifically for mp_cancel
#include "stream/stream.h"
-#include <X11/Xmd.h>
-#include <X11/Xlib.h>
-#include <X11/Xutil.h>
-#include <X11/Xatom.h>
-#include <X11/keysym.h>
-#include <X11/XKBlib.h>
-#include <X11/XF86keysym.h>
-
-#if HAVE_XSS
-#include <X11/extensions/scrnsaver.h>
-#endif
-
-#if HAVE_XEXT
-#include <X11/extensions/dpms.h>
-#endif
-
-#if HAVE_XINERAMA
-#include <X11/extensions/Xinerama.h>
-#endif
-
-#if HAVE_XRANDR
-#include <X11/extensions/Xrandr.h>
-#endif
-
-#if HAVE_ZLIB
-#include <zlib.h>
-#endif
-
#include "input/input.h"
#include "input/keycodes.h"
@@ -369,7 +354,6 @@ static int vo_wm_detect(struct vo *vo)
static void xrandr_read(struct vo_x11_state *x11)
{
-#if HAVE_XRANDR
for(int i = 0; i < x11->num_displays; i++)
talloc_free(x11->displays[i].name);
@@ -434,7 +418,6 @@ static void xrandr_read(struct vo_x11_state *x11)
}
XRRFreeScreenResources(r);
-#endif
}
static void vo_x11_update_screeninfo(struct vo *vo)
@@ -443,7 +426,6 @@ static void vo_x11_update_screeninfo(struct vo *vo)
struct vo_x11_state *x11 = vo->x11;
bool all_screens = opts->fullscreen && opts->fsscreen_id == -2;
x11->screenrc = (struct mp_rect){.x1 = x11->ws_width, .y1 = x11->ws_height};
-#if HAVE_XINERAMA
if (opts->screen_id >= -1 && XineramaIsActive(x11->display) && !all_screens)
{
int screen = opts->fullscreen ? opts->fsscreen_id : opts->screen_id;
@@ -476,7 +458,6 @@ static void vo_x11_update_screeninfo(struct vo *vo)
XFree(screens);
}
-#endif
}
// Get the monitors for the 4 edges of the rectangle spanning all screens.
@@ -484,7 +465,6 @@ static void vo_x11_get_bounding_monitors(struct vo_x11_state *x11, long b[4])
{
//top bottom left right
b[0] = b[1] = b[2] = b[3] = 0;
-#if HAVE_XINERAMA
int num_screens = 0;
XineramaScreenInfo *screens = XineramaQueryScreens(x11->display, &num_screens);
if (!screens)
@@ -501,7 +481,6 @@ static void vo_x11_get_bounding_monitors(struct vo_x11_state *x11, long b[4])
b[3] = n;
}
XFree(screens);
-#endif
}
static void *screensaver_thread(void *arg)
@@ -1337,7 +1316,6 @@ static void vo_x11_xembed_send_message(struct vo_x11_state *x11, long m[4])
XSendEvent(x11->display, x11->parent, False, NoEventMask, &ev);
}
-#if HAVE_ZLIB
static bstr decompress_gz(bstr in)
{
bstr res = {0};
@@ -1380,12 +1358,6 @@ static bstr decompress_gz(bstr in)
error:
return res;
}
-#else
-static bstr decompress_gz(bstr in)
-{
- return (bstr){0};
-}
-#endif
#define MAX_ICONS 10
@@ -1978,9 +1950,6 @@ static void xscreensaver_heartbeat(struct vo_x11_state *x11)
static int xss_suspend(Display *mDisplay, Bool suspend)
{
-#if !HAVE_XSS
- return 0;
-#else
int event, error, major, minor;
if (XScreenSaverQueryExtension(mDisplay, &event, &error) != True ||
XScreenSaverQueryVersion(mDisplay, &major, &minor) != True)
@@ -1989,7 +1958,6 @@ static int xss_suspend(Display *mDisplay, Bool suspend)
return 0;
XScreenSaverSuspend(mDisplay, suspend);
return 1;
-#endif
}
static void set_screensaver(struct vo_x11_state *x11, bool enabled)
@@ -2001,7 +1969,6 @@ static void set_screensaver(struct vo_x11_state *x11, bool enabled)
x11->screensaver_enabled = enabled;
if (xss_suspend(mDisplay, !enabled))
return;
-#if HAVE_XEXT
int nothing;
if (DPMSQueryExtension(mDisplay, &nothing, &nothing)) {
BOOL onoff = 0;
@@ -2022,7 +1989,6 @@ static void set_screensaver(struct vo_x11_state *x11, bool enabled)
MP_WARN(x11, "DPMS state could not be set.\n");
}
}
-#endif
}
static void vo_x11_selectinput_witherr(struct vo *vo,
diff --git a/video/vaapi.c b/video/vaapi.c
index 5c8ce4c..171ccdf 100644
--- a/video/vaapi.c
+++ b/video/vaapi.c
@@ -210,6 +210,8 @@ struct mp_vaapi_ctx *va_initialize(VADisplay *display, struct mp_log *plog,
// libva drivers (such as the vdpau wraper). So don't error out on failure.
open_lavu_vaapi_device(res);
+ res->hwctx.emulated = va_guess_if_emulated(res);
+
return res;
error:
@@ -716,7 +718,13 @@ static const struct va_native_display *const native_displays[] = {
NULL
};
-struct mp_vaapi_ctx *va_create_standalone(struct mp_log *plog, bool probing)
+static void va_destroy_ctx(struct mp_hwdec_ctx *ctx)
+{
+ va_destroy(ctx->ctx);
+}
+
+struct mp_hwdec_ctx *va_create_standalone(struct mpv_global *global,
+ struct mp_log *plog, bool probing)
{
for (int n = 0; native_displays[n]; n++) {
VADisplay *display = NULL;
@@ -731,7 +739,8 @@ struct mp_vaapi_ctx *va_create_standalone(struct mp_log *plog, bool probing)
}
ctx->native_ctx = native_ctx;
ctx->destroy_native_ctx = native_displays[n]->destroy;
- return ctx;
+ ctx->hwctx.destroy = va_destroy_ctx;
+ return &ctx->hwctx;
}
}
return NULL;
diff --git a/video/vaapi.h b/video/vaapi.h
index de7d6d9..d21e3a3 100644
--- a/video/vaapi.h
+++ b/video/vaapi.h
@@ -73,6 +73,8 @@ void va_surface_init_subformat(struct mp_image *mpi);
bool va_guess_if_emulated(struct mp_vaapi_ctx *ctx);
-struct mp_vaapi_ctx *va_create_standalone(struct mp_log *plog, bool probing);
+struct mpv_global;
+struct mp_hwdec_ctx *va_create_standalone(struct mpv_global *global,
+ struct mp_log *plog, bool probing);
#endif
diff --git a/video/vdpau.c b/video/vdpau.c
index f4c85a0..8895053 100644
--- a/video/vdpau.c
+++ b/video/vdpau.c
@@ -605,3 +605,31 @@ bool mp_vdpau_guess_if_emulated(struct mp_vdpau_ctx *ctx)
CHECK_VDP_WARNING(ctx, "Error when calling vdp_get_information_string");
return vdp_st == VDP_STATUS_OK && info && strstr(info, "VAAPI");
}
+
+static void vdpau_destroy_standalone(struct mp_hwdec_ctx *ctx)
+{
+ struct mp_vdpau_ctx *vdp = ctx->ctx;
+ Display *display = vdp->x11;
+ mp_vdpau_destroy(vdp);
+ XCloseDisplay(display);
+}
+
+struct mp_hwdec_ctx *vdpau_create_standalone(struct mpv_global *global,
+ struct mp_log *plog, bool probing)
+{
+ XInitThreads();
+
+ Display *display = XOpenDisplay(NULL);
+ if (!display)
+ return NULL;
+
+ struct mp_vdpau_ctx *vdp = mp_vdpau_create_device_x11(plog, display, probing);
+ if (!vdp) {
+ XCloseDisplay(display);
+ return NULL;
+ }
+
+ vdp->hwctx.emulated = mp_vdpau_guess_if_emulated(vdp);
+ vdp->hwctx.destroy = vdpau_destroy_standalone;
+ return &vdp->hwctx;
+}
diff --git a/video/vdpau.h b/video/vdpau.h
index bffe901..b320fa5 100644
--- a/video/vdpau.h
+++ b/video/vdpau.h
@@ -88,6 +88,10 @@ struct mp_vdpau_ctx *mp_vdpau_create_device_x11(struct mp_log *log, Display *x11
bool probing);
void mp_vdpau_destroy(struct mp_vdpau_ctx *ctx);
+struct mpv_global;
+struct mp_hwdec_ctx *vdpau_create_standalone(struct mpv_global *global,
+ struct mp_log *plog, bool probing);
+
int mp_vdpau_handle_preemption(struct mp_vdpau_ctx *ctx, uint64_t *counter);
struct mp_image *mp_vdpau_get_video_surface(struct mp_vdpau_ctx *ctx,
diff --git a/video/vt.c b/video/vt.c
new file mode 100644
index 0000000..8488256
--- /dev/null
+++ b/video/vt.c
@@ -0,0 +1,74 @@
+#include <CoreVideo/CoreVideo.h>
+
+#include "video/decode/lavc.h"
+
+#include "mp_image.h"
+#include "mp_image_pool.h"
+#include "vt.h"
+
+static const uint32_t map_imgfmt_cvpixfmt[][2] = {
+ {IMGFMT_420P, kCVPixelFormatType_420YpCbCr8Planar},
+ {IMGFMT_UYVY, kCVPixelFormatType_422YpCbCr8},
+ {IMGFMT_NV12, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange},
+ {0}
+};
+
+uint32_t mp_imgfmt_to_cvpixelformat(int mpfmt)
+{
+ for (int n = 0; map_imgfmt_cvpixfmt[n][0]; n++) {
+ if (map_imgfmt_cvpixfmt[n][0] == mpfmt)
+ return map_imgfmt_cvpixfmt[n][1];
+ }
+ return 0;
+}
+
+int mp_imgfmt_from_cvpixelformat(uint32_t cvpixfmt)
+{
+ for (int n = 0; map_imgfmt_cvpixfmt[n][0]; n++) {
+ if (map_imgfmt_cvpixfmt[n][1] == cvpixfmt)
+ return map_imgfmt_cvpixfmt[n][0];
+ }
+ return 0;
+}
+
+// (ctx is unused - it's for compatibility with mp_hwdec_ctx.download_image())
+struct mp_image *mp_vt_download_image(struct mp_hwdec_ctx *ctx,
+ struct mp_image *hw_image,
+ struct mp_image_pool *swpool)
+{
+ if (hw_image->imgfmt != IMGFMT_VIDEOTOOLBOX)
+ return NULL;
+
+ struct mp_image *image = NULL;
+ CVPixelBufferRef pbuf = (CVPixelBufferRef)hw_image->planes[3];
+ CVPixelBufferLockBaseAddress(pbuf, kCVPixelBufferLock_ReadOnly);
+ size_t width = CVPixelBufferGetWidth(pbuf);
+ size_t height = CVPixelBufferGetHeight(pbuf);
+ uint32_t cvpixfmt = CVPixelBufferGetPixelFormatType(pbuf);
+ int imgfmt = mp_imgfmt_from_cvpixelformat(cvpixfmt);
+ if (!imgfmt)
+ goto unlock;
+
+ struct mp_image img = {0};
+ mp_image_setfmt(&img, imgfmt);
+ mp_image_set_size(&img, width, height);
+
+ if (CVPixelBufferIsPlanar(pbuf)) {
+ int planes = CVPixelBufferGetPlaneCount(pbuf);
+ for (int i = 0; i < planes; i++) {
+ img.planes[i] = CVPixelBufferGetBaseAddressOfPlane(pbuf, i);
+ img.stride[i] = CVPixelBufferGetBytesPerRowOfPlane(pbuf, i);
+ }
+ } else {
+ img.planes[0] = CVPixelBufferGetBaseAddress(pbuf);
+ img.stride[0] = CVPixelBufferGetBytesPerRow(pbuf);
+ }
+
+ mp_image_copy_attributes(&img, hw_image);
+
+ image = mp_image_pool_new_copy(swpool, &img);
+
+unlock:
+ CVPixelBufferUnlockBaseAddress(pbuf, kCVPixelBufferLock_ReadOnly);
+ return image;
+}
diff --git a/video/vt.h b/video/vt.h
new file mode 100644
index 0000000..e488f29
--- /dev/null
+++ b/video/vt.h
@@ -0,0 +1,16 @@
+#ifndef MPV_VT_H
+#define MPV_VT_H
+
+#include <stdint.h>
+
+int mp_imgfmt_from_cvpixelformat(uint32_t cvpixfmt);
+uint32_t mp_imgfmt_to_cvpixelformat(int mpfmt);
+
+struct mp_image;
+struct mp_image_pool;
+struct mp_hwdec_ctx;
+struct mp_image *mp_vt_download_image(struct mp_hwdec_ctx *ctx,
+ struct mp_image *hw_image,
+ struct mp_image_pool *swpool);
+
+#endif
diff --git a/waftools/fragments/touchbar.m b/waftools/fragments/touchbar.m
new file mode 100644
index 0000000..3fa4f27
--- /dev/null
+++ b/waftools/fragments/touchbar.m
@@ -0,0 +1,7 @@
+#import <AppKit/AppKit.h>
+
+int main(int argc, char **argv)
+{
+ [[NSTouchBar alloc] init];
+ return 0;
+}
diff --git a/waftools/generators/headers.py b/waftools/generators/headers.py
index 84f914c..c7f5b48 100644
--- a/waftools/generators/headers.py
+++ b/waftools/generators/headers.py
@@ -29,6 +29,7 @@ def __add_mpv_defines__(ctx):
ctx.define("CONFIGURATION", " ".join(argv))
ctx.define("MPV_CONFDIR", ctx.env.CONFLOADDIR)
ctx.define("FULLCONFIG", __escape_c_string(__get_features_string__(ctx)))
+ ctx.define("HAVE_GPL", 1)
def configure(ctx):
__add_mpv_defines__(ctx)
diff --git a/waftools/waf_customizations.py b/waftools/waf_customizations.py
index fb5f130..f264f9c 100644
--- a/waftools/waf_customizations.py
+++ b/waftools/waf_customizations.py
@@ -34,12 +34,12 @@ def build(ctx):
cls = Task.classes['cprogram']
class cprogram(cls):
try:
- run_str = cls.orig_run_str + '${LAST_LINKFLAGS}'
+ run_str = cls.orig_run_str + ' ${LAST_LINKFLAGS}'
except AttributeError:
try:
- run_str = cls.hcode + '${LAST_LINKFLAGS}'
+ run_str = cls.hcode + ' ${LAST_LINKFLAGS}'
except TypeError:
- run_str = cls.hcode.decode('iso8859-1') + '${LAST_LINKFLAGS}'
+ run_str = cls.hcode.decode('iso8859-1') + ' ${LAST_LINKFLAGS}'
cls = Task.classes['macplist']
class macplist(cls):
diff --git a/wscript b/wscript
index e660ff7..6535627 100644
--- a/wscript
+++ b/wscript
@@ -72,7 +72,7 @@ build_options = [
'desc': 'C plugins',
'deps': [ 'libdl' ],
'default': 'disable',
- 'func': check_cc(linkflags=['-Wl,-export-dynamic']),
+ 'func': check_cc(linkflags=['-rdynamic']),
}, {
'name': 'dlopen',
'desc': 'dlopen',
@@ -171,19 +171,11 @@ main_dependencies = [
'atomic_int_least64_t test = ATOMIC_VAR_INIT(123);'
'atomic_fetch_add(&test, 1)'))
}, {
- 'name': 'atomic-builtins',
- 'desc': 'compiler support for __atomic built-ins',
- 'func': check_libs(['atomic'],
- check_statement('stdint.h',
- 'int64_t test = 0;'
- 'test = __atomic_add_fetch(&test, 1, __ATOMIC_SEQ_CST)')),
- 'deps_neg': [ 'stdatomic' ],
- }, {
'name': 'atomics',
- 'desc': 'stdatomic.h support or emulation',
+ 'desc': 'stdatomic.h support or slow emulation',
'func': check_true,
'req': True,
- 'deps_any': ['stdatomic', 'atomic-builtins', 'gnuc'],
+ 'deps_any': ['stdatomic', 'gnuc'],
}, {
'name': 'c11-tls',
'desc': 'C11 TLS support',
@@ -337,19 +329,28 @@ iconv support use --disable-iconv.",
'name': '--libbluray',
'desc': 'Bluray support',
'func': check_pkg_config('libbluray', '>= 0.3.0'),
+ #'default': 'disable',
}, {
'name': '--dvdread',
'desc': 'dvdread support',
'func': check_pkg_config('dvdread', '>= 4.1.0'),
+ 'default': 'disable',
}, {
'name': '--dvdnav',
'desc': 'dvdnav support',
- 'deps': [ 'dvdread' ],
- 'func': check_pkg_config('dvdnav', '>= 4.2.0'),
+ 'func': check_pkg_config('dvdnav', '>= 4.2.0',
+ 'dvdread', '>= 4.1.0'),
+ 'default': 'disable',
+ }, {
+ 'name': 'dvdread-common',
+ 'desc': 'DVD/IFO support',
+ 'deps_any': [ 'dvdread', 'dvdnav' ],
+ 'func': check_true,
}, {
'name': '--cdda',
'desc': 'cdda support (libcdio)',
'func': check_pkg_config('libcdio_paranoia'),
+ 'default': 'disable',
}, {
'name': '--uchardet',
'desc': 'uchardet support',
@@ -578,33 +579,17 @@ video_output_features = [
} , {
'name': '--x11',
'desc': 'X11',
- 'func': check_pkg_config('x11'),
- } , {
- 'name': '--xss',
- 'desc': 'Xss screensaver extensions',
- 'deps': [ 'x11' ],
- 'func': check_pkg_config('xscrnsaver'),
- } , {
- 'name': '--xext',
- 'desc': 'X extensions',
- 'deps': [ 'x11' ],
- 'func': check_pkg_config('xext'),
+ 'func': check_pkg_config('x11', '>= 1.0.0',
+ 'xscrnsaver', '>= 1.0.0',
+ 'xext', '>= 1.0.0',
+ 'xinerama', '>= 1.0.0',
+ 'xrandr', '>= 1.2.0'),
} , {
'name': '--xv',
'desc': 'Xv video output',
'deps': [ 'x11' ],
'func': check_pkg_config('xv'),
} , {
- 'name': '--xinerama',
- 'desc': 'Xinerama',
- 'deps': [ 'x11' ],
- 'func': check_pkg_config('xinerama'),
- }, {
- 'name': '--xrandr',
- 'desc': 'Xrandr',
- 'deps': [ 'x11' ],
- 'func': check_pkg_config('xrandr', '>= 1.2.0'),
- } , {
'name': '--gl-cocoa',
'desc': 'OpenGL Cocoa Backend',
'deps': [ 'cocoa' ],
@@ -671,8 +656,10 @@ video_output_features = [
'groups': [ 'gl' ],
'func': check_statement(['EGL/egl.h'],
'eglCreateWindowSurface(0, 0, 0, 0)',
- cflags="-DGL_APICALL= -DEGLAPI= -DANGLE_NO_ALIASES -DANGLE_EXPORT=",
- lib=['EGL', 'GLESv2', 'dxguid', 'd3d9', 'gdi32', 'stdc++'])
+ cflags=['-DGL_APICALL=', '-DEGLAPI=',
+ '-DANGLE_NO_ALIASES', '-DANGLE_EXPORT='],
+ lib=['EGL', 'GLESv2', 'dxguid', 'd3d9',
+ 'gdi32', 'stdc++'])
} , {
'name': '--vdpau',
'desc': 'VDPAU acceleration',
@@ -741,37 +728,19 @@ video_output_features = [
'name': '--rpi',
'desc': 'Raspberry Pi support',
'func': check_rpi,
- }, {
- 'name': '--standard-gl',
- 'desc': 'Desktop standard OpenGL support',
- 'func': compose_checks(
- check_statement('GL/gl.h', '(void)GL_RGB32F'), # arbitrary OpenGL 3.0 symbol
- check_statement('GL/gl.h', '(void)GL_LUMINANCE16') # arbitrary OpenGL legacy-only symbol
- ),
- } , {
- 'name': '--android-gl',
- 'desc': 'Android OpenGL ES support',
- 'deps': ['android'],
- 'func': check_statement('GLES3/gl3.h', '(void)GL_RGB32F'), # arbitrary OpenGL ES 3.0 symbol
} , {
'name': '--ios-gl',
- 'desc': 'iOS OpenGL ES support',
+ 'desc': 'iOS OpenGL ES hardware decoding interop support',
'func': check_statement('OpenGLES/ES3/glext.h', '(void)GL_RGB32F'), # arbitrary OpenGL ES 3.0 symbol
} , {
- 'name': '--any-gl',
- 'desc': 'Any OpenGL (ES) support',
- 'deps_any': ['standard-gl', 'android-gl', 'ios-gl', 'cocoa'],
- 'func': check_true
- } , {
'name': '--plain-gl',
'desc': 'OpenGL without platform-specific code (e.g. for libmpv)',
- 'deps': ['any-gl'],
'deps_any': [ 'libmpv-shared', 'libmpv-static' ],
'func': check_true,
}, {
'name': '--mali-fbdev',
'desc': 'MALI via Linux fbdev',
- 'deps': ['standard-gl', 'libdl'],
+ 'deps': ['libdl'],
'func': compose_checks(
check_cc(lib="EGL"),
check_cc(lib="GLESv2"),
@@ -803,7 +772,7 @@ hwaccel_features = [
'name': '--vaapi-hwaccel',
'desc': 'libavcodec VAAPI hwaccel',
'deps': [ 'vaapi' ],
- 'func': check_headers('libavcodec/vaapi.h', use='libav'),
+ 'func': check_true,
}, {
'name': '--vaapi-hwaccel-new',
'desc': 'libavcodec VAAPI hwaccel (new)',
@@ -834,20 +803,35 @@ hwaccel_features = [
'desc': 'Videotoolbox with OpenGL',
'deps': [ 'gl-cocoa', 'videotoolbox-hwaccel' ],
'func': check_true
- } , {
+ }, {
'name': '--vdpau-hwaccel',
'desc': 'libavcodec VDPAU hwaccel',
'deps': [ 'vdpau' ],
+ 'func': check_true,
+ }, {
+ 'name': '--vdpau-hwaccel-new',
+ 'desc': 'libavcodec VDPAU hwaccel (new)',
+ 'deps': [ 'vdpau-hwaccel' ],
+ 'func': check_statement('libavcodec/version.h',
+ 'int x[(LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 37, 1) && '
+ ' LIBAVCODEC_VERSION_MICRO < 100) ||'
+ ' (LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 85, 101) && '
+ ' LIBAVCODEC_VERSION_MICRO >= 100)'
+ ' ? 1 : -1]',
+ use='libav'),
+ }, {
+ 'name': '--vdpau-hwaccel-old',
+ 'desc': 'libavcodec VDPAU hwaccel (old)',
+ 'deps': [ 'vdpau' ],
+ 'deps_neg': [ 'vdpau-hwaccel-new' ],
'func': check_statement('libavcodec/vdpau.h',
'av_vdpau_bind_context(0,0,0,AV_HWACCEL_FLAG_ALLOW_HIGH_DEPTH)',
use='libav'),
}, {
'name': '--d3d-hwaccel',
- 'desc': 'libavcodec DXVA2 and D3D11VA hwaccel',
+ 'desc': 'DXVA2 and D3D11VA hwaccel',
'deps': [ 'win32' ],
- 'func': compose_checks(
- check_headers('libavcodec/dxva2.h', use='libav'),
- check_headers('libavcodec/d3d11va.h', use='libav')),
+ 'func': check_true,
}, {
'name': '--cuda-hwaccel',
'desc': 'CUDA hwaccel',
@@ -910,7 +894,16 @@ standalone_features = [
'desc': 'Apple Remote support',
'deps': [ 'cocoa' ],
'func': check_true
- }
+ }, {
+ 'name': '--macos-touchbar',
+ 'desc': 'macOS Touch Bar support',
+ 'deps': [ 'cocoa' ],
+ 'func': check_cc(
+ fragment=load_fragment('touchbar.m'),
+ framework_name=['AppKit'],
+ compile_filename='test-touchbar.m',
+ linkflags='-fobjc-arc')
+ }
]
_INSTALL_DIRS_LIST = [
@@ -1041,7 +1034,7 @@ def configure(ctx):
# not linked against libmpv. The C plugin needs to be able to pick
# up the libmpv symbols from the binary. We still restrict the set
# of exported symbols via mpv.def.
- ctx.env.LINKFLAGS += ['-Wl,-export-dynamic']
+ ctx.env.LINKFLAGS += ['-rdynamic']
ctx.store_dependencies_lists()
diff --git a/wscript_build.py b/wscript_build.py
index 4f65f1b..9be4a59 100644
--- a/wscript_build.py
+++ b/wscript_build.py
@@ -124,7 +124,6 @@ def build(ctx):
( "audio/decode/dec_audio.c" ),
( "audio/filter/af.c" ),
( "audio/filter/af_channels.c" ),
- ( "audio/filter/af_drc.c" ),
( "audio/filter/af_equalizer.c" ),
( "audio/filter/af_format.c" ),
( "audio/filter/af_lavcac3enc.c" ),
@@ -216,6 +215,7 @@ def build(ctx):
( "misc/node.c" ),
( "misc/ring.c" ),
( "misc/rendezvous.c" ),
+ ( "misc/thread_pool.c" ),
## Options
( "options/m_config.c" ),
@@ -260,8 +260,8 @@ def build(ctx):
( "stream/stream_bluray.c", "libbluray" ),
( "stream/stream_cdda.c", "cdda" ),
( "stream/stream_dvb.c", "dvbin" ),
- ( "stream/stream_dvd.c", "dvdread" ),
- ( "stream/stream_dvd_common.c", "dvdread" ),
+ ( "stream/stream_dvd.c", "dvdread-common" ),
+ ( "stream/stream_dvd_common.c", "dvdread-common" ),
( "stream/stream_dvdnav.c", "dvdnav" ),
( "stream/stream_edl.c" ),
( "stream/stream_file.c" ),
@@ -289,6 +289,7 @@ def build(ctx):
( "sub/osd_libass.c", "libass-osd" ),
( "sub/sd_ass.c", "libass" ),
( "sub/sd_lavc.c" ),
+ ( "sub/filter_sdh.c" ),
## Video
( "video/csputils.c" ),
@@ -303,14 +304,14 @@ def build(ctx):
( "video/vaapi.c", "vaapi" ),
( "video/vdpau.c", "vdpau" ),
( "video/vdpau_mixer.c", "vdpau" ),
+ ( "video/vt.c", "videotoolbox-hwaccel" ),
( "video/decode/d3d.c", "win32" ),
( "video/decode/dec_video.c"),
( "video/decode/hw_cuda.c", "cuda-hwaccel" ),
( "video/decode/hw_dxva2.c", "d3d-hwaccel" ),
( "video/decode/hw_d3d11va.c", "d3d-hwaccel" ),
- ( "video/decode/hw_vaapi.c", "vaapi-hwaccel-new" ),
( "video/decode/hw_vaapi_old.c", "vaapi-hwaccel-old" ),
- ( "video/decode/hw_vdpau.c", "vdpau-hwaccel" ),
+ ( "video/decode/hw_vdpau.c", "vdpau-hwaccel-old" ),
( "video/decode/hw_videotoolbox.c", "videotoolbox-hwaccel" ),
( "video/decode/vd_lavc.c" ),
( "video/filter/refqueue.c" ),
@@ -354,6 +355,7 @@ def build(ctx):
( "video/out/opengl/context_dxinterop.c","gl-dxinterop" ),
( "video/out/opengl/context_mali_fbdev.c","mali-fbdev" ),
( "video/out/opengl/context_rpi.c", "rpi" ),
+ ( "video/out/opengl/context_vdpau.c", "vdpau-gl-x11" ),
( "video/out/opengl/context_wayland.c", "gl-wayland" ),
( "video/out/opengl/context_w32.c", "gl-win32" ),
( "video/out/opengl/context_x11.c", "gl-x11" ),
@@ -365,10 +367,10 @@ def build(ctx):
( "video/out/opengl/hwdec_cuda.c", "cuda-hwaccel" ),
( "video/out/opengl/hwdec_d3d11egl.c", "egl-angle" ),
( "video/out/opengl/hwdec_d3d11eglrgb.c","egl-angle" ),
- ( "video/out/opengl/hwdec_dxva2.c", "gl-win32" ),
( "video/out/opengl/hwdec_dxva2gldx.c", "gl-dxinterop" ),
( "video/out/opengl/hwdec_dxva2egl.c", "egl-angle" ),
( "video/out/opengl/hwdec_osx.c", "videotoolbox-gl" ),
+ ( "video/out/opengl/hwdec_ios.m", "ios-gl" ),
( "video/out/opengl/hwdec_rpi.c", "rpi" ),
( "video/out/opengl/hwdec_vaegl.c", "vaapi-egl" ),
( "video/out/opengl/hwdec_vaglx.c", "vaapi-glx" ),
@@ -398,6 +400,7 @@ def build(ctx):
( "video/out/vo_xv.c", "xv" ),
( "video/out/w32_common.c", "win32" ),
( "video/out/win32/displayconfig.c", "win32" ),
+ ( "video/out/win32/droptarget.c", "win32" ),
( "video/out/win32/exclusive_hack.c", "gl-win32" ),
( "video/out/wayland_common.c", "wayland" ),
( "video/out/wayland/buffer.c", "wayland" ),
@@ -416,6 +419,7 @@ def build(ctx):
( "osdep/ar/HIDRemote.m", "apple-remote" ),
( "osdep/macosx_application.m", "cocoa" ),
( "osdep/macosx_events.m", "cocoa" ),
+ ( "osdep/macosx_touchbar.m", "macos-touchbar" ),
( "osdep/semaphore_osx.c" ),
( "osdep/subprocess.c" ),
( "osdep/subprocess-posix.c", "posix-spawn" ),