summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMateusz Łukasik <mati75@linuxmint.pl>2016-03-13 23:31:21 +0100
committerMateusz Łukasik <mati75@linuxmint.pl>2016-03-13 23:31:21 +0100
commitfe66d42debb01fbb0e0c85679d1aaed2ccbc531b (patch)
treeac2481f12e78eb56a597824f5bd52e3bb19e6233
parentee91c4184837d9ecc2ab4a699c870baa4f79bb68 (diff)
Imported Upstream version 0.16.0
-rw-r--r--CONTRIBUTING.md32
-rw-r--r--DOCS/compile-windows.md39
-rw-r--r--DOCS/contribute.md20
-rw-r--r--DOCS/edl-mpv.rst5
-rw-r--r--DOCS/interface-changes.rst21
-rw-r--r--DOCS/man/af.rst10
-rw-r--r--DOCS/man/ao.rst26
-rw-r--r--DOCS/man/encode.rst8
-rw-r--r--DOCS/man/input.rst98
-rw-r--r--DOCS/man/ipc.rst8
-rw-r--r--DOCS/man/lua.rst12
-rw-r--r--DOCS/man/mpv.rst74
-rw-r--r--DOCS/man/options.rst180
-rw-r--r--DOCS/man/osc.rst25
-rw-r--r--DOCS/man/vf.rst69
-rw-r--r--DOCS/man/vo.rst58
-rw-r--r--README.md4
-rw-r--r--RELEASE_NOTES187
-rw-r--r--TOOLS/lua/ao-null-reload.lua20
-rw-r--r--TOOLS/lua/autoload.lua5
-rwxr-xr-xTOOLS/old-configure1013
-rw-r--r--TOOLS/old-makefile488
-rwxr-xr-xTOOLS/osxbundle.py11
-rwxr-xr-xTOOLS/stats-conv.py12
-rwxr-xr-xTOOLS/zsh.pl2
-rw-r--r--VERSION2
-rw-r--r--audio/audio.c110
-rw-r--r--audio/audio.h2
-rw-r--r--audio/audio_buffer.c14
-rw-r--r--audio/audio_buffer.h14
-rw-r--r--audio/chmap.c14
-rw-r--r--audio/chmap.h14
-rw-r--r--audio/chmap_sel.c36
-rw-r--r--audio/chmap_sel.h14
-rw-r--r--audio/decode/ad.h3
-rw-r--r--audio/decode/ad_lavc.c109
-rw-r--r--audio/decode/ad_spdif.c24
-rw-r--r--audio/decode/dec_audio.c205
-rw-r--r--audio/decode/dec_audio.h40
-rw-r--r--audio/filter/af.c14
-rw-r--r--audio/filter/af.h3
-rw-r--r--audio/filter/af_lavfi.c46
-rw-r--r--audio/filter/af_lavrresample.c45
-rw-r--r--audio/filter/af_rubberband.c14
-rw-r--r--audio/mixer.c16
-rw-r--r--audio/out/ao.c10
-rw-r--r--audio/out/ao_coreaudio.c8
-rw-r--r--audio/out/ao_coreaudio_chmap.c41
-rw-r--r--audio/out/ao_coreaudio_chmap.h14
-rw-r--r--audio/out/ao_coreaudio_properties.c2
-rw-r--r--audio/out/ao_coreaudio_utils.c31
-rw-r--r--audio/out/ao_coreaudio_utils.h3
-rw-r--r--audio/out/ao_dsound.c707
-rw-r--r--audio/out/ao_lavc.c2
-rw-r--r--audio/out/ao_null.c2
-rw-r--r--audio/out/ao_openal.c1
-rw-r--r--audio/out/ao_opensles.c250
-rw-r--r--audio/out/ao_pcm.c2
-rw-r--r--audio/out/ao_pulse.c55
-rw-r--r--audio/out/ao_rsound.c2
-rw-r--r--audio/out/ao_sdl.c16
-rw-r--r--audio/out/ao_wasapi.c470
-rw-r--r--[-rwxr-xr-x]audio/out/ao_wasapi.h119
-rw-r--r--[-rwxr-xr-x]audio/out/ao_wasapi_changenotify.c82
-rw-r--r--[-rwxr-xr-x]audio/out/ao_wasapi_utils.c798
-rwxr-xr-xaudio/out/ao_wasapi_utils.h49
-rw-r--r--audio/out/pull.c16
-rw-r--r--audio/out/push.c14
-rw-r--r--common/av_common.c14
-rw-r--r--common/av_common.h14
-rw-r--r--common/av_log.c11
-rw-r--r--common/codecs.c16
-rw-r--r--common/codecs.h14
-rw-r--r--common/common.c50
-rw-r--r--common/common.h14
-rw-r--r--common/encode_lavc.c2
-rw-r--r--common/global.h1
-rw-r--r--common/msg.c2
-rw-r--r--common/playlist.c28
-rw-r--r--common/playlist.h20
-rw-r--r--common/tags.c14
-rw-r--r--common/version.c14
-rw-r--r--demux/codec_tags.c30
-rw-r--r--demux/codec_tags.h22
-rw-r--r--demux/cue.c31
-rw-r--r--demux/cue.h15
-rw-r--r--demux/demux.c370
-rw-r--r--demux/demux.h21
-rw-r--r--demux/demux_cue.c30
-rw-r--r--demux/demux_disc.c83
-rw-r--r--demux/demux_edl.c16
-rw-r--r--demux/demux_lavf.c231
-rw-r--r--demux/demux_mf.c38
-rw-r--r--demux/demux_mkv.c231
-rw-r--r--demux/demux_mkv_timeline.c28
-rw-r--r--demux/demux_playlist.c32
-rw-r--r--demux/demux_rar.c14
-rw-r--r--demux/demux_raw.c56
-rw-r--r--demux/demux_timeline.c388
-rw-r--r--demux/demux_tv.c45
-rw-r--r--demux/ebml.c2
-rw-r--r--demux/packet.c5
-rw-r--r--demux/packet.h17
-rw-r--r--demux/stheader.h57
-rw-r--r--etc/input.conf8
-rw-r--r--etc/mpv.conf (renamed from etc/example.conf)83
-rw-r--r--etc/mpv.desktop2
-rw-r--r--input/cmd_list.c2
-rw-r--r--input/cmd_list.h2
-rw-r--r--input/cmd_parse.c1
-rw-r--r--input/event.c14
-rw-r--r--input/event.h14
-rw-r--r--input/input.c47
-rw-r--r--input/input.h7
-rw-r--r--input/ipc.c16
-rw-r--r--input/keycodes.c2
-rw-r--r--input/keycodes.h3
-rw-r--r--input/pipe-win32.c121
-rw-r--r--libmpv/client.h7
-rw-r--r--misc/bstr.c2
-rw-r--r--misc/bstr.h2
-rw-r--r--misc/charset_conv.c23
-rw-r--r--misc/charset_conv.h1
-rw-r--r--misc/dispatch.c27
-rw-r--r--misc/json.c14
-rw-r--r--misc/json.h14
-rw-r--r--misc/ring.c16
-rw-r--r--misc/ring.h14
-rw-r--r--mpv_talloc.h (renamed from talloc.h)0
-rw-r--r--options/m_config.c2
-rw-r--r--options/m_option.c2
-rw-r--r--options/m_property.c2
-rw-r--r--options/options.c20
-rw-r--r--options/options.h4
-rw-r--r--options/path.c17
-rw-r--r--options/path.h2
-rw-r--r--osdep/android/strnlen.c40
-rw-r--r--osdep/android/strnlen.h33
-rw-r--r--osdep/atomics.h14
-rw-r--r--osdep/glob-win.c16
-rw-r--r--osdep/io.c16
-rw-r--r--osdep/io.h14
-rw-r--r--osdep/macosx_application.m2
-rw-r--r--osdep/macosx_events.m5
-rw-r--r--osdep/main-fn-win.c5
-rw-r--r--osdep/mpv.exe.manifest2
-rw-r--r--osdep/mpv.rc2
-rw-r--r--osdep/path-macosx.m14
-rw-r--r--osdep/path-unix.c14
-rw-r--r--osdep/path-win.c31
-rw-r--r--osdep/strnlen.h29
-rw-r--r--osdep/subprocess-posix.c14
-rw-r--r--osdep/subprocess-win.c61
-rw-r--r--osdep/subprocess.c26
-rw-r--r--osdep/subprocess.h14
-rw-r--r--osdep/threads.c15
-rw-r--r--osdep/timer.c14
-rw-r--r--osdep/win32-console-wrapper.c7
-rw-r--r--osdep/windows_utils.c152
-rw-r--r--osdep/windows_utils.h29
-rw-r--r--player/audio.c472
-rw-r--r--player/client.c13
-rw-r--r--player/client.h1
-rw-r--r--player/command.c367
-rw-r--r--player/configfiles.c72
-rw-r--r--player/core.h164
-rw-r--r--player/external_files.c45
-rw-r--r--player/lavfi.c740
-rw-r--r--player/lavfi.h32
-rw-r--r--player/loadfile.c549
-rw-r--r--player/lua.c16
-rw-r--r--player/lua/osc.lua82
-rw-r--r--player/lua/ytdl_hook.lua9
-rw-r--r--player/main.c12
-rw-r--r--player/misc.c68
-rw-r--r--player/osd.c36
-rw-r--r--player/playloop.c381
-rw-r--r--player/screenshot.c29
-rw-r--r--player/screenshot.h14
-rw-r--r--player/scripting.c14
-rw-r--r--player/sub.c294
-rw-r--r--player/video.c594
-rw-r--r--stream/cache.c25
-rw-r--r--stream/cache_file.c15
-rw-r--r--stream/dvb_tune.c206
-rw-r--r--stream/dvb_tune.h2
-rw-r--r--stream/dvbin.h24
-rw-r--r--stream/rar.c2
-rw-r--r--stream/stream.c6
-rw-r--r--stream/stream.h3
-rw-r--r--stream/stream_avdevice.c14
-rw-r--r--stream/stream_bluray.c2
-rw-r--r--stream/stream_cdda.c2
-rw-r--r--stream/stream_dvb.c416
-rw-r--r--stream/stream_dvd.c2
-rw-r--r--stream/stream_dvdnav.c37
-rw-r--r--stream/stream_file.c7
-rw-r--r--stream/stream_lavf.c6
-rw-r--r--stream/stream_memory.c14
-rw-r--r--sub/ass_mp.c14
-rw-r--r--sub/ass_mp.h1
-rw-r--r--sub/dec_sub.c562
-rw-r--r--sub/dec_sub.h26
-rw-r--r--sub/draw_bmp.c184
-rw-r--r--sub/img_convert.c16
-rw-r--r--sub/lavc_conv.c (renamed from sub/sd_lavc_conv.c)138
-rw-r--r--sub/osd.c16
-rw-r--r--sub/osd_dummy.c2
-rw-r--r--sub/osd_libass.c16
-rw-r--r--sub/sd.h58
-rw-r--r--sub/sd_ass.c296
-rw-r--r--sub/sd_lavc.c59
-rw-r--r--sub/sd_lavf_srt.c94
-rw-r--r--sub/sd_microdvd.c344
-rw-r--r--sub/sd_movtext.c56
-rw-r--r--sub/sd_srt.c477
-rw-r--r--ta/ta_utils.c1
-rw-r--r--test/chmap_sel.c11
-rw-r--r--test/gl_video.c2
-rw-r--r--video/csputils.c41
-rw-r--r--video/csputils.h21
-rw-r--r--video/decode/dec_video.c417
-rw-r--r--video/decode/dec_video.h50
-rw-r--r--video/decode/dxva2.c363
-rw-r--r--video/decode/lavc.h10
-rw-r--r--video/decode/rpi.c16
-rw-r--r--video/decode/vaapi.c9
-rw-r--r--video/decode/vd.h3
-rw-r--r--video/decode/vd_lavc.c234
-rw-r--r--video/decode/vdpau.c18
-rw-r--r--video/decode/videotoolbox.c14
-rw-r--r--video/dxva2.c89
-rw-r--r--video/dxva2.h32
-rw-r--r--video/filter/vf.c86
-rw-r--r--video/filter/vf.h22
-rw-r--r--video/filter/vf_buffer.c11
-rw-r--r--video/filter/vf_crop.c21
-rw-r--r--video/filter/vf_dlopen.c64
-rw-r--r--video/filter/vf_dsize.c14
-rw-r--r--video/filter/vf_expand.c20
-rw-r--r--video/filter/vf_format.c12
-rw-r--r--video/filter/vf_lavfi.c65
-rw-r--r--video/filter/vf_scale.c8
-rw-r--r--video/filter/vf_stereo3d.c4
-rw-r--r--video/filter/vf_sub.c23
-rw-r--r--video/filter/vf_vapoursynth.c54
-rw-r--r--video/filter/vf_vavpp.c14
-rw-r--r--video/filter/vf_vdpaurb.c14
-rw-r--r--video/filter/vf_yadif.c4
-rw-r--r--video/fmt-conversion.c14
-rw-r--r--video/hwdec.h5
-rw-r--r--video/image_writer.c12
-rw-r--r--video/img_format.c43
-rw-r--r--video/img_format.h29
-rw-r--r--video/mp_image.c46
-rw-r--r--video/mp_image.h8
-rw-r--r--video/mp_image_pool.c29
-rw-r--r--video/mp_image_pool.h2
-rw-r--r--video/out/aspect.c8
-rw-r--r--video/out/bitmap_packer.c16
-rw-r--r--video/out/cocoa_common.m51
-rw-r--r--video/out/dither.c21
-rw-r--r--video/out/drm_common.c21
-rw-r--r--video/out/drm_common.h14
-rw-r--r--video/out/filter_kernels.c170
-rw-r--r--video/out/filter_kernels.h16
-rw-r--r--video/out/opengl/common.c215
-rw-r--r--video/out/opengl/common.h96
-rw-r--r--video/out/opengl/context.c228
-rw-r--r--video/out/opengl/context.h99
-rw-r--r--video/out/opengl/context_angle.c (renamed from video/out/opengl/angle.c)21
-rw-r--r--video/out/opengl/context_cocoa.c (renamed from video/out/opengl/cocoa.c)31
-rw-r--r--video/out/opengl/context_drm_egl.c (renamed from video/out/opengl/drm_egl.c)24
-rw-r--r--video/out/opengl/context_dxinterop.c (renamed from video/out/opengl/dxinterop.c)111
-rw-r--r--video/out/opengl/context_rpi.c (renamed from video/out/opengl/rpi.c)23
-rw-r--r--video/out/opengl/context_rpi.h (renamed from video/out/opengl/rpi.h)0
-rw-r--r--video/out/opengl/context_w32.c (renamed from video/out/opengl/w32.c)38
-rw-r--r--video/out/opengl/context_wayland.c (renamed from video/out/opengl/wayland.c)16
-rw-r--r--video/out/opengl/context_x11.c (renamed from video/out/opengl/x11.c)35
-rw-r--r--video/out/opengl/context_x11egl.c (renamed from video/out/opengl/x11egl.c)23
-rw-r--r--video/out/opengl/egl_helpers.c30
-rw-r--r--video/out/opengl/egl_helpers.h10
-rw-r--r--video/out/opengl/header_fixes.h43
-rw-r--r--video/out/opengl/hwdec.c49
-rw-r--r--video/out/opengl/hwdec.h6
-rw-r--r--video/out/opengl/hwdec_dxva2.c3
-rw-r--r--video/out/opengl/hwdec_dxva2gldx.c222
-rw-r--r--video/out/opengl/hwdec_osx.c17
-rw-r--r--video/out/opengl/hwdec_vaegl.c76
-rw-r--r--video/out/opengl/hwdec_vaglx.c3
-rw-r--r--video/out/opengl/hwdec_vdpau.c33
-rw-r--r--video/out/opengl/lcms.c21
-rw-r--r--video/out/opengl/nnedi3.c35
-rw-r--r--video/out/opengl/nnedi3.h19
-rw-r--r--video/out/opengl/osd.c19
-rw-r--r--video/out/opengl/superxbr.c33
-rw-r--r--video/out/opengl/superxbr.h19
-rw-r--r--video/out/opengl/utils.c35
-rw-r--r--video/out/opengl/utils.h20
-rw-r--r--video/out/opengl/video.c343
-rw-r--r--video/out/opengl/video.h16
-rw-r--r--video/out/opengl/video_shaders.c41
-rw-r--r--video/out/opengl/video_shaders.h22
-rw-r--r--video/out/vo.c5
-rw-r--r--video/out/vo_direct3d.c4
-rw-r--r--video/out/vo_drm.c23
-rw-r--r--video/out/vo_image.c2
-rw-r--r--video/out/vo_lavc.c11
-rw-r--r--video/out/vo_opengl.c27
-rw-r--r--video/out/vo_opengl_cb.c8
-rw-r--r--video/out/vo_rpi.c114
-rw-r--r--video/out/vo_sdl.c22
-rw-r--r--video/out/vo_vaapi.c2
-rw-r--r--video/out/vo_vdpau.c2
-rw-r--r--video/out/vo_wayland.c38
-rw-r--r--video/out/vo_x11.c5
-rw-r--r--video/out/vo_xv.c2
-rw-r--r--video/out/w32_common.c10
-rw-r--r--video/out/wayland/buffer.c14
-rw-r--r--video/out/wayland/buffer.h14
-rw-r--r--video/out/wayland/memfile.c14
-rw-r--r--video/out/wayland/memfile.h14
-rw-r--r--video/out/wayland_common.c29
-rw-r--r--video/out/wayland_common.h14
-rw-r--r--video/out/win32/displayconfig.c2
-rw-r--r--video/out/win_state.c6
-rw-r--r--video/out/x11_common.c12
-rw-r--r--video/sws_utils.c18
-rw-r--r--video/vaapi.c2
-rw-r--r--video/vaapi.h3
-rw-r--r--video/vdpau.c23
-rw-r--r--video/vdpau.h1
-rw-r--r--waftools/checks/custom.py7
-rw-r--r--waftools/detections/compiler.py2
-rw-r--r--waftools/generators/headers.py2
-rw-r--r--wscript77
-rw-r--r--wscript_build.py99
337 files changed, 10675 insertions, 10342 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..ffc4379
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,32 @@
+Reporting bugs
+==============
+
+- State your platform and mpv version.
+- Make sure you're actually using the latest mpv release. Linux distributions
+ in particular love redistributing ancient and unmaintained mpv releases.
+- Attach a log file made with ``-v`` or ``--log-file=output.txt``.
+- Avoid attaching text files packed in archive files (zip/rar/...), unless
+ explicitly asked so. There are better means, such as using http://sprunge.us
+- If you think this is file specific, upload a sample file and link it.
+- Don't report multiple unrelated/barely related bugs in one issue.
+- If you don't get a reply after days, be sure to post a new message to the
+ issue - it could have been overlooked or forgotten.
+- Read: https://mpv.io/bug-reports/
+
+Asking questions
+================
+
+Preferably, asking questions should be done via IRC (#mpv on irc.freenode.net),
+but asking questions by opening a github issue is also barely tolerated.
+
+When asking on IRC (#mpv on irc.freenode.net), don't ask to ask, just state
+your problem. Stay long enough until someone gets a chance to reply. Sometimes
+it can take hours.
+
+Sending patches
+===============
+
+See: https://github.com/mpv-player/mpv/blob/master/DOCS/contribute.md
+
+In particular, be aware that all changes to GPL code must come with the
+implicit agreement that they might be LGPLv2.1+ relicensed at a later point.
diff --git a/DOCS/compile-windows.md b/DOCS/compile-windows.md
index 9acd6e2..6e08d78 100644
--- a/DOCS/compile-windows.md
+++ b/DOCS/compile-windows.md
@@ -68,7 +68,7 @@ echo "MXE_TARGETS := i686-w64-mingw32.static" >> settings.mk
# Build required packages. The following provide a minimum required to build
# a reasonable mpv binary (though not an absolute minimum).
-make gcc ffmpeg libass jpeg pthreads lua
+make gcc ffmpeg libass jpeg lua
# Add MXE binaries to $PATH
export PATH=/opt/mxe/usr/bin/:$PATH
@@ -173,40 +173,3 @@ mv -f /mingw64/bin/libmpv.dll.a /mingw64/lib/
sed -i 's_/mingw64/bin_/mingw64/lib_' /mingw64/lib/pkgconfig/mpv.pc
rmdir /mingw64/bin/pkgconfig
```
-
-Additional dependencies
-=======================
-
-pthreads
---------
-
-mpv will use a pthreads wrapper by default. Either pthreads-win32 or
-winpthreads should work. The latter is packaged with most MinGW-w64
-environments, including MSYS2, so it shouldn't be a problem. If you don't have
-a pthreads wrapper or you want to build mpv without one, configure with:
-
-```bash
-./waf configure --enable-win32-internal-pthreads
-```
-
-libwaio
--------
-
-If you want to use ``--input-file``, you need libwaio. It's available from
-git://midipix.org/waio
-
-To compile libwaio in MSYS2, run:
-
-```bash
-git clone git://midipix.org/waio && cd waio
-
-# 32-bit build, run from mingw32_shell.bat
-./build-mingw-nt32 lib-static CC=gcc AR=ar
-cp -r include/waio /mingw32/include
-cp lib32/libwaio.a /mingw32/lib
-
-# 64-bit build, run from mingw64_shell.bat
-./build-mingw-nt64 lib-static CC=gcc AR=ar
-cp -r include/waio /mingw64/include
-cp lib64/libwaio.a /mingw64/lib
-```
diff --git a/DOCS/contribute.md b/DOCS/contribute.md
index d3bf1d4..9fc9d8d 100644
--- a/DOCS/contribute.md
+++ b/DOCS/contribute.md
@@ -14,9 +14,11 @@ Sending patches
``git format-patch``. diffs posted as pastebins (especially if the http link
returns HTML) just cause extra work for everyone, because they lack commit
message and authorship information.
-- When creating pull requests, be sure to test your changes. If you didn't, please
- say so in the pull request message.
-
+- All new code must be LGPLv2.1+ licensed, or come with the implicit agreement
+ that it will be relicensed to LGPLv2.1+ later (see ``Copyright`` in the
+ repository root directory).
+- When creating pull requests, be sure to test your changes. If you didn't,
+ please say so in the pull request message.
- Write informative commit messages. Use present tense to describe the
situation with the patch applied, and past tense for the situation before
the change.
@@ -27,8 +29,8 @@ Sending patches
For example, you fixed a crash in af_volume.c:
- Bad: ``fixed the bug (wtf?)``
- Good: ``af_volume: fix crash due to null pointer access``
+ - Bad: ``fixed the bug (wtf?)``
+ - Good: ``af_volume: fix crash due to null pointer access``
Having a prefix gives context, and is especially useful when trying to find
a specific change by looking at the history, or when running ``git blame``.
@@ -53,10 +55,10 @@ Sending patches
something like reformatting a whole file, and hiding an actual functional
change in the same commit.
- If you add a new command line option, document it in options.rst. If you
- add a new input property, document it in input.rst.
-- All new code must be LGPLv2.1+ licensed, or come with the implicit agreement
- that it will be relicensed to LGPLv2.1+ later (see ``Copyright`` in the
- repository root directory).
+ add a new input property, document it in input.rst. Changes to the user
+ interface (options, properties, commands) should be documented in
+ interface-changes.rst. Changes to libmpv should be documented in
+ client-api-changes.rst.
Code formatting
---------------
diff --git a/DOCS/edl-mpv.rst b/DOCS/edl-mpv.rst
index b83706c..b0f7238 100644
--- a/DOCS/edl-mpv.rst
+++ b/DOCS/edl-mpv.rst
@@ -31,9 +31,8 @@ estimated remaining duration of the source file is used.
Note::
- mpv can't use ordered chapter files in EDL entries. Usage of relative or
- absolute paths as well as any protocol prefixes is prevented for security
- reasons.
+ Usage of relative or absolute paths as well as any protocol prefixes may be
+ prevented for security reasons.
Syntax of mpv EDL files
diff --git a/DOCS/interface-changes.rst b/DOCS/interface-changes.rst
index a713279..4217de8 100644
--- a/DOCS/interface-changes.rst
+++ b/DOCS/interface-changes.rst
@@ -1,7 +1,7 @@
Introduction
============
-mpv provides access to its internal via the following means:
+mpv provides access to its internals via the following means:
- options
- commands
@@ -19,6 +19,25 @@ Interface changes
::
+ --- mpv 0.16.0 ---
+ - change --audio-channels default to stereo (use --audio-channels=auto to
+ get the old default)
+ - add --audio-normalize-downmix
+ - change the default downmix behavior (--audio-normalize-downmix=yes to get
+ the old default)
+ - VO opengl custom shaders must now use "sample_pixel" as function name,
+ instead of "sample"
+ - change VO opengl scaler-resizes-only default to enabled
+ - add VO opengl "interpolation-threshold" suboption (introduces new default
+ behavior, which can change e.g. ``--video-sync=display-vdrop`` to the
+ worse, but is usually what you want)
+ - make "volume" and "mute" properties changeable even if no audio output is
+ active (this gives not-ideal behavior if --softvol=no is used)
+ - add "volume-max" and "mixer-active" properties
+ - ignore --input-cursor option for events injected by input commands like
+ "mouse", "keydown", etc.
+ --- mpv 0.15.0 ---
+ - change "yadif" video filter defaults
--- mpv 0.14.0 ---
- vo_opengl interpolation now requires --video-sync=display-... to be set
- change some vo_opengl defaults (including changing tscale)
diff --git a/DOCS/man/af.rst b/DOCS/man/af.rst
index ec6ce3f..be90f09 100644
--- a/DOCS/man/af.rst
+++ b/DOCS/man/af.rst
@@ -63,13 +63,9 @@ Available filters are:
(If you just want to set defaults for this filter that will be used
even by automatically inserted lavrresample instances, you should
prefer setting them with ``--af-defaults=lavrresample:...``.)
- ``normalize=<yes|no>``
- Whether to normalize when remixing channel layouts (default: yes). This
- is e.g. applied when downmixing surround audio to stereo. The advantage
- is that this guarantees that no clipping can happen. Unfortunately,
- this can also lead to too low volume levels. Whether you enable or
- disable this is essentially a matter of taste, but the default uses
- the safer choice.
+ ``normalize=<yes|no|auto>``
+ Whether to normalize when remixing channel layouts (default: auto).
+ ``auto`` uses the value set by ``--audio-normalize-downmix``.
``o=<string>``
Set AVOptions on the SwrContext or AVAudioResampleContext. These should
be documented by FFmpeg or Libav.
diff --git a/DOCS/man/ao.rst b/DOCS/man/ao.rst
index a35b9c1..8bddc98 100644
--- a/DOCS/man/ao.rst
+++ b/DOCS/man/ao.rst
@@ -20,10 +20,8 @@ normal driver parameters.
See ``--ao=help`` for a list of compiled-in audio output drivers. The
driver ``--ao=alsa`` is preferred. ``--ao=pulse`` is preferred on systems
- where PulseAudio is used. On Windows, ``--ao=wasapi`` is preferred,
- though it might cause trouble sometimes, in which case ``--ao=dsound``
- should be used. On BSD systems, ``--ao=oss`` or `--ao=sndio`` may work
- (the latter being experimental). On OS X systems, use ``--ao=coreaudio``.
+ where PulseAudio is used. On BSD systems, ``--ao=oss`` or ``--ao=sndio``
+ may work (the latter being experimental).
.. admonition:: Examples
@@ -165,7 +163,7 @@ Available audio output drivers are:
will actually work. The disadvantage is that it will change the
system-wide audio settings. This is equivalent to changing the ``Format``
setting in the ``Audio Devices`` dialog in the ``Audio MIDI Setup``
- utility. Note that this does not effect the selected speaker setup.
+ utility. Note that this does not affect the selected speaker setup.
``exclusive``
Use exclusive mode access. This merely redirects to
@@ -205,19 +203,7 @@ Available audio output drivers are:
(it used to be required to get good behavior on old PulseAudio versions).
If you have stuttering video when using pulse, try to enable this
- option. (Or alternatively, try to update PulseAudio.)
-
-``dsound`` (Windows only)
- DirectX DirectSound audio output driver
-
- .. note:: This driver is for compatibility with old systems.
-
- ``device=<devicenum>``
- Sets the device number to use. Playing a file with ``-v`` will show a
- list of available devices.
-
- ``buffersize=<ms>``
- DirectSound buffer size in milliseconds (default: 200).
+ option. (Or try to update PulseAudio.)
``sdl``
SDL 1.2+ audio output driver. Should work on any platform supported by SDL
@@ -256,7 +242,7 @@ Available audio output drivers are:
``speed``
Simulated audio playback speed as a multiplier. Usually, a real audio
device will not go exactly as fast as the system clock. It will deviate
- just a little, and this option helps simulating this.
+ just a little, and this option helps to simulate this.
``latency``
Simulated device latency. This is additional to EOF.
@@ -325,7 +311,7 @@ Available audio output drivers are:
String are valid; the GUID string is guaranteed to not change
unless the driver is uninstalled.
- Also supports searching active devices by human readable name. If more
+ Also supports searching active devices by human-readable name. If more
than one device matches the name, refuses loading it.
This option is mostly deprecated in favour of the more general
diff --git a/DOCS/man/encode.rst b/DOCS/man/encode.rst
index b3a2123..794b8a1 100644
--- a/DOCS/man/encode.rst
+++ b/DOCS/man/encode.rst
@@ -93,8 +93,8 @@ You can encode files from one format/codec to another using this facility.
Completely empties the options list.
``--oafirst``
- Force the audio stream to become the first stream in the output. By default
- the order is unspecified.
+ Force the audio stream to become the first stream in the output.
+ By default, the order is unspecified.
``--ovc=<codec>``
Specifies the output video codec. This can be a comma separated list of
@@ -134,8 +134,8 @@ You can encode files from one format/codec to another using this facility.
Completely empties the options list.
``--ovfirst``
- Force the video stream to become the first stream in the output. By default
- the order is unspecified.
+ Force the video stream to become the first stream in the output.
+ By default, the order is unspecified.
``--ocopyts``
Copies input pts to the output video (not supported by some output
diff --git a/DOCS/man/input.rst b/DOCS/man/input.rst
index c3e69b3..11a8f00 100644
--- a/DOCS/man/input.rst
+++ b/DOCS/man/input.rst
@@ -418,7 +418,7 @@ List of Input Commands
<reselect> (default)
Select the default audio and subtitle streams, which typically selects
- external files with highest preference. (The implementation is not
+ external files with the highest preference. (The implementation is not
perfect, and could be improved on request.)
<keep-selection>
@@ -546,6 +546,12 @@ Input Commands that are Possibly Subject to Change
Always bind a key. (The input section that was made active most recently
wins if there are ambiguities.)
+ This command can be used to dispatch arbitrary keys to a script or a client
+ API user. If the input section defines ``script-binding`` commands, it is
+ also possible to get separate events on key up/down, and relatively detailed
+ information about the key state. The special key name ``unmapped`` can be
+ used to match any unmapped key.
+
``overlay-add <id> <x> <y> "<file>" <offset> "<fmt>" <w> <h> <stride>``
Add an OSD overlay sourced from raw data. This might be useful for scripts
and applications controlling mpv, and which want to display things on top
@@ -647,14 +653,20 @@ Input Commands that are Possibly Subject to Change
For completeness, here is how this command works internally. The details
could change any time. On any matching key event, ``script_message_to``
or ``script_message`` is called (depending on whether the script name is
- included), where the first argument is the string ``key-binding``, the
- second argument is the name of the binding, and the third argument is the
- key state as string. The key state consists of a number of letters. The
- first letter is one of ``d`` (key was pressed down), ``u`` (was released),
- ``r`` (key is still down, and was repeated; only if key repeat is enabled
- for this binding), ``p`` (key was pressed; happens if up/down can't be
- tracked). The second letter whether the event originates from the mouse,
- either ``m`` (mouse button) or ``-`` (something else).
+ included), with the following arguments:
+
+ 1. The string ``key-binding``.
+ 2. The name of the binding (as established above).
+ 3. The key state as string (see below).
+ 4. The key name (since mpv 0.15.0).
+
+ The key state consists of 2 letters:
+
+ 1. One of ``d`` (key was pressed down), ``u`` (was released), ``r`` (key
+ is still down, and was repeated; only if key repeat is enabled for this
+ binding), ``p`` (key was pressed; happens if up/down can't be tracked).
+ 2. Whether the event originates from the mouse, either ``m`` (mouse button)
+ or ``-`` (something else).
``ab-loop``
Cycle through A-B loop states. The first command will set the ``A`` point
@@ -682,6 +694,18 @@ Input Commands that are Possibly Subject to Change
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.
+``vf-command "<label>" "<cmd>" "<args>"``
+ Send a command to the filter with the given ``<label>``. Use ``all`` to send
+ it to all filters at once. The command and argument string is filter
+ specific. Currently, this only works with the ``lavfi`` filter - see
+ the libavfilter documentation for which commands a filter supports.
+
+ Note that the ``<label>`` is a mpv filter label, not a libavfilter filter
+ name.
+
+``af-command "<label>" "<cmd>" "<args>"``
+ Same as ``vf-command``, but for audio filters.
+
Undocumented commands: ``tv-last-channel`` (TV/DVB only),
``ao-reload`` (experimental/internal).
@@ -741,6 +765,17 @@ The following hooks are currently defined:
``file-local-options/<option name>``. The player will wait until all
hooks are run.
+``on_preloaded``
+ Called after a file has been opened, and before tracks are selected and
+ decoders are created. This has some usefulness if an API users wants
+ to select tracks manually, based on the set of available tracks. It's
+ also useful to initialize ``--lavfi-complex`` in a specific way by API,
+ without having to "probe" the available streams at first.
+
+ Note that this does not yet apply default track selection. Which operations
+ exactly can be done and not be done, and what information is available and
+ what is not yet available yet, is all subject to change.
+
``on_unload``
Run before closing a file, and before actually uninitializing
everything. It's not possible to resume playback in this state.
@@ -782,7 +817,7 @@ Input sections group a set of bindings, and enable or disable them at once.
In ``input.conf``, each key binding is assigned to an input section, rather
than actually having explicit text sections.
-Also see ``enable_section`` and ``disable_section`` commands.
+See also: ``enable_section`` and ``disable_section`` commands.
Predefined bindings:
@@ -961,9 +996,8 @@ Property list
``playback-time`` (RW)
Position in current file in seconds. Unlike ``time-pos``, the time is
clamped to the range of the file. (Inaccurate file durations etc. could
- make it go out of range. Also helpful when the user attempts to seek
- outside of the file, as the seek target time is considered the current
- position during seeking.)
+ make it go out of range. Useful on attempts to seek outside of the file,
+ as the seek target time is considered the current position during seeking.)
``chapter`` (RW)
Current chapter number. The number of the first chapter is 0.
@@ -1109,13 +1143,13 @@ Property list
``vf-metadata/<filter-label>``
Metadata added by video filters. Accessed by the filter label,
- which if not explicitly specified using the ``@filter-label:`` syntax,
+ which, if not explicitly specified using the ``@filter-label:`` syntax,
will be ``<filter-name>NN``.
Works similar to ``metadata`` property. It allows the same access
methods (using sub-properties).
- An example of these kind of metadata are the cropping parameters
+ An example of this kind of metadata are the cropping parameters
added by ``--vf=lavfi=cropdetect``.
``af-metadata/<filter-label>``
@@ -1142,7 +1176,7 @@ Property list
``cache-size`` (RW)
Network cache size in KB. This is similar to ``--cache``. This allows
- to set the cache size at runtime. Currently, it's not possible to enable
+ setting the cache size at runtime. Currently, it's not possible to enable
or disable the cache at runtime using this property, just to resize an
existing cache.
@@ -1201,11 +1235,30 @@ Property list
``hr-seek`` (RW)
See ``--hr-seek``.
+``mixer-active``
+ Return ``yes`` if the audio mixer is active, ``no`` otherwise. This has
+ implications for ``--softvol=no`` mode: if the mixer is active, changing
+ ``volume`` doesn't actually change anything on the system mixer. If the
+ ``--volume`` or ``--mute`` option are used, these might not be applied
+ property until the mixer becomes active either. (The options, if set, will
+ just overwrite the mixer state at audio initialization.)
+
+ While the behavior with ``mixer-active==yes`` is relatively well-defined,
+ the ``no`` case will provide possibly wrong or insignificant values.
+
+ Note that an active mixer does not necessarily imply active audio output,
+ although this is implied in the current implementation.
+
``volume`` (RW)
- Current volume (see ``--volume`` for details).
+ Current volume (see ``--volume`` for details). Also see ``mixer-active``
+ property.
+
+``volume-max``
+ Current maximum value the volume property can be set to. (This may depend
+ on the ``--softvol-max`` option.)
``mute`` (RW)
- Current mute status (``yes``/``no``).
+ Current mute status (``yes``/``no``). Also see ``mixer-active`` property.
``audio-delay`` (RW)
See ``--audio-delay``.
@@ -1531,6 +1584,15 @@ Property list
``program`` (W)
Switch TS program (write-only).
+``dvb-channel`` (W)
+ Pair of integers: card,channel of current DVB stream.
+ Can be switched to switch to another channel on the same card.
+
+``dvb-channel-name`` (RW)
+ Name of current DVB program.
+ On write, a channel-switch to the named channel on the same
+ card is performed. Can also be used for channel switching.
+
``sid`` (RW)
Current subtitle track (similar to ``--sid``).
diff --git a/DOCS/man/ipc.rst b/DOCS/man/ipc.rst
index 31333a2..e3ff1bd 100644
--- a/DOCS/man/ipc.rst
+++ b/DOCS/man/ipc.rst
@@ -111,7 +111,7 @@ rely on this.
Commands
--------
-Additionally to the commands described in `List of Input Commands`_, a few
+In addition to the commands described in `List of Input Commands`_, a few
extra commands can also be used as part of the protocol:
``client_name``
@@ -189,7 +189,7 @@ extra commands can also be used as part of the protocol:
``unobserve_property``
Undo ``observe_property`` or ``observe_property_string``. This requires the
- numeric id passed to the observe command as argument.
+ numeric id passed to the observed command as argument.
Example:
@@ -228,7 +228,9 @@ extra commands can also be used as part of the protocol:
``get_version``
Returns the client API version the C API of the remote mpv instance
- provides. (Also see ``DOCS/client-api-changes.rst``.)
+ provides.
+
+ See also: ``DOCS/client-api-changes.rst``.
UTF-8
-----
diff --git a/DOCS/man/lua.rst b/DOCS/man/lua.rst
index d51a6d9..4d47774 100644
--- a/DOCS/man/lua.rst
+++ b/DOCS/man/lua.rst
@@ -198,7 +198,7 @@ The ``mp`` module is preloaded, although it can be loaded manually with
be called (unless the user remapped the key with another binding).
The ``name`` argument should be a short symbolic string. It allows the user
- to remap the key binding via input.conf using the ``script_message``
+ to remap the key binding via input.conf using the ``script-message``
command, and the name of the key binding (see below for
an example). The name should be unique across other bindings in the same
script - if not, the previous binding with the same name will be
@@ -220,8 +220,8 @@ The ``mp`` module is preloaded, although it can be loaded manually with
has an ``is_mouse`` entry, which tells whether the event was caused
by a mouse button.
- Internally, key bindings are dispatched via the ``script_message_to`` or
- ``script_binding`` input commands and ``mp.register_script_message``.
+ Internally, key bindings are dispatched via the ``script-message-to`` or
+ ``script-binding`` input commands and ``mp.register_script_message``.
Trying to map multiple commands to a key will essentially prefer a random
binding, while the other bindings are not called. It is guaranteed that
@@ -244,7 +244,7 @@ The ``mp`` module is preloaded, although it can be loaded manually with
::
- y script_binding something
+ y script-binding something
This will print the message when the key ``y`` is pressed. (``x`` will
@@ -255,7 +255,7 @@ The ``mp`` module is preloaded, although it can be loaded manually with
::
- y script_binding fooscript.something
+ y script-binding fooscript.something
``mp.add_forced_key_binding(...)``
This works almost the same as ``mp.add_key_binding``, but registers the
@@ -389,7 +389,7 @@ The ``mp`` module is preloaded, although it can be loaded manually with
``mp.get_script_name()``
Return the name of the current script. The name is usually made of the
filename of the script, with directory and file extension removed. If
- there are several script which would have the same name, it's made unique
+ there are several scripts which would have the same name, it's made unique
by appending a number.
.. admonition:: Example
diff --git a/DOCS/man/mpv.rst b/DOCS/man/mpv.rst
index 9527b8a..6716c38 100644
--- a/DOCS/man/mpv.rst
+++ b/DOCS/man/mpv.rst
@@ -37,6 +37,10 @@ LIRC support - configure remotes as input devices instead).
See the ``--input-`` options for ways to customize it.
+The following listings are not necessarily complete. See ``etc/input.conf`` for
+a list of default bindings. User ``input.conf`` files and Lua scripts can
+define additional key bindings.
+
Keyboard Control
----------------
@@ -50,7 +54,7 @@ UP and DOWN
Ctrl+LEFT and Ctrl+RIGHT
Seek to the previous/next subtitle. Subject to some restrictions and
- might not work always; see ``sub_seek`` command.
+ might not always work; see ``sub_seek`` command.
[ and ]
Decrease/increase current playback speed by 10%.
@@ -222,7 +226,10 @@ PREVIOUS and NEXT
support.)
h and k
- Select previous/next channel.
+ Select previous/next tv-channel.
+
+H and K
+ Select previous/next dvb-channel.
Mouse Control
-------------
@@ -378,6 +385,45 @@ file stops playing. If option ``--c`` is changed during playback of
``file2.mkv``, it is reset when advancing to ``file3.mkv``. This only affects
file-local options. The option ``--a`` is never reset here.
+
+Playing DVDs
+------------
+
+DVDs can be played with the ``dvd://[title]`` syntax. The optional
+title specifier is a number which selects between separate video
+streams on the DVD. If no title is given (``dvd://``) then the longest
+title is selected automatically by the library. This is usually what
+you want. mpv does not support DVD menus.
+
+DVDs which have been copied on to a hard drive or other mounted
+filesystem (by e.g. the ``dvdbackup`` tool) are accommodated by
+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::
+
+ mpv uses a different default DVD library than MPlayer. MPlayer
+ uses libdvdread by default, and mpv uses libdvdnav by default.
+ Both libraries are developed in parallel, but libdvdnav is
+ intended to support more sophisticated DVD features such as menus
+ and multi-angle playback. mpv uses libdvdnav for files specified
+ as either ``dvd://...`` or ``dvdnav://...``. To use libdvdread,
+ which will produce behavior more like MPlayer, specify
+ ``dvdread://...`` instead. Some users have experienced problems
+ 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``).
+
+ 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
+ shortcuts generally do not work with image-based subtitles.
+ Exceptions include options like ``--stretch-dvd-subs`` and
+ ``--stretch-image-subs-to-screen``.
+
+
CONFIGURATION FILES
===================
@@ -520,8 +566,9 @@ TAKING SCREENSHOTS
Screenshots of the currently played file can be taken using the 'screenshot'
input mode command, which is by default bound to the ``s`` key. Files named
-``shotNNNN.jpg`` will be saved in the working directory, using the first
-available number - no files will be overwritten.
+``mpv-shotNNNN.jpg`` will be saved in the working directory, using the first
+available number - no files will be overwritten. In pseudo-GUI mode, the
+screenshot will be saved somewhere else. See `PSEUDO GUI MODE`_.
A screenshot will usually contain the unscaled video contents at the end of the
video filter chain and subtitles. By default, ``S`` takes screenshots without
@@ -571,7 +618,7 @@ listed.
certainty.
- Dropped frames, e.g. ``Dropped: 4``. Shows up only if the count is not 0. Can
grow if the video framerate is higher than that of the display, or if video
- rendering is too slow. Also can be incremented on "hiccups" and when the video
+ rendering is too slow. May also be incremented on "hiccups" and when the video
frame couldn't be displayed on time. (``vo-drop-frame-count`` property.)
If the decoder drops frames, the number of decoder-dropped frames is appended
to the display as well, e.g.: ``Dropped: 4/34``. This happens only if
@@ -602,7 +649,7 @@ PROTOCOLS
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,
+ 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.
@@ -654,9 +701,8 @@ PROTOCOLS
absolute path.
``fd://123``
- Read data from the given UNIX FD (for example 123). This is similar to
- piping data to stdin via ``-``, but can use an arbitrary file descriptor.
- Will not work correctly on MS Windows.
+ 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.
@@ -728,7 +774,7 @@ EMBEDDING INTO OTHER PROGRAMS (LIBMPV)
======================================
mpv can be embedded into other programs as video/audio playback backend. The
-recommended way to to so is using libmpv. See ``libmpv/client.h`` in the mpv
+recommended way to do so is using libmpv. See ``libmpv/client.h`` in the mpv
source code repository. This provides a C API. Bindings for other languages
might be available (see wiki).
@@ -803,7 +849,7 @@ libdvdcss:
key
is the default method. libdvdcss will use a set of calculated
- player keys to try and get the disc key. This can fail if the drive
+ player keys to try to get the disc key. This can fail if the drive
does not recognize any of the player keys.
disc
@@ -919,7 +965,7 @@ locations are different. They are generally located under ``%APPDATA%/mpv/``.
For example, the path to mpv.conf is ``%APPDATA%/mpv/mpv.conf``, which maps to
a system and user-specific path, for example
- ``C:\users\USERNAME\Application Data\mpv\mpv.conf``
+ ``C:\users\USERNAME\AppData\Roaming\mpv\mpv.conf``
You can find the exact path by running ``echo %APPDATA%\mpv\mpv.conf`` in cmd.exe.
@@ -942,8 +988,8 @@ lower priority. Some config files are loaded only once, which means that
e.g. of 2 ``input.conf`` files located in two config directories, only the
one from the directory with higher priority will be loaded.
-A third config directory with lowest priority is the directory named ``mpv``
-in the same directory as ``mpv.exe``. This used to be the directory with
+A third config directory with the lowest priority is the directory named ``mpv``
+in the same directory as ``mpv.exe``. This used to be the directory with the
highest priority, but is now discouraged to use and might be removed in the
future.
diff --git a/DOCS/man/options.rst b/DOCS/man/options.rst
index 14a0a7b..ec71dd7 100644
--- a/DOCS/man/options.rst
+++ b/DOCS/man/options.rst
@@ -136,7 +136,9 @@ Playback Control
``--chapter=<start[-end]>``
Specify which chapter to start playing at. Optionally specify which
- chapter to end playing at. Also see ``--start``.
+ chapter to end playing at.
+
+ See also: ``--start``.
``--playlist-pos=<no|index>``
Set which file on the internal playlist to start playback with. The index
@@ -321,7 +323,7 @@ Program Behavior
Files explicitly requested by command line options, like
``--include`` or ``--use-filedir-conf``, will still be loaded.
- Also see ``--config-dir``.
+ See also: ``--config-dir``.
``--list-options``
Prints all available options.
@@ -392,8 +394,7 @@ Program Behavior
``--merge-files``
Pretend that all files passed to mpv are concatenated into a single, big
- file. This uses timeline/EDL support internally. Note that this won't work
- for ordered chapter files.
+ file. This uses timeline/EDL support internally.
``--no-resume-playback``
Do not restore playback position from the ``watch_later`` configuration
@@ -537,7 +538,7 @@ Video
filters all frames, but doesn't render them on the VO. It tries to query
the display FPS (X11 only, not correct on multi-monitor systems), or
assumes infinite display FPS if that fails. Drops are indicated in
- the terminal status line as ``D:`` field. If the decoder is too slow,
+ the terminal status line as ``Dropped:`` field. If the decoder is too slow,
in theory all frames would have to be dropped (because all frames are
too late) - to avoid this, frame dropping stops if the effective
framerate is below 10 FPS.
@@ -558,9 +559,13 @@ Video
``--display-fps=<fps>``
Set the display FPS used with the ``--video-sync=display-*`` modes. By
- default a detected value is used (X11 only, not correct on multi-monitor
- systems). Keep in mind that setting an incorrect value (even if slightly
- incorrect) can ruin video playback.
+ default, a detected value is used. Keep in mind that setting an incorrect
+ value (even if slightly incorrect) can ruin video playback. On multi-monitor
+ systems, there is a chance that the detected value is from the wrong
+ monitor.
+
+ Set this option only if you have reason to believe the automatically
+ determined value is wrong.
``--hwdec=<api>``
Specify the hardware video decoding API that should be used if possible.
@@ -613,7 +618,7 @@ Video
For ``opengl-cb``, if set, load the interop context as soon as the OpenGL
context is created. Since ``opengl-cb`` has no on-demand loading, this
- allows enabling hardware decoding at runtime at all, without having to
+ allows enabling hardware decoding at runtime at all, without having
to temporarily set the ``hwdec`` option just during OpenGL context
initialization with ``mpv_opengl_cb_init_gl()``.
@@ -675,7 +680,7 @@ Video
example ``--video-zoom`` does nothing if this option is enabled.)
The video and monitor aspects aspect will be ignored. Aspect correction
- would require to scale the video in the X or Y direction, but this option
+ would require scaling the video in the X or Y direction, but this option
disables scaling, disabling all aspect correction.
Note that the scaler algorithm may still be used, even if the video isn't
@@ -695,7 +700,7 @@ Video
This option is disabled if the ``--no-keepaspect`` option is used.
-``--video-rotate=<0-360|no>``
+``--video-rotate=<0-359|no>``
Rotate the video clockwise, in degrees. Currently supports 90° steps only.
If ``no`` is given, the video is never rotated, even if the file has
rotation metadata. (The rotation value is added to the rotation metadata,
@@ -820,7 +825,7 @@ Video
You can get the list of allowed codecs with ``mpv --vd=help``. Remove the
prefix, e.g. instead of ``lavc:h264`` use ``h264``.
- By default this is set to ``h264,vc1,wmv3,hevc,mpeg2video``. Note that the
+ By default, this is set to ``h264,vc1,wmv3,hevc,mpeg2video``. Note that the
hardware acceleration special codecs like ``h264_vdpau`` are not relevant
anymore, and in fact have been removed from Libav in this form.
@@ -973,7 +978,7 @@ Audio
There is not much reason to use this. HDMI supports uncompressed
multichannel PCM, and mpv supports lossless DTS-HD decoding via
- FFmpeg's libdcadec wrapper.
+ FFmpeg's new DCA decoder (based on libdcadec).
``--ad=<[+|-]family1:(*|decoder1),[+|-]family2:(*|decoder2),...[-]>``
Specify a priority list of audio decoders to be used, according to their
@@ -1027,7 +1032,9 @@ Audio
``--mute=<auto|yes|no>``
Set startup audio mute status. ``auto`` (default) will not change the mute
- status. Also see ``--volume``.
+ status.
+
+ See also: ``--volume``.
``--softvol=<mode>``
Control whether to use the volume controls of the audio output driver or
@@ -1085,8 +1092,8 @@ Audio
This and enabling passthrough via ``--ad`` are deprecated in favor of
using ``--audio-spdif=dts-hd``.
-``--audio-channels=<number|layout>``
- Request a channel layout for audio output (default: auto). This will ask
+``--audio-channels=<auto|number|layout>``
+ Request a channel layout for audio output (default: stereo). This will ask
the AO to open a device with the given channel layout. It's up to the AO
to accept this layout, or to pick a fallback or to error out if the
requested layout is not supported.
@@ -1099,9 +1106,10 @@ Audio
lists speaker names, which can be used to express arbitrary channel
layouts (e.g. ``fl-fr-lfe`` is 2.1).
- The default is ``--audio-channels=auto``, which tries to play audio using
- the input file's channel layout. (Or more precisely, the output of the
- audio filter chain.) (``empty`` is an accepted obsolete alias for ``auto``.)
+ ``--audio-channels=auto`` tries to play audio using the input file's
+ channel layout. There is no guarantee that the audio API handles this
+ correctly. See the HDMI warning below.
+ (``empty`` is an accepted obsolete alias for ``auto``.)
This will also request the channel layout from the decoder. If the decoder
does not support the layout, it will fall back to its native channel layout.
@@ -1120,6 +1128,16 @@ Audio
channel layout, random things can happen, such as dropping the
additional channels, or adding noise.
+``--audio-normalize-downmix=<yes|no>``
+ Enable/disable normalization if surround audio is downmixed to stereo
+ (default: no). If this is disabled, downmix can cause clipping. If it's
+ enabled, the output might be too silent. It depends on the source audio.
+
+ Technically, this changes the ``normalize`` suboption of the
+ ``lavrresample`` audio filter, which performs the downmixing.
+
+ If downmix happens outside of mpv for some reason, this has no effect.
+
``--audio-display=<no|attachment>``
Setting this option to ``attachment`` (default) will display image
attachments (e.g. album cover art) when playing audio files. It will
@@ -1152,8 +1170,8 @@ Audio
point of file change. Default: ``weak``.
:no: Disable gapless audio.
- :yes: The audio device is opened using parameters chosen according to the
- first file played and is then kept open for gapless playback. This
+ :yes: The audio device is opened using parameters chosen for the first
+ file played and is then kept open for gapless playback. This
means that if the first file for example has a low sample rate, then
the following files may get resampled to the same low sample rate,
resulting in reduced sound quality. If you play files with different
@@ -1200,7 +1218,11 @@ Audio
:no: Don't automatically load external audio files.
:exact: Load the media filename with audio file extension (default).
:fuzzy: Load all audio files containing media filename.
- :all: Load all audio files in the current directory.
+ :all: Load all aufio files in the current and ``--audio-file-paths``
+ directories.
+
+``--audio-file-paths=<path1:path2:...>``
+ Equivalent to ``--sub-paths`` option, but for auto-loaded audio files.
``--audio-client-name=<name>``
The application name the player reports to the audio API. Can be useful
@@ -1237,6 +1259,14 @@ Audio
Subtitles
---------
+.. note::
+
+ Changing styling and position does not work with all subtitles. Image-based
+ subtitles (DVD, Bluray/PGS, DVB) can not changed for fundamental reasons.
+ Subtitles in ASS format are normally not changed intentionally, but
+ overriding them can be controlled with ``--ass-style-override``.
+
+
``--no-sub``
Do not select any subtitle when the file is loaded.
@@ -1365,7 +1395,7 @@ Subtitles
.. admonition:: Warning
Enabling hinting can lead to mispositioned text (in situations it's
- supposed to match up with video background), or reduce the smoothness
+ supposed to match up video background), or reduce the smoothness
of animations with some badly authored ASS scripts. It is recommended
to not use this option, unless really needed.
@@ -1600,10 +1630,10 @@ Subtitles
``<rate>`` > video fps speeds the subtitles up for frame-based
subtitle files and slows them down for time-based ones.
- Also see ``--sub-speed`` option.
+ See also: ``--sub-speed``.
``--sub-gauss=<0.0-3.0>``
- Apply Gaussian blur to image subtitles (default: 0). This can help making
+ Apply Gaussian blur to image subtitles (default: 0). This can help to make
pixelated DVD/Vobsubs look nicer. A value other than 0 also switches to
software subtitle scaling. Might be slow.
@@ -1612,7 +1642,7 @@ Subtitles
Never applied to text subtitles.
``--sub-gray``
- Convert image subtitles to grayscale. Can help making yellow DVD/Vobsubs
+ Convert image subtitles to grayscale. Can help to make yellow DVD/Vobsubs
look nicer.
.. note::
@@ -1984,8 +2014,8 @@ Window
always re-enabled when the player is paused.
This is not supported on all video outputs or platforms. Sometimes it is
- implemented, but does not work (happens often on GNOME). You might be able
- to to work this around using ``--heartbeat-cmd`` instead.
+ implemented, but does not work (known to happen with GNOME). You might be
+ able to work around this using ``--heartbeat-cmd`` instead.
``--wid=<ID>``
This tells mpv to attach to an existing window. If a VO is selected that
@@ -2004,7 +2034,7 @@ Window
parent, like with X11.
On OSX/Cocoa, the ID is interpreted as ``NSView*``. Pass it as value cast
- to ``intptr_t``. mpv will creates its own sub-view. Because OSX does not
+ to ``intptr_t``. mpv will create its own sub-view. Because OSX does not
support window embedding of foreign processes, this works only with libmpv,
and will crash when used from the command line.
@@ -2213,7 +2243,7 @@ Demuxer
Encryption key the demuxer should use. This is the raw binary data of
the key converted to a hexadecimal string.
-``--demuxer-mkv-subtitle-preroll``, ``--mkv-subtitle-preroll``
+``--demuxer-mkv-subtitle-preroll=<yes|index|no>``, ``--mkv-subtitle-preroll``
Try harder to show embedded soft subtitles when seeking somewhere. Normally,
it can happen that the subtitle at the seek target is not shown due to how
some container file formats are designed. The subtitles appear only if
@@ -2245,7 +2275,11 @@ Demuxer
overlap with a seek target. In these cases, mpv will reduce the amount
of data read to a minimum. (Although it will still read *all* data between
the cluster that contains the first wanted subtitle packet, and the seek
- target.)
+ target.) If the ``index`` choice (which is the default) is specified, then
+ prerolling will be done only if this information is actually available. If
+ this method is used, the maximum amount of data to skip can be additionally
+ controlled by ``--demuxer-mkv-subtitle-preroll-secs-index`` (it still uses
+ the value of the option without ``-index`` if that is higher).
See also ``--hr-seek-demuxer-offset`` option. This option can achieve a
similar effect, but only if hr-seek is active. It works with any demuxer,
@@ -2257,6 +2291,9 @@ Demuxer
``--demuxer-mkv-subtitle-preroll-secs=<value>``
See ``--demuxer-mkv-subtitle-preroll``.
+``--demuxer-mkv-subtitle-preroll-secs-index=<value>``
+ See ``--demuxer-mkv-subtitle-preroll``.
+
``--demuxer-mkv-probe-video-duration=<yes|no|full>``
When opening the file, seek to the end of it, and check what timestamp the
last video packet has, and report that as file duration. This is strictly
@@ -2381,7 +2418,7 @@ Input
``--input-key-fifo-size=<2-65000>``
Specify the size of the FIFO that buffers key events (default: 7). If it
- is too small some events may be lost. The main disadvantage of setting it
+ is too small, some events may be lost. The main disadvantage of setting it
to a very large value is that if you hold down a key triggering some
particularly slow command then the player may be unresponsive while it
processes all the queued commands.
@@ -2448,7 +2485,7 @@ Input
On X11, a sub-window with input enabled grabs all keyboard input as long
as it is 1. a child of a focused window, and 2. the mouse is inside of
- the sub-window. The can steal away all keyboard input from the
+ the sub-window. It can steal away all keyboard input from the
application embedding the mpv window, and on the other hand, the mpv
window will receive no input if the mouse is outside of the mpv window,
even though mpv has focus. Modern toolkits work around this weird X11
@@ -2718,7 +2755,7 @@ Screenshot
Specify the filename template used to save screenshots. The template
specifies the filename without file extension, and can contain format
specifiers, which will be substituted when taking a screenshot.
- By default the template is ``mpv-shot%n``, which results in filenames like
+ By default, the template is ``mpv-shot%n``, which results in filenames like
``mpv-shot0012.png`` for example.
The template can start with a relative or absolute path, in order to
@@ -2874,7 +2911,7 @@ Terminal
Particularly useful on slow terminals or broken ones which do not properly
handle carriage return (i.e. ``\r``).
- Also see ``--really-quiet`` and ``--msg-level``.
+ See also: ``--really-quiet`` and ``--msg-level``.
``--really-quiet``
Display even less output and status messages than with ``--quiet``.
@@ -3007,8 +3044,9 @@ TV
maximum size of the capture buffer in megabytes (default: dynamical)
``--tv-norm=<value>``
- See the console output for a list of all available norms, also see the
- ``normid`` option below.
+ See the console output for a list of all available norms.
+
+ See also: ``--tv-normid``.
``--tv-normid=<value> (v4l2 only)``
Sets the TV norm to the given numeric ID. The TV norm depends on the
@@ -3218,7 +3256,7 @@ Cache
multiple cache streams, and using the same file for them obviously
clashes.
- Also see ``--cache-file-size``.
+ See also: ``--cache-file-size``.
``--cache-file-size=<kBytes>``
Maximum size of the file created with ``--cache-file``. For read accesses
@@ -3346,8 +3384,13 @@ DVB
``--dvbin-full-transponder=<yes|no>``
Apply no filters on program PIDs, only tune to frequency and pass full
- transponder to demuxer. This is useful to record multiple programs
- on a single transponder, or to work around issues in the ``channels.conf``.
+ transponder to demuxer.
+ The player frontend selects the streams from the full TS in this case,
+ so the program which is shown initially may not match the chosen channel.
+ Switching between the programs is possible by cycling the ``program``
+ property.
+ This is useful to record multiple programs on a single transponder,
+ or to work around issues in the ``channels.conf``.
It is also recommended to use this for channels which switch PIDs
on-the-fly, e.g. for regional news.
@@ -3380,7 +3423,7 @@ Miscellaneous
implement a perfect audio delay measurement. With this value, if large A/V
sync offsets occur, they will only take about 1 or 2 seconds to settle
out. This delay in reaction time to sudden A/V offsets should be the only
- side-effect of turning this option on, for all sound drivers.
+ side effect of turning this option on, for all sound drivers.
``--video-sync=<audio|...>``
How the player synchronizes audio and video.
@@ -3509,3 +3552,58 @@ Miscellaneous
Force the contents of the ``media-title`` property to this value. Useful
for scripts which want to set a title, without overriding the user's
setting in ``--title``.
+
+``--external-file=<filename>``
+ Add all tracks from the given file. Unlike ``--sub-file`` and
+ ``--audio-file``, this includes all tracks, and does not cause default
+ stream selection over the "proper" file.
+
+``--lavfi-complex=<string>``
+ Set a "complex" libavfilter filter, which means a single filter graph can
+ take input from multiple source audio and video tracks. The graph can result
+ in a single audio or video output (or both).
+
+ Currently, the filter graph labels are used to select the participating
+ input tracks and audio/video output. The following rules apply:
+
+ - A label of the form ``aidN`` selects audio track N as input (e.g.
+ ``aid1``).
+ - A label of the form ``vidN`` selects video track N as input.
+ - A label named ``ao`` will be connected to the audio input.
+ - A label named ``vo`` will be connected to the video output.
+
+ Each label can be used only once. If you want to use e.g. an audio stream
+ for multiple filters, you need to use the ``asplit`` filter. Multiple
+ video or audio outputs are not possible, but you can use filters to merge
+ them into one.
+
+ The complex filter can not be changed yet during playback. It's also not
+ possible to change the tracks connected to the filter at runtime. Other
+ tracks, as long as they're not connected to the filter, and the
+ corresponding output is not connected to the filter, can still be freely
+ changed.
+
+ Note that the normal filter chains (``--af``, ``--vf``) are applied between
+ the complex graphs (e.g. ``ao`` label) and the actual output.
+
+ .. admonition:: Examples
+
+ - ``--lavfi-complex='[aid1] asplit [ao] [t] ; [t] aphasemeter [vo]'``
+ Play audio track 1, and visualize it as video using the ``aphasemeter``
+ filter.
+ - ``--lavfi-complex='[aid1] [aid2] amix [ao]'``
+ Play audio track 1 and 2 at the same time.
+ - ``--lavfi-complex='[vid1] [vid2] vstack [vo]'``
+ Stack video track 1 and 2 and play them at the same time. Note that
+ both tracks need to have the same width, or filter initialization
+ will fail (you can add ``scale`` filters before the ``vstack`` filter
+ to fix the size).
+ - ``--lavfi-complex='[aid1] asplit [ao] [t] ; [t] aphasemeter [t2] ; [vid1] [t2] overlay [vo]'``
+ Play audio track 1, and overlay its visualization over video track 1.
+ - ``--lavfi-complex='[aid1] asplit [t1] [ao] ; [t1] showvolume [t2] ; [vid1] [t2] overlay [vo]'``
+ Play audio track 1, and overlay the measured volume for each speaker
+ over video track 1.
+
+ See the FFmpeg libavfilter documentation for details on the filter.
+
+
diff --git a/DOCS/man/osc.rst b/DOCS/man/osc.rst
index f4f8d66..587b810 100644
--- a/DOCS/man/osc.rst
+++ b/DOCS/man/osc.rst
@@ -130,7 +130,7 @@ these keys. In case of collision, the function needs to be bound to a
different key. See the `Script Commands`_ section.
============= ================================================
-del Hide the OSC permanently until mpv is restarted.
+del Cycles visibility between never / auto (mouse-move) / always
============= ================================================
Configuration
@@ -206,8 +206,8 @@ Configurable Options
``hidetimeout``
| Default: 500
- | Duration in ms until the OSC hides if no mouse movement, negative value
- disables auto-hide
+ | Duration in ms until the OSC hides if no mouse movement, must not be
+ negative
``fadeduration``
| Default: 200
@@ -243,28 +243,29 @@ Configurable Options
| Default: no
| Display timecodes with milliseconds
+``visibility``
+ | Default: auto (auto hide/show on mouse move)
+ | Also supports ``never`` and ``always``
+
Script Commands
~~~~~~~~~~~~~~~
The OSC script listens to certain script commands. These commands can bound
in ``input.conf``, or sent by other scripts.
-``enable-osc``
- Undoes ``disable-osc`` or the effect of the ``del`` key.
-
-``disable-osc``
- Hide the OSC permanently. This is also what the ``del`` key does.
-
``osc-message``
Show a message on screen using the OSC. First argument is the message,
second the duration in seconds.
+``osc-visibility``
+ Controls visibility mode ``never`` / ``auto`` (on mouse move) / ``always``
+ and also ``cycle`` to cycle between the modes
Example
You could put this into ``input.conf`` to hide the OSC with the ``a`` key and
-to unhide it with ``b``::
+to set auto mode (the default) with ``b``::
- a script_message disable-osc
- b script_message enable-osc
+ a script_message osc-visibility never
+ b script_message osc-visibility auto
diff --git a/DOCS/man/vf.rst b/DOCS/man/vf.rst
index 72e1559..faca411 100644
--- a/DOCS/man/vf.rst
+++ b/DOCS/man/vf.rst
@@ -151,15 +151,15 @@ Available filters are:
:yes: Enable accurate rounding.
``dsize[=w:h:aspect-method:r:aspect]``
- Changes the intended display size/aspect at an arbitrary point in the
+ Changes the intended display aspect at an arbitrary point in the
filter chain. Aspect can be given as a fraction (4/3) or floating point
- number (1.33). Alternatively, you may specify the exact display width and
- height desired. Note that this filter does *not* do any scaling itself; it
+ number (1.33). Note that this filter does *not* do any scaling itself; it
just affects what later scalers (software or hardware) will do when
auto-scaling to the correct aspect.
``<w>,<h>``
- New display width and height.
+ New aspect ratio given by a display width and height. Unlike older mpv
+ versions or MPlayer, this does not set the display size.
Can also be these special values:
@@ -289,27 +289,27 @@ Available filters are:
:prophoto: ProPhoto RGB (ROMM)
:cie1931: CIE 1931 RGB
- ``<gamma>``
- Gamma function the source file was encoded with. Normally this should be set
- in the file header, but when playing broken or mistagged files this can be
- used to override the setting.
+ ``<gamma>``
+ Gamma function the source file was encoded with. Normally this should be set
+ in the file header, but when playing broken or mistagged files this can be
+ used to override the setting.
- This option only affects video output drivers that perform color management.
+ This option only affects video output drivers that perform color management.
- If this option is set to ``auto`` (which is the default), the gamma will
- be set to BT.1886 for YCbCr content, sRGB for RGB content and Linear for
- XYZ content.
+ If this option is set to ``auto`` (which is the default), the gamma will
+ be set to BT.1886 for YCbCr content, sRGB for RGB content and Linear for
+ XYZ content.
- Available gamma functions are:
+ Available gamma functions are:
- :auto: automatic selection (default)
- :bt.1886: ITU-R BT.1886 (approximation of BT.601/BT.709/BT.2020 curve)
- :srgb: IEC 61966-2-4 (sRGB)
- :linear: Linear light
- :gamma1.8: Pure power curve (gamma 1.8)
- :gamma2.2: Pure power curve (gamma 2.2)
- :gamma2.8: Pure power curve (gamma 2.8)
- :prophoto: ProPhoto RGB (ROMM) curve
+ :auto: automatic selection (default)
+ :bt.1886: ITU-R BT.1886 (approximation of BT.601/BT.709/BT.2020 curve)
+ :srgb: IEC 61966-2-4 (sRGB)
+ :linear: Linear light
+ :gamma1.8: Pure power curve (gamma 1.8)
+ :gamma2.2: Pure power curve (gamma 2.2)
+ :gamma2.8: Pure power curve (gamma 2.8)
+ :prophoto: ProPhoto RGB (ROMM) curve
``<stereo-in>``
Set the stereo mode the video is assumed to be encoded in. Takes the
@@ -438,9 +438,8 @@ Available filters are:
generating an occasional mismatched frame, but it may also cause an
excessive number of frames to be dropped during high motion sequences.
Conversely, setting it to -1 will make ``pullup`` match fields more
- easily. This may help processing of video where there is slight
- blurring between the fields, but may also cause there to be interlaced
- frames in the output.
+ easily. This may help process video with slight blurring between the
+ fields, but may also cause interlaced frames in the output.
``mp`` (metric plane)
This option may be set to ``u`` or ``v`` to use a chroma plane instead of the
@@ -455,16 +454,15 @@ Available filters are:
``<mode>``
:frame: Output 1 frame for each frame.
- :field: Output 1 frame for each field.
+ :field: Output 1 frame for each field (default).
:frame-nospatial: Like ``frame`` but skips spatial interlacing check.
:field-nospatial: Like ``field`` but skips spatial interlacing check.
``<interlaced-only>``
- :no: Deinterlace all frames (default).
- :yes: Only deinterlace frames marked as interlaced (default if this
- filter is inserted via ``deinterlace`` property).
+ :no: Deinterlace all frames.
+ :yes: Only deinterlace frames marked as interlaced (default).
- This filter, is automatically inserted when using the ``d`` key (or any
+ This filter is automatically inserted when using the ``d`` key (or any
other key that toggles the ``deinterlace`` property or when using the
``--deinterlace`` switch), assuming the video output does not have native
deinterlacing support.
@@ -473,7 +471,7 @@ Available filters are:
into ``--vf-defaults`` instead, and enable deinterlacing with ``d`` or
``--deinterlace``.
- Also note that the ``d`` key is stupid enough to insert a deinterlacer twice
+ Also, note that the ``d`` key is stupid enough to insert a deinterlacer twice
when inserting yadif with ``--vf``, so using the above methods is
recommended.
@@ -645,10 +643,11 @@ Available filters are:
``buffered-frames``
Maximum number of decoded video frames that should be buffered before
the filter (default: 4). This specifies the maximum number of frames
- the script can requests backwards. E.g. if ``buffered-frames=5``, and
- the script just requested frame 15, it can still request frame 10, but
- frame 9 is not available anymore. If it requests frame 30, mpv will
- decode 15 more frames, and keep only frames 25-30.
+ the script can request in reverse direction.
+ E.g. if ``buffered-frames=5``, and the script just requested frame 15,
+ it can still request frame 10, but frame 9 is not available anymore.
+ If it requests frame 30, mpv will decode 15 more frames, and keep only
+ frames 25-30.
The actual number of buffered frames also depends on the value of the
``concurrent-frames`` option. Currently, both option values are
@@ -803,6 +802,6 @@ Available filters are:
``buffer=<num>``
Buffer ``<num>`` frames in the filter chain. This filter is probably pretty
- useless, except for debugging. (Note that this won't help smoothing out
+ useless, except for debugging. (Note that this won't help to smooth out
latencies with decoding, because the filter will never output a frame if
the buffer isn't full, except on EOF.)
diff --git a/DOCS/man/vo.rst b/DOCS/man/vo.rst
index 697584c..58bd91c 100644
--- a/DOCS/man/vo.rst
+++ b/DOCS/man/vo.rst
@@ -444,13 +444,15 @@ Available video output drivers are:
Disable the scaler if the video image is not resized. In that case,
``bilinear`` is used instead whatever is set with ``scale``. Bilinear
will reproduce the source image perfectly if no scaling is performed.
- Note that this option never affects ``cscale``.
+ Enabled by default. Note that this option never affects ``cscale``.
``pbo``
- Enable use of PBOs. This is slightly faster, but can sometimes lead to
- sporadic and temporary image corruption (in theory, because reupload
- is not retried when it fails), and perhaps actually triggers slower
- paths with drivers that don't support PBOs properly.
+ Enable use of PBOs. On some drivers this can be faster, especially if
+ the source video size is huge (e.g. so called "4K" video). On other
+ drivers it might be slower or cause latency issues.
+
+ In theory, this can sometimes lead to sporadic and temporary image
+ corruption (because reupload is not retried when it fails).
``dither-depth=<N|no|auto>``
Set dither target depth to N. Default: no.
@@ -481,7 +483,7 @@ Available video output drivers are:
``temporal-dither``
Enable temporal dithering. (Only active if dithering is enabled in
- general.) This changes between 8 different dithering pattern on each
+ general.) This changes between 8 different dithering patterns on each
frame by changing the orientation of the tiled dithering matrix.
Unfortunately, this can lead to flicker on LCD displays, since these
have a high reaction time.
@@ -492,7 +494,7 @@ Available video output drivers are:
video frame, 2 on every other frame, etc.
``debug``
- Check for OpenGL errors, i.e. call ``glGetError()``. Also request a
+ Check for OpenGL errors, i.e. call ``glGetError()``. Also, request a
debug OpenGL context (which does nothing with current graphics drivers
as of this writing).
@@ -547,6 +549,20 @@ Available video output drivers are:
manifest themselves as short flashes or fringes of black, mostly
around moving edges) in exchange for potentially adding more blur.
+ ``interpolation-threshold=<0..1,-1>``
+ Threshold below which frame ratio interpolation gets disabled (default:
+ ``0.0001``). This is calculated as ``abs(disphz/vfps - 1) < threshold``,
+ where ``vfps`` is the speed-adjusted display FPS, and ``disphz`` the
+ display refresh rate.
+
+ The default is intended to almost always enable interpolation if the
+ playback rate is even slightly different from the display refresh rate.
+ But note that if you use e.g. ``--video-sync=display-vdrop``, small
+ deviations in the rate can disable interpolation and introduce a
+ discontinuity every other minute.
+
+ Set this to ``-1`` to disable this logic.
+
``dscale-radius``, ``cscale-radius``, ``tscale-radius``, etc.
Set filter parameters for ``dscale``, ``cscale`` and ``tscale``,
respectively.
@@ -655,7 +671,11 @@ Available video output drivers are:
These files must define a function with the following signature::
- vec4 sample(sampler2D tex, vec2 pos, vec2 tex_size)
+ vec4 sample_pixel(sampler2D tex, vec2 pos, vec2 tex_size)
+
+ (If there is no string ``sample_pixel`` in the shader script, it will
+ use ``sample`` instead. This is a compatibility hack for older shader
+ scripts, and is deprecated.)
The meanings of the parameters are as follows:
@@ -741,7 +761,7 @@ Available video output drivers are:
``glfinish``
Call ``glFinish()`` before and after swapping buffers (default: disabled).
- Slower, but might help getting better results when doing framedropping.
+ Slower, but might improve results when doing framedropping.
Can completely ruin performance. The details depend entirely on the
OpenGL driver.
@@ -770,9 +790,9 @@ Available video output drivers are:
The value ``auto`` will try to determine whether the compositor is
active, and calls ``DwmFlush`` only if it seems to be.
- This may help getting more consistent frame intervals, especially with
- high-fps clips - which might also reduce dropped frames. Typically a
- value of ``windowed`` should be enough since full screen may bypass the
+ This may help to get more consistent frame intervals, especially with
+ high-fps clips - which might also reduce dropped frames. Typically, a
+ value of ``windowed`` should be enough, since full screen may bypass the
DWM.
Windows only.
@@ -794,7 +814,7 @@ Available video output drivers are:
angle
Direct3D11 through the OpenGL ES translation layer ANGLE. This
supports almost everything the ``win`` backend does, except ICC
- profiles, high bit depth video input, and the ``nnedi3`` prescaler.
+ profiles, and the ``nnedi3`` prescaler.
dxinterop (experimental)
Win32, using WGL for rendering and Direct3D 9Ex for presentation.
Works on Nvidia and AMD only.
@@ -822,8 +842,8 @@ Available video output drivers are:
influence performance and quality of the video output.
``fmt`` can be one of: rgb, rgba, rgb8, rgb10, rgb10_a2, rgb16, rgb16f,
rgb32f, rgba12, rgba16, rgba16f, rgba32f.
- Default: ``auto``, which maps to rgba16 on desktop GL, and rgb10_a2 on
- GLES (e.g. ANGLE).
+ Default: ``auto``, which maps to rgba16 on desktop GL, and rgba16f or
+ rgb10_a2 on GLES (e.g. ANGLE).
``gamma=<0.1..2.0>``
Set a gamma value (default: 1.0). If gamma is adjusted in other ways
@@ -955,9 +975,11 @@ Available video output drivers are:
things like softsubbed ASS signs to match the video colors,
but may cause SRT subtitles or similar to look slightly off.
- ``alpha=<blend|yes|no>``
- Decides what to do if the input has an alpha component (default: blend).
+ ``alpha=<blend-tiles|blend|yes|no>``
+ Decides what to do if the input has an alpha component.
+ blend-tiles
+ Blend the frame against a 16x16 gray/white tiles background (default).
blend
Blend the frame against a black background.
yes
@@ -984,7 +1006,7 @@ Available video output drivers are:
This is equivalent to::
- --vo=opengl:scale=spline36:cscale=spline36:dscale=mitchell:dither-depth=auto:correct-downscaling:sigmoid-upscaling:pbo:deband:es=no
+ --vo=opengl:scale=spline36:cscale=spline36:dscale=mitchell:dither-depth=auto:correct-downscaling:sigmoid-upscaling:deband:es=no
Note that some cheaper LCDs do dithering that gravely interferes with
``opengl``'s dithering. Disabling dithering with ``dither-depth=no`` helps.
diff --git a/README.md b/README.md
index 62735a3..6405add 100644
--- a/README.md
+++ b/README.md
@@ -111,10 +111,6 @@ the separately available build wrapper ([mpv-build][mpv-build]) that first compi
libraries and libass, and then compiles the player statically linked against
those.
-If you are running Mac OSX and using homebrew we provide [homebrew-mpv][homebrew-mpv], an up
-to date formula that compiles mpv with sensible dependencies and defaults for
-OSX.
-
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].
diff --git a/RELEASE_NOTES b/RELEASE_NOTES
index faebe00..74790c7 100644
--- a/RELEASE_NOTES
+++ b/RELEASE_NOTES
@@ -1,3 +1,190 @@
+Release 0.16.0
+==============
+
+This release changes the license of some non-MPlayer source files to LGPL 2.1 or later.
+
+
+Build System Changes
+--------------------
+
+- build: enable vaapi under drm-only as well (issue #2808)
+- build: enable vo_opengl_cb if GL headers are present
+- build: make libavfilter mandatory
+- build: make posix_spawn optional
+- wscript: don’t install the encoding profiles with encoding disabled
+
+
+Features
+--------
+
+New
+~~~
+
+- Initial Android support
+- ao: initial OpenSL ES support
+- dxva2: support HEVC Main 10
+- osc: add always-on mode and unify visibility mode (always/never/auto)
+- player: add complex filter graph support
+- rpi: add mpeg-4, vc-1 decoding support
+- stream_dvb: support frontends with multiple delivery systems (e.g. DVB-C/DVB-T combo cards)
+- vo_opengl: 10 bit support with ANGLE
+- vo_opengl: add KMS/DRM VAAPI hardware decoding interop
+- vo_opengl: dxinterop: add dxva2 passthrough
+- vo_rpi: add geometry handling (--geometry, --autofit, fullscreen switching, etc.)
+- vo_x11: add 16bpp support
+
+
+Options and Commands
+--------------------
+
+Added
+~~~~~
+
+- --lavfi-complex option for complex filter graphs
+- audio: change downmix behavior, add --audio-normalize-downmix
+- command: add vf-command and af-command commands
+- player: add --external-file option
+- vo_opengl: add interpolation-threshold sub-option
+
+
+Changed
+~~~~~~~
+
+- audio: change --audio-channels default back to stereo
+- audio: remove default preference for libdcadec (decoder was merged with FFmpeg)
+- command: always allow setting volume/mute properties
+- command: show original aspect in video-aspect property too
+- input: ignore --input-cursor for events injected by input commands (issue #2750)
+- options: set fs=yes by default on RPI, and change RPI defaults handling
+- sub: implement "sub-seek 0" (issue #2791)
+- vo_opengl: default scaler-resizes-only sub-option to yes
+
+
+Fixes and Minor Enhancements
+----------------------------
+
+- OS X/cocoa: fix charcode retrieving for accented characters
+- TOOLS/lua/ao-null-reload.lua: send ao-reload on audio-device-list change (issue #2738)
+- TOOLS/lua/autoload.lua: remove the extension prior to sort
+- Windows: fix dropping URIs (issue #2782)
+- af_lavrresample: prevent channels from being dropped, e.g. when going 7.1 -> 7.1(wide) and similar cases
+- ao_coreaudio: fix 7.1(rear) channel mapping
+- ao_openal: wipe out global context on init error (PR #2719)
+- ao_wasapi: avoid under-run cascade in exclusive mode
+- ao_wasapi: set buffer size to device period in exclusive mode
+- audio: fix spdif PCM fallback
+- build: add special openbsd case for iconv check (issue #2710)
+- command: fix NULL pointer deref in "video-codec" property (issue #2729)
+- command: fix track cycling logic (issue #2784)
+- demux: disable stream cache if no tracks are selected (issue #2692)
+- demux_mkv: add hack to fix opus gapless behavior
+- demux_mkv: support channel layout in VfW muxed PCM (issue #2820)
+- osc: fix runtime enable_osc(true/false)
+- player: fix initial audio sync in certain cases (issue #2770)
+- player: honor --force-window if video is selected, but inactive
+- player: never show "DS: (unavailable)"
+- player: restore old/correct --force-window behavior (issue #2825)
+- player: rewrite timeline/ordered chapter support
+- vaapi: fix compilation on older FFmpeg/Libav (issue #2737)
+- vdpau: force driver to report preemption early
+- video: don't wait for last video frame in the normal case (issue #2745)
+- video: fix coverart switching
+- video: slightly improve video stream switching
+- vo_opengl: add precision qualifier to usampler2D on ANGLE (issue #2761)
+- vo_opengl: default to rgba16f FBOs on ANGLE
+- vo_opengl: don't use normalized coords for debanding rectangle textures (issue #2831)
+- vo_opengl: dxinterop: fix compatibility issue with Vista
+- vo_opengl: pass the correct target to deband functions with Apple hwdec interop
+- vo_opengl: rename custom shader entrypoint from sample to sample_pixel (issue #2733)
+- x11: get *current* XRandR screen configuration instead of polling for new screens, too
+
+
+This listing is not complete. Check DOCS/client-api-changes.rst for a history
+of changes to the client API. A complete changelog can be seen by running
+`git log v0.15.0..v0.16.0` in the git repository or by visiting either
+https://github.com/mpv-player/mpv/compare/v0.15.0...v0.16.0 or
+http://git.srsfckn.biz/mpv/log/?qt=range&q=v0.15.0..v0.16.0
+
+
+
+Release 0.15.0
+==============
+
+Build System Changes
+--------------------
+
+- OS X bundle: remove git sha from the Info.plist version (issue #2677)
+- add "lua51" ("51obsd") to list of possible lua names
+- add option to customize config files system path (issue #2704)
+
+
+Features
+--------
+
+New
+~~~
+
+- vo_opengl: implement support for transparent video display on OS X (alpha=yes suboption)
+- vo_opengl: use a checkerboard pattern as background for transparent video by default
+
+
+Options and Commands
+--------------------
+
+Added
+~~~~~
+
+- add --audio-file-paths (issue #2632)
+- player, stream_dvb: implement dvb-channel-name property, add switch binding
+
+
+Changed
+~~~~~~~
+
+- vf_stereo3d: add alternating modes
+- vo_opengl: disable pbo by default for opengl-hq due to driver problems
+- vf_yadif: change defaults (issue #2539)
+- command: change heuristic for files with 1 chapter (issue #2550)
+- demux_mkv: adjust subtitle preroll defaults
+- exclude 360 from --video-rotate range (issue #2647)
+- osd: make osd-width/height properties watchable
+
+
+Fixes and Minor Enhancements
+----------------------------
+
+- ao_pulse: check for sample rate bounds, attempt fallback (issue #2654)
+- ao_wasapi: remove volume "restore" on exit
+- demux_cue: better error resilience
+- mixer: fix volume initialization with --af=volume
+- mpv.desktop: add audio/mp4 mime type
+- player: detect audio PTS jumps, make video PTS heuristic less aggressive
+- player: make watch later/resume work when "playing" directories
+- player: reset playback abort when reloading a file (issue #2568)
+- recognize frame sequenced 3D Matroska video
+- stream_dvb: fix channel switching
+- vaapi: add VP9 profile (requires VA-API 0.38.1 or newer)
+- vo_opengl: dxinterop: prevent crash after lost device
+- vo_opengl: enable brightness/contrast controls for RGB
+- vo_opengl: fix operation on GLES 2.0
+- vo_opengl: fix operation on GLSL versions earlier than 1.30
+- vo_opengl: flip screenshot image if backend uses flipped rendering (issue #2635)
+- vo_opengl: reset nnedi3 weights properly (issue #2661)
+- vo_rpi: handle rotation
+- vo_rpi: work around firmware oddness leading to incorrect video rect
+- windows: fix fd://
+- ytdl: Include Referer header as well
+- TOOLS/zsh.pl: add .opus extension in zsh completions
+
+
+This listing is not complete. Check DOCS/client-api-changes.rst for a history
+of changes to the client API. A complete changelog can be seen by running
+`git log v0.14.0..v0.15.0` in the git repository or by visiting either
+https://github.com/mpv-player/mpv/compare/v0.14.0...v0.15.0 or
+http://git.srsfckn.biz/mpv/log/?qt=range&q=v0.14.0..v0.15.0
+
+
+
Release 0.14.0
==============
diff --git a/TOOLS/lua/ao-null-reload.lua b/TOOLS/lua/ao-null-reload.lua
new file mode 100644
index 0000000..5b2330b
--- /dev/null
+++ b/TOOLS/lua/ao-null-reload.lua
@@ -0,0 +1,20 @@
+-- Handles the edge case where previous attempts to init audio have failed, but
+-- might start working due to a newly added device. This is required in
+-- particular for ao=wasapi, since the internal IMMNotificationClient code that
+-- normally triggers ao-reload will not be running in this case.
+
+function do_reload()
+ mp.command("ao-reload")
+ reloading = nil
+end
+
+function on_audio_device_list_change()
+ if mp.get_property("current-ao") == "null" and not reloading then
+ mp.msg.verbose("audio-device-list changed: reloading audio")
+ -- avoid calling ao-reload too often
+ reloading = mp.add_timeout(0.5, do_reload)
+ end
+end
+
+mp.set_property("options/audio-fallback-to-null", "yes")
+mp.observe_property("audio-device-list", "native", on_audio_device_list_change)
diff --git a/TOOLS/lua/autoload.lua b/TOOLS/lua/autoload.lua
index 0c8c9a5..1f22bb4 100644
--- a/TOOLS/lua/autoload.lua
+++ b/TOOLS/lua/autoload.lua
@@ -72,6 +72,11 @@ function find_and_add_entries()
return EXTENSIONS[string.lower(ext)]
end)
table.sort(files, function (a, b)
+ local len = string.len(a) - string.len(b)
+ if len ~= 0 then -- case for ordering filename ending with such as X.Y.Z
+ local ext = string.len(get_extension(a)) + 1
+ return string.sub(a, 1, -ext) < string.sub(b, 1, -ext)
+ end
return string.lower(a) < string.lower(b)
end)
diff --git a/TOOLS/old-configure b/TOOLS/old-configure
deleted file mode 100755
index 6b5635c..0000000
--- a/TOOLS/old-configure
+++ /dev/null
@@ -1,1013 +0,0 @@
-#! /bin/sh
-#
-# Original version (C) 2000 Pontscho/fresh!mindworkz pontscho@makacs.poliod.hu
-# Cleanups all over the place (c) 2001 pl
-# Rewritten for mpv in 2014.
-#
-#
-# Warning: this is not officially supported. Use on your own risk. Might require
-# modifications when used on systems other than Linux. Contributors
-# are not expected to update these files when making changes to the
-# normal/preferred waf build system.
-#
-#
-# This configure script is *not* autoconf-based and has different semantics.
-# It attempts to autodetect all settings and options where possible. It is
-# possible to override autodetection with the --enable-option/--disable-option
-# command line parameters. --enable-option forces the option on skipping
-# autodetection. Yes, this means that compilation may fail and yes, this is not
-# how autoconf-based configure scripts behave.
-#
-# configure generates a series of configuration files:
-# - config.h contains #defines that are used in the C code.
-# - config.mak is included from the Makefiles.
-#
-# If you want to add a new check for $feature, look at the existing checks
-# and try to use helper functions where you can.
-#############################################################################
-
-# Prevent locale nonsense from breaking basic text processing utils
-export LC_ALL=C
-
-# Store the configure line that was used
-configuration="$*"
-
-# Prefer these macros to full length text !
-# These macros only return an error code - NO display is done
-command_check() {
- echo >> "$TMPLOG"
- echo "$@" >> "$TMPLOG"
- "$@" >> "$TMPLOG" 2>&1
- TMPRES="$?"
- echo >> "$TMPLOG"
- return "$TMPRES"
-}
-
-compile_check() {
- source="$1"
- shift
- echo >> "$TMPLOG"
- cat "$source" >> "$TMPLOG"
- echo >> "$TMPLOG"
- echo "$_cc $OURCFLAGS $CFLAGS $source $extra_cflags $_ld_static $extra_ldflags $libs_mplayer -o $TMPEXE $@" >> "$TMPLOG"
- rm -f "$TMPEXE"
- $_cc $OURCFLAGS $CFLAGS "$source" $extra_cflags $_ld_static $extra_ldflags $libs_mplayer -o "$TMPEXE" $@ >> "$TMPLOG" 2>&1
- TMPRES="$?"
- echo >> "$TMPLOG"
- echo >> "$TMPLOG"
- return "$TMPRES"
-}
-
-cflag_check() {
- echo "int main(void) { return 0; }" > $TMPC
- compile_check $TMPC $@
-}
-
-statement_check() {
- echo "" > $TMPC
- for _header in $1 ; do echo "#include <$_header>" >> $TMPC ; done
- echo "int main(void) { $2; return 0; }" >> $TMPC
- shift 2
- compile_check $TMPC $@
-}
-
-pkg_config_add() {
- echo >> "$TMPLOG"
- echo "$_pkg_config --cflags $@" >> "$TMPLOG"
- ctmp=$($_pkg_config --cflags "$@" 2>> "$TMPLOG") || return $?
- echo >> "$TMPLOG"
- echo "$_pkg_config --libs $@" >> "$TMPLOG"
- ltmp=$($_pkg_config --libs "$@" 2>> "$TMPLOG") || return $?
- echo >> "$TMPLOG"
- echo "cflags: $ctmp" >> "$TMPLOG"
- echo "libs: $ltmp" >> "$TMPLOG"
- echo >> "$TMPLOG"
- extra_cflags="$extra_cflags $ctmp"
- libs_mplayer="$libs_mplayer $ltmp"
-}
-
-# Display error message, flushes tempfile, exit
-die () {
- echo
- echo "Error: $@" >&2
- echo >&2
- rm -f "$TMPEXE" "$TMPC"
- echo "Check \"$TMPLOG\" if you do not understand why it failed."
- exit 1
-}
-
-# Use this before starting a check
-echocheck() {
- echo "============ Checking for $@ ============" >> "$TMPLOG"
- echo ${_echo_n} "Checking for $@ ... ${_echo_c}"
-}
-
-# Use this to echo the results of a check
-echores() {
- test "$res_comment" && res_comment="($res_comment)"
- echo "Result is: $@ $res_comment" >> "$TMPLOG"
- echo "##########################################" >> "$TMPLOG"
- echo "" >> "$TMPLOG"
- echo "$@ $res_comment"
- res_comment=""
-}
-
-# Check how echo works in this /bin/sh
-case $(echo -n) in
- -n) _echo_n= _echo_c='\c' ;; # SysV echo
- *) _echo_n='-n ' _echo_c= ;; # BSD echo
-esac
-
-# setind $a b sets the variable named by the value of the variable a to b
-setind() { eval "$1=\"\$2\"" ; }
-
-# Generate --enable-NAME/--disable-NAME options, set $1 to the option value.
-# Since shell has no data structures, do a weird statemachine thing.
-# Arguments: "_name"($1) "description"($2) "default"($3)
-# If "default"($3) is empty, use "auto"
-# Option name: a leading "_" in name is stripped, further "_" are changed to "-"
-opt_yes_no() {
- _name=$(echo "$1" | sed 's/^_//' | tr _ -)
- _defval="$3"
- test -z "$_defval" && _defval=auto
-
- case "$_opt_state_mode" in
- init)
- setind "$1" "$_defval"
- ;;
- help)
- if test "$_defval" = yes || test "$_defval" = auto ; then
- _defdesc=enable
- test "$_defval" = auto && _defdesc=auto
- printf " %-21s disable $2 [$_defdesc]\n" "--disable-$_name"
- unset _defdesc
- else
- printf " %-21s enable $2 [disable]\n" "--enable-$_name"
- fi
- ;;
- parse)
- if test "$_opt_state_name" = "--enable-$_name" ; then
- setind "$1" yes
- _opt_state_known=yes
- elif test "$_opt_state_name" = "--disable-$_name" ; then
- setind "$1" no
- _opt_state_known=yes
- elif test "$_opt_state_name" = "--auto-$_name" ; then
- setind "$1" auto
- _opt_state_known=yes
- fi
- ;;
- esac
- unset _name
- unset _defval
-}
-
-options_state_machine() {
- _opt_state_mode=$1
- _opt_state_name=$2
- _opt_state_known=no
-
- opt_yes_no _gl "OpenGL video output"
- opt_yes_no _libguess "libguess"
- opt_yes_no _termios "termios database for key codes"
- opt_yes_no _iconv "iconv for encoding conversion"
- opt_yes_no _vm "X video mode extensions"
- opt_yes_no _dvb "DVB input"
- opt_yes_no _tv "TV interface (TV/DVB grabbers)" yes
- opt_yes_no _tv_v4l2 "Video4Linux2 TV interface"
- opt_yes_no _libv4l2 "libv4l2"
- opt_yes_no _smb "Samba (SMB) input"
- opt_yes_no _lcms2 "LCMS2 support"
- opt_yes_no _bluray "Blu-ray support"
- opt_yes_no _dvdread "libdvdread"
- opt_yes_no _dvdnav "libdvdnav"
- opt_yes_no _enca "ENCA charset oracle library"
- opt_yes_no _uchardet "uchardet charset detection library"
- opt_yes_no _libass "subtitle rendering with libass"
- opt_yes_no _libavdevice "libavdevice demuxers"
- opt_yes_no _libavfilter "libavfilter"
- opt_yes_no _jpeg "support for writing JPEG screenshots"
- opt_yes_no _libcdio "libcdio support"
- opt_yes_no _librubberband "librubberband support"
- opt_yes_no _ffmpeg "skip FFmpeg/Libav autodetection"
- opt_yes_no _libavresample "libavresample (preferred over libswresample)"
- opt_yes_no _libswresample "libswresample"
- opt_yes_no _caca "CACA video output"
- opt_yes_no _sdl2 "SDL2 video and audio outputs" no
- opt_yes_no _xv "Xv video output"
- opt_yes_no _vdpau "VDPAU acceleration"
- opt_yes_no _vaapi "VAAPI acceleration"
- opt_yes_no _xrandr "Xrandr support (used for monitor FPS detection)"
- opt_yes_no _xinerama "Xinerama support"
- opt_yes_no _x11 "X11 video output"
- opt_yes_no _wayland "Wayland video output"
- opt_yes_no _xss "support for disabling screensaver via xss"
- opt_yes_no _alsa "ALSA audio output"
- opt_yes_no _ossaudio "OSS audio output"
- opt_yes_no _rsound "RSound audio output"
- opt_yes_no _sndio "sndio audio output"
- opt_yes_no _pulse "Pulseaudio audio output"
- opt_yes_no _jack "JACK audio output"
- opt_yes_no _openal "OpenAL audio output"
- opt_yes_no _shm "X11/Xv shared memory"
- opt_yes_no _lua "Lua scripting"
- opt_yes_no _vapoursynth "VapourSynth filter bridge (Python)"
- opt_yes_no _vapoursynth_lazy "VapourSynth filter bridge (Lua)"
- opt_yes_no _libarchive "libarchive"
- opt_yes_no _encoding "encoding functionality" yes
- opt_yes_no _build_man "building manpage"
-}
-
-show_help(){
-cat << EOF
-Usage: $0 [OPTIONS]...
-
-Configuration:
- -h, --help display this help and exit
-
-Installation directories:
- --prefix=DIR prefix directory for installation [/usr/local]
- --bindir=DIR directory for installing binaries [PREFIX/bin]
- --datadir=DIR directory for installing machine independent
- data files (skins, etc) [PREFIX/share/mpv]
- --mandir=DIR directory for installing man pages [PREFIX/share/man]
- --confdir=DIR directory for installing configuration files
- [PREFIX/etc/mpv]
-
-Compilation options:
- --cc=COMPILER C compiler to build mpv [gcc]
- --pkg-config=PKGCONFIG pkg-config to find some libraries [pkg-config]
- --enable-static build a statically linked binary
- --disable-debug compile-in debugging information [enable]
- --disable-optimization compile without -O2 [enable]
-
-Use these options if autodetection fails:
- --extra-cflags=FLAGS extra CFLAGS
- --extra-ldflags=FLAGS extra LDFLAGS
- --extra-libs=FLAGS extra linker flags
-
-Features:
-EOF
-options_state_machine help
-cat << EOF
-
-This configure script is NOT autoconf-based, even though its output is similar.
-It will try to autodetect all configuration options. If you --enable an option
-it will be forcefully turned on, skipping autodetection. This can break
-compilation, so you need to know what you are doing.
-EOF
-exit 0
-} #show_help()
-
-# GOTCHA: the variables below defines the default behavior for autodetection
-# and have - unless stated otherwise - at least 2 states : yes no
-# If autodetection is available then the third state is: auto
-_pkg_config=auto
-_cc=auto
-test -n "$CC" && _cc="$CC"
-_opt=-O2
-_prefix="/usr/local"
-options_state_machine init
-for ac_option do
- case "$ac_option" in
- --help|-help|-h)
- show_help
- ;;
- --prefix=*)
- _prefix=$(echo $ac_option | cut -d '=' -f 2)
- ;;
- --bindir=*)
- _bindir=$(echo $ac_option | cut -d '=' -f 2)
- ;;
- --mandir=*)
- _mandir=$(echo $ac_option | cut -d '=' -f 2)
- ;;
- --confdir=*)
- _confdir=$(echo $ac_option | cut -d '=' -f 2)
- ;;
- --extra-cflags=*)
- extra_cflags="$extra_cflags $(echo $ac_option | cut -d '=' -f 2-)"
- ;;
- --extra-ldflags=*)
- extra_ldflags="$extra_ldflags $(echo $ac_option | cut -d '=' -f 2-)"
- ;;
- --extra-libs=*)
- libs_mplayer=$(echo $ac_option | cut -d '=' -f 2)
- ;;
- --cc=*)
- _cc=$(echo $ac_option | cut -d '=' -f 2)
- ;;
- --pkg-config=*)
- _pkg_config=$(echo $ac_option | cut -d '=' -f 2)
- ;;
- --enable-static)
- _ld_static='-static'
- ;;
- --disable-static)
- _ld_static=''
- ;;
- --enable-optimization)
- _opt='-O2'
- ;;
- --enable-optimization=*)
- _opt=$(echo $_echo_n '-O'$_echo_c; echo $ac_option | cut -d '=' -f 2)
- ;;
- --disable-optimization)
- _opt=
- ;;
- *)
- options_state_machine parse "$ac_option"
- if test "$_opt_state_known" != yes ; then
- echo "Unknown parameter: $ac_option" >&2
- exit 1
- fi
- ;;
-
- esac
-done
-
-test -z "$_bindir" && _bindir="$_prefix/bin"
-test -z "$_mandir" && _mandir="$_prefix/share/man"
-test -z "$_confdir" && _confdir="$_prefix/etc/mpv"
-
-mplayer_tmpdir=$(mktemp -d -p ${TMPDIR:=/tmp} mpv-configure-XXXXXX)
-test -n "$mplayer_tmpdir" || die "Unable to create tmpdir."
-trap 'rm -rf "$mplayer_tmpdir"' EXIT
-
-mkdir old_build 2> /dev/null
-
-TMPLOG="old_build/config.log"
-
-rm -f "$TMPLOG"
-echo Parameters configure was run with: > "$TMPLOG"
-echo CFLAGS="'$CFLAGS'" PKG_CONFIG_PATH="'$PKG_CONFIG_PATH'" ./configure $configuration >> "$TMPLOG"
-echo >> "$TMPLOG"
-
-TMPC="$mplayer_tmpdir/tmp.c"
-TMPEXE="$mplayer_tmpdir/tmp"
-CONFIG_MAK="$mplayer_tmpdir/config.mak"
-CONFIG_H="$mplayer_tmpdir/config.h"
-
-echo > $CONFIG_MAK
-echo > $CONFIG_H
-
-test "$_pkg_config" = auto && _pkg_config=pkg-config
-test "$_cc" = auto && _cc=cc
-
-extra_cflags="-I. -D_GNU_SOURCE $extra_cflags"
-
-_rst2man=rst2man
-test -f "$(which rst2man.py)" && _rst2man=rst2man.py
-
-echocheck "whether to build manpages with rst2man"
-if test "$_build_man" = auto ; then
- _build_man=no
- command_check "$_rst2man" --version && _build_man=yes
-fi
-echores "$_build_man"
-
-echocheck "working compiler"
-cflag_check "" || die "Compiler is not functioning correctly. Check your installation and custom CFLAGS $CFLAGS ."
-echores "yes"
-
-echocheck "perl"
-command_check perl -Mv5.8 -e';' || die "Perl is not functioning correctly or is ancient. Install the latest perl available."
-echores yes
-
-echocheck "compiler support of -pipe option"
-cflag_check -pipe -I. && _pipe="-pipe" && echores "yes" || echores "no"
-
-addcflags() { cflag_check "$@" && OURCFLAGS="$OURCFLAGS $@" ; }
-
-OURCFLAGS="-std=c99 -Wall $_opt"
-
-addcflags -g -g3 -ggdb
-addcflags -Wundef -Wmissing-prototypes -Wshadow -Wno-switch -Wparentheses -Wpointer-arith -Wno-redundant-decls -Wno-pointer-sign -Werror=implicit-function-declaration -Wno-error=deprecated-declarations -Wno-error=unused-function
-# clang
-addcflags -Wno-logical-op-parentheses -fcolor-diagnostics -Wno-tautological-compare -Wno-tautological-constant-out-of-range-compare
-# extra
-addcflags -Wno-format-zero-length -Wempty-body -Wdisabled-optimization -Wstrict-prototypes
-
-cflag_check -MD -MP && DEPFLAGS="-MD -MP"
-cflag_check -lm && _ld_lm="-lm"
-
-extra_ldflags="$extra_ldflags $LDFLAGS"
-extra_cflags="$extra_cflags $CPPFLAGS"
-
-# If $1 is "yes", define $2 as 1 in config.h, else define it as 0
-define_yes_no() {
- if test "$1" = yes ; then
- echo "#define $2 1" >> $CONFIG_H
- else
- echo "#define $2 0" >> $CONFIG_H
- fi
-}
-
-# Write the results of a check to config.mak/.h.
-# Arguments: "yes/no"($1) "name"($2)
-check_yes_no() {
- define_yes_no $1 "HAVE_$2"
- echo "$2 = $1" >> $CONFIG_MAK
-}
-
-check_trivial() {
- echocheck "$1"
- check_yes_no $2 $3
- echores "$2"
-}
-
-# Arguments: "message"($1) "setting"($2) "name"($3) "code"($4)
-# Also, $5 - $N can be libraries needed - it'll try each separately.
-# Use " " as first entry if you want to try with no libraries too.
-check_compile() {
- _res="$2"
- _name="$3"
- _code="$4"
- echocheck "$1"
- if test $_res = auto ; then
- _res=no
- if test $# -gt 4 ; then
- shift 4
- else
- shift $#
- fi
- while true ; do
- compile_check "$_code" "$1" && libs_mplayer="$libs_mplayer $1" && _res=yes && break
- test -z "$1" && break
- shift
- done
- fi
- check_yes_no $_res $_name
- echores $_res
- test $_res = yes && return 0 || return 1
-}
-
-# Arguments: "message"($1) "setting"($2) "name"($3) "include"($4) "statement"($5)
-# Also, $6 - $N can be libraries needed - it'll try each separately.
-# Use " " as first entry if you want to try with no libraries too.
-check_statement_libs() {
- _res="$2"
- _name="$3"
- _inc="$4"
- _st="$5"
- echocheck "$1"
- if test $_res = auto ; then
- _res=no
- if test $# -gt 5 ; then
- shift 5
- else
- shift $#
- fi
- while true ; do
- statement_check "$_inc" "$_st" "$1" && libs_mplayer="$libs_mplayer $1" && _res=yes && break
- test -z "$1" && break
- shift
- done
- fi
- check_yes_no $_res $_name
- echores $_res
- test $_res = yes && return 0 || return 1
-}
-
-# Print "yes" if previous command succeeded, else "no"
-defretval() { # shell is retarded?
- if test $? = 0 ; then
- echo "yes"
- else
- echo "no"
- fi
-}
-
-echocheck "dynamic loader"
-_dl=no
-for _ld_tmp in "" "-ldl"; do
- statement_check dlfcn.h 'dlopen("", 0)' $_ld_tmp && _ld_dl="$_ld_tmp" && _dl=yes && break
-done
-define_yes_no $_dl HAVE_LIBDL
-echores "$_dl"
-
-echocheck "pthread"
-cflag_check -pthread && _ld_pthread="$_ld_pthread -pthread"
-cflag_check -lpthread && _ld_pthread="$_ld_pthread -lpthread"
-cflag_check -lrt && _ld_pthread="$_ld_pthread -lrt"
-extra_cflags="$extra_cflags -D_REENTRANT -D_THREAD_SAFE"
-compile_check waftools/fragments/pthreads.c "$_ld_pthread" || die "Unable to find pthreads support."
-echores "yes"
-
-check_statement_libs "support for stdatomic.h" auto STDATOMIC \
- stdatomic.h 'atomic_int_least64_t test = ATOMIC_VAR_INIT(123); int test2 = atomic_load(&test)' \
- " " "-latomic"
-_stdatomic=$(defretval)
-
-_atomic=auto
-test "$_stdatomic" = yes && _atomic=no
-check_statement_libs "compiler support for __atomic built-ins" $_atomic ATOMIC_BUILTINS \
- stdint.h 'int64_t test = 0; test = __atomic_add_fetch(&test, 1, __ATOMIC_SEQ_CST)' \
- " " "-latomic"
-_atomic=$(defretval)
-
-_sync=auto
-(test "$_atomic" = yes || test "$_stdatomic" = yes ) && _sync=no
-check_statement_libs "compiler support for __sync built-ins" $_sync SYNC_BUILTINS \
- stdint.h 'int64_t test = 0; test = __sync_add_and_fetch(&test, 1)'
-_sync=$(defretval)
-
-_any_atomic=yes
-if test "$_atomic" = no && test "$_sync" = no && test "$_stdatomic" = no ; then
- echo "your compiler must support either stdatomic.h, or __atomic, or __sync built-ins."
- _any_atomic=no
-fi
-define_yes_no $_any_atomic HAVE_ATOMICS
-
-check_compile "iconv" $_iconv ICONV waftools/fragments/iconv.c " " "-liconv" "-liconv $_ld_dl"
-_iconv=$(defretval)
-if test "$_iconv" != yes ; then
- die "Unable to find iconv which should be part of standard compilation environment. Aborting. If you really mean to compile without iconv support use --disable-iconv."
-fi
-
-_soundcard_header=sys/soundcard.h
-_check_snd=yes
-check_statement_libs "sys/soundcard.h" auto SYS_SOUNDCARD_H sys/soundcard.h
-test $(defretval) = yes && _soundcard_header=sys/soundcard.h && _check_snd=no
-check_statement_libs "soundcard.h" $_check_snd SOUNDCARD_H soundcard.h
-test $(defretval) = yes && _soundcard_header=soundcard.h
-
-check_statement_libs "sys/videoio.h" auto SYS_VIDEOIO_H sys/videoio.h
-
-_termios_ok=no
-check_statement_libs "termios.h" $_termios TERMIOS_H termios.h
-test $(defretval) = yes && _termios_ok=yes && _termios=no
-check_statement_libs "sys/termios.h" $_termios SYS_TERMIOS_H 'sys/termios.h'
-test $(defretval) = yes && _termios_ok=yes
-define_yes_no $_termios_ok HAVE_TERMIOS
-
-check_statement_libs "shm" $_shm SHM "sys/types.h sys/ipc.h sys/shm.h" \
- "shmget(0, 0, 0); shmat(0, 0, 0); shmctl(0, 0, 0);"
-
-echocheck "pkg-config"
-if $($_pkg_config --version > /dev/null 2>&1); then
- if test "$_ld_static"; then
- _pkg_config="$_pkg_config --static"
- fi
- echores "yes"
-else
- _pkg_config=false
- echores "no"
-fi
-
-# Arguments: "message"($1) "setting"($2) "name"($3) "pkg-config string"($4)
-# The name will be used as "#define HAVE_$name 1/0" in config.h, and as
-# "$name = yes/no" in config.mak
-# "setting"($2) is yes/no/auto and represents the --enable/--disable option
-check_pkg_config() {
- echocheck "$1"
- _res=$2
- if test "$2" = auto ; then
- _res=no
- if pkg_config_add "$4" ; then
- _res=yes
- fi
- fi
- check_yes_no $_res $3
- echores "$_res"
- test $_res = yes && return 0 || return 1
-}
-
-check_pkg_config "libguess support" $_libguess LIBGUESS 'libguess >= 1.0'
-
-check_pkg_config "Samba support (libsmbclient)" $_smb LIBSMBCLIENT 'smbclient >= 0.2.0'
-
-_wlver="1.6.0"
-check_pkg_config "Wayland" $_wayland WAYLAND "wayland-client >= $_wlver wayland-cursor >= $_wlver xkbcommon >= 0.3.0"
-_wayland=$(defretval)
-
-check_pkg_config "X11" $_x11 X11 "x11"
-_x11=$(defretval)
-
-# Disable X11 dependencies
-_xext=auto
-if test "$_x11" = no ; then
- _xss=no
- _xext=no
- _xv=no
- _vdpau=no
- _vaapi=no
- _xinerama=no
- _vm=no
-fi
-
-check_pkg_config "Xss screensaver extensions" $_xss XSS "xscrnsaver"
-
-check_pkg_config "X extensions" $_xext XEXT "xext"
-
-check_pkg_config "Xv" $_xv XV "xv"
-
-check_pkg_config "VDPAU" $_vdpau VDPAU "vdpau >= 0.2"
-_vdpau=$(defretval)
-define_yes_no $_vdpau HAVE_VDPAU_HWACCEL
-
-check_pkg_config "VAAPI" $_vaapi VAAPI 'libva >= 0.32.0 libva-x11 >= 0.32.0'
-_vaapi=$(defretval)
-define_yes_no $_vaapi HAVE_VAAPI_HWACCEL
-define_yes_no $_vaapi HAVE_VAAPI_X11
-
-_vaapi_wayland=no
-if test "$_vaapi" = yes ; then
-_vaapi_wayland=auto
-fi
-check_pkg_config "VAAPI Wayland" $_vaapi_wayland VAAPI_WAYLAND 'libva-wayland >= 0.34.0'
-
-check_pkg_config "Xinerama" $_xinerama XINERAMA 'xinerama'
-
-check_pkg_config "Xrandr" $_xrandr XRANDR 'xrandr >= 1.2.0'
-
-check_pkg_config "CACA" $_caca CACA 'caca >= 0.99.beta18'
-
-check_compile "DVB" $_dvb DVB waftools/fragments/dvb.c
-_dvbin=$(defretval)
-check_yes_no $_dvbin DVBIN
-
-check_statement_libs "JPEG support" $_jpeg JPEG "stdio.h jpeglib.h" "" "-ljpeg $_ld_lm"
-
-_gl_x11_egl=no
-(test "$_x11" = no && test "$_wayland" = no) && _gl=no
-echocheck "OpenGL"
-#Note: this test is run even with --enable-gl since we autodetect linker flags
-if test "$_gl" != no ; then
- cat > $TMPC << EOF
-#if defined(GL_WAYLAND) || defined(EGL_X11)
-#include <EGL/egl.h>
-#else
-#include <X11/Xlib.h>
-#include <GL/glx.h>
-#endif
-#include <GL/gl.h>
-#include <GL/glext.h>
-int main(int argc, char *argv[]) {
-#if defined(GL_WAYLAND)
- eglCreateContext(NULL, NULL, EGL_NO_CONTEXT, NULL);
-#else
- glXCreateContext(NULL, NULL, NULL, True);
-#endif
- glFinish();
- return !GL_INVALID_FRAMEBUFFER_OPERATION; // check correct glext.h
-}
-EOF
- _gl=no
- if test "$_x11" = yes ; then
- for _ld_tmp in "" -lGL "-lGL -lXdamage" "-lGL $_ld_pthread" ; do
- if compile_check $TMPC $_ld_tmp $_ld_lm ; then
- _gl=yes
- _gl_x11=yes
- libs_mplayer="$libs_mplayer $_ld_tmp $_ld_dl"
- test "$_gl_x11" = yes && res_comment="$res_comment x11"
- break
- fi
- done
- fi
- if test "$_wayland" = yes && compile_check $TMPC -DGL_WAYLAND -lGL -lEGL &&
- pkg_config_add "wayland-egl >= 9.0.0"; then
- _gl=yes
- _gl_wayland=yes
- libs_mplayer="$libs_mplayer -lGL -lEGL"
- test "$_gl_wayland" = yes && res_comment="$res_comment wayland"
- else
- _gl_wayland=no
- fi
- if test "$_x11" = yes && test "$_gl" = yes && pkg_config_add "egl"; then
- _gl_x11_egl=yes
- res_comment="$res_comment x11egl"
- fi
-else
- _gl=no
-fi
-
-if test "$_gl" = no ; then
- _gl_x11=no
- _gl_wayland=no
- _gl_x11_egl=no
-fi
-check_yes_no $_gl GL
-check_yes_no $_gl_x11 GL_X11
-check_yes_no $_gl_x11_egl EGL_X11
-check_yes_no $_gl_wayland GL_WAYLAND
-echores "$_gl"
-
-echocheck "VDPAU with OpenGL/X11"
-_vdpau_gl_x11=no
-(test "$_gl_x11" = yes && test "$_vdpau" = yes) && _vdpau_gl_x11=yes
-check_yes_no $_vdpau_gl_x11 VDPAU_GL_X11
-echores "$_vdpau_gl_x11"
-
-_vaapi_glx=no
-(test "$_gl_x11" = yes && test "$_vaapi" = yes) && _vaapi_glx=auto
-check_pkg_config "VAAPI with OpenGL/X11" $_vaapi_glx VAAPI_GLX 'libva-glx >= 0.32.0'
-
-_vaapi_x_egl=no
-(test "$_gl_x11_egl" = yes && test "$_vaapi" = yes) && _vaapi_x_egl=yes
-check_yes_no $_vaapi_x_egl VAAPI_X_EGL
-check_yes_no $_vaapi_x_egl VAAPI_EGL
-
-check_pkg_config "SDL 2.0" $_sdl2 SDL2 'sdl2'
-
-check_statement_libs "OSS Audio" $_ossaudio OSS_AUDIO $_soundcard_header "int x = SNDCTL_DSP_SETFRAGMENT;"
-
-check_statement_libs "RSound" $_rsound RSOUND rsound.h 'rsd_init(NULL);' -lrsound
-
-check_statement_libs "sndio" $_sndio SNDIO sndio.h 'struct sio_par par; sio_initpar(&par); const char *s = SIO_DEVANY' -lsndio
-
-check_pkg_config "PulseAudio" $_pulse PULSE 'libpulse >= 1.0'
-
-check_pkg_config "JACK" $_jack JACK 'jack'
-
-check_pkg_config "OpenAL" $_openal OPENAL 'openal >= 1.13'
-
-check_pkg_config "ALSA audio" $_alsa ALSA 'alsa >= 1.0.9'
-
-check_pkg_config "Blu-ray support" $_bluray LIBBLURAY 'libbluray >= 0.3.0'
-
-check_pkg_config "dvdread" $_dvdread DVDREAD 'dvdread >= 4.1.0'
-
-check_pkg_config "dvdnav" $_dvdnav DVDNAV 'dvdnav >= 4.2.0'
-
-check_pkg_config "libcdio" $_libcdio CDDA 'libcdio_paranoia'
-
-check_pkg_config "rubberband" $_librubberband RUBBERBAND 'rubberband'
-
-_oldass=$_libass
-check_pkg_config "SSA/ASS support" $_libass LIBASS 'libass'
-_libass=$(defretval)
-if test $_oldass != no && test $_libass = no ; then
- die "Unable to find development files for libass. Aborting. If you really mean to compile without libass support use --disable-libass."
-fi
-
-_dummy_osd=yes
-test $_libass = yes && _dummy_osd=no
-echo "LIBASS_OSD = $_libass" >> $CONFIG_MAK
-echo "DUMMY_OSD = $_dummy_osd" >> $CONFIG_MAK
-
-check_pkg_config "ENCA" $_enca ENCA 'enca'
-check_pkg_config "uchardet" $_uchardet UCHARDET 'uchardet'
-
-check_pkg_config "zlib" auto ZLIB 'zlib'
-test $(defretval) = no && die "Unable to find development files for zlib."
-
-check_pkg_config "LCMS2 support" $_lcms2 LCMS2 'lcms2 >= 2.6'
-
-check_pkg_config "FFmpeg/Libav" $_ffmpeg FFMPEG \
- "libavutil >= 54.02.0 libavcodec >= 56.1.0 libavformat >= 56.01.0 libswscale >= 2.1.3"
-test $(defretval) = no && die "Unable to find development files for some of the required Libav libraries above. Aborting."
-
-check_pkg_config "Libswresample" $_libswresample LIBSWRESAMPLE 'libswresample >= 1.1.100'
-_libswresample=$(defretval)
-
-_libavresample=auto
-test $_libswresample = yes && _libavresample=no
-check_pkg_config "Libavresample" $_libavresample LIBAVRESAMPLE 'libavresample >= 2.1.0'
-_libavresample=$(defretval)
-
-if test "$_libswresample" = no && test "$_libavresample" = no ; then
- die "No resampler found. Install libavresample or libswresample (FFmpeg)."
-fi
-
-# Arguments: "message"($1) "define"($2) "header"($3) "code"($4)
-api_statement_check() {
- echocheck "$1"
- _res=no
- statement_check "$3" "$4" && _res=yes
- define_yes_no $_res "$2"
- echores "$_res"
-}
-
-api_statement_check \
- "libavcodec avcodec_enum_to_chroma_pos API" \
- HAVE_AVCODEC_CHROMA_POS_API \
- libavcodec/avcodec.h \
- 'int x, y; avcodec_enum_to_chroma_pos(&x, &y, AVCHROMA_LOC_UNSPECIFIED)'
-
-api_statement_check \
- "libavutil AVFrame metadata" \
- HAVE_AVFRAME_METADATA \
- libavutil/frame.h \
- 'av_frame_get_metadata(NULL)'
-
-api_statement_check \
- "libavutil AVFrame skip samples metadata" \
- HAVE_AVFRAME_SKIP_SAMPLES \
- libavutil/frame.h \
- 'enum AVFrameSideDataType type = AV_FRAME_DATA_SKIP_SAMPLES'
-
-api_statement_check \
- "libavutil av_version_info()" \
- HAVE_AV_VERSION_INFO \
- libavutil/avutil.h \
- 'const char *x = av_version_info()'
-
-api_statement_check \
- "libavutil new pixdesc fields" \
- HAVE_AV_NEW_PIXDESC \
- libavutil/pixdesc.h \
- 'AVComponentDescriptor d; int x = d.depth'
-
-api_statement_check \
- "libavcodec 64 bit AVPacket.duration" \
- HAVE_AV_AVPACKET_INT64_DURATION \
- libavcodec/avcodec.h \
- 'int x[(int)sizeof(((AVPacket){0}).duration) - 7]'
-
-api_statement_check \
- "libavcodec AVSubtitleRect AVPicture removal" \
- HAVE_AV_SUBTITLE_NOPICT \
- libavcodec/avcodec.h \
- 'AVSubtitleRect r = {.linesize={0}}'
-
-check_pkg_config "libavfilter" $_libavfilter LIBAVFILTER 'libavfilter >= 5.0.0'
-
-check_pkg_config "libavdevice" $_libavdevice LIBAVDEVICE 'libavdevice >= 55.0.0'
-
-check_trivial "TV interface" $_tv TV
-
-check_statement_libs "Video 4 Linux 2 TV interface" $_tv_v4l2 TV_V4L2 \
- "sys/time.h linux/videodev2.h"
-_tv_v4l2=$(defretval)
-check_trivial "TV audio input" $_tv_v4l2 AUDIO_INPUT
-
-test $_tv_v4l2 = no && _libv4l2=no
-check_pkg_config "libv4l2 support" $_libv4l2 LIBV4L2 'libv4l2'
-
-# Note: Lua has no official .pc file, so there are different OS-specific ones.
-# Also, we support luajit, which is compatible to 5.1.
-
-test_lua() {
- if test "$_lua" = auto && $_pkg_config "$1" ; then
- check_pkg_config "Lua ($1)" $_lua LUA "$1"
- _lua=$(defretval)
- fi
-}
-
-test_lua "lua >= 5.1.0 lua < 5.2.0"
-test_lua "lua5.1 >= 5.1.0" # debian
-test_lua "lua51 >= 5.1.0" # OpenBSD
-test_lua "luajit >= 2.0.0"
-test_lua "lua5.2 >= 5.2.0" # debian
-test_lua "lua52 >= 5.2.0" # OpenBSD
-test_lua "lua >= 5.2.0"
-
-test "$_lua" != yes && check_yes_no no LUA
-
-if ! ( $_pkg_config 'vapoursynth >= 24' ) ; then
- _vapoursynth=no
- _vapoursynth_lazy=no
-fi
-check_pkg_config "VapourSynth support (Python)" $_vapoursynth VAPOURSYNTH 'vapoursynth >= 23 vapoursynth-script >= 23'
-_vapoursynth=$(defretval)
-if test "$_lua" = no ; then
- _vapoursynth_lazy=no
-fi
-check_pkg_config "VapourSynth support (Lua)" $_vapoursynth_lazy VAPOURSYNTH_LAZY 'vapoursynth >= 23'
-_vapoursynth_lazy=$(defretval)
-
-_vapoursynth_core=yes
-if test "$_vapoursynth" = no && test "$_vapoursynth_lazy" = no ; then
- _vapoursynth_core=no
-fi
-check_trivial "VapourSynth core" $_vapoursynth_core VAPOURSYNTH_CORE
-
-check_pkg_config "libarchive support" $_libarchive LIBARCHIVE 'libarchive >= 3.0.0'
-
-check_trivial "encoding" $_encoding ENCODING
-
-# needs dlopen on unix
-_dlopen="$_dl"
-check_yes_no $_dlopen DLOPEN
-
-extra_ldflags="$extra_ldflags $_ld_pthread"
-libs_mplayer="$libs_mplayer $_ld_dl"
-
-CFLAGS="$CFLAGS -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE64_SOURCE"
-
-# This is done so waf builds won't conflict with this. In fact, waf and old
-# build system can coexist in parallel, at the same time. This is because
-# waf always does out-of-tree builds, while this build system does always
-# in-tree builds.
-
-if test ! -f Makefile ; then
- ln -s TOOLS/old-makefile Makefile
-fi
-
-cat > old_build/config.mak << EOF
-# -------- Generated by configure -----------
-export LC_ALL = C
-
-CONFIGURATION = $configuration
-
-prefix = \$(DESTDIR)$_prefix
-BINDIR = \$(DESTDIR)$_bindir
-MANDIR = \$(DESTDIR)$_mandir
-CONFDIR = \$(DESTDIR)$_confdir
-
-CC = $_cc
-INSTALL = install
-
-CFLAGS = -Iold_build $OURCFLAGS $CFLAGS $extra_cflags
-DEPFLAGS = $DEPFLAGS
-
-EXTRALIBS = $extra_ldflags $_ld_static $_ld_lm $libs_mplayer $end_ldflags
-
-RST2MAN = $_rst2man
-BUILD_MAN = $_build_man
-
-# features
-EOF
-cat $CONFIG_MAK >> old_build/config.mak
-
-cat > $TMPC << EOF
-/*----------------------------------------------------------------------------
-** This file has been automatically generated by configure any changes in it
-** will be lost when you run configure again.
-** Instead of modifying definitions here, use the --enable/--disable options
-** of the configure script! See ./configure --help for details.
-*---------------------------------------------------------------------------*/
-
-#ifndef MPV_CONFIG_H
-#define MPV_CONFIG_H
-
-#define CONFIGURATION "$configuration"
-
-#define MPV_CONFDIR "$_confdir"
-
-/* we didn't bother to add actual config checks for this, or they are
- for platforms not supported by this configure script */
-#define HAVE_BSD_FSTATFS 0
-#define HAVE_LINUX_FSTATFS 1
-#define HAVE_GL_COCOA 0
-#define HAVE_COCOA 0
-#define HAVE_COCOA_APPLICATION 0
-#define HAVE_COREVIDEO 0
-#define HAVE_COREAUDIO 0
-#define HAVE_GL_WIN32 0
-#define HAVE_DIRECT3D 0
-#define HAVE_DSOUND 0
-#define HAVE_WASAPI 0
-#define HAVE_DOS_PATHS 0
-#define HAVE_PRIORITY 0
-#define HAVE_GLOB 1
-#define HAVE_NANOSLEEP 1
-#define HAVE_SDL1 0
-#define HAVE_WAIO 0
-#define HAVE_POSIX_SPAWN 1
-#define HAVE_GLIBC_THREAD_NAME (!!__GLIBC__)
-#define HAVE_OSX_THREAD_NAME 0
-#define HAVE_BSD_THREAD_NAME 0
-#define HAVE_NETBSD_THREAD_NAME 0
-#define HAVE_DXVA2_HWACCEL 0
-#define HAVE_FCHMOD 0
-#define HAVE_RPI 0
-#define HAVE_RPI_GLES 0
-#define HAVE_AV_PIX_FMT_MMAL 0
-#define HAVE_DRM 0
-#define HAVE_EGL_DRM 0
-#define HAVE_VIDEOTOOLBOX_HWACCEL 0
-#define HAVE_VIDEOTOOLBOX_GL 0
-#define HAVE_SSE4_INTRINSICS 1
-#define HAVE_C11_TLS 1
-#define HAVE_EGL_ANGLE 0
-#define HAVE_GPL3 1
-#define HAVE_WIN32 0
-
-#ifdef __OpenBSD__
-#define DEFAULT_CDROM_DEVICE "/dev/rcd0c"
-#define DEFAULT_DVD_DEVICE "/dev/rcd0c"
-#else
-#define DEFAULT_CDROM_DEVICE "/dev/cdrom"
-#define DEFAULT_DVD_DEVICE "/dev/dvd"
-#endif
-#define PATH_DEV_DSP "/dev/dsp"
-#define PATH_DEV_MIXER "/dev/mixer"
-
-EOF
-cat $CONFIG_H >> $TMPC
-echo '#endif /* MPV_CONFIG_H */' >> $TMPC
-
-# Do not overwrite an unchanged config.h to avoid superfluous rebuilds.
-cmp -s "$TMPC" old_build/config.h || mv -f "$TMPC" old_build/config.h
-
-cat <<EOF
-
-Configuration successful. See $TMPLOG for details.
-
-NOTE: The --enable-* parameters unconditionally force options on, completely
-skipping autodetection. This behavior is unlike what you may be used to from
-autoconf-based configure scripts that can decide to override you. This greater
-level of control comes at a price. You may have to provide the correct compiler
-and linker flags yourself.
-If you used one of these options and experience a compilation or
-linking failure, make sure you have passed the necessary compiler/linker flags
-to configure.
-
-WARNING: The ./old-configure + make build system you are using is deprecated in
-favour of waf and will be removed in a future version of mpv. Check the
-README for instructions on how to build mpv with the new build system.
-This will not work correctly on MinGW, Cygwin, OSX, and other systems.
-
-EOF
diff --git a/TOOLS/old-makefile b/TOOLS/old-makefile
deleted file mode 100644
index c1b5079..0000000
--- a/TOOLS/old-makefile
+++ /dev/null
@@ -1,488 +0,0 @@
-# MPlayer Makefile
-#
-# copyright (c) 2008 Diego Biurrun
-# Rewritten entirely from a set of Makefiles written by Arpi and many others.
-#
-# This file is part of MPlayer.
-#
-# MPlayer 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.
-#
-# MPlayer 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
-include old_build/config.mak
-
-###### variable declarations #######
-
-SOURCES_AUDIO_INPUT-$(ALSA) += stream/ai_alsa1x.c
-SOURCES_AUDIO_INPUT-$(OSS_AUDIO)+= stream/ai_oss.c
-SOURCES_AUDIO_INPUT-$(SNDIO) += stream/ai_sndio.c
-SOURCES-$(AUDIO_INPUT) += $(SOURCES_AUDIO_INPUT-yes)
-SOURCES-$(CDDA) += stream/stream_cdda.c
-SOURCES-$(DVBIN) += stream/dvb_tune.c \
- stream/stream_dvb.c
-SOURCES-$(DVDREAD) += stream/stream_dvd.c \
- stream/stream_dvd_common.c
-SOURCES-$(DVDNAV) += stream/stream_dvdnav.c \
- stream/stream_dvd_common.c
-
-SOURCES-$(RUBBERBAND) += audio/filter/af_rubberband.c
-SOURCES-$(LIBASS) += sub/ass_mp.c sub/sd_ass.c
-
-SOURCES-$(LIBBLURAY) += stream/stream_bluray.c
-
-SOURCES-$(LIBSMBCLIENT) += stream/stream_smb.c
-
-SOURCES-$(TV) += stream/stream_tv.c stream/tv.c \
- stream/frequencies.c stream/tvi_dummy.c \
- demux/demux_tv.c
-
-SOURCES-$(TV_V4L2) += stream/tvi_v4l2.c stream/audio_in.c
-SOURCES-$(DUMMY_OSD) += sub/osd_dummy.c
-SOURCES-$(LIBASS_OSD) += sub/osd_libass.c
-
-SOURCES-$(ALSA) += audio/out/ao_alsa.c
-SOURCES-$(CACA) += video/out/vo_caca.c
-SOURCES-$(SDL2) += audio/out/ao_sdl.c video/out/vo_sdl.c
-SOURCES-$(GL) += video/out/opengl/common.c \
- video/out/opengl/osd.c \
- video/out/opengl/lcms.c \
- video/out/opengl/video.c \
- video/out/opengl/video_shaders.c \
- video/out/dither.c \
- video/out/opengl/hwdec.c \
- video/out/opengl/utils.c \
- video/out/opengl/superxbr.c \
- video/out/opengl/nnedi3.c \
- video/out/vo_opengl.c \
- video/out/vo_opengl_cb.c
-
-SOURCES-$(ENCODING) += video/out/vo_lavc.c audio/out/ao_lavc.c \
- common/encode_lavc.c
-
-SOURCES-$(GL_X11) += video/out/x11_common.c video/out/opengl/x11.c
-SOURCES-$(EGL_X11) += video/out/x11_common.c video/out/opengl/x11egl.c
-SOURCES-$(GL_WAYLAND) += video/out/wayland_common.c \
- video/out/opengl/wayland.c
-
-SOURCES-$(JACK) += audio/out/ao_jack.c
-SOURCES-$(OPENAL) += audio/out/ao_openal.c
-SOURCES-$(OSS_AUDIO) += audio/out/ao_oss.c
-SOURCES-$(PULSE) += audio/out/ao_pulse.c
-SOURCES-$(RSOUND) += audio/out/ao_rsound.c
-SOURCES-$(SNDIO) += audio/out/ao_sndio.c
-SOURCES-$(VDPAU) += video/vdpau.c video/vdpau_mixer.c \
- video/out/vo_vdpau.c video/decode/vdpau.c \
- video/filter/vf_vdpaupp.c \
- video/filter/vf_vdpaurb.c
-SOURCES-$(VDPAU_GL_X11) += video/out/opengl/hwdec_vdpau.c
-SOURCES-$(VAAPI) += video/out/vo_vaapi.c \
- video/decode/vaapi.c \
- video/filter/vf_vavpp.c \
- video/vaapi.c
-SOURCES-$(VAAPI_GLX) += video/out/opengl/hwdec_vaglx.c
-SOURCES-$(VAAPI_X_EGL) += video/out/opengl/hwdec_vaegl.c
-
-SOURCES-$(X11) += video/out/vo_x11.c video/out/x11_common.c
-SOURCES-$(XV) += video/out/vo_xv.c
-SOURCES-$(WAYLAND) += video/out/vo_wayland.c \
- video/out/wayland_common.c \
- video/out/wayland/buffer.c \
- video/out/wayland/memfile.c
-
-SOURCES-$(LIBAVFILTER) += video/filter/vf_lavfi.c \
- video/filter/vf_gradfun.c \
- video/filter/vf_pullup.c \
- video/filter/vf_rotate.c \
- video/filter/vf_yadif.c \
- audio/filter/af_lavfi.c
-
-SOURCES-$(LUA) += player/lua.c
-SOURCES-$(VAPOURSYNTH_CORE) += video/filter/vf_vapoursynth.c
-SOURCES-$(LIBARCHIVE) += demux/demux_libarchive.c \
- stream/stream_libarchive.c
-SOURCES-$(DLOPEN) += video/filter/vf_dlopen.c
-
-SOURCES = audio/audio.c \
- audio/audio_buffer.c \
- audio/chmap.c \
- audio/chmap_sel.c \
- audio/fmt-conversion.c \
- audio/format.c \
- audio/mixer.c \
- audio/decode/ad_lavc.c \
- audio/decode/ad_spdif.c \
- audio/decode/dec_audio.c \
- audio/filter/af.c \
- audio/filter/af_channels.c \
- audio/filter/af_delay.c \
- audio/filter/af_equalizer.c \
- audio/filter/af_format.c \
- audio/filter/af_lavcac3enc.c \
- audio/filter/af_lavrresample.c \
- audio/filter/af_pan.c \
- audio/filter/af_scaletempo.c \
- audio/filter/af_drc.c \
- audio/filter/af_volume.c \
- audio/filter/tools.c \
- audio/out/ao.c \
- audio/out/ao_null.c \
- audio/out/ao_pcm.c \
- audio/out/pull.c \
- audio/out/push.c \
- common/av_common.c \
- common/av_log.c \
- common/codecs.c \
- common/common.c \
- common/msg.c \
- common/playlist.c \
- common/tags.c \
- common/version.c \
- demux/codec_tags.c \
- demux/cue.c \
- demux/demux.c \
- demux/demux_edl.c \
- demux/demux_cue.c \
- demux/demux_disc.c \
- demux/demux_lavf.c \
- demux/demux_mf.c \
- demux/demux_mkv.c \
- demux/demux_mkv_timeline.c \
- demux/demux_playlist.c \
- demux/demux_rar.c \
- demux/demux_raw.c \
- demux/ebml.c \
- demux/packet.c \
- demux/timeline.c \
- input/cmd_list.c \
- input/cmd_parse.c \
- input/event.c \
- input/input.c \
- input/ipc.c \
- input/keycodes.c \
- misc/bstr.c \
- misc/charset_conv.c \
- misc/dispatch.c \
- misc/json.c \
- misc/rendezvous.c \
- misc/ring.c \
- options/m_config.c \
- options/m_option.c \
- options/m_property.c \
- options/options.c \
- options/parse_commandline.c \
- options/parse_configfile.c \
- options/path.c \
- osdep/io.c \
- osdep/path-unix.c \
- osdep/semaphore_osx.c \
- osdep/subprocess.c \
- osdep/subprocess-posix.c \
- osdep/terminal-unix.c \
- osdep/timer.c \
- osdep/timer-linux.c \
- osdep/threads.c \
- player/audio.c \
- player/client.c \
- player/configfiles.c \
- player/command.c \
- player/external_files.c \
- player/loadfile.c \
- player/main.c \
- player/misc.c \
- player/osd.c \
- player/playloop.c \
- player/screenshot.c \
- player/scripting.c \
- player/sub.c \
- player/video.c \
- stream/cache.c \
- stream/cache_file.c \
- stream/cookies.c \
- stream/rar.c \
- stream/stream.c \
- stream/stream_avdevice.c \
- stream/stream_edl.c \
- stream/stream_file.c \
- stream/stream_lavf.c \
- stream/stream_memory.c \
- stream/stream_mf.c \
- stream/stream_null.c \
- stream/stream_rar.c \
- sub/dec_sub.c \
- sub/draw_bmp.c \
- sub/img_convert.c \
- sub/osd.c \
- sub/sd_lavc.c \
- sub/sd_lavc_conv.c \
- sub/sd_lavf_srt.c \
- sub/sd_microdvd.c \
- sub/sd_movtext.c \
- sub/sd_srt.c \
- ta/ta.c \
- ta/ta_utils.c \
- ta/ta_talloc.c \
- video/csputils.c \
- video/fmt-conversion.c \
- video/gpu_memcpy.c \
- video/image_writer.c \
- video/img_format.c \
- video/mp_image.c \
- video/mp_image_pool.c \
- video/sws_utils.c \
- video/decode/dec_video.c \
- video/decode/vd_lavc.c \
- video/filter/vf.c \
- video/filter/vf_buffer.c \
- video/filter/vf_crop.c \
- video/filter/vf_dsize.c \
- video/filter/vf_eq.c \
- video/filter/vf_expand.c \
- video/filter/vf_flip.c \
- video/filter/vf_format.c \
- video/filter/vf_mirror.c \
- video/filter/vf_noformat.c \
- video/filter/vf_scale.c \
- video/filter/vf_stereo3d.c \
- video/filter/vf_sub.c \
- video/out/bitmap_packer.c \
- video/out/aspect.c \
- video/out/filter_kernels.c \
- video/out/vo.c \
- video/out/vo_null.c \
- video/out/vo_image.c \
- video/out/win_state.c \
- $(SOURCES-yes)
-
-OBJECTS += $(addsuffix .o, $(basename $(SOURCES)))
-OBJECTS += $(OBJECTS-yes)
-
-DEP_FILES = $(patsubst %.S,%.d,$(patsubst %.cpp,%.d,$(patsubst %.c,%.d,$(SOURCES:.m=.d) $(SOURCES:.m=.d))))
-
-ALL_TARGETS += mpv
-
-INSTALL_BIN += install-mpv
-INSTALL_BIN_STRIP += install-mpv-strip
-INSTALL_MAN =
-
-ifeq ($(BUILD_MAN),yes)
- INSTALL_MAN += install-mpv-man
- ALL_TARGETS += DOCS/man/mpv.1
-endif
-
-DIRS = . \
- audio \
- audio/decode \
- audio/filter \
- audio/out \
- common \
- compat \
- input \
- player/timeline \
- demux \
- misc \
- options \
- osdep \
- osdep/ar \
- player \
- stream \
- sub \
- ta \
- video \
- video/decode \
- video/filter \
- video/out
-
-
-ADDSUFFIXES = $(foreach suf,$(1),$(addsuffix $(suf),$(2)))
-ADD_ALL_DIRS = $(call ADDSUFFIXES,$(1),$(DIRS))
-
-###### brief build output #######
-
-ifndef V
-$(eval override CC = @printf "CC\t$$@\n"; $(CC))
-$(eval override RM = @$(RM))
-endif
-
-###### generic rules #######
-
-all: $(ALL_TARGETS)
-
-%.1: %.rst
- $(RST2MAN) $< $@
-
-%.o: %.c
- $(CC) $(DEPFLAGS) $(CFLAGS) -c -o $@ $<
-
-mpv: $(OBJECTS) osdep/main-fn-unix.o
- $(CC) -o $@ $^ $(EXTRALIBS)
-
-input/input.c: input/input_conf.h
-input/input_conf.h: TOOLS/file2string.pl etc/input.conf
- ./$^ >$@
-
-MKVLIB_DEPS = TOOLS/lib/Parse/Matroska.pm \
- TOOLS/lib/Parse/Matroska/Definitions.pm \
- TOOLS/lib/Parse/Matroska/Element.pm \
- TOOLS/lib/Parse/Matroska/Reader.pm \
- TOOLS/lib/Parse/Matroska/Utils.pm \
-
-demux/ebml.c demux/demux_mkv.c: demux/ebml_types.h
-demux/ebml_types.h: TOOLS/matroska.pl $(MKVLIB_DEPS)
- ./$< --generate-header > $@
-
-demux/ebml.c: demux/ebml_defs.c
-demux/ebml_defs.c: TOOLS/matroska.pl $(MKVLIB_DEPS)
- ./$< --generate-definitions > $@
-
-video/out/x11_common.c: video/out/x11_icon.inc
-video/out/x11_icon.inc: TOOLS/file2string.pl video/out/x11_icon.bin
- ./$^ >$@
-
-video/out/opengl/nnedi3.c: video/out/opengl/nnedi3_weights.inc
-video/out/opengl/nnedi3_weights.inc: TOOLS/file2string.pl video/out/opengl/nnedi3_weights.bin
- ./$^ >$@
-
-sub/osd_libass.c: sub/osd_font.h
-sub/osd_font.h: TOOLS/file2string.pl sub/osd_font.otf
- ./$^ >$@
-
-player/lua/%.inc: TOOLS/file2string.pl player/lua/%.lua
- ./$^ >$@
-
-player/lua.c: player/lua/defaults.inc \
- player/lua/assdraw.inc \
- player/lua/osc.inc \
- player/lua/ytdl_hook.inc \
- player/lua/options.inc
-
-etc/_mpv: TOOLS/zsh.pl ./mpv
- ./$< > $@
-
-# ./configure must be rerun if it changed
-config.mak: configure
- @echo "############################################################"
- @echo "####### Please run ./configure again - it's changed! #######"
- @echo "############################################################"
-
-old_build/version.h .version: version.sh
- ./version.sh --versionh=old_build/version.h
-
-# Force version.sh to run to potentially regenerate version.h
--include .version
-
-%: %.c
- $(CC) $(CFLAGS) -o $@ $^
-
-
-###### dependency declarations / specific CFLAGS ######
-
-common/version.c: old_build/version.h
-
-DOCS/man/mpv.1: DOCS/man/af.rst \
- DOCS/man/ao.rst \
- DOCS/man/changes.rst \
- DOCS/man/encode.rst \
- DOCS/man/input.rst \
- DOCS/man/options.rst \
- DOCS/man/vf.rst \
- DOCS/man/vo.rst
-
-###### installation / clean / generic rules #######
-
-install: $(INSTALL_BIN) install-data $(INSTALL_MAN)
-install-no-man: $(INSTALL_BIN) install-data
-install-strip: $(INSTALL_BIN_STRIP) install-data $(INSTALL_MAN)
-install-strip-no-man: $(INSTALL_BIN_STRIP) install-data
-
-install-dirs:
- if test ! -d $(BINDIR) ; then $(INSTALL) -d $(BINDIR) ; fi
-
-install-%: % install-dirs
- $(INSTALL) -m 755 $< $(BINDIR)
-
-install-%-strip: % install-dirs
- $(INSTALL) -m 755 -s $< $(BINDIR)
-
-install-mpv-man: install-mpv-man-en
-
-install-mpv-man-en: DOCS/man/mpv.1
- if test ! -d $(MANDIR)/man1 ; then $(INSTALL) -d $(MANDIR)/man1 ; fi
- $(INSTALL) -m 644 DOCS/man/mpv.1 $(MANDIR)/man1/
-
-ICONSIZES = 16x16 32x32 64x64
-
-define ICON_INSTALL_RULE
-install-mpv-icon-$(size): etc/mpv-icon-8bit-$(size).png
- $(INSTALL) -d $(prefix)/share/icons/hicolor/$(size)/apps
- $(INSTALL) -m 644 etc/mpv-icon-8bit-$(size).png $(prefix)/share/icons/hicolor/$(size)/apps/mpv.png
-endef
-
-$(foreach size,$(ICONSIZES),$(eval $(ICON_INSTALL_RULE)))
-
-install-mpv-icons: $(foreach size,$(ICONSIZES),install-mpv-icon-$(size))
-
-install-mpv-desktop: etc/mpv.desktop
- $(INSTALL) -d $(prefix)/share/applications
- $(INSTALL) -m 644 etc/mpv.desktop $(prefix)/share/applications/
-
-install-mpv-config: etc/encoding-profiles.conf
- $(INSTALL) -d $(CONFDIR)
- $(INSTALL) -m 644 etc/encoding-profiles.conf $(CONFDIR)
-
-install-mpv-zsh: etc/_mpv
- $(INSTALL) -d $(prefix)/share/zsh/vendor-completions
- $(INSTALL) -m 644 etc/_mpv $(prefix)/share/zsh/vendor-completions/
-
-install-data: install-mpv-icons install-mpv-desktop install-mpv-config install-mpv-zsh
-
-uninstall:
- $(RM) $(BINDIR)/mpv
- $(RM) $(MANDIR)/man1/mpv.1 $(MANDIR)/man1/mpv.1
- $(RM) $(prefix)/share/applications/mpv.desktop
- $(RM) $(prefix)/share/zsh/vendor-completions/_mpv
- $(RM) $(foreach size,$(ICONSIZES),$(prefix)/share/icons/hicolor/$(size)/apps/mpv.png)
-
-clean:
- -$(RM) $(call ADD_ALL_DIRS,/*.o /*.d /*.a /*.ho /*~)
- -$(RM) $(call ADD_ALL_DIRS,/*.o /*.a /*.ho /*~)
- -$(RM) mpv
- -$(RM) DOCS/man/*/mpv.1
- -$(RM) old_build/version.h version.h
- -$(RM) input/input_conf.h
- -$(RM) video/out/vdpau_template.c
- -$(RM) demux/ebml_types.h demux/ebml_defs.c
- -$(RM) video/out/x11_icon.inc
- -$(RM) sub/osd_font.h
- -$(RM) player/lua/defaults.inc
- -$(RM) player/lua/assdraw.inc
- -$(RM) player/lua/osc.inc
- -$(RM) player/lua/ytdl_hook.inc
- -$(RM) player/lua/options.inc
-
-distclean: clean
- -$(RM) config.log old_build/config.h old_build/config.mak Makefile
- -rmdir old_build/
-
--include $(DEP_FILES)
-
-.PHONY: all *install* *clean .version
-
-# Disable suffix rules. Most of the builtin rules are suffix rules,
-# so this saves some time on slow systems.
-.SUFFIXES:
-
-# If a command returns failure but changed its target file, delete the
-# (presumably malformed) file. Otherwise the file would be considered to
-# be up to date if make is restarted.
-
-.DELETE_ON_ERROR:
diff --git a/TOOLS/osxbundle.py b/TOOLS/osxbundle.py
index 3ad52cb..e9ef8bc 100755
--- a/TOOLS/osxbundle.py
+++ b/TOOLS/osxbundle.py
@@ -39,8 +39,17 @@ def apply_plist_template(plist_file, version):
for line in fileinput.input(plist_file, inplace=1):
print (line.rstrip().replace('${VERSION}', version))
+def bundle_version():
+ if os.path.exists('VERSION'):
+ x = open('VERSION')
+ version = x.read()
+ x.close()
+ else:
+ version = sh("./version.sh").strip()
+ return version
+
def main():
- version = sh("./version.sh").strip()
+ version = bundle_version().rstrip()
usage = "usage: %prog [options] arg"
parser = OptionParser(usage)
diff --git a/TOOLS/stats-conv.py b/TOOLS/stats-conv.py
index e0b168d..120f691 100755
--- a/TOOLS/stats-conv.py
+++ b/TOOLS/stats-conv.py
@@ -40,7 +40,7 @@ class G:
events = {}
start = None
markers = ["o", "s", "t", "d"]
- curveno = 0
+ curveno = {}
def find_marker():
if len(G.markers) == 0:
@@ -66,6 +66,10 @@ def get_event(event, evtype):
G.events[event] = e
return G.events[event]
+colors = [(0.0, 0.5, 0.0), (0.0, 0.0, 1.0), (0.0, 0.0, 0.0), (1.0, 0.0, 0.0), (0.75, 0.75, 0), (0.0, 0.75, 0.75), (0.75, 0, 0.75)]
+def mkColor(t):
+ return pg.mkColor(int(t[0] * 255), int(t[1] * 255), int(t[2] * 255))
+
SCALE = 1e6 # microseconds to seconds
for line in [line.split("#")[0].strip() for line in open(filename, "r")]:
@@ -152,8 +156,10 @@ for e in G.sevents:
args['symbol'] = e.marker
args['pen'] = None
else:
- args['pen'] = pg.mkPen(pg.intColor(G.curveno), width=0)
- G.curveno += 1
+ if not cur in G.curveno:
+ G.curveno[cur] = 0
+ args['pen'] = pg.mkPen(mkColor(colors[G.curveno[cur] % len(colors)]), width=0)
+ G.curveno[cur] += 1
n = cur.plot([x for x,y in e.vals], [y for x,y in e.vals], **args)
QtGui.QApplication.instance().exec_()
diff --git a/TOOLS/zsh.pl b/TOOLS/zsh.pl
index 309c0d4..cdf97d6 100755
--- a/TOOLS/zsh.pl
+++ b/TOOLS/zsh.pl
@@ -120,7 +120,7 @@ $profile_comp
_tags files urls
while _tags; do
_requested files expl 'media file' _files -g \\
- "*.(#i)(asf|asx|avi|flac|flv|m1v|m2p|m2v|m4v|mjpg|mka|mkv|mov|mp3|mp4|mpe|mpeg|mpg|ogg|ogm|ogv|qt|rm|ts|vob|wav|webm|wma|wmv)(-.)" && rc=0
+ "*.(#i)(asf|asx|avi|flac|flv|m1v|m2p|m2v|m4v|mjpg|mka|mkv|mov|mp3|mp4|mpe|mpeg|mpg|ogg|ogm|ogv|opus|qt|rm|ts|vob|wav|webm|wma|wmv)(-.)" && rc=0
if _requested urls; then
while _next_label urls expl URL; do
_urls "\$expl[@]" && rc=0
diff --git a/VERSION b/VERSION
index a803cc2..04a373e 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.14.0
+0.16.0
diff --git a/audio/audio.c b/audio/audio.c
index c4ad402..ae85a4b 100644
--- a/audio/audio.c
+++ b/audio/audio.c
@@ -22,9 +22,10 @@
#include <libavutil/buffer.h>
#include <libavutil/frame.h>
+#include <libavutil/mem.h>
#include <libavutil/version.h>
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "common/common.h"
#include "fmt-conversion.h"
#include "audio.h"
@@ -251,8 +252,43 @@ void mp_audio_skip_samples(struct mp_audio *data, int samples)
data->planes[n] = (uint8_t *)data->planes[n] + samples * data->sstride;
data->samples -= samples;
+
+ if (data->pts != MP_NOPTS_VALUE)
+ data->pts += samples / (double)data->rate;
}
+// Clip the given frame to the given timestamp range. Adjusts the frame size
+// and timestamp.
+void mp_audio_clip_timestamps(struct mp_audio *f, double start, double end)
+{
+ if (f->pts == MP_NOPTS_VALUE || f->rate < 1)
+ return;
+ double f_end = f->pts + f->samples / (double)f->rate;
+ if (end != MP_NOPTS_VALUE) {
+ if (f_end >= end) {
+ if (f->pts >= end) {
+ f->samples = 0;
+ } else {
+ int new = (end - f->pts) * f->rate;
+ f->samples = MPCLAMP(new, 0, f->samples);
+ }
+ }
+ }
+ if (start != MP_NOPTS_VALUE) {
+ if (f->pts < start) {
+ if (f_end <= start) {
+ f->samples = 0;
+ f->pts = f_end;
+ } else {
+ int skip = (start - f->pts) * f->rate;
+ skip = MPCLAMP(skip, 0, f->samples);
+ mp_audio_skip_samples(f, skip);
+ }
+ }
+ }
+}
+
+
// Return false if the frame data is shared, true otherwise.
// Will return true for non-refcounted frames.
bool mp_audio_is_writeable(struct mp_audio *data)
@@ -350,6 +386,78 @@ fail:
return NULL;
}
+// Returns NULL on failure. The input is always unreffed.
+struct AVFrame *mp_audio_to_avframe_and_unref(struct mp_audio *frame)
+{
+ struct AVFrame *avframe = av_frame_alloc();
+ if (!avframe)
+ goto fail;
+
+ avframe->nb_samples = frame->samples;
+ avframe->format = af_to_avformat(frame->format);
+ if (avframe->format == AV_SAMPLE_FMT_NONE)
+ goto fail;
+
+ avframe->channel_layout = mp_chmap_to_lavc(&frame->channels);
+ if (!avframe->channel_layout)
+ goto fail;
+#if LIBAVUTIL_VERSION_MICRO >= 100
+ // FFmpeg being a stupid POS (but I respect it)
+ avframe->channels = frame->channels.num;
+#endif
+ avframe->sample_rate = frame->rate;
+
+ if (frame->num_planes > AV_NUM_DATA_POINTERS) {
+ avframe->extended_data =
+ av_mallocz_array(frame->num_planes, sizeof(avframe->extended_data[0]));
+ int extbufs = frame->num_planes - AV_NUM_DATA_POINTERS;
+ avframe->extended_buf =
+ av_mallocz_array(extbufs, sizeof(avframe->extended_buf[0]));
+ if (!avframe->extended_data || !avframe->extended_buf)
+ goto fail;
+ avframe->nb_extended_buf = extbufs;
+ }
+
+ for (int p = 0; p < frame->num_planes; p++)
+ avframe->extended_data[p] = frame->planes[p];
+ avframe->linesize[0] = frame->samples * frame->sstride;
+
+ for (int p = 0; p < AV_NUM_DATA_POINTERS; p++)
+ avframe->data[p] = avframe->extended_data[p];
+
+ for (int p = 0; p < frame->num_planes; p++) {
+ if (!frame->allocated[p])
+ break;
+ AVBufferRef *nref = av_buffer_ref(frame->allocated[p]);
+ if (!nref)
+ goto fail;
+ if (p < AV_NUM_DATA_POINTERS) {
+ avframe->buf[p] = nref;
+ } else {
+ avframe->extended_buf[p - AV_NUM_DATA_POINTERS] = nref;
+ }
+ }
+
+ // Force refcounted frame.
+ if (!avframe->buf[0]) {
+ AVFrame *tmp = av_frame_alloc();
+ if (!tmp)
+ goto fail;
+ if (av_frame_ref(tmp, avframe) < 0)
+ goto fail;
+ av_frame_free(&avframe);
+ avframe = tmp;
+ }
+
+ talloc_free(frame);
+ return avframe;
+
+fail:
+ av_frame_free(&avframe);
+ talloc_free(frame);
+ return NULL;
+}
+
struct mp_audio_pool {
AVBufferPool *avpool;
int element_size;
diff --git a/audio/audio.h b/audio/audio.h
index c74d0f7..c469f7a 100644
--- a/audio/audio.h
+++ b/audio/audio.h
@@ -72,12 +72,14 @@ void mp_audio_copy(struct mp_audio *dst, int dst_offset,
struct mp_audio *src, int src_offset, int length);
void mp_audio_copy_attributes(struct mp_audio *dst, struct mp_audio *src);
void mp_audio_skip_samples(struct mp_audio *data, int samples);
+void mp_audio_clip_timestamps(struct mp_audio *f, double start, double end);
bool mp_audio_is_writeable(struct mp_audio *data);
int mp_audio_make_writeable(struct mp_audio *data);
struct AVFrame;
struct mp_audio *mp_audio_from_avframe(struct AVFrame *avframe);
+struct AVFrame *mp_audio_to_avframe_and_unref(struct mp_audio *frame);
struct mp_audio_pool;
struct mp_audio_pool *mp_audio_pool_create(void *ta_parent);
diff --git a/audio/audio_buffer.c b/audio/audio_buffer.c
index c0f1341..a443a21 100644
--- a/audio/audio_buffer.c
+++ b/audio/audio_buffer.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 <stddef.h>
diff --git a/audio/audio_buffer.h b/audio/audio_buffer.h
index f517542..212d187 100644
--- a/audio/audio_buffer.h
+++ b/audio/audio_buffer.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 MP_AUDIO_BUFFER_H
diff --git a/audio/chmap.c b/audio/chmap.c
index e0f485c..1d4970d 100644
--- a/audio/chmap.c
+++ b/audio/chmap.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>
diff --git a/audio/chmap.h b/audio/chmap.h
index b32c63b..aa9b1c5 100644
--- a/audio/chmap.h
+++ b/audio/chmap.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 MP_CHMAP_H
diff --git a/audio/chmap_sel.c b/audio/chmap_sel.c
index 8ddb713..45b696c 100644
--- a/audio/chmap_sel.c
+++ b/audio/chmap_sel.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>
@@ -274,19 +274,25 @@ static bool mp_chmap_is_better(struct mp_chmap *req, struct mp_chmap *old,
if (new_lost_r != old_lost_r)
return new_lost_r < old_lost_r;
+ struct mp_chmap old_p = *old, new_p = *new;
+ mp_chmap_remove_na(&old_p);
+ mp_chmap_remove_na(&new_p);
+
+ // If the situation is equal with replaced speakers, but the replacement is
+ // perfect for only one of them, let the better one win. This prefers
+ // inexact equivalents over exact supersets.
+ bool perfect_r_new = !new_lost_r && new_p.num <= old_p.num;
+ bool perfect_r_old = !old_lost_r && old_p.num <= new_p.num;
+ if (perfect_r_new != perfect_r_old)
+ return perfect_r_new;
+
int old_lost = mp_chmap_diffn(req, old);
int new_lost = mp_chmap_diffn(req, new);
-
- // If the situation is equal with replaced speakers, but one of them loses
- // less if no replacements are performed, pick the better one, even if it
- // means an upmix. This prefers exact supersets over inexact equivalents.
+ // If the situation is equal with replaced speakers, pick the better one,
+ // even if it means an upmix.
if (new_lost != old_lost)
return new_lost < old_lost;
- struct mp_chmap old_p = *old, new_p = *new;
- mp_chmap_remove_na(&old_p);
- mp_chmap_remove_na(&new_p);
-
// Some kind of upmix. If it's perfect, prefer the smaller one. Even if not,
// both have equal loss, so also prefer the smaller one.
// Drop padding channels (NA) for the sake of this check, as the number of
diff --git a/audio/chmap_sel.h b/audio/chmap_sel.h
index 12ded3b..5bd8783 100644
--- a/audio/chmap_sel.h
+++ b/audio/chmap_sel.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 MP_CHMAP_SEL_H
diff --git a/audio/decode/ad.h b/audio/decode/ad.h
index 0513954..771ceb7 100644
--- a/audio/decode/ad.h
+++ b/audio/decode/ad.h
@@ -35,7 +35,8 @@ struct ad_functions {
int (*init)(struct dec_audio *da, const char *decoder);
void (*uninit)(struct dec_audio *da);
int (*control)(struct dec_audio *da, int cmd, void *arg);
- int (*decode_packet)(struct dec_audio *da, struct mp_audio **out);
+ int (*decode_packet)(struct dec_audio *da, struct demux_packet *pkt,
+ struct mp_audio **out);
};
enum ad_ctrl {
diff --git a/audio/decode/ad_lavc.c b/audio/decode/ad_lavc.c
index 3188834..c30aff7 100644
--- a/audio/decode/ad_lavc.c
+++ b/audio/decode/ad_lavc.c
@@ -26,7 +26,7 @@
#include <libavutil/common.h>
#include <libavutil/intreadwrite.h>
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "config.h"
#include "common/av_common.h"
@@ -42,8 +42,9 @@ struct priv {
AVFrame *avframe;
struct mp_audio frame;
bool force_channel_map;
- struct demux_packet *packet;
- uint32_t skip_samples;
+ uint32_t skip_samples, trim_samples;
+ bool preroll_done;
+ double next_pts;
};
static void uninit(struct dec_audio *da);
@@ -60,7 +61,7 @@ const struct m_sub_options ad_lavc_conf = {
.opts = (const m_option_t[]) {
OPT_FLOATRANGE("ac3drc", ac3drc, 0, 0, 6),
OPT_FLAG("downmix", downmix, 0),
- OPT_INTRANGE("threads", threads, 0, 1, 16),
+ OPT_INTRANGE("threads", threads, 0, 0, 16),
OPT_KEYVALUELIST("o", avopts, 0),
{0}
},
@@ -78,13 +79,12 @@ static int init(struct dec_audio *da, const char *decoder)
struct ad_lavc_params *opts = mpopts->ad_lavc_params;
AVCodecContext *lavc_context;
AVCodec *lavc_codec;
- struct sh_stream *sh = da->header;
- struct sh_audio *sh_audio = sh->audio;
+ struct mp_codec_params *c = da->codec;
struct priv *ctx = talloc_zero(NULL, struct priv);
da->priv = ctx;
- ctx->force_channel_map = sh_audio->force_channels;
+ ctx->force_channel_map = c->force_channels;
lavc_codec = avcodec_find_decoder_by_name(decoder);
if (!lavc_codec) {
@@ -116,20 +116,20 @@ static int init(struct dec_audio *da, const char *decoder)
mp_set_avopts(da->log, lavc_context, opts->avopts);
- lavc_context->codec_tag = sh->codec_tag;
- lavc_context->sample_rate = sh_audio->samplerate;
- lavc_context->bit_rate = sh_audio->bitrate;
- lavc_context->block_align = sh_audio->block_align;
- lavc_context->bits_per_coded_sample = sh_audio->bits_per_coded_sample;
- lavc_context->channels = sh_audio->channels.num;
- if (!mp_chmap_is_unknown(&sh_audio->channels))
- lavc_context->channel_layout = mp_chmap_to_lavc(&sh_audio->channels);
+ lavc_context->codec_tag = c->codec_tag;
+ lavc_context->sample_rate = c->samplerate;
+ lavc_context->bit_rate = c->bitrate;
+ lavc_context->block_align = c->block_align;
+ lavc_context->bits_per_coded_sample = c->bits_per_coded_sample;
+ lavc_context->channels = c->channels.num;
+ if (!mp_chmap_is_unknown(&c->channels))
+ lavc_context->channel_layout = mp_chmap_to_lavc(&c->channels);
// demux_mkv
- mp_lavc_set_extradata(lavc_context, sh->extradata, sh->extradata_size);
+ mp_lavc_set_extradata(lavc_context, c->extradata, c->extradata_size);
- if (sh->lav_headers)
- mp_copy_lav_codec_headers(lavc_context, sh->lav_headers);
+ if (c->lav_headers)
+ mp_copy_lav_codec_headers(lavc_context, c->lav_headers);
mp_set_avcodec_threads(da->log, lavc_context, opts->threads);
@@ -140,6 +140,8 @@ static int init(struct dec_audio *da, const char *decoder)
return 0;
}
+ ctx->next_pts = MP_NOPTS_VALUE;
+
return 1;
}
@@ -165,27 +167,21 @@ static int control(struct dec_audio *da, int cmd, void *arg)
switch (cmd) {
case ADCTRL_RESET:
avcodec_flush_buffers(ctx->avctx);
- talloc_free(ctx->packet);
- ctx->packet = NULL;
ctx->skip_samples = 0;
+ ctx->trim_samples = 0;
+ ctx->preroll_done = false;
+ ctx->next_pts = MP_NOPTS_VALUE;
return CONTROL_TRUE;
}
return CONTROL_UNKNOWN;
}
-static int decode_packet(struct dec_audio *da, struct mp_audio **out)
+static int decode_packet(struct dec_audio *da, struct demux_packet *mpkt,
+ struct mp_audio **out)
{
struct priv *priv = da->priv;
AVCodecContext *avctx = priv->avctx;
- struct demux_packet *mpkt = priv->packet;
- if (!mpkt) {
- if (demux_read_packet_async(da->header, &mpkt) == 0)
- return AD_WAIT;
- }
-
- priv->packet = talloc_steal(priv, mpkt);
-
int in_len = mpkt ? mpkt->len : 0;
AVPacket pkt;
@@ -203,58 +199,69 @@ static int decode_packet(struct dec_audio *da, struct mp_audio **out)
mpkt->len -= ret;
mpkt->pts = MP_NOPTS_VALUE; // don't reset PTS next time
}
- if (mpkt->len == 0 || ret < 0) {
- talloc_free(mpkt);
- priv->packet = NULL;
- }
// LATM may need many packets to find mux info
- if (ret == AVERROR(EAGAIN))
- return AD_OK;
+ if (ret == AVERROR(EAGAIN)) {
+ mpkt->len = 0;
+ return 0;
+ }
}
if (ret < 0) {
MP_ERR(da, "Error decoding audio.\n");
- return AD_ERR;
+ return -1;
}
if (!got_frame)
- return mpkt ? AD_OK : AD_EOF;
+ return 0;
double out_pts = mp_pts_from_av(priv->avframe->pkt_pts, NULL);
struct mp_audio *mpframe = mp_audio_from_avframe(priv->avframe);
if (!mpframe)
- return AD_ERR;
+ return -1;
struct mp_chmap lavc_chmap = mpframe->channels;
if (lavc_chmap.num != avctx->channels)
mp_chmap_from_channels(&lavc_chmap, avctx->channels);
if (priv->force_channel_map) {
- struct sh_audio *sh_audio = da->header->audio;
- if (lavc_chmap.num == sh_audio->channels.num)
- lavc_chmap = sh_audio->channels;
+ if (lavc_chmap.num == da->codec->channels.num)
+ lavc_chmap = da->codec->channels;
}
mp_audio_set_channels(mpframe, &lavc_chmap);
mpframe->pts = out_pts;
+ if (mpframe->pts == MP_NOPTS_VALUE)
+ mpframe->pts = priv->next_pts;
+ if (mpframe->pts != MP_NOPTS_VALUE)
+ priv->next_pts = mpframe->pts + mpframe->samples / (double)mpframe->rate;
+
#if HAVE_AVFRAME_SKIP_SAMPLES
AVFrameSideData *sd =
av_frame_get_side_data(priv->avframe, AV_FRAME_DATA_SKIP_SAMPLES);
if (sd && sd->size >= 10) {
char *d = sd->data;
priv->skip_samples += AV_RL32(d + 0);
- uint32_t pad = AV_RL32(d + 4);
- uint32_t skip = MPMIN(priv->skip_samples, mpframe->samples);
- if (skip) {
- mp_audio_skip_samples(mpframe, skip);
- if (mpframe->pts != MP_NOPTS_VALUE)
- mpframe->pts += skip / (double)mpframe->rate;
- priv->skip_samples -= skip;
- }
- if (pad <= mpframe->samples)
- mpframe->samples -= pad;
+ priv->trim_samples += AV_RL32(d + 4);
}
#endif
+ if (!priv->preroll_done) {
+ // Skip only if this isn't already handled by AV_FRAME_DATA_SKIP_SAMPLES.
+ if (!priv->skip_samples)
+ priv->skip_samples = avctx->delay;
+ priv->preroll_done = true;
+ }
+
+ uint32_t skip = MPMIN(priv->skip_samples, mpframe->samples);
+ if (skip) {
+ mp_audio_skip_samples(mpframe, skip);
+ priv->skip_samples -= skip;
+ }
+ uint32_t trim = MPMIN(priv->trim_samples, mpframe->samples);
+ if (trim) {
+ mpframe->samples -= trim;
+ priv->trim_samples -= trim;
+ }
+
*out = mpframe;
av_frame_unref(priv->avframe);
diff --git a/audio/decode/ad_spdif.c b/audio/decode/ad_spdif.c
index 5e9dcf1..7298d9e 100644
--- a/audio/decode/ad_spdif.c
+++ b/audio/decode/ad_spdif.c
@@ -41,6 +41,7 @@ struct spdifContext {
bool need_close;
bool use_dts_hd;
struct mp_audio fmt;
+ struct mp_audio_pool *pool;
};
static int write_packet(void *p, uint8_t *buf, int buf_size)
@@ -79,6 +80,7 @@ static int init(struct dec_audio *da, const char *decoder)
da->priv = spdif_ctx;
spdif_ctx->log = da->log;
spdif_ctx->use_dts_hd = da->opts->dtshd;
+ spdif_ctx->pool = mp_audio_pool_create(spdif_ctx);
if (strcmp(decoder, "dts-hd") == 0) {
decoder = "dts";
@@ -189,7 +191,8 @@ static int init_filter(struct dec_audio *da, AVPacket *pkt)
break;
case AV_CODEC_ID_DTS: {
bool is_hd = profile == FF_PROFILE_DTS_HD_HRA ||
- profile == FF_PROFILE_DTS_HD_MA;
+ profile == FF_PROFILE_DTS_HD_MA ||
+ profile == FF_PROFILE_UNKNOWN;
if (spdif_ctx->use_dts_hd && is_hd) {
av_dict_set(&format_opts, "dtshd_rate", "768000", 0); // 4*192000
sample_format = AF_FORMAT_S_DTSHD;
@@ -240,38 +243,35 @@ fail:
return -1;
}
-static int decode_packet(struct dec_audio *da, struct mp_audio **out)
+static int decode_packet(struct dec_audio *da, struct demux_packet *mpkt,
+ struct mp_audio **out)
{
struct spdifContext *spdif_ctx = da->priv;
spdif_ctx->out_buffer_len = 0;
- struct demux_packet *mpkt;
- if (demux_read_packet_async(da->header, &mpkt) == 0)
- return AD_WAIT;
-
if (!mpkt)
- return AD_EOF;
+ return 0;
double pts = mpkt->pts;
AVPacket pkt;
mp_set_av_packet(&pkt, mpkt, NULL);
+ mpkt->len = 0; // will be fully consumed
pkt.pts = pkt.dts = 0;
if (!spdif_ctx->lavf_ctx) {
if (init_filter(da, &pkt) < 0)
- return AD_ERR;
+ return -1;
}
int ret = av_write_frame(spdif_ctx->lavf_ctx, &pkt);
- talloc_free(mpkt);
avio_flush(spdif_ctx->lavf_ctx->pb);
if (ret < 0)
- return AD_ERR;
+ return -1;
int samples = spdif_ctx->out_buffer_len / spdif_ctx->fmt.sstride;
- *out = mp_audio_pool_get(da->pool, &spdif_ctx->fmt, samples);
+ *out = mp_audio_pool_get(spdif_ctx->pool, &spdif_ctx->fmt, samples);
if (!*out)
- return AD_ERR;
+ return -1;
memcpy((*out)->planes[0], spdif_ctx->out_buffer, spdif_ctx->out_buffer_len);
(*out)->pts = pts;
diff --git a/audio/decode/dec_audio.c b/audio/decode/dec_audio.c
index 03172ed..e60ebe3 100644
--- a/audio/decode/dec_audio.c
+++ b/audio/decode/dec_audio.c
@@ -61,8 +61,6 @@ static void uninit_decoder(struct dec_audio *d_audio)
d_audio->ad_driver = NULL;
talloc_free(d_audio->priv);
d_audio->priv = NULL;
- d_audio->afilter->initialized = -1;
- d_audio->decode_format = (struct mp_audio){0};
}
static int init_audio_codec(struct dec_audio *d_audio, const char *decoder)
@@ -88,12 +86,12 @@ struct mp_decoder_list *audio_decoder_list(void)
static struct mp_decoder_list *audio_select_decoders(struct dec_audio *d_audio)
{
struct MPOpts *opts = d_audio->opts;
- const char *codec = d_audio->header->codec;
+ const char *codec = d_audio->codec->codec;
struct mp_decoder_list *list = audio_decoder_list();
struct mp_decoder_list *new =
mp_select_decoders(list, codec, opts->audio_decoders);
- if (d_audio->spdif_passthrough) {
+ if (d_audio->try_spdif) {
struct mp_decoder_list *spdif =
mp_select_decoder_list(list, codec, "spdif", opts->audio_spdif);
mp_append_decoders(spdif, new);
@@ -146,7 +144,7 @@ int audio_init_best_codec(struct dec_audio *d_audio)
MP_VERBOSE(d_audio, "Selected audio codec: %s\n", d_audio->decoder_desc);
} else {
MP_ERR(d_audio, "Failed to initialize an audio decoder for codec '%s'.\n",
- d_audio->header->codec ? d_audio->header->codec : "<unknown>");
+ d_audio->codec->codec);
}
talloc_free(list);
@@ -157,125 +155,132 @@ void audio_uninit(struct dec_audio *d_audio)
{
if (!d_audio)
return;
- MP_VERBOSE(d_audio, "Uninit audio filters...\n");
uninit_decoder(d_audio);
- af_destroy(d_audio->afilter);
- talloc_free(d_audio->waiting);
talloc_free(d_audio);
}
-static int decode_new_frame(struct dec_audio *da)
+void audio_reset_decoding(struct dec_audio *d_audio)
{
- while (!da->waiting) {
- int ret = da->ad_driver->decode_packet(da, &da->waiting);
- if (ret < 0)
- return ret;
-
- if (da->waiting) {
- if (da->waiting->pts != MP_NOPTS_VALUE) {
- if (da->pts != MP_NOPTS_VALUE) {
- da->pts += da->pts_offset / (double)da->waiting->rate;
- da->pts_offset = 0;
- }
- // Keep the interpolated timestamp if it doesn't deviate more
- // than 1 ms from the real one. (MKV rounded timestamps.)
- if (da->pts == MP_NOPTS_VALUE || da->pts_offset != 0 ||
- fabs(da->pts - da->waiting->pts) > 0.001)
- {
- da->pts = da->waiting->pts;
- da->pts_offset = 0;
- }
- }
- da->pts_offset += da->waiting->samples;
- da->decode_format = *da->waiting;
- mp_audio_set_null_data(&da->decode_format);
- }
-
- if (da->pts == MP_NOPTS_VALUE && da->header->missing_timestamps)
- da->pts = 0;
- }
- return mp_audio_config_valid(da->waiting) ? AD_OK : AD_ERR;
+ if (d_audio->ad_driver)
+ d_audio->ad_driver->control(d_audio, ADCTRL_RESET, NULL);
+ d_audio->pts = MP_NOPTS_VALUE;
+ talloc_free(d_audio->current_frame);
+ d_audio->current_frame = NULL;
+ talloc_free(d_audio->packet);
+ d_audio->packet = NULL;
+ talloc_free(d_audio->new_segment);
+ d_audio->new_segment = NULL;
+ d_audio->start = d_audio->end = MP_NOPTS_VALUE;
}
-/* Decode packets until we know the audio format. Then reinit the buffer.
- * Returns AD_OK on success, negative AD_* code otherwise.
- * Also returns AD_OK if already initialized (and does nothing).
- */
-int initial_audio_decode(struct dec_audio *da)
+static void fix_audio_pts(struct dec_audio *da)
{
- return decode_new_frame(da);
-}
+ if (!da->current_frame)
+ return;
-static bool copy_output(struct af_stream *afs, struct mp_audio_buffer *outbuf,
- int minsamples, bool eof)
-{
- while (mp_audio_buffer_samples(outbuf) < minsamples) {
- if (af_output_frame(afs, eof) < 0)
- return true; // error, stop doing stuff
- struct mp_audio *mpa = af_read_output_frame(afs);
- if (!mpa)
- return false; // out of data
- mp_audio_buffer_append(outbuf, mpa);
- talloc_free(mpa);
+ if (da->current_frame->pts != MP_NOPTS_VALUE) {
+ double newpts = da->current_frame->pts;
+ // Keep the interpolated timestamp if it doesn't deviate more
+ // than 1 ms from the real one. (MKV rounded timestamps.)
+ if (da->pts == MP_NOPTS_VALUE || fabs(da->pts - newpts) > 0.001)
+ da->pts = da->current_frame->pts;
}
- return true;
+
+ if (da->pts == MP_NOPTS_VALUE && da->header->missing_timestamps)
+ da->pts = 0;
+
+ da->current_frame->pts = da->pts;
+
+ if (da->pts != MP_NOPTS_VALUE)
+ da->pts += da->current_frame->samples / (double)da->current_frame->rate;
}
-/* Try to get at least minsamples decoded+filtered samples in outbuf
- * (total length including possible existing data).
- * Return 0 on success, or negative AD_* error code.
- * In the former case outbuf has at least minsamples buffered on return.
- * In case of EOF/error it might or might not be. */
-int audio_decode(struct dec_audio *da, struct mp_audio_buffer *outbuf,
- int minsamples)
+void audio_work(struct dec_audio *da)
{
- struct af_stream *afs = da->afilter;
- if (afs->initialized < 1)
- return AD_ERR;
+ if (da->current_frame)
+ return;
- MP_STATS(da, "start audio");
+ if (!da->packet && demux_read_packet_async(da->header, &da->packet) == 0) {
+ da->current_state = DATA_WAIT;
+ return;
+ }
- int res;
- while (1) {
- res = 0;
+ if (da->packet && da->packet->new_segment) {
+ assert(!da->new_segment);
+ da->new_segment = da->packet;
+ da->packet = NULL;
+ }
- if (copy_output(afs, outbuf, minsamples, false))
- break;
+ bool had_packet = da->packet || da->new_segment;
- res = decode_new_frame(da);
- if (res < 0) {
- // drain filters first (especially for true EOF case)
- copy_output(afs, outbuf, minsamples, true);
- break;
- }
+ int ret = da->ad_driver->decode_packet(da, da->packet, &da->current_frame);
+ if (ret < 0 || (da->packet && da->packet->len == 0)) {
+ talloc_free(da->packet);
+ da->packet = NULL;
+ }
- // On format change, make sure to drain the filter chain.
- if (!mp_audio_config_equals(&afs->input, da->waiting)) {
- copy_output(afs, outbuf, minsamples, true);
- res = AD_NEW_FMT;
- break;
- }
+ if (da->current_frame && !mp_audio_config_valid(da->current_frame)) {
+ talloc_free(da->current_frame);
+ da->current_frame = NULL;
+ }
- struct mp_audio *mpa = da->waiting;
- da->waiting = NULL;
- if (af_filter_frame(afs, mpa) < 0)
- return AD_ERR;
+ da->current_state = DATA_OK;
+ if (!da->current_frame) {
+ da->current_state = DATA_EOF;
+ if (had_packet)
+ da->current_state = DATA_AGAIN;
}
- MP_STATS(da, "end audio");
+ fix_audio_pts(da);
+
+ bool segment_end = true;
+
+ if (da->current_frame) {
+ mp_audio_clip_timestamps(da->current_frame, da->start, da->end);
+ if (da->current_frame->pts != MP_NOPTS_VALUE && da->start != MP_NOPTS_VALUE)
+ segment_end = da->current_frame->pts >= da->start;
+ if (da->current_frame->samples == 0) {
+ talloc_free(da->current_frame);
+ da->current_frame = NULL;
+ }
+ }
- return res;
+ // If there's a new segment, start it as soon as we're drained/finished.
+ if (segment_end && da->new_segment) {
+ struct demux_packet *new_segment = da->new_segment;
+ da->new_segment = NULL;
+
+ // Could avoid decoder reinit; would still need flush.
+ da->codec = new_segment->codec;
+ if (da->ad_driver)
+ da->ad_driver->uninit(da);
+ da->ad_driver = NULL;
+ audio_init_best_codec(da);
+
+ da->start = new_segment->start;
+ da->end = new_segment->end;
+
+ new_segment->new_segment = false;
+
+ da->packet = new_segment;
+ da->current_state = DATA_AGAIN;
+ }
}
-void audio_reset_decoding(struct dec_audio *d_audio)
+// Fetch an audio frame decoded with audio_work(). Returns one of:
+// DATA_OK: *out_frame is set to a new image
+// DATA_WAIT: waiting for demuxer; will receive a wakeup signal
+// DATA_EOF: end of file, no more frames to be expected
+// DATA_AGAIN: dropped frame or something similar
+int audio_get_frame(struct dec_audio *da, struct mp_audio **out_frame)
{
- if (d_audio->ad_driver)
- d_audio->ad_driver->control(d_audio, ADCTRL_RESET, NULL);
- af_seek_reset(d_audio->afilter);
- d_audio->pts = MP_NOPTS_VALUE;
- d_audio->pts_offset = 0;
- if (d_audio->waiting) {
- talloc_free(d_audio->waiting);
- d_audio->waiting = NULL;
+ *out_frame = NULL;
+ if (da->current_frame) {
+ *out_frame = da->current_frame;
+ da->current_frame = NULL;
+ return DATA_OK;
}
+ if (da->current_state == DATA_OK)
+ return DATA_AGAIN;
+ return da->current_state;
}
diff --git a/audio/decode/dec_audio.h b/audio/decode/dec_audio.h
index 0f7f4d2..7bc8b00 100644
--- a/audio/decode/dec_audio.h
+++ b/audio/decode/dec_audio.h
@@ -30,39 +30,33 @@ struct dec_audio {
struct mp_log *log;
struct MPOpts *opts;
struct mpv_global *global;
- bool spdif_passthrough, spdif_failed;
const struct ad_functions *ad_driver;
struct sh_stream *header;
- struct af_stream *afilter;
+ struct mp_codec_params *codec;
char *decoder_desc;
- int init_retries;
- struct mp_audio_pool *pool;
- struct mp_audio decode_format;
- struct mp_audio *waiting; // used on format-change
- // set by decoder
- int bitrate; // input bitrate, can change with VBR sources
- // last known pts value in output from decoder
- double pts;
- // number of samples output by decoder after last known pts
- int pts_offset;
+
+ bool try_spdif;
+
// For free use by the ad_driver
void *priv;
-};
-enum {
- AD_OK = 0,
- AD_ERR = -1,
- AD_EOF = -2,
- AD_NEW_FMT = -3,
- AD_WAIT = -4,
+ // Strictly internal (dec_audio.c).
+
+ double pts; // endpts of previous frame
+ double start, end;
+ struct demux_packet *packet;
+ struct demux_packet *new_segment;
+ struct mp_audio *current_frame;
+ int current_state;
};
struct mp_decoder_list *audio_decoder_list(void);
int audio_init_best_codec(struct dec_audio *d_audio);
-int audio_decode(struct dec_audio *d_audio, struct mp_audio_buffer *outbuf,
- int minsamples);
-int initial_audio_decode(struct dec_audio *d_audio);
-void audio_reset_decoding(struct dec_audio *d_audio);
void audio_uninit(struct dec_audio *d_audio);
+void audio_work(struct dec_audio *d_audio);
+int audio_get_frame(struct dec_audio *d_audio, struct mp_audio **out_frame);
+
+void audio_reset_decoding(struct dec_audio *d_audio);
+
#endif /* MPLAYER_DEC_AUDIO_H */
diff --git a/audio/filter/af.c b/audio/filter/af.c
index 7ff3b49..ac1b492 100644
--- a/audio/filter/af.c
+++ b/audio/filter/af.c
@@ -59,9 +59,7 @@ static const struct af_info *const filter_list[] = {
&af_info_rubberband,
#endif
&af_info_scaletempo,
-#if HAVE_LIBAVFILTER
&af_info_lavfi,
-#endif
NULL
};
@@ -166,6 +164,7 @@ static struct af_instance *af_create(struct af_stream *s, char *name,
.info = desc.p,
.data = talloc_zero(af, struct mp_audio),
.log = mp_log_new(af, s->log, name),
+ .opts = s->opts,
.replaygain_data = s->replaygain_data,
.out_pool = mp_audio_pool_create(af),
};
@@ -695,6 +694,17 @@ int af_control_by_label(struct af_stream *s, int cmd, void *arg, bstr label)
}
}
+int af_send_command(struct af_stream *s, char *label, char *cmd, char *arg)
+{
+ char *args[2] = {cmd, arg};
+ if (strcmp(label, "all") == 0) {
+ af_control_all(s, AF_CONTROL_COMMAND, args);
+ return 0;
+ } else {
+ return af_control_by_label(s, AF_CONTROL_COMMAND, args, bstr0(label));
+ }
+}
+
// Used by filters to add a filtered frame to the output queue.
// Ownership of frame is transferred from caller to the filter chain.
void af_add_output_frame(struct af_instance *af, struct mp_audio *frame)
diff --git a/audio/filter/af.h b/audio/filter/af.h
index ba64379..9c49081 100644
--- a/audio/filter/af.h
+++ b/audio/filter/af.h
@@ -53,6 +53,7 @@ struct af_info {
struct af_instance {
const struct af_info *info;
struct mp_log *log;
+ struct MPOpts *opts;
struct replaygain_data *replaygain_data;
int (*control)(struct af_instance *af, int cmd, void *arg);
void (*uninit)(struct af_instance *af);
@@ -123,6 +124,7 @@ enum af_control {
AF_CONTROL_SET_PLAYBACK_SPEED,
AF_CONTROL_SET_PLAYBACK_SPEED_RESAMPLE,
AF_CONTROL_GET_METADATA,
+ AF_CONTROL_COMMAND,
};
// Argument for AF_CONTROL_SET_PAN_LEVEL
@@ -143,6 +145,7 @@ struct af_instance *af_control_any_rev(struct af_stream *s, int cmd, void *arg);
void af_control_all(struct af_stream *s, int cmd, void *arg);
int af_control_by_label(struct af_stream *s, int cmd, void *arg, bstr label);
void af_seek_reset(struct af_stream *s);
+int af_send_command(struct af_stream *s, char *label, char *cmd, char *arg);
void af_add_output_frame(struct af_instance *af, struct mp_audio *frame);
int af_filter_frame(struct af_stream *s, struct mp_audio *frame);
diff --git a/audio/filter/af_lavfi.c b/audio/filter/af_lavfi.c
index af521ab..bc4a687 100644
--- a/audio/filter/af_lavfi.c
+++ b/audio/filter/af_lavfi.c
@@ -3,18 +3,18 @@
*
* Filter graph creation code taken from FFmpeg ffplay.c (LGPL 2.1 or later)
*
- * 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>
@@ -50,6 +50,7 @@
#if LIBAVFILTER_VERSION_MICRO < 100
#define graph_parse(graph, filters, inputs, outputs, log_ctx) \
avfilter_graph_parse(graph, filters, inputs, outputs, log_ctx)
+#define avfilter_graph_send_command(a, b, c, d, e, f, g) -1
#else
#define graph_parse(graph, filters, inputs, outputs, log_ctx) \
avfilter_graph_parse_ptr(graph, filters, &(inputs), &(outputs), log_ctx)
@@ -222,6 +223,14 @@ static int control(struct af_instance *af, int cmd, void *arg)
return mp_audio_config_equals(in, &orig_in) ? AF_OK : AF_FALSE;
}
+ case AF_CONTROL_COMMAND: {
+ if (!p->graph)
+ break;
+ char **args = arg;
+ return avfilter_graph_send_command(p->graph, "all",
+ args[0], args[1], &(char){0}, 0, 0)
+ >= 0 ? CONTROL_OK : CONTROL_ERROR;
+ }
case AF_CONTROL_GET_METADATA:
if (p->metadata) {
*(struct mp_tags *)arg = *p->metadata;
@@ -257,32 +266,15 @@ static int filter_frame(struct af_instance *af, struct mp_audio *data)
if (!p->graph)
goto error;
- AVFilterLink *l_in = p->in->outputs[0];
-
if (data) {
- frame = av_frame_alloc();
+ frame = mp_audio_to_avframe_and_unref(data);
+ data = NULL;
if (!frame)
goto error;
- frame->nb_samples = data->samples;
- frame->format = l_in->format;
-
// Timebase is 1/sample_rate
frame->pts = p->samples_in;
-
- frame->channel_layout = l_in->channel_layout;
- frame->sample_rate = l_in->sample_rate;
-#if LIBAVFILTER_VERSION_MICRO >= 100
- // FFmpeg being a stupid POS
- frame->channels = l_in->channels;
-#endif
-
- frame->extended_data = frame->data;
- for (int n = 0; n < data->num_planes; n++)
- frame->data[n] = data->planes[n];
- frame->linesize[0] = frame->nb_samples * data->sstride;
-
- p->samples_in += data->samples;
+ p->samples_in += frame->nb_samples;
}
if (av_buffersrc_add_frame(p->in, frame) < 0)
diff --git a/audio/filter/af_lavrresample.c b/audio/filter/af_lavrresample.c
index f7f448c..6fbb445 100644
--- a/audio/filter/af_lavrresample.c
+++ b/audio/filter/af_lavrresample.c
@@ -179,6 +179,37 @@ bool af_lavrresample_test_conversion(int src_format, int dst_format)
check_output_conversion(dst_format) != AV_SAMPLE_FMT_NONE;
}
+static struct mp_chmap fudge_pairs[][2] = {
+ {MP_CHMAP2(BL, BR), MP_CHMAP2(SL, SR)},
+ {MP_CHMAP2(SL, SR), MP_CHMAP2(BL, BR)},
+ {MP_CHMAP2(SDL, SDR), MP_CHMAP2(SL, SR)},
+ {MP_CHMAP2(SL, SR), MP_CHMAP2(SDL, SDR)},
+};
+
+// Modify out_layout and return the new value. The intention is reducing the
+// loss libswresample's rematrixing will cause by exchanging similar, but
+// strictly speaking incompatible channel pairs. For example, 7.1 should be
+// changed to 7.1(wide) without dropping the SL/SR channels. (We still leave
+// it to libswresample to create the remix matrix.)
+static uint64_t fudge_layout_conversion(struct af_instance *af,
+ uint64_t in, uint64_t out)
+{
+ for (int n = 0; n < MP_ARRAY_SIZE(fudge_pairs); n++) {
+ uint64_t a = mp_chmap_to_lavc(&fudge_pairs[n][0]);
+ uint64_t b = mp_chmap_to_lavc(&fudge_pairs[n][1]);
+ if ((in & a) == a && (in & b) == 0 &&
+ (out & a) == 0 && (out & b) == b)
+ {
+ out = (out & ~b) | a;
+
+ MP_VERBOSE(af, "Fudge: %s -> %s\n",
+ mp_chmap_to_str(&fudge_pairs[n][0]),
+ mp_chmap_to_str(&fudge_pairs[n][1]));
+ }
+ }
+ return out;
+}
+
// mp_chmap_get_reorder() performs:
// to->speaker[n] = from->speaker[src[n]]
// but libavresample does:
@@ -230,10 +261,13 @@ static int configure_lavrr(struct af_instance *af, struct mp_audio *in,
av_opt_set_double(s->avrctx, "cutoff", s->opts.cutoff, 0);
+ int normalize = s->opts.normalize;
+ if (normalize < 0)
+ normalize = af->opts->audio_normalize;
#if HAVE_LIBSWRESAMPLE
- av_opt_set_double(s->avrctx, "rematrix_maxval", s->opts.normalize ? 1 : 1000, 0);
+ av_opt_set_double(s->avrctx, "rematrix_maxval", normalize ? 1 : 1000, 0);
#else
- av_opt_set_int(s->avrctx, "normalize_mix_level", s->opts.normalize, 0);
+ av_opt_set_int(s->avrctx, "normalize_mix_level", !!normalize, 0);
#endif
if (mp_set_avopts(af->log, s->avrctx, s->avopts) < 0)
@@ -297,6 +331,8 @@ static int configure_lavrr(struct af_instance *af, struct mp_audio *in,
if (map_out.num > out_lavc.num)
mp_audio_set_channels(&s->pool_fmt, &map_out);
+ out_ch_layout = fudge_layout_conversion(af, in_ch_layout, out_ch_layout);
+
// Real conversion; output is input to avrctx_out.
av_opt_set_int(s->avrctx, "in_channel_layout", in_ch_layout, 0);
av_opt_set_int(s->avrctx, "out_channel_layout", out_ch_layout, 0);
@@ -583,7 +619,7 @@ const struct af_info af_info_lavrresample = {
.filter_size = 16,
.cutoff = 0.0,
.phase_shift = 10,
- .normalize = 1,
+ .normalize = -1,
},
.playback_speed = 1.0,
.allow_detach = 1,
@@ -594,7 +630,8 @@ const struct af_info af_info_lavrresample = {
OPT_FLAG("linear", opts.linear, 0),
OPT_DOUBLE("cutoff", opts.cutoff, M_OPT_RANGE, .min = 0, .max = 1),
OPT_FLAG("detach", allow_detach, 0),
- OPT_FLAG("normalize", opts.normalize, 0),
+ OPT_CHOICE("normalize", opts.normalize, 0,
+ ({"no", 0}, {"yes", 1}, {"auto", -1})),
OPT_KEYVALUELIST("o", avopts, 0),
{0}
},
diff --git a/audio/filter/af_rubberband.c b/audio/filter/af_rubberband.c
index 2619e4b..48bb510 100644
--- a/audio/filter/af_rubberband.c
+++ b/audio/filter/af_rubberband.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>
diff --git a/audio/mixer.c b/audio/mixer.c
index 26f426c..01bb4d5 100644
--- a/audio/mixer.c
+++ b/audio/mixer.c
@@ -27,7 +27,7 @@
#include "audio/filter/af.h"
#include "common/global.h"
#include "common/msg.h"
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "mixer.h"
struct mixer {
@@ -120,8 +120,11 @@ void mixer_getvolume(struct mixer *mixer, float *l, float *r)
*r = mixer->vol_r;
}
-static void setvolume_internal(struct mixer *mixer, float l, float r)
+static void setvolume_internal(struct mixer *mixer)
{
+ float l = mixer->vol_l, r = mixer->vol_r;
+ if (mixer->emulate_mute && mixer->muted)
+ l = r = 0;
if (!mixer->softvol) {
MP_DBG(mixer, "Setting volume on AO.\n");
struct ao_control_vol vol = {.left = l, .right = r};
@@ -147,8 +150,8 @@ void mixer_setvolume(struct mixer *mixer, float l, float r)
float max = mixer_getmaxvolume(mixer);
mixer->vol_l = MPCLAMP(l, 0, max);
mixer->vol_r = MPCLAMP(r, 0, max);
- if (mixer->ao && !(mixer->emulate_mute && mixer->muted))
- setvolume_internal(mixer, mixer->vol_l, mixer->vol_r);
+ if (mixer->ao)
+ setvolume_internal(mixer);
}
void mixer_getbothvolume(struct mixer *mixer, float *b)
@@ -167,7 +170,7 @@ void mixer_setmute(struct mixer *mixer, bool mute)
mixer->muted = mute;
mixer->muted_by_us = mute;
if (mixer->emulate_mute) {
- setvolume_internal(mixer, mixer->vol_l*!mute, mixer->vol_r*!mute);
+ setvolume_internal(mixer);
} else {
ao_control(mixer->ao, AOCONTROL_SET_MUTE, &mute);
}
@@ -293,6 +296,8 @@ static void restore_volume(struct mixer *mixer)
const char *prev_driver = mixer->driver;
mixer->driver = mixer->softvol ? "softvol" : ao_get_name(ao);
+ if (!prev_driver[0])
+ prev_driver = mixer->driver;
// Restore old parameters if volume won't survive reinitialization.
// But not if volume scale is possibly different.
@@ -394,4 +399,5 @@ void mixer_uninit_audio(struct mixer *mixer)
}
mixer->ao = NULL;
mixer->af = NULL;
+ mixer->softvol = false;
}
diff --git a/audio/out/ao.c b/audio/out/ao.c
index daa9c30..9c0f644 100644
--- a/audio/out/ao.c
+++ b/audio/out/ao.c
@@ -20,7 +20,7 @@
#include <string.h>
#include <assert.h>
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "config.h"
#include "ao.h"
@@ -43,9 +43,9 @@ extern const struct ao_driver audio_out_sndio;
extern const struct ao_driver audio_out_pulse;
extern const struct ao_driver audio_out_jack;
extern const struct ao_driver audio_out_openal;
+extern const struct ao_driver audio_out_opensles;
extern const struct ao_driver audio_out_null;
extern const struct ao_driver audio_out_alsa;
-extern const struct ao_driver audio_out_dsound;
extern const struct ao_driver audio_out_wasapi;
extern const struct ao_driver audio_out_pcm;
extern const struct ao_driver audio_out_lavc;
@@ -65,9 +65,6 @@ static const struct ao_driver * const audio_out_drivers[] = {
#if HAVE_WASAPI
&audio_out_wasapi,
#endif
-#if HAVE_DSOUND
- &audio_out_dsound,
-#endif
#if HAVE_OSS_AUDIO
&audio_out_oss,
#endif
@@ -78,6 +75,9 @@ static const struct ao_driver * const audio_out_drivers[] = {
#if HAVE_OPENAL
&audio_out_openal,
#endif
+#if HAVE_OPENSLES
+ &audio_out_opensles,
+#endif
#if HAVE_SDL1 || HAVE_SDL2
&audio_out_sdl,
#endif
diff --git a/audio/out/ao_coreaudio.c b/audio/out/ao_coreaudio.c
index facbc60..471ab6d 100644
--- a/audio/out/ao_coreaudio.c
+++ b/audio/out/ao_coreaudio.c
@@ -384,8 +384,8 @@ static int hotplug_init(struct ao *ao)
err = AudioObjectAddPropertyListener(
kAudioObjectSystemObject, &addr, hotplug_cb, (void *)ao);
if (err != noErr) {
- char *c1 = fourcc_repr(hotplug_properties[i]);
- char *c2 = fourcc_repr(err);
+ char *c1 = mp_tag_str(hotplug_properties[i]);
+ char *c2 = mp_tag_str(err);
MP_ERR(ao, "failed to set device listener %s (%s)", c1, c2);
goto coreaudio_error;
}
@@ -409,8 +409,8 @@ static void hotplug_uninit(struct ao *ao)
err = AudioObjectRemovePropertyListener(
kAudioObjectSystemObject, &addr, hotplug_cb, (void *)ao);
if (err != noErr) {
- char *c1 = fourcc_repr(hotplug_properties[i]);
- char *c2 = fourcc_repr(err);
+ char *c1 = mp_tag_str(hotplug_properties[i]);
+ char *c2 = mp_tag_str(err);
MP_ERR(ao, "failed to set device listener %s (%s)", c1, c2);
}
}
diff --git a/audio/out/ao_coreaudio_chmap.c b/audio/out/ao_coreaudio_chmap.c
index bdd625f..3db2bdf 100644
--- a/audio/out/ao_coreaudio_chmap.c
+++ b/audio/out/ao_coreaudio_chmap.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 "common/common.h"
@@ -133,6 +133,29 @@ coreaudio_error:
return NULL;
}
+
+#define CHMAP(n, ...) &(struct mp_chmap) MP_CONCAT(MP_CHMAP, n) (__VA_ARGS__)
+
+// Replace each channel in a with b (a->num == b->num)
+static void replace_submap(struct mp_chmap *dst, struct mp_chmap *a,
+ struct mp_chmap *b)
+{
+ struct mp_chmap t = *dst;
+ if (!mp_chmap_is_valid(&t) || mp_chmap_diffn(a, &t) != 0)
+ return;
+ assert(a->num == b->num);
+ for (int n = 0; n < t.num; n++) {
+ for (int i = 0; i < a->num; i++) {
+ if (t.speaker[n] == a->speaker[i]) {
+ t.speaker[n] = b->speaker[i];
+ break;
+ }
+ }
+ }
+ if (mp_chmap_is_valid(&t))
+ *dst = t;
+}
+
static bool ca_layout_to_mp_chmap(struct ao *ao, AudioChannelLayout *layout,
struct mp_chmap *chmap)
{
@@ -163,6 +186,10 @@ static bool ca_layout_to_mp_chmap(struct ao *ao, AudioChannelLayout *layout,
chmap->speaker[n] = speaker;
}
+ // Remap weird 7.1(rear) layouts correctly.
+ replace_submap(chmap, CHMAP(6, FL, FR, BL, BR, SDL, SDR),
+ CHMAP(6, FL, FR, SL, SR, BL, BR));
+
talloc_free(talloc_ctx);
MP_VERBOSE(ao, "mp chmap: %s\n", mp_chmap_to_str(chmap));
return mp_chmap_is_valid(chmap) && !mp_chmap_is_unknown(chmap);
diff --git a/audio/out/ao_coreaudio_chmap.h b/audio/out/ao_coreaudio_chmap.h
index a67e1dc..d58270f 100644
--- a/audio/out/ao_coreaudio_chmap.h
+++ b/audio/out/ao_coreaudio_chmap.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 MPV_COREAUDIO_CHMAP_H
diff --git a/audio/out/ao_coreaudio_properties.c b/audio/out/ao_coreaudio_properties.c
index cf2b4d1..b74cf07 100644
--- a/audio/out/ao_coreaudio_properties.c
+++ b/audio/out/ao_coreaudio_properties.c
@@ -21,7 +21,7 @@
#include "audio/out/ao_coreaudio_properties.h"
#include "audio/out/ao_coreaudio_utils.h"
-#include "talloc.h"
+#include "mpv_talloc.h"
OSStatus ca_get(AudioObjectID id, ca_scope scope, ca_sel selector,
uint32_t size, void *data)
diff --git a/audio/out/ao_coreaudio_utils.c b/audio/out/ao_coreaudio_utils.c
index dff672e..6b5d8a7 100644
--- a/audio/out/ao_coreaudio_utils.c
+++ b/audio/out/ao_coreaudio_utils.c
@@ -136,36 +136,11 @@ coreaudio_error:
return err;
}
-char *fourcc_repr_buf(char *buf, size_t buf_size, uint32_t code)
-{
- // Extract FourCC letters from the uint32_t and finde out if it's a valid
- // code that is made of letters.
- unsigned char fcc[4] = {
- (code >> 24) & 0xFF,
- (code >> 16) & 0xFF,
- (code >> 8) & 0xFF,
- code & 0xFF,
- };
-
- bool valid_fourcc = true;
- for (int i = 0; i < 4; i++) {
- if (fcc[i] < 32 || fcc[i] >= 128)
- valid_fourcc = false;
- }
-
- if (valid_fourcc)
- snprintf(buf, buf_size, "'%c%c%c%c'", fcc[0], fcc[1], fcc[2], fcc[3]);
- else
- snprintf(buf, buf_size, "%u", (unsigned int)code);
-
- return buf;
-}
-
bool check_ca_st(struct ao *ao, int level, OSStatus code, const char *message)
{
if (code == noErr) return true;
- mp_msg(ao->log, level, "%s (%s)\n", message, fourcc_repr(code));
+ mp_msg(ao->log, level, "%s (%s)\n", message, mp_tag_str(code));
return false;
}
@@ -258,7 +233,7 @@ void ca_print_asbd(struct ao *ao, const char *description,
const AudioStreamBasicDescription *asbd)
{
uint32_t flags = asbd->mFormatFlags;
- char *format = fourcc_repr(asbd->mFormatID);
+ char *format = mp_tag_str(asbd->mFormatID);
int mpfmt = ca_asbd_to_mp_format(asbd);
MP_VERBOSE(ao,
@@ -470,7 +445,7 @@ int64_t ca_get_device_latency_us(struct ao *ao, AudioDeviceID device)
if (err == noErr) {
latency_frames += temp;
MP_VERBOSE(ao, "Latency property %s: %d frames\n",
- fourcc_repr(latency_properties[n]), (int)temp);
+ mp_tag_str(latency_properties[n]), (int)temp);
}
}
diff --git a/audio/out/ao_coreaudio_utils.h b/audio/out/ao_coreaudio_utils.h
index a0b5aa0..780c568 100644
--- a/audio/out/ao_coreaudio_utils.h
+++ b/audio/out/ao_coreaudio_utils.h
@@ -31,9 +31,6 @@
CFStringRef cfstr_from_cstr(char *str);
char *cfstr_get_cstr(CFStringRef cfstr);
-char *fourcc_repr_buf(char *buf, size_t buf_size, uint32_t code);
-#define fourcc_repr(code) fourcc_repr_buf((char[40]){0}, 40, code)
-
bool check_ca_st(struct ao *ao, int level, OSStatus code, const char *message);
#define CHECK_CA_ERROR_L(label, message) \
diff --git a/audio/out/ao_dsound.c b/audio/out/ao_dsound.c
deleted file mode 100644
index c581bf5..0000000
--- a/audio/out/ao_dsound.c
+++ /dev/null
@@ -1,707 +0,0 @@
-/*
- * Windows DirectSound interface
- *
- * Copyright (c) 2004 Gabor Szecsi <deje@miki.hu>
- *
- * 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/>.
- */
-
-/**
-\todo verify/extend multichannel support
-*/
-
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <windows.h>
-#define DIRECTSOUND_VERSION 0x0600
-#include <dsound.h>
-#include <math.h>
-
-#include <libavutil/avutil.h>
-#include <libavutil/common.h>
-
-#include "config.h"
-#include "audio/format.h"
-#include "ao.h"
-#include "internal.h"
-#include "common/msg.h"
-#include "osdep/timer.h"
-#include "osdep/io.h"
-#include "options/m_option.h"
-
-/**
-\todo use the definitions from the win32 api headers when they define these
-*/
-#define WAVE_FORMAT_IEEE_FLOAT 0x0003
-#define WAVE_FORMAT_DOLBY_AC3_SPDIF 0x0092
-#define WAVE_FORMAT_EXTENSIBLE 0xFFFE
-
-static const GUID KSDATAFORMAT_SUBTYPE_PCM = {
- 0x1, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
-};
-
-#if 0
-#define DSSPEAKER_HEADPHONE 0x00000001
-#define DSSPEAKER_MONO 0x00000002
-#define DSSPEAKER_QUAD 0x00000003
-#define DSSPEAKER_STEREO 0x00000004
-#define DSSPEAKER_SURROUND 0x00000005
-#define DSSPEAKER_5POINT1 0x00000006
-#endif
-
-#ifndef _WAVEFORMATEXTENSIBLE_
-typedef struct {
- WAVEFORMATEX Format;
- union {
- WORD wValidBitsPerSample; /* bits of precision */
- WORD wSamplesPerBlock; /* valid if wBitsPerSample==0 */
- WORD wReserved; /* If neither applies, set to zero. */
- } Samples;
- DWORD dwChannelMask; /* which channels are */
- /* present in stream */
- GUID SubFormat;
-} WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE;
-#endif
-
-struct priv {
- HINSTANCE hdsound_dll; ///handle to the dll
- LPDIRECTSOUND hds; ///direct sound object
- LPDIRECTSOUNDBUFFER hdspribuf; ///primary direct sound buffer
- LPDIRECTSOUNDBUFFER hdsbuf; ///secondary direct sound buffer (stream buffer)
- int buffer_size; ///size in bytes of the direct sound buffer
- int write_offset; ///offset of the write cursor in the direct sound buffer
- int min_free_space; ///if the free space is below this value get_space() will return 0
- ///there will always be at least this amout of free space to prevent
- ///get_space() from returning wrong values when buffer is 100% full.
- ///will be replaced with nBlockAlign in init()
- int underrun_check; ///0 or last reported free space (underrun detection)
- int device_num; ///wanted device number
- GUID device; ///guid of the device
- int audio_volume;
-
- int device_index;
-
- int outburst; ///play in multiple of chunks of this size
-
- int cfg_device;
- int cfg_buffersize;
-
- struct ao_device_list *listing; ///temporary during list_devs()
-};
-
-/***************************************************************************************/
-
-/**
-\brief output error message
-\param err error code
-\return string with the error message
-*/
-static char * dserr2str(int err)
-{
- switch (err) {
- case DS_OK: return "DS_OK";
- case DS_NO_VIRTUALIZATION: return "DS_NO_VIRTUALIZATION";
- case DSERR_ALLOCATED: return "DS_NO_VIRTUALIZATION";
- case DSERR_CONTROLUNAVAIL: return "DSERR_CONTROLUNAVAIL";
- case DSERR_INVALIDPARAM: return "DSERR_INVALIDPARAM";
- case DSERR_INVALIDCALL: return "DSERR_INVALIDCALL";
- case DSERR_GENERIC: return "DSERR_GENERIC";
- case DSERR_PRIOLEVELNEEDED: return "DSERR_PRIOLEVELNEEDED";
- case DSERR_OUTOFMEMORY: return "DSERR_OUTOFMEMORY";
- case DSERR_BADFORMAT: return "DSERR_BADFORMAT";
- case DSERR_UNSUPPORTED: return "DSERR_UNSUPPORTED";
- case DSERR_NODRIVER: return "DSERR_NODRIVER";
- case DSERR_ALREADYINITIALIZED: return "DSERR_ALREADYINITIALIZED";
- case DSERR_NOAGGREGATION: return "DSERR_NOAGGREGATION";
- case DSERR_BUFFERLOST: return "DSERR_BUFFERLOST";
- case DSERR_OTHERAPPHASPRIO: return "DSERR_OTHERAPPHASPRIO";
- case DSERR_UNINITIALIZED: return "DSERR_UNINITIALIZED";
- case DSERR_NOINTERFACE: return "DSERR_NOINTERFACE";
- case DSERR_ACCESSDENIED: return "DSERR_ACCESSDENIED";
- }
- return "unknown";
-}
-
-/**
-\brief uninitialize direct sound
-*/
-static void UninitDirectSound(struct ao *ao)
-{
- struct priv *p = ao->priv;
-
- // finally release the DirectSound object
- if (p->hds) {
- IDirectSound_Release(p->hds);
- p->hds = NULL;
- }
- // free DSOUND.DLL
- if (p->hdsound_dll) {
- FreeLibrary(p->hdsound_dll);
- p->hdsound_dll = NULL;
- }
- MP_VERBOSE(ao, "DirectSound uninitialized\n");
-}
-
-/**
-\brief enumerate direct sound devices
-\return TRUE to continue with the enumeration
-*/
-static BOOL CALLBACK DirectSoundEnum(LPGUID guid, LPCSTR desc, LPCSTR module,
- LPVOID context)
-{
- struct ao *ao = context;
- struct priv *p = ao->priv;
-
- MP_VERBOSE(ao, "%i %s ", p->device_index, desc);
- if (p->device_num == p->device_index) {
- MP_VERBOSE(ao, "<--");
- if (guid)
- memcpy(&p->device, guid, sizeof(GUID));
- }
- char *guidstr = talloc_strdup(NULL, "");
- if (guid) {
- wchar_t guidwstr[80] = {0};
- StringFromGUID2(guid, guidwstr, MP_ARRAY_SIZE(guidwstr));
- char *nstr = mp_to_utf8(NULL, guidwstr);
- if (nstr) {
- talloc_free(guidstr);
- guidstr = nstr;
- }
- }
- if (p->device_num < 0 && ao->device) {
- if (strcmp(ao->device, guidstr) == 0) {
- MP_VERBOSE(ao, "<--");
- p->device_num = p->device_index;
- if (guid)
- memcpy(&p->device, guid, sizeof(GUID));
- }
- }
- if (p->listing) {
- struct ao_device_desc e = {guidstr, desc};
- ao_device_list_add(p->listing, ao, &e);
- }
- talloc_free(guidstr);
-
- MP_VERBOSE(ao, "\n");
- p->device_index++;
- return TRUE;
-}
-
-static void EnumDevs(struct ao *ao)
-{
- struct priv *p = ao->priv;
-
- p->device_index = 0;
- p->device_num = p->cfg_device;
-
- HRESULT (WINAPI *OurDirectSoundEnumerate)(LPDSENUMCALLBACKA, LPVOID);
- OurDirectSoundEnumerate = (void *)GetProcAddress(p->hdsound_dll,
- "DirectSoundEnumerateA");
-
- if (OurDirectSoundEnumerate == NULL) {
- MP_ERR(ao, "GetProcAddress FAILED\n");
- return;
- }
-
- // Enumerate all directsound p->devices
- MP_VERBOSE(ao, "Output Devices:\n");
- OurDirectSoundEnumerate(DirectSoundEnum, ao);
-}
-
-static int LoadDirectSound(struct ao *ao)
-{
- struct priv *p = ao->priv;
-
- // initialize directsound
- p->hdsound_dll = LoadLibrary(L"DSOUND.DLL");
- if (p->hdsound_dll == NULL) {
- MP_ERR(ao, "cannot load DSOUND.DLL\n");
- return 0;
- }
-
- return 1;
-}
-
-static void list_devs(struct ao *ao, struct ao_device_list *list)
-{
- struct priv *p = ao->priv;
- bool need_init = !p->hdsound_dll;
- if (need_init && !LoadDirectSound(ao))
- return;
-
- p->listing = list;
- EnumDevs(ao);
- p->listing = NULL;
-
- if (need_init)
- UninitDirectSound(ao);
-}
-
-/**
-\brief initilize direct sound
-\return 0 if error, 1 if ok
-*/
-static int InitDirectSound(struct ao *ao)
-{
- struct priv *p = ao->priv;
-
- DSCAPS dscaps;
-
- if (!LoadDirectSound(ao))
- return 0;
-
- HRESULT (WINAPI *OurDirectSoundCreate)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN);
- OurDirectSoundCreate =
- (void *)GetProcAddress(p->hdsound_dll, "DirectSoundCreate");
-
- if (OurDirectSoundCreate == NULL) {
- MP_ERR(ao, "GetProcAddress FAILED\n");
- FreeLibrary(p->hdsound_dll);
- return 0;
- }
-
- EnumDevs(ao);
-
- // Create the direct sound object
- if (FAILED(OurDirectSoundCreate((p->device_num > 0) ? &p->device : NULL,
- &p->hds, NULL)))
- {
- MP_ERR(ao, "cannot create a DirectSound device\n");
- FreeLibrary(p->hdsound_dll);
- return 0;
- }
-
- /* Set DirectSound Cooperative level, ie what control we want over Windows
- * sound device. In our case, DSSCL_EXCLUSIVE means that we can modify the
- * settings of the primary buffer, but also that only the sound of our
- * application will be audible when it will have the focus.
- * !!! (this is not really working as intended yet because to set the
- * cooperative level you need the window handle of your application, and
- * I don't know of any easy way to get it. Especially since we might play
- * sound without any video, and so what window handle should we use ???
- * The hack for now is to use the Desktop window handle - it seems to be
- * working */
- if (IDirectSound_SetCooperativeLevel(p->hds, GetDesktopWindow(),
- DSSCL_EXCLUSIVE))
- {
- MP_ERR(ao, "cannot set direct sound cooperative level\n");
- IDirectSound_Release(p->hds);
- FreeLibrary(p->hdsound_dll);
- return 0;
- }
- MP_VERBOSE(ao, "DirectSound initialized\n");
-
- memset(&dscaps, 0, sizeof(DSCAPS));
- dscaps.dwSize = sizeof(DSCAPS);
- if (DS_OK == IDirectSound_GetCaps(p->hds, &dscaps)) {
- if (dscaps.dwFlags & DSCAPS_EMULDRIVER)
- MP_VERBOSE(ao, "DirectSound is emulated\n");
- } else {
- MP_VERBOSE(ao, "cannot get device capabilities\n");
- }
-
- return 1;
-}
-
-/**
-\brief destroy the direct sound buffer
-*/
-static void DestroyBuffer(struct ao *ao)
-{
- struct priv *p = ao->priv;
-
- if (p->hdsbuf) {
- IDirectSoundBuffer_Release(p->hdsbuf);
- p->hdsbuf = NULL;
- }
- if (p->hdspribuf) {
- IDirectSoundBuffer_Release(p->hdspribuf);
- p->hdspribuf = NULL;
- }
-}
-
-/**
-\brief fill sound buffer
-\param data pointer to the sound data to copy
-\param len length of the data to copy in bytes
-\return number of copyed bytes
-*/
-static int write_buffer(struct ao *ao, unsigned char *data, int len)
-{
- struct priv *p = ao->priv;
- HRESULT res;
- LPVOID lpvPtr1;
- DWORD dwBytes1;
- LPVOID lpvPtr2;
- DWORD dwBytes2;
-
- p->underrun_check = 0;
-
- // Lock the buffer
- res = IDirectSoundBuffer_Lock(p->hdsbuf, p->write_offset, len, &lpvPtr1,
- &dwBytes1, &lpvPtr2, &dwBytes2, 0);
- // If the buffer was lost, restore and retry lock.
- if (DSERR_BUFFERLOST == res) {
- IDirectSoundBuffer_Restore(p->hdsbuf);
- res = IDirectSoundBuffer_Lock(p->hdsbuf, p->write_offset, len, &lpvPtr1,
- &dwBytes1, &lpvPtr2, &dwBytes2, 0);
- }
-
-
- if (SUCCEEDED(res)) {
- memcpy(lpvPtr1, data, dwBytes1);
- if (NULL != lpvPtr2)
- memcpy(lpvPtr2, data + dwBytes1, dwBytes2);
- p->write_offset += dwBytes1 + dwBytes2;
- if (p->write_offset >= p->buffer_size)
- p->write_offset = dwBytes2;
-
- // Release the data back to DirectSound.
- res = IDirectSoundBuffer_Unlock(p->hdsbuf, lpvPtr1, dwBytes1, lpvPtr2,
- dwBytes2);
- if (SUCCEEDED(res)) {
- // Success.
- DWORD status;
- IDirectSoundBuffer_GetStatus(p->hdsbuf, &status);
- if (!(status & DSBSTATUS_PLAYING))
- res = IDirectSoundBuffer_Play(p->hdsbuf, 0, 0, DSBPLAY_LOOPING);
- return dwBytes1 + dwBytes2;
- }
- }
- // Lock, Unlock, or Restore failed.
- return 0;
-}
-
-/***************************************************************************************/
-
-/**
-\brief handle control commands
-\param cmd command
-\param arg argument
-\return CONTROL_OK or CONTROL_UNKNOWN in case the command is not supported
-*/
-static int control(struct ao *ao, enum aocontrol cmd, void *arg)
-{
- struct priv *p = ao->priv;
- DWORD volume;
- switch (cmd) {
- case AOCONTROL_GET_VOLUME: {
- ao_control_vol_t *vol = (ao_control_vol_t *)arg;
- vol->left = vol->right = p->audio_volume;
- return CONTROL_OK;
- }
- case AOCONTROL_SET_VOLUME: {
- ao_control_vol_t *vol = (ao_control_vol_t *)arg;
- volume = p->audio_volume = vol->right;
- if (volume < 1)
- volume = 1;
- volume = (DWORD)(log10(volume) * 5000.0) - 10000;
- IDirectSoundBuffer_SetVolume(p->hdsbuf, volume);
- return CONTROL_OK;
- }
- case AOCONTROL_HAS_SOFT_VOLUME:
- return CONTROL_TRUE;
- }
- return CONTROL_UNKNOWN;
-}
-
-/**
-\brief setup sound device
-\param rate samplerate
-\param channels number of channels
-\param format format
-\param flags unused
-\return 0=success -1=fail
-*/
-static int init(struct ao *ao)
-{
- struct priv *p = ao->priv;
- int res;
-
- if (!InitDirectSound(ao))
- return -1;
-
- p->audio_volume = 100;
-
- // ok, now create the buffers
- WAVEFORMATEXTENSIBLE wformat;
- DSBUFFERDESC dsbpridesc;
- DSBUFFERDESC dsbdesc;
- int format = af_fmt_from_planar(ao->format);
- int rate = ao->samplerate;
-
- if (!af_fmt_is_spdif(format)) {
- struct mp_chmap_sel sel = {0};
- mp_chmap_sel_add_waveext(&sel);
- if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels))
- return -1;
- }
- switch (format) {
- case AF_FORMAT_S24:
- case AF_FORMAT_S16:
- case AF_FORMAT_U8:
- break;
- default:
- if (af_fmt_is_spdif(format))
- break;
- MP_VERBOSE(ao, "format %s not supported defaulting to Signed 16-bit Little-Endian\n",
- af_fmt_to_str(format));
- format = AF_FORMAT_S16;
- }
- //set our audio parameters
- ao->samplerate = rate;
- ao->format = format;
- ao->bps = ao->channels.num * rate * af_fmt_to_bytes(format);
- int buffersize = ao->bps * p->cfg_buffersize / 1000;
- MP_VERBOSE(ao, "Samplerate:%iHz Channels:%i Format:%s\n", rate,
- ao->channels.num, af_fmt_to_str(format));
- MP_VERBOSE(ao, "Buffersize:%d bytes (%f msec)\n",
- buffersize, buffersize * 1000.0 / ao->bps);
-
- //fill waveformatex
- ZeroMemory(&wformat, sizeof(WAVEFORMATEXTENSIBLE));
- wformat.Format.cbSize = (ao->channels.num > 2)
- ? sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) : 0;
- wformat.Format.nChannels = ao->channels.num;
- wformat.Format.nSamplesPerSec = rate;
- if (af_fmt_is_spdif(format)) {
- // Whether it also works with e.g. DTS is unknown, but probably does.
- wformat.Format.wFormatTag = WAVE_FORMAT_DOLBY_AC3_SPDIF;
- wformat.Format.wBitsPerSample = 16;
- wformat.Format.nBlockAlign = 4;
- } else {
- wformat.Format.wFormatTag = (ao->channels.num > 2)
- ? WAVE_FORMAT_EXTENSIBLE : WAVE_FORMAT_PCM;
- int bps = af_fmt_to_bytes(format);
- wformat.Format.wBitsPerSample = bps * 8;
- wformat.Format.nBlockAlign = wformat.Format.nChannels * bps;
- }
-
- // fill in primary sound buffer descriptor
- memset(&dsbpridesc, 0, sizeof(DSBUFFERDESC));
- dsbpridesc.dwSize = sizeof(DSBUFFERDESC);
- dsbpridesc.dwFlags = DSBCAPS_PRIMARYBUFFER;
- dsbpridesc.dwBufferBytes = 0;
- dsbpridesc.lpwfxFormat = NULL;
-
- // fill in the secondary sound buffer (=stream buffer) descriptor
- memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));
- dsbdesc.dwSize = sizeof(DSBUFFERDESC);
- dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 /** Better position accuracy */
- | DSBCAPS_GLOBALFOCUS /** Allows background playing */
- | DSBCAPS_CTRLVOLUME; /** volume control enabled */
-
- if (ao->channels.num > 2) {
- wformat.dwChannelMask = mp_chmap_to_waveext(&ao->channels);
- wformat.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
- wformat.Samples.wValidBitsPerSample = wformat.Format.wBitsPerSample;
- // Needed for 5.1 on emu101k - shit soundblaster
- dsbdesc.dwFlags |= DSBCAPS_LOCHARDWARE;
- }
- wformat.Format.nAvgBytesPerSec = wformat.Format.nSamplesPerSec *
- wformat.Format.nBlockAlign;
-
- dsbdesc.dwBufferBytes = buffersize;
- dsbdesc.lpwfxFormat = (WAVEFORMATEX *)&wformat;
- p->buffer_size = dsbdesc.dwBufferBytes;
- p->write_offset = 0;
- p->min_free_space = wformat.Format.nBlockAlign;
- p->outburst = wformat.Format.nBlockAlign * 512;
-
- // create primary buffer and set its format
-
- res = IDirectSound_CreateSoundBuffer(p->hds, &dsbpridesc, &p->hdspribuf, NULL);
- if (res != DS_OK) {
- UninitDirectSound(ao);
- MP_ERR(ao, "cannot create primary buffer (%s)\n", dserr2str(res));
- return -1;
- }
- res = IDirectSoundBuffer_SetFormat(p->hdspribuf, (WAVEFORMATEX *)&wformat);
- if (res != DS_OK) {
- MP_WARN(ao, "cannot set primary buffer format (%s), using "
- "standard setting (bad quality)", dserr2str(res));
- }
-
- MP_VERBOSE(ao, "primary buffer created\n");
-
- // now create the stream buffer
-
- res = IDirectSound_CreateSoundBuffer(p->hds, &dsbdesc, &p->hdsbuf, NULL);
- if (res != DS_OK) {
- if (dsbdesc.dwFlags & DSBCAPS_LOCHARDWARE) {
- // Try without DSBCAPS_LOCHARDWARE
- dsbdesc.dwFlags &= ~DSBCAPS_LOCHARDWARE;
- res = IDirectSound_CreateSoundBuffer(p->hds, &dsbdesc, &p->hdsbuf, NULL);
- }
- if (res != DS_OK) {
- UninitDirectSound(ao);
- MP_ERR(ao, "cannot create secondary (stream)buffer (%s)\n",
- dserr2str(res));
- return -1;
- }
- }
- MP_VERBOSE(ao, "secondary (stream)buffer created\n");
- return 0;
-}
-
-
-
-/**
-\brief stop playing and empty buffers (for seeking/pause)
-*/
-static void reset(struct ao *ao)
-{
- struct priv *p = ao->priv;
-
- IDirectSoundBuffer_Stop(p->hdsbuf);
- // reset directsound buffer
- IDirectSoundBuffer_SetCurrentPosition(p->hdsbuf, 0);
- p->write_offset = 0;
- p->underrun_check = 0;
-}
-
-/**
-\brief stop playing, keep buffers (for pause)
-*/
-static void audio_pause(struct ao *ao)
-{
- struct priv *p = ao->priv;
-
- IDirectSoundBuffer_Stop(p->hdsbuf);
-}
-
-/**
-\brief resume playing, after audio_pause()
-*/
-static void audio_resume(struct ao *ao)
-{
- struct priv *p = ao->priv;
-
- IDirectSoundBuffer_Play(p->hdsbuf, 0, 0, DSBPLAY_LOOPING);
-}
-
-/**
-\brief close audio device
-\param immed stop playback immediately
-*/
-static void uninit(struct ao *ao)
-{
- reset(ao);
-
- DestroyBuffer(ao);
- UninitDirectSound(ao);
-}
-
-// return exact number of free (safe to write) bytes
-static int check_free_buffer_size(struct ao *ao)
-{
- struct priv *p = ao->priv;
- int space;
- DWORD play_offset;
- IDirectSoundBuffer_GetCurrentPosition(p->hdsbuf, &play_offset, NULL);
- space = p->buffer_size - (p->write_offset - play_offset);
- // | | <-- const --> | | |
- // buffer start play_cursor write_cursor p->write_offset buffer end
- // play_cursor is the actual position of the play cursor
- // write_cursor is the position after which it is assumed to be save to write data
- // p->write_offset is the position where we actually write the data to
- if (space > p->buffer_size)
- space -= p->buffer_size; // p->write_offset < play_offset
- // Check for buffer underruns. An underrun happens if DirectSound
- // started to play old data beyond the current p->write_offset. Detect this
- // by checking whether the free space shrinks, even though no data was
- // written (i.e. no write_buffer). Doesn't always work, but the only
- // reason we need this is to deal with the situation when playback ends,
- // and the buffer is only half-filled.
- if (space < p->underrun_check) {
- // there's no useful data in the buffers
- space = p->buffer_size;
- reset(ao);
- }
- p->underrun_check = space;
- return space;
-}
-
-/**
- \brief find out how many bytes can be written into the audio buffer without
- \return free space in bytes, has to return 0 if the buffer is almost full
- */
-static int get_space(struct ao *ao)
-{
- struct priv *p = ao->priv;
-
- int space = check_free_buffer_size(ao);
- if (space < p->min_free_space)
- return 0;
- return (space - p->min_free_space) / p->outburst * p->outburst / ao->sstride;
-}
-
-/**
-\brief play 'len' bytes of 'data'
-\param data pointer to the data to play
-\param len size in bytes of the data buffer, gets rounded down to outburst*n
-\param flags currently unused
-\return number of played bytes
-*/
-static int play(struct ao *ao, void **data, int samples, int flags)
-{
- struct priv *p = ao->priv;
- int len = samples * ao->sstride;
-
- int space = check_free_buffer_size(ao);
- if (space < len)
- len = space;
-
- if (!(flags & AOPLAY_FINAL_CHUNK))
- len = (len / p->outburst) * p->outburst;
- return write_buffer(ao, data[0], len) / ao->sstride;
-}
-
-/**
-\brief get the delay between the first and last sample in the buffer
-\return delay in seconds
-*/
-static double get_delay(struct ao *ao)
-{
- struct priv *p = ao->priv;
-
- int space = check_free_buffer_size(ao);
- return (p->buffer_size - space) / (double)ao->bps;
-}
-
-#define OPT_BASE_STRUCT struct priv
-
-const struct ao_driver audio_out_dsound = {
- .description = "Windows DirectSound audio output",
- .name = "dsound",
- .init = init,
- .uninit = uninit,
- .control = control,
- .get_space = get_space,
- .play = play,
- .get_delay = get_delay,
- .pause = audio_pause,
- .resume = audio_resume,
- .reset = reset,
- .list_devs = list_devs,
- .priv_size = sizeof(struct priv),
- .options = (const struct m_option[]) {
- OPT_INT("device", cfg_device, 0, OPTDEF_INT(-1)),
- OPT_INTRANGE("buffersize", cfg_buffersize, 0, 1, 10000, OPTDEF_INT(200)),
- {0}
- },
-};
diff --git a/audio/out/ao_lavc.c b/audio/out/ao_lavc.c
index 332350f..b671301 100644
--- a/audio/out/ao_lavc.c
+++ b/audio/out/ao_lavc.c
@@ -31,7 +31,7 @@
#include "common/common.h"
#include "audio/format.h"
#include "audio/fmt-conversion.h"
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "ao.h"
#include "internal.h"
#include "common/msg.h"
diff --git a/audio/out/ao_null.c b/audio/out/ao_null.c
index 0af0a97..7d45795 100644
--- a/audio/out/ao_null.c
+++ b/audio/out/ao_null.c
@@ -26,7 +26,7 @@
#include <stdlib.h>
#include <math.h>
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "config.h"
#include "osdep/timer.h"
diff --git a/audio/out/ao_openal.c b/audio/out/ao_openal.c
index c6c924b..72e8799 100644
--- a/audio/out/ao_openal.c
+++ b/audio/out/ao_openal.c
@@ -236,6 +236,7 @@ static int init(struct ao *ao)
return 0;
err_out:
+ ao_data = NULL;
return -1;
}
diff --git a/audio/out/ao_opensles.c b/audio/out/ao_opensles.c
new file mode 100644
index 0000000..0e80829
--- /dev/null
+++ b/audio/out/ao_opensles.c
@@ -0,0 +1,250 @@
+/*
+ * OpenSL ES audio output driver.
+ * Copyright (C) 2016 Ilya Zhuravlev <whatever@xyz.is>
+ *
+ * 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 "ao.h"
+#include "internal.h"
+#include "common/msg.h"
+#include "audio/format.h"
+#include "options/m_option.h"
+#include "osdep/timer.h"
+
+#include <SLES/OpenSLES.h>
+#include <SLES/OpenSLES_Android.h>
+
+#include <pthread.h>
+
+struct priv {
+ SLObjectItf sl, output_mix, player;
+ SLBufferQueueItf buffer_queue;
+ SLEngineItf engine;
+ SLPlayItf play;
+ char *curbuf, *buf1, *buf2;
+ size_t buffer_size;
+ pthread_mutex_t buffer_lock;
+
+ int cfg_frames_per_buffer;
+ int cfg_sample_rate;
+};
+
+static const int fmtmap[][2] = {
+ { AF_FORMAT_U8, SL_PCMSAMPLEFORMAT_FIXED_8 },
+ { AF_FORMAT_S16, SL_PCMSAMPLEFORMAT_FIXED_16 },
+ { AF_FORMAT_S32, SL_PCMSAMPLEFORMAT_FIXED_32 },
+ { 0 }
+};
+
+#define DESTROY(thing) \
+ if (p->thing) { \
+ (*p->thing)->Destroy(p->thing); \
+ p->thing = NULL; \
+ }
+
+static void uninit(struct ao *ao)
+{
+ struct priv *p = ao->priv;
+
+ DESTROY(player);
+ DESTROY(output_mix);
+ DESTROY(sl);
+
+ p->buffer_queue = NULL;
+ p->engine = NULL;
+ p->play = NULL;
+
+ pthread_mutex_destroy(&p->buffer_lock);
+
+ free(p->buf1);
+ free(p->buf2);
+ p->curbuf = p->buf1 = p->buf2 = NULL;
+ p->buffer_size = 0;
+}
+
+#undef DESTROY
+
+static void buffer_callback(SLBufferQueueItf buffer_queue, void *context)
+{
+ struct ao *ao = context;
+ struct priv *p = ao->priv;
+ SLresult res;
+ void *data[1];
+ double delay;
+
+ pthread_mutex_lock(&p->buffer_lock);
+
+ data[0] = p->curbuf;
+ delay = 2 * p->buffer_size / (double)ao->bps;
+ ao_read_data(ao, data, p->buffer_size / ao->sstride,
+ mp_time_us() + 1000000LL * delay);
+
+ res = (*buffer_queue)->Enqueue(buffer_queue, p->curbuf, p->buffer_size);
+ if (res != SL_RESULT_SUCCESS)
+ MP_ERR(ao, "Failed to Enqueue: %d\n", res);
+ else
+ p->curbuf = (p->curbuf == p->buf1) ? p->buf2 : p->buf1;
+
+ pthread_mutex_unlock(&p->buffer_lock);
+}
+
+#define DEFAULT_BUFFER_SIZE_MS 50
+
+#define CHK(stmt) \
+ { \
+ SLresult res = stmt; \
+ if (res != SL_RESULT_SUCCESS) { \
+ MP_ERR(ao, "%s: %d\n", #stmt, res); \
+ goto error; \
+ } \
+ }
+
+static int init(struct ao *ao)
+{
+ struct priv *p = ao->priv;
+ SLDataLocator_BufferQueue locator_buffer_queue;
+ SLDataLocator_OutputMix locator_output_mix;
+ SLDataFormat_PCM pcm;
+ SLDataSource audio_source;
+ SLDataSink audio_sink;
+
+ // This AO only supports two channels at the moment
+ mp_chmap_from_channels(&ao->channels, 2);
+
+ CHK(slCreateEngine(&p->sl, 0, NULL, 0, NULL, NULL));
+ CHK((*p->sl)->Realize(p->sl, SL_BOOLEAN_FALSE));
+ CHK((*p->sl)->GetInterface(p->sl, SL_IID_ENGINE, (void*)&p->engine));
+ CHK((*p->engine)->CreateOutputMix(p->engine, &p->output_mix, 0, NULL, NULL));
+ CHK((*p->output_mix)->Realize(p->output_mix, SL_BOOLEAN_FALSE));
+
+ locator_buffer_queue.locatorType = SL_DATALOCATOR_BUFFERQUEUE;
+ locator_buffer_queue.numBuffers = 2;
+
+ pcm.formatType = SL_DATAFORMAT_PCM;
+ pcm.numChannels = 2;
+
+ int compatible_formats[AF_FORMAT_COUNT];
+ af_get_best_sample_formats(ao->format, compatible_formats);
+ pcm.bitsPerSample = 0;
+ for (int i = 0; compatible_formats[i] && !pcm.bitsPerSample; ++i)
+ for (int j = 0; fmtmap[j][0]; ++j)
+ if (compatible_formats[i] == fmtmap[j][0]) {
+ ao->format = fmtmap[j][0];
+ pcm.bitsPerSample = fmtmap[j][1];
+ break;
+ }
+ if (!pcm.bitsPerSample) {
+ MP_ERR(ao, "Cannot find compatible audio format\n");
+ goto error;
+ }
+ pcm.containerSize = 8 * af_fmt_to_bytes(ao->format);
+ pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
+ pcm.endianness = SL_BYTEORDER_LITTLEENDIAN;
+
+ if (p->cfg_sample_rate)
+ ao->samplerate = p->cfg_sample_rate;
+
+ // samplesPerSec is misnamed, actually it's samples per ms
+ pcm.samplesPerSec = ao->samplerate * 1000;
+
+ if (p->cfg_frames_per_buffer)
+ ao->device_buffer = p->cfg_frames_per_buffer;
+ else
+ ao->device_buffer = ao->samplerate * DEFAULT_BUFFER_SIZE_MS / 1000;
+ p->buffer_size = ao->device_buffer * ao->channels.num *
+ af_fmt_to_bytes(ao->format);
+ p->buf1 = calloc(1, p->buffer_size);
+ p->buf2 = calloc(1, p->buffer_size);
+ p->curbuf = p->buf1;
+ if (!p->buf1 || !p->buf2) {
+ MP_ERR(ao, "Failed to allocate device buffer\n");
+ goto error;
+ }
+ int r = pthread_mutex_init(&p->buffer_lock, NULL);
+ if (r) {
+ MP_ERR(ao, "Failed to initialize the mutex: %d\n", r);
+ goto error;
+ }
+
+ audio_source.pFormat = (void*)&pcm;
+ audio_source.pLocator = (void*)&locator_buffer_queue;
+
+ locator_output_mix.locatorType = SL_DATALOCATOR_OUTPUTMIX;
+ locator_output_mix.outputMix = p->output_mix;
+
+ audio_sink.pLocator = (void*)&locator_output_mix;
+ audio_sink.pFormat = NULL;
+
+ SLboolean required[] = { SL_BOOLEAN_TRUE };
+ SLInterfaceID iid_array[] = { SL_IID_BUFFERQUEUE };
+ CHK((*p->engine)->CreateAudioPlayer(p->engine, &p->player, &audio_source,
+ &audio_sink, 1, iid_array, required));
+ CHK((*p->player)->Realize(p->player, SL_BOOLEAN_FALSE));
+ CHK((*p->player)->GetInterface(p->player, SL_IID_PLAY, (void*)&p->play));
+ CHK((*p->player)->GetInterface(p->player, SL_IID_BUFFERQUEUE,
+ (void*)&p->buffer_queue));
+ CHK((*p->buffer_queue)->RegisterCallback(p->buffer_queue,
+ buffer_callback, ao));
+
+ return 1;
+error:
+ uninit(ao);
+ return -1;
+}
+
+#undef CHK
+
+static void set_play_state(struct ao *ao, SLuint32 state)
+{
+ struct priv *p = ao->priv;
+ SLresult res = (*p->play)->SetPlayState(p->play, state);
+ if (res != SL_RESULT_SUCCESS)
+ MP_ERR(ao, "Failed to SetPlayState(%d): %d\n", state, res);
+}
+
+static void reset(struct ao *ao)
+{
+ set_play_state(ao, SL_PLAYSTATE_STOPPED);
+}
+
+static void resume(struct ao *ao)
+{
+ struct priv *p = ao->priv;
+ set_play_state(ao, SL_PLAYSTATE_PLAYING);
+
+ // enqueue two buffers
+ buffer_callback(p->buffer_queue, ao);
+ buffer_callback(p->buffer_queue, ao);
+}
+
+#define OPT_BASE_STRUCT struct priv
+
+const struct ao_driver audio_out_opensles = {
+ .description = "OpenSL ES audio output",
+ .name = "opensles",
+ .init = init,
+ .uninit = uninit,
+ .reset = reset,
+ .resume = resume,
+
+ .priv_size = sizeof(struct priv),
+ .options = (const struct m_option[]) {
+ OPT_INTRANGE("frames-per-buffer", cfg_frames_per_buffer, 0, 1, 10000),
+ OPT_INTRANGE("sample-rate", cfg_sample_rate, 0, 1000, 100000),
+ {0}
+ },
+};
diff --git a/audio/out/ao_pcm.c b/audio/out/ao_pcm.c
index 76f0315..c26f614 100644
--- a/audio/out/ao_pcm.c
+++ b/audio/out/ao_pcm.c
@@ -27,7 +27,7 @@
#include <libavutil/common.h>
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "options/m_option.h"
#include "audio/format.h"
diff --git a/audio/out/ao_pulse.c b/audio/out/ao_pulse.c
index 78d8ac2..d553b67 100644
--- a/audio/out/ao_pulse.c
+++ b/audio/out/ao_pulse.c
@@ -376,22 +376,8 @@ fail:
return -1;
}
-static int init(struct ao *ao)
+static bool set_format(struct ao *ao, pa_format_info *format)
{
- struct pa_channel_map map;
- pa_proplist *proplist = NULL;
- pa_format_info *format = NULL;
- struct priv *priv = ao->priv;
- char *sink = priv->cfg_sink && priv->cfg_sink[0] ? priv->cfg_sink : ao->device;
-
- if (pa_init_boilerplate(ao) < 0)
- return -1;
-
- pa_threaded_mainloop_lock(priv->mainloop);
-
- if (!(format = pa_format_info_new()))
- goto unlock_and_fail;
-
ao->format = af_fmt_from_planar(ao->format);
format->encoding = map_digital_format(ao->format);
@@ -411,8 +397,29 @@ static int init(struct ao *ao)
pa_format_info_set_sample_format(format, fmt_map->pa_format);
}
+ struct pa_channel_map map;
+
if (!select_chmap(ao, &map))
- goto unlock_and_fail;
+ return false;
+
+ pa_format_info_set_rate(format, ao->samplerate);
+ pa_format_info_set_channels(format, ao->channels.num);
+ pa_format_info_set_channel_map(format, &map);
+
+ return ao->samplerate < PA_RATE_MAX && pa_format_info_valid(format);
+}
+
+static int init(struct ao *ao)
+{
+ pa_proplist *proplist = NULL;
+ pa_format_info *format = NULL;
+ struct priv *priv = ao->priv;
+ char *sink = priv->cfg_sink && priv->cfg_sink[0] ? priv->cfg_sink : ao->device;
+
+ if (pa_init_boilerplate(ao) < 0)
+ return -1;
+
+ pa_threaded_mainloop_lock(priv->mainloop);
if (!(proplist = pa_proplist_new())) {
MP_ERR(ao, "Failed to allocate proplist\n");
@@ -420,13 +427,17 @@ static int init(struct ao *ao)
}
(void)pa_proplist_sets(proplist, PA_PROP_MEDIA_ICON_NAME, ao->client_name);
- pa_format_info_set_rate(format, ao->samplerate);
- pa_format_info_set_channels(format, ao->channels.num);
- pa_format_info_set_channel_map(format, &map);
-
- if (!pa_format_info_valid(format)) {
- MP_ERR(ao, "Invalid audio format\n");
+ if (!(format = pa_format_info_new()))
goto unlock_and_fail;
+
+ if (!set_format(ao, format)) {
+ ao->channels = (struct mp_chmap) MP_CHMAP_INIT_STEREO;
+ ao->samplerate = 48000;
+ ao->format = AF_FORMAT_FLOAT;
+ if (!set_format(ao, format)) {
+ MP_ERR(ao, "Invalid audio format\n");
+ goto unlock_and_fail;
+ }
}
if (!(priv->stream = pa_stream_new_extended(priv->context, "audio stream",
diff --git a/audio/out/ao_rsound.c b/audio/out/ao_rsound.c
index 0385b09..5379e73 100644
--- a/audio/out/ao_rsound.c
+++ b/audio/out/ao_rsound.c
@@ -26,7 +26,7 @@
#include <unistd.h>
#include <rsound.h>
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "options/m_option.h"
#include "osdep/timer.h"
diff --git a/audio/out/ao_sdl.c b/audio/out/ao_sdl.c
index bf65a4a..627a109 100644
--- a/audio/out/ao_sdl.c
+++ b/audio/out/ao_sdl.c
@@ -4,23 +4,23 @@
*
* 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"
#include "audio/format.h"
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "ao.h"
#include "internal.h"
#include "common/common.h"
diff --git a/audio/out/ao_wasapi.c b/audio/out/ao_wasapi.c
index 2edbdf5..eecfded 100644
--- a/audio/out/ao_wasapi.c
+++ b/audio/out/ao_wasapi.c
@@ -3,63 +3,78 @@
*
* Original author: Jonathan Yong <10walls@gmail.com>
*
- * 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 <math.h>
#include <inttypes.h>
-#include <process.h>
-#include <initguid.h>
-#include <audioclient.h>
-#include <endpointvolume.h>
-#include <mmdeviceapi.h>
-#include <avrt.h>
+#include <libavutil/mathematics.h>
-#include "audio/out/ao_wasapi.h"
-#include "audio/out/ao_wasapi_utils.h"
-
-#include "audio/format.h"
+#include "options/m_option.h"
#include "osdep/timer.h"
#include "osdep/io.h"
+#include "misc/dispatch.h"
+#include "ao_wasapi.h"
+
+// naive av_rescale for unsigned
+static UINT64 uint64_scale(UINT64 x, UINT64 num, UINT64 den)
+{
+ return (x / den) * num
+ + ((x % den) * (num / den))
+ + ((x % den) * (num % den)) / den;
+}
-static HRESULT get_device_delay(struct wasapi_state *state, double *delay) {
+static HRESULT get_device_delay(struct wasapi_state *state, double *delay_us) {
UINT64 sample_count = atomic_load(&state->sample_count);
UINT64 position, qpc_position;
HRESULT hr;
hr = IAudioClock_GetPosition(state->pAudioClock, &position, &qpc_position);
- /* GetPosition succeeded, but the result may be inaccurate due to the length of the call */
- /* http://msdn.microsoft.com/en-us/library/windows/desktop/dd370889%28v=vs.85%29.aspx */
- if (hr == S_FALSE) {
- MP_DBG(state, "Possibly inaccurate device position.\n");
- hr = S_OK;
- }
EXIT_ON_ERROR(hr);
+ // GetPosition succeeded, but the result may be
+ // inaccurate due to the length of the call
+ // http://msdn.microsoft.com/en-us/library/windows/desktop/dd370889%28v=vs.85%29.aspx
+ if (hr == S_FALSE)
+ MP_VERBOSE(state, "Possibly inaccurate device position.\n");
+
+ // convert position to number of samples careful to avoid overflow
+ UINT64 sample_position = uint64_scale(position,
+ state->format.Format.nSamplesPerSec,
+ state->clock_frequency);
+ INT64 diff = sample_count - sample_position;
+ *delay_us = diff * 1e6 / state->format.Format.nSamplesPerSec;
+
+ // Correct for any delay in IAudioClock_GetPosition above.
+ // This should normally be very small (<1 us), but just in case. . .
+ LARGE_INTEGER qpc;
+ QueryPerformanceCounter(&qpc);
+ INT64 qpc_diff = av_rescale(qpc.QuadPart, 10000000, state->qpc_frequency.QuadPart)
+ - qpc_position;
+ // ignore the above calculation if it yields more than 10 seconds (due to
+ // possible overflow inside IAudioClock_GetPosition)
+ if (qpc_diff < 10 * 10000000) {
+ *delay_us -= qpc_diff / 10.0; // convert to us
+ } else {
+ MP_VERBOSE(state, "Insane qpc delay correction of %g seconds. "
+ "Ignoring it.\n", qpc_diff / 10000000.0);
+ }
- LARGE_INTEGER qpc_count;
- QueryPerformanceCounter(&qpc_count);
- double qpc_diff = (qpc_count.QuadPart * 1e7 / state->qpc_frequency.QuadPart) - qpc_position;
-
- position += state->clock_frequency * (uint64_t) (qpc_diff / 1e7);
-
- /* convert position to the same base as sample_count */
- position = position * state->format.Format.nSamplesPerSec / state->clock_frequency;
-
- double diff = sample_count - position;
- *delay = diff / state->format.Format.nSamplesPerSec;
-
- MP_TRACE(state, "Device delay: %g samples (%g ms)\n", diff, *delay * 1000);
+ if (sample_count > 0 && *delay_us <= 0) {
+ MP_WARN(state, "Under-run: Device delay: %g us\n", *delay_us);
+ } else {
+ MP_TRACE(state, "Device delay: %g us\n", *delay_us);
+ }
return S_OK;
exit_label:
@@ -67,24 +82,44 @@ exit_label:
return hr;
}
-static void thread_feed(struct ao *ao)
+static bool thread_feed(struct ao *ao)
{
struct wasapi_state *state = ao->priv;
HRESULT hr;
UINT32 frame_count = state->bufferFrameCount;
-
+ UINT32 padding;
+ hr = IAudioClient_GetCurrentPadding(state->pAudioClient, &padding);
+ EXIT_ON_ERROR(hr);
+ bool refill = false;
if (state->share_mode == AUDCLNT_SHAREMODE_SHARED) {
- UINT32 padding = 0;
- hr = IAudioClient_GetCurrentPadding(state->pAudioClient, &padding);
- EXIT_ON_ERROR(hr);
-
+ // Return if there's nothing to do.
+ if (frame_count <= padding)
+ return false;
+ // In shared mode, there is only one buffer of size bufferFrameCount.
+ // We must therefore take care not to overwrite the samples that have
+ // yet to play.
frame_count -= padding;
- MP_TRACE(ao, "Frame to fill: %"PRIu32". Padding: %"PRIu32"\n", frame_count, padding);
+ } else if (padding >= 2 * frame_count) {
+ // In exclusive mode, we exchange entire buffers of size
+ // bufferFrameCount with the device. If there are already two such
+ // full buffers waiting to play, there is no work to do.
+ return false;
+ } else if (padding < frame_count) {
+ // If there is not at least one full buffer of audio queued to play in
+ // exclusive mode, call this function again immediately to try and catch
+ // up and avoid a cascade of under-runs. WASAPI doesn't seem to be smart
+ // enough to send more feed events when it gets behind.
+ refill = true;
}
- double delay;
- hr = get_device_delay(state, &delay);
+ MP_TRACE(ao, "Frame to fill: %"PRIu32". Padding: %"PRIu32"\n",
+ frame_count, padding);
+
+ double delay_us;
+ hr = get_device_delay(state, &delay_us);
EXIT_ON_ERROR(hr);
+ // add the buffer delay
+ delay_us += frame_count * 1e6 / state->format.Format.nSamplesPerSec;
BYTE *pData;
hr = IAudioRenderClient_GetBuffer(state->pRenderClient,
@@ -93,50 +128,23 @@ static void thread_feed(struct ao *ao)
BYTE *data[1] = {pData};
- ao_read_data(ao, (void**)data, frame_count, (int64_t) (
- mp_time_us() + delay * 1e6 +
- frame_count * 1e6 / state->format.Format.nSamplesPerSec));
+ ao_read_data(ao, (void **)data, frame_count,
+ mp_time_us() + (int64_t)llrint(delay_us));
+ // note, we can't use ao_read_data return value here since we already
+ // commited to frame_count above in the GetBuffer call
hr = IAudioRenderClient_ReleaseBuffer(state->pRenderClient,
frame_count, 0);
EXIT_ON_ERROR(hr);
atomic_fetch_add(&state->sample_count, frame_count);
- return;
+ return refill;
exit_label:
MP_ERR(state, "Error feeding audio: %s\n", mp_HRESULT_to_str(hr));
MP_VERBOSE(ao, "Requesting ao reload\n");
ao_request_reload(ao);
- return;
-}
-
-static void thread_resume(struct ao *ao)
-{
- struct wasapi_state *state = ao->priv;
- HRESULT hr;
-
- MP_DBG(state, "Thread Resume\n");
- UINT32 padding = 0;
- hr = IAudioClient_GetCurrentPadding(state->pAudioClient, &padding);
- if (hr != S_OK) {
- MP_ERR(state, "IAudioClient_GetCurrentPadding returned %s\n",
- mp_HRESULT_to_str(hr));
- }
-
- /* Fill the buffer before starting, but only if there is no audio queued to play. */
- /* This prevents overfilling the buffer, which leads to problems in exclusive mode */
- if (padding < (UINT32) state->bufferFrameCount)
- thread_feed(ao);
-
- // start feeding next wakeup if something else hasn't been requested
- int expected = WASAPI_THREAD_RESUME;
- atomic_compare_exchange_strong(&state->thread_state, &expected, WASAPI_THREAD_FEED);
- hr = IAudioClient_Start(state->pAudioClient);
- if (hr != S_OK)
- MP_ERR(state, "IAudioClient_Start returned %s\n", mp_HRESULT_to_str(hr));
-
- return;
+ return false;
}
static void thread_reset(struct ao *ao)
@@ -145,20 +153,43 @@ static void thread_reset(struct ao *ao)
HRESULT hr;
MP_DBG(state, "Thread Reset\n");
hr = IAudioClient_Stop(state->pAudioClient);
- /* we may get S_FALSE if the stream is already stopped */
- if (hr != S_OK && hr != S_FALSE)
+ if (FAILED(hr))
MP_ERR(state, "IAudioClient_Stop returned: %s\n", mp_HRESULT_to_str(hr));
- /* we may get S_FALSE if the stream is already reset */
hr = IAudioClient_Reset(state->pAudioClient);
- if (hr != S_OK && hr != S_FALSE)
+ if (FAILED(hr))
MP_ERR(state, "IAudioClient_Reset returned: %s\n", mp_HRESULT_to_str(hr));
atomic_store(&state->sample_count, 0);
- // start feeding next wakeup if something else hasn't been requested
- int expected = WASAPI_THREAD_RESET;
- atomic_compare_exchange_strong(&state->thread_state, &expected, WASAPI_THREAD_FEED);
- return;
+}
+
+static void thread_resume(struct ao *ao)
+{
+ struct wasapi_state *state = ao->priv;
+ MP_DBG(state, "Thread Resume\n");
+ thread_reset(ao);
+ thread_feed(ao);
+
+ HRESULT hr = IAudioClient_Start(state->pAudioClient);
+ if (FAILED(hr)) {
+ MP_ERR(state, "IAudioClient_Start returned %s\n",
+ mp_HRESULT_to_str(hr));
+ }
+}
+
+static void thread_wakeup(void *ptr)
+{
+ struct ao *ao = ptr;
+ struct wasapi_state *state = ao->priv;
+ SetEvent(state->hWake);
+}
+
+static void set_thread_state(struct ao *ao,
+ enum wasapi_thread_state thread_state)
+{
+ struct wasapi_state *state = ao->priv;
+ atomic_store(&state->thread_state, thread_state);
+ thread_wakeup(ao);
}
static DWORD __stdcall AudioThread(void *lpParameter)
@@ -169,43 +200,38 @@ static DWORD __stdcall AudioThread(void *lpParameter)
state->init_ret = wasapi_thread_init(ao);
SetEvent(state->hInitDone);
- if (state->init_ret != S_OK)
+ if (FAILED(state->init_ret))
goto exit_label;
MP_DBG(ao, "Entering dispatch loop\n");
- while (true) { /* watch events */
- HANDLE events[] = {state->hWake};
- switch (MsgWaitForMultipleObjects(MP_ARRAY_SIZE(events), events, FALSE, INFINITE,
- QS_POSTMESSAGE | QS_SENDMESSAGE)) {
- /* AudioThread wakeup */
- case WAIT_OBJECT_0:
- switch (atomic_load(&state->thread_state)) {
- case WASAPI_THREAD_FEED:
- thread_feed(ao);
- break;
- case WASAPI_THREAD_RESET:
- thread_reset(ao);
- break;
- case WASAPI_THREAD_RESUME:
- thread_reset(ao);
- thread_resume(ao);
- break;
- case WASAPI_THREAD_SHUTDOWN:
- thread_reset(ao);
- goto exit_label;
- default:
- MP_ERR(ao, "Unhandled thread state\n");
- goto exit_label;
- }
+ while (true) {
+ if (WaitForSingleObject(state->hWake, INFINITE) != WAIT_OBJECT_0)
+ MP_ERR(ao, "Unexpected return value from WaitForSingleObject\n");
+
+ mp_dispatch_queue_process(state->dispatch, 0);
+
+ int thread_state = atomic_load(&state->thread_state);
+ switch (thread_state) {
+ case WASAPI_THREAD_FEED:
+ // fill twice on under-full buffer (see comment in thread_feed)
+ if (thread_feed(ao) && thread_feed(ao))
+ MP_ERR(ao, "Unable to fill buffer fast enough\n");
break;
- /* messages to dispatch (COM marshalling) */
- case (WAIT_OBJECT_0 + MP_ARRAY_SIZE(events)):
- wasapi_dispatch(ao);
+ case WASAPI_THREAD_RESET:
+ thread_reset(ao);
break;
- default:
- MP_ERR(ao, "Unhandled thread event\n");
+ case WASAPI_THREAD_RESUME:
+ thread_resume(ao);
+ break;
+ case WASAPI_THREAD_SHUTDOWN:
+ thread_reset(ao);
goto exit_label;
+ default:
+ MP_ERR(ao, "Unhandled thread state: %d\n", thread_state);
}
+ // the default is to feed unless something else is requested
+ atomic_compare_exchange_strong(&state->thread_state, &thread_state,
+ WASAPI_THREAD_FEED);
}
exit_label:
wasapi_thread_uninit(ao);
@@ -215,33 +241,28 @@ exit_label:
return 0;
}
-static void set_thread_state(struct ao *ao, enum wasapi_thread_state thread_state)
-{
- struct wasapi_state *state = ao->priv;
- atomic_store(&state->thread_state, thread_state);
- SetEvent(state->hWake);
-}
-
static void uninit(struct ao *ao)
{
MP_DBG(ao, "Uninit wasapi\n");
struct wasapi_state *state = ao->priv;
- wasapi_release_proxies(state);
if (state->hWake)
set_thread_state(ao, WASAPI_THREAD_SHUTDOWN);
- /* wait up to 10 seconds */
if (state->hAudioThread &&
- WaitForSingleObject(state->hAudioThread, 10000) == WAIT_TIMEOUT)
+ WaitForSingleObject(state->hAudioThread, INFINITE) != WAIT_OBJECT_0)
{
- MP_ERR(ao, "Audio loop thread refuses to abort\n");
- return;
+ MP_ERR(ao, "Unexpected return value from WaitForSingleObject "
+ "while waiting for audio thread to terminate\n");
}
SAFE_RELEASE(state->hInitDone, CloseHandle(state->hInitDone));
SAFE_RELEASE(state->hWake, CloseHandle(state->hWake));
SAFE_RELEASE(state->hAudioThread,CloseHandle(state->hAudioThread));
+ wasapi_change_uninit(ao);
+
+ talloc_free(state->deviceID);
+
CoUninitialize();
MP_DBG(ao, "Uninit wasapi done\n");
}
@@ -254,114 +275,176 @@ static int init(struct ao *ao)
struct wasapi_state *state = ao->priv;
state->log = ao->log;
+ state->deviceID = wasapi_find_deviceID(ao);
+ if (!state->deviceID) {
+ uninit(ao);
+ return -1;
+ }
+
+ wasapi_change_init(ao, false);
+
state->hInitDone = CreateEventW(NULL, FALSE, FALSE, NULL);
state->hWake = CreateEventW(NULL, FALSE, FALSE, NULL);
if (!state->hInitDone || !state->hWake) {
- MP_ERR(ao, "Error creating events\n");
+ MP_FATAL(ao, "Error creating events\n");
uninit(ao);
return -1;
}
+ state->dispatch = mp_dispatch_create(state);
+ mp_dispatch_set_wakeup_fn(state->dispatch, thread_wakeup, ao);
+
state->init_ret = E_FAIL;
state->hAudioThread = CreateThread(NULL, 0, &AudioThread, ao, 0, NULL);
if (!state->hAudioThread) {
- MP_ERR(ao, "Failed to create audio thread\n");
+ MP_FATAL(ao, "Failed to create audio thread\n");
uninit(ao);
return -1;
}
- WaitForSingleObject(state->hInitDone, INFINITE); /* wait on init complete */
+ WaitForSingleObject(state->hInitDone, INFINITE); // wait on init complete
SAFE_RELEASE(state->hInitDone,CloseHandle(state->hInitDone));
- if (state->init_ret != S_OK) {
+ if (FAILED(state->init_ret)) {
if (!ao->probing)
- MP_ERR(ao, "Received failure from audio thread\n");
+ MP_FATAL(ao, "Received failure from audio thread\n");
uninit(ao);
return -1;
}
- wasapi_setup_proxies(state);
MP_DBG(ao, "Init wasapi done\n");
return 0;
}
-static int control(struct ao *ao, enum aocontrol cmd, void *arg)
+static int thread_control_exclusive(struct ao *ao, enum aocontrol cmd, void *arg)
{
struct wasapi_state *state = ao->priv;
- ao_control_vol_t *vol = arg;
- BOOL mute;
+ if (!state->pEndpointVolume)
+ return CONTROL_UNKNOWN;
switch (cmd) {
case AOCONTROL_GET_VOLUME:
- if (state->opt_exclusive)
- IAudioEndpointVolume_GetMasterVolumeLevelScalar(state->pEndpointVolumeProxy,
- &state->audio_volume);
- else
- ISimpleAudioVolume_GetMasterVolume(state->pAudioVolumeProxy,
- &state->audio_volume);
-
- /* check to see if user manually changed volume through mixer;
- this information is used in exclusive mode for restoring the mixer volume on uninit */
- if (state->audio_volume != state->previous_volume) {
- MP_VERBOSE(state, "Mixer difference: %.2g now, expected %.2g\n",
- state->audio_volume, state->previous_volume);
- state->initial_volume = state->audio_volume;
- }
+ case AOCONTROL_SET_VOLUME:
+ if (!(state->vol_hw_support & ENDPOINT_HARDWARE_SUPPORT_VOLUME))
+ return CONTROL_FALSE;
+ break;
+ case AOCONTROL_GET_MUTE:
+ case AOCONTROL_SET_MUTE:
+ if (!(state->vol_hw_support & ENDPOINT_HARDWARE_SUPPORT_MUTE))
+ return CONTROL_FALSE;
+ break;
+ case AOCONTROL_HAS_PER_APP_VOLUME:
+ return CONTROL_FALSE;
+ }
- vol->left = vol->right = 100.0f * state->audio_volume;
+ float volume;
+ BOOL mute;
+ switch (cmd) {
+ case AOCONTROL_GET_VOLUME:
+ IAudioEndpointVolume_GetMasterVolumeLevelScalar(
+ state->pEndpointVolume, &volume);
+ *(ao_control_vol_t *)arg = (ao_control_vol_t){
+ .left = 100.0f * volume,
+ .right = 100.0f * volume,
+ };
return CONTROL_OK;
case AOCONTROL_SET_VOLUME:
- state->audio_volume = vol->left / 100.f;
- if (state->opt_exclusive)
- IAudioEndpointVolume_SetMasterVolumeLevelScalar(state->pEndpointVolumeProxy,
- state->audio_volume, NULL);
- else
- ISimpleAudioVolume_SetMasterVolume(state->pAudioVolumeProxy,
- state->audio_volume, NULL);
-
- state->previous_volume = state->audio_volume;
+ volume = ((ao_control_vol_t *)arg)->left / 100.f;
+ IAudioEndpointVolume_SetMasterVolumeLevelScalar(
+ state->pEndpointVolume, volume, NULL);
return CONTROL_OK;
case AOCONTROL_GET_MUTE:
- if (state->opt_exclusive)
- IAudioEndpointVolume_GetMute(state->pEndpointVolumeProxy, &mute);
- else
- ISimpleAudioVolume_GetMute(state->pAudioVolumeProxy, &mute);
- *(bool*)arg = mute;
-
+ IAudioEndpointVolume_GetMute(state->pEndpointVolume, &mute);
+ *(bool *)arg = mute;
return CONTROL_OK;
case AOCONTROL_SET_MUTE:
- mute = *(bool*)arg;
- if (state->opt_exclusive)
- IAudioEndpointVolume_SetMute(state->pEndpointVolumeProxy, mute, NULL);
- else
- ISimpleAudioVolume_SetMute(state->pAudioVolumeProxy, mute, NULL);
+ mute = *(bool *)arg;
+ IAudioEndpointVolume_SetMute(state->pEndpointVolume, mute, NULL);
+ return CONTROL_OK;
+ }
+ return CONTROL_UNKNOWN;
+}
+static int thread_control_shared(struct ao *ao, enum aocontrol cmd, void *arg)
+{
+ struct wasapi_state *state = ao->priv;
+ if (!state->pAudioVolume)
+ return CONTROL_UNKNOWN;
+
+ float volume;
+ BOOL mute;
+ switch(cmd) {
+ case AOCONTROL_GET_VOLUME:
+ ISimpleAudioVolume_GetMasterVolume(state->pAudioVolume, &volume);
+ *(ao_control_vol_t *)arg = (ao_control_vol_t){
+ .left = 100.0f * volume,
+ .right = 100.0f * volume,
+ };
+ return CONTROL_OK;
+ case AOCONTROL_SET_VOLUME:
+ volume = ((ao_control_vol_t *)arg)->left / 100.f;
+ ISimpleAudioVolume_SetMasterVolume(state->pAudioVolume, volume, NULL);
+ return CONTROL_OK;
+ case AOCONTROL_GET_MUTE:
+ ISimpleAudioVolume_GetMute(state->pAudioVolume, &mute);
+ *(bool *)arg = mute;
+ return CONTROL_OK;
+ case AOCONTROL_SET_MUTE:
+ mute = *(bool *)arg;
+ ISimpleAudioVolume_SetMute(state->pAudioVolume, mute, NULL);
return CONTROL_OK;
case AOCONTROL_HAS_PER_APP_VOLUME:
- return state->share_mode == AUDCLNT_SHAREMODE_SHARED ?
- CONTROL_TRUE : CONTROL_FALSE;
- case AOCONTROL_UPDATE_STREAM_TITLE: {
- MP_VERBOSE(state, "Updating stream title to \"%s\"\n", (char*)arg);
- wchar_t *title = mp_from_utf8(NULL, (char*)arg);
+ return CONTROL_TRUE;
+ }
+ return CONTROL_UNKNOWN;
+}
- wchar_t *tmp = NULL;
+static int thread_control(struct ao *ao, enum aocontrol cmd, void *arg)
+{
+ struct wasapi_state *state = ao->priv;
- /* There is a weird race condition in the IAudioSessionControl itself --
- it seems that *sometimes* the SetDisplayName does not take effect and it still shows
- the old title. Use this loop to insist until it works. */
+ // common to exclusive and shared
+ switch (cmd) {
+ case AOCONTROL_UPDATE_STREAM_TITLE:
+ if (!state->pSessionControl)
+ return CONTROL_FALSE;
+
+ wchar_t *title = mp_from_utf8(NULL, (char*)arg);
+ wchar_t *tmp = NULL;
+ // There is a weird race condition in the IAudioSessionControl itself --
+ // it seems that *sometimes* the SetDisplayName does not take effect and
+ // it still shows the old title. Use this loop to insist until it works.
do {
- IAudioSessionControl_SetDisplayName(state->pSessionControlProxy, title, NULL);
+ IAudioSessionControl_SetDisplayName(state->pSessionControl, title, NULL);
SAFE_RELEASE(tmp, CoTaskMemFree(tmp));
- IAudioSessionControl_GetDisplayName(state->pSessionControlProxy, &tmp);
+ IAudioSessionControl_GetDisplayName(state->pSessionControl, &tmp);
} while (lstrcmpW(title, tmp));
SAFE_RELEASE(tmp, CoTaskMemFree(tmp));
talloc_free(title);
-
return CONTROL_OK;
}
- default:
- return CONTROL_UNKNOWN;
- }
+
+ return state->share_mode == AUDCLNT_SHAREMODE_EXCLUSIVE ?
+ thread_control_exclusive(ao, cmd, arg) :
+ thread_control_shared(ao, cmd, arg);
+}
+
+static void run_control(void *p)
+{
+ void **pp = p;
+ struct ao *ao = pp[0];
+ enum aocontrol cmd = *(enum aocontrol *)pp[1];
+ void *arg = pp[2];
+ *(int *)pp[3] = thread_control(ao, cmd, arg);
+}
+
+static int control(struct ao *ao, enum aocontrol cmd, void *arg)
+{
+ struct wasapi_state *state = ao->priv;
+ int ret;
+ void *p[] = {ao, &cmd, arg, &ret};
+ mp_dispatch_run(state->dispatch, run_control, p);
+ return ret;
}
static void audio_reset(struct ao *ao)
@@ -377,9 +460,7 @@ static void audio_resume(struct ao *ao)
static void hotplug_uninit(struct ao *ao)
{
MP_DBG(ao, "Hotplug uninit\n");
- struct wasapi_state *state = ao->priv;
wasapi_change_uninit(ao);
- SAFE_RELEASE(state->pEnumerator, IMMDeviceEnumerator_Release(state->pEnumerator));
CoUninitialize();
}
@@ -389,15 +470,12 @@ static int hotplug_init(struct ao *ao)
struct wasapi_state *state = ao->priv;
state->log = ao->log;
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
- HRESULT hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL,
- &IID_IMMDeviceEnumerator, (void **)&state->pEnumerator);
- EXIT_ON_ERROR(hr);
- hr = wasapi_change_init(ao, true);
+ HRESULT hr = wasapi_change_init(ao, true);
EXIT_ON_ERROR(hr);
return 0;
exit_label:
- MP_ERR(state, "Error setting up audio hotplug: %s\n", mp_HRESULT_to_str(hr));
+ MP_FATAL(state, "Error setting up audio hotplug: %s\n", mp_HRESULT_to_str(hr));
hotplug_uninit(ao);
return -1;
}
diff --git a/audio/out/ao_wasapi.h b/audio/out/ao_wasapi.h
index 2fbd2d5..3ae5015 100755..100644
--- a/audio/out/ao_wasapi.h
+++ b/audio/out/ao_wasapi.h
@@ -3,34 +3,41 @@
*
* Original author: Jonathan Yong <10walls@gmail.com>
*
- * 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 MP_AO_WASAPI_H_
#define MP_AO_WASAPI_H_
+#include <stdlib.h>
#include <stdbool.h>
+#include <windows.h>
+#include <mmdeviceapi.h>
#include <audioclient.h>
#include <audiopolicy.h>
-#include <mmdeviceapi.h>
-#include <avrt.h>
+#include <endpointvolume.h>
+#include "common/msg.h"
#include "osdep/atomics.h"
+#include "osdep/windows_utils.h"
+#include "internal.h"
+#include "ao.h"
typedef struct change_notify {
- IMMNotificationClient client; /* this must be first in the structure! */
- LPWSTR monitored; /* Monitored device */
+ IMMNotificationClient client; // this must be first in the structure!
+ IMMDeviceEnumerator *pEnumerator; // object where client is registered
+ LPWSTR monitored; // Monitored device
bool is_hotplug;
struct ao *ao;
} change_notify;
@@ -52,64 +59,62 @@ enum wasapi_thread_state {
typedef struct wasapi_state {
struct mp_log *log;
- /* Init phase */
- HRESULT init_ret;
- HANDLE hInitDone;
- int share_mode;
-
- /* volume control */
- DWORD vol_hw_support;
- float audio_volume;
- float previous_volume;
- float initial_volume;
-
- /* WASAPI handles, owned by audio thread */
+
+ // Thread handles
+ HRESULT init_ret; // status of init phase
+ HANDLE hInitDone; // set when init is complete in audio thread
+ HANDLE hAudioThread; // the audio thread itself
+ HANDLE hWake; // thread wakeup event
+ atomic_int thread_state; // enum wasapi_thread_state (what to do on wakeup)
+ struct mp_dispatch_queue *dispatch; // for volume/mute/session display
+
+ // for setting the audio thread priority
+ HANDLE hTask;
+
+ // ID of the device to use
+ LPWSTR deviceID;
+ // WASAPI object handles owned and used by audio thread
IMMDevice *pDevice;
IAudioClient *pAudioClient;
IAudioRenderClient *pRenderClient;
- ISimpleAudioVolume *pAudioVolume;
- IAudioEndpointVolume *pEndpointVolume;
- IAudioSessionControl *pSessionControl;
- IMMDeviceEnumerator *pEnumerator;
-
- /* thread handles */
- HANDLE hAudioThread; /* the thread itself */
- HANDLE hWake; /* thread wakeup event */
- atomic_int thread_state; /* enum wasapi_thread_state */
-
- /* for setting the audio thread priority */
- HANDLE hTask; /* AV thread */
-
- /* WASAPI proxy handles, for Single-Threaded Apartment communication.
- One is needed for each audio thread object that's accessed from the main thread. */
- ISimpleAudioVolume *pAudioVolumeProxy;
- IAudioEndpointVolume *pEndpointVolumeProxy;
- IAudioSessionControl *pSessionControlProxy;
-
- /* Streams used to marshal the proxy objects. The thread owning the actual objects
- needs to marshal proxy objects into these streams, and the thread that wants the
- proxies unmarshals them from here. */
- IStream *sAudioVolume;
- IStream *sEndpointVolume;
- IStream *sSessionControl;
-
- /* WASAPI internal clock information, for estimating delay */
+
+ // WASAPI internal clock information, for estimating delay
IAudioClock *pAudioClock;
- UINT64 clock_frequency; /* scale for the "samples" returned by the clock */
- atomic_ullong sample_count; /* the amount of samples per channel written to a GetBuffer buffer */
- LARGE_INTEGER qpc_frequency; /* frequency of windows' high resolution timer */
+ atomic_ullong sample_count; // samples per channel written by GetBuffer
+ UINT64 clock_frequency; // scale for position returned by GetPosition
+ LARGE_INTEGER qpc_frequency; // frequency of Windows' high resolution timer
- /* ao options */
+ // WASAPI control
+ IAudioSessionControl *pSessionControl; // setting the stream title
+ IAudioEndpointVolume *pEndpointVolume; // exclusive mode volume/mute
+ ISimpleAudioVolume *pAudioVolume; // shared mode volume/mute
+ DWORD vol_hw_support; // is hardware volume supported for exclusive-mode?
+
+ // ao options
int opt_exclusive;
int opt_list;
char *opt_device;
- /* format info */
+ // format info
WAVEFORMATEXTENSIBLE format;
- size_t buffer_block_size; /* Size of each block in bytes */
- UINT32 bufferFrameCount; /* wasapi buffer block size, number of frames, frame size at format.nBlockAlign */
+ AUDCLNT_SHAREMODE share_mode; // AUDCLNT_SHAREMODE_EXCLUSIVE / SHARED
+ UINT32 bufferFrameCount; // number of frames in buffer
change_notify change;
} wasapi_state;
+char *mp_PKEY_to_str_buf(char *buf, size_t buf_size, const PROPERTYKEY *pkey);
+#define mp_PKEY_to_str(pkey) mp_PKEY_to_str_buf((char[42]){0}, 42, (pkey))
+
+void wasapi_list_devs(struct ao *ao, struct ao_device_list *list);
+bstr wasapi_get_specified_device_string(struct ao *ao);
+LPWSTR wasapi_find_deviceID(struct ao *ao);
+
+void wasapi_dispatch(struct ao *ao);
+HRESULT wasapi_thread_init(struct ao *ao);
+void wasapi_thread_uninit(struct ao *ao);
+
+void wasapi_receive_proxies(wasapi_state *state);
+void wasapi_release_proxies(wasapi_state *state);
+
#endif
diff --git a/audio/out/ao_wasapi_changenotify.c b/audio/out/ao_wasapi_changenotify.c
index 1521578..661e957 100755..100644
--- a/audio/out/ao_wasapi_changenotify.c
+++ b/audio/out/ao_wasapi_changenotify.c
@@ -3,34 +3,28 @@
*
* Original author: Jonathan Yong <10walls@gmail.com>
*
- * 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 <initguid.h>
-#include <audioclient.h>
-#include <endpointvolume.h>
-#include <mmdeviceapi.h>
#include <wchar.h>
-#include <stdlib.h>
#include "ao_wasapi.h"
-#include "ao_wasapi_utils.h"
static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_QueryInterface(
IMMNotificationClient* This, REFIID riid, void **ppvObject)
{
- /* Compatible with IMMNotificationClient and IUnknown */
+ // Compatible with IMMNotificationClient and IUnknown
if (IsEqualGUID(&IID_IMMNotificationClient, riid) ||
IsEqualGUID(&IID_IUnknown, riid))
{
@@ -42,14 +36,14 @@ static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_QueryInterface(
}
}
-/* these are required, but not actually used */
+// these are required, but not actually used
static ULONG STDMETHODCALLTYPE sIMMNotificationClient_AddRef(
IMMNotificationClient *This)
{
return 1;
}
-/* MSDN says it should free itself, but we're static */
+// MSDN says it should free itself, but we're static
static ULONG STDMETHODCALLTYPE sIMMNotificationClient_Release(
IMMNotificationClient *This)
{
@@ -65,7 +59,8 @@ static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_OnDeviceStateChanged(
struct ao *ao = change->ao;
if (change->is_hotplug) {
- MP_VERBOSE(ao, "OnDeviceStateChanged triggered: sending hotplug event\n");
+ MP_VERBOSE(ao,
+ "OnDeviceStateChanged triggered: sending hotplug event\n");
ao_hotplug_event(ao);
} else if (pwstrDeviceId && !wcscmp(pwstrDeviceId, change->monitored)) {
switch (dwNewState) {
@@ -99,7 +94,7 @@ static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_OnDeviceAdded(
return S_OK;
}
-/* maybe MPV can go over to the prefered device once it is plugged in? */
+// maybe MPV can go over to the prefered device once it is plugged in?
static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_OnDeviceRemoved(
IMMNotificationClient *This,
LPCWSTR pwstrDeviceId)
@@ -127,31 +122,33 @@ static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_OnDefaultDeviceChanged(
{
change_notify *change = (change_notify *)This;
struct ao *ao = change->ao;
- struct wasapi_state *state = ao->priv;
- /* don't care about "eCapture" or non-"eMultimedia" roles */
+ // don't care about "eCapture" or non-"eMultimedia" roles
if (flow == eCapture || role != eMultimedia) return S_OK;
if (change->is_hotplug) {
- MP_VERBOSE(ao, "OnDefaultDeviceChanged triggered: sending hotplug event\n");
+ MP_VERBOSE(ao,
+ "OnDefaultDeviceChanged triggered: sending hotplug event\n");
ao_hotplug_event(ao);
} else {
- /* stay on the device the user specified */
- if (state->opt_device) {
+ // stay on the device the user specified
+ bstr device = wasapi_get_specified_device_string(ao);
+ if (device.len) {
MP_VERBOSE(ao, "OnDefaultDeviceChanged triggered: "
- "staying on specified device %s\n", state->opt_device);
+ "staying on specified device %.*s\n", BSTR_P(device));
return S_OK;
}
- /* don't reload if already on the new default */
+ // don't reload if already on the new default
if (pwstrDeviceId && !wcscmp(pwstrDeviceId, change->monitored)) {
MP_VERBOSE(ao, "OnDefaultDeviceChanged triggered: "
"already using default device, no reload required\n");
return S_OK;
}
- /* if we got here, we need to reload */
- MP_VERBOSE(ao, "OnDefaultDeviceChanged triggered: requesting ao reload\n");
+ // if we got here, we need to reload
+ MP_VERBOSE(ao,
+ "OnDefaultDeviceChanged triggered: requesting ao reload\n");
ao_request_reload(ao);
}
@@ -184,7 +181,7 @@ static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_OnPropertyValueChanged(
return S_OK;
}
-static CONST_VTBL IMMNotificationClientVtbl sIMMDeviceEnumeratorVtbl_vtbl = {
+static CONST_VTBL IMMNotificationClientVtbl sIMMNotificationClientVtbl = {
.QueryInterface = sIMMNotificationClient_QueryInterface,
.AddRef = sIMMNotificationClient_AddRef,
.Release = sIMMNotificationClient_Release,
@@ -200,27 +197,30 @@ HRESULT wasapi_change_init(struct ao *ao, bool is_hotplug)
{
struct wasapi_state *state = ao->priv;
struct change_notify *change = &state->change;
- HRESULT hr;
- /* COM voodoo to emulate c++ class */
- change->client.lpVtbl = &sIMMDeviceEnumeratorVtbl_vtbl;
+ HRESULT hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL,
+ &IID_IMMDeviceEnumerator,
+ (void **)&change->pEnumerator);
+ EXIT_ON_ERROR(hr);
+
+ // COM voodoo to emulate c++ class
+ change->client.lpVtbl = &sIMMNotificationClientVtbl;
- /* register the change notification client */
+ // register the change notification client
hr = IMMDeviceEnumerator_RegisterEndpointNotificationCallback(
- state->pEnumerator, (IMMNotificationClient *)change);
+ change->pEnumerator, (IMMNotificationClient *)change);
EXIT_ON_ERROR(hr);
- /* so the callbacks can access the ao */
+ // so the callbacks can access the ao
change->ao = ao;
- /* whether or not this is the hotplug instance */
+ // whether or not this is the hotplug instance
change->is_hotplug = is_hotplug;
if (is_hotplug) {
MP_DBG(ao, "Monitoring for hotplug events\n");
} else {
- /* Get the device string to compare with the pwstrDeviceId */
- hr = IMMDevice_GetId(state->pDevice, &change->monitored);
- EXIT_ON_ERROR(hr);
+ // Get the device string to compare with the pwstrDeviceId
+ change->monitored = state->deviceID;
MP_VERBOSE(ao, "Monitoring changes in device %S\n", change->monitored);
}
@@ -237,10 +237,10 @@ void wasapi_change_uninit(struct ao *ao)
struct wasapi_state *state = ao->priv;
struct change_notify *change = &state->change;
- if (state->pEnumerator && change->client.lpVtbl) {
+ if (change->pEnumerator && change->client.lpVtbl) {
IMMDeviceEnumerator_UnregisterEndpointNotificationCallback(
- state->pEnumerator, (IMMNotificationClient *)change);
+ change->pEnumerator, (IMMNotificationClient *)change);
}
- if (change->monitored) CoTaskMemFree(change->monitored);
+ SAFE_RELEASE(change->pEnumerator, IMMDeviceEnumerator_Release(change->pEnumerator));
}
diff --git a/audio/out/ao_wasapi_utils.c b/audio/out/ao_wasapi_utils.c
index be81f0e..8d0ea30 100755..100644
--- a/audio/out/ao_wasapi_utils.c
+++ b/audio/out/ao_wasapi_utils.c
@@ -3,36 +3,34 @@
*
* Original author: Jonathan Yong <10walls@gmail.com>
*
- * 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 <math.h>
-#include <libavutil/common.h>
+#include <wchar.h>
#include <windows.h>
+#include <initguid.h>
#include <errors.h>
#include <ksguid.h>
#include <ksmedia.h>
-#include <audioclient.h>
-#include <endpointvolume.h>
-#include <mmdeviceapi.h>
#include <avrt.h>
-#include "audio/out/ao_wasapi_utils.h"
-
#include "audio/format.h"
-#include "osdep/io.h"
#include "osdep/timer.h"
+#include "osdep/io.h"
+#include "osdep/strnlen.h"
+#include "ao_wasapi.h"
#define MIXER_DEFAULT_LABEL L"mpv - video player"
@@ -97,8 +95,8 @@ static const GUID *format_to_subtype(int format)
return &KSDATAFORMAT_SUBTYPE_PCM;
}
-// "solve" the under-determined inverse of format_to_subtype by
-// assuming the input subtype is "special" (i.e. IEC61937)
+// "solve" the under-determined inverse of format_to_subtype by assuming the
+// input subtype is "special" (i.e. IEC61937)
static int special_subtype_to_format(const GUID *subtype) {
for (int i = 0; wasapi_fmt_table[i].format; i++) {
if (IsEqualGUID(subtype, wasapi_fmt_table[i].subtype))
@@ -107,84 +105,15 @@ static int special_subtype_to_format(const GUID *subtype) {
return 0;
}
-char *mp_GUID_to_str_buf(char *buf, size_t buf_size, const GUID *guid)
-{
- snprintf(buf, buf_size,
- "{%8.8x-%4.4x-%4.4x-%2.2x%2.2x-%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x}",
- (unsigned) guid->Data1, guid->Data2, guid->Data3,
- guid->Data4[0], guid->Data4[1],
- guid->Data4[2], guid->Data4[3],
- guid->Data4[4], guid->Data4[5],
- guid->Data4[6], guid->Data4[7]);
- return buf;
-}
-
char *mp_PKEY_to_str_buf(char *buf, size_t buf_size, const PROPERTYKEY *pkey)
{
buf = mp_GUID_to_str_buf(buf, buf_size, &pkey->fmtid);
size_t guid_len = strnlen(buf, buf_size);
- snprintf(buf + guid_len, buf_size - guid_len, ",%"PRIu32, (uint32_t) pkey->pid);
+ snprintf(buf + guid_len, buf_size - guid_len, ",%"PRIu32,
+ (uint32_t) pkey->pid);
return buf;
}
-static char *wasapi_explain_err(const HRESULT hr)
-{
-#define E(x) case x : return # x ;
- switch (hr) {
- E(S_OK)
- E(S_FALSE)
- E(E_FAIL)
- E(E_OUTOFMEMORY)
- E(E_POINTER)
- E(E_HANDLE)
- E(E_NOTIMPL)
- E(E_INVALIDARG)
- E(E_PROP_ID_UNSUPPORTED)
- E(REGDB_E_IIDNOTREG)
- E(CO_E_NOTINITIALIZED)
- E(AUDCLNT_E_NOT_INITIALIZED)
- E(AUDCLNT_E_ALREADY_INITIALIZED)
- E(AUDCLNT_E_WRONG_ENDPOINT_TYPE)
- E(AUDCLNT_E_DEVICE_INVALIDATED)
- E(AUDCLNT_E_NOT_STOPPED)
- E(AUDCLNT_E_BUFFER_TOO_LARGE)
- E(AUDCLNT_E_OUT_OF_ORDER)
- E(AUDCLNT_E_UNSUPPORTED_FORMAT)
- E(AUDCLNT_E_INVALID_SIZE)
- E(AUDCLNT_E_DEVICE_IN_USE)
- E(AUDCLNT_E_BUFFER_OPERATION_PENDING)
- E(AUDCLNT_E_THREAD_NOT_REGISTERED)
- E(AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED)
- E(AUDCLNT_E_ENDPOINT_CREATE_FAILED)
- E(AUDCLNT_E_SERVICE_NOT_RUNNING)
- E(AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED)
- E(AUDCLNT_E_EXCLUSIVE_MODE_ONLY)
- E(AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL)
- E(AUDCLNT_E_EVENTHANDLE_NOT_SET)
- E(AUDCLNT_E_INCORRECT_BUFFER_SIZE)
- E(AUDCLNT_E_BUFFER_SIZE_ERROR)
- E(AUDCLNT_E_CPUUSAGE_EXCEEDED)
- E(AUDCLNT_E_BUFFER_ERROR)
- E(AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED)
- E(AUDCLNT_E_INVALID_DEVICE_PERIOD)
- E(AUDCLNT_E_INVALID_STREAM_FLAG)
- E(AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE)
- E(AUDCLNT_E_RESOURCES_INVALIDATED)
- E(AUDCLNT_S_BUFFER_EMPTY)
- E(AUDCLNT_S_THREAD_ALREADY_REGISTERED)
- E(AUDCLNT_S_POSITION_STALLED)
- default:
- return "<Unknown>";
- }
-#undef E
-}
-
-char *mp_HRESULT_to_str_buf(char *buf, size_t buf_size, HRESULT hr)
-{
- snprintf(buf, buf_size, "%s (0x%"PRIx32")",
- wasapi_explain_err(hr), (uint32_t) hr);
- return buf;
-}
static void update_waveformat_datarate(WAVEFORMATEXTENSIBLE *wformat)
{
WAVEFORMATEX *wf = &wformat->Format;
@@ -203,15 +132,15 @@ static void set_waveformat(WAVEFORMATEXTENSIBLE *wformat,
wformat->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
wformat->SubFormat = *format_to_subtype(format);
- wformat->Samples.wValidBitsPerSample = valid_bits ? valid_bits : wformat->Format.wBitsPerSample;
+ wformat->Samples.wValidBitsPerSample =
+ valid_bits ? valid_bits : wformat->Format.wBitsPerSample;
wformat->dwChannelMask = mp_chmap_to_waveext(channels);
update_waveformat_datarate(wformat);
}
-// This implicitly transforms all pcm formats to:
-// interleaved / signed (except 8-bit is unsigned) / waveext channel order.
-// "Special" formats should be exempt as they should already
-// satisfy these properties.
+// This implicitly transforms all pcm formats to: interleaved / signed (except
+// 8-bit is unsigned) / waveext channel order. "Special" formats should be
+// exempt as they should already satisfy these properties.
static void set_waveformat_with_ao(WAVEFORMATEXTENSIBLE *wformat, struct ao *ao)
{
struct mp_chmap channels = ao->channels;
@@ -256,7 +185,8 @@ static int format_from_waveformat(WAVEFORMATEX *wf)
WAVEFORMATEXTENSIBLE *wformat = (WAVEFORMATEXTENSIBLE *)wf;
if (IsEqualGUID(&wformat->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM)) {
format = wf->wBitsPerSample == 8 ? AF_FORMAT_U8 : AF_FORMAT_S32;
- } else if (IsEqualGUID(&wformat->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) {
+ } else if (IsEqualGUID(&wformat->SubFormat,
+ &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) {
format = AF_FORMAT_FLOAT;
} else {
format = special_subtype_to_format(&wformat->SubFormat);
@@ -274,16 +204,17 @@ static int format_from_waveformat(WAVEFORMATEX *wf)
}
// https://msdn.microsoft.com/en-us/library/windows/hardware/ff538802%28v=vs.85%29.aspx:
// Since mpv doesn't have the notion of "valid bits", we just specify a
- // format with the container size. The least significant, "invalid"
- // bits will be excess precision ignored by wasapi.
- // The change_bytes operations should be a no-op for properly
- // configured "special" formats, otherwise it will return 0.
+ // format with the container size. The least significant, "invalid" bits
+ // will be excess precision ignored by wasapi. The change_bytes operations
+ // should be a no-op for properly configured "special" formats, otherwise it
+ // will return 0.
if (wf->wBitsPerSample % 8)
return 0;
return af_fmt_change_bytes(format, wf->wBitsPerSample / 8);
}
-static bool chmap_from_waveformat(struct mp_chmap *channels, const WAVEFORMATEX *wf)
+static bool chmap_from_waveformat(struct mp_chmap *channels,
+ const WAVEFORMATEX *wf)
{
if (wf->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
WAVEFORMATEXTENSIBLE *wformat = (WAVEFORMATEXTENSIBLE *)wf;
@@ -311,7 +242,8 @@ static char *waveformat_to_str_buf(char *buf, size_t buf_size, WAVEFORMATEX *wf)
snprintf(validstr, sizeof(validstr), " (%u valid)", valid_bits);
snprintf(buf, buf_size, "%s %s%s @ %uhz",
- mp_chmap_to_str(&channels), af_fmt_to_str(format_from_waveformat(wf)),
+ mp_chmap_to_str(&channels),
+ af_fmt_to_str(format_from_waveformat(wf)),
validstr, (unsigned) wf->nSamplesPerSec);
return buf;
}
@@ -326,7 +258,8 @@ static void waveformat_copy(WAVEFORMATEXTENSIBLE* dst, WAVEFORMATEX* src)
}
}
-static bool set_ao_format(struct ao *ao, WAVEFORMATEX *wf, AUDCLNT_SHAREMODE share_mode)
+static bool set_ao_format(struct ao *ao, WAVEFORMATEX *wf,
+ AUDCLNT_SHAREMODE share_mode)
{
struct wasapi_state *state = ao->priv;
int format = format_from_waveformat(wf);
@@ -336,7 +269,8 @@ static bool set_ao_format(struct ao *ao, WAVEFORMATEX *wf, AUDCLNT_SHAREMODE sha
return false;
}
- // Do not touch the ao for passthrough, just assume that we set WAVEFORMATEX correctly.
+ // Do not touch the ao for passthrough, just assume that we set WAVEFORMATEX
+ // correctly.
if (af_fmt_is_pcm(format)) {
struct mp_chmap channels;
if (!chmap_from_waveformat(&channels, wf)) {
@@ -356,14 +290,15 @@ static bool set_ao_format(struct ao *ao, WAVEFORMATEX *wf, AUDCLNT_SHAREMODE sha
static bool try_format_exclusive(struct ao *ao, WAVEFORMATEXTENSIBLE *wformat)
{
struct wasapi_state *state = ao->priv;
- MP_VERBOSE(ao, "Trying %s (exclusive)\n", waveformat_to_str(&wformat->Format));
+ MP_VERBOSE(ao, "Trying %s (exclusive)\n",
+ waveformat_to_str(&wformat->Format));
HRESULT hr = IAudioClient_IsFormatSupported(state->pAudioClient,
AUDCLNT_SHAREMODE_EXCLUSIVE,
&wformat->Format, NULL);
if (hr != AUDCLNT_E_UNSUPPORTED_FORMAT)
EXIT_ON_ERROR(hr);
- return hr == S_OK;
+ return SUCCEEDED(hr);
exit_label:
MP_ERR(state, "Error testing exclusive format: %s\n", mp_HRESULT_to_str(hr));
return false;
@@ -469,7 +404,8 @@ static bool search_channels(struct ao *ao, WAVEFORMATEXTENSIBLE *wformat)
if (!wformat->Format.nSamplesPerSec) {
if (search_samplerates(ao, wformat, &entry)) {
mp_chmap_sel_add_map(&chmap_sel, &entry);
- MP_VERBOSE(ao, "%s is supported\n", waveformat_to_str(&wformat->Format));
+ MP_VERBOSE(ao, "%s is supported\n",
+ waveformat_to_str(&wformat->Format));
}
} else {
change_waveformat_channels(wformat, &entry);
@@ -493,8 +429,8 @@ static bool find_formats_exclusive(struct ao *ao, bool do_search)
WAVEFORMATEXTENSIBLE wformat;
set_waveformat_with_ao(&wformat, ao);
- // Try the requested format as is. If that doesn't work, and the
- // do_search argument is set, do the pcm format search.
+ // Try the requested format as is. If that doesn't work, and the do_search
+ // argument is set, do the pcm format search.
if (!try_format_exclusive_with_spdif_fallback(ao, &wformat) &&
(!do_search || !search_channels(ao, &wformat)))
return false;
@@ -549,7 +485,8 @@ static bool find_formats_shared(struct ao *ao)
af_fmt_to_str(ao->format), ao->samplerate);
return true;
exit_label:
- MP_ERR(state, "Error finding shared mode format: %s\n", mp_HRESULT_to_str(hr));
+ MP_ERR(state, "Error finding shared mode format: %s\n",
+ mp_HRESULT_to_str(hr));
return false;
}
@@ -586,7 +523,8 @@ static HRESULT init_clock(struct wasapi_state *state) {
atomic_store(&state->sample_count, 0);
- MP_VERBOSE(state, "IAudioClock::GetFrequency gave a frequency of %"PRIu64".\n",
+ MP_VERBOSE(state,
+ "IAudioClock::GetFrequency gave a frequency of %"PRIu64".\n",
(uint64_t) state->clock_frequency);
return S_OK;
@@ -596,27 +534,66 @@ exit_label:
return hr;
}
-static HRESULT init_session_display(struct wasapi_state *state) {
- wchar_t path[MAX_PATH+12] = {0};
-
+static void init_session_display(struct wasapi_state *state) {
HRESULT hr = IAudioClient_GetService(state->pAudioClient,
&IID_IAudioSessionControl,
(void **)&state->pSessionControl);
EXIT_ON_ERROR(hr);
+ wchar_t path[MAX_PATH+12] = {0};
GetModuleFileNameW(NULL, path, MAX_PATH);
lstrcatW(path, L",-IDI_ICON1");
-
- hr = IAudioSessionControl_SetDisplayName(state->pSessionControl, MIXER_DEFAULT_LABEL, NULL);
- EXIT_ON_ERROR(hr);
hr = IAudioSessionControl_SetIconPath(state->pSessionControl, path, NULL);
- EXIT_ON_ERROR(hr);
+ if (FAILED(hr)) {
+ // don't goto exit_label here since SetDisplayName might still work
+ MP_WARN(state, "Error setting audio session icon: %s\n",
+ mp_HRESULT_to_str(hr));
+ }
- return S_OK;
+ hr = IAudioSessionControl_SetDisplayName(state->pSessionControl,
+ MIXER_DEFAULT_LABEL, NULL);
+ EXIT_ON_ERROR(hr);
+ return;
exit_label:
+ // if we got here then the session control is useless - release it
+ SAFE_RELEASE(state->pSessionControl,
+ IAudioSessionControl_Release(state->pSessionControl));
MP_WARN(state, "Error setting audio session display name: %s\n",
mp_HRESULT_to_str(hr));
- return S_OK; // No reason to abort initialization.
+ return;
+}
+
+static void init_volume_control(struct wasapi_state *state)
+{
+ HRESULT hr;
+ if (state->share_mode == AUDCLNT_SHAREMODE_EXCLUSIVE) {
+ MP_DBG(state, "Activating pEndpointVolume interface\n");
+ hr = IMMDeviceActivator_Activate(state->pDevice,
+ &IID_IAudioEndpointVolume,
+ CLSCTX_ALL, NULL,
+ (void **)&state->pEndpointVolume);
+ EXIT_ON_ERROR(hr);
+
+ MP_DBG(state, "IAudioEndpointVolume::QueryHardwareSupport\n");
+ hr = IAudioEndpointVolume_QueryHardwareSupport(state->pEndpointVolume,
+ &state->vol_hw_support);
+ EXIT_ON_ERROR(hr);
+ } else {
+ MP_DBG(state, "IAudioClient::Initialize pAudioVolume\n");
+ hr = IAudioClient_GetService(state->pAudioClient,
+ &IID_ISimpleAudioVolume,
+ (void **)&state->pAudioVolume);
+ EXIT_ON_ERROR(hr);
+ }
+ return;
+exit_label:
+ state->vol_hw_support = 0;
+ SAFE_RELEASE(state->pEndpointVolume,
+ IAudioEndpointVolume_Release(state->pEndpointVolume));
+ SAFE_RELEASE(state->pAudioVolume,
+ ISimpleAudioVolume_Release(state->pAudioVolume));
+ MP_WARN(state, "Error setting up volume control: %s\n",
+ mp_HRESULT_to_str(hr));
}
static HRESULT fix_format(struct ao *ao)
@@ -625,20 +602,25 @@ static HRESULT fix_format(struct ao *ao)
REFERENCE_TIME devicePeriod, bufferDuration, bufferPeriod;
MP_DBG(state, "IAudioClient::GetDevicePeriod\n");
- HRESULT hr = IAudioClient_GetDevicePeriod(state->pAudioClient,&devicePeriod, NULL);
- MP_VERBOSE(state, "Device period: %.2g ms\n", (double) devicePeriod / 10000.0 );
-
- /* integer multiple of device period close to 50ms */
- bufferPeriod = bufferDuration = ceil(50.0 * 10000.0 / devicePeriod) * devicePeriod;
+ HRESULT hr = IAudioClient_GetDevicePeriod(state->pAudioClient,&devicePeriod,
+ NULL);
+ MP_VERBOSE(state, "Device period: %.2g ms\n",
+ (double) devicePeriod / 10000.0 );
+
+ if (state->share_mode == AUDCLNT_SHAREMODE_SHARED) {
+ // for shared mode, use integer multiple of device period close to 50ms
+ bufferDuration = devicePeriod * ceil(50.0 * 10000.0 / devicePeriod);
+ bufferPeriod = 0;
+ } else {
+ // in exclusive mode, these should all be the same
+ bufferPeriod = bufferDuration = devicePeriod;
+ }
- /* 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 */
+ // 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
int retries=0;
reinit:
- if (state->share_mode == AUDCLNT_SHAREMODE_SHARED)
- bufferPeriod = 0;
-
MP_DBG(state, "IAudioClient::Initialize\n");
hr = IAudioClient_Initialize(state->pAudioClient,
state->share_mode,
@@ -647,20 +629,25 @@ reinit:
bufferPeriod,
&(state->format.Format),
NULL);
- /* something about buffer sizes on Win7 */
+ // something about buffer sizes on Win7
if (hr == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED) {
if (retries > 0) {
EXIT_ON_ERROR(hr);
} else {
retries ++;
}
- MP_VERBOSE(state, "IAudioClient::Initialize negotiation failed with %s, used %lld * 100ns\n",
+ MP_VERBOSE(state, "IAudioClient::Initialize negotiation failed with %s,"
+ "used %lld * 100ns\n",
mp_HRESULT_to_str(hr), bufferDuration);
- IAudioClient_GetBufferSize(state->pAudioClient, &state->bufferFrameCount);
- bufferPeriod = bufferDuration =
- (REFERENCE_TIME) ((10000.0 * 1000 / state->format.Format.nSamplesPerSec *
- state->bufferFrameCount) + 0.5);
+ IAudioClient_GetBufferSize(state->pAudioClient,
+ &state->bufferFrameCount);
+ bufferDuration = (REFERENCE_TIME) (0.5 +
+ (10000.0 * 1000 / state->format.Format.nSamplesPerSec
+ * state->bufferFrameCount));
+ if (state->share_mode == AUDCLNT_SHAREMODE_EXCLUSIVE)
+ bufferPeriod = bufferDuration;
+
IAudioClient_Release(state->pAudioClient);
state->pAudioClient = NULL;
@@ -677,12 +664,6 @@ reinit:
(void **)&state->pRenderClient);
EXIT_ON_ERROR(hr);
- MP_DBG(state, "IAudioClient::Initialize pAudioVolume\n");
- hr = IAudioClient_GetService(state->pAudioClient,
- &IID_ISimpleAudioVolume,
- (void **)&state->pAudioVolume);
- EXIT_ON_ERROR(hr);
-
MP_DBG(state, "IAudioClient::Initialize IAudioClient_SetEventHandle\n");
hr = IAudioClient_SetEventHandle(state->pAudioClient, state->hWake);
EXIT_ON_ERROR(hr);
@@ -693,20 +674,17 @@ reinit:
EXIT_ON_ERROR(hr);
ao->device_buffer = state->bufferFrameCount;
- state->buffer_block_size = state->format.Format.nChannels *
- state->format.Format.wBitsPerSample / 8 *
- state->bufferFrameCount;
- bufferDuration =
- (REFERENCE_TIME) ((10000.0 * 1000 / state->format.Format.nSamplesPerSec *
- state->bufferFrameCount) + 0.5);
+ bufferDuration = (REFERENCE_TIME) (0.5 +
+ (10000.0 * 1000 / state->format.Format.nSamplesPerSec
+ * state->bufferFrameCount));
MP_VERBOSE(state, "Buffer frame count: %"PRIu32" (%.2g ms)\n",
state->bufferFrameCount, (double) bufferDuration / 10000.0 );
hr = init_clock(state);
EXIT_ON_ERROR(hr);
- hr = init_session_display(state);
- EXIT_ON_ERROR(hr);
+ init_session_display(state);
+ init_volume_control(state);
state->hTask = AvSetMmThreadCharacteristics(L"Pro Audio", &(DWORD){0});
if (!state->hTask) {
@@ -714,341 +692,253 @@ reinit:
mp_LastError_to_str());
}
- MP_VERBOSE(state, "Format fixed. Using %lld byte buffer block size\n",
- (long long) state->buffer_block_size);
-
return S_OK;
exit_label:
MP_ERR(state, "Error initializing device: %s\n", mp_HRESULT_to_str(hr));
return hr;
}
-static char* get_device_id(IMMDevice *pDevice) {
- if (!pDevice)
- return NULL;
-
- LPWSTR devid = NULL;
- char *idstr = NULL;
-
- HRESULT hr = IMMDevice_GetId(pDevice, &devid);
- EXIT_ON_ERROR(hr);
-
- idstr = mp_to_utf8(NULL, devid);
-
- if (strstr(idstr, "{0.0.0.00000000}.")) {
- char *stripped = talloc_strdup(NULL, idstr + strlen("{0.0.0.00000000}."));
- talloc_free(idstr);
- idstr = stripped;
- }
-
-exit_label:
- SAFE_RELEASE(devid, CoTaskMemFree(devid));
- return idstr;
-}
-
-static char* get_device_name(IMMDevice *pDevice) {
- if (!pDevice)
- return NULL;
+struct device_desc {
+ LPWSTR deviceID;
+ char *id;
+ char *name;
+};
- IPropertyStore *pProps = NULL;
+static char* get_device_name(struct mp_log *l, void *talloc_ctx, IMMDevice *pDevice)
+{
char *namestr = NULL;
+ IPropertyStore *pProps = NULL;
+ PROPVARIANT devname;
+ PropVariantInit(&devname);
HRESULT hr = IMMDevice_OpenPropertyStore(pDevice, STGM_READ, &pProps);
EXIT_ON_ERROR(hr);
- PROPVARIANT devname;
- PropVariantInit(&devname);
-
- hr = IPropertyStore_GetValue(pProps, &mp_PKEY_Device_FriendlyName, &devname);
+ hr = IPropertyStore_GetValue(pProps, &mp_PKEY_Device_FriendlyName,
+ &devname);
EXIT_ON_ERROR(hr);
- namestr = mp_to_utf8(NULL, devname.pwszVal);
+ namestr = mp_to_utf8(talloc_ctx, devname.pwszVal);
exit_label:
+ if (FAILED(hr))
+ mp_warn(l, "Failed getting device name: %s\n", mp_HRESULT_to_str(hr));
PropVariantClear(&devname);
SAFE_RELEASE(pProps, IPropertyStore_Release(pProps));
- return namestr;
+ return namestr ? namestr : talloc_strdup(talloc_ctx, "");
}
-static char* get_device_desc(IMMDevice *pDevice) {
- if (!pDevice)
+static struct device_desc *get_device_desc(struct mp_log *l, IMMDevice *pDevice)
+{
+ LPWSTR deviceID;
+ HRESULT hr = IMMDevice_GetId(pDevice, &deviceID);
+ if (FAILED(hr)) {
+ mp_err(l, "Failed getting device id: %s\n", mp_HRESULT_to_str(hr));
return NULL;
+ }
+ struct device_desc *d = talloc_zero(NULL, struct device_desc);
+ d->deviceID = talloc_memdup(d, deviceID,
+ (wcslen(deviceID) + 1) * sizeof(wchar_t));
+ SAFE_RELEASE(deviceID, CoTaskMemFree(deviceID));
- IPropertyStore *pProps = NULL;
- char *desc = NULL;
+ char *full_id = mp_to_utf8(NULL, d->deviceID);
+ bstr id = bstr0(full_id);
+ bstr_eatstart0(&id, "{0.0.0.00000000}.");
+ d->id = bstrdup0(d, id);
+ talloc_free(full_id);
- HRESULT hr = IMMDevice_OpenPropertyStore(pDevice, STGM_READ, &pProps);
- EXIT_ON_ERROR(hr);
+ d->name = get_device_name(l, d, pDevice);
+ return d;
+}
- PROPVARIANT devdesc;
- PropVariantInit(&devdesc);
+struct enumerator {
+ struct mp_log *log;
+ IMMDeviceEnumerator *pEnumerator;
+ IMMDeviceCollection *pDevices;
+ UINT count;
+};
+
+static void destroy_enumerator(struct enumerator *e)
+{
+ if (!e)
+ return;
+ SAFE_RELEASE(e->pDevices, IMMDeviceCollection_Release(e->pDevices));
+ SAFE_RELEASE(e->pEnumerator, IMMDeviceEnumerator_Release(e->pEnumerator));
+ talloc_free(e);
+}
- hr = IPropertyStore_GetValue(pProps, &mp_PKEY_Device_DeviceDesc, &devdesc);
+static struct enumerator *create_enumerator(struct mp_log *log)
+{
+ struct enumerator *e = talloc_zero(NULL, struct enumerator);
+ e->log = log;
+ HRESULT hr = CoCreateInstance(
+ &CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &IID_IMMDeviceEnumerator,
+ (void **)&e->pEnumerator);
+ EXIT_ON_ERROR(hr);
+
+ hr = IMMDeviceEnumerator_EnumAudioEndpoints(
+ e->pEnumerator, eRender, DEVICE_STATE_ACTIVE, &e->pDevices);
EXIT_ON_ERROR(hr);
- desc = mp_to_utf8(NULL, devdesc.pwszVal);
+ hr = IMMDeviceCollection_GetCount(e->pDevices, &e->count);
+ EXIT_ON_ERROR(hr);
+ return e;
exit_label:
- PropVariantClear(&devdesc);
- SAFE_RELEASE(pProps, IPropertyStore_Release(pProps));
- return desc;
+ mp_err(log, "Error getting device enumerator: %s\n", mp_HRESULT_to_str(hr));
+ destroy_enumerator(e);
+ return NULL;
}
-// frees *idstr
-static int device_id_match(char *idstr, char *candidate) {
- if (idstr == NULL || candidate == NULL)
- return 0;
-
- int found = 0;
-#define FOUND(x) do { found = (x); goto end; } while(0)
- if (strcmp(idstr, candidate) == 0)
- FOUND(1);
- if (strstr(idstr, "{0.0.0.00000000}.")) {
- char *start = idstr + strlen("{0.0.0.00000000}.");
- if (strcmp(start, candidate) == 0)
- FOUND(1);
+static struct device_desc *device_desc_for_num(struct enumerator *e, UINT i)
+{
+ IMMDevice *pDevice = NULL;
+ HRESULT hr = IMMDeviceCollection_Item(e->pDevices, i, &pDevice);
+ if (FAILED(hr)) {
+ MP_ERR(e, "Failed getting device #%d: %s\n", i, mp_HRESULT_to_str(hr));
+ return NULL;
}
-#undef FOUND
-end:
- talloc_free(idstr);
- return found;
+ struct device_desc *d = get_device_desc(e->log, pDevice);
+ SAFE_RELEASE(pDevice, IMMDevice_Release(pDevice));
+ return d;
}
-void wasapi_list_devs(struct ao *ao, struct ao_device_list *list)
+static struct device_desc *default_device_desc(struct enumerator *e)
{
- struct wasapi_state *state = ao->priv;
- IMMDeviceCollection *pDevices = NULL;
IMMDevice *pDevice = NULL;
- char *name = NULL, *id = NULL;
-
- HRESULT hr = IMMDeviceEnumerator_EnumAudioEndpoints(state->pEnumerator, eRender,
- DEVICE_STATE_ACTIVE, &pDevices);
- EXIT_ON_ERROR(hr);
-
- int count;
- hr = IMMDeviceCollection_GetCount(pDevices, &count);
- EXIT_ON_ERROR(hr);
- if (count > 0)
- MP_VERBOSE(ao, "Output devices:\n");
-
- for (int i = 0; i < count; i++) {
- hr = IMMDeviceCollection_Item(pDevices, i, &pDevice);
- EXIT_ON_ERROR(hr);
-
- name = get_device_name(pDevice);
- id = get_device_id(pDevice);
- if (!id) {
- hr = E_FAIL;
- EXIT_ON_ERROR(hr);
- }
- char *safe_name = name ? name : "";
- ao_device_list_add(list, ao, &(struct ao_device_desc){id, safe_name});
+ HRESULT hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(
+ e->pEnumerator, eRender, eMultimedia, &pDevice);
+ if (FAILED(hr)) {
+ MP_ERR(e, "Error from GetDefaultAudioEndpoint: %s\n",
+ mp_HRESULT_to_str(hr));
+ return NULL;
+ }
+ struct device_desc *d = get_device_desc(e->log, pDevice);
+ SAFE_RELEASE(pDevice, IMMDevice_Release(pDevice));
+ return d;
+}
- MP_VERBOSE(ao, "#%d, GUID: \'%s\', name: \'%s\'\n", i, id, safe_name);
+void wasapi_list_devs(struct ao *ao, struct ao_device_list *list)
+{
+ struct enumerator *enumerator = create_enumerator(ao->log);
+ if (!enumerator)
+ return;
- talloc_free(name);
- talloc_free(id);
- SAFE_RELEASE(pDevice, IMMDevice_Release(pDevice));
+ for (UINT i = 0; i < enumerator->count; i++) {
+ struct device_desc *d = device_desc_for_num(enumerator, i);
+ if (!d)
+ goto exit_label;
+ ao_device_list_add(list, ao, &(struct ao_device_desc){d->id, d->name});
+ talloc_free(d);
}
- SAFE_RELEASE(pDevices, IMMDeviceCollection_Release(pDevices));
- return;
exit_label:
- MP_ERR(ao, "Error enumerating devices: %s\n", mp_HRESULT_to_str(hr));
- talloc_free(name);
- talloc_free(id);
- SAFE_RELEASE(pDevice, IMMDevice_Release(pDevice));
- SAFE_RELEASE(pDevices, IMMDeviceCollection_Release(pDevices));
+ destroy_enumerator(enumerator);
}
-static HRESULT load_default_device(struct ao *ao, IMMDeviceEnumerator* pEnumerator,
- IMMDevice **ppDevice)
+static HRESULT load_device(struct mp_log *l,
+ IMMDevice **ppDevice, LPWSTR deviceID)
{
- HRESULT hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(pEnumerator,
- eRender, eMultimedia,
- ppDevice);
+ IMMDeviceEnumerator *pEnumerator = NULL;
+ HRESULT hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL,
+ &IID_IMMDeviceEnumerator,
+ (void **)&pEnumerator);
EXIT_ON_ERROR(hr);
- char *id = get_device_id(*ppDevice);
- MP_VERBOSE(ao, "Default device ID: %s\n", id);
- talloc_free(id);
+ hr = IMMDeviceEnumerator_GetDevice(pEnumerator, deviceID, ppDevice);
+ EXIT_ON_ERROR(hr);
- return S_OK;
exit_label:
- MP_ERR(ao , "Error loading default device: %s\n", mp_HRESULT_to_str(hr));
+ if (FAILED(hr))
+ mp_err(l, "Error loading selected device: %s\n", mp_HRESULT_to_str(hr));
+ SAFE_RELEASE(pEnumerator, IMMDeviceEnumerator_Release(pEnumerator));
return hr;
}
-static HRESULT find_and_load_device(struct ao *ao, IMMDeviceEnumerator* pEnumerator,
- IMMDevice **ppDevice, char *search)
+static LPWSTR select_device(struct mp_log *l, struct device_desc *d)
{
- HRESULT hr;
- IMMDeviceCollection *pDevices = NULL;
- IMMDevice *pTempDevice = NULL;
- LPWSTR deviceID = NULL;
-
- char *end;
- int devno = strtol(search, &end, 10);
-
- char *devid = NULL;
- if (end == search || *end)
- devid = search;
-
- int search_err = 0;
-
- if (devid == NULL) {
- hr = IMMDeviceEnumerator_EnumAudioEndpoints(pEnumerator, eRender,
- DEVICE_STATE_ACTIVE, &pDevices);
- EXIT_ON_ERROR(hr);
-
- int count;
- IMMDeviceCollection_GetCount(pDevices, &count);
-
- if (devno >= count) {
- MP_ERR(ao, "No device #%d\n", devno);
- } else {
- MP_VERBOSE(ao, "Finding device #%d\n", devno);
- hr = IMMDeviceCollection_Item(pDevices, devno, &pTempDevice);
- EXIT_ON_ERROR(hr);
+ if (!d)
+ return NULL;
+ mp_verbose(l, "Selecting device \'%s\' (%s)\n", d->id, d->name);
+ return talloc_memdup(NULL, d->deviceID,
+ (wcslen(d->deviceID) + 1) * sizeof(wchar_t));
+}
- hr = IMMDevice_GetId(pTempDevice, &deviceID);
- EXIT_ON_ERROR(hr);
+bstr wasapi_get_specified_device_string(struct ao *ao)
+{
+ struct wasapi_state *state = ao->priv;
+ bstr device = bstr_strip(bstr0(state->opt_device));
+ if (!device.len)
+ device = bstr_strip(bstr0(ao->device));
+ return device;
+}
- MP_VERBOSE(ao, "Found device #%d\n", devno);
+LPWSTR wasapi_find_deviceID(struct ao *ao)
+{
+ LPWSTR deviceID = NULL;
+ bstr device = wasapi_get_specified_device_string(ao);
+ MP_DBG(ao, "Find device \'%.*s\'\n", BSTR_P(device));
+
+ struct device_desc *d = NULL;
+ struct enumerator *enumerator = create_enumerator(ao->log);
+ if (!enumerator)
+ goto exit_label;
+
+ if (!enumerator->count) {
+ MP_ERR(ao, "There are no playback devices available\n");
+ goto exit_label;
+ }
+
+ if (!device.len) {
+ MP_VERBOSE(ao, "No device specified. Selecting default.\n");
+ d = default_device_desc(enumerator);
+ deviceID = select_device(ao->log, d);
+ goto exit_label;
+ }
+
+ // try selecting by number
+ bstr rest;
+ long long devno = bstrtoll(device, &rest, 10);
+ if (!rest.len && 0 <= devno && devno < (long long)enumerator->count) {
+ MP_VERBOSE(ao, "Selecting device by number: #%lld\n", devno);
+ d = device_desc_for_num(enumerator, devno);
+ deviceID = select_device(ao->log, d);
+ goto exit_label;
+ }
+
+ // select by id or name
+ bstr_eatstart0(&device, "{0.0.0.00000000}.");
+ for (UINT i = 0; i < enumerator->count; i++) {
+ d = device_desc_for_num(enumerator, i);
+ if (!d)
+ goto exit_label;
+
+ if (bstrcmp(device, bstr_strip(bstr0(d->id))) == 0) {
+ MP_VERBOSE(ao, "Selecting device by id: \'%.*s\'\n", BSTR_P(device));
+ deviceID = select_device(ao->log, d);
+ goto exit_label;
}
- } else {
- hr = IMMDeviceEnumerator_EnumAudioEndpoints(pEnumerator, eRender,
- DEVICE_STATE_ACTIVE|DEVICE_STATE_UNPLUGGED,
- &pDevices);
- EXIT_ON_ERROR(hr);
-
- int count;
- IMMDeviceCollection_GetCount(pDevices, &count);
-
- MP_VERBOSE(ao, "Finding device %s\n", devid);
-
- IMMDevice *prevDevice = NULL;
- for (int i = 0; i < count; i++) {
- hr = IMMDeviceCollection_Item(pDevices, i, &pTempDevice);
- EXIT_ON_ERROR(hr);
-
- if (device_id_match(get_device_id(pTempDevice), devid)) {
- hr = IMMDevice_GetId(pTempDevice, &deviceID);
- EXIT_ON_ERROR(hr);
- break;
- }
- char *desc = get_device_desc(pTempDevice);
- if (strstr(desc, devid)) {
- if (deviceID) {
- char *name;
- if (!search_err) {
- MP_ERR(ao, "Multiple matching devices found\n");
- name = get_device_name(prevDevice);
- MP_ERR(ao, "%s\n", name);
- talloc_free(name);
- search_err = 1;
- }
- name = get_device_name(pTempDevice);
- MP_ERR(ao, "%s\n", name);
- talloc_free(name);
- }
- hr = IMMDevice_GetId(pTempDevice, &deviceID);
- prevDevice = pTempDevice;
+ if (bstrcmp(device, bstr_strip(bstr0(d->name))) == 0) {
+ if (!deviceID) {
+ MP_VERBOSE(ao, "Selecting device by name: \'%.*s\'\n", BSTR_P(device));
+ deviceID = select_device(ao->log, d);
+ } else {
+ MP_WARN(ao, "Multiple devices matched \'%.*s\'."
+ "Ignoring device \'%s\' (%s).\n",
+ BSTR_P(device), d->id, d->name);
}
- talloc_free(desc);
-
- SAFE_RELEASE(pTempDevice, IMMDevice_Release(pTempDevice));
}
-
- if (deviceID == NULL)
- MP_ERR(ao, "Could not find device %s\n", devid);
+ SAFE_RELEASE(d, talloc_free(d));
}
- SAFE_RELEASE(pTempDevice, IMMDevice_Release(pTempDevice));
- SAFE_RELEASE(pDevices, IMMDeviceCollection_Release(pDevices));
-
- if (deviceID == NULL || search_err) {
- hr = E_NOTFOUND;
- } else {
- MP_VERBOSE(ao, "Loading device %S\n", deviceID);
-
- hr = IMMDeviceEnumerator_GetDevice(pEnumerator, deviceID, ppDevice);
-
- if (FAILED(hr))
- MP_ERR(ao, "Could not load requested device\n");
- }
+ if (!deviceID)
+ MP_ERR(ao, "Failed to find device \'%.*s\'\n", BSTR_P(device));
exit_label:
- SAFE_RELEASE(pTempDevice, IMMDevice_Release(pTempDevice));
- SAFE_RELEASE(pDevices, IMMDeviceCollection_Release(pDevices));
-
- CoTaskMemFree(deviceID);
- return hr;
-}
-
-HRESULT wasapi_setup_proxies(struct wasapi_state *state) {
- HRESULT hr;
-
-#define UNMARSHAL(type, to, from) do { \
- hr = CoGetInterfaceAndReleaseStream((from), &(type), (void **)&(to)); \
- (from) = NULL; \
- EXIT_ON_ERROR(hr); \
-} while (0)
-
- UNMARSHAL(IID_ISimpleAudioVolume, state->pAudioVolumeProxy, state->sAudioVolume);
- UNMARSHAL(IID_IAudioEndpointVolume, state->pEndpointVolumeProxy, state->sEndpointVolume);
- UNMARSHAL(IID_IAudioSessionControl, state->pSessionControlProxy, state->sSessionControl);
-
-#undef UNMARSHAL
-
- return S_OK;
-exit_label:
- MP_ERR(state, "Error reading COM proxy: %s\n", mp_HRESULT_to_str(hr));
- return hr;
-}
-
-void wasapi_release_proxies(wasapi_state *state) {
- SAFE_RELEASE(state->pAudioVolumeProxy, IUnknown_Release(state->pAudioVolumeProxy));
- SAFE_RELEASE(state->pEndpointVolumeProxy, IUnknown_Release(state->pEndpointVolumeProxy));
- SAFE_RELEASE(state->pSessionControlProxy, IUnknown_Release(state->pSessionControlProxy));
-}
-
-static HRESULT create_proxies(struct wasapi_state *state) {
- HRESULT hr;
-
-#define MARSHAL(type, to, from) do { \
- hr = CreateStreamOnHGlobal(NULL, TRUE, &(to)); \
- EXIT_ON_ERROR(hr); \
- hr = CoMarshalInterThreadInterfaceInStream(&(type), \
- (IUnknown *)(from), \
- &(to)); \
- EXIT_ON_ERROR(hr); \
-} while (0)
-
- MARSHAL(IID_ISimpleAudioVolume, state->sAudioVolume, state->pAudioVolume);
- MARSHAL(IID_IAudioEndpointVolume, state->sEndpointVolume, state->pEndpointVolume);
- MARSHAL(IID_IAudioSessionControl, state->sSessionControl, state->pSessionControl);
-
- return S_OK;
-exit_label:
- MP_ERR(state, "Error creating COM proxy: %s\n", mp_HRESULT_to_str(hr));
- return hr;
-}
-
-static void destroy_proxies(struct wasapi_state *state) {
- SAFE_RELEASE(state->sAudioVolume, IUnknown_Release(state->sAudioVolume));
- SAFE_RELEASE(state->sEndpointVolume, IUnknown_Release(state->sEndpointVolume));
- SAFE_RELEASE(state->sSessionControl, IUnknown_Release(state->sSessionControl));
-}
-
-void wasapi_dispatch(struct ao *ao)
-{
- MP_DBG(ao, "Dispatch\n");
- /* dispatch any possible pending messages */
- MSG msg;
- while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
- DispatchMessage(&msg);
+ talloc_free(d);
+ destroy_enumerator(enumerator);
+ return deviceID;
}
HRESULT wasapi_thread_init(struct ao *ao)
@@ -1056,48 +946,16 @@ HRESULT wasapi_thread_init(struct ao *ao)
struct wasapi_state *state = ao->priv;
MP_DBG(ao, "Init wasapi thread\n");
int64_t retry_wait = 1;
-retry:
- state->initial_volume = -1.0;
-
- HRESULT hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL,
- &IID_IMMDeviceEnumerator, (void **)&state->pEnumerator);
+retry: ;
+ HRESULT hr = load_device(ao->log, &state->pDevice, state->deviceID);
EXIT_ON_ERROR(hr);
- char *device = state->opt_device;
- if (!device || !device[0])
- device = ao->device;
-
-
- if (!device || !device[0]) {
- hr = load_default_device(ao, state->pEnumerator, &state->pDevice);
- } else {
- hr = find_and_load_device(ao, state->pEnumerator, &state->pDevice, device);
- }
- EXIT_ON_ERROR(hr);
-
- char *name = get_device_name(state->pDevice);
- MP_VERBOSE(ao, "Device loaded: %s\n", name);
- talloc_free(name);
-
MP_DBG(ao, "Activating pAudioClient interface\n");
hr = IMMDeviceActivator_Activate(state->pDevice, &IID_IAudioClient,
- CLSCTX_ALL, NULL, (void **)&state->pAudioClient);
- EXIT_ON_ERROR(hr);
-
- MP_DBG(ao, "Activating pEndpointVolume interface\n");
- hr = IMMDeviceActivator_Activate(state->pDevice, &IID_IAudioEndpointVolume,
CLSCTX_ALL, NULL,
- (void **)&state->pEndpointVolume);
+ (void **)&state->pAudioClient);
EXIT_ON_ERROR(hr);
- MP_DBG(ao, "Query hardware volume support\n");
- hr = IAudioEndpointVolume_QueryHardwareSupport(state->pEndpointVolume,
- &state->vol_hw_support);
- if (hr != S_OK) {
- MP_WARN(ao, "Error querying hardware volume control: %s\n",
- mp_HRESULT_to_str(hr));
- }
-
MP_DBG(ao, "Probing formats\n");
if (!find_formats(ao)) {
hr = E_FAIL;
@@ -1117,26 +975,10 @@ retry:
}
EXIT_ON_ERROR(hr);
- MP_DBG(ao, "Creating proxies\n");
- hr = create_proxies(state);
- EXIT_ON_ERROR(hr);
-
- MP_DBG(ao, "Read volume levels\n");
- if (state->opt_exclusive) {
- IAudioEndpointVolume_GetMasterVolumeLevelScalar(state->pEndpointVolume,
- &state->initial_volume);
- } else {
- ISimpleAudioVolume_GetMasterVolume(state->pAudioVolume,
- &state->initial_volume);
- }
- state->previous_volume = state->initial_volume;
-
- wasapi_change_init(ao, false);
-
MP_DBG(ao, "Init wasapi thread done\n");
return S_OK;
exit_label:
- MP_ERR(state, "Error setting up audio thread: %s\n", mp_HRESULT_to_str(hr));
+ MP_FATAL(state, "Error setting up audio thread: %s\n", mp_HRESULT_to_str(hr));
return hr;
}
@@ -1144,19 +986,10 @@ void wasapi_thread_uninit(struct ao *ao)
{
struct wasapi_state *state = ao->priv;
MP_DBG(ao, "Thread shutdown\n");
- wasapi_dispatch(ao);
if (state->pAudioClient)
IAudioClient_Stop(state->pAudioClient);
- wasapi_change_uninit(ao);
-
- if (state->opt_exclusive && state->pEndpointVolume && state->initial_volume > 0 ) {
- IAudioEndpointVolume_SetMasterVolumeLevelScalar(state->pEndpointVolume,
- state->initial_volume, NULL);
- }
- destroy_proxies(state);
-
SAFE_RELEASE(state->pRenderClient, IAudioRenderClient_Release(state->pRenderClient));
SAFE_RELEASE(state->pAudioClock, IAudioClock_Release(state->pAudioClock));
SAFE_RELEASE(state->pAudioVolume, ISimpleAudioVolume_Release(state->pAudioVolume));
@@ -1164,7 +997,6 @@ void wasapi_thread_uninit(struct ao *ao)
SAFE_RELEASE(state->pSessionControl, IAudioSessionControl_Release(state->pSessionControl));
SAFE_RELEASE(state->pAudioClient, IAudioClient_Release(state->pAudioClient));
SAFE_RELEASE(state->pDevice, IMMDevice_Release(state->pDevice));
- SAFE_RELEASE(state->pEnumerator, IMMDeviceEnumerator_Release(state->pEnumerator));
SAFE_RELEASE(state->hTask, AvRevertMmThreadCharacteristics(state->hTask));
MP_DBG(ao, "Thread uninit done\n");
}
diff --git a/audio/out/ao_wasapi_utils.h b/audio/out/ao_wasapi_utils.h
deleted file mode 100755
index deaffd7..0000000
--- a/audio/out/ao_wasapi_utils.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * This file is part of mpv.
- *
- * Original author: Jonathan Yong <10walls@gmail.com>
- *
- * mpv is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * mpv is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with mpv. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef MP_AO_WASAPI_UTILS_H_
-#define MP_AO_WASAPI_UTILS_H_
-
-#include "audio/out/ao_wasapi.h"
-
-#include "options/m_option.h"
-#include "common/msg.h"
-#include "ao.h"
-#include "internal.h"
-
-char *mp_GUID_to_str_buf(char *buf, size_t buf_size, const GUID *guid);
-char *mp_PKEY_to_str_buf(char *buf, size_t buf_size, const PROPERTYKEY *pkey);
-char *mp_HRESULT_to_str_buf(char *buf, size_t buf_size, HRESULT hr);
-#define mp_GUID_to_str(guid) mp_GUID_to_str_buf((char[40]){0}, 40, (guid))
-#define mp_PKEY_to_str(pkey) mp_PKEY_to_str_buf((char[42]){0}, 42, (pkey))
-#define mp_HRESULT_to_str(hr) mp_HRESULT_to_str_buf((char[60]){0}, 60, (hr))
-#define mp_LastError_to_str() mp_HRESULT_to_str(HRESULT_FROM_WIN32(GetLastError()))
-
-bool wasapi_fill_VistaBlob(wasapi_state *state);
-
-void wasapi_list_devs(struct ao *ao, struct ao_device_list *list);
-
-void wasapi_dispatch(struct ao *ao);
-HRESULT wasapi_thread_init(struct ao *ao);
-void wasapi_thread_uninit(struct ao *ao);
-
-HRESULT wasapi_setup_proxies(wasapi_state *state);
-void wasapi_release_proxies(wasapi_state *state);
-
-#endif
diff --git a/audio/out/pull.c b/audio/out/pull.c
index ed85c4e..8980580 100644
--- a/audio/out/pull.c
+++ b/audio/out/pull.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 <stddef.h>
@@ -113,7 +113,7 @@ static int play(struct ao *ao, void **data, int samples, int flags)
// rest of the user-provided buffer with silence.
// This basically assumes that the audio device doesn't care about underruns.
// If this is called in paused mode, it will always return 0.
-// The caller should set out_time_us to the expected delay the last sample
+// The caller should set out_time_us to the expected delay until the last sample
// reaches the speakers, in microseconds, using mp_time_us() as reference.
int ao_read_data(struct ao *ao, void **data, int samples, int64_t out_time_us)
{
diff --git a/audio/out/push.c b/audio/out/push.c
index 301004b..4fa2bc5 100644
--- a/audio/out/push.c
+++ b/audio/out/push.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 <stddef.h>
diff --git a/common/av_common.c b/common/av_common.c
index c1b1884..8b979ca 100644
--- a/common/av_common.c
+++ b/common/av_common.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 <assert.h>
diff --git a/common/av_common.h b/common/av_common.h
index 0b02609..e9df328 100644
--- a/common/av_common.h
+++ b/common/av_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 MP_AVCOMMON_H
diff --git a/common/av_log.c b/common/av_log.c
index 05542cd..7e0b271 100644
--- a/common/av_log.c
+++ b/common/av_log.c
@@ -36,15 +36,12 @@
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
+#include <libavfilter/avfilter.h>
#if HAVE_LIBAVDEVICE
#include <libavdevice/avdevice.h>
#endif
-#if HAVE_LIBAVFILTER
-#include <libavfilter/avfilter.h>
-#endif
-
#if HAVE_LIBAVRESAMPLE
#include <libavresample/avresample.h>
#endif
@@ -161,10 +158,8 @@ void init_libav(struct mpv_global *global)
avcodec_register_all();
av_register_all();
avformat_network_init();
-
-#if HAVE_LIBAVFILTER
avfilter_register_all();
-#endif
+
#if HAVE_LIBAVDEVICE
avdevice_register_all();
#endif
@@ -196,9 +191,7 @@ void print_libav_versions(struct mp_log *log, int v)
{"libavcodec", LIBAVCODEC_VERSION_INT, avcodec_version()},
{"libavformat", LIBAVFORMAT_VERSION_INT, avformat_version()},
{"libswscale", LIBSWSCALE_VERSION_INT, swscale_version()},
-#if HAVE_LIBAVFILTER
{"libavfilter", LIBAVFILTER_VERSION_INT, avfilter_version()},
-#endif
#if HAVE_LIBAVRESAMPLE
{"libavresample", LIBAVRESAMPLE_VERSION_INT, avresample_version()},
#endif
diff --git a/common/codecs.c b/common/codecs.c
index 35d2709..c0d99eb 100644
--- a/common/codecs.c
+++ b/common/codecs.c
@@ -1,22 +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 <assert.h>
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "misc/bstr.h"
#include "common/msg.h"
#include "codecs.h"
diff --git a/common/codecs.h b/common/codecs.h
index a262ed6..17316c8 100644
--- a/common/codecs.h
+++ b/common/codecs.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 MP_CODECS_H
diff --git a/common/common.c b/common/common.c
index d3dcb61..eead096 100644
--- a/common/common.c
+++ b/common/common.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 <stdarg.h>
@@ -21,9 +21,11 @@
#include <libavutil/common.h>
#include <libavutil/error.h>
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "misc/bstr.h"
+#include "misc/ctype.h"
#include "common/common.h"
+#include "osdep/strnlen.h"
#define appendf(ptr, ...) \
do {(*(ptr)) = talloc_asprintf_append_buffer(*(ptr), __VA_ARGS__);} while(0)
@@ -148,7 +150,7 @@ void mp_append_utf8_bstr(void *talloc_ctx, struct bstr *buf, uint32_t codepoint)
bstr_xappend(talloc_ctx, buf, (bstr){data, output - data});
}
-// Parse a C-style escape beginning at code, and append the result to *str
+// Parse a C/JSON-style escape beginning at code, and append the result to *str
// using talloc. The input string (*code) must point to the first character
// after the initial '\', and after parsing *code is set to the first character
// after the current escape.
@@ -161,6 +163,7 @@ static bool mp_parse_escape(void *talloc_ctx, bstr *dst, bstr *code)
switch (code->start[0]) {
case '"': replace = '"'; break;
case '\\': replace = '\\'; break;
+ case '/': replace = '/'; break;
case 'b': replace = '\b'; break;
case 'f': replace = '\f'; break;
case 'n': replace = '\n'; break;
@@ -185,9 +188,20 @@ static bool mp_parse_escape(void *talloc_ctx, bstr *dst, bstr *code)
}
if (code->start[0] == 'u' && code->len >= 5) {
bstr num = bstr_splice(*code, 1, 5);
- int c = bstrtoll(num, &num, 16);
+ uint32_t c = bstrtoll(num, &num, 16);
if (num.len)
return false;
+ if (c >= 0xd800 && c <= 0xdbff) {
+ if (code->len < 5 + 6 // udddd + \udddd
+ || code->start[5] != '\\' || code->start[6] != 'u')
+ return false;
+ *code = bstr_cut(*code, 5 + 1);
+ bstr num2 = bstr_splice(*code, 1, 5);
+ uint32_t c2 = bstrtoll(num2, &num2, 16);
+ if (num2.len || c2 < 0xdc00 || c2 > 0xdfff)
+ return false;
+ c = ((c - 0xd800) << 10) + 0x10000 + (c2 - 0xdc00);
+ }
mp_append_utf8_bstr(talloc_ctx, dst, c);
*code = bstr_cut(*code, 5);
return true;
@@ -257,3 +271,19 @@ char *mp_strerror_buf(char *buf, size_t buf_size, int errnum)
av_strerror(AVERROR(errnum), buf, buf_size);
return buf;
}
+
+char *mp_tag_str_buf(char *buf, size_t buf_size, uint32_t tag)
+{
+ if (buf_size < 1)
+ return buf;
+ buf[0] = '\0';
+ for (int n = 0; n < 4; n++) {
+ uint8_t val = (tag >> (n * 8)) & 0xFF;
+ if (mp_isalnum(val) || val == '_' || val == ' ') {
+ mp_snprintf_cat(buf, buf_size, "%c", val);
+ } else {
+ mp_snprintf_cat(buf, buf_size, "[%d]", val);
+ }
+ }
+ return buf;
+}
diff --git a/common/common.h b/common/common.h
index cc7093a..4b7da54 100644
--- a/common/common.h
+++ b/common/common.h
@@ -24,7 +24,7 @@
#include <stdint.h>
#include "osdep/compiler.h"
-#include "talloc.h"
+#include "mpv_talloc.h"
// double should be able to represent this exactly
#define MP_NOPTS_VALUE (-0x1p+63)
@@ -32,8 +32,6 @@
#define MP_CONCAT_(a, b) a ## b
#define MP_CONCAT(a, b) MP_CONCAT_(a, b)
-#define ROUND(x) ((int)((x) < 0 ? (x) - 0.5 : (x) + 0.5))
-
#define MPMAX(a, b) ((a) > (b) ? (a) : (b))
#define MPMIN(a, b) ((a) > (b) ? (b) : (a))
#define MPCLAMP(a, min, max) (((a) < (min)) ? (min) : (((a) > (max)) ? (max) : (a)))
@@ -59,6 +57,13 @@ enum stream_type {
STREAM_TYPE_COUNT,
};
+enum {
+ DATA_OK = 1,
+ DATA_WAIT = 0,
+ DATA_AGAIN = -1,
+ DATA_EOF = -2,
+};
+
extern const char *const mpv_version;
extern const char *const mpv_builddate;
@@ -92,4 +97,7 @@ bool mp_append_escaped_string(void *talloc_ctx, struct bstr *dst,
char *mp_strerror_buf(char *buf, size_t buf_size, int errnum);
#define mp_strerror(e) mp_strerror_buf((char[80]){0}, 80, e)
+char *mp_tag_str_buf(char *buf, size_t buf_size, uint32_t tag);
+#define mp_tag_str(t) mp_tag_str_buf((char[22]){0}, 22, t)
+
#endif /* MPLAYER_MPCOMMON_H */
diff --git a/common/encode_lavc.c b/common/encode_lavc.c
index 2a01bee..6dd47a3 100644
--- a/common/encode_lavc.c
+++ b/common/encode_lavc.c
@@ -29,7 +29,7 @@
#include "options/options.h"
#include "osdep/timer.h"
#include "video/out/vo.h"
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "stream/stream.h"
#define OPT_BASE_STRUCT struct encode_opts
diff --git a/common/global.h b/common/global.h
index 546c585..fb70b4f 100644
--- a/common/global.h
+++ b/common/global.h
@@ -7,6 +7,7 @@
struct mpv_global {
struct MPOpts *opts;
struct mp_log *log;
+ struct mp_client_api *client_api;
};
#endif
diff --git a/common/msg.c b/common/msg.c
index 4576754..01d0bb7 100644
--- a/common/msg.c
+++ b/common/msg.c
@@ -24,7 +24,7 @@
#include <pthread.h>
#include <stdint.h>
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "misc/bstr.h"
#include "osdep/atomics.h"
diff --git a/common/playlist.c b/common/playlist.c
index 9fd087b..4c003c5 100644
--- a/common/playlist.c
+++ b/common/playlist.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 <assert.h>
@@ -21,7 +21,7 @@
#include "common/common.h"
#include "common/global.h"
#include "common/msg.h"
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "options/path.h"
#include "demux/demux.h"
@@ -206,6 +206,18 @@ void playlist_add_base_path(struct playlist *pl, bstr base_path)
}
}
+// Add redirected_from as new redirect entry to each item in pl.
+void playlist_add_redirect(struct playlist *pl, const char *redirected_from)
+{
+ for (struct playlist_entry *e = pl->first; e; e = e->next) {
+ if (e->num_redirects >= 10) // arbitrary limit for sanity
+ break;
+ char *s = talloc_strdup(e, redirected_from);
+ if (s)
+ MP_TARRAY_APPEND(e, e->redirects, e->num_redirects, s);
+ }
+}
+
// Move all entries from source_pl to pl, appending them after the current entry
// of pl. source_pl will be empty, and all entries have changed ownership to pl.
void playlist_transfer_entries(struct playlist *pl, struct playlist *source_pl)
diff --git a/common/playlist.h b/common/playlist.h
index be9fd99..d35fff6 100644
--- a/common/playlist.h
+++ b/common/playlist.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_PLAYLIST_H
@@ -36,6 +36,11 @@ struct playlist_entry {
char *title;
+ // If the user plays a playlist, then the playlist's URL will be appended
+ // as redirect to each entry. (Same for directories etc.)
+ char **redirects;
+ int num_redirects;
+
// Set to true if playback didn't seem to work, or if the file could be
// played only for a very short time. This is used to make playlist
// navigation just work in case the user has unplayable files in the
@@ -88,6 +93,7 @@ void playlist_add_file(struct playlist *pl, const char *filename);
void playlist_shuffle(struct playlist *pl);
struct playlist_entry *playlist_get_next(struct playlist *pl, int direction);
void playlist_add_base_path(struct playlist *pl, bstr base_path);
+void playlist_add_redirect(struct playlist *pl, const char *redirected_from);
void playlist_transfer_entries(struct playlist *pl, struct playlist *source_pl);
void playlist_append_entries(struct playlist *pl, struct playlist *source_pl);
diff --git a/common/tags.c b/common/tags.c
index 3554344..29459d0 100644
--- a/common/tags.c
+++ b/common/tags.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 <stddef.h>
diff --git a/common/version.c b/common/version.c
index af110fa..b1d42b9 100644
--- a/common/version.c
+++ b/common/version.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 "common.h"
diff --git a/demux/codec_tags.c b/demux/codec_tags.c
index c7b48f6..5f4f569 100644
--- a/demux/codec_tags.c
+++ b/demux/codec_tags.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 <libavformat/avformat.h>
@@ -70,20 +70,20 @@ static const char *map_audio_pcm_tag(uint32_t tag, int bits)
}
}
-void mp_set_codec_from_tag(struct sh_stream *sh)
+void mp_set_codec_from_tag(struct mp_codec_params *c)
{
- sh->codec = lookup_tag(sh->type, sh->codec_tag);
+ c->codec = lookup_tag(c->type, c->codec_tag);
- if (sh->audio && sh->audio->bits_per_coded_sample) {
+ if (c->type == STREAM_AUDIO && c->bits_per_coded_sample) {
const char *codec =
- map_audio_pcm_tag(sh->codec_tag, sh->audio->bits_per_coded_sample);
+ map_audio_pcm_tag(c->codec_tag, c->bits_per_coded_sample);
if (codec)
- sh->codec = codec;
+ c->codec = codec;
}
}
-void mp_set_pcm_codec(struct sh_stream *sh, bool sign, bool is_float, int bits,
- bool is_be)
+void mp_set_pcm_codec(struct mp_codec_params *c, bool sign, bool is_float,
+ int bits, bool is_be)
{
// This uses libavcodec pcm codec names, e.g. "pcm_u16le".
char codec[64] = "pcm_";
@@ -95,7 +95,7 @@ void mp_set_pcm_codec(struct sh_stream *sh, bool sign, bool is_float, int bits,
mp_snprintf_cat(codec, sizeof(codec), "%d", bits);
if (bits != 8)
mp_snprintf_cat(codec, sizeof(codec), is_be ? "be" : "le");
- sh->codec = talloc_strdup(sh->audio, codec);
+ c->codec = talloc_strdup(c, codec);
}
static const char *const mimetype_to_codec[][2] = {
diff --git a/demux/codec_tags.h b/demux/codec_tags.h
index 3489f0d..147760b 100644
--- a/demux/codec_tags.h
+++ b/demux/codec_tags.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 MP_CODEC_TAGS_H
@@ -21,12 +21,12 @@
#include <stdint.h>
#include <stdbool.h>
-struct sh_stream;
+struct mp_codec_params;
-void mp_set_codec_from_tag(struct sh_stream *sh);
+void mp_set_codec_from_tag(struct mp_codec_params *c);
-void mp_set_pcm_codec(struct sh_stream *sh, bool sign, bool is_float, int bits,
- bool is_be);
+void mp_set_pcm_codec(struct mp_codec_params *c, bool sign, bool is_float,
+ int bits, bool is_be);
const char *mp_map_mimetype_to_video_codec(const char *mimetype);
diff --git a/demux/cue.c b/demux/cue.c
index d72e842..69f30f4 100644
--- a/demux/cue.c
+++ b/demux/cue.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>
@@ -20,7 +20,7 @@
#include <string.h>
#include <inttypes.h>
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "misc/bstr.h"
#include "common/common.h"
@@ -218,3 +218,18 @@ struct cue_file *mp_parse_cue(struct bstr data)
return f;
}
+
+int mp_check_embedded_cue(struct cue_file *f)
+{
+ char *fn0 = f->tracks[0].filename;
+ for (int n = 1; n < f->num_tracks; n++) {
+ char *fn = f->tracks[n].filename;
+ // both filenames have the same address (including NULL)
+ if (fn0 == fn)
+ continue;
+ // only one filename is NULL, or the strings don't match
+ if (!fn0 || !fn || strcmp(fn0, fn) != 0)
+ return -1;
+ }
+ return 0;
+}
diff --git a/demux/cue.h b/demux/cue.h
index cf4b4c1..61f18e6 100644
--- a/demux/cue.h
+++ b/demux/cue.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 MP_CUE_H_
@@ -38,5 +38,6 @@ struct cue_track {
bool mp_probe_cue(struct bstr data);
struct cue_file *mp_parse_cue(struct bstr data);
+int mp_check_embedded_cue(struct cue_file *f);
#endif
diff --git a/demux/demux.c b/demux/demux.c
index dcad692..05a8551 100644
--- a/demux/demux.c
+++ b/demux/demux.c
@@ -29,13 +29,14 @@
#include "config.h"
#include "options/options.h"
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "common/msg.h"
#include "common/global.h"
#include "osdep/threads.h"
#include "stream/stream.h"
#include "demux.h"
+#include "timeline.h"
#include "stheader.h"
#include "cue.h"
@@ -52,6 +53,7 @@ extern const demuxer_desc_t demuxer_desc_playlist;
extern const demuxer_desc_t demuxer_desc_disc;
extern const demuxer_desc_t demuxer_desc_rar;
extern const demuxer_desc_t demuxer_desc_libarchive;
+extern const demuxer_desc_t demuxer_desc_timeline;
/* Please do not add any new demuxers here. If you want to implement a new
* demuxer, add it to libavformat, except for wrappers around external
@@ -103,6 +105,11 @@ struct demux_internal {
void (*wakeup_cb)(void *ctx);
void *wakeup_cb_ctx;
+ struct sh_stream **streams;
+ int num_streams;
+
+ int events;
+
bool warned_queue_overflow;
bool last_eof; // last actual global EOF status
bool eof; // whether we're in EOF state (reset for retry)
@@ -155,6 +162,10 @@ struct demux_stream {
int64_t last_pos;
struct demux_packet *head;
struct demux_packet *tail;
+
+ // for closed captions (demuxer_feed_caption)
+ struct sh_stream *cc;
+
};
// Return "a", or if that is NOPTS, return "def".
@@ -198,42 +209,84 @@ void demux_set_ts_offset(struct demuxer *demuxer, double offset)
pthread_mutex_unlock(&in->lock);
}
-struct sh_stream *new_sh_stream(demuxer_t *demuxer, enum stream_type type)
+// Allocate a new sh_stream of the given type. It either has to be released
+// with talloc_free(), or added to a demuxer with demux_add_sh_stream(). You
+// cannot add or read packets from the stream before it has been added.
+struct sh_stream *demux_alloc_sh_stream(enum stream_type type)
{
- assert(demuxer == demuxer->in->d_thread);
-
- if (demuxer->num_streams > MAX_SH_STREAMS) {
- MP_WARN(demuxer, "Too many streams.\n");
- return NULL;
- }
-
- int demuxer_id = 0;
- for (int n = 0; n < demuxer->num_streams; n++) {
- if (demuxer->streams[n]->type == type)
- demuxer_id++;
- }
-
- struct sh_stream *sh = talloc_ptrtype(demuxer, sh);
+ struct sh_stream *sh = talloc_ptrtype(NULL, sh);
*sh = (struct sh_stream) {
.type = type,
- .index = demuxer->num_streams,
- .ff_index = demuxer->num_streams,
- .demuxer_id = demuxer_id, // may be overwritten by demuxer
- .ds = talloc(sh, struct demux_stream),
+ .index = -1,
+ .ff_index = -1, // may be overwritten by demuxer
+ .demuxer_id = -1, // ... same
+ .codec = talloc_zero(sh, struct mp_codec_params),
};
+ sh->codec->type = type;
+ return sh;
+}
+
+// Add a new sh_stream to the demuxer. Note that as soon as the stream has been
+// added, it must be immutable, and must not be released (this will happen when
+// the demuxer is destroyed).
+void demux_add_sh_stream(struct demuxer *demuxer, struct sh_stream *sh)
+{
+ struct demux_internal *in = demuxer->in;
+ pthread_mutex_lock(&in->lock);
+
+ assert(!sh->ds); // must not be added yet
+
+ sh->ds = talloc(sh, struct demux_stream);
*sh->ds = (struct demux_stream) {
- .in = demuxer->in,
+ .in = in,
.type = sh->type,
- .selected = demuxer->in->autoselect,
+ .selected = in->autoselect,
};
- MP_TARRAY_APPEND(demuxer, demuxer->streams, demuxer->num_streams, sh);
- switch (sh->type) {
- case STREAM_VIDEO: sh->video = talloc_zero(demuxer, struct sh_video); break;
- case STREAM_AUDIO: sh->audio = talloc_zero(demuxer, struct sh_audio); break;
- case STREAM_SUB: sh->sub = talloc_zero(demuxer, struct sh_sub); break;
+
+ if (!sh->codec->codec)
+ sh->codec->codec = "";
+
+ sh->index = in->num_streams;
+ if (sh->ff_index < 0)
+ sh->ff_index = sh->index;
+ if (sh->demuxer_id < 0) {
+ sh->demuxer_id = 0;
+ for (int n = 0; n < in->num_streams; n++) {
+ if (in->streams[n]->type == sh->type)
+ sh->demuxer_id += 1;
+ }
}
- return sh;
+ MP_TARRAY_APPEND(in, in->streams, in->num_streams, sh);
+
+ in->events |= DEMUX_EVENT_STREAMS;
+ if (in->wakeup_cb)
+ in->wakeup_cb(in->wakeup_cb_ctx);
+ pthread_mutex_unlock(&in->lock);
+}
+
+// Return a stream with the given index. Since streams can only be added during
+// the lifetime of the demuxer, it is guaranteed that an index within the valid
+// range [0, demux_get_num_stream()) always returns a valid sh_stream pointer,
+// which will be valid until the demuxer is destroyed.
+struct sh_stream *demux_get_stream(struct demuxer *demuxer, int index)
+{
+ struct demux_internal *in = demuxer->in;
+ pthread_mutex_lock(&in->lock);
+ assert(index >= 0 && index < in->num_streams);
+ struct sh_stream *r = in->streams[index];
+ pthread_mutex_unlock(&in->lock);
+ return r;
+}
+
+// See demux_get_stream().
+int demux_get_num_stream(struct demuxer *demuxer)
+{
+ struct demux_internal *in = demuxer->in;
+ pthread_mutex_lock(&in->lock);
+ int r = in->num_streams;
+ pthread_mutex_unlock(&in->lock);
+ return r;
}
void free_demuxer(demuxer_t *demuxer)
@@ -247,8 +300,10 @@ void free_demuxer(demuxer_t *demuxer)
if (demuxer->desc->close)
demuxer->desc->close(in->d_thread);
- for (int n = 0; n < demuxer->num_streams; n++)
- ds_flush(demuxer->streams[n]->ds);
+ for (int n = in->num_streams - 1; n >= 0; n--) {
+ ds_flush(in->streams[n]->ds);
+ talloc_free(in->streams[n]);
+ }
pthread_mutex_destroy(&in->lock);
pthread_cond_destroy(&in->wakeup);
talloc_free(demuxer);
@@ -312,6 +367,27 @@ const char *stream_type_name(enum stream_type type)
}
}
+void demuxer_feed_caption(struct sh_stream *stream, demux_packet_t *dp)
+{
+ struct demuxer *demuxer = stream->ds->in->d_thread;
+ struct sh_stream *sh = stream->ds->cc;
+
+ if (!sh) {
+ sh = demux_alloc_sh_stream(STREAM_SUB);
+ if (!sh) {
+ talloc_free(dp);
+ return;
+ }
+ sh->codec->codec = "eia_608";
+ stream->ds->cc = sh;
+ demux_add_sh_stream(demuxer, sh);
+ }
+
+ dp->pts = MP_ADD_PTS(dp->pts, -stream->ds->in->ts_offset);
+ dp->dts = MP_ADD_PTS(dp->dts, -stream->ds->in->ts_offset);
+ demux_add_packet(sh, dp);
+}
+
void demux_add_packet(struct sh_stream *stream, demux_packet_t *dp)
{
struct demux_stream *ds = stream ? stream->ds : NULL;
@@ -388,8 +464,8 @@ static bool read_packet(struct demux_internal *in)
// safe-guards against packet queue overflow.
bool active = false, read_more = false;
size_t packs = 0, bytes = 0;
- for (int n = 0; n < in->d_buffer->num_streams; n++) {
- struct demux_stream *ds = in->d_buffer->streams[n]->ds;
+ for (int n = 0; n < in->num_streams; n++) {
+ struct demux_stream *ds = in->streams[n]->ds;
active |= ds->active;
read_more |= ds->active && !ds->head;
packs += ds->packs;
@@ -404,17 +480,22 @@ static bool read_packet(struct demux_internal *in)
if (!in->warned_queue_overflow) {
in->warned_queue_overflow = true;
MP_ERR(in, "Too many packets in the demuxer packet queues:\n");
- for (int n = 0; n < in->d_buffer->num_streams; n++) {
- struct demux_stream *ds = in->d_buffer->streams[n]->ds;
+ for (int n = 0; n < in->num_streams; n++) {
+ struct demux_stream *ds = in->streams[n]->ds;
if (ds->selected) {
MP_ERR(in, " %s/%d: %zd packets, %zd bytes\n",
stream_type_name(ds->type), n, ds->packs, ds->bytes);
}
}
}
- for (int n = 0; n < in->d_buffer->num_streams; n++) {
- struct demux_stream *ds = in->d_buffer->streams[n]->ds;
- ds->eof |= !ds->head;
+ for (int n = 0; n < in->num_streams; n++) {
+ struct demux_stream *ds = in->streams[n]->ds;
+ bool eof = !ds->head;
+ if (eof && !ds->eof) {
+ if (in->wakeup_cb)
+ in->wakeup_cb(in->wakeup_cb_ctx);
+ }
+ ds->eof |= eof;
}
pthread_cond_signal(&in->wakeup);
return false;
@@ -433,8 +514,8 @@ static bool read_packet(struct demux_internal *in)
pthread_mutex_lock(&in->lock);
if (eof) {
- for (int n = 0; n < in->d_buffer->num_streams; n++)
- in->d_buffer->streams[n]->ds->eof = true;
+ for (int n = 0; n < in->num_streams; n++)
+ in->streams[n]->ds->eof = true;
// If we had EOF previously, then don't wakeup (avoids wakeup loop)
if (!in->last_eof) {
if (in->wakeup_cb)
@@ -478,8 +559,8 @@ static void start_refreshing(struct demux_internal *in)
in->start_refresh_seek = false;
double start_ts = MP_NOPTS_VALUE;
- for (int n = 0; n < demux->num_streams; n++) {
- struct demux_stream *ds = demux->streams[n]->ds;
+ for (int n = 0; n < in->num_streams; n++) {
+ struct demux_stream *ds = in->streams[n]->ds;
if (ds->type == STREAM_VIDEO || ds->type == STREAM_AUDIO)
start_ts = MP_PTS_MIN(start_ts, ds->base_ts);
}
@@ -488,8 +569,8 @@ static void start_refreshing(struct demux_internal *in)
demux->partially_seekable || !demux->allow_refresh_seeks)
return;
- for (int n = 0; n < demux->num_streams; n++) {
- struct demux_stream *ds = demux->streams[n]->ds;
+ for (int n = 0; n < in->num_streams; n++) {
+ struct demux_stream *ds = in->streams[n]->ds;
// Streams which didn't read any packets yet can return all packets,
// or they'd be stuck forever; affects newly selected streams too.
if (ds->last_pos != -1)
@@ -499,8 +580,7 @@ static void start_refreshing(struct demux_internal *in)
pthread_mutex_unlock(&in->lock);
// Seek back to player's current position, with a small offset added.
- in->d_thread->desc->seek(in->d_thread, start_ts - 1.0,
- SEEK_ABSOLUTE | SEEK_BACKWARD | SEEK_HR);
+ in->d_thread->desc->seek(in->d_thread, start_ts - 1.0, SEEK_BACKWARD | SEEK_HR);
pthread_mutex_lock(&in->lock);
}
@@ -509,11 +589,18 @@ static void execute_trackswitch(struct demux_internal *in)
{
in->tracks_switched = false;
+ bool any_selected = false;
+ for (int n = 0; n < in->num_streams; n++)
+ any_selected |= in->streams[n]->ds->selected;
+
pthread_mutex_unlock(&in->lock);
if (in->d_thread->desc->control)
in->d_thread->desc->control(in->d_thread, DEMUXER_CTRL_SWITCHED_TRACKS, 0);
+ stream_control(in->d_thread->stream, STREAM_CTRL_SET_READAHEAD,
+ &(int){any_selected});
+
pthread_mutex_lock(&in->lock);
if (in->start_refresh_seek)
@@ -615,9 +702,27 @@ static struct demux_packet *dequeue_packet(struct demux_stream *ds)
pkt->pts = MP_ADD_PTS(pkt->pts, ds->in->ts_offset);
pkt->dts = MP_ADD_PTS(pkt->dts, ds->in->ts_offset);
+ pkt->start = MP_ADD_PTS(pkt->start, ds->in->ts_offset);
+ pkt->end = MP_ADD_PTS(pkt->end, ds->in->ts_offset);
+
return pkt;
}
+// Sparse packets (Subtitles) interleaved with other non-sparse packets (video,
+// audio) should never be read actively, meaning the demuxer thread does not
+// try to exceed default readahead in order to find a new packet.
+static bool use_lazy_subtitle_reading(struct demux_stream *ds)
+{
+ if (ds->type != STREAM_SUB)
+ return false;
+ for (int n = 0; n < ds->in->num_streams; n++) {
+ struct demux_stream *s = ds->in->streams[n]->ds;
+ if (s->type != STREAM_SUB && s->selected && !s->eof)
+ return true;
+ }
+ return false;
+}
+
// Read a packet from the given stream. The returned packet belongs to the
// caller, who has to free it with talloc_free(). Might block. Returns NULL
// on EOF.
@@ -627,7 +732,8 @@ struct demux_packet *demux_read_packet(struct sh_stream *sh)
struct demux_packet *pkt = NULL;
if (ds) {
pthread_mutex_lock(&ds->in->lock);
- ds_get_packets(ds);
+ if (!use_lazy_subtitle_reading(ds))
+ ds_get_packets(ds);
pkt = dequeue_packet(ds);
pthread_cond_signal(&ds->in->wakeup); // possibly read more
pthread_mutex_unlock(&ds->in->lock);
@@ -638,12 +744,15 @@ struct demux_packet *demux_read_packet(struct sh_stream *sh)
// Poll the demuxer queue, and if there's a packet, return it. Otherwise, just
// make the demuxer thread read packets for this stream, and if there's at
// least one packet, call the wakeup callback.
-// Unlike demux_read_packet(), this always enables readahead (which means you
-// must not use it on interleaved subtitle streams).
+// Unlike demux_read_packet(), this always enables readahead (except for
+// interleaved subtitles).
// Returns:
// < 0: EOF was reached, *out_pkt=NULL
// == 0: no new packet yet, but maybe later, *out_pkt=NULL
// > 0: new packet read, *out_pkt is set
+// Note: when reading interleaved subtitles, the demuxer won't try to forcibly
+// read ahead to get the next subtitle packet (as the next packet could be
+// minutes away). In this situation, this function will just return -1.
int demux_read_packet_async(struct sh_stream *sh, struct demux_packet **out_pkt)
{
struct demux_stream *ds = sh ? sh->ds : NULL;
@@ -653,10 +762,14 @@ int demux_read_packet_async(struct sh_stream *sh, struct demux_packet **out_pkt)
if (ds->in->threading) {
pthread_mutex_lock(&ds->in->lock);
*out_pkt = dequeue_packet(ds);
- r = *out_pkt ? 1 : ((ds->eof || !ds->selected) ? -1 : 0);
- ds->active = ds->selected; // enable readahead
- ds->in->eof = false; // force retry
- pthread_cond_signal(&ds->in->wakeup); // possibly read more
+ if (use_lazy_subtitle_reading(ds)) {
+ r = *out_pkt ? 1 : -1;
+ } else {
+ r = *out_pkt ? 1 : ((ds->eof || !ds->selected) ? -1 : 0);
+ ds->active = ds->selected; // enable readahead
+ ds->in->eof = false; // force retry
+ pthread_cond_signal(&ds->in->wakeup); // possibly read more
+ }
pthread_mutex_unlock(&ds->in->lock);
} else {
*out_pkt = demux_read_packet(sh);
@@ -666,22 +779,6 @@ int demux_read_packet_async(struct sh_stream *sh, struct demux_packet **out_pkt)
return r;
}
-// Return the pts of the next packet that demux_read_packet() would return.
-// Might block. Sometimes used to force a packet read, without removing any
-// packets from the queue.
-double demux_get_next_pts(struct sh_stream *sh)
-{
- double res = MP_NOPTS_VALUE;
- if (sh) {
- pthread_mutex_lock(&sh->ds->in->lock);
- ds_get_packets(sh->ds);
- if (sh->ds->head)
- res = MP_ADD_PTS(sh->ds->head->pts, sh->ds->in->ts_offset);
- pthread_mutex_unlock(&sh->ds->in->lock);
- }
- return res;
-}
-
// Return whether a packet is queued. Never blocks, never forces any reads.
bool demux_has_packet(struct sh_stream *sh)
{
@@ -694,24 +791,25 @@ bool demux_has_packet(struct sh_stream *sh)
return has_packet;
}
-// Read and return any packet we find.
+// Read and return any packet we find. NULL means EOF.
struct demux_packet *demux_read_any_packet(struct demuxer *demuxer)
{
- assert(!demuxer->in->threading); // doesn't work with threading
+ struct demux_internal *in = demuxer->in;
+ assert(!in->threading); // doesn't work with threading
bool read_more = true;
while (read_more) {
- for (int n = 0; n < demuxer->num_streams; n++) {
- struct sh_stream *sh = demuxer->streams[n];
+ for (int n = 0; n < in->num_streams; n++) {
+ struct sh_stream *sh = in->streams[n];
sh->ds->active = sh->ds->selected; // force read_packet() to read
struct demux_packet *pkt = dequeue_packet(sh->ds);
if (pkt)
return pkt;
}
// retry after calling this
- pthread_mutex_lock(&demuxer->in->lock);
- read_more = read_packet(demuxer->in);
- read_more &= !demuxer->in->eof;
- pthread_mutex_unlock(&demuxer->in->lock);
+ pthread_mutex_lock(&in->lock); // lock only because read_packet unlocks
+ read_more = read_packet(in);
+ read_more &= !in->eof;
+ pthread_mutex_unlock(&in->lock);
}
return NULL;
}
@@ -795,13 +893,14 @@ static int decode_peak(demuxer_t *demuxer, const char *tag, float *out)
static void apply_replaygain(demuxer_t *demuxer, struct replaygain_data *rg)
{
- for (int n = 0; n < demuxer->num_streams; n++) {
- struct sh_stream *sh = demuxer->streams[n];
- if (sh->audio && !sh->audio->replaygain_data) {
+ struct demux_internal *in = demuxer->in;
+ for (int n = 0; n < in->num_streams; n++) {
+ struct sh_stream *sh = in->streams[n];
+ if (sh->type == STREAM_AUDIO && !sh->codec->replaygain_data) {
MP_VERBOSE(demuxer, "Replaygain: Track=%f/%f Album=%f/%f\n",
rg->track_gain, rg->track_peak,
rg->album_gain, rg->album_peak);
- sh->audio->replaygain_data = talloc_memdup(demuxer, rg, sizeof(*rg));
+ sh->codec->replaygain_data = talloc_memdup(in, rg, sizeof(*rg));
}
}
}
@@ -847,17 +946,11 @@ static void demux_copy(struct demuxer *dst, struct demuxer *src)
dst->partially_seekable = src->partially_seekable;
dst->filetype = src->filetype;
dst->ts_resets_possible = src->ts_resets_possible;
- dst->rel_seeks = src->rel_seeks;
dst->allow_refresh_seeks = src->allow_refresh_seeks;
dst->fully_read = src->fully_read;
dst->start_time = src->start_time;
dst->priv = src->priv;
}
- if (src->events & DEMUX_EVENT_STREAMS) {
- // The stream structs themselves are immutable.
- for (int n = dst->num_streams; n < src->num_streams; n++)
- MP_TARRAY_APPEND(dst, dst->streams, dst->num_streams, src->streams[n]);
- }
if (src->events & DEMUX_EVENT_METADATA) {
talloc_free(dst->metadata);
dst->metadata = mp_tags_dup(dst, src->metadata);
@@ -905,6 +998,8 @@ void demux_update(demuxer_t *demuxer)
pthread_mutex_lock(&in->lock);
demux_copy(demuxer, in->d_buffer);
+ demuxer->events |= in->events;
+ in->events = 0;
if (in->stream_metadata && (demuxer->events & DEMUX_EVENT_METADATA))
mp_tags_merge(demuxer->metadata, in->stream_metadata);
pthread_mutex_unlock(&in->lock);
@@ -926,10 +1021,15 @@ static void demux_init_cuesheet(struct demuxer *demuxer)
if (cue && !demuxer->num_chapters) {
struct cue_file *f = mp_parse_cue(bstr0(cue));
if (f) {
- for (int n = 0; n < f->num_tracks; n++) {
- struct cue_track *t = &f->tracks[n];
- int idx = demuxer_add_chapter(demuxer, "", t->start, -1);
- mp_tags_merge(demuxer->chapters[idx].metadata, t->tags);
+ if (mp_check_embedded_cue(f) < 0) {
+ MP_WARN(demuxer, "Embedded cue sheet references more than one file. "
+ "Ignoring it.\n");
+ } else {
+ for (int n = 0; n < f->num_tracks; n++) {
+ struct cue_track *t = &f->tracks[n];
+ int idx = demuxer_add_chapter(demuxer, "", t->start, -1);
+ mp_tags_merge(demuxer->chapters[idx].metadata, t->tags);
+ }
}
}
talloc_free(f);
@@ -987,10 +1087,11 @@ static struct demuxer *open_given_type(struct mpv_global *global,
in->d_user->metadata = talloc_zero(in->d_user, struct mp_tags);
in->d_buffer->metadata = talloc_zero(in->d_buffer, struct mp_tags);
- mp_verbose(log, "Trying demuxer: %s (force-level: %s)\n",
- desc->name, d_level(check));
+ mp_dbg(log, "Trying demuxer: %s (force-level: %s)\n",
+ desc->name, d_level(check));
- if (stream->seekable) // not for DVD/BD/DVB in particular
+ // not for DVD/BD/DVB in particular
+ if (stream->seekable && (!params || !params->timeline))
stream_seek(stream, 0);
// Peek this much data to avoid that stream_read() run by some demuxers
@@ -1017,6 +1118,17 @@ static struct demuxer *open_given_type(struct mpv_global *global,
demux_init_cache(demuxer);
demux_changed(in->d_thread, DEMUX_EVENT_ALL);
demux_update(demuxer);
+ stream_control(demuxer->stream, STREAM_CTRL_SET_READAHEAD, &(int){false});
+ struct timeline *tl = timeline_load(global, log, demuxer);
+ if (tl) {
+ struct demuxer_params params2 = {0};
+ params2.timeline = tl;
+ struct demuxer *sub = open_given_type(global, log,
+ &demuxer_desc_timeline, stream,
+ &params2, DEMUX_CHECK_FORCE);
+ if (sub)
+ return sub;
+ }
return demuxer;
}
@@ -1060,6 +1172,7 @@ struct demuxer *demux_open(struct stream *stream, struct demuxer_params *params,
// Test demuxers from first to last, one pass for each check_levels[] entry
for (int pass = 0; check_levels[pass] != -1; pass++) {
enum demux_check level = check_levels[pass];
+ mp_verbose(log, "Trying demuxers for level=%s.\n", d_level(level));
for (int n = 0; demuxer_list[n]; n++) {
const struct demuxer_desc *desc = demuxer_list[n];
if (!check_desc || desc == check_desc) {
@@ -1108,8 +1221,8 @@ struct demuxer *demux_open_url(const char *url,
static void flush_locked(demuxer_t *demuxer)
{
- for (int n = 0; n < demuxer->num_streams; n++)
- ds_flush(demuxer->streams[n]->ds);
+ for (int n = 0; n < demuxer->in->num_streams; n++)
+ ds_flush(demuxer->in->streams[n]->ds);
demuxer->in->warned_queue_overflow = false;
demuxer->in->eof = false;
demuxer->in->last_eof = false;
@@ -1125,7 +1238,7 @@ void demux_flush(demuxer_t *demuxer)
pthread_mutex_unlock(&demuxer->in->lock);
}
-int demux_seek(demuxer_t *demuxer, double rel_seek_secs, int flags)
+int demux_seek(demuxer_t *demuxer, double seek_pts, int flags)
{
struct demux_internal *in = demuxer->in;
assert(demuxer == in->d_user);
@@ -1135,32 +1248,22 @@ int demux_seek(demuxer_t *demuxer, double rel_seek_secs, int flags)
return 0;
}
- if ((flags & SEEK_FACTOR) && !(flags & SEEK_ABSOLUTE)) {
- MP_WARN(demuxer, "Invalid seek flags.\n");
+ if (seek_pts == MP_NOPTS_VALUE)
return 0;
- }
- if (rel_seek_secs == MP_NOPTS_VALUE && (flags & SEEK_ABSOLUTE))
- return 0;
-
- if (!(flags & (SEEK_BACKWARD | SEEK_FORWARD))) {
- if (flags & SEEK_ABSOLUTE || rel_seek_secs < 0) {
- flags |= SEEK_BACKWARD;
- } else {
- flags |= SEEK_FORWARD;
- }
- }
+ if (!(flags & SEEK_FORWARD))
+ flags |= SEEK_BACKWARD;
pthread_mutex_lock(&in->lock);
- MP_VERBOSE(in, "queuing seek to %f%s\n", rel_seek_secs,
+ MP_VERBOSE(in, "queuing seek to %f%s\n", seek_pts,
in->seeking ? " (cascade)" : "");
flush_locked(demuxer);
in->seeking = true;
in->seek_flags = flags;
- in->seek_pts = rel_seek_secs;
- if ((flags & SEEK_ABSOLUTE) && !(flags & SEEK_FACTOR))
+ in->seek_pts = seek_pts;
+ if (!(flags & SEEK_FACTOR))
in->seek_pts = MP_ADD_PTS(in->seek_pts, -in->ts_offset);
if (!in->threading)
@@ -1187,44 +1290,35 @@ void demux_set_enable_refresh_seeks(struct demuxer *demuxer, bool enabled)
struct sh_stream *demuxer_stream_by_demuxer_id(struct demuxer *d,
enum stream_type t, int id)
{
- for (int n = 0; n < d->num_streams; n++) {
- struct sh_stream *s = d->streams[n];
+ int num = demux_get_num_stream(d);
+ for (int n = 0; n < num; n++) {
+ struct sh_stream *s = demux_get_stream(d, n);
if (s->type == t && s->demuxer_id == id)
- return d->streams[n];
+ return s;
}
return NULL;
}
-void demuxer_switch_track(struct demuxer *demuxer, enum stream_type type,
- struct sh_stream *stream)
-{
- assert(!stream || stream->type == type);
-
- for (int n = 0; n < demuxer->num_streams; n++) {
- struct sh_stream *cur = demuxer->streams[n];
- if (cur->type == type)
- demuxer_select_track(demuxer, cur, cur == stream);
- }
-}
-
void demuxer_select_track(struct demuxer *demuxer, struct sh_stream *stream,
bool selected)
{
struct demux_internal *in = demuxer->in;
pthread_mutex_lock(&in->lock);
- bool update = false;
// don't flush buffers if stream is already selected / unselected
if (stream->ds->selected != selected) {
stream->ds->selected = selected;
stream->ds->active = false;
ds_flush(stream->ds);
- if (selected && in->refresh_seeks_enabled && in->threading)
+ in->tracks_switched = true;
+ if (selected && in->refresh_seeks_enabled)
in->start_refresh_seek = true;
- update = true;
+ if (in->threading) {
+ pthread_cond_signal(&in->wakeup);
+ } else {
+ execute_trackswitch(in);
+ }
}
pthread_mutex_unlock(&in->lock);
- if (update)
- demux_control(demuxer, DEMUXER_CTRL_SWITCHED_TRACKS, NULL);
}
void demux_set_stream_autoselect(struct demuxer *demuxer, bool autoselect)
@@ -1395,16 +1489,12 @@ static int cached_demux_control(struct demux_internal *in, int cmd, void *arg)
c->res = r;
return DEMUXER_CTRL_OK;
}
- case DEMUXER_CTRL_SWITCHED_TRACKS:
- in->tracks_switched = true;
- pthread_cond_signal(&in->wakeup);
- return DEMUXER_CTRL_OK;
case DEMUXER_CTRL_GET_BITRATE_STATS: {
double *rates = arg;
for (int n = 0; n < STREAM_TYPE_COUNT; n++)
rates[n] = -1;
- for (int n = 0; n < in->d_user->num_streams; n++) {
- struct demux_stream *ds = in->d_user->streams[n]->ds;
+ for (int n = 0; n < in->num_streams; n++) {
+ struct demux_stream *ds = in->streams[n]->ds;
if (ds->selected && ds->bitrate >= 0)
rates[ds->type] = MPMAX(0, rates[ds->type]) + ds->bitrate;
}
@@ -1418,8 +1508,8 @@ static int cached_demux_control(struct demux_internal *in, int cmd, void *arg)
.ts_duration = -1,
};
int num_packets = 0;
- for (int n = 0; n < in->d_user->num_streams; n++) {
- struct demux_stream *ds = in->d_user->streams[n]->ds;
+ for (int n = 0; n < in->num_streams; n++) {
+ struct demux_stream *ds = in->streams[n]->ds;
if (ds->active) {
r->underrun |= !ds->head && !ds->eof;
r->ts_range[0] = MP_PTS_MAX(r->ts_range[0], ds->base_ts);
diff --git a/demux/demux.h b/demux/demux.h
index 96717d5..e882e90 100644
--- a/demux/demux.h
+++ b/demux/demux.h
@@ -57,7 +57,6 @@ struct demux_ctrl_stream_ctrl {
int res;
};
-#define SEEK_ABSOLUTE (1 << 0) // argument is a timestamp
#define SEEK_FACTOR (1 << 1) // argument is in range [0,1]
#define SEEK_FORWARD (1 << 2) // prefer later time if not exact
#define SEEK_BACKWARD (1 << 3) // prefer earlier time if not exact
@@ -86,8 +85,6 @@ enum demux_event {
DEMUX_EVENT_ALL = 0xFFFF,
};
-#define MAX_SH_STREAMS 256
-
struct demuxer;
struct timeline;
@@ -162,7 +159,7 @@ struct demuxer_params {
struct matroska_segment_uid *matroska_wanted_uids;
int matroska_wanted_segment;
bool *matroska_was_valid;
- bool expect_subtitle;
+ struct timeline *timeline;
// -- demux_open_url() only
int stream_flags;
bool allow_capture;
@@ -181,9 +178,6 @@ typedef struct demuxer {
double start_time;
// File format allows PTS resets (even if the current file is without)
bool ts_resets_possible;
- // Send relative seek requests, instead of SEEK_ABSOLUTE or SEEK_FACTOR.
- // This is only done if the user explicitly uses a relative seek.
- bool rel_seeks;
// Enable fast track switching hacks. This requires from the demuxer:
// - seeking is somewhat reliable; packet contents must not change
// - packet position (demux_packet.pos) is set, not negative, unique, and
@@ -199,9 +193,6 @@ typedef struct demuxer {
// Bitmask of DEMUX_EVENT_*
int events;
- struct sh_stream **streams;
- int num_streams;
-
struct demux_edition *editions;
int num_editions;
int edition;
@@ -243,15 +234,19 @@ void free_demuxer(struct demuxer *demuxer);
void free_demuxer_and_stream(struct demuxer *demuxer);
void demux_add_packet(struct sh_stream *stream, demux_packet_t *dp);
+void demuxer_feed_caption(struct sh_stream *stream, demux_packet_t *dp);
struct demux_packet *demux_read_packet(struct sh_stream *sh);
int demux_read_packet_async(struct sh_stream *sh, struct demux_packet **out_pkt);
bool demux_stream_is_selected(struct sh_stream *stream);
-double demux_get_next_pts(struct sh_stream *sh);
bool demux_has_packet(struct sh_stream *sh);
struct demux_packet *demux_read_any_packet(struct demuxer *demuxer);
-struct sh_stream *new_sh_stream(struct demuxer *demuxer, enum stream_type type);
+struct sh_stream *demux_get_stream(struct demuxer *demuxer, int index);
+int demux_get_num_stream(struct demuxer *demuxer);
+
+struct sh_stream *demux_alloc_sh_stream(enum stream_type type);
+void demux_add_sh_stream(struct demuxer *demuxer, struct sh_stream *sh);
struct demuxer *demux_open(struct stream *stream, struct demuxer_params *params,
struct mpv_global *global);
@@ -275,8 +270,6 @@ void demux_set_ts_offset(struct demuxer *demuxer, double offset);
int demux_control(struct demuxer *demuxer, int cmd, void *arg);
-void demuxer_switch_track(struct demuxer *demuxer, enum stream_type type,
- struct sh_stream *stream);
void demuxer_select_track(struct demuxer *demuxer, struct sh_stream *stream,
bool selected);
void demux_set_stream_autoselect(struct demuxer *demuxer, bool autoselect);
diff --git a/demux/demux_cue.c b/demux/demux_cue.c
index 4342057..673e8b9 100644
--- a/demux/demux_cue.c
+++ b/demux/demux_cue.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 <stdlib.h>
@@ -23,7 +23,7 @@
#include <dirent.h>
#include <inttypes.h>
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "misc/bstr.h"
#include "common/msg.h"
@@ -150,8 +150,18 @@ static void build_timeline(struct timeline *tl)
add_source(tl, tl->demuxer);
- struct cue_track *tracks = p->f->tracks;
- size_t track_count = p->f->num_tracks;
+ struct cue_track *tracks = NULL;
+ size_t track_count = 0;
+
+ for (size_t n = 0; n < p->f->num_tracks; n++) {
+ struct cue_track *track = &p->f->tracks[n];
+ if (track->filename) {
+ MP_TARRAY_APPEND(ctx, tracks, track_count, *track);
+ } else {
+ MP_WARN(tl->demuxer, "No file specified for track entry %zd. "
+ "It will be removed\n", n + 1);
+ }
+ }
if (track_count == 0) {
MP_ERR(tl, "CUE: no tracks found!\n");
diff --git a/demux/demux_disc.c b/demux/demux_disc.c
index b994b3c..91b87a4 100644
--- a/demux/demux_disc.c
+++ b/demux/demux_disc.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 <string.h>
@@ -23,6 +23,7 @@
#include "common/msg.h"
#include "stream/stream.h"
+#include "video/mp_image.h"
#include "demux.h"
#include "stheader.h"
@@ -41,7 +42,6 @@ struct priv {
double base_time; // playback display start time of current segment
double base_dts; // packet DTS that maps to base_time
double last_dts; // DTS of previously demuxed packet
- double seek_pts;
bool seek_reinit; // needs reinit after seek
};
@@ -53,9 +53,10 @@ struct priv {
static void reselect_streams(demuxer_t *demuxer)
{
struct priv *p = demuxer->priv;
- for (int n = 0; n < MPMIN(p->slave->num_streams, p->num_streams); n++) {
+ int num_slave = demux_get_num_stream(p->slave);
+ for (int n = 0; n < MPMIN(num_slave, p->num_streams); n++) {
if (p->streams[n]) {
- demuxer_select_track(p->slave, p->slave->streams[n],
+ demuxer_select_track(p->slave, demux_get_stream(p->slave, n),
demux_stream_is_selected(p->streams[n]));
}
}
@@ -80,11 +81,9 @@ static void add_dvd_streams(demuxer_t *demuxer)
struct stream_dvd_info_req info;
if (stream_control(stream, STREAM_CTRL_GET_DVD_INFO, &info) > 0) {
for (int n = 0; n < MPMIN(32, info.num_subs); n++) {
- struct sh_stream *sh = new_sh_stream(demuxer, STREAM_SUB);
- if (!sh)
- break;
+ struct sh_stream *sh = demux_alloc_sh_stream(STREAM_SUB);
sh->demuxer_id = n + 0x20;
- sh->codec = "dvd_subtitle";
+ sh->codec->codec = "dvd_subtitle";
get_disc_lang(stream, sh);
// p->streams _must_ match with p->slave->streams, so we can't add
// it yet - it has to be done when the real stream appears, which
@@ -111,8 +110,10 @@ static void add_dvd_streams(demuxer_t *demuxer)
}
s = talloc_asprintf_append(s, "\n");
- sh->extradata = s;
- sh->extradata_size = strlen(s);
+ sh->codec->extradata = s;
+ sh->codec->extradata_size = strlen(s);
+
+ demux_add_sh_stream(demuxer, sh);
}
}
}
@@ -121,9 +122,9 @@ static void add_streams(demuxer_t *demuxer)
{
struct priv *p = demuxer->priv;
- for (int n = p->num_streams; n < p->slave->num_streams; n++) {
- struct sh_stream *src = p->slave->streams[n];
- if (src->sub) {
+ for (int n = p->num_streams; n < demux_get_num_stream(p->slave); n++) {
+ struct sh_stream *src = demux_get_stream(p->slave, n);
+ if (src->type == STREAM_SUB) {
struct sh_stream *sub = NULL;
if (src->demuxer_id >= 0x20 && src->demuxer_id <= 0x3F)
sub = p->dvd_subs[src->demuxer_id - 0x20];
@@ -133,58 +134,51 @@ static void add_streams(demuxer_t *demuxer)
continue;
}
}
- struct sh_stream *sh = new_sh_stream(demuxer, src->type);
- if (!sh)
- break;
+ struct sh_stream *sh = demux_alloc_sh_stream(src->type);
assert(p->num_streams == n); // directly mapped
MP_TARRAY_APPEND(p, p->streams, p->num_streams, sh);
// Copy all stream fields that might be relevant
- sh->codec = talloc_strdup(sh, src->codec);
- sh->codec_tag = src->codec_tag;
- sh->lav_headers = src->lav_headers;
+ *sh->codec = *src->codec;
sh->demuxer_id = src->demuxer_id;
- if (src->video) {
+ if (src->type == STREAM_VIDEO) {
double ar;
if (stream_control(demuxer->stream, STREAM_CTRL_GET_ASPECT_RATIO, &ar)
== STREAM_OK)
- sh->video->aspect = ar;
+ {
+ struct mp_image_params f = {.w = src->codec->disp_w,
+ .h = src->codec->disp_h};
+ mp_image_params_set_dsize(&f, 1728 * ar, 1728);
+ sh->codec->par_w = f.p_w;
+ sh->codec->par_h = f.p_h;
+ }
}
- if (src->audio)
- sh->audio = src->audio;
get_disc_lang(demuxer->stream, sh);
+ demux_add_sh_stream(demuxer, sh);
}
reselect_streams(demuxer);
}
-static void d_seek(demuxer_t *demuxer, double rel_seek_secs, int flags)
+static void d_seek(demuxer_t *demuxer, double seek_pts, int flags)
{
struct priv *p = demuxer->priv;
if (demuxer->stream->uncached_type == STREAMTYPE_CDDA) {
- demux_seek(p->slave, rel_seek_secs, flags);
+ demux_seek(p->slave, seek_pts, flags);
return;
}
- double pts = p->seek_pts;
- if (flags & SEEK_ABSOLUTE)
- pts = 0.0f;
- double base_pts = pts; // to what pts is relative
-
if (flags & SEEK_FACTOR) {
double tmp = 0;
stream_control(demuxer->stream, STREAM_CTRL_GET_TIME_LENGTH, &tmp);
- pts += tmp * rel_seek_secs;
- } else {
- pts += rel_seek_secs;
+ seek_pts *= tmp;
}
- MP_VERBOSE(demuxer, "seek to: %f\n", pts);
+ MP_VERBOSE(demuxer, "seek to: %f\n", seek_pts);
- double seek_arg[] = {pts, base_pts, flags};
+ double seek_arg[] = {seek_pts, flags};
stream_control(demuxer->stream, STREAM_CTRL_SEEK_TO_TIME, seek_arg);
demux_control(p->slave, DEMUXER_CTRL_RESYNC, NULL);
- p->seek_pts = pts;
p->seek_reinit = true;
}
@@ -265,9 +259,6 @@ static int d_fill_buffer(demuxer_t *demuxer)
MP_TRACE(demuxer, "opts: %d %f %f\n", sh->type, pkt->pts, pkt->dts);
- if (pkt->pts != MP_NOPTS_VALUE)
- p->seek_pts = pkt->pts;
-
demux_add_packet(sh, pkt);
return 1;
}
@@ -325,8 +316,6 @@ static int d_open(demuxer_t *demuxer, enum demux_check check)
// Can be seekable even if the stream isn't.
demuxer->seekable = true;
-
- demuxer->rel_seeks = true;
}
add_dvd_streams(demuxer);
diff --git a/demux/demux_edl.c b/demux/demux_edl.c
index 9ba0307..aeccab4 100644
--- a/demux/demux_edl.c
+++ b/demux/demux_edl.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 <stdlib.h>
@@ -23,7 +23,7 @@
#include <inttypes.h>
#include <math.h>
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "demux.h"
#include "timeline.h"
diff --git a/demux/demux_lavf.c b/demux/demux_lavf.c
index 2d12f5c..8a92a28 100644
--- a/demux/demux_lavf.c
+++ b/demux/demux_lavf.c
@@ -40,6 +40,7 @@
#include "common/tags.h"
#include "common/av_common.h"
#include "misc/bstr.h"
+#include "misc/charset_conv.h"
#include "stream/stream.h"
#include "demux.h"
@@ -108,6 +109,7 @@ struct format_hack {
bool no_stream : 1; // do not wrap struct stream as AVIOContext
bool use_stream_ids : 1; // export the native stream IDs
bool fully_read : 1; // set demuxer.fully_read flag
+ bool detect_charset : 1; // format is a small text file, possibly not UTF8
bool image_format : 1; // expected to contain exactly 1 frame
// Do not confuse player's position estimation (position is into external
// segment, with e.g. HLS, player knows about the playlist main file only).
@@ -115,8 +117,8 @@ struct format_hack {
};
#define BLACKLIST(fmt) {fmt, .ignore = true}
-#define TEXTSUB(fmt) {fmt, .fully_read = true}
-#define IMAGEFMT(fmt) {fmt, .image_format = true}
+#define TEXTSUB(fmt) {fmt, .fully_read = true, .detect_charset = true}
+#define TEXTSUB_UTF8(fmt) {fmt, .fully_read = true}
static const struct format_hack format_hacks[] = {
// for webradios
@@ -135,10 +137,12 @@ static const struct format_hack format_hacks[] = {
{"h264", .if_flags = AVFMT_NOTIMESTAMPS },
{"hevc", .if_flags = AVFMT_NOTIMESTAMPS },
- TEXTSUB("aqtitle"), TEXTSUB("ass"), TEXTSUB("jacosub"), TEXTSUB("microdvd"),
+ TEXTSUB("aqtitle"), TEXTSUB("jacosub"), TEXTSUB("microdvd"),
TEXTSUB("mpl2"), TEXTSUB("mpsub"), TEXTSUB("pjs"), TEXTSUB("realtext"),
TEXTSUB("sami"), TEXTSUB("srt"), TEXTSUB("stl"), TEXTSUB("subviewer"),
- TEXTSUB("subviewer1"), TEXTSUB("vplayer"), TEXTSUB("webvtt"),
+ TEXTSUB("subviewer1"), TEXTSUB("vplayer"), TEXTSUB("ass"),
+
+ TEXTSUB_UTF8("webvtt"),
// Useless non-sense, sometimes breaks MLP2 subreader.c fallback
BLACKLIST("tty"),
@@ -148,23 +152,24 @@ static const struct format_hack format_hacks[] = {
// Useless, does not work with custom streams.
BLACKLIST("image2"),
// Image demuxers ("<name>_pipe" is detected explicitly)
- IMAGEFMT("image2pipe"),
+ {"image2pipe", .image_format = true},
{0}
};
typedef struct lavf_priv {
+ struct stream *stream;
char *filename;
struct format_hack format_hack;
AVInputFormat *avif;
int avif_flags;
AVFormatContext *avfc;
AVIOContext *pb;
- int64_t last_pts;
struct sh_stream **streams; // NULL for unknown streams
int num_streams;
int cur_program;
char *mime_type;
bool merge_track_metadata;
+ double seek_delay;
} lavf_priv_t;
// At least mp4 has name="mov,mp4,m4a,3gp,3g2,mj2", so we split the name
@@ -187,7 +192,8 @@ static bool matches_avinputformat_name(struct lavf_priv *priv,
static int mp_read(void *opaque, uint8_t *buf, int size)
{
struct demuxer *demuxer = opaque;
- struct stream *stream = demuxer->stream;
+ lavf_priv_t *priv = demuxer->priv;
+ struct stream *stream = priv->stream;
int ret;
ret = stream_read(stream, buf, size);
@@ -200,9 +206,13 @@ static int mp_read(void *opaque, uint8_t *buf, int size)
static int64_t mp_seek(void *opaque, int64_t pos, int whence)
{
struct demuxer *demuxer = opaque;
- struct stream *stream = demuxer->stream;
+ lavf_priv_t *priv = demuxer->priv;
+ struct stream *stream = priv->stream;
int64_t current_pos;
- MP_TRACE(demuxer, "mp_seek(%p, %"PRId64", %d)\n", stream, pos, whence);
+ MP_TRACE(demuxer, "mp_seek(%p, %"PRId64", %s)\n", stream, pos,
+ whence == SEEK_END ? "end" :
+ whence == SEEK_CUR ? "cur" :
+ whence == SEEK_SET ? "set" : "size");
if (whence == SEEK_END || whence == AVSEEK_SIZE) {
int64_t end = stream_get_size(stream);
if (end < 0)
@@ -230,7 +240,8 @@ static int64_t mp_seek(void *opaque, int64_t pos, int whence)
static int64_t mp_read_seek(void *opaque, int stream_idx, int64_t ts, int flags)
{
struct demuxer *demuxer = opaque;
- struct stream *stream = demuxer->stream;
+ lavf_priv_t *priv = demuxer->priv;
+ struct stream *stream = priv->stream;
struct stream_avseek cmd = {
.stream_index = stream_idx,
@@ -253,6 +264,34 @@ static void list_formats(struct demuxer *demuxer)
MP_INFO(demuxer, "%15s : %s\n", fmt->name, fmt->long_name);
}
+static void convert_charset(struct demuxer *demuxer)
+{
+ lavf_priv_t *priv = demuxer->priv;
+ char *cp = demuxer->opts->sub_cp;
+ if (!cp || mp_charset_is_utf8(cp))
+ return;
+ bstr data = stream_read_complete(priv->stream, NULL, 128 * 1024 * 1024);
+ if (!data.start) {
+ MP_WARN(demuxer, "File too big (or error reading) - skip charset probing.\n");
+ return;
+ }
+ void *alloc = data.start;
+ cp = (char *)mp_charset_guess(priv, demuxer->log, data, cp, 0);
+ if (cp && !mp_charset_is_utf8(cp))
+ MP_INFO(demuxer, "Using subtitle charset: %s\n", cp);
+ // libavformat transparently converts UTF-16 to UTF-8
+ if (!mp_charset_is_utf16(cp) && !mp_charset_is_utf8(cp)) {
+ bstr conv = mp_iconv_to_utf8(demuxer->log, data, cp, MP_ICONV_VERBOSE);
+ if (conv.start && conv.start != data.start)
+ talloc_steal(alloc, conv.start);
+ if (conv.start)
+ data = conv;
+ }
+ if (data.start)
+ priv->stream = open_memory_stream(data.start, data.len);
+ talloc_free(alloc);
+}
+
static char *remove_prefix(char *s, const char *const *prefixes)
{
for (int n = 0; prefixes[n]; n++) {
@@ -270,12 +309,8 @@ static int lavf_check_file(demuxer_t *demuxer, enum demux_check check)
{
struct MPOpts *opts = demuxer->opts;
struct demux_lavf_opts *lavfdopts = opts->demux_lavf;
- struct stream *s = demuxer->stream;
- lavf_priv_t *priv;
-
- assert(!demuxer->priv);
- demuxer->priv = talloc_zero(NULL, lavf_priv_t);
- priv = demuxer->priv;
+ lavf_priv_t *priv = demuxer->priv;
+ struct stream *s = priv->stream;
priv->filename = remove_prefix(s->url, prefixes);
@@ -292,7 +327,7 @@ static int lavf_check_file(demuxer_t *demuxer, enum demux_check check)
priv->filename = sep + 1;
}
- char *mime_type = demuxer->stream->mime_type;
+ char *mime_type = s->mime_type;
if (!lavfdopts->allow_mimetype || !mime_type)
mime_type = "";
@@ -393,6 +428,9 @@ static int lavf_check_file(demuxer_t *demuxer, enum demux_check check)
demuxer->filetype = priv->avif->name;
+ if (priv->format_hack.detect_charset)
+ convert_charset(demuxer);
+
return 0;
}
@@ -464,7 +502,8 @@ static void select_tracks(struct demuxer *demuxer, int start)
}
}
-static void export_replaygain(demuxer_t *demuxer, sh_audio_t *sh, AVStream *st)
+static void export_replaygain(demuxer_t *demuxer, struct mp_codec_params *c,
+ AVStream *st)
{
for (int i = 0; i < st->nb_side_data; i++) {
AVReplayGain *av_rgain;
@@ -489,7 +528,7 @@ static void export_replaygain(demuxer_t *demuxer, sh_audio_t *sh, AVStream *st)
rgain->album_peak = (av_rgain->album_peak != 0.0) ?
av_rgain->album_peak / 100000.0f : 1.0;
- sh->replaygain_data = rgain;
+ c->replaygain_data = rgain;
}
}
@@ -506,7 +545,7 @@ static int dict_get_decimal(AVDictionary *dict, const char *entry, int def)
return def;
}
-static void handle_stream(demuxer_t *demuxer, int i)
+static void handle_new_stream(demuxer_t *demuxer, int i)
{
lavf_priv_t *priv = demuxer->priv;
AVFormatContext *avfc = priv->avfc;
@@ -516,27 +555,26 @@ static void handle_stream(demuxer_t *demuxer, int i)
switch (codec->codec_type) {
case AVMEDIA_TYPE_AUDIO: {
- sh = new_sh_stream(demuxer, STREAM_AUDIO);
- if (!sh)
- break;
- sh_audio_t *sh_audio = sh->audio;
+ sh = demux_alloc_sh_stream(STREAM_AUDIO);
// probably unneeded
- mp_chmap_set_unknown(&sh_audio->channels, codec->channels);
+ mp_chmap_set_unknown(&sh->codec->channels, codec->channels);
if (codec->channel_layout)
- mp_chmap_from_lavc(&sh_audio->channels, codec->channel_layout);
- sh_audio->samplerate = codec->sample_rate;
- sh_audio->bitrate = codec->bit_rate;
+ mp_chmap_from_lavc(&sh->codec->channels, codec->channel_layout);
+ sh->codec->samplerate = codec->sample_rate;
+ sh->codec->bitrate = codec->bit_rate;
- export_replaygain(demuxer, sh_audio, st);
+ double delay = 0;
+ if (codec->sample_rate > 0)
+ delay = codec->delay / (double)codec->sample_rate;
+ priv->seek_delay = MPMAX(priv->seek_delay, delay);
+
+ export_replaygain(demuxer, sh->codec, st);
break;
}
case AVMEDIA_TYPE_VIDEO: {
- sh = new_sh_stream(demuxer, STREAM_VIDEO);
- if (!sh)
- break;
- sh_video_t *sh_video = sh->video;
+ sh = demux_alloc_sh_stream(STREAM_VIDEO);
if (st->disposition & AV_DISPOSITION_ATTACHED_PIC) {
sh->attached_picture = new_demux_packet_from(st->attached_pic.data,
@@ -548,8 +586,8 @@ static void handle_stream(demuxer_t *demuxer, int i)
}
}
- sh_video->disp_w = codec->width;
- sh_video->disp_h = codec->height;
+ sh->codec->disp_w = codec->width;
+ sh->codec->disp_h = codec->height;
/* Try to make up some frame rate value, even if it's not reliable.
* FPS information is needed to support subtitle formats which base
* timing on frame numbers.
@@ -564,39 +602,31 @@ static void handle_stream(demuxer_t *demuxer, int i)
fps = 1.0 / FFMAX(av_q2d(st->time_base),
av_q2d(st->codec->time_base) *
st->codec->ticks_per_frame);
- sh_video->fps = fps;
+ sh->codec->fps = fps;
if (priv->format_hack.image_format)
- sh_video->fps = demuxer->opts->mf_fps;
- if (st->sample_aspect_ratio.num)
- sh_video->aspect = codec->width * st->sample_aspect_ratio.num
- / (float)(codec->height * st->sample_aspect_ratio.den);
- else
- sh_video->aspect = codec->width * codec->sample_aspect_ratio.num
- / (float)(codec->height * codec->sample_aspect_ratio.den);
+ sh->codec->fps = demuxer->opts->mf_fps;
+ sh->codec->par_w = st->sample_aspect_ratio.num;
+ sh->codec->par_h = st->sample_aspect_ratio.den;
uint8_t *sd = av_stream_get_side_data(st, AV_PKT_DATA_DISPLAYMATRIX, NULL);
if (sd) {
double r = av_display_rotation_get((uint32_t *)sd);
if (!isnan(r))
- sh_video->rotate = (((int)(-r) % 360) + 360) % 360;
+ sh->codec->rotate = (((int)(-r) % 360) + 360) % 360;
}
// This also applies to vfw-muxed mkv, but we can't detect these easily.
- sh_video->avi_dts = matches_avinputformat_name(priv, "avi");
+ sh->codec->avi_dts = matches_avinputformat_name(priv, "avi");
break;
}
case AVMEDIA_TYPE_SUBTITLE: {
- sh_sub_t *sh_sub;
- sh = new_sh_stream(demuxer, STREAM_SUB);
- if (!sh)
- break;
- sh_sub = sh->sub;
+ sh = demux_alloc_sh_stream(STREAM_SUB);
if (codec->extradata_size) {
- sh->extradata = talloc_size(sh, codec->extradata_size);
- memcpy(sh->extradata, codec->extradata, codec->extradata_size);
- sh->extradata_size = codec->extradata_size;
+ sh->codec->extradata = talloc_size(sh, codec->extradata_size);
+ memcpy(sh->codec->extradata, codec->extradata, codec->extradata_size);
+ sh->codec->extradata_size = codec->extradata_size;
}
if (matches_avinputformat_name(priv, "microdvd")) {
@@ -604,18 +634,14 @@ static void handle_stream(demuxer_t *demuxer, int i)
if (av_opt_get_q(avfc, "subfps", AV_OPT_SEARCH_CHILDREN, &r) >= 0) {
// File headers don't have a FPS set.
if (r.num < 1 || r.den < 1)
- sh_sub->frame_based = av_q2d(av_inv_q(codec->time_base));
+ sh->codec->frame_based = av_q2d(av_inv_q(codec->time_base));
} else {
// Older libavformat versions. If the FPS matches the microdvd
// reader's default, assume it uses frame based timing.
if (codec->time_base.num == 125 && codec->time_base.den == 2997)
- sh_sub->frame_based = 23.976;
+ sh->codec->frame_based = 23.976;
}
}
-
- if (matches_avinputformat_name(priv, "ass"))
- sh_sub->is_utf8 = true;
-
break;
}
case AVMEDIA_TYPE_ATTACHMENT: {
@@ -637,12 +663,11 @@ static void handle_stream(demuxer_t *demuxer, int i)
if (sh) {
sh->ff_index = st->index;
- sh->codec = mp_codec_from_av_codec_id(codec->codec_id);
- sh->codec_tag = codec->codec_tag;
- sh->lav_headers = avcodec_alloc_context3(NULL);
- if (!sh->lav_headers)
- return;
- mp_copy_lav_codec_headers(sh->lav_headers, codec);
+ sh->codec->codec = mp_codec_from_av_codec_id(codec->codec_id);
+ sh->codec->codec_tag = codec->codec_tag;
+ sh->codec->lav_headers = avcodec_alloc_context3(NULL);
+ if (sh->codec->lav_headers)
+ mp_copy_lav_codec_headers(sh->codec->lav_headers, codec);
if (st->disposition & AV_DISPOSITION_DEFAULT)
sh->default_track = true;
@@ -660,10 +685,10 @@ static void handle_stream(demuxer_t *demuxer, int i)
if (!sh->title && sh->hls_bitrate > 0)
sh->title = talloc_asprintf(sh, "bitrate %d", sh->hls_bitrate);
sh->missing_timestamps = !!(priv->avif_flags & AVFMT_NOTIMESTAMPS);
+ demux_add_sh_stream(demuxer, sh);
}
select_tracks(demuxer, i);
- demux_changed(demuxer, DEMUX_EVENT_STREAMS);
}
// Add any new streams that might have been added
@@ -671,7 +696,7 @@ static void add_new_streams(demuxer_t *demuxer)
{
lavf_priv_t *priv = demuxer->priv;
while (priv->num_streams < priv->avfc->nb_streams)
- handle_stream(demuxer, priv->num_streams);
+ handle_new_stream(demuxer, priv->num_streams);
}
static void update_metadata(demuxer_t *demuxer, AVPacket *pkt)
@@ -697,7 +722,8 @@ static void update_metadata(demuxer_t *demuxer, AVPacket *pkt)
static int interrupt_cb(void *ctx)
{
struct demuxer *demuxer = ctx;
- return mp_cancel_test(demuxer->stream->cancel);
+ lavf_priv_t *priv = demuxer->priv;
+ return mp_cancel_test(priv->stream->cancel);
}
static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
@@ -707,15 +733,13 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
AVFormatContext *avfc;
AVDictionaryEntry *t = NULL;
float analyze_duration = 0;
- int i;
+ lavf_priv_t *priv = talloc_zero(NULL, lavf_priv_t);
+ demuxer->priv = priv;
+ priv->stream = demuxer->stream;
if (lavf_check_file(demuxer, check) < 0)
return -1;
- lavf_priv_t *priv = demuxer->priv;
- if (!priv)
- return -1;
-
avfc = avformat_alloc_context();
if (!avfc)
return -1;
@@ -754,7 +778,7 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
AVDictionary *dopts = NULL;
if ((priv->avif_flags & AVFMT_NOFILE) ||
- demuxer->stream->type == STREAMTYPE_AVDEVICE ||
+ priv->stream->type == STREAMTYPE_AVDEVICE ||
priv->format_hack.no_stream)
{
mp_setup_av_network_options(&dopts, demuxer->global, demuxer->log, opts);
@@ -773,7 +797,7 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
priv->pb->read_seek = mp_read_seek;
priv->pb->seekable = demuxer->seekable ? AVIO_SEEKABLE_NORMAL : 0;
avfc->pb = priv->pb;
- if (stream_control(demuxer->stream, STREAM_CTRL_HAS_AVSEEK, NULL) > 0)
+ if (stream_control(priv->stream, STREAM_CTRL_HAS_AVSEEK, NULL) > 0)
demuxer->seekable = true;
}
@@ -813,9 +837,9 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
}
MP_VERBOSE(demuxer, "avformat_find_stream_info() finished after %"PRId64
- " bytes.\n", stream_tell(demuxer->stream));
+ " bytes.\n", stream_tell(priv->stream));
- for (i = 0; i < avfc->nb_chapters; i++) {
+ for (int i = 0; i < avfc->nb_chapters; i++) {
AVChapter *c = avfc->chapters[i];
t = av_dict_get(c->metadata, "title", NULL, 0);
int index = demuxer_add_chapter(demuxer, t ? t->value : "",
@@ -827,7 +851,7 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
// Often useful with OGG audio-only files, which have metadata in the audio
// track metadata instead of the main metadata.
- if (demuxer->num_streams == 1) {
+ if (demux_get_num_stream(demuxer) == 1) {
priv->merge_track_metadata = true;
for (int n = 0; n < priv->num_streams; n++) {
if (priv->streams[n])
@@ -895,11 +919,6 @@ static int demux_lavf_fill_buffer(demuxer_t *demux)
#endif
dp->pos = pkt->pos;
dp->keyframe = pkt->flags & AV_PKT_FLAG_KEY;
- if (dp->pts != MP_NOPTS_VALUE) {
- priv->last_pts = dp->pts * AV_TIME_BASE;
- } else if (dp->dts != MP_NOPTS_VALUE) {
- priv->last_pts = dp->dts * AV_TIME_BASE;
- }
av_packet_unref(pkt);
if (priv->format_hack.clear_filepos)
@@ -909,48 +928,44 @@ static int demux_lavf_fill_buffer(demuxer_t *demux)
return 1;
}
-static void demux_seek_lavf(demuxer_t *demuxer, double rel_seek_secs, int flags)
+static void demux_seek_lavf(demuxer_t *demuxer, double seek_pts, int flags)
{
lavf_priv_t *priv = demuxer->priv;
int avsflags = 0;
+ int64_t seek_pts_av = 0;
- if (flags & SEEK_ABSOLUTE)
- priv->last_pts = 0;
- else if (rel_seek_secs < 0)
- avsflags = AVSEEK_FLAG_BACKWARD;
-
- if (flags & SEEK_FORWARD)
- avsflags = 0;
- else if (flags & SEEK_BACKWARD)
+ if (flags & SEEK_BACKWARD)
avsflags = AVSEEK_FLAG_BACKWARD;
if (flags & SEEK_FACTOR) {
- struct stream *s = demuxer->stream;
+ struct stream *s = priv->stream;
int64_t end = stream_get_size(s);
if (end > 0 && demuxer->ts_resets_possible &&
!(priv->avif_flags & AVFMT_NO_BYTE_SEEK))
{
avsflags |= AVSEEK_FLAG_BYTE;
- priv->last_pts = end * rel_seek_secs;
+ seek_pts_av = end * seek_pts;
} else if (priv->avfc->duration != 0 &&
priv->avfc->duration != AV_NOPTS_VALUE)
{
- priv->last_pts = rel_seek_secs * priv->avfc->duration;
+ seek_pts_av = seek_pts * priv->avfc->duration;
}
} else {
- priv->last_pts += rel_seek_secs * AV_TIME_BASE;
+ if (flags & SEEK_BACKWARD)
+ seek_pts -= priv->seek_delay;
+ seek_pts_av = seek_pts * AV_TIME_BASE;
}
int r;
if (!priv->avfc->iformat->read_seek2) {
// Normal seeking.
- r = av_seek_frame(priv->avfc, -1, priv->last_pts, avsflags);
+ r = av_seek_frame(priv->avfc, -1, seek_pts_av, avsflags);
if (r < 0 && (avsflags & AVSEEK_FLAG_BACKWARD)) {
// When seeking before the beginning of the file, and seeking fails,
// try again without the backwards flag to make it seek to the
// beginning.
avsflags &= ~AVSEEK_FLAG_BACKWARD;
- r = av_seek_frame(priv->avfc, -1, priv->last_pts, avsflags);
+ r = av_seek_frame(priv->avfc, -1, seek_pts_av, avsflags);
}
} else {
// av_seek_frame() won't work. Use "new" seeking API. We don't use this
@@ -958,11 +973,11 @@ static void demux_seek_lavf(demuxer_t *demuxer, double rel_seek_secs, int flags)
// Set max_ts==ts, so that demuxing starts from an earlier position in
// the worst case.
r = avformat_seek_file(priv->avfc, -1, INT64_MIN,
- priv->last_pts, priv->last_pts, avsflags);
+ seek_pts_av, seek_pts_av, avsflags);
// Similar issue as in the normal seeking codepath.
if (r < 0) {
r = avformat_seek_file(priv->avfc, -1, INT64_MIN,
- priv->last_pts, INT64_MAX, avsflags);
+ seek_pts_av, INT64_MAX, avsflags);
}
}
if (r < 0) {
@@ -1045,6 +1060,12 @@ redo:
goto redo;
}
priv->cur_program = prog->progid = program->id;
+
+ mp_tags_copy_from_av_dictionary(demuxer->metadata, priv->avfc->programs[p]->metadata);
+ update_metadata(demuxer, NULL);
+ // Enforce metadata update even if no explicit METADATA_UPDATED since we switched program.
+ demux_changed(demuxer, DEMUX_EVENT_METADATA);
+
return DEMUXER_CTRL_OK;
}
case DEMUXER_CTRL_RESYNC:
@@ -1061,10 +1082,10 @@ redo:
*/
// avio_flush() is designed for write-only streams, and does the wrong
// thing when reading. Flush it manually instead.
- stream_drop_buffers(demuxer->stream);
+ stream_drop_buffers(priv->stream);
priv->avfc->pb->buf_ptr = priv->avfc->pb->buf_end = priv->avfc->pb->buffer;
- priv->avfc->pb->pos = stream_tell(demuxer->stream);
- av_seek_frame(priv->avfc, 0, stream_tell(demuxer->stream),
+ priv->avfc->pb->pos = stream_tell(priv->stream);
+ av_seek_frame(priv->avfc, 0, stream_tell(priv->stream),
AVSEEK_FLAG_BYTE);
return DEMUXER_CTRL_OK;
default:
@@ -1085,8 +1106,10 @@ static void demux_close_lavf(demuxer_t *demuxer)
av_freep(&priv->pb);
for (int n = 0; n < priv->num_streams; n++) {
if (priv->streams[n])
- avcodec_free_context(&priv->streams[n]->lav_headers);
+ avcodec_free_context(&priv->streams[n]->codec->lav_headers);
}
+ if (priv->stream != demuxer->stream)
+ free_stream(priv->stream);
talloc_free(priv);
demuxer->priv = NULL;
}
diff --git a/demux/demux_mf.c b/demux/demux_mf.c
index ab703c0..c0b159e 100644
--- a/demux/demux_mf.c
+++ b/demux/demux_mf.c
@@ -24,7 +24,7 @@
#include "osdep/io.h"
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "common/msg.h"
#include "options/options.h"
#include "options/path.h"
@@ -39,7 +39,7 @@
typedef struct mf {
struct mp_log *log;
- struct sh_video *sh;
+ struct sh_stream *sh;
int curr_frame;
int nr_of_files;
char **names;
@@ -108,6 +108,7 @@ static mf_t *open_mf_pattern(void *talloc_ctx, struct mp_log *log, char *filenam
char *fname = talloc_size(mf, strlen(filename) + 32);
+#if HAVE_GLOB || HAVE_GLOB_WIN32_REPLACEMENT
if (!strchr(filename, '%')) {
strcpy(fname, filename);
if (!strchr(filename, '*'))
@@ -130,6 +131,7 @@ static mf_t *open_mf_pattern(void *talloc_ctx, struct mp_log *log, char *filenam
globfree(&gg);
goto exit_mf;
}
+#endif
mp_info(log, "search expr: %s\n", filename);
@@ -157,15 +159,12 @@ static mf_t *open_mf_single(void *talloc_ctx, struct mp_log *log, char *filename
return mf;
}
-static void demux_seek_mf(demuxer_t *demuxer, double rel_seek_secs, int flags)
+static void demux_seek_mf(demuxer_t *demuxer, double seek_pts, int flags)
{
mf_t *mf = demuxer->priv;
- int newpos = (flags & SEEK_ABSOLUTE) ? 0 : mf->curr_frame - 1;
-
+ int newpos = seek_pts * mf->sh->codec->fps;
if (flags & SEEK_FACTOR)
- newpos += rel_seek_secs * (mf->nr_of_files - 1);
- else
- newpos += rel_seek_secs * mf->sh->fps;
+ newpos = seek_pts * (mf->nr_of_files - 1);
if (newpos < 0)
newpos = 0;
if (newpos >= mf->nr_of_files)
@@ -199,9 +198,9 @@ static int demux_mf_fill_buffer(demuxer_t *demuxer)
demux_packet_t *dp = new_demux_packet(data.len);
if (dp) {
memcpy(dp->buffer, data.start, data.len);
- dp->pts = mf->curr_frame / mf->sh->fps;
+ dp->pts = mf->curr_frame / mf->sh->codec->fps;
dp->keyframe = true;
- demux_add_packet(demuxer->streams[0], dp);
+ demux_add_packet(mf->sh, dp);
}
}
talloc_free(data.start);
@@ -291,7 +290,6 @@ static const char *probe_format(mf_t *mf, char *type, enum demux_check check)
static int demux_open_mf(demuxer_t *demuxer, enum demux_check check)
{
- sh_video_t *sh_video = NULL;
mf_t *mf;
if (strncmp(demuxer->stream->url, "mf://", 5) == 0 &&
@@ -316,15 +314,17 @@ static int demux_open_mf(demuxer_t *demuxer, enum demux_check check)
mf->curr_frame = 0;
// create a new video stream header
- struct sh_stream *sh = new_sh_stream(demuxer, STREAM_VIDEO);
- sh_video = sh->video;
+ struct sh_stream *sh = demux_alloc_sh_stream(STREAM_VIDEO);
+ struct mp_codec_params *c = sh->codec;
+
+ c->codec = codec;
+ c->disp_w = 0;
+ c->disp_h = 0;
+ c->fps = demuxer->opts->mf_fps;
- sh->codec = codec;
- sh_video->disp_w = 0;
- sh_video->disp_h = 0;
- sh_video->fps = demuxer->opts->mf_fps;
+ demux_add_sh_stream(demuxer, sh);
- mf->sh = sh_video;
+ mf->sh = sh;
demuxer->priv = (void *)mf;
demuxer->seekable = true;
@@ -344,7 +344,7 @@ static int demux_control_mf(demuxer_t *demuxer, int cmd, void *arg)
switch (cmd) {
case DEMUXER_CTRL_GET_TIME_LENGTH:
- *((double *)arg) = (double)mf->nr_of_files / mf->sh->fps;
+ *((double *)arg) = (double)mf->nr_of_files / mf->sh->codec->fps;
return DEMUXER_CTRL_OK;
default:
diff --git a/demux/demux_mkv.c b/demux/demux_mkv.c
index a11691b..c932b45 100644
--- a/demux/demux_mkv.c
+++ b/demux/demux_mkv.c
@@ -41,13 +41,14 @@
#include <zlib.h>
#endif
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "common/av_common.h"
#include "options/options.h"
#include "options/m_option.h"
#include "misc/bstr.h"
#include "stream/stream.h"
#include "video/csputils.h"
+#include "video/mp_image.h"
#include "demux.h"
#include "stheader.h"
#include "ebml.h"
@@ -145,14 +146,14 @@ typedef struct mkv_track {
typedef struct mkv_index {
int tnum;
- uint64_t timecode, duration;
+ int64_t timecode, duration;
uint64_t filepos; // position of the cluster which contains the packet
} mkv_index_t;
struct block_info {
uint64_t duration, discardpadding;
bool simple, keyframe;
- uint64_t timecode;
+ int64_t timecode;
mkv_track_t *track;
bstr data;
void *alloc;
@@ -162,14 +163,14 @@ struct block_info {
typedef struct mkv_demuxer {
int64_t segment_start, segment_end;
- double duration, last_pts;
+ double duration;
mkv_track_t **tracks;
int num_tracks;
struct ebml_tags *tags;
- uint64_t tc_scale, cluster_tc;
+ int64_t tc_scale, cluster_tc;
uint64_t cluster_start;
uint64_t cluster_end;
@@ -185,7 +186,7 @@ typedef struct mkv_demuxer {
} *headers;
int num_headers;
- uint64_t skip_to_timecode;
+ int64_t skip_to_timecode;
int v_skip_to_keyframe, a_skip_to_keyframe;
int a_skip_preroll;
int subtitle_preroll;
@@ -201,15 +202,19 @@ typedef struct mkv_demuxer {
struct demux_mkv_opts {
int subtitle_preroll;
double subtitle_preroll_secs;
+ double subtitle_preroll_secs_index;
int probe_duration;
int probe_start_time;
};
const struct m_sub_options demux_mkv_conf = {
.opts = (const m_option_t[]) {
- OPT_FLAG("subtitle-preroll", subtitle_preroll, 0),
+ OPT_CHOICE("subtitle-preroll", subtitle_preroll, 0,
+ ({"no", 0}, {"yes", 1}, {"index", 2})),
OPT_DOUBLE("subtitle-preroll-secs", subtitle_preroll_secs,
M_OPT_MIN, .min = 0),
+ OPT_DOUBLE("subtitle-preroll-secs-index", subtitle_preroll_secs_index,
+ M_OPT_MIN, .min = 0),
OPT_CHOICE("probe-video-duration", probe_duration, 0,
({"no", 0}, {"yes", 1}, {"full", 2})),
OPT_FLAG("probe-start-time", probe_start_time, 0),
@@ -217,7 +222,9 @@ const struct m_sub_options demux_mkv_conf = {
},
.size = sizeof(struct demux_mkv_opts),
.defaults = &(const struct demux_mkv_opts){
+ .subtitle_preroll = 2,
.subtitle_preroll_secs = 1.0,
+ .subtitle_preroll_secs_index = 10.0,
.probe_start_time = 1,
},
};
@@ -699,7 +706,7 @@ static int demux_mkv_read_tracks(demuxer_t *demuxer)
}
static void cue_index_add(demuxer_t *demuxer, int track_id, uint64_t filepos,
- uint64_t timecode, uint64_t duration)
+ int64_t timecode, int64_t duration)
{
mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv;
@@ -717,7 +724,7 @@ static void cue_index_add(demuxer_t *demuxer, int track_id, uint64_t filepos,
static void add_block_position(demuxer_t *demuxer, struct mkv_track *track,
uint64_t filepos,
- uint64_t timecode, uint64_t duration)
+ int64_t timecode, int64_t duration)
{
mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv;
@@ -781,8 +788,8 @@ static int demux_mkv_read_cues(demuxer_t *demuxer)
time, trackpos->cue_duration);
mkv_d->index_has_durations |= trackpos->n_cue_duration > 0;
MP_DBG(demuxer, "|+ found cue point for track %" PRIu64
- ": timecode %" PRIu64 ", filepos: %" PRIu64
- " offset %" PRIu64 ", duration %" PRIu64 "\n",
+ ": timecode %" PRId64 ", filepos: %" PRIu64
+ " offset %" PRIu64 ", duration %" PRId64 "\n",
trackpos->cue_track, time, pos,
trackpos->cue_relative_position, trackpos->cue_duration);
}
@@ -1192,11 +1199,9 @@ static void add_coverart(struct demuxer *demuxer)
const char *codec = mp_map_mimetype_to_video_codec(att->type);
if (!codec)
continue;
- struct sh_stream *sh = new_sh_stream(demuxer, STREAM_VIDEO);
- if (!sh)
- break;
+ struct sh_stream *sh = demux_alloc_sh_stream(STREAM_VIDEO);
sh->demuxer_id = -1 - sh->index; // don't clash with mkv IDs
- sh->codec = codec;
+ sh->codec->codec = codec;
sh->attached_picture = new_demux_packet_from(att->data, att->data_size);
if (sh->attached_picture) {
sh->attached_picture->pts = 0;
@@ -1204,6 +1209,7 @@ static void add_coverart(struct demuxer *demuxer)
sh->attached_picture->keyframe = true;
}
sh->title = att->name;
+ demux_add_sh_stream(demuxer, sh);
}
}
@@ -1265,18 +1271,16 @@ static int demux_mkv_open_video(demuxer_t *demuxer, mkv_track_t *track)
{
unsigned char *extradata = NULL;
unsigned int extradata_size = 0;
- struct sh_stream *sh = new_sh_stream(demuxer, STREAM_VIDEO);
- if (!sh)
- return 1;
+ struct sh_stream *sh = demux_alloc_sh_stream(STREAM_VIDEO);
init_track(demuxer, track, sh);
- sh_video_t *sh_v = sh->video;
+ struct mp_codec_params *sh_v = sh->codec;
sh_v->bits_per_coded_sample = 24;
if (!strcmp(track->codec_id, "V_MS/VFW/FOURCC")) { /* AVI compatibility mode */
// The private_data contains a BITMAPINFOHEADER struct
if (track->private_data == NULL || track->private_size < 40)
- return 1;
+ goto done;
unsigned char *h = track->private_data;
if (track->v_width == 0)
@@ -1284,11 +1288,11 @@ static int demux_mkv_open_video(demuxer_t *demuxer, mkv_track_t *track)
if (track->v_height == 0)
track->v_height = AV_RL32(h + 8); // biHeight
sh_v->bits_per_coded_sample = AV_RL16(h + 14); // biBitCount
- sh->codec_tag = AV_RL32(h + 16); // biCompression
+ sh_v->codec_tag = AV_RL32(h + 16); // biCompression
extradata = track->private_data + 40;
extradata_size = track->private_size - 40;
- mp_set_codec_from_tag(sh);
+ mp_set_codec_from_tag(sh_v);
sh_v->avi_dts = true;
} else if (track->private_size >= RVPROPERTIES_SIZE
&& (!strcmp(track->codec_id, "V_REAL/RV10")
@@ -1304,10 +1308,10 @@ static int demux_mkv_open_video(demuxer_t *demuxer, mkv_track_t *track)
cnt = track->private_size - RVPROPERTIES_SIZE;
uint32_t t2 = AV_RB32(src - 4);
switch (t2 == 0x10003000 || t2 == 0x10003001 ? '1' : track->codec_id[9]) {
- case '1': sh->codec = "rv10"; break;
- case '2': sh->codec = "rv20"; break;
- case '3': sh->codec = "rv30"; break;
- case '4': sh->codec = "rv40"; break;
+ case '1': sh_v->codec = "rv10"; break;
+ case '2': sh_v->codec = "rv20"; break;
+ case '3': sh_v->codec = "rv30"; break;
+ case '4': sh_v->codec = "rv40"; break;
}
// copy type1 and type2 info from rv properties
extradata_size = cnt + 8;
@@ -1316,8 +1320,8 @@ static int demux_mkv_open_video(demuxer_t *demuxer, mkv_track_t *track)
track->parse_timebase = 1e3;
} else if (strcmp(track->codec_id, "V_UNCOMPRESSED") == 0) {
// raw video, "like AVI" - this is a FourCC
- sh->codec_tag = track->colorspace;
- sh->codec = "rawvideo";
+ sh_v->codec_tag = track->colorspace;
+ sh_v->codec = "rawvideo";
} else if (strcmp(track->codec_id, "V_QUICKTIME") == 0) {
uint32_t fourcc1 = 0, fourcc2 = 0;
if (track->private_size >= 8) {
@@ -1327,14 +1331,14 @@ static int demux_mkv_open_video(demuxer_t *demuxer, mkv_track_t *track)
if (fourcc1 == MP_FOURCC('S', 'V', 'Q', '3') ||
fourcc2 == MP_FOURCC('S', 'V', 'Q', '3'))
{
- sh->codec = "svq3";
+ sh_v->codec = "svq3";
extradata = track->private_data;
extradata_size = track->private_size;
}
} else {
for (int i = 0; mkv_video_tags[i][0]; i++) {
if (!strcmp(mkv_video_tags[i][0], track->codec_id)) {
- sh->codec = mkv_video_tags[i][1];
+ sh_v->codec = mkv_video_tags[i][1];
break;
}
}
@@ -1344,22 +1348,22 @@ static int demux_mkv_open_video(demuxer_t *demuxer, mkv_track_t *track)
}
}
- const char *codec = sh->codec ? sh->codec : "";
+ const char *codec = sh_v->codec ? sh_v->codec : "";
if (!strcmp(codec, "vp9")) {
track->parse = true;
track->parse_timebase = 1e9;
} else if (!strcmp(codec, "mjpeg")) {
- sh->codec_tag = MP_FOURCC('m', 'j', 'p', 'g');
+ sh_v->codec_tag = MP_FOURCC('m', 'j', 'p', 'g');
}
if (extradata_size > 0x1000000) {
MP_WARN(demuxer, "Invalid CodecPrivate\n");
- return 1;
+ goto done;
}
- sh->extradata = talloc_memdup(sh_v, extradata, extradata_size);
- sh->extradata_size = extradata_size;
- if (!sh->codec) {
+ sh_v->extradata = talloc_memdup(sh_v, extradata, extradata_size);
+ sh_v->extradata_size = extradata_size;
+ if (!sh_v->codec) {
MP_WARN(demuxer, "Unknown/unsupported CodecID (%s) or missing/bad "
"CodecPrivate data (track %u).\n",
track->codec_id, track->tnum);
@@ -1367,12 +1371,19 @@ static int demux_mkv_open_video(demuxer_t *demuxer, mkv_track_t *track)
sh_v->fps = track->v_frate;
sh_v->disp_w = track->v_width;
sh_v->disp_h = track->v_height;
- uint32_t dw = track->v_dwidth_set ? track->v_dwidth : track->v_width;
- uint32_t dh = track->v_dheight_set ? track->v_dheight : track->v_height;
- sh_v->aspect = (dw && dh) ? (double) dw / dh : 0;
- MP_VERBOSE(demuxer, "Aspect: %f\n", sh_v->aspect);
+
+ int dw = track->v_dwidth_set ? track->v_dwidth : track->v_width;
+ int dh = track->v_dheight_set ? track->v_dheight : track->v_height;
+ struct mp_image_params p = {.w = track->v_width, .h = track->v_height};
+ mp_image_params_set_dsize(&p, dw, dh);
+ sh_v->par_w = p.p_w;
+ sh_v->par_h = p.p_h;
+
sh_v->stereo_mode = track->stereo_mode;
+done:
+ demux_add_sh_stream(demuxer, sh);
+
return 0;
}
@@ -1464,17 +1475,16 @@ static const char *const mkv_audio_tags[][2] = {
static int demux_mkv_open_audio(demuxer_t *demuxer, mkv_track_t *track)
{
- struct sh_stream *sh = new_sh_stream(demuxer, STREAM_AUDIO);
- if (!sh)
- return 1;
+ struct sh_stream *sh = demux_alloc_sh_stream(STREAM_AUDIO);
init_track(demuxer, track, sh);
- sh_audio_t *sh_a = sh->audio;
+ struct mp_codec_params *sh_a = sh->codec;
if (track->private_size > 0x1000000)
goto error;
unsigned char *extradata = track->private_data;
unsigned int extradata_len = track->private_size;
+ uint64_t chmask = 0;
if (!track->a_osfreq)
track->a_osfreq = track->a_sfreq;
@@ -1483,7 +1493,7 @@ static int demux_mkv_open_audio(demuxer_t *demuxer, mkv_track_t *track)
for (int i = 0; mkv_audio_tags[i][0]; i++) {
if (!strcmp(mkv_audio_tags[i][0], track->codec_id)) {
- sh->codec = mkv_audio_tags[i][1];
+ sh_a->codec = mkv_audio_tags[i][1];
break;
}
}
@@ -1494,7 +1504,7 @@ static int demux_mkv_open_audio(demuxer_t *demuxer, mkv_track_t *track)
goto error;
MP_VERBOSE(demuxer, "track with MS compat audio.\n");
unsigned char *h = track->private_data;
- sh->codec_tag = AV_RL16(h + 0); // wFormatTag
+ sh_a->codec_tag = AV_RL16(h + 0); // wFormatTag
if (track->a_channels == 0)
track->a_channels = AV_RL16(h + 2); // nChannels
if (sh_a->samplerate == 0)
@@ -1506,15 +1516,18 @@ static int demux_mkv_open_audio(demuxer_t *demuxer, mkv_track_t *track)
extradata = track->private_data + 18;
extradata_len = track->private_size - 18;
sh_a->bits_per_coded_sample = track->a_bps;
- mp_set_codec_from_tag(sh);
+ mp_set_codec_from_tag(sh_a);
+ // WAVEFORMATEXTENSIBLE.dwChannelMask
+ if (sh_a->codec_tag == 0xfffe && extradata_len >= 6)
+ chmask = AV_RL32(extradata + 2);
} else if (!strcmp(track->codec_id, "A_PCM/INT/LIT")) {
bool sign = sh_a->bits_per_coded_sample > 8;
- mp_set_pcm_codec(sh, sign, false, sh_a->bits_per_coded_sample, false);
+ mp_set_pcm_codec(sh_a, sign, false, sh_a->bits_per_coded_sample, false);
} else if (!strcmp(track->codec_id, "A_PCM/INT/BIG")) {
bool sign = sh_a->bits_per_coded_sample > 8;
- mp_set_pcm_codec(sh, sign, false, sh_a->bits_per_coded_sample, true);
+ mp_set_pcm_codec(sh_a, sign, false, sh_a->bits_per_coded_sample, true);
} else if (!strcmp(track->codec_id, "A_PCM/FLOAT/IEEE")) {
- sh->codec = sh_a->bits_per_coded_sample == 64 ? "pcm_f64le" : "pcm_f32le";
+ sh_a->codec = sh_a->bits_per_coded_sample == 64 ? "pcm_f64le" : "pcm_f32le";
} else if (!strncmp(track->codec_id, "A_REAL/", 7)) {
if (track->private_size < RAPROPERTIES4_SIZE)
goto error;
@@ -1554,29 +1567,29 @@ static int demux_mkv_open_audio(demuxer_t *demuxer, mkv_track_t *track)
extradata = src + offset;
if (!strcmp(track->codec_id, "A_REAL/ATRC")) {
- sh->codec = "atrac3";
+ sh_a->codec = "atrac3";
if (flavor >= MP_ARRAY_SIZE(atrc_fl2bps))
goto error;
sh_a->bitrate = atrc_fl2bps[flavor] * 8;
sh_a->block_align = track->sub_packet_size;
} else if (!strcmp(track->codec_id, "A_REAL/COOK")) {
- sh->codec = "cook";
+ sh_a->codec = "cook";
if (flavor >= MP_ARRAY_SIZE(cook_fl2bps))
goto error;
sh_a->bitrate = cook_fl2bps[flavor] * 8;
sh_a->block_align = track->sub_packet_size;
} else if (!strcmp(track->codec_id, "A_REAL/SIPR")) {
- sh->codec = "sipr";
+ sh_a->codec = "sipr";
if (flavor >= MP_ARRAY_SIZE(sipr_fl2bps))
goto error;
sh_a->bitrate = sipr_fl2bps[flavor] * 8;
sh_a->block_align = track->coded_framesize;
} else if (!strcmp(track->codec_id, "A_REAL/28_8")) {
- sh->codec = "ra_288";
+ sh_a->codec = "ra_288";
sh_a->bitrate = 3600 * 8;
sh_a->block_align = track->coded_framesize;
} else if (!strcmp(track->codec_id, "A_REAL/DNET")) {
- sh->codec = "ac3";
+ sh_a->codec = "ac3";
} else {
goto error;
}
@@ -1586,7 +1599,7 @@ static int demux_mkv_open_audio(demuxer_t *demuxer, mkv_track_t *track)
track->audio_timestamp =
talloc_array(track, double, track->sub_packet_h);
} else if (!strncmp(track->codec_id, "A_AAC/", 6)) {
- sh->codec = "aac";
+ sh_a->codec = "aac";
/* Recreate the 'private data' (not needed for plain A_AAC) */
int srate_idx = aac_get_sample_rate_index(track->a_sfreq);
@@ -1618,17 +1631,19 @@ static int demux_mkv_open_audio(demuxer_t *demuxer, mkv_track_t *track)
track->default_duration = 1024.0 / sh_a->samplerate;
}
} else if (!strncmp(track->codec_id, "A_AC3/", 6)) {
- sh->codec = "ac3";
+ sh_a->codec = "ac3";
} else if (!strncmp(track->codec_id, "A_EAC3/", 7)) {
- sh->codec = "eac3";
+ sh_a->codec = "eac3";
}
- if (!sh->codec)
+ if (!sh_a->codec)
goto error;
- mp_chmap_set_unknown(&sh_a->channels, track->a_channels);
+ mp_chmap_from_waveext(&sh_a->channels, chmask);
+ if (sh_a->channels.num != track->a_channels)
+ mp_chmap_set_unknown(&sh_a->channels, track->a_channels);
- const char *codec = sh->codec;
+ const char *codec = sh_a->codec;
if (!strcmp(codec, "mp3") || !strcmp(codec, "truehd")) {
track->parse = true;
} else if (!strcmp(codec, "flac")) {
@@ -1664,16 +1679,22 @@ static int demux_mkv_open_audio(demuxer_t *demuxer, mkv_track_t *track)
AV_WL32(data + 10, track->a_osfreq);
// Bogus: last frame won't be played.
AV_WL32(data + 14, 0);
+ } else if (!strcmp(codec, "opus")) {
+ // Hardcode the rate libavcodec's opus decoder outputs, so that
+ // AV_PKT_DATA_SKIP_SAMPLES actually works. The Matroska header only
+ // has an arbitrary "input" samplerate, while libavcodec is fixed to
+ // output 48000.
+ sh_a->samplerate = 48000;
}
- // Some files have broken default DefaultDuration set, which will lead to
- // audio packets with incorrect timestamps. This follows FFmpeg commit
- // 6158a3b, sample see FFmpeg ticket 2508.
- if (sh_a->samplerate == 8000 && strcmp(codec, "ac3") == 0)
- track->default_duration = 0;
+ // This field tends to be broken, and our decoder can interpolate the
+ // missing timestamps anyway.
+ track->default_duration = 0;
- sh->extradata = extradata;
- sh->extradata_size = extradata_len;
+ sh_a->extradata = extradata;
+ sh_a->extradata_size = extradata_len;
+
+ demux_add_sh_stream(demuxer, sh);
return 0;
@@ -1681,6 +1702,7 @@ static int demux_mkv_open_audio(demuxer_t *demuxer, mkv_track_t *track)
MP_WARN(demuxer, "Unknown/unsupported audio "
"codec ID '%s' for track %u or missing/faulty\n"
"private codec data.\n", track->codec_id, track->tnum);
+ demux_add_sh_stream(demuxer, sh); // add it anyway
return 1;
}
@@ -1712,12 +1734,10 @@ static int demux_mkv_open_sub(demuxer_t *demuxer, mkv_track_t *track)
if (track->private_size > 0x10000000)
return 1;
- struct sh_stream *sh = new_sh_stream(demuxer, STREAM_SUB);
- if (!sh)
- return 1;
+ struct sh_stream *sh = demux_alloc_sh_stream(STREAM_SUB);
init_track(demuxer, track, sh);
- sh->codec = subtitle_type;
+ sh->codec->codec = subtitle_type;
bstr in = (bstr){track->private_data, track->private_size};
bstr buffer = demux_mkv_decode(demuxer->log, track, in, 2);
if (buffer.start && buffer.start != track->private_data) {
@@ -1726,8 +1746,10 @@ static int demux_mkv_open_sub(demuxer_t *demuxer, mkv_track_t *track)
track->private_data = buffer.start;
track->private_size = buffer.len;
}
- sh->extradata = track->private_data;
- sh->extradata_size = track->private_size;
+ sh->codec->extradata = track->private_data;
+ sh->codec->extradata_size = track->private_size;
+
+ demux_add_sh_stream(demuxer, sh);
if (!subtitle_type)
MP_ERR(demuxer, "Subtitle type '%s' is not supported.\n", track->codec_id);
@@ -1842,6 +1864,7 @@ static int demux_mkv_open(demuxer_t *demuxer, enum demux_check check)
mkv_d->segment_start = stream_tell(s);
mkv_d->segment_end = end_pos;
mkv_d->a_skip_preroll = 1;
+ mkv_d->skip_to_timecode = INT64_MIN;
if (demuxer->params && demuxer->params->matroska_was_valid)
*demuxer->params->matroska_was_valid = true;
@@ -2015,7 +2038,7 @@ static bool handle_realaudio(demuxer_t *demuxer, mkv_track_t *track,
if (!track->audio_buf || !track->audio_timestamp || !track->stream)
return false;
- const char *codec = track->stream->codec ? track->stream->codec : "";
+ const char *codec = track->stream->codec->codec ? track->stream->codec->codec : "";
if (!strcmp(codec, "ra_288")) {
for (int x = 0; x < sph / 2; x++) {
uint64_t dst_offset = x * 2 * w + spc * (uint64_t)cfs;
@@ -2075,7 +2098,7 @@ static bool handle_realaudio(demuxer_t *demuxer, mkv_track_t *track,
if (++(track->sub_packet_cnt) == sph) {
track->sub_packet_cnt = 0;
// apk_usize has same range as coded_framesize in worst case
- uint32_t apk_usize = track->stream->audio->block_align;
+ uint32_t apk_usize = track->stream->codec->block_align;
if (apk_usize > audiobuf_size)
goto error;
// Release all the audio packets
@@ -2116,6 +2139,8 @@ static void mkv_seek_reset(demuxer_t *demuxer)
}
free_block(&mkv_d->tmp_block);
+
+ mkv_d->skip_to_timecode = INT64_MIN;
}
// Copied from libavformat/matroskadec.c (FFmpeg 310f9dd / 2013-05-30)
@@ -2210,7 +2235,7 @@ static void mkv_parse_and_add_packet(demuxer_t *demuxer, mkv_track_t *track,
if (stream->type == STREAM_AUDIO && handle_realaudio(demuxer, track, dp))
return;
- if (stream->codec && strcmp(stream->codec, "wavpack") == 0) {
+ if (strcmp(stream->codec->codec, "wavpack") == 0) {
int size = dp->len;
uint8_t *parsed;
if (libav_parse_wavpack(track, dp->buffer, &parsed, &size) >= 0) {
@@ -2224,7 +2249,7 @@ static void mkv_parse_and_add_packet(demuxer_t *demuxer, mkv_track_t *track,
}
}
- if (stream->codec && strcmp(stream->codec, "prores") == 0) {
+ if (strcmp(stream->codec->codec, "prores") == 0) {
size_t newlen = dp->len + 8;
struct demux_packet *new = new_demux_packet(newlen);
if (new) {
@@ -2239,7 +2264,7 @@ static void mkv_parse_and_add_packet(demuxer_t *demuxer, mkv_track_t *track,
}
if (track->parse && !track->av_parser) {
- int id = mp_codec_to_av_codec_id(track->stream->codec);
+ int id = mp_codec_to_av_codec_id(track->stream->codec->codec);
const AVCodec *codec = avcodec_find_decoder(id);
track->av_parser = av_parser_init(id);
if (codec)
@@ -2367,7 +2392,7 @@ static int handle_block(demuxer_t *demuxer, struct block_info *block_info)
bstr data = block_info->data;
bool keyframe = block_info->keyframe;
uint64_t block_duration = block_info->duration;
- uint64_t tc = block_info->timecode;
+ int64_t tc = block_info->timecode;
mkv_track_t *track = block_info->track;
struct sh_stream *stream = track->stream;
uint32_t lace_size[MAX_NUM_LACES];
@@ -2416,7 +2441,6 @@ static int handle_block(demuxer_t *demuxer, struct block_info *block_info)
if (use_this_block) {
uint64_t filepos = block_info->filepos;
- mkv_d->last_pts = current_pts;
for (int i = 0; i < laces; i++) {
bstr block = bstr_splice(data, 0, lace_size[i]);
@@ -2434,13 +2458,13 @@ static int handle_block(demuxer_t *demuxer, struct block_info *block_info)
* values being the same). Also, don't use it for extra
* packets resulting from parsing. */
if (i == 0 || track->default_duration)
- dp->pts = mkv_d->last_pts + i * track->default_duration;
- if (stream->video && stream->video->avi_dts)
+ dp->pts = current_pts + i * track->default_duration;
+ if (stream->codec->avi_dts)
MPSWAP(double, dp->pts, dp->dts);
if (i == 0)
dp->duration = block_duration / 1e9;
if (stream->type == STREAM_AUDIO) {
- unsigned int srate = track->a_sfreq;
+ unsigned int srate = stream->codec->samplerate;
demux_packet_set_padding(dp,
mkv_d->a_skip_preroll ? track->codec_delay * srate : 0,
block_info->discardpadding / 1e9 * srate);
@@ -2454,7 +2478,7 @@ static int handle_block(demuxer_t *demuxer, struct block_info *block_info)
if (stream->type == STREAM_VIDEO) {
mkv_d->v_skip_to_keyframe = 0;
- mkv_d->skip_to_timecode = 0;
+ mkv_d->skip_to_timecode = INT64_MIN;
mkv_d->subtitle_preroll = 0;
} else if (stream->type == STREAM_AUDIO) {
mkv_d->a_skip_to_keyframe = 0;
@@ -2597,7 +2621,7 @@ static int read_next_block(demuxer_t *demuxer, struct block_info *block)
}
// For the sake of robustness, consider even unknown level 1
// elements the same as unknown/broken IDs.
- if (!ebml_is_mkv_level1_id(id) ||
+ if ((!ebml_is_mkv_level1_id(id) && id != EBML_ID_VOID) ||
ebml_read_skip(demuxer->log, -1, s) != 0)
{
ebml_resync_cluster(demuxer->log, s);
@@ -2646,7 +2670,7 @@ static mkv_index_t *get_highest_index_entry(struct demuxer *demuxer)
return index;
}
-static int create_index_until(struct demuxer *demuxer, uint64_t timecode)
+static int create_index_until(struct demuxer *demuxer, int64_t timecode)
{
struct mkv_demuxer *mkv_d = demuxer->priv;
struct stream *s = demuxer->stream;
@@ -2660,7 +2684,7 @@ static int create_index_until(struct demuxer *demuxer, uint64_t timecode)
if (!index || index->timecode * mkv_d->tc_scale < timecode) {
stream_seek(s, index ? index->filepos : mkv_d->cluster_start);
- MP_VERBOSE(demuxer, "creating index until TC %" PRIu64 "\n", timecode);
+ MP_VERBOSE(demuxer, "creating index until TC %" PRId64 "\n", timecode);
for (;;) {
int res;
struct block_info block;
@@ -2696,8 +2720,7 @@ static struct mkv_index *seek_with_cues(struct demuxer *demuxer, int seek_id,
for (size_t i = 0; i < mkv_d->num_indexes; i++) {
if (seek_id < 0 || mkv_d->indexes[i].tnum == seek_id) {
int64_t diff =
- target_timecode -
- (int64_t) (mkv_d->indexes[i].timecode * mkv_d->tc_scale);
+ target_timecode - mkv_d->indexes[i].timecode * mkv_d->tc_scale;
if (flags & FLAG_BACKWARD)
diff = -diff;
if (min_diff != INT64_MIN) {
@@ -2718,10 +2741,12 @@ static struct mkv_index *seek_with_cues(struct demuxer *demuxer, int seek_id,
// Find the cluster with the highest filepos, that has a timestamp
// still lower than min_tc.
double secs = opts->demux_mkv->subtitle_preroll_secs;
- uint64_t pre = MPMIN(INT64_MAX, secs * 1e9 / mkv_d->tc_scale);
- uint64_t min_tc = pre < index->timecode ? index->timecode - pre : 0;
+ if (mkv_d->index_has_durations)
+ secs = MPMAX(secs, opts->demux_mkv->subtitle_preroll_secs_index);
+ int64_t pre = MPMIN(INT64_MAX, secs * 1e9 / mkv_d->tc_scale);
+ int64_t min_tc = pre < index->timecode ? index->timecode - pre : 0;
uint64_t prev_target = 0;
- uint64_t prev_tc = 0;
+ int64_t prev_tc = 0;
for (size_t i = 0; i < mkv_d->num_indexes; i++) {
if (seek_id < 0 || mkv_d->indexes[i].tnum == seek_id) {
struct mkv_index *cur = &mkv_d->indexes[i];
@@ -2758,7 +2783,7 @@ static struct mkv_index *seek_with_cues(struct demuxer *demuxer, int seek_id,
return index;
}
-static void demux_mkv_seek(demuxer_t *demuxer, double rel_seek_secs, int flags)
+static void demux_mkv_seek(demuxer_t *demuxer, double seek_pts, int flags)
{
mkv_demuxer_t *mkv_d = demuxer->priv;
int64_t old_pos = stream_tell(demuxer->stream);
@@ -2780,21 +2805,21 @@ static void demux_mkv_seek(demuxer_t *demuxer, double rel_seek_secs, int flags)
int cueflags = (flags & SEEK_BACKWARD) ? FLAG_BACKWARD : 0;
mkv_d->subtitle_preroll = NUM_SUB_PREROLL_PACKETS;
- if (((flags & SEEK_HR) || demuxer->opts->demux_mkv->subtitle_preroll) &&
- st_active[STREAM_SUB] && st_active[STREAM_VIDEO])
+ int preroll_opt = demuxer->opts->demux_mkv->subtitle_preroll;
+ if (((flags & SEEK_HR) || preroll_opt == 1 ||
+ (preroll_opt == 2 && mkv_d->index_has_durations))
+ && st_active[STREAM_SUB] && st_active[STREAM_VIDEO])
cueflags |= FLAG_SUBPREROLL;
// Adjust the target a little bit to catch cases where the target position
// specifies a keyframe with high, but not perfect, precision.
- rel_seek_secs += flags & SEEK_FORWARD ? -0.005 : 0.005;
+ seek_pts += flags & SEEK_FORWARD ? -0.005 : 0.005;
if (!(flags & SEEK_FACTOR)) { /* time in secs */
mkv_index_t *index = NULL;
- if (!(flags & SEEK_ABSOLUTE)) /* relative seek */
- rel_seek_secs += mkv_d->last_pts;
- rel_seek_secs = FFMAX(rel_seek_secs, 0);
- int64_t target_timecode = rel_seek_secs * 1e9 + 0.5;
+ seek_pts = FFMAX(seek_pts, 0);
+ int64_t target_timecode = seek_pts * 1e9 + 0.5;
if (create_index_until(demuxer, target_timecode) >= 0) {
int seek_id = st_active[STREAM_VIDEO] ? v_tnum : a_tnum;
@@ -2810,7 +2835,7 @@ static void demux_mkv_seek(demuxer_t *demuxer, double rel_seek_secs, int flags)
mkv_d->skip_to_timecode = target_timecode;
} else {
mkv_d->skip_to_timecode = index ? index->timecode * mkv_d->tc_scale
- : 0;
+ : INT64_MIN;
}
} else {
stream_t *s = demuxer->stream;
@@ -2818,7 +2843,7 @@ static void demux_mkv_seek(demuxer_t *demuxer, double rel_seek_secs, int flags)
read_deferred_cues(demuxer);
int64_t size = stream_get_size(s);
- int64_t target_filepos = size * MPCLAMP(rel_seek_secs, 0, 1);
+ int64_t target_filepos = size * MPCLAMP(seek_pts, 0, 1);
mkv_index_t *index = NULL;
if (mkv_d->index_complete) {
diff --git a/demux/demux_mkv_timeline.c b/demux/demux_mkv_timeline.c
index a874f9c..8affe13 100644
--- a/demux/demux_mkv_timeline.c
+++ b/demux/demux_mkv_timeline.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>
@@ -29,7 +29,7 @@
#include "osdep/io.h"
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "common/msg.h"
#include "common/global.h"
@@ -458,8 +458,9 @@ static void check_track_compatibility(struct timeline *tl)
if (p->source == mainsrc)
continue;
- for (int i = 0; i < p->source->num_streams; i++) {
- struct sh_stream *s = p->source->streams[i];
+ int num_source_streams = demux_get_num_stream(p->source);
+ for (int i = 0; i < num_source_streams; i++) {
+ struct sh_stream *s = demux_get_stream(p->source, i);
if (s->attached_picture)
continue;
@@ -473,8 +474,9 @@ static void check_track_compatibility(struct timeline *tl)
}
}
- for (int i = 0; i < mainsrc->num_streams; i++) {
- struct sh_stream *m = mainsrc->streams[i];
+ int num_main_streams = demux_get_num_stream(mainsrc);
+ for (int i = 0; i < num_main_streams; i++) {
+ struct sh_stream *m = demux_get_stream(mainsrc, i);
if (m->attached_picture)
continue;
@@ -483,7 +485,7 @@ static void check_track_compatibility(struct timeline *tl)
if (s) {
// There are actually many more things that in theory have to
// match (though mpv's implementation doesn't care).
- if (s->codec && m->codec && strcmp(s->codec, m->codec) != 0)
+ if (strcmp(s->codec->codec, m->codec->codec) != 0)
MP_WARN(tl, "Timeline segments have mismatching codec.\n");
} else {
MP_WARN(tl, "Source %s lacks %s stream with TID=%d, which "
diff --git a/demux/demux_playlist.c b/demux/demux_playlist.c
index 25ad22b..a5e9ca4 100644
--- a/demux/demux_playlist.c
+++ b/demux/demux_playlist.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>
@@ -169,23 +169,6 @@ static int parse_ref_init(struct pl_parser *p)
return 0;
}
-static int parse_mov_rtsptext(struct pl_parser *p)
-{
- bstr line = pl_get_line(p);
- if (!bstr_eatstart(&line, bstr0("RTSPtext")))
- return -1;
- if (p->probing)
- return 0;
- line = bstr_strip(line);
- do {
- if (bstr_case_startswith(line, bstr0("rtsp://"))) {
- pl_add(p, line);
- return 0;
- }
- } while (!pl_eof(p) && (line = bstr_strip(pl_get_line(p))).len);
- return -1;
-}
-
static int parse_pls(struct pl_parser *p)
{
bstr line = {0};
@@ -285,7 +268,6 @@ static const struct pl_format formats[] = {
{"m3u", parse_m3u,
MIME_TYPES("audio/mpegurl", "audio/x-mpegurl", "application/x-mpegurl")},
{"ini", parse_ref_init},
- {"mov", parse_mov_rtsptext},
{"pls", parse_pls,
MIME_TYPES("audio/x-scpls")},
{"txt", parse_txt},
diff --git a/demux/demux_rar.c b/demux/demux_rar.c
index 46311bc..f35c2cc 100644
--- a/demux/demux_rar.c
+++ b/demux/demux_rar.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 "common/common.h"
diff --git a/demux/demux_raw.c b/demux/demux_raw.c
index a672877..0d7517c 100644
--- a/demux/demux_raw.c
+++ b/demux/demux_raw.c
@@ -117,6 +117,7 @@ const struct m_sub_options demux_rawvideo_conf = {
};
struct priv {
+ struct sh_stream *sh;
int frame_size;
int read_frames;
double frame_rate;
@@ -125,29 +126,30 @@ struct priv {
static int demux_rawaudio_open(demuxer_t *demuxer, enum demux_check check)
{
struct demux_rawaudio_opts *opts = demuxer->opts->demux_rawaudio;
- struct sh_stream *sh;
- sh_audio_t *sh_audio;
if (check != DEMUX_CHECK_REQUEST && check != DEMUX_CHECK_FORCE)
return -1;
- sh = new_sh_stream(demuxer, STREAM_AUDIO);
- sh_audio = sh->audio;
- sh_audio->channels = opts->channels;
- sh_audio->force_channels = true;
- sh_audio->samplerate = opts->samplerate;
+ struct sh_stream *sh = demux_alloc_sh_stream(STREAM_AUDIO);
+ struct mp_codec_params *c = sh->codec;
+ c->channels = opts->channels;
+ c->force_channels = true;
+ c->samplerate = opts->samplerate;
int f = opts->aformat;
- // See PCM(): sign float bits endian
- mp_set_pcm_codec(sh, f & 1, f & 2, f >> 3, f & 4);
+ // See PCM(): sign float bits endian
+ mp_set_pcm_codec(sh->codec, f & 1, f & 2, f >> 3, f & 4);
int samplesize = ((f >> 3) + 7) / 8;
+ demux_add_sh_stream(demuxer, sh);
+
struct priv *p = talloc_ptrtype(demuxer, p);
demuxer->priv = p;
*p = (struct priv) {
- .frame_size = samplesize * sh_audio->channels.num,
- .frame_rate = sh_audio->samplerate,
- .read_frames = sh_audio->samplerate / 8,
+ .sh = sh,
+ .frame_size = samplesize * c->channels.num,
+ .frame_rate = c->samplerate,
+ .read_frames = c->samplerate / 8,
};
return 0;
@@ -156,8 +158,6 @@ static int demux_rawaudio_open(demuxer_t *demuxer, enum demux_check check)
static int demux_rawvideo_open(demuxer_t *demuxer, enum demux_check check)
{
struct demux_rawvideo_opts *opts = demuxer->opts->demux_rawvideo;
- struct sh_stream *sh;
- sh_video_t *sh_video;
if (check != DEMUX_CHECK_REQUEST && check != DEMUX_CHECK_FORCE)
return -1;
@@ -219,19 +219,21 @@ static int demux_rawvideo_open(demuxer_t *demuxer, enum demux_check check)
imgsize = width * height * bpp / 8;
}
- sh = new_sh_stream(demuxer, STREAM_VIDEO);
- sh_video = sh->video;
- sh->codec = decoder;
- sh->codec_tag = imgfmt;
- sh_video->fps = opts->fps;
- sh_video->disp_w = width;
- sh_video->disp_h = height;
+ struct sh_stream *sh = demux_alloc_sh_stream(STREAM_VIDEO);
+ struct mp_codec_params *c = sh->codec;
+ c->codec = decoder;
+ c->codec_tag = imgfmt;
+ c->fps = opts->fps;
+ c->disp_w = width;
+ c->disp_h = height;
+ demux_add_sh_stream(demuxer, sh);
struct priv *p = talloc_ptrtype(demuxer, p);
demuxer->priv = p;
*p = (struct priv) {
+ .sh = sh,
.frame_size = imgsize,
- .frame_rate = sh_video->fps,
+ .frame_rate = c->fps,
.read_frames = 1,
};
@@ -256,22 +258,20 @@ static int raw_fill_buffer(demuxer_t *demuxer)
int len = stream_read(demuxer->stream, dp->buffer, dp->len);
demux_packet_shorten(dp, len);
- demux_add_packet(demuxer->streams[0], dp);
+ demux_add_packet(p->sh, dp);
return 1;
}
-static void raw_seek(demuxer_t *demuxer, double rel_seek_secs, int flags)
+static void raw_seek(demuxer_t *demuxer, double seek_pts, int flags)
{
struct priv *p = demuxer->priv;
stream_t *s = demuxer->stream;
int64_t end = 0;
stream_control(s, STREAM_CTRL_GET_SIZE, &end);
- int64_t pos = (flags & SEEK_ABSOLUTE) ? 0 : stream_tell(s);
+ int64_t pos = seek_pts * p->frame_rate * p->frame_size;
if (flags & SEEK_FACTOR)
- pos += end * rel_seek_secs;
- else
- pos += rel_seek_secs * p->frame_rate * p->frame_size;
+ pos = end * seek_pts;
if (pos < 0)
pos = 0;
if (end && pos > end)
diff --git a/demux/demux_timeline.c b/demux/demux_timeline.c
new file mode 100644
index 0000000..0c6c398
--- /dev/null
+++ b/demux/demux_timeline.c
@@ -0,0 +1,388 @@
+/*
+ * 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 <limits.h>
+
+#include "common/common.h"
+#include "common/msg.h"
+
+#include "demux.h"
+#include "timeline.h"
+#include "stheader.h"
+
+struct segment {
+ int index;
+ double start, end;
+ double d_start;
+ struct demuxer *d;
+ // stream_map[sh_stream.index] = index into priv.streams, where sh_stream
+ // is a stream from the source d. It's used to map the streams of the
+ // source onto the set of streams of the virtual timeline.
+ // Uses -1 for streams that do not appear in the virtual timeline.
+ int *stream_map;
+ int num_stream_map;
+};
+
+// Information for each stream on the virtual timeline. (Mirrors streams
+// exposed by demux_timeline.)
+struct virtual_stream {
+ struct sh_stream *sh; // stream exported by demux_timeline
+ bool selected; // ==demux_stream_is_selected(sh)
+ bool new_segment; // whether a new segment needs to be signaled
+ int eos_packets; // deal with b-frame delay
+};
+
+struct priv {
+ struct timeline *tl;
+
+ double duration;
+
+ struct segment **segments;
+ int num_segments;
+ struct segment *current;
+
+ // As the demuxer user sees it.
+ struct virtual_stream *streams;
+ int num_streams;
+
+ // Total number of packets received past end of segment. Used
+ // to be clever about determining when to switch segments.
+ int eos_packets;
+};
+
+static void reselect_streams(struct demuxer *demuxer)
+{
+ struct priv *p = demuxer->priv;
+
+ for (int n = 0; n < p->num_streams; n++) {
+ struct virtual_stream *vs = &p->streams[n];
+ vs->selected = demux_stream_is_selected(vs->sh);
+ }
+
+ for (int n = 0; n < p->num_segments; n++) {
+ struct segment *seg = p->segments[n];
+ for (int i = 0; i < seg->num_stream_map; i++) {
+ struct sh_stream *sh = demux_get_stream(seg->d, i);
+ bool selected = false;
+ if (seg->stream_map[i] >= 0)
+ selected = p->streams[seg->stream_map[i]].selected;
+ // This stops demuxer readahead for inactive segments.
+ if (!p->current || seg->d != p->current->d)
+ selected = false;
+ demuxer_select_track(seg->d, sh, selected);
+ }
+ }
+}
+
+static void switch_segment(struct demuxer *demuxer, struct segment *new,
+ double start_pts, int flags)
+{
+ struct priv *p = demuxer->priv;
+
+ if (p->current == new)
+ return;
+
+ if (!(flags & (SEEK_FORWARD | SEEK_BACKWARD)))
+ flags |= SEEK_BACKWARD;
+
+ MP_VERBOSE(demuxer, "switch to segment %d\n", new->index);
+
+ p->current = new;
+ reselect_streams(demuxer);
+ demux_set_ts_offset(new->d, new->start - new->d_start);
+ demux_seek(new->d, start_pts, flags);
+
+ for (int n = 0; n < p->num_streams; n++) {
+ struct virtual_stream *vs = &p->streams[n];
+ vs->new_segment = true;
+ vs->eos_packets = 0;
+ }
+
+ p->eos_packets = 0;
+}
+
+static void d_seek(struct demuxer *demuxer, double seek_pts, int flags)
+{
+ struct priv *p = demuxer->priv;
+
+ double pts = seek_pts * ((flags & SEEK_FACTOR) ? p->duration : 1);
+
+ flags &= SEEK_FORWARD | SEEK_BACKWARD | SEEK_HR;
+
+ struct segment *new = p->segments[p->num_segments - 1];
+ for (int n = 0; n < p->num_segments; n++) {
+ if (pts < p->segments[n]->end) {
+ new = p->segments[n];
+ break;
+ }
+ }
+
+ p->current = NULL; // force seek
+ switch_segment(demuxer, new, pts, flags);
+}
+
+static int d_fill_buffer(struct demuxer *demuxer)
+{
+ struct priv *p = demuxer->priv;
+
+ if (!p->current)
+ switch_segment(demuxer, p->segments[0], 0, 0);
+
+ struct segment *seg = p->current;
+
+ struct demux_packet *pkt = demux_read_any_packet(seg->d);
+ if (!pkt || pkt->pts >= seg->end)
+ p->eos_packets += 1;
+
+ // Test for EOF. Do this here to properly run into EOF even if other
+ // streams are disabled etc. If it somehow doesn't manage to reach the end
+ // after demuxing a high (bit arbitrary) number of packets, assume one of
+ // the streams went EOF early.
+ bool eos_reached = p->eos_packets > 0;
+ if (eos_reached && p->eos_packets < 100) {
+ for (int n = 0; n < p->num_streams; n++) {
+ struct virtual_stream *vs = &p->streams[n];
+ if (vs->selected) {
+ int max_packets = 0;
+ if (vs->sh->type == STREAM_AUDIO)
+ max_packets = 1;
+ if (vs->sh->type == STREAM_VIDEO)
+ max_packets = 16;
+ eos_reached &= vs->eos_packets >= max_packets;
+ }
+ }
+ }
+
+ if (eos_reached || !pkt) {
+ talloc_free(pkt);
+
+ struct segment *next = NULL;
+ for (int n = 0; n < p->num_segments - 1; n++) {
+ if (p->segments[n] == seg) {
+ next = p->segments[n + 1];
+ break;
+ }
+ }
+ if (!next)
+ return 0;
+ switch_segment(demuxer, next, next->start, 0);
+ return 1; // reader will retry
+ }
+
+ if (pkt->stream < 0 || pkt->stream > seg->num_stream_map)
+ goto drop;
+
+ if (!pkt->codec)
+ pkt->codec = demux_get_stream(seg->d, pkt->stream)->codec;
+
+ if (pkt->start == MP_NOPTS_VALUE || pkt->start < seg->start)
+ pkt->start = seg->start;
+ if (pkt->end == MP_NOPTS_VALUE || pkt->end > seg->end)
+ pkt->end = seg->end;
+
+ pkt->stream = seg->stream_map[pkt->stream];
+ if (pkt->stream < 0)
+ goto drop;
+
+ struct virtual_stream *vs = &p->streams[pkt->stream];
+
+ if (pkt->pts != MP_NOPTS_VALUE && pkt->pts >= seg->end) {
+ // Trust the keyframe flag. Might not always be a good idea, but will
+ // be sufficient at least with mkv. The problem is that this flag is
+ // not well-defined in libavformat and is container-dependent.
+ if (pkt->keyframe || vs->eos_packets == INT_MAX) {
+ vs->eos_packets = INT_MAX;
+ goto drop;
+ } else {
+ vs->eos_packets += 1;
+ }
+ }
+
+ pkt->new_segment |= vs->new_segment;
+ vs->new_segment = false;
+
+ demux_add_packet(vs->sh, pkt);
+ return 1;
+
+drop:
+ talloc_free(pkt);
+ return 1;
+}
+
+static void print_timeline(struct demuxer *demuxer)
+{
+ struct priv *p = demuxer->priv;
+
+ MP_VERBOSE(demuxer, "Timeline segments:\n");
+ for (int n = 0; n < p->num_segments; n++) {
+ struct segment *seg = p->segments[n];
+ int src_num = -1;
+ for (int i = 0; i < p->tl->num_sources; i++) {
+ if (p->tl->sources[i] == seg->d) {
+ src_num = i;
+ break;
+ }
+ }
+ MP_VERBOSE(demuxer, " %2d: %12f [%12f] (", n, seg->start, seg->d_start);
+ for (int i = 0; i < seg->num_stream_map; i++)
+ MP_VERBOSE(demuxer, "%s%d", i ? " " : "", seg->stream_map[i]);
+ MP_VERBOSE(demuxer, ") %d:'%s'\n", src_num, seg->d->filename);
+ }
+ MP_VERBOSE(demuxer, "Total duration: %f\n", p->duration);
+}
+
+static bool target_stream_used(struct segment *seg, int target_index)
+{
+ for (int n = 0; n < seg->num_stream_map; n++) {
+ if (seg->stream_map[n] == target_index)
+ return true;
+ }
+ return false;
+}
+
+// Create mapping from segment streams to virtual timeline streams.
+static void associate_streams(struct demuxer *demuxer, struct segment *seg)
+{
+ struct priv *p = demuxer->priv;
+
+ int counts[STREAM_TYPE_COUNT] = {0};
+
+ int num_streams = demux_get_num_stream(seg->d);
+ for (int n = 0; n < num_streams; n++) {
+ struct sh_stream *sh = demux_get_stream(seg->d, n);
+ // Try associating by demuxer ID (supposedly useful for ordered chapters).
+ struct sh_stream *other =
+ demuxer_stream_by_demuxer_id(demuxer, sh->type, sh->demuxer_id);
+ if (!other || !target_stream_used(seg, other->index)) {
+ // Try to associate the first unused stream with matching media type.
+ for (int i = 0; i < p->num_streams; i++) {
+ struct sh_stream *cur = p->streams[i].sh;
+ if (cur->type == sh->type && !target_stream_used(seg, cur->index))
+ {
+ other = cur;
+ break;
+ }
+ }
+ }
+
+ MP_TARRAY_APPEND(seg, seg->stream_map, seg->num_stream_map,
+ other ? other->index : -1);
+
+ counts[sh->type] += 1;
+ }
+}
+
+static int d_open(struct demuxer *demuxer, enum demux_check check)
+{
+ struct priv *p = demuxer->priv = talloc_zero(demuxer, struct priv);
+ p->tl = demuxer->params ? demuxer->params->timeline : NULL;
+ if (!p->tl || p->tl->num_parts < 1)
+ return -1;
+
+ p->duration = p->tl->parts[p->tl->num_parts].start;
+
+ demuxer->chapters = p->tl->chapters;
+ demuxer->num_chapters = p->tl->num_chapters;
+
+ struct demuxer *meta = p->tl->track_layout;
+ demuxer->metadata = meta->metadata;
+ demuxer->attachments = meta->attachments;
+ demuxer->num_attachments = meta->num_attachments;
+
+ int num_streams = demux_get_num_stream(meta);
+ for (int n = 0; n < num_streams; n++) {
+ struct sh_stream *sh = demux_get_stream(meta, n);
+ struct sh_stream *new = demux_alloc_sh_stream(sh->type);
+ new->demuxer_id = sh->demuxer_id;
+ new->codec = sh->codec;
+ new->title = sh->title;
+ new->lang = sh->lang;
+ new->default_track = sh->default_track;
+ new->forced_track = sh->forced_track;
+ new->hls_bitrate = sh->hls_bitrate;
+ new->missing_timestamps = sh->missing_timestamps;
+ demux_add_sh_stream(demuxer, new);
+ struct virtual_stream vs = {
+ .sh = new,
+ };
+ MP_TARRAY_APPEND(p, p->streams, p->num_streams, vs);
+ }
+
+ for (int n = 0; n < p->tl->num_parts; n++) {
+ struct timeline_part *part = &p->tl->parts[n];
+ struct timeline_part *next = &p->tl->parts[n + 1];
+
+ struct segment *seg = talloc_ptrtype(p, seg);
+ *seg = (struct segment){
+ .d = part->source,
+ .d_start = part->source_start,
+ .start = part->start,
+ .end = next->start,
+ };
+
+ associate_streams(demuxer, seg);
+
+ seg->index = n;
+ MP_TARRAY_APPEND(p, p->segments, p->num_segments, seg);
+ }
+
+ print_timeline(demuxer);
+
+ demuxer->seekable = true;
+ demuxer->partially_seekable = true;
+
+ demuxer->filetype = meta->filetype ? meta->filetype : meta->desc->name;
+
+ reselect_streams(demuxer);
+
+ return 0;
+}
+
+static void d_close(struct demuxer *demuxer)
+{
+ struct priv *p = demuxer->priv;
+ struct demuxer *master = p->tl->demuxer;
+ timeline_destroy(p->tl);
+ free_demuxer(master);
+}
+
+static int d_control(struct demuxer *demuxer, int cmd, void *arg)
+{
+ struct priv *p = demuxer->priv;
+
+ switch (cmd) {
+ case DEMUXER_CTRL_GET_TIME_LENGTH: {
+ *(double *)arg = p->duration;
+ return DEMUXER_CTRL_OK;
+ }
+ case DEMUXER_CTRL_SWITCHED_TRACKS:
+ reselect_streams(demuxer);
+ return DEMUXER_CTRL_OK;
+ }
+ return DEMUXER_CTRL_NOTIMPL;
+}
+
+const demuxer_desc_t demuxer_desc_timeline = {
+ .name = "timeline",
+ .desc = "timeline segments",
+ .fill_buffer = d_fill_buffer,
+ .open = d_open,
+ .close = d_close,
+ .seek = d_seek,
+ .control = d_control,
+};
diff --git a/demux/demux_tv.c b/demux/demux_tv.c
index ab58487..4f1e3ac 100644
--- a/demux/demux_tv.c
+++ b/demux/demux_tv.c
@@ -20,8 +20,6 @@
static int demux_open_tv(demuxer_t *demuxer, enum demux_check check)
{
tvi_handle_t *tvh;
- sh_video_t *sh_video;
- sh_audio_t *sh_audio = NULL;
const tvi_functions_t *funcs;
if (check > DEMUX_CHECK_REQUEST || demuxer->stream->type != STREAMTYPE_TV)
@@ -50,31 +48,30 @@ static int demux_open_tv(demuxer_t *demuxer, enum demux_check check)
funcs = tvh->functions;
demuxer->priv=tvh;
- struct sh_stream *sh_v = new_sh_stream(demuxer, STREAM_VIDEO);
- sh_video = sh_v->video;
+ struct sh_stream *sh_v = demux_alloc_sh_stream(STREAM_VIDEO);
/* get IMAGE FORMAT */
int fourcc;
funcs->control(tvh->priv, TVI_CONTROL_VID_GET_FORMAT, &fourcc);
if (fourcc == MP_FOURCC_MJPEG) {
- sh_v->codec = "mjpeg";
+ sh_v->codec->codec = "mjpeg";
} else {
- sh_v->codec = "rawvideo";
- sh_v->codec_tag = fourcc;
+ sh_v->codec->codec = "rawvideo";
+ sh_v->codec->codec_tag = fourcc;
}
/* set FPS and FRAMETIME */
- if(!sh_video->fps)
+ if(!sh_v->codec->fps)
{
float tmp;
if (funcs->control(tvh->priv, TVI_CONTROL_VID_GET_FPS, &tmp) != TVI_CONTROL_TRUE)
- sh_video->fps = 25.0f; /* on PAL */
- else sh_video->fps = tmp;
+ sh_v->codec->fps = 25.0f; /* on PAL */
+ else sh_v->codec->fps = tmp;
}
if (tvh->tv_param->fps != -1.0f)
- sh_video->fps = tvh->tv_param->fps;
+ sh_v->codec->fps = tvh->tv_param->fps;
/* If playback only mode, go to immediate mode, fail silently */
if(tvh->tv_param->immediate == 1)
@@ -84,10 +81,12 @@ static int demux_open_tv(demuxer_t *demuxer, enum demux_check check)
}
/* set width */
- funcs->control(tvh->priv, TVI_CONTROL_VID_GET_WIDTH, &sh_video->disp_w);
+ funcs->control(tvh->priv, TVI_CONTROL_VID_GET_WIDTH, &sh_v->codec->disp_w);
/* set height */
- funcs->control(tvh->priv, TVI_CONTROL_VID_GET_HEIGHT, &sh_video->disp_h);
+ funcs->control(tvh->priv, TVI_CONTROL_VID_GET_HEIGHT, &sh_v->codec->disp_h);
+
+ demux_add_sh_stream(demuxer, sh_v);
demuxer->seekable = 0;
@@ -115,21 +114,22 @@ static int demux_open_tv(demuxer_t *demuxer, enum demux_check check)
goto no_audio;
}
- struct sh_stream *sh_a = new_sh_stream(demuxer, STREAM_AUDIO);
- sh_audio = sh_a->audio;
+ struct sh_stream *sh_a = demux_alloc_sh_stream(STREAM_AUDIO);
funcs->control(tvh->priv, TVI_CONTROL_AUD_GET_SAMPLERATE,
- &sh_audio->samplerate);
- int nchannels = sh_audio->channels.num;
+ &sh_a->codec->samplerate);
+ int nchannels = sh_a->codec->channels.num;
funcs->control(tvh->priv, TVI_CONTROL_AUD_GET_CHANNELS,
&nchannels);
- mp_chmap_from_channels(&sh_audio->channels, nchannels);
+ mp_chmap_from_channels(&sh_a->codec->channels, nchannels);
// s16ne
- mp_set_pcm_codec(sh_a, true, false, 16, BYTE_ORDER == BIG_ENDIAN);
+ mp_set_pcm_codec(sh_a->codec, true, false, 16, BYTE_ORDER == BIG_ENDIAN);
+
+ demux_add_sh_stream(demuxer, sh_a);
MP_VERBOSE(tvh, " TV audio: %d channels, %d bits, %d Hz\n",
- nchannels, 16, sh_audio->samplerate);
+ nchannels, 16, sh_a->codec->samplerate);
}
no_audio:
@@ -168,8 +168,9 @@ static int demux_tv_fill_buffer(demuxer_t *demux)
unsigned int len=0;
struct sh_stream *want_audio = NULL, *want_video = NULL;
- for (int n = 0; n < demux->num_streams; n++) {
- struct sh_stream *sh = demux->streams[n];
+ int num_streams = demux_get_num_stream(demux);
+ for (int n = 0; n < num_streams; n++) {
+ struct sh_stream *sh = demux_get_stream(demux, n);
if (!demux_has_packet(sh) && demux_stream_is_selected(sh)) {
if (sh->type == STREAM_AUDIO)
want_audio = sh;
diff --git a/demux/ebml.c b/demux/ebml.c
index dbdc5e2..abbb73c 100644
--- a/demux/ebml.c
+++ b/demux/ebml.c
@@ -30,7 +30,7 @@
#include <libavutil/intfloat.h>
#include <libavutil/common.h>
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "ebml.h"
#include "stream/stream.h"
#include "common/msg.h"
diff --git a/demux/packet.c b/demux/packet.c
index 22b111b..32fabc4 100644
--- a/demux/packet.c
+++ b/demux/packet.c
@@ -49,6 +49,8 @@ struct demux_packet *new_demux_packet_from_avpacket(struct AVPacket *avpkt)
.dts = MP_NOPTS_VALUE,
.duration = -1,
.pos = -1,
+ .start = MP_NOPTS_VALUE,
+ .end = MP_NOPTS_VALUE,
.stream = -1,
.avpacket = talloc_zero(dp, AVPacket),
};
@@ -106,6 +108,9 @@ void demux_packet_copy_attribs(struct demux_packet *dst, struct demux_packet *sr
dst->dts = src->dts;
dst->duration = src->duration;
dst->pos = src->pos;
+ dst->start = src->start;
+ dst->end = src->end;
+ dst->new_segment = src->new_segment;
dst->keyframe = src->keyframe;
dst->stream = src->stream;
}
diff --git a/demux/packet.h b/demux/packet.h
index 784a1de..723dbc7 100644
--- a/demux/packet.h
+++ b/demux/packet.h
@@ -25,15 +25,24 @@
// Holds one packet/frame/whatever
typedef struct demux_packet {
int len;
+ unsigned char *buffer;
+
double pts;
double dts;
double duration;
- int64_t pos; // position in source file byte stream
- unsigned char *buffer;
bool keyframe;
- int stream; // source stream index
+
+ int64_t pos; // position in source file byte stream
+ int stream; // source stream index
+
+ // segmentation (ordered chapters, EDL)
+ struct mp_codec_params *codec;
+ double start, end;
+ bool new_segment;
+
+ // private
struct demux_packet *next;
- struct AVPacket *avpacket; // keep the buffer allocation
+ struct AVPacket *avpacket; // keep the buffer allocation and sidedata
} demux_packet_t;
struct demux_packet *new_demux_packet(size_t len);
diff --git a/demux/stheader.h b/demux/stheader.h
index a615867..35be208 100644
--- a/demux/stheader.h
+++ b/demux/stheader.h
@@ -37,23 +37,8 @@ struct sh_stream {
int demuxer_id;
// FFmpeg stream index (AVFormatContext.streams[index]), or equivalent.
int ff_index;
- // One of these is non-NULL, the others are NULL, depending on the stream
- // type.
- struct sh_audio *audio;
- struct sh_video *video;
- struct sh_sub *sub;
- // E.g. "h264" (usually corresponds to AVCodecDescriptor.name)
- const char *codec;
-
- // Usually a FourCC, exact meaning depends on codec.
- unsigned int codec_tag;
-
- unsigned char *extradata; // codec specific per-stream header
- int extradata_size;
-
- // Codec specific header data (set by demux_lavf.c only)
- struct AVCodecContext *lav_headers;
+ struct mp_codec_params *codec;
char *title;
char *lang; // language code
@@ -70,31 +55,43 @@ struct sh_stream {
struct demux_stream *ds;
};
-typedef struct sh_audio {
+struct mp_codec_params {
+ enum stream_type type;
+
+ // E.g. "h264" (usually corresponds to AVCodecDescriptor.name)
+ const char *codec;
+
+ // Usually a FourCC, exact meaning depends on codec.
+ unsigned int codec_tag;
+
+ unsigned char *extradata; // codec specific per-stream header
+ int extradata_size;
+
+ // Codec specific header data (set by demux_lavf.c only)
+ struct AVCodecContext *lav_headers;
+
+ // STREAM_AUDIO
int samplerate;
struct mp_chmap channels;
bool force_channels;
int bitrate; // compressed bits/sec
int block_align;
- int bits_per_coded_sample;
struct replaygain_data *replaygain_data;
-} sh_audio_t;
-typedef struct sh_video {
+ // STREAM_VIDEO
bool avi_dts; // use DTS timing; first frame and DTS is 0
float fps; // frames per second (set only if constant fps)
- float aspect; // aspect ratio stored in the file (for prescaling)
- int bits_per_coded_sample;
+ int par_w, par_h; // pixel aspect ratio (0 if unknown/square)
int disp_w, disp_h; // display size
int rotate; // intended display rotation, in degrees, [0, 359]
int stereo_mode; // mp_stereo3d_mode (0 if none/unknown)
-} sh_video_t;
-
-typedef struct sh_sub {
- double frame_based; // timestamps are frame-based (and this is the
- // fallback framerate used for timestamps)
- bool is_utf8; // if false, subtitle packet charset is unknown
- struct dec_sub *dec_sub; // decoder context
-} sh_sub_t;
+
+ // STREAM_VIDEO + STREAM_AUDIO
+ int bits_per_coded_sample;
+
+ // STREAM_SUB
+ double frame_based; // timestamps are frame-based (and this is the
+ // fallback framerate used for timestamps)
+};
#endif /* MPLAYER_STHEADER_H */
diff --git a/etc/input.conf b/etc/input.conf
index b5229b0..b4438bb 100644
--- a/etc/input.conf
+++ b/etc/input.conf
@@ -166,6 +166,10 @@
#h cycle tv-channel -1 # previous channel
#k cycle tv-channel +1 # next channel
+# For dvb://
+#H cycle dvb-channel-name -1 # previous channel
+#K cycle dvb-channel-name +1 # next channel
+
#
# Legacy bindings (may or may not be removed in the future)
#
@@ -181,10 +185,6 @@
# ? add sub-scale -0.1 # decrease subtitle font size
# ? sub-step -1 # immediately display next subtitle
# ? sub-step +1 # previous
-# ? cycle-values window-scale 0.5 2 1 # switch between 1/2, 2x, unresized window size
-# ? cycle colormatrix
-# ? add audio-delay 0.100 # this changes audio/video sync
-# ? add audio-delay -0.100
# ? cycle angle # switch DVD/Bluray angle
# ? add balance -0.1 # adjust audio balance in favor of left
# ? add balance 0.1 # right
diff --git a/etc/example.conf b/etc/mpv.conf
index 4c545d9..b9db3ce 100644
--- a/etc/example.conf
+++ b/etc/mpv.conf
@@ -1,5 +1,12 @@
#
-# mpv configuration file
+# Example mpv configuration file
+#
+# Warning:
+#
+# The commented example options usually do _not_ set the default values. Call
+# mpv with --list-options to see the default values for most options. There is
+# no builtin or example mpv.conf with all the defaults.
+#
#
# Configuration files are read system-wide from /usr/local/etc/mpv.conf
# and per-user from ~/.config/mpv/mpv.conf, where per-user settings override
@@ -17,18 +24,11 @@
#
# Profiles should be placed at the bottom of the configuration file to ensure
# that settings wanted as defaults are not restricted to specific profiles.
-#
-# Note that the commented example options usually do _not_ set the default
-# values. Calling mpv with --list-options is a nice way to see the default
-# values for most options.
##################
# video settings #
##################
-# Specify default video driver (see --vo=help for a list).
-#vo=vdpau
-
# Start in fullscreen mode by default.
#fs=yes
@@ -38,12 +38,36 @@
# don't allow a new window to have a size larger than 90% of the screen size
#autofit-larger=90%x90%
+# Do not close the window on exit.
+#keep-open=yes
+
+# Do not wait with showing the video window until it has loaded. (This will
+# resize the window once video is loaded. Also always shows a window with
+# audio.)
+#force-window=immediate
+
# Disable the On Screen Controller (OSC).
#osc=no
# Keep the player window on top of all other windows.
#ontop=yes
+# Specify default video driver (see --vo=help for a list).
+# This one selects high quality video scaling etc. - can cause problems with
+# some drivers and GPUs.
+#vo=opengl-hq
+
+# Force video to lock on the display's refresh rate, and change video and audio
+# speed to some degree to ensure synchronous playback - can cause problems
+# with some drivers and desktop environments.
+#video-sync=display-resample
+
+# Enable hardware decoding if available. Often, this does not work with all
+# video outputs, but should work well with default settings on most systems.
+# If performance or energy usage is an issue, forcing the vdpau or vaapi VOs
+# may or may not help.
+#hwdec=auto
+
##################
# audio settings #
##################
@@ -54,9 +78,8 @@
# Disable softvol usage, and always use the system mixer if available.
#softvol=no
-# Scale audio tempo by playback speed without altering pitch. (By default does
-# nothing if playback speed is not changed. May introduce artifacts.)
-#af=scaletempo
+# Do not filter audio to keep pitch when changing playback speed.
+audio-pitch-correction=no
# Output 5.1 audio natively, and upmix/downmix audio with a different format.
#audio-channels=5.1
@@ -75,15 +98,11 @@
# cache settings
#
-# Use 8MB input cache by default. The cache is enabled for network streams only.
-#cache-default=8192
+# Use 150MB input cache by default. The cache is enabled for network streams only.
+#cache-default=153600
#
-# Use 8MB input cache for everything, even local files.
-#cache=8192
-#
-# If a seek is issued, and the target is 1024KB past the cached range, then
-# keep reading until the seek target is hit, instead of doing a real seek.
-#cache-seek-min=1024
+# Use 150MB input cache for everything, even local files.
+#cache=153600
#
# Disable the behavior that the player will pause if the cache goes below a
# certain fill size.
@@ -91,6 +110,9 @@
#
# Read ahead about 5 seconds of audio and video packets.
#demuxer-readahead-secs=5.0
+#
+# Raise readahead from demuxer-readahead-secs to this value if a cache is active.
+#cache-secs=50.0
# Display English subtitles if available.
#slang=en
@@ -102,9 +124,10 @@
# If the file seems to be valid UTF-8, prefer UTF-8.
#sub-codepage=utf8:cp1256
-# Enable hardware decoding if available. Often, this requires using an certain
-# video output, otherwise no hardware decoding will be used.
-#hwdec=auto
+
+# You can also include other configuration files.
+#include=/path/to/the/file/you/want/to/include
+
############
# Profiles #
@@ -113,16 +136,8 @@
# 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=vdpau
-
-#[vdpau]
-# The profile forces the vdpau VO.
-#vo=vdpau
-# Use hardware decoding (this might break playback of some h264 files)
-#hwdec=vdpau
-# Most video filters do not work with hardware decoding.
-#vf-clr=yes
-
+# The following profile can be enabled on the command line with: --profile=invert
-# You can also include other configuration files.
-#include=/path/to/the/file/you/want/to/include
+#[invert]
+# The profile forces this video filter:
+#vf-add=flip
diff --git a/etc/mpv.desktop b/etc/mpv.desktop
index 5504d65..d79d846 100644
--- a/etc/mpv.desktop
+++ b/etc/mpv.desktop
@@ -27,5 +27,5 @@ TryExec=mpv
Exec=mpv --profile=pseudo-gui -- %U
Terminal=false
Categories=AudioVideo;Audio;Video;Player;TV;
-MimeType=application/ogg;application/x-ogg;application/sdp;application/smil;application/x-smil;application/streamingmedia;application/x-streamingmedia;application/vnd.rn-realmedia;application/vnd.rn-realmedia-vbr;audio/aac;audio/x-aac;audio/m4a;audio/x-m4a;audio/mp1;audio/x-mp1;audio/mp2;audio/x-mp2;audio/mp3;audio/x-mp3;audio/mpeg;audio/x-mpeg;audio/mpegurl;audio/x-mpegurl;audio/mpg;audio/x-mpg;audio/rn-mpeg;audio/ogg;audio/scpls;audio/x-scpls;audio/vnd.rn-realaudio;audio/wav;audio/x-pn-windows-pcm;audio/x-realaudio;audio/x-pn-realaudio;audio/x-ms-wma;audio/x-pls;audio/x-wav;video/mpeg;video/x-mpeg;video/x-mpeg2;video/mp4;video/msvideo;video/x-msvideo;video/ogg;video/quicktime;video/vnd.rn-realvideo;video/x-ms-afs;video/x-ms-asf;video/x-ms-wmv;video/x-ms-wmx;video/x-ms-wvxvideo;video/x-avi;video/x-fli;video/x-flv;video/x-theora;video/x-matroska;video/webm;audio/x-flac;audio/x-vorbis+ogg;video/x-ogm+ogg;audio/x-shorten;audio/x-ape;audio/x-wavpack;audio/x-tta;audio/AMR;audio/ac3;video/mp2t;audio/flac;
+MimeType=application/ogg;application/x-ogg;application/sdp;application/smil;application/x-smil;application/streamingmedia;application/x-streamingmedia;application/vnd.rn-realmedia;application/vnd.rn-realmedia-vbr;audio/aac;audio/x-aac;audio/m4a;audio/x-m4a;audio/mp1;audio/x-mp1;audio/mp2;audio/x-mp2;audio/mp3;audio/x-mp3;audio/mpeg;audio/x-mpeg;audio/mpegurl;audio/x-mpegurl;audio/mpg;audio/x-mpg;audio/rn-mpeg;audio/ogg;audio/scpls;audio/x-scpls;audio/vnd.rn-realaudio;audio/wav;audio/x-pn-windows-pcm;audio/x-realaudio;audio/x-pn-realaudio;audio/x-ms-wma;audio/x-pls;audio/x-wav;video/mpeg;video/x-mpeg;video/x-mpeg2;video/mp4;video/msvideo;video/x-msvideo;video/ogg;video/quicktime;video/vnd.rn-realvideo;video/x-ms-afs;video/x-ms-asf;video/x-ms-wmv;video/x-ms-wmx;video/x-ms-wvxvideo;video/x-avi;video/x-fli;video/x-flv;video/x-theora;video/x-matroska;video/webm;audio/x-flac;audio/x-vorbis+ogg;video/x-ogm+ogg;audio/x-shorten;audio/x-ape;audio/x-wavpack;audio/x-tta;audio/AMR;audio/ac3;video/mp2t;audio/flac;audio/mp4;
X-KDE-Protocols=ftp,http,https,mms,rtmp,rtsp,sftp,smb
diff --git a/input/cmd_list.c b/input/cmd_list.c
index 2f3bfb9..b5e29aa 100644
--- a/input/cmd_list.c
+++ b/input/cmd_list.c
@@ -180,9 +180,11 @@ const struct mp_cmd_def mp_cmds[] = {
{ MP_CMD_DROP_BUFFERS, "drop-buffers", },
{ MP_CMD_AF, "af", { ARG_STRING, ARG_STRING } },
+ { MP_CMD_AF_COMMAND, "af-command", { ARG_STRING, ARG_STRING, ARG_STRING } },
{ MP_CMD_AO_RELOAD, "ao-reload", },
{ MP_CMD_VF, "vf", { ARG_STRING, ARG_STRING } },
+ { MP_CMD_VF_COMMAND, "vf-command", { ARG_STRING, ARG_STRING, ARG_STRING } },
{ MP_CMD_VO_CMDLINE, "vo-cmdline", { ARG_STRING } },
diff --git a/input/cmd_list.h b/input/cmd_list.h
index c8c0dc3..4e324bf 100644
--- a/input/cmd_list.h
+++ b/input/cmd_list.h
@@ -92,10 +92,12 @@ enum mp_command_type {
/// Audio Filter commands
MP_CMD_AF,
+ MP_CMD_AF_COMMAND,
MP_CMD_AO_RELOAD,
/// Video filter commands
MP_CMD_VF,
+ MP_CMD_VF_COMMAND,
/// Video output commands
MP_CMD_VO_CMDLINE,
diff --git a/input/cmd_parse.c b/input/cmd_parse.c
index ba35cd5..c2c3270 100644
--- a/input/cmd_parse.c
+++ b/input/cmd_parse.c
@@ -417,6 +417,7 @@ mp_cmd_t *mp_cmd_clone(mp_cmd_t *cmd)
m_option_copy(ret->args[i].type, &ret->args[i].v, &cmd->args[i].v);
}
ret->original = bstrdup(ret, cmd->original);
+ ret->key_name = talloc_strdup(ret, ret->key_name);
if (cmd->id == MP_CMD_COMMAND_LIST) {
struct mp_cmd *prev = NULL;
diff --git a/input/event.c b/input/event.c
index 2c46978..f765342 100644
--- a/input/event.c
+++ b/input/event.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 "event.h"
diff --git a/input/event.h b/input/event.h
index e2ce36b..3a06ce1 100644
--- a/input/event.h
+++ b/input/event.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/>.
*/
#include "misc/bstr.h"
diff --git a/input/input.c b/input/input.c
index 5b33707..ff7dcea 100644
--- a/input/input.c
+++ b/input/input.c
@@ -48,7 +48,7 @@
#include "options/m_config.h"
#include "options/m_option.h"
#include "options/path.h"
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "options/options.h"
#include "misc/bstr.h"
#include "stream/stream.h"
@@ -446,7 +446,9 @@ static mp_cmd_t *get_cmd_from_keys(struct input_ctx *ictx, char *force_section,
return handle_test(ictx, code);
struct cmd_bind *cmd = find_any_bind_for_key(ictx, force_section, code);
- if (cmd == NULL) {
+ if (!cmd)
+ cmd = find_any_bind_for_key(ictx, force_section, MP_KEY_UNMAPPED);
+ if (!cmd) {
if (code == MP_KEY_CLOSE_WIN)
return mp_input_parse_cmd_strv(ictx->log, (const char*[]){"quit", 0});
int msgl = MSGL_WARN;
@@ -460,12 +462,9 @@ static mp_cmd_t *get_cmd_from_keys(struct input_ctx *ictx, char *force_section,
mp_cmd_t *ret = mp_input_parse_cmd(ictx, bstr0(cmd->cmd), cmd->location);
if (ret) {
ret->input_section = cmd->owner->section;
- if (mp_msg_test(ictx->log, MSGL_DEBUG)) {
- char *keyname = mp_input_get_key_combo_name(&code, 1);
- MP_DBG(ictx, "key '%s' -> '%s' in '%s'\n",
- keyname, cmd->cmd, ret->input_section);
- talloc_free(keyname);
- }
+ ret->key_name = talloc_steal(ret, mp_input_get_key_combo_name(&code, 1));
+ MP_DBG(ictx, "key '%s' -> '%s' in '%s'\n",
+ ret->key_name, cmd->cmd, ret->input_section);
ret->is_mouse_button = code & MP_KEY_EMIT_ON_UP;
} else {
char *key_buf = mp_input_get_key_combo_name(&code, 1);
@@ -605,7 +604,8 @@ static void interpret_key(struct input_ctx *ictx, int code, double scale)
mp_input_queue_cmd(ictx, cmd);
}
-static void mp_input_feed_key(struct input_ctx *ictx, int code, double scale)
+static void mp_input_feed_key(struct input_ctx *ictx, int code, double scale,
+ bool force_mouse)
{
struct input_opts *opts = ictx->opts;
@@ -616,7 +616,7 @@ static void mp_input_feed_key(struct input_ctx *ictx, int code, double scale)
release_down_cmd(ictx, false);
return;
}
- if (!opts->enable_mouse_movements && MP_KEY_IS_MOUSE(unmod))
+ if (!opts->enable_mouse_movements && MP_KEY_IS_MOUSE(unmod) && !force_mouse)
return;
if (unmod == MP_KEY_MOUSE_LEAVE || unmod == MP_KEY_MOUSE_ENTER) {
update_mouse_section(ictx);
@@ -644,7 +644,14 @@ static void mp_input_feed_key(struct input_ctx *ictx, int code, double scale)
void mp_input_put_key(struct input_ctx *ictx, int code)
{
input_lock(ictx);
- mp_input_feed_key(ictx, code, 1);
+ mp_input_feed_key(ictx, code, 1, false);
+ input_unlock(ictx);
+}
+
+void mp_input_put_key_artificial(struct input_ctx *ictx, int code)
+{
+ input_lock(ictx);
+ mp_input_feed_key(ictx, code, 1, true);
input_unlock(ictx);
}
@@ -663,7 +670,7 @@ void mp_input_put_axis(struct input_ctx *ictx, int direction, double value)
if (value == 0.0)
return;
input_lock(ictx);
- mp_input_feed_key(ictx, direction, value);
+ mp_input_feed_key(ictx, direction, value, false);
input_unlock(ictx);
}
@@ -700,11 +707,17 @@ bool mp_input_vo_keyboard_enabled(struct input_ctx *ictx)
void mp_input_set_mouse_pos(struct input_ctx *ictx, int x, int y)
{
input_lock(ictx);
+ if (ictx->opts->enable_mouse_movements)
+ mp_input_set_mouse_pos_artificial(ictx, x, y);
+ input_unlock(ictx);
+}
+
+void mp_input_set_mouse_pos_artificial(struct input_ctx *ictx, int x, int y)
+{
+ input_lock(ictx);
MP_DBG(ictx, "mouse move %d/%d\n", x, y);
- if ((ictx->mouse_vo_x == x && ictx->mouse_vo_y == y) ||
- !ictx->opts->enable_mouse_movements)
- {
+ if (ictx->mouse_vo_x == x && ictx->mouse_vo_y == y) {
input_unlock(ictx);
return;
}
@@ -1265,11 +1278,7 @@ void mp_input_load(struct input_ctx *ictx)
#if defined(__MINGW32__)
if (ictx->global->opts->input_file && *ictx->global->opts->input_file)
-#if HAVE_WAIO
mp_input_pipe_add(ictx, ictx->global->opts->input_file);
-#else
- MP_ERR(ictx, "Pipes not available.\n");
-#endif
#endif
}
diff --git a/input/input.h b/input/input.h
index 2b2299d..41432eb 100644
--- a/input/input.h
+++ b/input/input.h
@@ -86,6 +86,7 @@ typedef struct mp_cmd {
double scale; // for scaling numeric arguments
const struct mp_cmd_def *def;
char *sender; // name of the client API user which sent this
+ char *key_name; // string representation of the key binding
} mp_cmd_t;
struct mp_input_src {
@@ -131,6 +132,9 @@ void mp_input_src_feed_cmd_text(struct mp_input_src *src, char *buf, size_t len)
// with modifiers applied. MP_INPUT_RELEASE_ALL is also a valid value.
void mp_input_put_key(struct input_ctx *ictx, int code);
+// Like mp_input_put_key(), but ignore mouse disable option for mouse buttons.
+void mp_input_put_key_artificial(struct input_ctx *ictx, int code);
+
// Like mp_input_put_key(), but process all UTF-8 characters in the given
// string as key events.
void mp_input_put_key_utf8(struct input_ctx *ictx, int mods, struct bstr t);
@@ -142,6 +146,9 @@ void mp_input_put_axis(struct input_ctx *ictx, int direction, double value);
// Update mouse position (in window coordinates).
void mp_input_set_mouse_pos(struct input_ctx *ictx, int x, int y);
+// Like mp_input_set_mouse_pos(), but ignore mouse disable option.
+void mp_input_set_mouse_pos_artificial(struct input_ctx *ictx, int x, int y);
+
void mp_input_get_mouse_pos(struct input_ctx *ictx, int *x, int *y);
// Return whether we want/accept mouse input.
diff --git a/input/ipc.c b/input/ipc.c
index 5ed057e..c628fc9 100644
--- a/input/ipc.c
+++ b/input/ipc.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 <pthread.h>
@@ -740,7 +740,7 @@ static void *ipc_thread(void *p)
int rc;
int ipc_fd;
- struct sockaddr_un ipc_un;
+ struct sockaddr_un ipc_un = {0};
struct mp_ipc_ctx *arg = p;
diff --git a/input/keycodes.c b/input/keycodes.c
index 21a3c10..d6c0728 100644
--- a/input/keycodes.c
+++ b/input/keycodes.c
@@ -171,6 +171,8 @@ static const struct key_name key_names[] = {
{ MP_KEY_MOUSE_LEAVE, "MOUSE_LEAVE" },
{ MP_KEY_MOUSE_ENTER, "MOUSE_ENTER" },
+ { MP_KEY_UNMAPPED, "UNMAPPED" },
+
{ 0, NULL }
};
diff --git a/input/keycodes.h b/input/keycodes.h
index d95edb1..da88d52 100644
--- a/input/keycodes.h
+++ b/input/keycodes.h
@@ -200,6 +200,9 @@
#define MP_KEY_IS_MOUSE(code) \
(MP_KEY_IS_MOUSE_CLICK(code) || MP_KEY_IS_MOUSE_MOVE(code))
+// No input source should generate this.
+#define MP_KEY_UNMAPPED (MP_KEY_INTERN+4)
+
// Emit a command even on key-up (normally key-up is ignored). This means by
// default they binding will be triggered on key-up instead of key-down.
// This is a fixed part of the keycode, not a modifier than can change.
diff --git a/input/pipe-win32.c b/input/pipe-win32.c
index 4077ea8..3d47fb6 100644
--- a/input/pipe-win32.c
+++ b/input/pipe-win32.c
@@ -1,98 +1,107 @@
-#include <pthread.h>
-#include <stdio.h>
+/*
+ * 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 <io.h>
-#include <stdint.h>
-#include <waio/waio.h>
-
#include "common/msg.h"
+#include "osdep/atomics.h"
#include "osdep/io.h"
#include "input.h"
+struct priv {
+ atomic_bool cancel_requested;
+ int fd;
+ bool close_fd;
+ HANDLE file;
+ HANDLE thread;
+};
+
static void request_cancel(struct mp_input_src *src)
{
- HANDLE terminate = src->priv;
+ struct priv *p = src->priv;
MP_VERBOSE(src, "Exiting...\n");
- SetEvent(terminate);
+ atomic_store(&p->cancel_requested, true);
+
+ // The thread might not be peforming I/O at the exact moment when
+ // CancelIoEx is called, so call it in a loop until it succeeds or the
+ // thread exits
+ do {
+ if (CancelIoEx(p->file, NULL))
+ break;
+ } while (WaitForSingleObject(p->thread, 1) != WAIT_OBJECT_0);
}
static void uninit(struct mp_input_src *src)
{
- HANDLE terminate = src->priv;
+ struct priv *p = src->priv;
+
+ CloseHandle(p->thread);
+ if (p->close_fd)
+ close(p->fd);
- CloseHandle(terminate);
MP_VERBOSE(src, "Exited.\n");
}
static void read_pipe_thread(struct mp_input_src *src, void *param)
{
char *filename = talloc_strdup(src, param);
+ struct priv *p = talloc_zero(src, struct priv);
- struct waio_cx_interface *waio = NULL;
- int mode = O_RDONLY;
- int fd = -1;
- bool close_fd = true;
+ p->fd = -1;
+ p->close_fd = true;
if (strcmp(filename, "/dev/stdin") == 0) { // for symmetry with unix
- fd = STDIN_FILENO;
- close_fd = false;
+ p->fd = STDIN_FILENO;
+ p->close_fd = false;
}
- if (fd < 0)
- fd = open(filename, mode);
- if (fd < 0) {
+ if (p->fd < 0)
+ p->fd = open(filename, O_RDONLY);
+ if (p->fd < 0) {
MP_ERR(src, "Can't open %s.\n", filename);
- goto done;
+ return;
}
- // If we're reading from stdin, unset it. All I/O on synchronous handles is
- // serialized, so stupid DLLs that call GetFileType on stdin can hang the
- // process if they do it while we're reading from it. At least, the
- // VirtualBox OpenGL ICD is affected by this, but only on Windows XP.
- // GetFileType works differently in later versions of Windows. See:
- // https://support.microsoft.com/kb/2009703
- // http://blogs.msdn.com/b/oldnewthing/archive/2011/12/02/10243553.aspx
- if ((void*)_get_osfhandle(fd) == GetStdHandle(STD_INPUT_HANDLE))
- SetStdHandle(STD_INPUT_HANDLE, NULL);
-
- waio = waio_alloc((void *)_get_osfhandle(fd), 0, NULL, NULL);
- if (!waio) {
- MP_ERR(src, "Can't initialize win32 file reader.\n");
- goto done;
+ p->file = (HANDLE)_get_osfhandle(p->fd);
+ if (!p->file || p->file == INVALID_HANDLE_VALUE) {
+ MP_ERR(src, "Can't open %s.\n", filename);
+ return;
}
- HANDLE terminate = CreateEvent(NULL, TRUE, FALSE, NULL);
- if (!terminate)
- goto done;
+ atomic_store(&p->cancel_requested, false);
+ if (!DuplicateHandle(GetCurrentProcess(), GetCurrentThread(),
+ GetCurrentProcess(), &p->thread, SYNCHRONIZE, FALSE, 0))
+ return;
- src->priv = terminate;
+ src->priv = p;
src->cancel = request_cancel;
src->uninit = uninit;
mp_input_src_init_done(src);
- char buffer[128];
- struct waio_aiocb cb = {
- .aio_buf = buffer,
- .aio_nbytes = sizeof(buffer),
- .hsignal = terminate,
- };
- while (1) {
- if (waio_read(waio, &cb)) {
- MP_ERR(src, "Read operation failed.\n");
+ char buffer[4096];
+ while (!atomic_load(&p->cancel_requested)) {
+ DWORD r;
+ if (!ReadFile(p->file, buffer, 4096, &r, NULL)) {
+ if (GetLastError() != ERROR_OPERATION_ABORTED)
+ MP_ERR(src, "Read operation failed.\n");
break;
}
- if (waio_suspend(waio, (const struct waio_aiocb *[]){&cb}, 1, NULL))
- break;
- ssize_t r = waio_return(waio, &cb);
- if (r <= 0)
- break; // EOF or error
mp_input_src_feed_cmd_text(src, buffer, r);
}
-
-done:
- waio_free(waio);
- if (close_fd)
- close(fd);
}
void mp_input_pipe_add(struct input_ctx *ictx, const char *filename)
diff --git a/libmpv/client.h b/libmpv/client.h
index 190b2ae..d5faf45 100644
--- a/libmpv/client.h
+++ b/libmpv/client.h
@@ -403,7 +403,10 @@ const char *mpv_client_name(mpv_handle *ctx);
* client handle. (Whether concurrent access is definitely allowed or not has
* yet to be decided.)
*
- * @return a new mpv client API handle
+ * @return a new mpv client API handle. Returns NULL on error. Currently, this
+ * can happen in the following situations:
+ * - out of memory
+ * - LC_NUMERIC is not set to "C" (see general remarks)
*/
mpv_handle *mpv_create(void);
@@ -602,7 +605,7 @@ typedef enum mpv_format {
* Example for writing:
*
* int flag = 1;
- * mpv_set_property(ctx, "property", MPV_FORMAT_STRING, &flag);
+ * mpv_set_property(ctx, "property", MPV_FORMAT_FLAG, &flag);
*/
MPV_FORMAT_FLAG = 3,
/**
diff --git a/misc/bstr.c b/misc/bstr.c
index b168acc..13441ee 100644
--- a/misc/bstr.c
+++ b/misc/bstr.c
@@ -24,7 +24,7 @@
#include <libavutil/common.h>
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "common/common.h"
#include "misc/ctype.h"
diff --git a/misc/bstr.h b/misc/bstr.h
index ef5591a..f565166 100644
--- a/misc/bstr.h
+++ b/misc/bstr.h
@@ -24,7 +24,7 @@
#include <stdbool.h>
#include <stdarg.h>
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "osdep/compiler.h"
/* NOTE: 'len' is size_t, but most string-handling functions below assume
diff --git a/misc/charset_conv.c b/misc/charset_conv.c
index 3e7e47c..fcc346b 100644
--- a/misc/charset_conv.c
+++ b/misc/charset_conv.c
@@ -5,18 +5,18 @@
* of MPlayer (GPL).
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
*
- * 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>
@@ -52,6 +52,13 @@ bool mp_charset_is_utf8(const char *user_cp)
strcasecmp(user_cp, "utf-8") == 0);
}
+bool mp_charset_is_utf16(const char *user_cp)
+{
+ bstr s = bstr0(user_cp);
+ return bstr_case_startswith(s, bstr0("utf16")) ||
+ bstr_case_startswith(s, bstr0("utf-16"));
+}
+
// Split the string on ':' into components.
// out_arr is at least max entries long.
// Return number of out_arr entries filled.
@@ -179,6 +186,8 @@ static const char *mp_uchardet(void *talloc_ctx, struct mp_log *log, bstr buf)
iconv_close(icdsc);
}
}
+ if (!res && bstr_validate_utf8(buf) >= 0)
+ res = "utf-8";
uchardet_delete(det);
return res;
}
diff --git a/misc/charset_conv.h b/misc/charset_conv.h
index 3d3520f..ddfabbe 100644
--- a/misc/charset_conv.h
+++ b/misc/charset_conv.h
@@ -13,6 +13,7 @@ enum {
};
bool mp_charset_is_utf8(const char *user_cp);
+bool mp_charset_is_utf16(const char *user_cp);
bool mp_charset_requires_guess(const char *user_cp);
const char *mp_charset_guess(void *talloc_ctx, struct mp_log *log, bstr buf,
const char *user_cp, int flags);
diff --git a/misc/dispatch.c b/misc/dispatch.c
index 502742b..2ddac2c 100644
--- a/misc/dispatch.c
+++ b/misc/dispatch.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 <stdbool.h>
@@ -62,10 +62,15 @@ static void queue_dtor(void *p)
pthread_mutex_destroy(&queue->exclusive_lock);
}
-// A dispatch queue lets other threads runs callbacks in a target thread.
-// The target thread is the thread which created the queue and which calls
-// mp_dispatch_queue_process().
-// Free the dispatch queue with talloc_free(). (It must be empty.)
+// A dispatch queue lets other threads run callbacks in a target thread.
+// The target thread is the thread which calls mp_dispatch_queue_process().
+// Free the dispatch queue with talloc_free(). At the time of destruction,
+// the queue must be empty. The easiest way to guarantee this is to
+// terminate all potential senders, then call mp_dispatch_run() with a
+// function that e.g. makes the target thread exit, then pthread_join() the
+// target thread, and finally destroy the queue. Another way is calling
+// mp_dispatch_queue_process() after terminating all potential senders, and
+// then destroying the queue.
struct mp_dispatch_queue *mp_dispatch_create(void *ta_parent)
{
struct mp_dispatch_queue *queue = talloc_ptrtype(ta_parent, queue);
diff --git a/misc/json.c b/misc/json.c
index b0be746..2a7c164 100644
--- a/misc/json.c
+++ b/misc/json.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/>.
*/
/* JSON parser:
diff --git a/misc/json.h b/misc/json.h
index f5ad4cf..0a9f8d7 100644
--- a/misc/json.h
+++ b/misc/json.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 MP_JSON_H
diff --git a/misc/ring.c b/misc/ring.c
index 41c9c6a..9ba4306 100644
--- a/misc/ring.c
+++ b/misc/ring.c
@@ -3,24 +3,24 @@
* Copyright (c) 2012 wm4
* Copyright (c) 2013 Stefano Pigozzi <stefano.pigozzi@gmail.com>
*
- * 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>
#include <libavutil/common.h>
#include <assert.h>
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "osdep/atomics.h"
#include "ring.h"
diff --git a/misc/ring.h b/misc/ring.h
index e93baea..604b95f 100644
--- a/misc/ring.h
+++ b/misc/ring.h
@@ -3,18 +3,18 @@
* Copyright (c) 2012 wm4
* Copyright (c) 2013 Stefano Pigozzi <stefano.pigozzi@gmail.com>
*
- * 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 MPV_MP_RING_H
diff --git a/talloc.h b/mpv_talloc.h
index 11f996b..11f996b 100644
--- a/talloc.h
+++ b/mpv_talloc.h
diff --git a/options/m_config.c b/options/m_config.c
index 0f112dc..d58c406 100644
--- a/options/m_config.c
+++ b/options/m_config.c
@@ -30,7 +30,7 @@
#include "libmpv/client.h"
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "m_config.h"
#include "options/m_option.h"
diff --git a/options/m_option.c b/options/m_option.c
index 020bd37..9f2d875 100644
--- a/options/m_option.c
+++ b/options/m_option.c
@@ -36,7 +36,7 @@
#include "libmpv/client.h"
#include "player/client.h"
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "common/common.h"
#include "common/msg.h"
#include "common/msg_control.h"
diff --git a/options/m_property.c b/options/m_property.c
index 9318d5b..951b788 100644
--- a/options/m_property.c
+++ b/options/m_property.c
@@ -30,7 +30,7 @@
#include "libmpv/client.h"
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "m_option.h"
#include "m_property.h"
#include "common/msg.h"
diff --git a/options/options.c b/options/options.c
index 058265a..ac7d706 100644
--- a/options/options.c
+++ b/options/options.c
@@ -86,6 +86,7 @@ const struct m_opt_choice_alternatives mp_hwdec_names[] = {
{"videotoolbox",HWDEC_VIDEOTOOLBOX},
{"vaapi", HWDEC_VAAPI},
{"vaapi-copy", HWDEC_VAAPI_COPY},
+ {"dxva2", HWDEC_DXVA2},
{"dxva2-copy", HWDEC_DXVA2_COPY},
{"rpi", HWDEC_RPI},
{0}
@@ -226,6 +227,8 @@ const m_option_t mp_opts[] = {
OPT_STRINGLIST("alang", stream_lang[STREAM_AUDIO], 0),
OPT_STRINGLIST("slang", stream_lang[STREAM_SUB], 0),
+ OPT_STRING("lavfi-complex", lavfi_complex, 0),
+
OPT_CHOICE("audio-display", audio_display, 0,
({"no", 0}, {"attachment", 1})),
@@ -271,9 +274,10 @@ const m_option_t mp_opts[] = {
// force video/audio rate:
OPT_DOUBLE("fps", force_fps, CONF_MIN, .min = 0),
- OPT_INTRANGE("audio-samplerate", force_srate, 0, 1000, 8*48000),
+ OPT_INTRANGE("audio-samplerate", force_srate, 0, 1000, 16*48000),
OPT_CHMAP("audio-channels", audio_output_channels, CONF_MIN, .min = 0),
OPT_AUDIOFORMAT("audio-format", audio_output_format, 0),
+ OPT_FLAG("audio-normalize-downmix", audio_normalize, 0),
OPT_DOUBLE("speed", playback_speed, M_OPT_RANGE | M_OPT_FIXED,
.min = 0.01, .max = 100.0),
@@ -332,6 +336,8 @@ const m_option_t mp_opts[] = {
OPT_STRING_APPEND_LIST("sub-file", sub_name, M_OPT_FILE),
OPT_PATHLIST("sub-paths", sub_paths, 0),
+ OPT_PATHLIST("audio-file-paths", audiofile_paths, 0),
+ OPT_STRING_APPEND_LIST("external-file", external_files, M_OPT_FILE),
OPT_STRING("sub-codepage", sub_cp, 0),
OPT_FLOAT("sub-delay", sub_delay, 0),
OPT_FLOAT("sub-fps", sub_fps, 0),
@@ -437,7 +443,7 @@ const m_option_t mp_opts[] = {
OPT_FLOATRANGE("video-align-y", vo.align_y, 0, -1.0, 1.0),
OPT_FLAG("video-unscaled", vo.unscaled, 0),
OPT_FLAG("force-rgba-osd-rendering", force_rgba_osd, 0),
- OPT_CHOICE_OR_INT("video-rotate", video_rotate, 0, 0, 360,
+ OPT_CHOICE_OR_INT("video-rotate", video_rotate, 0, 0, 359,
({"no", -1})),
OPT_CHOICE_C("video-stereo-mode", video_stereo_mode, 0, mp_stereo3d_names),
@@ -678,7 +684,7 @@ const struct MPOpts mp_default_opts = {
.use_terminal = 1,
.msg_color = 1,
.audio_driver_list = NULL,
- .audio_decoders = "lavc:libdcadec,-spdif:*", // never select spdif by default
+ .audio_decoders = "-spdif:*", // never select spdif by default
.video_decoders = NULL,
.deinterlace = -1,
.softvol = SOFTVOL_AUTO,
@@ -786,7 +792,7 @@ const struct MPOpts mp_default_opts = {
.sub_visibility = 1,
.sub_pos = 100,
.sub_speed = 1.0,
- .audio_output_channels = {0}, // auto
+ .audio_output_channels = MP_CHMAP_INIT_STEREO,
.audio_output_format = 0, // AF_FORMAT_UNKNOWN
.playback_speed = 1.,
.pitch_correction = 1,
@@ -818,13 +824,9 @@ const struct MPOpts mp_default_opts = {
.mf_fps = 1.0,
-#if HAVE_RPI
- .hwdec_api = -1,
-#endif
-
.display_tags = (char **)(const char*[]){
"Artist", "Album", "Album_Artist", "Comment", "Composer", "Genre",
- "Performer", "Title", "Track", "icy-title",
+ "Performer", "Title", "Track", "icy-title", "service_name",
NULL
},
};
diff --git a/options/options.h b/options/options.h
index 53b8cc4..95268cd 100644
--- a/options/options.h
+++ b/options/options.h
@@ -186,6 +186,7 @@ typedef struct MPOpts {
int ignore_path_in_watch_later_config;
int pause;
int keep_open;
+ char *lavfi_complex;
int stream_id[2][STREAM_TYPE_COUNT];
int stream_id_ff[STREAM_TYPE_COUNT];
char **stream_lang[STREAM_TYPE_COUNT];
@@ -225,6 +226,7 @@ typedef struct MPOpts {
struct mp_chmap audio_output_channels;
int audio_output_format;
+ int audio_normalize;
int force_srate;
int dtshd;
double playback_speed;
@@ -237,6 +239,8 @@ typedef struct MPOpts {
int field_dominance;
char **sub_name;
char **sub_paths;
+ char **audiofile_paths;
+ char **external_files;
int sub_auto;
int audiofile_auto;
int osd_bar_visible;
diff --git a/options/path.c b/options/path.c
index 5072e73..cd1deb6 100644
--- a/options/path.c
+++ b/options/path.c
@@ -39,7 +39,7 @@
#include "common/msg.h"
#include "options/options.h"
#include "options/path.h"
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "osdep/io.h"
#include "osdep/path.h"
@@ -213,6 +213,21 @@ struct bstr mp_dirname(const char *path)
return ret;
}
+
+#if HAVE_DOS_PATHS
+static const char mp_path_separators[] = "\\/";
+#else
+static const char mp_path_separators[] = "/";
+#endif
+
+// Mutates path and removes a trailing '/' (or '\' on Windows)
+void mp_path_strip_trailing_separator(char *path)
+{
+ size_t len = strlen(path);
+ if (len > 0 && strchr(mp_path_separators, path[len - 1]))
+ path[len - 1] = '\0';
+}
+
char *mp_splitext(const char *path, bstr *root)
{
assert(path);
diff --git a/options/path.h b/options/path.h
index 763a8dd..203651a 100644
--- a/options/path.h
+++ b/options/path.h
@@ -65,6 +65,8 @@ char *mp_splitext(const char *path, bstr *root);
*/
struct bstr mp_dirname(const char *path);
+void mp_path_strip_trailing_separator(char *path);
+
/* Join two path components and return a newly allocated string
* for the result. '/' is inserted between the components if needed.
* If p2 is an absolute path then the value of p1 is ignored.
diff --git a/osdep/android/strnlen.c b/osdep/android/strnlen.c
new file mode 100644
index 0000000..c8c9d3d
--- /dev/null
+++ b/osdep/android/strnlen.c
@@ -0,0 +1,40 @@
+/*-
+ * Copyright (c) 2009 David Schultz <das@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stddef.h>
+#include "osdep/android/strnlen.h"
+
+size_t
+freebsd_strnlen(const char *s, size_t maxlen)
+{
+ size_t len;
+
+ for (len = 0; len < maxlen; len++, s++) {
+ if (!*s)
+ break;
+ }
+ return (len);
+}
diff --git a/osdep/android/strnlen.h b/osdep/android/strnlen.h
new file mode 100644
index 0000000..c1f3391
--- /dev/null
+++ b/osdep/android/strnlen.h
@@ -0,0 +1,33 @@
+/*-
+ * Copyright (c) 2009 David Schultz <das@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef MP_OSDEP_ANDROID_STRNLEN
+#define MP_OSDEP_ANDROID_STRNLEN
+
+size_t
+freebsd_strnlen(const char *s, size_t maxlen);
+
+#endif
diff --git a/osdep/atomics.h b/osdep/atomics.h
index 1cac2d5..c4f3128 100644
--- a/osdep/atomics.h
+++ b/osdep/atomics.h
@@ -2,18 +2,18 @@
* This file is part of mpv.
* Copyright (c) 2013 Stefano Pigozzi <stefano.pigozzi@gmail.com>
*
- * 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 MP_ATOMICS_H
diff --git a/osdep/glob-win.c b/osdep/glob-win.c
index 30dad4d..a3485cd 100644
--- a/osdep/glob-win.c
+++ b/osdep/glob-win.c
@@ -1,25 +1,25 @@
/*
* 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 <windows.h>
#include <stdbool.h>
#include <string.h>
#include "osdep/io.h"
-#include "talloc.h"
+#include "mpv_talloc.h"
static wchar_t *talloc_wcsdup(void *ctx, const wchar_t *wcs)
{
diff --git a/osdep/io.c b/osdep/io.c
index fdb625d..5952f21 100644
--- a/osdep/io.c
+++ b/osdep/io.c
@@ -5,25 +5,25 @@
*
* 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 <unistd.h>
#include <errno.h>
#include <assert.h>
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "config.h"
#include "osdep/io.h"
diff --git a/osdep/io.h b/osdep/io.h
index 8d3e24f..541e36a 100644
--- a/osdep/io.h
+++ b/osdep/io.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_OSDEP_IO
diff --git a/osdep/macosx_application.m b/osdep/macosx_application.m
index 6e91809..21d57b4 100644
--- a/osdep/macosx_application.m
+++ b/osdep/macosx_application.m
@@ -17,7 +17,7 @@
#include <stdio.h>
#include <pthread.h>
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "common/msg.h"
#include "input/input.h"
diff --git a/osdep/macosx_events.m b/osdep/macosx_events.m
index ae909d8..6090a19 100644
--- a/osdep/macosx_events.m
+++ b/osdep/macosx_events.m
@@ -25,7 +25,7 @@
#import <IOKit/hidsystem/ev_keymap.h>
#import <Cocoa/Cocoa.h>
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "input/event.h"
#include "input/input.h"
#include "input/keycodes.h"
@@ -451,7 +451,8 @@ void cocoa_set_input_context(struct input_ctx *input_context)
else
chars = [event charactersIgnoringModifiers];
- int key = convert_key([event keyCode], *[chars UTF8String]);
+ struct bstr t = bstr0([chars UTF8String]);
+ int key = convert_key([event keyCode], bstr_decode_utf8(t, &t));
if (key > -1)
[self handleMPKey:key withMask:[self keyModifierMask:event]];
diff --git a/osdep/main-fn-win.c b/osdep/main-fn-win.c
index 5b0f220..84a77a3 100644
--- a/osdep/main-fn-win.c
+++ b/osdep/main-fn-win.c
@@ -38,15 +38,12 @@ static void microsoft_nonsense(void)
HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
HMODULE kernel32 = GetModuleHandleW(L"kernel32.dll");
- WINBOOL (WINAPI *pSetDllDirectory)(LPCWSTR lpPathName) =
- (WINBOOL (WINAPI *)(LPCWSTR))GetProcAddress(kernel32, "SetDllDirectoryW");
WINBOOL (WINAPI *pSetSearchPathMode)(DWORD Flags) =
(WINBOOL (WINAPI *)(DWORD))GetProcAddress(kernel32, "SetSearchPathMode");
// Always use safe search paths for DLLs and other files, ie. never use the
// current directory
- if (pSetSearchPathMode)
- pSetDllDirectory(L"");
+ SetDllDirectoryW(L"");
if (pSetSearchPathMode)
pSetSearchPathMode(BASE_SEARCH_PATH_ENABLE_SAFE_SEARCHMODE);
}
diff --git a/osdep/mpv.exe.manifest b/osdep/mpv.exe.manifest
index f7a45d6..31386e8 100644
--- a/osdep/mpv.exe.manifest
+++ b/osdep/mpv.exe.manifest
@@ -24,6 +24,8 @@
</trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
+ <!-- Windows 10 -->
+ <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
<!-- Windows 8.1 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<!-- Windows 8 -->
diff --git a/osdep/mpv.rc b/osdep/mpv.rc
index f2d9042..9615c8a 100644
--- a/osdep/mpv.rc
+++ b/osdep/mpv.rc
@@ -34,7 +34,7 @@ FILETYPE VFT_APP
VALUE "CompanyName", "mpv\000"
VALUE "FileDescription", "mpv\000"
VALUE "FileVersion",VERSION
- VALUE "LegalCopyright", " (C) 2000-2015 mpv/mplayer2/MPlayer\000"
+ VALUE "LegalCopyright", " (C) 2000-2016 mpv/mplayer2/MPlayer\000"
//VALUE "LegalTrademarks"," \000";
VALUE "OriginalFilename", "mpv.exe\000"
VALUE "ProductName", "mpv\000"
diff --git a/osdep/path-macosx.m b/osdep/path-macosx.m
index f011289..73abb0d 100644
--- a/osdep/path-macosx.m
+++ b/osdep/path-macosx.m
@@ -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/>.
*/
#import <Foundation/Foundation.h>
diff --git a/osdep/path-unix.c b/osdep/path-unix.c
index cea4235..d9a49ab 100644
--- a/osdep/path-unix.c
+++ b/osdep/path-unix.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 <string.h>
diff --git a/osdep/path-win.c b/osdep/path-win.c
index a735fad..de7c093 100644
--- a/osdep/path-win.c
+++ b/osdep/path-win.c
@@ -1,22 +1,24 @@
/*
* 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 <windows.h>
#include <shlobj.h>
+#include <initguid.h>
+#include <knownfolders.h>
#include <pthread.h>
#include "osdep/path.h"
@@ -52,20 +54,21 @@ static char *mp_get_win_exe_subdir(void *ta_ctx, const char *name)
return talloc_asprintf(ta_ctx, "%s/%s", mp_get_win_exe_dir(ta_ctx), name);
}
-static char *mp_get_win_shell_dir(void *talloc_ctx, int folder)
+static char *mp_get_win_shell_dir(void *talloc_ctx, REFKNOWNFOLDERID folder)
{
- wchar_t w_appdir[MAX_PATH + 1] = {0};
+ wchar_t *w_appdir = NULL;
- if (SHGetFolderPathW(NULL, folder|CSIDL_FLAG_CREATE, NULL,
- SHGFP_TYPE_CURRENT, w_appdir) != S_OK)
+ if (FAILED(SHGetKnownFolderPath(folder, KF_FLAG_CREATE, NULL, &w_appdir)))
return NULL;
- return mp_to_utf8(talloc_ctx, w_appdir);
+ char *appdir = mp_to_utf8(talloc_ctx, w_appdir);
+ CoTaskMemFree(w_appdir);
+ return appdir;
}
static char *mp_get_win_app_dir(void *talloc_ctx)
{
- char *path = mp_get_win_shell_dir(talloc_ctx, CSIDL_APPDATA);
+ char *path = mp_get_win_shell_dir(talloc_ctx, &FOLDERID_RoamingAppData);
return path ? mp_path_join(talloc_ctx, path, "mpv") : NULL;
}
@@ -95,6 +98,6 @@ const char *mp_get_platform_path_win(void *talloc_ctx, const char *type)
return mp_get_win_exe_subdir(talloc_ctx, "mpv");
}
if (strcmp(type, "desktop") == 0)
- return mp_get_win_shell_dir(talloc_ctx, CSIDL_DESKTOPDIRECTORY);
+ return mp_get_win_shell_dir(talloc_ctx, &FOLDERID_Desktop);
return NULL;
}
diff --git a/osdep/strnlen.h b/osdep/strnlen.h
new file mode 100644
index 0000000..0a971d0
--- /dev/null
+++ b/osdep/strnlen.h
@@ -0,0 +1,29 @@
+/*
+ * strnlen wrapper
+ *
+ * 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_OSDEP_STRNLEN
+#define MP_OSDEP_STRNLEN
+
+#ifdef __ANDROID__
+// strnlen is broken on current android ndk, see https://code.google.com/p/android/issues/detail?id=74741
+#include "osdep/android/strnlen.h"
+#define strnlen freebsd_strnlen
+#endif
+
+#endif
diff --git a/osdep/subprocess-posix.c b/osdep/subprocess-posix.c
index 16f9735..163559e 100644
--- a/osdep/subprocess-posix.c
+++ b/osdep/subprocess-posix.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 <spawn.h>
diff --git a/osdep/subprocess-win.c b/osdep/subprocess-win.c
index 3f03309..614cae6 100644
--- a/osdep/subprocess-win.c
+++ b/osdep/subprocess-win.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 <windows.h>
@@ -23,7 +23,7 @@
#include "osdep/io.h"
#include "osdep/atomics.h"
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "common/common.h"
#include "stream/stream.h"
#include "misc/bstr.h"
@@ -118,7 +118,8 @@ static int create_overlapped_pipe(HANDLE *read, HANDLE *write)
// overlapped pipes, so instead, use a named pipe with a unique name
*read = CreateNamedPipeW(buf, PIPE_ACCESS_INBOUND |
FILE_FLAG_FIRST_PIPE_INSTANCE | FILE_FLAG_OVERLAPPED,
- PIPE_TYPE_BYTE | PIPE_WAIT, 1, 0, 4096, 0, NULL);
+ PIPE_TYPE_BYTE | PIPE_WAIT | PIPE_REJECT_REMOTE_CLIENTS,
+ 1, 0, 4096, 0, NULL);
if (*read == INVALID_HANDLE_VALUE)
goto error;
@@ -137,15 +138,7 @@ error:
static void delete_handle_list(void *p)
{
LPPROC_THREAD_ATTRIBUTE_LIST list = p;
- VOID (WINAPI *pDeleteProcThreadAttributeList)(LPPROC_THREAD_ATTRIBUTE_LIST);
-
- HMODULE kernel32 = GetModuleHandleW(L"kernel32.dll");
- pDeleteProcThreadAttributeList =
- (VOID (WINAPI*)(LPPROC_THREAD_ATTRIBUTE_LIST))
- GetProcAddress(kernel32, "DeleteProcThreadAttributeList");
-
- if (pDeleteProcThreadAttributeList)
- pDeleteProcThreadAttributeList(list);
+ DeleteProcThreadAttributeList(list);
}
// Create a PROC_THREAD_ATTRIBUTE_LIST that specifies exactly which handles are
@@ -153,38 +146,21 @@ static void delete_handle_list(void *p)
static LPPROC_THREAD_ATTRIBUTE_LIST create_handle_list(void *ctx,
HANDLE *handles, int num)
{
- WINBOOL (WINAPI *pInitializeProcThreadAttributeList)(
- LPPROC_THREAD_ATTRIBUTE_LIST, DWORD, DWORD, PSIZE_T);
- WINBOOL (WINAPI *pUpdateProcThreadAttribute)(LPPROC_THREAD_ATTRIBUTE_LIST,
- DWORD, DWORD_PTR, PVOID, SIZE_T, PVOID, PSIZE_T);
-
- // Load Windows Vista functions, if available
- HMODULE kernel32 = GetModuleHandleW(L"kernel32.dll");
- pInitializeProcThreadAttributeList =
- (WINBOOL (WINAPI*)(LPPROC_THREAD_ATTRIBUTE_LIST, DWORD, DWORD, PSIZE_T))
- GetProcAddress(kernel32, "InitializeProcThreadAttributeList");
- pUpdateProcThreadAttribute =
- (WINBOOL (WINAPI*)(LPPROC_THREAD_ATTRIBUTE_LIST, DWORD, DWORD_PTR,
- PVOID, SIZE_T, PVOID, PSIZE_T))
- GetProcAddress(kernel32, "UpdateProcThreadAttribute");
- if (!pInitializeProcThreadAttributeList || !pUpdateProcThreadAttribute)
- return NULL;
-
// Get required attribute list size
SIZE_T size = 0;
- if (!pInitializeProcThreadAttributeList(NULL, 1, 0, &size)) {
+ if (!InitializeProcThreadAttributeList(NULL, 1, 0, &size)) {
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
return NULL;
}
// Allocate attribute list
LPPROC_THREAD_ATTRIBUTE_LIST list = talloc_size(ctx, size);
- if (!pInitializeProcThreadAttributeList(list, 1, 0, &size))
+ if (!InitializeProcThreadAttributeList(list, 1, 0, &size))
goto error;
talloc_set_destructor(list, delete_handle_list);
- if (!pUpdateProcThreadAttribute(list, 0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
- handles, num * sizeof(HANDLE), NULL, NULL))
+ if (!UpdateProcThreadAttribute(list, 0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
+ handles, num * sizeof(HANDLE), NULL, NULL))
goto error;
return list;
@@ -265,7 +241,7 @@ int mp_subprocess(char **args, struct mp_cancel *cancel, void *ctx,
// Convert the args array to a UTF-16 Windows command-line string
wchar_t *cmdline = write_cmdline(tmp, args);
- DWORD flags = CREATE_UNICODE_ENVIRONMENT;
+ DWORD flags = CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT;
PROCESS_INFORMATION pi = {0};
STARTUPINFOEXW si = {
.StartupInfo = {
@@ -284,11 +260,6 @@ int mp_subprocess(char **args, struct mp_cancel *cancel, void *ctx,
(HANDLE[]){ pipes[0].write, pipes[1].write }, 2),
};
- // PROC_THREAD_ATTRIBUTE_LISTs are only supported in Vista and up. If not
- // supported, create_handle_list will return NULL.
- if (si.lpAttributeList)
- flags |= EXTENDED_STARTUPINFO_PRESENT;
-
// If we have a console, the subprocess will automatically attach to it so
// it can receive Ctrl+C events. If we don't have a console, prevent the
// subprocess from creating its own console window by specifying
diff --git a/osdep/subprocess.c b/osdep/subprocess.c
index 84a1b52..dbd6100 100644
--- a/osdep/subprocess.c
+++ b/osdep/subprocess.c
@@ -1,22 +1,24 @@
/*
* 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 <pthread.h>
+#include "config.h"
+
#include "common/common.h"
#include "common/msg.h"
#include "common/msg_control.h"
@@ -55,3 +57,13 @@ void mp_subprocess_detached(struct mp_log *log, char **args)
if (pthread_create(&thread, NULL, run_subprocess, p))
talloc_free(p);
}
+
+#if !HAVE_SUBPROCESS
+int mp_subprocess(char **args, struct mp_cancel *cancel, void *ctx,
+ subprocess_read_cb on_stdout, subprocess_read_cb on_stderr,
+ char **error)
+{
+ *error = "unsupported";
+ return -1;
+}
+#endif
diff --git a/osdep/subprocess.h b/osdep/subprocess.h
index 33c4013..a32e791 100644
--- a/osdep/subprocess.h
+++ b/osdep/subprocess.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 MP_SUBPROCESS_H_
diff --git a/osdep/threads.c b/osdep/threads.c
index cccce85..e4bd0b7 100644
--- a/osdep/threads.c
+++ b/osdep/threads.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 <stdio.h>
#include <errno.h>
#include <pthread.h>
diff --git a/osdep/timer.c b/osdep/timer.c
index 32f0172..f75aec7 100644
--- a/osdep/timer.c
+++ b/osdep/timer.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>
diff --git a/osdep/win32-console-wrapper.c b/osdep/win32-console-wrapper.c
index 8cebcf8..778d699 100644
--- a/osdep/win32-console-wrapper.c
+++ b/osdep/win32-console-wrapper.c
@@ -37,6 +37,7 @@ void cr_perror(const wchar_t *prefix)
int cr_runproc(wchar_t *name, wchar_t *cmdline)
{
STARTUPINFO si;
+ STARTUPINFO our_si;
PROCESS_INFORMATION pi;
DWORD retval = 1;
@@ -47,6 +48,12 @@ int cr_runproc(wchar_t *name, wchar_t *cmdline)
si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
si.dwFlags |= STARTF_USESTDHANDLES;
+ // Copy the list of inherited CRT file descriptors to the new process
+ our_si.cb = sizeof(our_si);
+ GetStartupInfo(&our_si);
+ si.lpReserved2 = our_si.lpReserved2;
+ si.cbReserved2 = our_si.cbReserved2;
+
ZeroMemory(&pi, sizeof(pi));
if (!CreateProcessW(name, cmdline, NULL, NULL, TRUE, 0,
diff --git a/osdep/windows_utils.c b/osdep/windows_utils.c
new file mode 100644
index 0000000..a1ea321
--- /dev/null
+++ b/osdep/windows_utils.c
@@ -0,0 +1,152 @@
+/*
+ * 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 <stdio.h>
+#include <inttypes.h>
+
+#include <windows.h>
+#include <errors.h>
+#include <audioclient.h>
+#include <d3d9.h>
+
+#include "windows_utils.h"
+
+char *mp_GUID_to_str_buf(char *buf, size_t buf_size, const GUID *guid)
+{
+ snprintf(buf, buf_size,
+ "{%8.8x-%4.4x-%4.4x-%2.2x%2.2x-%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x}",
+ (unsigned) guid->Data1, guid->Data2, guid->Data3,
+ guid->Data4[0], guid->Data4[1],
+ guid->Data4[2], guid->Data4[3],
+ guid->Data4[4], guid->Data4[5],
+ guid->Data4[6], guid->Data4[7]);
+ return buf;
+}
+
+static char *hresult_to_str(const HRESULT hr)
+{
+#define E(x) case x : return # x ;
+ switch (hr) {
+ E(S_OK)
+ E(S_FALSE)
+ E(E_FAIL)
+ E(E_OUTOFMEMORY)
+ E(E_POINTER)
+ E(E_HANDLE)
+ E(E_NOTIMPL)
+ E(E_INVALIDARG)
+ E(E_PROP_ID_UNSUPPORTED)
+ E(E_NOINTERFACE)
+ E(REGDB_E_IIDNOTREG)
+ E(CO_E_NOTINITIALIZED)
+ E(AUDCLNT_E_NOT_INITIALIZED)
+ E(AUDCLNT_E_ALREADY_INITIALIZED)
+ E(AUDCLNT_E_WRONG_ENDPOINT_TYPE)
+ E(AUDCLNT_E_DEVICE_INVALIDATED)
+ E(AUDCLNT_E_NOT_STOPPED)
+ E(AUDCLNT_E_BUFFER_TOO_LARGE)
+ E(AUDCLNT_E_OUT_OF_ORDER)
+ E(AUDCLNT_E_UNSUPPORTED_FORMAT)
+ E(AUDCLNT_E_INVALID_SIZE)
+ E(AUDCLNT_E_DEVICE_IN_USE)
+ E(AUDCLNT_E_BUFFER_OPERATION_PENDING)
+ E(AUDCLNT_E_THREAD_NOT_REGISTERED)
+ E(AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED)
+ E(AUDCLNT_E_ENDPOINT_CREATE_FAILED)
+ E(AUDCLNT_E_SERVICE_NOT_RUNNING)
+ E(AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED)
+ E(AUDCLNT_E_EXCLUSIVE_MODE_ONLY)
+ E(AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL)
+ E(AUDCLNT_E_EVENTHANDLE_NOT_SET)
+ E(AUDCLNT_E_INCORRECT_BUFFER_SIZE)
+ E(AUDCLNT_E_BUFFER_SIZE_ERROR)
+ E(AUDCLNT_E_CPUUSAGE_EXCEEDED)
+ E(AUDCLNT_E_BUFFER_ERROR)
+ E(AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED)
+ E(AUDCLNT_E_INVALID_DEVICE_PERIOD)
+ E(AUDCLNT_E_INVALID_STREAM_FLAG)
+ E(AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE)
+ E(AUDCLNT_E_RESOURCES_INVALIDATED)
+ E(AUDCLNT_S_BUFFER_EMPTY)
+ E(AUDCLNT_S_THREAD_ALREADY_REGISTERED)
+ E(AUDCLNT_S_POSITION_STALLED)
+ E(D3DERR_WRONGTEXTUREFORMAT)
+ E(D3DERR_UNSUPPORTEDCOLOROPERATION)
+ E(D3DERR_UNSUPPORTEDCOLORARG)
+ E(D3DERR_UNSUPPORTEDALPHAOPERATION)
+ E(D3DERR_UNSUPPORTEDALPHAARG)
+ E(D3DERR_TOOMANYOPERATIONS)
+ E(D3DERR_CONFLICTINGTEXTUREFILTER)
+ E(D3DERR_UNSUPPORTEDFACTORVALUE)
+ E(D3DERR_CONFLICTINGRENDERSTATE)
+ E(D3DERR_UNSUPPORTEDTEXTUREFILTER)
+ E(D3DERR_CONFLICTINGTEXTUREPALETTE)
+ E(D3DERR_DRIVERINTERNALERROR)
+ E(D3DERR_NOTFOUND)
+ E(D3DERR_MOREDATA)
+ E(D3DERR_DEVICELOST)
+ E(D3DERR_DEVICENOTRESET)
+ E(D3DERR_NOTAVAILABLE)
+ E(D3DERR_OUTOFVIDEOMEMORY)
+ E(D3DERR_INVALIDDEVICE)
+ E(D3DERR_INVALIDCALL)
+ E(D3DERR_DRIVERINVALIDCALL)
+ E(D3DERR_WASSTILLDRAWING)
+ E(D3DOK_NOAUTOGEN)
+ E(D3DERR_DEVICEREMOVED)
+ E(D3DERR_DEVICEHUNG)
+ E(S_NOT_RESIDENT)
+ E(S_RESIDENT_IN_SHARED_MEMORY)
+ E(S_PRESENT_MODE_CHANGED)
+ E(S_PRESENT_OCCLUDED)
+ E(D3DERR_UNSUPPORTEDOVERLAY)
+ E(D3DERR_UNSUPPORTEDOVERLAYFORMAT)
+ E(D3DERR_CANNOTPROTECTCONTENT)
+ E(D3DERR_UNSUPPORTEDCRYPTO)
+ E(D3DERR_PRESENT_STATISTICS_DISJOINT)
+ default:
+ return "<Unknown>";
+ }
+#undef E
+}
+
+static char *fmtmsg_buf(char *buf, size_t buf_size, DWORD errorID)
+{
+ DWORD n = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL, errorID, 0, buf, buf_size, NULL);
+ if (!n && GetLastError() == ERROR_MORE_DATA) {
+ snprintf(buf, buf_size,
+ "<Insufficient buffer size (%zd) for error message>",
+ buf_size);
+ } else {
+ if (n > 0 && buf[n-1] == '\n')
+ buf[n-1] = '\0';
+ if (n > 1 && buf[n-2] == '\r')
+ buf[n-2] = '\0';
+ }
+ return buf;
+}
+#define fmtmsg(hr) fmtmsg_buf((char[243]){0}, 243, (hr))
+
+char *mp_HRESULT_to_str_buf(char *buf, size_t buf_size, HRESULT hr)
+{
+ char* msg = fmtmsg(hr);
+ msg = msg[0] ? msg : hresult_to_str(hr);
+ snprintf(buf, buf_size, "%s (0x%"PRIx32")", msg, (uint32_t)hr);
+ return buf;
+}
diff --git a/osdep/windows_utils.h b/osdep/windows_utils.h
new file mode 100644
index 0000000..6c750de
--- /dev/null
+++ b/osdep/windows_utils.h
@@ -0,0 +1,29 @@
+/*
+ * 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_WINDOWS_UTILS_H_
+#define MP_WINDOWS_UTILS_H_
+
+#include <windows.h>
+
+char *mp_GUID_to_str_buf(char *buf, size_t buf_size, const GUID *guid);
+#define mp_GUID_to_str(guid) mp_GUID_to_str_buf((char[40]){0}, 40, (guid))
+char *mp_HRESULT_to_str_buf(char *buf, size_t buf_size, HRESULT hr);
+#define mp_HRESULT_to_str(hr) mp_HRESULT_to_str_buf((char[256]){0}, 256, (hr))
+#define mp_LastError_to_str() mp_HRESULT_to_str(HRESULT_FROM_WIN32(GetLastError()))
+
+#endif
diff --git a/player/audio.c b/player/audio.c
index 9634536..f17587a 100644
--- a/player/audio.c
+++ b/player/audio.c
@@ -23,7 +23,7 @@
#include <assert.h>
#include "config.h"
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "common/msg.h"
#include "common/encode.h"
@@ -43,6 +43,15 @@
#include "core.h"
#include "command.h"
+enum {
+ AD_OK = 0,
+ AD_ERR = -1,
+ AD_EOF = -2,
+ AD_NEW_FMT = -3,
+ AD_WAIT = -4,
+ AD_NO_PROGRESS = -5,
+};
+
// Use pitch correction only for speed adjustments by the user, not minor sync
// correction ones.
static int get_speed_method(struct MPContext *mpctx)
@@ -55,7 +64,7 @@ static int get_speed_method(struct MPContext *mpctx)
// return true; if filter recreation is needed, return false.
static bool update_speed_filters(struct MPContext *mpctx)
{
- struct af_stream *afs = mpctx->d_audio->afilter;
+ struct af_stream *afs = mpctx->ao_chain->af;
double speed = mpctx->audio_speed;
if (afs->initialized < 1)
@@ -81,7 +90,7 @@ static bool update_speed_filters(struct MPContext *mpctx)
// Update speed, and insert/remove filters if necessary.
static void recreate_speed_filters(struct MPContext *mpctx)
{
- struct af_stream *afs = mpctx->d_audio->afilter;
+ struct af_stream *afs = mpctx->ao_chain->af;
if (update_speed_filters(mpctx))
return;
@@ -113,9 +122,9 @@ fail:
static int recreate_audio_filters(struct MPContext *mpctx)
{
- assert(mpctx->d_audio);
+ assert(mpctx->ao_chain);
- struct af_stream *afs = mpctx->d_audio->afilter;
+ struct af_stream *afs = mpctx->ao_chain->af;
if (afs->initialized < 1 && af_init(afs) < 0)
goto fail;
@@ -134,12 +143,27 @@ fail:
int reinit_audio_filters(struct MPContext *mpctx)
{
- struct dec_audio *d_audio = mpctx->d_audio;
- if (!d_audio)
+ struct ao_chain *ao_c = mpctx->ao_chain;
+ if (!ao_c)
return 0;
- af_uninit(mpctx->d_audio->afilter);
- return recreate_audio_filters(mpctx) < 0 ? -1 : 1;
+ double delay = 0;
+ if (ao_c->af->initialized > 0)
+ delay = af_calc_delay(ao_c->af);
+
+ af_uninit(ao_c->af);
+ if (recreate_audio_filters(mpctx) < 0)
+ return -1;
+
+ // Only force refresh if the amount of dropped buffered data is going to
+ // cause "issues" for the A/V sync logic.
+ if (mpctx->audio_status == STATUS_PLAYING &&
+ mpctx->playback_pts != MP_NOPTS_VALUE && delay > 0.2)
+ {
+ queue_seek(mpctx, MPSEEK_ABSOLUTE, mpctx->playback_pts,
+ MPSEEK_EXACT, true);
+ }
+ return 1;
}
// Call this if opts->playback_speed or mpctx->speed_factor_* change.
@@ -148,20 +172,31 @@ void update_playback_speed(struct MPContext *mpctx)
mpctx->audio_speed = mpctx->opts->playback_speed * mpctx->speed_factor_a;
mpctx->video_speed = mpctx->opts->playback_speed * mpctx->speed_factor_v;
- if (!mpctx->d_audio || mpctx->d_audio->afilter->initialized < 1)
+ if (!mpctx->ao_chain || mpctx->ao_chain->af->initialized < 1)
return;
if (!update_speed_filters(mpctx))
recreate_audio_filters(mpctx);
}
+static void ao_chain_reset_state(struct ao_chain *ao_c)
+{
+ ao_c->pts = MP_NOPTS_VALUE;
+ ao_c->pts_reset = false;
+ talloc_free(ao_c->input_frame);
+ ao_c->input_frame = NULL;
+ af_seek_reset(ao_c->af);
+ mp_audio_buffer_clear(ao_c->ao_buffer);
+
+ if (ao_c->audio_src)
+ audio_reset_decoding(ao_c->audio_src);
+}
+
void reset_audio_state(struct MPContext *mpctx)
{
- if (mpctx->d_audio)
- audio_reset_decoding(mpctx->d_audio);
- if (mpctx->ao_buffer)
- mp_audio_buffer_clear(mpctx->ao_buffer);
- mpctx->audio_status = mpctx->d_audio ? STATUS_SYNCING : STATUS_EOF;
+ if (mpctx->ao_chain)
+ ao_chain_reset_state(mpctx->ao_chain);
+ mpctx->audio_status = mpctx->ao_chain ? STATUS_SYNCING : STATUS_EOF;
mpctx->delay = 0;
mpctx->audio_drop_throttle = 0;
mpctx->audio_stat_start = 0;
@@ -183,57 +218,51 @@ void uninit_audio_out(struct MPContext *mpctx)
mpctx->ao_decoder_fmt = NULL;
}
+static void ao_chain_uninit(struct ao_chain *ao_c)
+{
+ struct track *track = ao_c->track;
+ if (track) {
+ assert(track->ao_c == ao_c);
+ track->ao_c = NULL;
+ assert(track->d_audio == ao_c->audio_src);
+ track->d_audio = NULL;
+ audio_uninit(ao_c->audio_src);
+ }
+
+ if (ao_c->filter_src)
+ lavfi_set_connected(ao_c->filter_src, false);
+
+ af_destroy(ao_c->af);
+ talloc_free(ao_c->input_frame);
+ talloc_free(ao_c->ao_buffer);
+ talloc_free(ao_c);
+}
+
void uninit_audio_chain(struct MPContext *mpctx)
{
- if (mpctx->d_audio) {
+ if (mpctx->ao_chain) {
mixer_uninit_audio(mpctx->mixer);
- audio_uninit(mpctx->d_audio);
- mpctx->d_audio = NULL;
- talloc_free(mpctx->ao_buffer);
- mpctx->ao_buffer = NULL;
+ ao_chain_uninit(mpctx->ao_chain);
+ mpctx->ao_chain = NULL;
+
mpctx->audio_status = STATUS_EOF;
- reselect_demux_streams(mpctx);
mp_notify(mpctx, MPV_EVENT_AUDIO_RECONFIG, NULL);
}
}
-void reinit_audio_chain(struct MPContext *mpctx)
+static void reinit_audio_filters_and_output(struct MPContext *mpctx)
{
struct MPOpts *opts = mpctx->opts;
- struct track *track = mpctx->current_track[0][STREAM_AUDIO];
- struct sh_stream *sh = track ? track->stream : NULL;
- if (!sh) {
- uninit_audio_out(mpctx);
- goto no_audio;
- }
+ struct ao_chain *ao_c = mpctx->ao_chain;
+ assert(ao_c);
+ struct track *track = ao_c->track;
+ struct af_stream *afs = ao_c->af;
- mp_notify(mpctx, MPV_EVENT_AUDIO_RECONFIG, NULL);
+ if (ao_c->input_frame)
+ mp_audio_copy_config(&ao_c->input_format, ao_c->input_frame);
- if (!mpctx->d_audio) {
- mpctx->d_audio = talloc_zero(NULL, struct dec_audio);
- mpctx->d_audio->log = mp_log_new(mpctx->d_audio, mpctx->log, "!ad");
- mpctx->d_audio->global = mpctx->global;
- mpctx->d_audio->opts = opts;
- mpctx->d_audio->header = sh;
- mpctx->d_audio->pool = mp_audio_pool_create(mpctx->d_audio);
- mpctx->d_audio->afilter = af_new(mpctx->global);
- mpctx->d_audio->afilter->replaygain_data = sh->audio->replaygain_data;
- mpctx->d_audio->spdif_passthrough = true;
- mpctx->ao_buffer = mp_audio_buffer_create(NULL);
- if (!audio_init_best_codec(mpctx->d_audio))
- goto init_error;
- reset_audio_state(mpctx);
-
- if (mpctx->ao) {
- struct mp_audio fmt;
- ao_get_format(mpctx->ao, &fmt);
- mp_audio_buffer_reinit(mpctx->ao_buffer, &fmt);
- }
- }
- assert(mpctx->d_audio);
-
- struct mp_audio in_format = mpctx->d_audio->decode_format;
+ struct mp_audio in_format = ao_c->input_format;
if (!mp_audio_config_valid(&in_format)) {
// We don't know the audio format yet - so configure it later as we're
@@ -249,7 +278,8 @@ void reinit_audio_chain(struct MPContext *mpctx)
uninit_audio_out(mpctx);
}
- struct af_stream *afs = mpctx->d_audio->afilter;
+ if (mpctx->ao && mp_audio_config_equals(&in_format, &afs->input))
+ return;
afs->output = (struct mp_audio){0};
if (mpctx->ao) {
@@ -273,7 +303,7 @@ void reinit_audio_chain(struct MPContext *mpctx)
if (!mpctx->ao) {
bool spdif_fallback = af_fmt_is_spdif(afs->output.format) &&
- mpctx->d_audio->spdif_passthrough;
+ ao_c->spdif_passthrough;
bool ao_null_fallback = opts->ao_null_fallback && !spdif_fallback;
mp_chmap_remove_useless_channels(&afs->output.channels,
@@ -283,6 +313,7 @@ void reinit_audio_chain(struct MPContext *mpctx)
mpctx->ao = ao_init_best(mpctx->global, ao_null_fallback, mpctx->input,
mpctx->encode_lavc_ctx, afs->output.rate,
afs->output.format, afs->output.channels);
+ ao_c->ao = mpctx->ao;
struct mp_audio fmt = {0};
if (mpctx->ao)
@@ -294,18 +325,22 @@ void reinit_audio_chain(struct MPContext *mpctx)
MP_ERR(mpctx, "Passthrough format unsupported.\n");
ao_uninit(mpctx->ao);
mpctx->ao = NULL;
+ ao_c->ao = NULL;
}
}
if (!mpctx->ao) {
// If spdif was used, try to fallback to PCM.
- if (spdif_fallback) {
- mpctx->d_audio->spdif_passthrough = false;
- mpctx->d_audio->spdif_failed = true;
- if (!audio_init_best_codec(mpctx->d_audio))
+ if (spdif_fallback && ao_c->audio_src) {
+ MP_VERBOSE(mpctx, "Falling back to PCM output.\n");
+ ao_c->spdif_passthrough = false;
+ ao_c->spdif_failed = true;
+ ao_c->audio_src->try_spdif = false;
+ if (!audio_init_best_codec(ao_c->audio_src))
goto init_error;
reset_audio_state(mpctx);
- reinit_audio_chain(mpctx);
+ ao_c->input_format = (struct mp_audio){0};
+ mpctx->sleeptime = 0; // reinit with new format next time
return;
}
@@ -314,7 +349,7 @@ void reinit_audio_chain(struct MPContext *mpctx)
goto init_error;
}
- mp_audio_buffer_reinit(mpctx->ao_buffer, &fmt);
+ mp_audio_buffer_reinit(ao_c->ao_buffer, &fmt);
afs->output = fmt;
if (!mp_audio_config_equals(&afs->output, &afs->filter_output))
afs->initialized = 0;
@@ -338,47 +373,125 @@ void reinit_audio_chain(struct MPContext *mpctx)
init_error:
uninit_audio_chain(mpctx);
uninit_audio_out(mpctx);
+ error_on_track(mpctx, track);
+}
+
+int init_audio_decoder(struct MPContext *mpctx, struct track *track)
+{
+ assert(!track->d_audio);
+ if (!track->stream)
+ goto init_error;
+
+ track->d_audio = talloc_zero(NULL, struct dec_audio);
+ struct dec_audio *d_audio = track->d_audio;
+ d_audio->log = mp_log_new(d_audio, mpctx->log, "!ad");
+ d_audio->global = mpctx->global;
+ d_audio->opts = mpctx->opts;
+ d_audio->header = track->stream;
+ d_audio->codec = track->stream->codec;
+
+ d_audio->try_spdif = true;
+
+ if (!audio_init_best_codec(d_audio))
+ goto init_error;
+
+ return 1;
+
+init_error:
+ if (track->sink)
+ lavfi_set_connected(track->sink, false);
+ track->sink = NULL;
+ audio_uninit(track->d_audio);
+ track->d_audio = NULL;
+ error_on_track(mpctx, track);
+ return 0;
+}
+
+void reinit_audio_chain(struct MPContext *mpctx)
+{
+ reinit_audio_chain_src(mpctx, NULL);
+}
+
+void reinit_audio_chain_src(struct MPContext *mpctx, struct lavfi_pad *src)
+{
+ struct track *track = NULL;
+ struct sh_stream *sh = NULL;
+ if (!src) {
+ track = mpctx->current_track[0][STREAM_AUDIO];
+ if (!track)
+ return;
+ sh = track->stream;
+ if (!sh) {
+ uninit_audio_out(mpctx);
+ goto no_audio;
+ }
+ }
+ assert(!mpctx->ao_chain);
+
+ mp_notify(mpctx, MPV_EVENT_AUDIO_RECONFIG, NULL);
+
+ struct ao_chain *ao_c = talloc_zero(NULL, struct ao_chain);
+ mpctx->ao_chain = ao_c;
+ ao_c->log = mpctx->log;
+ ao_c->af = af_new(mpctx->global);
+ if (sh)
+ ao_c->af->replaygain_data = sh->codec->replaygain_data;
+ ao_c->spdif_passthrough = true;
+ ao_c->pts = MP_NOPTS_VALUE;
+ ao_c->ao_buffer = mp_audio_buffer_create(NULL);
+ ao_c->ao = mpctx->ao;
+
+ ao_c->filter_src = src;
+ if (!ao_c->filter_src) {
+ ao_c->track = track;
+ track->ao_c = ao_c;
+ if (!init_audio_decoder(mpctx, track))
+ goto init_error;
+ ao_c->audio_src = track->d_audio;
+ }
+
+ reset_audio_state(mpctx);
+
+ if (mpctx->ao) {
+ struct mp_audio fmt;
+ ao_get_format(mpctx->ao, &fmt);
+ mp_audio_buffer_reinit(ao_c->ao_buffer, &fmt);
+ }
+
+ mpctx->sleeptime = 0;
+ return;
+
+init_error:
+ uninit_audio_chain(mpctx);
+ uninit_audio_out(mpctx);
no_audio:
- if (track)
- error_on_track(mpctx, track);
+ error_on_track(mpctx, track);
}
// Return pts value corresponding to the end point of audio written to the
// ao so far.
double written_audio_pts(struct MPContext *mpctx)
{
- struct dec_audio *d_audio = mpctx->d_audio;
- if (!d_audio)
+ struct ao_chain *ao_c = mpctx->ao_chain;
+ if (!ao_c)
return MP_NOPTS_VALUE;
- struct mp_audio in_format = d_audio->decode_format;
+ struct mp_audio in_format = ao_c->input_format;
- if (!mp_audio_config_valid(&in_format) || d_audio->afilter->initialized < 1)
+ if (!mp_audio_config_valid(&in_format) || ao_c->af->initialized < 1)
return MP_NOPTS_VALUE;
// first calculate the end pts of audio that has been output by decoder
- double a_pts = d_audio->pts;
+ double a_pts = ao_c->pts;
if (a_pts == MP_NOPTS_VALUE)
return MP_NOPTS_VALUE;
- // d_audio->pts is the timestamp of the latest input packet with
- // known pts that the decoder has decoded. d_audio->pts_bytes is
- // the amount of bytes the decoder has written after that timestamp.
- a_pts += d_audio->pts_offset / (double)in_format.rate;
-
- // Now a_pts hopefully holds the pts for end of audio from decoder.
- // Subtract data in buffers between decoder and audio out.
-
- // Decoded but not filtered
- if (d_audio->waiting)
- a_pts -= d_audio->waiting->samples / (double)in_format.rate;
-
// Data buffered in audio filters, measured in seconds of "missing" output
- double buffered_output = af_calc_delay(d_audio->afilter);
+ double buffered_output = af_calc_delay(ao_c->af);
// Data that was ready for ao but was buffered because ao didn't fully
// accept everything to internal buffers yet
- buffered_output += mp_audio_buffer_seconds(mpctx->ao_buffer);
+ buffered_output += mp_audio_buffer_seconds(ao_c->ao_buffer);
// Filters divide audio length by audio_speed, so multiply by it
// to get the length in original units without speedup or slowdown
@@ -464,18 +577,19 @@ static bool get_sync_samples(struct MPContext *mpctx, int *skip)
}
double written_pts = written_audio_pts(mpctx);
- if (written_pts == MP_NOPTS_VALUE && !mp_audio_buffer_samples(mpctx->ao_buffer))
+ if (written_pts == MP_NOPTS_VALUE &&
+ !mp_audio_buffer_samples(mpctx->ao_chain->ao_buffer))
return false; // no audio read yet
- bool sync_to_video = mpctx->d_video && mpctx->sync_audio_to_video &&
+ bool sync_to_video = mpctx->vo_chain && !mpctx->vo_chain->is_coverart &&
mpctx->video_status != STATUS_EOF;
double sync_pts = MP_NOPTS_VALUE;
if (sync_to_video) {
if (mpctx->video_status < STATUS_READY)
return false; // wait until we know a video PTS
- if (mpctx->video_next_pts != MP_NOPTS_VALUE)
- sync_pts = mpctx->video_next_pts - opts->audio_delay;
+ if (mpctx->video_pts != MP_NOPTS_VALUE)
+ sync_pts = mpctx->video_pts - opts->audio_delay;
} else if (mpctx->hrseek_active) {
sync_pts = mpctx->hrseek_pts;
}
@@ -486,34 +600,137 @@ static bool get_sync_samples(struct MPContext *mpctx, int *skip)
double ptsdiff = written_pts - sync_pts;
// Missing timestamp, or PTS reset, or just broken.
- if (written_pts == MP_NOPTS_VALUE || fabs(ptsdiff) > 3600) {
+ if (written_pts == MP_NOPTS_VALUE) {
MP_WARN(mpctx, "Failed audio resync.\n");
mpctx->audio_status = STATUS_FILLING;
return true;
}
+ ptsdiff = MPCLAMP(ptsdiff, -3600, 3600);
int align = af_format_sample_alignment(out_format.format);
*skip = (int)(-ptsdiff * play_samplerate) / align * align;
return true;
}
-void fill_audio_out_buffers(struct MPContext *mpctx, double endpts)
+
+static bool copy_output(struct af_stream *afs, struct mp_audio_buffer *outbuf,
+ int minsamples, bool eof)
+{
+ while (mp_audio_buffer_samples(outbuf) < minsamples) {
+ if (af_output_frame(afs, eof) < 0)
+ return true; // error, stop doing stuff
+ struct mp_audio *mpa = af_read_output_frame(afs);
+ if (!mpa)
+ return false; // out of data
+ mp_audio_buffer_append(outbuf, mpa);
+ talloc_free(mpa);
+ }
+ return true;
+}
+
+static int decode_new_frame(struct ao_chain *ao_c)
+{
+ if (ao_c->input_frame)
+ return AD_OK;
+
+ int res = DATA_EOF;
+ if (ao_c->filter_src) {
+ res = lavfi_request_frame_a(ao_c->filter_src, &ao_c->input_frame);
+ } else if (ao_c->audio_src) {
+ audio_work(ao_c->audio_src);
+ res = audio_get_frame(ao_c->audio_src, &ao_c->input_frame);
+ }
+
+ switch (res) {
+ case DATA_OK: return AD_OK;
+ case DATA_WAIT: return AD_WAIT;
+ case DATA_AGAIN: return AD_NO_PROGRESS;
+ case DATA_EOF: return AD_EOF;
+ default: abort();
+ }
+}
+
+/* Try to get at least minsamples decoded+filtered samples in outbuf
+ * (total length including possible existing data).
+ * Return 0 on success, or negative AD_* error code.
+ * In the former case outbuf has at least minsamples buffered on return.
+ * In case of EOF/error it might or might not be. */
+static int filter_audio(struct ao_chain *ao_c, struct mp_audio_buffer *outbuf,
+ int minsamples)
+{
+ struct af_stream *afs = ao_c->af;
+ if (afs->initialized < 1)
+ return AD_ERR;
+
+ MP_STATS(ao_c, "start audio");
+
+ int res;
+ while (1) {
+ res = 0;
+
+ if (copy_output(afs, outbuf, minsamples, false))
+ break;
+
+ res = decode_new_frame(ao_c);
+ if (res == AD_NO_PROGRESS)
+ break;
+ if (res < 0) {
+ // drain filters first (especially for true EOF case)
+ copy_output(afs, outbuf, minsamples, true);
+ break;
+ }
+
+ // On format change, make sure to drain the filter chain.
+ if (!mp_audio_config_equals(&afs->input, ao_c->input_frame)) {
+ copy_output(afs, outbuf, minsamples, true);
+ res = AD_NEW_FMT;
+ break;
+ }
+
+ struct mp_audio *mpa = ao_c->input_frame;
+ ao_c->input_frame = NULL;
+ if (mpa->pts == MP_NOPTS_VALUE) {
+ ao_c->pts = MP_NOPTS_VALUE;
+ } else {
+ // Attempt to detect jumps in PTS. Even for the lowest sample rates
+ // and with worst container rounded timestamp, this should be a
+ // margin more than enough.
+ double desync = fabs(mpa->pts - ao_c->pts);
+ if (ao_c->pts != MP_NOPTS_VALUE && desync > 0.1) {
+ MP_WARN(ao_c, "Invalid audio PTS: %f -> %f\n",
+ ao_c->pts, mpa->pts);
+ if (desync >= 5)
+ ao_c->pts_reset = true;
+ }
+ ao_c->pts = mpa->pts + mpa->samples / (double)mpa->rate;
+ }
+ if (af_filter_frame(afs, mpa) < 0)
+ return AD_ERR;
+ }
+
+ MP_STATS(ao_c, "end audio");
+
+ return res;
+}
+
+void fill_audio_out_buffers(struct MPContext *mpctx)
{
struct MPOpts *opts = mpctx->opts;
- struct dec_audio *d_audio = mpctx->d_audio;
+ struct ao_chain *ao_c = mpctx->ao_chain;
dump_audio_stats(mpctx);
if (mpctx->ao && ao_query_and_reset_events(mpctx->ao, AO_EVENT_RELOAD)) {
ao_reset(mpctx->ao);
uninit_audio_out(mpctx);
- if (d_audio) {
- if (mpctx->d_audio->spdif_failed) {
- mpctx->d_audio->spdif_failed = false;
- mpctx->d_audio->spdif_passthrough = true;
- if (!audio_init_best_codec(mpctx->d_audio)) {
+ if (ao_c) {
+ struct dec_audio *d_audio = ao_c->audio_src;
+ if (d_audio && ao_c->spdif_failed) {
+ ao_c->spdif_failed = false;
+ d_audio->try_spdif = true;
+ if (!audio_init_best_codec(d_audio)) {
MP_ERR(mpctx, "Error reinitializing audio.\n");
- error_on_track(mpctx, mpctx->current_track[0][STREAM_AUDIO]);
+ error_on_track(mpctx, ao_c->track);
return;
}
}
@@ -521,28 +738,31 @@ void fill_audio_out_buffers(struct MPContext *mpctx, double endpts)
}
}
- if (!d_audio)
+ if (!ao_c)
return;
- if (d_audio->afilter->initialized < 1 || !mpctx->ao) {
+ if (ao_c->af->initialized < 1 || !mpctx->ao) {
// Probe the initial audio format. Returns AD_OK (and does nothing) if
// the format is already known.
- int r = initial_audio_decode(mpctx->d_audio);
+ int r = decode_new_frame(mpctx->ao_chain);
if (r == AD_WAIT)
return; // continue later when new data is available
- if (r != AD_OK) {
- mpctx->d_audio->init_retries += 1;
- if (mpctx->d_audio->init_retries >= 50) {
- MP_ERR(mpctx, "Error initializing audio.\n");
- error_on_track(mpctx, mpctx->current_track[0][STREAM_AUDIO]);
- return;
- }
+ if (r == AD_EOF) {
+ mpctx->audio_status = STATUS_EOF;
+ return;
}
- reinit_audio_chain(mpctx);
+ reinit_audio_filters_and_output(mpctx);
mpctx->sleeptime = 0;
return; // try again next iteration
}
+ if (mpctx->vo_chain && ao_c->pts_reset) {
+ MP_VERBOSE(mpctx, "Reset playback due to audio timestamp reset.\n");
+ reset_playback_state(mpctx);
+ mpctx->sleeptime = 0;
+ return;
+ }
+
struct mp_audio out_format = {0};
ao_get_format(mpctx->ao, &out_format);
double play_samplerate = out_format.rate / mpctx->audio_speed;
@@ -585,10 +805,14 @@ void fill_audio_out_buffers(struct MPContext *mpctx, double endpts)
int status = AD_OK;
bool working = false;
- if (playsize > mp_audio_buffer_samples(mpctx->ao_buffer)) {
- status = audio_decode(d_audio, mpctx->ao_buffer, playsize);
+ if (playsize > mp_audio_buffer_samples(ao_c->ao_buffer)) {
+ status = filter_audio(mpctx->ao_chain, ao_c->ao_buffer, playsize);
if (status == AD_WAIT)
return;
+ if (status == AD_NO_PROGRESS) {
+ mpctx->sleeptime = 0;
+ return;
+ }
if (status == AD_NEW_FMT) {
/* The format change isn't handled too gracefully. A more precise
* implementation would require draining buffered old-format audio
@@ -596,7 +820,7 @@ void fill_audio_out_buffers(struct MPContext *mpctx, double endpts)
*/
if (mpctx->opts->gapless_audio < 1)
uninit_audio_out(mpctx);
- reinit_audio_chain(mpctx);
+ reinit_audio_filters_and_output(mpctx);
mpctx->sleeptime = 0;
return; // retry on next iteration
}
@@ -615,10 +839,11 @@ void fill_audio_out_buffers(struct MPContext *mpctx, double endpts)
bool end_sync = false;
if (skip >= 0) {
- int max = mp_audio_buffer_samples(mpctx->ao_buffer);
- mp_audio_buffer_skip(mpctx->ao_buffer, MPMIN(skip, max));
+ int max = mp_audio_buffer_samples(ao_c->ao_buffer);
+ mp_audio_buffer_skip(ao_c->ao_buffer, MPMIN(skip, max));
// If something is left, we definitely reached the target time.
end_sync |= sync_known && skip < max;
+ working |= skip > 0;
} else if (skip < 0) {
if (-skip > playsize) { // heuristic against making the buffer too large
ao_reset(mpctx->ao); // some AOs repeat data on underflow
@@ -626,20 +851,20 @@ void fill_audio_out_buffers(struct MPContext *mpctx, double endpts)
mpctx->delay = 0;
return;
}
- mp_audio_buffer_prepend_silence(mpctx->ao_buffer, -skip);
+ mp_audio_buffer_prepend_silence(ao_c->ao_buffer, -skip);
end_sync = true;
}
if (skip_duplicate) {
- int max = mp_audio_buffer_samples(mpctx->ao_buffer);
+ int max = mp_audio_buffer_samples(ao_c->ao_buffer);
if (abs(skip_duplicate) > max)
skip_duplicate = skip_duplicate >= 0 ? max : -max;
mpctx->last_av_difference += skip_duplicate / play_samplerate;
if (skip_duplicate >= 0) {
- mp_audio_buffer_skip(mpctx->ao_buffer, skip_duplicate);
+ mp_audio_buffer_skip(ao_c->ao_buffer, skip_duplicate);
MP_STATS(mpctx, "drop-audio");
} else {
- mp_audio_buffer_duplicate(mpctx->ao_buffer, -skip_duplicate);
+ mp_audio_buffer_duplicate(ao_c->ao_buffer, -skip_duplicate);
MP_STATS(mpctx, "duplicate-audio");
}
MP_VERBOSE(mpctx, "audio skip_duplicate=%d\n", skip_duplicate);
@@ -648,7 +873,7 @@ void fill_audio_out_buffers(struct MPContext *mpctx, double endpts)
if (mpctx->audio_status == STATUS_SYNCING) {
if (end_sync)
mpctx->audio_status = STATUS_FILLING;
- if (status != AD_OK && !mp_audio_buffer_samples(mpctx->ao_buffer))
+ if (status != AD_OK && !mp_audio_buffer_samples(ao_c->ao_buffer))
mpctx->audio_status = STATUS_EOF;
if (working || end_sync)
mpctx->sleeptime = 0;
@@ -659,8 +884,8 @@ void fill_audio_out_buffers(struct MPContext *mpctx, double endpts)
// Even if we're done decoding and syncing, let video start first - this is
// required, because sending audio to the AO already starts playback.
- if (mpctx->audio_status == STATUS_FILLING && mpctx->sync_audio_to_video &&
- mpctx->video_status <= STATUS_READY)
+ if (mpctx->audio_status == STATUS_FILLING && mpctx->vo_chain &&
+ !mpctx->vo_chain->is_coverart && mpctx->video_status <= STATUS_READY)
{
mpctx->audio_status = STATUS_READY;
return;
@@ -670,6 +895,7 @@ void fill_audio_out_buffers(struct MPContext *mpctx, double endpts)
bool partial_fill = false;
int playflags = 0;
+ double endpts = get_play_end_pts(mpctx);
if (endpts != MP_NOPTS_VALUE) {
double samples = (endpts - written_audio_pts(mpctx) - opts->audio_delay)
* play_samplerate;
@@ -680,8 +906,8 @@ void fill_audio_out_buffers(struct MPContext *mpctx, double endpts)
}
}
- if (playsize > mp_audio_buffer_samples(mpctx->ao_buffer)) {
- playsize = mp_audio_buffer_samples(mpctx->ao_buffer);
+ if (playsize > mp_audio_buffer_samples(ao_c->ao_buffer)) {
+ playsize = mp_audio_buffer_samples(ao_c->ao_buffer);
partial_fill = true;
}
@@ -693,13 +919,13 @@ void fill_audio_out_buffers(struct MPContext *mpctx, double endpts)
playflags |= AOPLAY_FINAL_CHUNK;
struct mp_audio data;
- mp_audio_buffer_peek(mpctx->ao_buffer, &data);
+ mp_audio_buffer_peek(ao_c->ao_buffer, &data);
if (audio_eof || data.samples >= align)
data.samples = data.samples / align * align;
data.samples = MPMIN(data.samples, mpctx->paused ? 0 : playsize);
int played = write_to_ao(mpctx, &data, playflags);
assert(played >= 0 && played <= data.samples);
- mp_audio_buffer_skip(mpctx->ao_buffer, played);
+ mp_audio_buffer_skip(ao_c->ao_buffer, played);
mpctx->audio_drop_throttle =
MPMAX(0, mpctx->audio_drop_throttle - played / play_samplerate);
@@ -721,6 +947,4 @@ void clear_audio_output_buffers(struct MPContext *mpctx)
{
if (mpctx->ao)
ao_reset(mpctx->ao);
- if (mpctx->ao_buffer)
- mp_audio_buffer_clear(mpctx->ao_buffer);
}
diff --git a/player/client.c b/player/client.c
index f4758f6..d3b0567 100644
--- a/player/client.c
+++ b/player/client.c
@@ -21,6 +21,7 @@
#include <assert.h>
#include "common/common.h"
+#include "common/global.h"
#include "common/msg.h"
#include "common/msg_control.h"
#include "input/input.h"
@@ -137,6 +138,7 @@ void mp_clients_init(struct MPContext *mpctx)
*mpctx->clients = (struct mp_client_api) {
.mpctx = mpctx,
};
+ mpctx->global->client_api = mpctx->clients;
pthread_mutex_init(&mpctx->clients->lock, NULL);
}
@@ -264,6 +266,11 @@ struct MPContext *mp_client_get_core(struct mpv_handle *ctx)
return ctx->mpctx;
}
+struct MPContext *mp_client_api_get_core(struct mp_client_api *api)
+{
+ return api->mpctx;
+}
+
static void wakeup_client(struct mpv_handle *ctx)
{
pthread_mutex_lock(&ctx->wakeup_lock);
@@ -451,7 +458,7 @@ void mpv_terminate_destroy(mpv_handle *ctx)
static bool check_locale(void)
{
char *name = setlocale(LC_NUMERIC, NULL);
- return strcmp(name, "C") == 0;
+ return !name || strcmp(name, "C") == 0;
}
mpv_handle *mpv_create(void)
@@ -826,8 +833,10 @@ static bool conv_node_to_format(void *dst, mpv_format dst_fmt, mpv_node *src)
return true;
}
if (dst_fmt == MPV_FORMAT_INT64 && src->format == MPV_FORMAT_DOUBLE) {
- if (src->u.double_ >= INT64_MIN && src->u.double_ <= INT64_MAX)
+ if (src->u.double_ >= INT64_MIN && src->u.double_ <= INT64_MAX) {
*(int64_t *)dst = src->u.double_;
+ return true;
+ }
}
return false;
}
diff --git a/player/client.h b/player/client.h
index 5bc3c77..a9d6cbd 100644
--- a/player/client.h
+++ b/player/client.h
@@ -31,6 +31,7 @@ void mp_client_property_change(struct MPContext *mpctx, const char *name);
struct mpv_handle *mp_new_client(struct mp_client_api *clients, const char *name);
struct mp_log *mp_client_get_log(struct mpv_handle *ctx);
struct MPContext *mp_client_get_core(struct mpv_handle *ctx);
+struct MPContext *mp_client_api_get_core(struct mp_client_api *api);
void mp_resume_all(struct mpv_handle *ctx);
diff --git a/player/command.c b/player/command.c
index cc30d2b..642330e 100644
--- a/player/command.c
+++ b/player/command.c
@@ -30,7 +30,7 @@
#include <libavutil/common.h>
#include "config.h"
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "client.h"
#include "common/msg.h"
#include "common/msg_control.h"
@@ -240,7 +240,7 @@ static char *format_file_size(int64_t size)
static char *format_delay(double time)
{
- return talloc_asprintf(NULL, "%d ms", ROUND(time * 1000));
+ return talloc_asprintf(NULL, "%d ms", (int)lrint(time * 1000));
}
// Property-option bridge. (Maps the property to the option with the same name.)
@@ -350,7 +350,7 @@ static int mp_property_stream_open_filename(void *ctx, struct m_property *prop,
return M_PROPERTY_UNAVAILABLE;
switch (action) {
case M_PROPERTY_SET: {
- if (mpctx->stream)
+ if (mpctx->demuxer)
return M_PROPERTY_ERROR;
mpctx->stream_open_filename =
talloc_strdup(mpctx->stream_open_filename, *(char **)arg);
@@ -390,11 +390,14 @@ static int mp_property_media_title(void *ctx, struct m_property *prop,
name = mpctx->opts->media_title;
if (name && name[0])
return m_property_strdup_ro(action, arg, name);
- if (mpctx->master_demuxer) {
- name = mp_tags_get_str(mpctx->master_demuxer->metadata, "title");
+ if (mpctx->demuxer) {
+ name = mp_tags_get_str(mpctx->demuxer->metadata, "service_name");
+ if (name && name[0])
+ return m_property_strdup_ro(action, arg, name);
+ name = mp_tags_get_str(mpctx->demuxer->metadata, "title");
if (name && name[0])
return m_property_strdup_ro(action, arg, name);
- name = mp_tags_get_str(mpctx->master_demuxer->metadata, "icy-title");
+ name = mp_tags_get_str(mpctx->demuxer->metadata, "icy-title");
if (name && name[0])
return m_property_strdup_ro(action, arg, name);
}
@@ -436,7 +439,7 @@ static int mp_property_demuxer(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
- struct demuxer *demuxer = mpctx->master_demuxer;
+ struct demuxer *demuxer = mpctx->demuxer;
if (!demuxer)
return M_PROPERTY_UNAVAILABLE;
return m_property_strdup_ro(action, arg, demuxer->desc->name);
@@ -446,7 +449,7 @@ static int mp_property_file_format(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
- struct demuxer *demuxer = mpctx->master_demuxer;
+ struct demuxer *demuxer = mpctx->demuxer;
if (!demuxer)
return M_PROPERTY_UNAVAILABLE;
const char *name = demuxer->filetype ? demuxer->filetype : demuxer->desc->name;
@@ -533,7 +536,7 @@ static int mp_property_avsync(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
- if (!mpctx->d_audio || !mpctx->d_video)
+ if (!mpctx->ao_chain || !mpctx->vo_chain)
return M_PROPERTY_UNAVAILABLE;
if (action == M_PROPERTY_PRINT) {
*(char **)arg = talloc_asprintf(NULL, "%7.3f", mpctx->last_av_difference);
@@ -546,7 +549,7 @@ static int mp_property_total_avsync_change(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
- if (!mpctx->d_audio || !mpctx->d_video)
+ if (!mpctx->ao_chain || !mpctx->vo_chain)
return M_PROPERTY_UNAVAILABLE;
if (mpctx->total_avsync_change == MP_NOPTS_VALUE)
return M_PROPERTY_UNAVAILABLE;
@@ -557,17 +560,17 @@ static int mp_property_drop_frame_cnt(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
- if (!mpctx->d_video)
+ if (!mpctx->vo_chain)
return M_PROPERTY_UNAVAILABLE;
- return m_property_int_ro(action, arg, mpctx->dropped_frames_total);
+ return m_property_int_ro(action, arg, mpctx->vo_chain->video_src->dropped_frames);
}
static int mp_property_mistimed_frame_count(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
- if (!mpctx->d_video || !mpctx->display_sync_active)
+ if (!mpctx->vo_chain || !mpctx->display_sync_active)
return M_PROPERTY_UNAVAILABLE;
return m_property_int_ro(action, arg, mpctx->mistimed_frames_total);
@@ -577,7 +580,7 @@ static int mp_property_vsync_ratio(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
- if (!mpctx->d_video || !mpctx->display_sync_active)
+ if (!mpctx->vo_chain || !mpctx->display_sync_active)
return M_PROPERTY_UNAVAILABLE;
int vsyncs = 0, frames = 0;
@@ -599,7 +602,7 @@ static int mp_property_vo_drop_frame_count(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
- if (!mpctx->d_video)
+ if (!mpctx->vo_chain)
return M_PROPERTY_UNAVAILABLE;
return m_property_int_ro(action, arg, vo_get_drop_count(mpctx->video_out));
@@ -609,7 +612,7 @@ static int mp_property_vo_delayed_frame_count(void *ctx, struct m_property *prop
int action, void *arg)
{
MPContext *mpctx = ctx;
- if (!mpctx->d_video)
+ if (!mpctx->vo_chain)
return M_PROPERTY_UNAVAILABLE;
return m_property_int_ro(action, arg, vo_get_delayed_count(mpctx->video_out));
@@ -731,7 +734,7 @@ static int mp_property_disc_title(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
- struct demuxer *d = mpctx->master_demuxer;
+ struct demuxer *d = mpctx->demuxer;
if (!d)
return M_PROPERTY_UNAVAILABLE;
unsigned int title = -1;
@@ -791,9 +794,7 @@ static int mp_property_chapter(void *ctx, struct m_property *prop,
int step_all;
if (action == M_PROPERTY_SWITCH) {
struct m_property_switch_arg *sarg = arg;
- step_all = ROUND(sarg->inc);
- if (num < 2) // semi-broken file; ignore for user convenience
- return M_PROPERTY_UNAVAILABLE;
+ step_all = lrint(sarg->inc);
// Check threshold for relative backward seeks
if (mpctx->opts->chapter_seek_threshold >= 0 && step_all < 0) {
double current_chapter_start =
@@ -814,6 +815,9 @@ static int mp_property_chapter(void *ctx, struct m_property *prop,
if (mpctx->opts->keep_open) {
seek_to_last_frame(mpctx);
} else {
+ // semi-broken file; ignore for user convenience
+ if (action == M_PROPERTY_SWITCH && num < 2)
+ return M_PROPERTY_UNAVAILABLE;
if (!mpctx->stop_play)
mpctx->stop_play = PT_NEXT_ENTRY;
}
@@ -883,7 +887,7 @@ static int mp_property_edition(void *ctx, struct m_property *prop,
{
MPContext *mpctx = ctx;
struct MPOpts *opts = mpctx->opts;
- struct demuxer *demuxer = mpctx->master_demuxer;
+ struct demuxer *demuxer = mpctx->demuxer;
if (!demuxer)
return M_PROPERTY_UNAVAILABLE;
if (demuxer->num_editions <= 0)
@@ -922,7 +926,7 @@ static int get_edition_entry(int item, int action, void *arg, void *ctx)
{
struct MPContext *mpctx = ctx;
- struct demuxer *demuxer = mpctx->master_demuxer;
+ struct demuxer *demuxer = mpctx->demuxer;
struct demux_edition *ed = &demuxer->editions[item];
char *title = mp_tags_get_str(ed->metadata, "title");
@@ -942,7 +946,7 @@ static int property_list_editions(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
- struct demuxer *demuxer = mpctx->master_demuxer;
+ struct demuxer *demuxer = mpctx->demuxer;
if (!demuxer)
return M_PROPERTY_UNAVAILABLE;
@@ -983,7 +987,7 @@ static int mp_property_disc_titles(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
- struct demuxer *demuxer = mpctx->master_demuxer;
+ struct demuxer *demuxer = mpctx->demuxer;
unsigned int num_titles;
if (!demuxer || demux_stream_control(demuxer, STREAM_CTRL_GET_NUM_TITLES,
&num_titles) < 1)
@@ -994,7 +998,7 @@ static int mp_property_disc_titles(void *ctx, struct m_property *prop,
static int get_disc_title_entry(int item, int action, void *arg, void *ctx)
{
struct MPContext *mpctx = ctx;
- struct demuxer *demuxer = mpctx->master_demuxer;
+ struct demuxer *demuxer = mpctx->demuxer;
double len = item;
if (demux_stream_control(demuxer, STREAM_CTRL_GET_TITLE_LENGTH, &len) < 1)
@@ -1014,7 +1018,7 @@ static int mp_property_list_disc_titles(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
- struct demuxer *demuxer = mpctx->master_demuxer;
+ struct demuxer *demuxer = mpctx->demuxer;
unsigned int num_titles;
if (!demuxer || demux_stream_control(demuxer, STREAM_CTRL_GET_NUM_TITLES,
&num_titles) < 1)
@@ -1038,7 +1042,7 @@ static int mp_property_editions(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
- struct demuxer *demuxer = mpctx->master_demuxer;
+ struct demuxer *demuxer = mpctx->demuxer;
if (!demuxer)
return M_PROPERTY_UNAVAILABLE;
if (demuxer->num_editions <= 0)
@@ -1051,7 +1055,7 @@ static int mp_property_angle(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
- struct demuxer *demuxer = mpctx->master_demuxer;
+ struct demuxer *demuxer = mpctx->demuxer;
if (!demuxer)
return M_PROPERTY_UNAVAILABLE;
@@ -1192,7 +1196,7 @@ static int mp_property_metadata(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
- struct demuxer *demuxer = mpctx->master_demuxer;
+ struct demuxer *demuxer = mpctx->demuxer;
if (!demuxer)
return M_PROPERTY_UNAVAILABLE;
@@ -1233,14 +1237,14 @@ static int mp_property_filter_metadata(void *ctx, struct m_property *prop,
struct mp_tags metadata = {0};
int res = CONTROL_UNKNOWN;
if (strcmp(type, "vf") == 0) {
- if (!(mpctx->d_video && mpctx->d_video->vfilter))
+ if (!mpctx->vo_chain)
return M_PROPERTY_UNAVAILABLE;
- struct vf_chain *vf = mpctx->d_video->vfilter;
+ struct vf_chain *vf = mpctx->vo_chain->vf;
res = vf_control_by_label(vf, VFCTRL_GET_METADATA, &metadata, key);
} else if (strcmp(type, "af") == 0) {
- if (!(mpctx->d_audio && mpctx->d_audio->afilter))
+ if (!(mpctx->ao_chain && mpctx->ao_chain->af))
return M_PROPERTY_UNAVAILABLE;
- struct af_stream *af = mpctx->d_audio->afilter;
+ struct af_stream *af = mpctx->ao_chain->af;
res = af_control_by_label(af, AF_CONTROL_GET_METADATA, &metadata, key);
}
switch (res) {
@@ -1534,13 +1538,18 @@ static int mp_property_partially_seekable(void *ctx, struct m_property *prop,
return m_property_flag_ro(action, arg, mpctx->demuxer->partially_seekable);
}
+static int mp_property_mixer_active(void *ctx, struct m_property *prop,
+ int action, void *arg)
+{
+ MPContext *mpctx = ctx;
+ return m_property_flag_ro(action, arg, mixer_audio_initialized(mpctx->mixer));
+}
+
/// Volume (RW)
static int mp_property_volume(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
- if (!mixer_audio_initialized(mpctx->mixer))
- return M_PROPERTY_UNAVAILABLE;
switch (action) {
case M_PROPERTY_GET:
mixer_getbothvolume(mpctx->mixer, arg);
@@ -1574,13 +1583,18 @@ static int mp_property_volume(void *ctx, struct m_property *prop,
return M_PROPERTY_NOT_IMPLEMENTED;
}
+static int mp_property_volume_max(void *ctx, struct m_property *prop,
+ int action, void *arg)
+{
+ MPContext *mpctx = ctx;
+ return m_property_float_ro(action, arg, mixer_getmaxvolume(mpctx->mixer));
+}
+
/// Mute (RW)
static int mp_property_mute(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
- if (!mixer_audio_initialized(mpctx->mixer))
- return M_PROPERTY_ERROR;
switch (action) {
case M_PROPERTY_SET:
mixer_setmute(mpctx->mixer, *(int *) arg);
@@ -1692,7 +1706,7 @@ static int mp_property_audio_delay(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
- if (!(mpctx->d_audio && mpctx->d_video))
+ if (!(mpctx->ao_chain && mpctx->vo_chain))
return M_PROPERTY_UNAVAILABLE;
float delay = mpctx->opts->audio_delay;
switch (action) {
@@ -1712,7 +1726,8 @@ static int mp_property_audio_codec_name(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
- const char *c = mpctx->d_audio ? mpctx->d_audio->header->codec : NULL;
+ struct track *track = mpctx->current_track[0][STREAM_AUDIO];
+ const char *c = track && track->stream ? track->stream->codec->codec : NULL;
return m_property_strdup_ro(action, arg, c);
}
@@ -1721,7 +1736,8 @@ static int mp_property_audio_codec(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
- const char *c = mpctx->d_audio ? mpctx->d_audio->decoder_desc : NULL;
+ struct track *track = mpctx->current_track[0][STREAM_AUDIO];
+ const char *c = track && track->d_audio ? track->d_audio->decoder_desc : NULL;
return m_property_strdup_ro(action, arg, c);
}
@@ -1747,8 +1763,8 @@ static int mp_property_audio_params(void *ctx, struct m_property *prop,
{
MPContext *mpctx = ctx;
struct mp_audio fmt = {0};
- if (mpctx->d_audio)
- fmt = mpctx->d_audio->decode_format;
+ if (mpctx->ao_chain)
+ fmt = mpctx->ao_chain->input_format;
return property_audiofmt(fmt, action, arg);
}
@@ -1804,27 +1820,18 @@ static int mp_property_balance(void *ctx, struct m_property *prop,
return M_PROPERTY_NOT_IMPLEMENTED;
}
-static struct track* track_next(struct MPContext *mpctx, int order,
- enum stream_type type, int direction,
- struct track *track)
+static struct track* track_next(struct MPContext *mpctx, enum stream_type type,
+ int direction, struct track *track)
{
assert(direction == -1 || direction == +1);
struct track *prev = NULL, *next = NULL;
bool seen = track == NULL;
for (int n = 0; n < mpctx->num_tracks; n++) {
struct track *cur = mpctx->tracks[n];
- // One track can be selected only one time - pretend already selected
- // tracks don't exist.
- for (int r = 0; r < NUM_PTRACKS; r++) {
- if (r != order && mpctx->current_track[r][type] == cur)
- cur = NULL;
- }
- if (!cur)
- continue;
if (cur->type == type) {
if (cur == track) {
seen = true;
- } else {
+ } else if (!cur->selected) {
if (seen && !next) {
next = cur;
}
@@ -1873,7 +1880,7 @@ static int property_switch_track(struct m_property *prop, int action, void *arg,
return M_PROPERTY_ERROR;
struct m_property_switch_arg *sarg = arg;
mp_switch_track_n(mpctx, order, type,
- track_next(mpctx, order, type, sarg->inc >= 0 ? +1 : -1, track),
+ track_next(mpctx, type, sarg->inc >= 0 ? +1 : -1, track),
FLAG_MARK_SELECTION);
print_track_list(mpctx, "Track switched:");
return M_PROPERTY_OK;
@@ -1929,8 +1936,7 @@ static int property_switch_track_ff(void *ctx, struct m_property *prop,
static int track_channels(struct track *track)
{
- return track->stream && track->stream->audio
- ? track->stream->audio->channels.num : 0;
+ return track->stream ? track->stream->codec->channels.num : 0;
}
static int get_track_entry(int item, int action, void *arg, void *ctx)
@@ -1938,7 +1944,7 @@ static int get_track_entry(int item, int action, void *arg, void *ctx)
struct MPContext *mpctx = ctx;
struct track *track = mpctx->tracks[item];
- const char *codec = track->stream ? track->stream->codec : NULL;
+ const char *codec = track->stream ? track->stream->codec->codec : NULL;
struct m_sub_property props[] = {
{"id", SUB_PROP_INT(track->user_tid)},
@@ -2010,7 +2016,7 @@ static int property_list_tracks(void *ctx, struct m_property *prop,
res = talloc_asprintf_append(res, "\n");
}
- struct demuxer *demuxer = mpctx->master_demuxer;
+ struct demuxer *demuxer = mpctx->demuxer;
if (demuxer && demuxer->num_editions > 1)
res = talloc_asprintf_append(res, "\nEdition: %d of %d\n",
demuxer->edition + 1,
@@ -2055,7 +2061,7 @@ static int mp_property_program(void *ctx, struct m_property *prop,
MPContext *mpctx = ctx;
demux_program_t prog;
- struct demuxer *demuxer = mpctx->master_demuxer;
+ struct demuxer *demuxer = mpctx->demuxer;
if (!demuxer)
return M_PROPERTY_UNAVAILABLE;
@@ -2098,8 +2104,9 @@ static int mp_property_hwdec(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
+ struct track *track = mpctx->current_track[0][STREAM_VIDEO];
+ struct dec_video *vd = track ? track->d_video : NULL;
struct MPOpts *opts = mpctx->opts;
- struct dec_video *vd = mpctx->d_video;
if (action == M_PROPERTY_SET) {
int new = *(int *)arg;
@@ -2115,9 +2122,8 @@ static int mp_property_hwdec(void *ctx, struct m_property *prop,
int current = -2;
video_vd_control(vd, VDCTRL_GET_HWDEC, &current);
if (current != opts->hwdec_api) {
+ video_vd_control(vd, VDCTRL_REINIT, NULL);
double last_pts = mpctx->last_vo_pts;
- uninit_video_chain(mpctx);
- reinit_video_chain(mpctx);
if (last_pts != MP_NOPTS_VALUE)
queue_seek(mpctx, MPSEEK_ABSOLUTE, last_pts, MPSEEK_EXACT, true);
}
@@ -2130,7 +2136,8 @@ static int mp_property_hwdec_active(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
- struct dec_video *vd = mpctx->d_video;
+ struct track *track = mpctx->current_track[0][STREAM_VIDEO];
+ struct dec_video *vd = track ? track->d_video : NULL;
bool active = false;
if (vd) {
int current = 0;
@@ -2144,7 +2151,8 @@ static int mp_property_detected_hwdec(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
- struct dec_video *vd = mpctx->d_video;
+ struct track *track = mpctx->current_track[0][STREAM_VIDEO];
+ struct dec_video *vd = track ? track->d_video : NULL;
switch (action) {
case M_PROPERTY_GET_TYPE: {
@@ -2181,10 +2189,10 @@ static bool probe_deint_filter(struct MPContext *mpctx, const char *filt)
static bool check_output_format(struct MPContext *mpctx, int imgfmt)
{
- struct dec_video *vd = mpctx->d_video;
- if (!vd)
+ struct vo_chain *vo_c = mpctx->vo_chain;
+ if (!vo_c)
return false;
- return vd->vfilter->allowed_output_formats[imgfmt - IMGFMT_START];
+ return vo_c->vf->allowed_output_formats[imgfmt - IMGFMT_START];
}
static int probe_deint_filters(struct MPContext *mpctx)
@@ -2207,20 +2215,20 @@ static int probe_deint_filters(struct MPContext *mpctx)
if (check_output_format(mpctx, IMGFMT_VAAPI) &&
probe_deint_filter(mpctx, "vavpp"))
return 0;
- if (probe_deint_filter(mpctx, "yadif:mode=field:interlaced-only=yes"))
+ if (probe_deint_filter(mpctx, "yadif"))
return 0;
return -1;
}
static int get_deinterlacing(struct MPContext *mpctx)
{
- struct dec_video *vd = mpctx->d_video;
+ struct vo_chain *vo_c = mpctx->vo_chain;
int enabled = 0;
- if (video_vf_vo_control(vd, VFCTRL_GET_DEINTERLACE, &enabled) != CONTROL_OK)
+ if (video_vf_vo_control(vo_c, VFCTRL_GET_DEINTERLACE, &enabled) != CONTROL_OK)
enabled = -1;
if (enabled < 0) {
// vf_lavfi doesn't support VFCTRL_GET_DEINTERLACE
- if (vf_find_by_label(vd->vfilter, VF_DEINTERLACE_LABEL))
+ if (vf_find_by_label(vo_c->vf, VF_DEINTERLACE_LABEL))
enabled = 1;
}
return enabled;
@@ -2233,14 +2241,14 @@ void remove_deint_filter(struct MPContext *mpctx)
void set_deinterlacing(struct MPContext *mpctx, bool enable)
{
- struct dec_video *vd = mpctx->d_video;
- if (vf_find_by_label(vd->vfilter, VF_DEINTERLACE_LABEL)) {
+ struct vo_chain *vo_c = mpctx->vo_chain;
+ if (vf_find_by_label(vo_c->vf, VF_DEINTERLACE_LABEL)) {
if (!enable)
remove_deint_filter(mpctx);
} else {
if ((get_deinterlacing(mpctx) > 0) != enable) {
int arg = enable;
- if (video_vf_vo_control(vd, VFCTRL_SET_DEINTERLACE, &arg) != CONTROL_OK)
+ if (video_vf_vo_control(vo_c, VFCTRL_SET_DEINTERLACE, &arg) != CONTROL_OK)
probe_deint_filters(mpctx);
}
}
@@ -2251,7 +2259,7 @@ static int mp_property_deinterlace(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
- if (!mpctx->d_video || !mpctx->d_video->vfilter)
+ if (!mpctx->vo_chain)
return mp_property_generic_option(mpctx, prop, action, arg);
switch (action) {
case M_PROPERTY_GET:
@@ -2355,11 +2363,11 @@ static int get_frame_count(struct MPContext *mpctx)
{
struct demuxer *demuxer = mpctx->demuxer;
if (!demuxer)
- return 0;
- if (!mpctx->d_video)
- return 0;
+ return -1;
+ if (!mpctx->vo_chain)
+ return -1;
double len = get_time_length(mpctx);
- double fps = mpctx->d_video->fps;
+ double fps = mpctx->vo_chain->container_fps;
if (len < 0 || fps <= 0)
return 0;
@@ -2370,33 +2378,23 @@ static int mp_property_frame_number(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
- if (!mpctx->d_video)
+ int frames = get_frame_count(mpctx);
+ if (frames < 0)
return M_PROPERTY_UNAVAILABLE;
- int frame_number = ROUND(get_current_pos_ratio(mpctx, false) *
- (double)get_frame_count(mpctx));
- return m_property_int_ro(action, arg, frame_number);
+ return m_property_int_ro(action, arg,
+ lrint(get_current_pos_ratio(mpctx, false) * frames));
}
static int mp_property_frame_count(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
-
- if (!mpctx->d_video)
+ int frames = get_frame_count(mpctx);
+ if (frames < 0)
return M_PROPERTY_UNAVAILABLE;
- return m_property_int_ro(action, arg, get_frame_count(mpctx));
-}
-
-static int mp_property_framedrop(void *ctx, struct m_property *prop,
- int action, void *arg)
-{
- MPContext *mpctx = ctx;
- if (!mpctx->d_video)
- return M_PROPERTY_UNAVAILABLE;
-
- return mp_property_generic_option(mpctx, prop, action, arg);
+ return m_property_int_ro(action, arg, frames);
}
static int mp_property_video_color(void *ctx, struct m_property *prop,
@@ -2404,17 +2402,17 @@ static int mp_property_video_color(void *ctx, struct m_property *prop,
{
const char *name = prop->priv ? prop->priv : prop->name;
MPContext *mpctx = ctx;
- if (!mpctx->d_video)
+ if (!mpctx->vo_chain)
return M_PROPERTY_UNAVAILABLE;
switch (action) {
case M_PROPERTY_SET: {
- if (video_set_colors(mpctx->d_video, name, *(int *) arg) <= 0)
+ if (video_set_colors(mpctx->vo_chain, name, *(int *) arg) <= 0)
return M_PROPERTY_UNAVAILABLE;
break;
}
case M_PROPERTY_GET:
- if (video_get_colors(mpctx->d_video, name, (int *)arg) <= 0)
+ if (video_get_colors(mpctx->vo_chain, name, (int *)arg) <= 0)
return M_PROPERTY_UNAVAILABLE;
// Write new value to option variable
mp_property_generic_option(mpctx, prop, M_PROPERTY_SET, arg);
@@ -2431,7 +2429,8 @@ static int mp_property_video_format(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
- const char *c = mpctx->d_video ? mpctx->d_video->header->codec : NULL;
+ struct track *track = mpctx->current_track[0][STREAM_VIDEO];
+ const char *c = track && track->stream ? track->stream->codec->codec : NULL;
return m_property_strdup_ro(action, arg, c);
}
@@ -2440,7 +2439,8 @@ static int mp_property_video_codec(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
- const char *c = mpctx->d_video ? mpctx->d_video->decoder_desc : NULL;
+ struct track *track = mpctx->current_track[0][STREAM_VIDEO];
+ const char *c = track && track->d_video ? track->d_video->decoder_desc : NULL;
return m_property_strdup_ro(action, arg, c);
}
@@ -2449,8 +2449,8 @@ static int property_imgparams(struct mp_image_params p, int action, void *arg)
if (!p.imgfmt)
return M_PROPERTY_UNAVAILABLE;
- double dar = p.d_w / (double)p.d_h;
- double sar = p.w / (double)p.h;
+ int d_w, d_h;
+ mp_image_params_get_dsize(&p, &d_w, &d_h);
struct mp_imgfmt_desc desc = mp_imgfmt_get_desc(p.imgfmt);
int bpp = 0;
@@ -2465,10 +2465,10 @@ static int property_imgparams(struct mp_image_params p, int action, void *arg)
.unavailable = !(desc.flags & MP_IMGFLAG_PLANAR)},
{"w", SUB_PROP_INT(p.w)},
{"h", SUB_PROP_INT(p.h)},
- {"dw", SUB_PROP_INT(p.d_w)},
- {"dh", SUB_PROP_INT(p.d_h)},
- {"aspect", SUB_PROP_FLOAT(dar)},
- {"par", SUB_PROP_FLOAT(dar / sar)},
+ {"dw", SUB_PROP_INT(d_w)},
+ {"dh", SUB_PROP_INT(d_h)},
+ {"aspect", SUB_PROP_FLOAT(d_w / (double)d_h)},
+ {"par", SUB_PROP_FLOAT(p.p_w / (double)p.p_h)},
{"colormatrix",
SUB_PROP_STR(m_opt_choice_str(mp_csp_names, p.colorspace))},
{"colorlevels",
@@ -2492,11 +2492,10 @@ static int property_imgparams(struct mp_image_params p, int action, void *arg)
static struct mp_image_params get_video_out_params(struct MPContext *mpctx)
{
- if (!mpctx->d_video || !mpctx->d_video->vfilter ||
- mpctx->d_video->vfilter->initialized < 1)
+ if (!mpctx->vo_chain || mpctx->vo_chain->vf->initialized < 1)
return (struct mp_image_params){0};
- return mpctx->d_video->vfilter->output_params;
+ return mpctx->vo_chain->vf->output_params;
}
static int mp_property_vo_imgparams(void *ctx, struct m_property *prop,
@@ -2509,18 +2508,20 @@ static int mp_property_vd_imgparams(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
- struct dec_video *vd = mpctx->d_video;
- if (!vd)
+ struct vo_chain *vo_c = mpctx->vo_chain;
+ if (!vo_c)
return M_PROPERTY_UNAVAILABLE;
- struct sh_video *sh = vd->header->video;
- if (vd->vfilter->override_params.imgfmt) {
- return property_imgparams(vd->vfilter->override_params, action, arg);
- } else if (sh->disp_w && sh->disp_h) {
+ struct track *track = mpctx->current_track[0][STREAM_VIDEO];
+ struct mp_codec_params *c =
+ track && track->stream ? track->stream->codec : NULL;
+ if (vo_c->vf->input_params.imgfmt) {
+ return property_imgparams(vo_c->vf->input_params, action, arg);
+ } else if (c && c->disp_w && c->disp_h) {
// Simplistic fallback for stupid scripts querying "width"/"height"
// before the first frame is decoded.
struct m_sub_property props[] = {
- {"w", SUB_PROP_INT(sh->disp_w)},
- {"h", SUB_PROP_INT(sh->disp_h)},
+ {"w", SUB_PROP_INT(c->disp_w)},
+ {"h", SUB_PROP_INT(c->disp_h)},
{0}
};
return m_property_read_sub(props, action, arg);
@@ -2561,8 +2562,8 @@ static int mp_property_window_scale(void *ctx, struct m_property *prop,
return M_PROPERTY_UNAVAILABLE;
struct mp_image_params params = get_video_out_params(mpctx);
- int vid_w = params.d_w;
- int vid_h = params.d_h;
+ int vid_w, vid_h;
+ mp_image_params_get_dsize(&params, &vid_w, &vid_h);
if (vid_w < 1 || vid_h < 1)
return M_PROPERTY_UNAVAILABLE;
@@ -2743,7 +2744,7 @@ static int mp_property_fps(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
- float fps = mpctx->d_video ? mpctx->d_video->fps : 0;
+ float fps = mpctx->vo_chain ? mpctx->vo_chain->container_fps : 0;
if (fps < 0.1 || !isfinite(fps))
return M_PROPERTY_UNAVAILABLE;;
return m_property_float_ro(action, arg, fps);
@@ -2753,7 +2754,7 @@ static int mp_property_vf_fps(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
- if (!mpctx->d_video)
+ if (!mpctx->vo_chain)
return M_PROPERTY_UNAVAILABLE;
double avg = calc_average_frame_duration(mpctx);
if (avg <= 0)
@@ -2766,27 +2767,41 @@ static int mp_property_aspect(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
+
+ float aspect = mpctx->opts->movie_aspect;
+ if (mpctx->vo_chain && aspect <= 0) {
+ struct mp_image_params *params = &mpctx->vo_chain->vf->input_params;
+ if (params && params->p_w > 0 && params->p_h > 0) {
+ int d_w, d_h;
+ mp_image_params_get_dsize(params, &d_w, &d_h);
+ aspect = (float)d_w / d_h;
+ }
+ }
+ struct track *track = mpctx->current_track[0][STREAM_VIDEO];
+ if (track && track->d_video && aspect <= 0) {
+ struct dec_video *d_video = track->d_video;
+ struct mp_codec_params *c = d_video->header->codec;
+ if (c->disp_w && c->disp_h)
+ aspect = (float)c->disp_w / c->disp_h;
+ }
+
switch (action) {
case M_PROPERTY_SET: {
mpctx->opts->movie_aspect = *(float *)arg;
- if (mpctx->d_video) {
- reinit_video_filters(mpctx);
+ if (track && track->d_video) {
+ video_reset_aspect(track->d_video);
mp_force_video_refresh(mpctx);
}
return M_PROPERTY_OK;
}
- case M_PROPERTY_GET: {
- float aspect = mpctx->opts->movie_aspect;
- if (mpctx->d_video && aspect <= 0) {
- struct dec_video *d_video = mpctx->d_video;
- struct sh_video *sh_video = d_video->header->video;
- struct mp_image_params *params = &d_video->vfilter->override_params;
- if (params && params->d_w && params->d_h) {
- aspect = (float)params->d_w / params->d_h;
- } else if (sh_video->disp_w && sh_video->disp_h) {
- aspect = (float)sh_video->disp_w / sh_video->disp_h;
- }
+ case M_PROPERTY_PRINT: {
+ if (mpctx->opts->movie_aspect <= 0) {
+ *(char **)arg = talloc_asprintf(NULL, "%.3f (original)", aspect);
+ return M_PROPERTY_OK;
}
+ break;
+ }
+ case M_PROPERTY_GET: {
*(float *)arg = aspect;
return M_PROPERTY_OK;
}
@@ -2978,7 +2993,6 @@ static int mp_property_dvb_channel(void *ctx, struct m_property *prop,
int r;
switch (action) {
case M_PROPERTY_SET:
- mpctx->last_dvb_step = 1;
r = prop_stream_ctrl(mpctx, STREAM_CTRL_DVB_SET_CHANNEL, arg);
if (r == M_PROPERTY_OK && !mpctx->stop_play)
mpctx->stop_play = PT_RELOAD_FILE;
@@ -2986,7 +3000,6 @@ static int mp_property_dvb_channel(void *ctx, struct m_property *prop,
case M_PROPERTY_SWITCH: {
struct m_property_switch_arg *sa = arg;
int dir = sa->inc >= 0 ? 1 : -1;
- mpctx->last_dvb_step = dir;
r = prop_stream_ctrl(mpctx, STREAM_CTRL_DVB_STEP_CHANNEL, &dir);
if (r == M_PROPERTY_OK && !mpctx->stop_play)
mpctx->stop_play = PT_RELOAD_FILE;
@@ -2999,6 +3012,35 @@ static int mp_property_dvb_channel(void *ctx, struct m_property *prop,
return M_PROPERTY_NOT_IMPLEMENTED;
}
+static int mp_property_dvb_channel_name(void *ctx, struct m_property *prop,
+ int action, void *arg)
+{
+ MPContext *mpctx = ctx;
+ int r;
+ switch (action) {
+ case M_PROPERTY_SET:
+ r = prop_stream_ctrl(mpctx, STREAM_CTRL_DVB_SET_CHANNEL_NAME, arg);
+ if (r == M_PROPERTY_OK && !mpctx->stop_play)
+ mpctx->stop_play = PT_RELOAD_FILE;
+ return r;
+ case M_PROPERTY_SWITCH: {
+ struct m_property_switch_arg *sa = arg;
+ int dir = sa->inc >= 0 ? 1 : -1;
+ r = prop_stream_ctrl(mpctx, STREAM_CTRL_DVB_STEP_CHANNEL, &dir);
+ if (r == M_PROPERTY_OK && !mpctx->stop_play)
+ mpctx->stop_play = PT_RELOAD_FILE;
+ return r;
+ }
+ case M_PROPERTY_GET: {
+ return prop_stream_ctrl(mpctx, STREAM_CTRL_DVB_GET_CHANNEL_NAME, arg);
+ }
+ case M_PROPERTY_GET_TYPE:
+ *(struct m_option *)arg = (struct m_option){.type = CONF_TYPE_STRING};
+ return M_PROPERTY_OK;
+ }
+ return M_PROPERTY_NOT_IMPLEMENTED;
+}
+
static int mp_property_playlist_pos(void *ctx, struct m_property *prop,
int action, void *arg)
{
@@ -3514,7 +3556,9 @@ static const struct m_property mp_properties[] = {
M_PROPERTY_ALIAS("playlist-count", "playlist/count"),
// Audio
+ {"mixer-active", mp_property_mixer_active},
{"volume", mp_property_volume},
+ {"volume-max", mp_property_volume_max},
{"mute", mp_property_mute},
{"audio-delay", mp_property_audio_delay},
{"audio-codec-name", mp_property_audio_codec_name},
@@ -3538,7 +3582,7 @@ static const struct m_property mp_properties[] = {
{"ontop", mp_property_ontop},
{"border", mp_property_border},
{"on-all-workspaces", mp_property_all_workspaces},
- {"framedrop", mp_property_framedrop},
+ {"framedrop", mp_property_generic_option},
{"gamma", mp_property_video_color},
{"brightness", mp_property_video_color},
{"contrast", mp_property_video_color},
@@ -3626,6 +3670,7 @@ static const struct m_property mp_properties[] = {
{"tv-scan", mp_property_tv_scan},
{"tv-channel", mp_property_tv_channel},
{"dvb-channel", mp_property_dvb_channel},
+ {"dvb-channel-name", mp_property_dvb_channel_name},
{"cursor-autohide", mp_property_cursor_autohide},
@@ -3694,15 +3739,15 @@ static const char *const *const mp_event_property_change[] = {
E(MPV_EVENT_AUDIO_RECONFIG, "audio-format", "audio-codec", "audio-bitrate",
"samplerate", "channels", "audio", "volume", "mute", "balance",
"volume-restore-data", "current-ao", "audio-codec-name", "audio-params",
- "audio-out-params"),
- E(MPV_EVENT_SEEK, "seeking", "core-idle"),
- E(MPV_EVENT_PLAYBACK_RESTART, "seeking", "core-idle"),
+ "audio-out-params", "volume-max", "mixer-active"),
+ E(MPV_EVENT_SEEK, "seeking", "core-idle", "eof-reached"),
+ E(MPV_EVENT_PLAYBACK_RESTART, "seeking", "core-idle", "eof-reached"),
E(MPV_EVENT_METADATA_UPDATE, "metadata", "filtered-metadata", "media-title"),
E(MPV_EVENT_CHAPTER_CHANGE, "chapter", "chapter-metadata"),
E(MP_EVENT_CACHE_UPDATE, "cache", "cache-free", "cache-used", "cache-idle",
"demuxer-cache-duration", "demuxer-cache-idle", "paused-for-cache",
"demuxer-cache-time"),
- E(MP_EVENT_WIN_RESIZE, "window-scale"),
+ E(MP_EVENT_WIN_RESIZE, "window-scale", "osd-width", "osd-height", "osd-par"),
E(MP_EVENT_WIN_STATE, "window-minimized", "display-names", "display-fps", "fullscreen"),
};
#undef E
@@ -3999,9 +4044,6 @@ static int set_filters(struct MPContext *mpctx, enum stream_type mediatype,
reinit_filters(mpctx, mediatype);
}
- if (mediatype == STREAM_VIDEO)
- mp_force_video_refresh(mpctx);
-
return success ? 0 : -1;
}
@@ -4526,7 +4568,8 @@ int run_command(struct MPContext *mpctx, struct mp_cmd *cmd, struct mpv_node *re
case MP_CMD_SUB_SEEK: {
if (!mpctx->playback_initialized)
return -1;
- struct dec_sub *sub = mpctx->d_sub[0];
+ struct track *track = mpctx->current_track[0][STREAM_SUB];
+ struct dec_sub *sub = track ? track->d_sub : NULL;
double refpts = get_current_time(mpctx);
if (sub && refpts != MP_NOPTS_VALUE) {
double a[2];
@@ -4542,7 +4585,7 @@ int run_command(struct MPContext *mpctx, struct mp_cmd *cmd, struct mpv_node *re
// frame which actually shows the sub first (because video
// frame PTS and sub PTS rarely match exactly). Add some
// rounding for the mess of it.
- a[0] += 0.01 * (a[1] > 0 ? 1 : -1);
+ a[0] += 0.01 * (a[1] >= 0 ? 1 : -1);
mark_seek(mpctx);
queue_seek(mpctx, MPSEEK_RELATIVE, a[0], MPSEEK_EXACT, false);
set_osd_function(mpctx, (a[0] > 0) ? OSD_FFW : OSD_REW);
@@ -4890,6 +4933,18 @@ int run_command(struct MPContext *mpctx, struct mp_cmd *cmd, struct mpv_node *re
return edit_filters_osd(mpctx, STREAM_VIDEO, cmd->args[0].v.s,
cmd->args[1].v.s, msg_osd);
+ case MP_CMD_VF_COMMAND:
+ if (!mpctx->vo_chain)
+ return -1;
+ return vf_send_command(mpctx->vo_chain->vf, cmd->args[0].v.s,
+ cmd->args[1].v.s, cmd->args[2].v.s);
+
+ case MP_CMD_AF_COMMAND:
+ if (!mpctx->ao_chain)
+ return -1;
+ return af_send_command(mpctx->ao_chain->af, cmd->args[0].v.s,
+ cmd->args[1].v.s, cmd->args[2].v.s);
+
case MP_CMD_SCRIPT_BINDING: {
mpv_event_client_message event = {0};
char *name = cmd->args[0].v.s;
@@ -4906,8 +4961,8 @@ int run_command(struct MPContext *mpctx, struct mp_cmd *cmd, struct mpv_node *re
char state[3] = {'p', cmd->is_mouse_button ? 'm' : '-'};
if (cmd->is_up_down)
state[0] = cmd->repeated ? 'r' : (cmd->is_up ? 'u' : 'd');
- event.num_args = 3;
- event.args = (const char*[3]){"key-binding", name, state};
+ event.num_args = 4;
+ event.args = (const char*[4]){"key-binding", name, state, cmd->key_name};
if (mp_client_send_event_dup(mpctx, target,
MPV_EVENT_CLIENT_MESSAGE, &event) < 0)
{
@@ -4988,7 +5043,7 @@ int run_command(struct MPContext *mpctx, struct mp_cmd *cmd, struct mpv_node *re
const int x = cmd->args[0].v.i, y = cmd->args[1].v.i;
int button = cmd->args[2].v.i;
if (button == -1) {// no button
- mp_input_set_mouse_pos(mpctx->input, x, y);
+ mp_input_set_mouse_pos_artificial(mpctx->input, x, y);
break;
}
if (button < 0 || button >= 20) {// invalid button
@@ -4997,8 +5052,8 @@ int run_command(struct MPContext *mpctx, struct mp_cmd *cmd, struct mpv_node *re
}
const bool dbc = cmd->args[3].v.i;
button += dbc ? MP_MOUSE_BASE_DBL : MP_MOUSE_BASE;
- mp_input_set_mouse_pos(mpctx->input, x, y);
- mp_input_put_key(mpctx->input, button);
+ mp_input_set_mouse_pos_artificial(mpctx->input, x, y);
+ mp_input_put_key_artificial(mpctx->input, button);
break;
}
@@ -5013,21 +5068,21 @@ int run_command(struct MPContext *mpctx, struct mp_cmd *cmd, struct mpv_node *re
if (cmd->id == MP_CMD_KEYDOWN)
code |= MP_KEY_STATE_DOWN;
- mp_input_put_key(mpctx->input, code);
+ mp_input_put_key_artificial(mpctx->input, code);
break;
}
case MP_CMD_KEYUP: {
const char *key_name = cmd->args[0].v.s;
if (key_name[0] == '\0') {
- mp_input_put_key(mpctx->input, MP_INPUT_RELEASE_ALL);
+ mp_input_put_key_artificial(mpctx->input, MP_INPUT_RELEASE_ALL);
} else {
int code = mp_input_get_key_from_name(key_name);
if (code < 0) {
MP_ERR(mpctx, "%s is not a valid input name.\n", key_name);
return -1;
}
- mp_input_put_key(mpctx->input, code | MP_KEY_STATE_UP);
+ mp_input_put_key_artificial(mpctx->input, code | MP_KEY_STATE_UP);
}
break;
}
diff --git a/player/configfiles.c b/player/configfiles.c
index db19685..3c42331 100644
--- a/player/configfiles.c
+++ b/player/configfiles.c
@@ -25,7 +25,7 @@
#include <libavutil/md5.h>
#include "config.h"
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "osdep/io.h"
@@ -273,11 +273,35 @@ static bool needs_config_quoting(const char *s)
return false;
}
+static void write_filename(struct MPContext *mpctx, FILE *file, char *filename)
+{
+ if (mpctx->opts->write_filename_in_watch_later_config) {
+ char write_name[1024] = {0};
+ for (int n = 0; filename[n] && n < sizeof(write_name) - 1; n++)
+ write_name[n] = (unsigned char)filename[n] < 32 ? '_' : filename[n];
+ fprintf(file, "# %s\n", write_name);
+ }
+}
+
+static void write_redirect(struct MPContext *mpctx, char *path)
+{
+ char *conffile = mp_get_playback_resume_config_filename(mpctx, path);
+ if (conffile) {
+ FILE *file = fopen(conffile, "wb");
+ if (file) {
+ fprintf(file, "# redirect entry\n");
+ write_filename(mpctx, file, path);
+ fclose(file);
+ }
+ talloc_free(conffile);
+ }
+}
+
void mp_write_watch_later_conf(struct MPContext *mpctx)
{
- char *filename = mpctx->filename;
+ struct playlist_entry *cur = mpctx->playing;
char *conffile = NULL;
- if (!filename)
+ if (!cur)
goto exit;
struct demuxer *demux = mpctx->demuxer;
@@ -288,7 +312,7 @@ void mp_write_watch_later_conf(struct MPContext *mpctx)
mp_mk_config_dir(mpctx->global, MP_WATCH_LATER_CONF);
- conffile = mp_get_playback_resume_config_filename(mpctx, filename);
+ conffile = mp_get_playback_resume_config_filename(mpctx, cur->filename);
if (!conffile)
goto exit;
@@ -297,12 +321,9 @@ void mp_write_watch_later_conf(struct MPContext *mpctx)
FILE *file = fopen(conffile, "wb");
if (!file)
goto exit;
- if (mpctx->opts->write_filename_in_watch_later_config) {
- char write_name[1024] = {0};
- for (int n = 0; filename[n] && n < sizeof(write_name) - 1; n++)
- write_name[n] = (unsigned char)filename[n] < 32 ? '_' : filename[n];
- fprintf(file, "# %s\n", write_name);
- }
+
+ write_filename(mpctx, file, cur->filename);
+
double pos = get_current_time(mpctx);
if (pos != MP_NOPTS_VALUE)
fprintf(file, "start=%f\n", pos);
@@ -328,6 +349,37 @@ void mp_write_watch_later_conf(struct MPContext *mpctx)
}
fclose(file);
+ // This allows us to recursively resume directories etc., whose entries are
+ // expanded the first time it's "played". For example, if "/a/b/c.mkv" is
+ // the current entry, then we want to resume this file if the user does
+ // "mpv /a". This would expand to the directory entries in "/a", and if
+ // "/a/a.mkv" is not the first entry, this would be played.
+ // Here, we write resume entries for "/a" and "/a/b".
+ // (Unfortunately, this will leave stray resume files on resume, because
+ // obviously it resumes only from one of those paths.)
+ for (int n = 0; n < cur->num_redirects; n++)
+ write_redirect(mpctx, cur->redirects[n]);
+ // And at last, for local directories, we write an entry for each path
+ // prefix, so the user can resume from an arbitrary directory. This starts
+ // with the first redirect (all other redirects are further prefixes).
+ if (cur->num_redirects) {
+ char *path = cur->redirects[0];
+ char tmp[4096];
+ if (!mp_is_url(bstr0(path)) && strlen(path) < sizeof(tmp)) {
+ snprintf(tmp, sizeof(tmp), "%s", path);
+ for (;;) {
+ bstr dir = mp_dirname(tmp);
+ if (dir.len == strlen(tmp) || !dir.len || bstr_equals0(dir, "."))
+ break;
+
+ tmp[dir.len] = '\0';
+ if (strlen(tmp) >= 2) // keep "/"
+ mp_path_strip_trailing_separator(tmp);
+ write_redirect(mpctx, tmp);
+ }
+ }
+ }
+
exit:
talloc_free(conffile);
}
diff --git a/player/core.h b/player/core.h
index 04d4fb6..f26cacb 100644
--- a/player/core.h
+++ b/player/core.h
@@ -26,9 +26,12 @@
#include "common/common.h"
#include "options/options.h"
#include "sub/osd.h"
-#include "demux/timeline.h"
+#include "audio/audio.h"
+#include "video/mp_image.h"
#include "video/out/vo.h"
+#include "lavfi.h"
+
// definitions used internally by the core player code
enum stop_play_reason {
@@ -66,6 +69,7 @@ enum seek_type {
MPSEEK_RELATIVE,
MPSEEK_ABSOLUTE,
MPSEEK_FACTOR,
+ MPSEEK_BACKSTEP,
};
enum seek_precision {
@@ -128,22 +132,75 @@ struct track {
char *external_filename;
bool auto_loaded;
- // If the track's stream changes with the timeline (ordered chapters).
- bool under_timeline;
-
- // Does not change with under_timeline, but it useless for most purposes.
- struct sh_stream *original_stream;
-
- // Value can change if under_timeline==true.
struct demuxer *demuxer;
// Invariant: !stream || stream->demuxer == demuxer
struct sh_stream *stream;
+ // Current subtitle state (or cached state if selected==false).
+ struct dec_sub *d_sub;
+
+ // Current decoding state (NULL if selected==false)
+ struct dec_video *d_video;
+ struct dec_audio *d_audio;
+
+ // Where the decoded result goes to (one of them is not NULL if active)
+ struct vo_chain *vo_c;
+ struct ao_chain *ao_c;
+ struct lavfi_pad *sink;
+
// For external subtitles, which are read fully on init. Do not attempt
// to read packets from them.
bool preloaded;
};
+// Summarizes video filtering and output.
+struct vo_chain {
+ struct mp_log *log;
+
+ struct mp_hwdec_info *hwdec_info;
+ double container_fps;
+
+ struct vf_chain *vf;
+ struct vo *vo;
+
+ // 1-element input frame queue.
+ struct mp_image *input_mpi;
+
+ // Last known input_mpi format (so vf can be reinitialized any time).
+ struct mp_image_params input_format;
+
+ struct track *track;
+ struct lavfi_pad *filter_src;
+ struct dec_video *video_src;
+
+ // - video consists of a single picture, which should be shown only once
+ // - do not sync audio to video in any way
+ bool is_coverart;
+};
+
+// Like vo_chain, for audio.
+struct ao_chain {
+ struct mp_log *log;
+
+ double pts; // timestamp of first sample output by decoder
+ bool spdif_passthrough, spdif_failed;
+ bool pts_reset;
+
+ struct af_stream *af;
+ struct ao *ao;
+ struct mp_audio_buffer *ao_buffer;
+
+ // 1-element input frame queue.
+ struct mp_audio *input_frame;
+
+ // Last known input_mpi format (so vf can be reinitialized any time).
+ struct mp_audio input_format;
+
+ struct track *track;
+ struct lavfi_pad *filter_src;
+ struct dec_audio *audio_src;
+};
+
/* Note that playback can be paused, stopped, etc. at any time. While paused,
* playback restart is still active, because you want seeking to work even
* if paused.
@@ -216,18 +273,10 @@ typedef struct MPContext {
// Current file statistics
int64_t shown_vframes, shown_aframes;
- struct stream *stream; // stream that was initially opened
- struct demuxer **sources; // all open demuxers
- int num_sources;
-
- struct timeline *tl;
- struct timeline_part *timeline;
- int num_timeline_parts;
- int timeline_part;
struct demux_chapter *chapters;
int num_chapters;
- struct demuxer *demuxer; // can change with timeline
+ struct demuxer *demuxer;
struct mp_tags *filtered_tags;
struct track **tracks;
@@ -240,25 +289,21 @@ typedef struct MPContext {
// Currently, this is used for the secondary subtitle track only.
struct track *current_track[NUM_PTRACKS][STREAM_TYPE_COUNT];
- struct dec_video *d_video;
- struct dec_audio *d_audio;
- struct dec_sub *d_sub[2];
-
- // Uses: accessing metadata (consider ordered chapters case, where the main
- // demuxer defines metadata), or special purpose demuxers like TV.
- struct demuxer *master_demuxer;
- struct demuxer *track_layout; // complication for ordered chapters
+ struct lavfi *lavfi;
struct mixer *mixer;
struct ao *ao;
struct mp_audio *ao_decoder_fmt; // for weak gapless audio check
- struct mp_audio_buffer *ao_buffer; // queued audio; passed to ao_play() later
+ struct ao_chain *ao_chain;
+
+ struct vo_chain *vo_chain;
struct vo *video_out;
// next_frame[0] is the next frame, next_frame[1] the one after that.
- struct mp_image *next_frames[VO_MAX_REQ_FRAMES];
+ // The +1 is for adding 1 additional frame in backstep mode.
+ struct mp_image *next_frames[VO_MAX_REQ_FRAMES + 1];
int num_next_frames;
- struct mp_image *saved_frame; // for hrseek_lastframe
+ struct mp_image *saved_frame; // for hrseek_lastframe and hrseek_backstep
enum playback_status video_status, audio_status;
bool restart_complete;
@@ -276,12 +321,10 @@ typedef struct MPContext {
double audio_drop_throttle;
// Number of mistimed frames.
int mistimed_frames_total;
- /* Set if audio should be timed to start with video frame after seeking,
- * not set when e.g. playing cover art */
- bool sync_audio_to_video;
bool hrseek_active; // skip all data until hrseek_pts
bool hrseek_framedrop; // allow decoder to drop frames before hrseek_pts
bool hrseek_lastframe; // drop everything until last frame reached
+ bool hrseek_backstep; // go to frame before seek target
double hrseek_pts;
// AV sync: the next frame should be shown when the audio out has this
// much (in seconds) buffered data left. Increased when more data is
@@ -292,10 +335,8 @@ typedef struct MPContext {
// How much video timing has been changed to make it match the audio
// timeline. Used for status line information only.
double total_avsync_change;
- // Total number of dropped frames that were dropped by decoder.
- int dropped_frames_total;
- // Number of frames dropped in a row.
- int dropped_frames;
+ // Used to compute the number of frames dropped in a row.
+ int dropped_frames_start;
// A-V sync difference when last frame was displayed. Kept to display
// the same value if the status line is updated at a time where no new
// video frame is shown.
@@ -304,8 +345,6 @@ typedef struct MPContext {
* (or at least queued to be flipped by VO) */
double video_pts;
double last_seek_pts;
- // Mostly unused; for proper audio resync on speed changes.
- double video_next_pts;
// As video_pts, but is not reset when seeking away. (For the very short
// period of time until a new frame is decoded and shown.)
double last_vo_pts;
@@ -317,15 +356,7 @@ typedef struct MPContext {
int last_chapter;
- // History of video frames timestamps that were queued in the VO
- // This includes even skipped frames during hr-seek
- double vo_pts_history_pts[MAX_NUM_VO_PTS];
- // Whether the PTS at vo_pts_history[n] is after a seek reset
- uint64_t vo_pts_history_seek[MAX_NUM_VO_PTS];
- uint64_t vo_pts_history_seek_ts;
- uint64_t backstep_start_seek_ts;
- bool backstep_active;
- // Past timestamps etc. (stupidly duplicated with vo_pts_history).
+ // Past timestamps etc.
// The newest frame is at index 0.
struct frame_info *past_frames;
int num_past_frames;
@@ -362,17 +393,6 @@ typedef struct MPContext {
int last_chapter_seek;
double last_chapter_pts;
- /* Subtitle renderer. This is separate, because we want to keep fonts
- * loaded across ordered chapters, instead of reloading and rescanning
- * them on each transition. (Both of these objects contain this state.)
- */
- pthread_mutex_t ass_lock;
- struct ass_renderer *ass_renderer;
- struct ass_library *ass_library;
- struct mp_log *ass_log;
-
- int last_dvb_step;
-
bool paused;
// step this many frames, then pause
int step_frames;
@@ -401,14 +421,17 @@ typedef struct MPContext {
// audio.c
void reset_audio_state(struct MPContext *mpctx);
void reinit_audio_chain(struct MPContext *mpctx);
+int init_audio_decoder(struct MPContext *mpctx, struct track *track);
int reinit_audio_filters(struct MPContext *mpctx);
double playing_audio_pts(struct MPContext *mpctx);
-void fill_audio_out_buffers(struct MPContext *mpctx, double endpts);
+void fill_audio_out_buffers(struct MPContext *mpctx);
double written_audio_pts(struct MPContext *mpctx);
void clear_audio_output_buffers(struct MPContext *mpctx);
void update_playback_speed(struct MPContext *mpctx);
void uninit_audio_out(struct MPContext *mpctx);
void uninit_audio_chain(struct MPContext *mpctx);
+int init_audio_decoder(struct MPContext *mpctx, struct track *track);
+void reinit_audio_chain_src(struct MPContext *mpctx, struct lavfi_pad *src);
// configfiles.c
void mp_parse_cfgfiles(struct MPContext *mpctx);
@@ -431,8 +454,6 @@ void mp_switch_track_n(struct MPContext *mpctx, int order,
void mp_deselect_track(struct MPContext *mpctx, struct track *track);
struct track *mp_track_by_tid(struct MPContext *mpctx, enum stream_type type,
int tid);
-bool timeline_switch_to_time(struct MPContext *mpctx, double pts);
-int timeline_get_for_time(struct MPContext *mpctx, double pts);
void add_demuxer_tracks(struct MPContext *mpctx, struct demuxer *demuxer);
bool mp_remove_track(struct MPContext *mpctx, struct track *track);
struct playlist_entry *mp_next_file(struct MPContext *mpctx, int direction,
@@ -441,7 +462,7 @@ void mp_set_playlist_entry(struct MPContext *mpctx, struct playlist_entry *e);
void mp_play_files(struct MPContext *mpctx);
void update_demuxer_properties(struct MPContext *mpctx);
void print_track_list(struct MPContext *mpctx, const char *msg);
-void reselect_demux_streams(struct MPContext *mpctx);
+void reselect_demux_stream(struct MPContext *mpctx, struct track *track);
void prepare_playlist(struct MPContext *mpctx, struct playlist *pl);
void autoload_external_files(struct MPContext *mpctx);
struct track *select_default_track(struct MPContext *mpctx, int order,
@@ -455,10 +476,8 @@ void mp_print_version(struct mp_log *log, int always);
void wakeup_playloop(void *ctx);
// misc.c
-double get_main_demux_pts(struct MPContext *mpctx);
double rel_time_to_abs(struct MPContext *mpctx, struct m_rel_time t);
double get_play_end_pts(struct MPContext *mpctx);
-double get_relative_time(struct MPContext *mpctx);
void merge_playlist_files(struct playlist *pl);
float mp_get_cache_percent(struct MPContext *mpctx);
bool mp_get_cache_idle(struct MPContext *mpctx);
@@ -483,6 +502,7 @@ void set_osd_bar_chapters(struct MPContext *mpctx, int type);
// playloop.c
void mp_wait_events(struct MPContext *mpctx, double sleeptime);
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);
@@ -505,7 +525,6 @@ void run_playloop(struct MPContext *mpctx);
void mp_idle(struct MPContext *mpctx);
void idle_loop(struct MPContext *mpctx);
int handle_force_window(struct MPContext *mpctx, bool force);
-void add_frame_pts(struct MPContext *mpctx, double pts);
void seek_to_last_frame(struct MPContext *mpctx);
// scripting.c
@@ -517,22 +536,27 @@ void mp_load_scripts(struct MPContext *mpctx);
// sub.c
void reset_subtitle_state(struct MPContext *mpctx);
-void uninit_stream_sub_decoders(struct demuxer *demuxer);
-void reinit_subs(struct MPContext *mpctx, int order);
-void uninit_sub(struct MPContext *mpctx, int order);
+void reinit_sub(struct MPContext *mpctx, struct track *track);
+void reinit_sub_all(struct MPContext *mpctx);
+void uninit_sub(struct MPContext *mpctx, struct track *track);
void uninit_sub_all(struct MPContext *mpctx);
void update_osd_msg(struct MPContext *mpctx);
-void update_subtitles(struct MPContext *mpctx);
-void uninit_sub_renderer(struct MPContext *mpctx);
+bool update_subtitles(struct MPContext *mpctx, double video_pts);
// video.c
+int video_get_colors(struct vo_chain *vo_c, const char *item, int *value);
+int video_set_colors(struct vo_chain *vo_c, const char *item, int value);
+int video_vf_vo_control(struct vo_chain *vo_c, int vf_cmd, void *data);
void reset_video_state(struct MPContext *mpctx);
+int init_video_decoder(struct MPContext *mpctx, struct track *track);
int reinit_video_chain(struct MPContext *mpctx);
+int reinit_video_chain_src(struct MPContext *mpctx, struct lavfi_pad *src);
int reinit_video_filters(struct MPContext *mpctx);
-void write_video(struct MPContext *mpctx, double endpts);
+void write_video(struct MPContext *mpctx);
void mp_force_video_refresh(struct MPContext *mpctx);
void uninit_video_out(struct MPContext *mpctx);
void uninit_video_chain(struct MPContext *mpctx);
double calc_average_frame_duration(struct MPContext *mpctx);
+int init_video_decoder(struct MPContext *mpctx, struct track *track);
#endif /* MPLAYER_MP_CORE_H */
diff --git a/player/external_files.c b/player/external_files.c
index 486b492..c1b7d53 100644
--- a/player/external_files.c
+++ b/player/external_files.c
@@ -87,7 +87,7 @@ static struct bstr guess_lang_from_filename(struct bstr name)
static void append_dir_subtitles(struct mpv_global *global,
struct subfn **slist, int *nsub,
struct bstr path, const char *fname,
- int limit_fuzziness)
+ int limit_fuzziness, int limit_type)
{
void *tmpmem = talloc_new(NULL);
struct MPOpts *opts = global->opts;
@@ -136,7 +136,7 @@ static void append_dir_subtitles(struct mpv_global *global,
break;
}
- if (fuzz < 0)
+ if (fuzz < 0 || (limit_type >= 0 && limit_type != type))
goto next_sub;
// we have a (likely) subtitle file
@@ -226,6 +226,25 @@ static void filter_subidx(struct subfn **slist, int *nsub)
}
}
+static void load_paths(struct mpv_global *global, struct subfn **slist,
+ int *nsubs, const char *fname, char **paths,
+ char *cfg_path, int type)
+{
+ for (int i = 0; paths && paths[i]; i++) {
+ char *path = mp_path_join_bstr(*slist, mp_dirname(fname),
+ bstr0(paths[i]));
+ append_dir_subtitles(global, slist, nsubs, bstr0(path), fname, 0, type);
+ }
+
+ // Load subtitles in ~/.mpv/sub (or similar) limiting sub fuzziness
+ char *mp_subdir = mp_find_config_file(NULL, global, cfg_path);
+ if (mp_subdir) {
+ append_dir_subtitles(global, slist, nsubs, bstr0(mp_subdir), fname, 1,
+ type);
+ }
+ talloc_free(mp_subdir);
+}
+
// Return a list of subtitles and audio files found, sorted by priority.
// Last element is terminated with a fname==NULL entry.
struct subfn *find_external_files(struct mpv_global *global, const char *fname)
@@ -235,23 +254,17 @@ struct subfn *find_external_files(struct mpv_global *global, const char *fname)
int n = 0;
// Load subtitles from current media directory
- append_dir_subtitles(global, &slist, &n, mp_dirname(fname), fname, 0);
+ append_dir_subtitles(global, &slist, &n, mp_dirname(fname), fname, 0, -1);
+ // Load subtitles in dirs specified by sub-paths option
if (opts->sub_auto >= 0) {
- // Load subtitles in dirs specified by sub-paths option
- if (opts->sub_paths) {
- for (int i = 0; opts->sub_paths[i]; i++) {
- char *path = mp_path_join_bstr(slist, mp_dirname(fname),
- bstr0(opts->sub_paths[i]));
- append_dir_subtitles(global, &slist, &n, bstr0(path), fname, 0);
- }
- }
+ load_paths(global, &slist, &n, fname, opts->sub_paths, "sub/",
+ STREAM_SUB);
+ }
- // Load subtitles in ~/.mpv/sub limiting sub fuzziness
- char *mp_subdir = mp_find_config_file(NULL, global, "sub/");
- if (mp_subdir)
- append_dir_subtitles(global, &slist, &n, bstr0(mp_subdir), fname, 1);
- talloc_free(mp_subdir);
+ if (opts->audiofile_auto >= 0) {
+ load_paths(global, &slist, &n, fname, opts->audiofile_paths, "audio/",
+ STREAM_AUDIO);
}
// Sort by name for filter_subidx()
diff --git a/player/lavfi.c b/player/lavfi.c
new file mode 100644
index 0000000..ada66e7
--- /dev/null
+++ b/player/lavfi.c
@@ -0,0 +1,740 @@
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <inttypes.h>
+#include <stdarg.h>
+#include <assert.h>
+
+#include <libavutil/avstring.h>
+#include <libavutil/mem.h>
+#include <libavutil/mathematics.h>
+#include <libavutil/rational.h>
+#include <libavutil/error.h>
+#include <libavfilter/avfilter.h>
+#include <libavfilter/buffersink.h>
+#include <libavfilter/buffersrc.h>
+
+#include "common/common.h"
+#include "common/av_common.h"
+#include "common/msg.h"
+
+#include "audio/audio.h"
+#include "video/mp_image.h"
+#include "audio/fmt-conversion.h"
+#include "video/fmt-conversion.h"
+
+#include "lavfi.h"
+
+struct lavfi {
+ struct mp_log *log;
+ char *graph_string;
+
+ AVFilterGraph *graph;
+ // Set to true once all inputs have been initialized, and the graph is
+ // linked.
+ bool initialized;
+
+ // Set if all inputs have been marked as LAVFI_WAIT (except LAVFI_EOF pads).
+ bool all_waiting;
+
+ // Graph is draining to undo previously sent EOF. (If a stream leaves EOF
+ // state, the graph needs to be recreated to "unstuck" it.)
+ bool draining_recover_eof;
+ // Graph is draining for format changes.
+ bool draining_new_format;
+
+ // Filter can't be put into a working state.
+ bool failed;
+
+ struct lavfi_pad **pads;
+ int num_pads;
+};
+
+struct lavfi_pad {
+ struct lavfi *main;
+ 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
+
+ AVFilterContext *filter;
+ int filter_pad;
+ // buffersrc or buffersink connected to filter/filter_pad
+ AVFilterContext *buffer;
+ AVRational timebase;
+ bool buffer_is_eof; // received/sent EOF to the buffer
+
+ // 1-frame queue (used for both input and output)
+ struct mp_image *pending_v;
+ struct mp_audio *pending_a;
+
+ // -- dir==LAVFI_IN
+
+ bool input_needed; // filter has signaled it needs new input
+ bool input_waiting; // caller notified us that it will feed after a wakeup
+ bool input_again; // caller wants us to feed data in the next iteration
+ 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_audio in_fmt_a;
+
+ // -- dir==LAVFI_OUT
+
+ bool output_needed; // caller has signaled it needs new output
+ bool output_eof; // last filter output was EOF
+};
+
+static void add_pad(struct lavfi *c, enum lavfi_direction dir, AVFilterInOut *item)
+{
+ int type = -1;
+ enum AVMediaType avmt;
+ if (dir == LAVFI_IN) {
+ avmt = avfilter_pad_get_type(item->filter_ctx->input_pads, item->pad_idx);
+ } else {
+ avmt = avfilter_pad_get_type(item->filter_ctx->output_pads, item->pad_idx);
+ }
+ switch (avmt) {
+ case AVMEDIA_TYPE_VIDEO: type = STREAM_VIDEO; break;
+ case AVMEDIA_TYPE_AUDIO: type = STREAM_AUDIO; break;
+ default: abort();
+ }
+
+ if (!item->name) {
+ MP_FATAL(c, "filter pad without name label\n");
+ c->failed = true;
+ return;
+ }
+
+ struct lavfi_pad *p = lavfi_find_pad(c, item->name);
+ if (p) {
+ // Graph recreation case: reassociate an existing pad.
+ if (p->dir != dir || p->type != type) {
+ MP_FATAL(c, "pad '%s' changed type or direction\n", item->name);
+ c->failed = true;
+ return;
+ }
+ } else {
+ p = talloc_zero(c, struct lavfi_pad);
+ 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);
+ }
+ p->filter = item->filter_ctx;
+ p->filter_pad = item->pad_idx;
+}
+
+static void add_pads(struct lavfi *c, enum lavfi_direction dir, AVFilterInOut *list)
+{
+ for (; list; list = list->next)
+ add_pad(c, dir, list);
+}
+
+// Parse the user-provided filter graph, and populate the unlinked filter pads.
+static void precreate_graph(struct lavfi *c)
+{
+ assert(!c->graph);
+ c->graph = avfilter_graph_alloc();
+ if (!c->graph)
+ abort();
+ AVFilterInOut *in = NULL, *out = NULL;
+ if (avfilter_graph_parse2(c->graph, c->graph_string, &in, &out) < 0) {
+ c->graph = NULL;
+ MP_FATAL(c, "parsing the filter graph failed\n");
+ c->failed = true;
+ return;
+ }
+ add_pads(c, LAVFI_IN, in);
+ add_pads(c, LAVFI_OUT, out);
+ avfilter_inout_free(&in);
+ avfilter_inout_free(&out);
+
+ // Now check for pads which could not be reassociated.
+ for (int n = 0; n < c->num_pads; n++) {
+ struct lavfi_pad *pad = c->pads[n];
+ // ok, not much we can do
+ if (!pad->filter)
+ MP_FATAL(c, "filter pad '%s' can not be reconnected\n", pad->name);
+ }
+}
+
+static void free_graph(struct lavfi *c)
+{
+ avfilter_graph_free(&c->graph);
+ for (int n = 0; n < c->num_pads; n++) {
+ struct lavfi_pad *pad = c->pads[n];
+ pad->filter = NULL;
+ pad->filter_pad = -1;
+ pad->buffer = NULL;
+ pad->in_fmt_v = (struct mp_image_params){0};
+ pad->in_fmt_a = (struct mp_audio){0};
+ pad->buffer_is_eof = false;
+ pad->input_needed = false;
+ pad->input_waiting = false;
+ pad->input_again = false;
+ pad->input_eof = false;
+ pad->output_needed = false;
+ pad->output_eof = false;
+ }
+ c->initialized = false;
+ c->all_waiting = false;
+ c->draining_recover_eof = false;
+ c->draining_new_format = false;
+}
+
+static void drop_pad_data(struct lavfi_pad *pad)
+{
+ talloc_free(pad->pending_a);
+ pad->pending_a = NULL;
+ talloc_free(pad->pending_v);
+ pad->pending_v = NULL;
+}
+
+static void clear_data(struct lavfi *c)
+{
+ for (int n = 0; n < c->num_pads; n++)
+ drop_pad_data(c->pads[n]);
+}
+
+void lavfi_seek_reset(struct lavfi *c)
+{
+ free_graph(c);
+ clear_data(c);
+ precreate_graph(c);
+}
+
+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;
+ precreate_graph(c);
+ return c;
+}
+
+void lavfi_destroy(struct lavfi *c)
+{
+ free_graph(c);
+ clear_data(c);
+ talloc_free(c);
+}
+
+struct lavfi_pad *lavfi_find_pad(struct lavfi *c, char *name)
+{
+ for (int n = 0; n < c->num_pads; n++) {
+ if (strcmp(c->pads[n]->name, name) == 0)
+ return c->pads[n];
+ }
+ return NULL;
+}
+
+enum lavfi_direction lavfi_pad_direction(struct lavfi_pad *pad)
+{
+ return pad->dir;
+}
+
+enum stream_type lavfi_pad_type(struct lavfi_pad *pad)
+{
+ return pad->type;
+}
+
+void lavfi_set_connected(struct lavfi_pad *pad, bool connected)
+{
+ pad->connected = connected;
+}
+
+bool lavfi_get_connected(struct lavfi_pad *pad)
+{
+ return pad->connected;
+}
+
+// Ensure to send EOF to each input pad, so the graph can be drained properly.
+static void send_global_eof(struct lavfi *c)
+{
+ for (int n = 0; n < c->num_pads; n++) {
+ struct lavfi_pad *pad = c->pads[n];
+ if (!pad->buffer || pad->dir != LAVFI_IN || pad->buffer_is_eof)
+ continue;
+
+ if (av_buffersrc_add_frame(pad->buffer, NULL) < 0)
+ MP_FATAL(c, "could not send EOF to filter\n");
+
+ pad->buffer_is_eof = true;
+ }
+}
+
+// libavfilter allows changing some parameters on the fly, but not
+// others.
+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)
+{
+ 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;
+}
+
+static void check_format_changes(struct lavfi *c)
+{
+ // check each pad for new input format
+ for (int n = 0; n < c->num_pads; n++) {
+ struct lavfi_pad *pad = c->pads[n];
+ if (!pad->buffer || pad->dir != LAVFI_IN)
+ continue;
+
+ if (pad->type == STREAM_AUDIO && pad->pending_a && pad->in_fmt_a.format) {
+ 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 (c->initialized && c->draining_new_format)
+ send_global_eof(c);
+}
+
+// Attempt to initialize all pads. Return true if all are initialized, or
+// false if more data is needed (or on error).
+static bool init_pads(struct lavfi *c)
+{
+ if (!c->graph)
+ goto error;
+
+ for (int n = 0; n < c->num_pads; n++) {
+ struct lavfi_pad *pad = c->pads[n];
+ if (pad->buffer)
+ continue;
+
+ if (!pad->filter)
+ goto error; // can happen if pad reassociation fails
+
+ if (pad->dir == LAVFI_OUT) {
+ AVFilter *dst_filter;
+ if (pad->type == STREAM_AUDIO) {
+ dst_filter = avfilter_get_by_name("abuffersink");
+ } else if (pad->type == STREAM_VIDEO) {
+ dst_filter = avfilter_get_by_name("buffersink");
+ } else {
+ assert(0);
+ }
+
+ char name[256];
+ snprintf(name, sizeof(name), "mpv_sink_%s", pad->name);
+
+ if (avfilter_graph_create_filter(&pad->buffer, dst_filter,
+ name, NULL, NULL, c->graph) < 0)
+ goto error;
+
+ if (avfilter_link(pad->filter, pad->filter_pad, pad->buffer, 0) < 0)
+ goto error;
+ } else {
+ char src_args[256];
+ AVFilter *src_filter;
+
+ pad->input_eof |= !pad->connected;
+
+ if (pad->pending_a) {
+ assert(pad->type == STREAM_AUDIO);
+ 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;
+ } 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.
+ if (pad->type == STREAM_AUDIO) {
+ mp_audio_set_format(&pad->in_fmt_a, AF_FORMAT_FLOAT);
+ 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,
+ };
+ }
+ } else {
+ // no input data, format unknown, can't init, wait longer.
+ pad->input_needed = true;
+ return false;
+ }
+
+ 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");
+ } 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");
+ } else {
+ assert(0);
+ }
+
+ char name[256];
+ snprintf(name, sizeof(name), "mpv_src_%s", pad->name);
+
+ if (avfilter_graph_create_filter(&pad->buffer, src_filter,
+ name, src_args, NULL, c->graph) < 0)
+ goto error;
+
+ if (avfilter_link(pad->buffer, 0, pad->filter, pad->filter_pad) < 0)
+ goto error;
+ }
+ }
+
+ return true;
+error:
+ MP_FATAL(c, "could not initialize filter pads\n");
+ c->failed = true;
+ return false;
+}
+
+static void dump_graph(struct lavfi *c)
+{
+#if LIBAVFILTER_VERSION_MICRO >= 100
+ MP_VERBOSE(c, "Filter graph:\n");
+ char *s = avfilter_graph_dump(c->graph, NULL);
+ if (s)
+ MP_VERBOSE(c, "%s\n", s);
+ av_free(s);
+#endif
+}
+
+// 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)
+{
+ assert(!c->initialized);
+
+ if (init_pads(c)) {
+ // 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");
+ free_graph(c);
+ c->failed = true;
+ return;
+ }
+
+ for (int n = 0; n < c->num_pads; n++) {
+ struct lavfi_pad *pad = c->pads[n];
+ if (pad->dir == LAVFI_OUT)
+ pad->timebase = pad->buffer->inputs[0]->time_base;
+ }
+
+ c->initialized = true;
+
+ dump_graph(c);
+ }
+}
+
+static void feed_input_pads(struct lavfi *c)
+{
+ assert(c->initialized);
+
+ for (int n = 0; n < c->num_pads; n++) {
+ struct lavfi_pad *pad = c->pads[n];
+ if (pad->dir != LAVFI_IN)
+ continue;
+
+ pad->input_needed = false;
+ pad->input_eof |= !pad->connected;
+
+#if LIBAVFILTER_VERSION_MICRO >= 100
+ if (!av_buffersrc_get_nb_failed_requests(pad->buffer))
+ continue;
+#endif
+
+ if (c->draining_recover_eof || c->draining_new_format)
+ continue;
+
+ if (pad->buffer_is_eof)
+ continue;
+
+ AVFrame *frame = NULL;
+ double pts = 0;
+ bool eof = false;
+ if (pad->pending_v) {
+ pts = pad->pending_v->pts;
+ frame = mp_image_to_av_frame_and_unref(pad->pending_v);
+ pad->pending_v = NULL;
+ } else if (pad->pending_a) {
+ pts = pad->pending_a->pts;
+ frame = mp_audio_to_avframe_and_unref(pad->pending_a);
+ pad->pending_a = NULL;
+ } else {
+ if (!pad->input_eof) {
+ pad->input_needed = true;
+ continue;
+ }
+ eof = true;
+ }
+
+ if (!frame && !eof) {
+ MP_FATAL(c, "out of memory or unsupported format\n");
+ continue;
+ }
+
+ if (frame)
+ frame->pts = mp_pts_to_av(pts, &pad->timebase);
+
+ pad->buffer_is_eof = !frame;
+
+ if (av_buffersrc_add_frame(pad->buffer, frame) < 0)
+ MP_FATAL(c, "could not pass frame to filter\n");
+ av_frame_free(&frame);
+
+ pad->input_again = false;
+ pad->input_eof = eof;
+ pad->input_waiting = eof; // input _might_ come again in the future
+ }
+}
+
+static void read_output_pads(struct lavfi *c)
+{
+ assert(c->initialized);
+
+ for (int n = 0; n < c->num_pads; n++) {
+ struct lavfi_pad *pad = c->pads[n];
+
+ if (pad->dir != LAVFI_OUT)
+ continue;
+
+ // If disconnected, read and discard everything.
+ if (!pad->pending_v && !pad->pending_a && !pad->connected)
+ pad->output_needed = true;
+
+ if (!pad->output_needed)
+ continue;
+
+ assert(pad->buffer);
+ assert(!pad->pending_v && !pad->pending_a);
+
+ int r = AVERROR_EOF;
+ if (!pad->buffer_is_eof)
+ r = av_buffersink_get_frame(pad->buffer, pad->tmp_frame);
+ if (r >= 0) {
+ pad->output_needed = false;
+ double pts = mp_pts_from_av(pad->tmp_frame->pts, &pad->timebase);
+ if (pad->type == STREAM_AUDIO) {
+ pad->pending_a = mp_audio_from_avframe(pad->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);
+ if (pad->pending_v)
+ pad->pending_v->pts = pts;
+ } else {
+ assert(0);
+ }
+ av_frame_unref(pad->tmp_frame);
+ if (!pad->pending_v && !pad->pending_a)
+ MP_ERR(c, "could not use filter output\n");
+ pad->output_eof = false;
+ if (!pad->connected)
+ drop_pad_data(pad);
+ } else if (r == AVERROR(EAGAIN)) {
+ // We expect that libavfilter will request input on one of the
+ // input pads (via av_buffersrc_get_nb_failed_requests()).
+ pad->output_eof = false;
+ } else if (r == AVERROR_EOF) {
+ pad->buffer_is_eof = true;
+ if (!c->draining_recover_eof && !c->draining_new_format)
+ pad->output_eof = true;
+ } else {
+ // Real error - ignore it.
+ MP_ERR(c, "error on filtering (%d)\n", r);
+ }
+ }
+}
+
+// Process filter input and outputs. Return if progress was made (then the
+// caller should repeat). If it returns false, the caller should go to sleep
+// (as all inputs are asleep as well and no further output can be produced).
+bool lavfi_process(struct lavfi *c)
+{
+ check_format_changes(c);
+
+ if (!c->initialized)
+ init_graph(c);
+
+ if (c->initialized) {
+ read_output_pads(c);
+ feed_input_pads(c);
+ }
+
+ bool all_waiting = true;
+ bool any_needs_input = false;
+ bool any_needs_output = false;
+ bool all_lavfi_eof = true;
+ bool all_input_eof = true;
+
+ // Determine the graph state
+ for (int n = 0; n < c->num_pads; n++) {
+ struct lavfi_pad *pad = c->pads[n];
+
+ if (pad->dir == LAVFI_IN) {
+ all_waiting &= pad->input_waiting;
+ any_needs_input |= pad->input_needed;
+ all_input_eof &= pad->input_eof;
+ } else if (pad->dir == LAVFI_OUT) {
+ all_lavfi_eof &= pad->buffer_is_eof;
+ any_needs_output |= pad->output_needed;
+ }
+ }
+
+ if (all_lavfi_eof && !all_input_eof) {
+ free_graph(c);
+ precreate_graph(c);
+ all_waiting = false;
+ any_needs_input = true;
+ }
+
+ c->all_waiting = all_waiting;
+ return (any_needs_input || any_needs_output) && !all_waiting;
+}
+
+bool lavfi_has_failed(struct lavfi *c)
+{
+ return c->failed;
+}
+
+// Request an output frame on this output pad.
+// Returns req_status
+static int lavfi_request_frame(struct lavfi_pad *pad)
+{
+ assert(pad->dir == LAVFI_OUT);
+
+ if (pad->main->failed)
+ return DATA_EOF;
+
+ if (!(pad->pending_a || pad->pending_v)) {
+ pad->output_needed = true;
+ lavfi_process(pad->main);
+ }
+
+ if (pad->pending_a || pad->pending_v) {
+ return DATA_OK;
+ } else if (pad->output_eof) {
+ return DATA_EOF;
+ } else if (pad->main->all_waiting) {
+ return DATA_WAIT;
+ }
+ return DATA_AGAIN;
+}
+
+// Try to read a new frame from an output pad. Returns one of the following:
+// DATA_OK: a frame is returned
+// DATA_AGAIN: needs more input data
+// DATA_WAIT: needs more input data, and all inputs in LAVFI_WAIT state
+// DATA_EOF: no more data
+int lavfi_request_frame_a(struct lavfi_pad *pad, struct mp_audio **out_aframe)
+{
+ int r = lavfi_request_frame(pad);
+ *out_aframe = pad->pending_a;
+ pad->pending_a = NULL;
+ return r;
+}
+
+// See lavfi_request_frame_a() for remarks.
+int lavfi_request_frame_v(struct lavfi_pad *pad, struct mp_image **out_vframe)
+{
+ int r = lavfi_request_frame(pad);
+ *out_vframe = pad->pending_v;
+ pad->pending_v = NULL;
+ return r;
+}
+
+bool lavfi_needs_input(struct lavfi_pad *pad)
+{
+ assert(pad->dir == LAVFI_IN);
+ lavfi_process(pad->main);
+ return pad->input_needed;
+}
+
+// A filter user is supposed to call lavfi_needs_input(), and if that returns
+// true, send either a new status or a frame. A status can be one of:
+// DATA_AGAIN: a new frame/status will come, caller will retry
+// DATA_WAIT: a new frame/status will come, but caller goes to sleep
+// DATA_EOF: no more input possible (in near time)
+// If you have a new frame, use lavfi_send_frame_ instead.
+// Calling this without lavfi_needs_input() returning true before is not
+// allowed.
+void lavfi_send_status(struct lavfi_pad *pad, int status)
+{
+ assert(pad->dir == LAVFI_IN);
+ assert(pad->input_needed);
+ assert(status != DATA_OK);
+ assert(!pad->pending_v && !pad->pending_a);
+
+ pad->input_waiting = status == DATA_WAIT || status == DATA_EOF;
+ pad->input_again = status == DATA_AGAIN;
+ pad->input_eof = status == DATA_EOF;
+}
+
+static void lavfi_sent_frame(struct lavfi_pad *pad)
+{
+ assert(pad->dir == LAVFI_IN);
+ assert(pad->input_needed);
+ assert(pad->pending_a || pad->pending_v);
+ pad->input_waiting = pad->input_again = pad->input_eof = false;
+ pad->input_needed = false;
+}
+
+// See lavfi_send_status() for remarks.
+void lavfi_send_frame_a(struct lavfi_pad *pad, struct mp_audio *aframe)
+{
+ assert(pad->type == STREAM_AUDIO);
+ assert(!pad->pending_a);
+ pad->pending_a = aframe;
+ lavfi_sent_frame(pad);
+}
+
+// See lavfi_send_status() for remarks.
+void lavfi_send_frame_v(struct lavfi_pad *pad, struct mp_image *vframe)
+{
+ assert(pad->type == STREAM_VIDEO);
+ assert(!pad->pending_v);
+ pad->pending_v = vframe;
+ lavfi_sent_frame(pad);
+}
+
diff --git a/player/lavfi.h b/player/lavfi.h
new file mode 100644
index 0000000..d39ecb0
--- /dev/null
+++ b/player/lavfi.h
@@ -0,0 +1,32 @@
+#ifndef MP_LAVFI
+#define MP_LAVFI
+
+struct mp_log;
+struct lavfi;
+struct lavfi_pad;
+struct mp_image;
+struct mp_audio;
+
+enum lavfi_direction {
+ LAVFI_IN = 1,
+ LAVFI_OUT,
+};
+
+struct lavfi *lavfi_create(struct mp_log *log, char *graph_string);
+void lavfi_destroy(struct lavfi *c);
+struct lavfi_pad *lavfi_find_pad(struct lavfi *c, char *name);
+enum lavfi_direction lavfi_pad_direction(struct lavfi_pad *pad);
+enum stream_type lavfi_pad_type(struct lavfi_pad *pad);
+void lavfi_set_connected(struct lavfi_pad *pad, bool connected);
+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);
+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);
+void lavfi_send_status(struct lavfi_pad *pad, int status);
+void lavfi_send_frame_a(struct lavfi_pad *pad, struct mp_audio *aframe);
+void lavfi_send_frame_v(struct lavfi_pad *pad, struct mp_image *vframe);
+
+#endif
diff --git a/player/loadfile.c b/player/loadfile.c
index 3e8c87f..b1e9cd2 100644
--- a/player/loadfile.c
+++ b/player/loadfile.c
@@ -24,7 +24,7 @@
#include <libavutil/avutil.h>
#include "config.h"
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "osdep/io.h"
#include "osdep/terminal.h"
@@ -60,48 +60,27 @@
static void uninit_demuxer(struct MPContext *mpctx)
{
- assert(!mpctx->d_video && !mpctx->d_audio &&
- !mpctx->d_sub[0] && !mpctx->d_sub[1]);
for (int r = 0; r < NUM_PTRACKS; r++) {
for (int t = 0; t < STREAM_TYPE_COUNT; t++)
mpctx->current_track[r][t] = NULL;
}
- mpctx->track_layout = NULL;
- mpctx->demuxer = NULL;
talloc_free(mpctx->chapters);
mpctx->chapters = NULL;
mpctx->num_chapters = 0;
- // per-stream cached subtitle state
- for (int i = 0; i < mpctx->num_sources; i++)
- uninit_stream_sub_decoders(mpctx->sources[i]);
-
// close demuxers for external tracks
for (int n = mpctx->num_tracks - 1; n >= 0; n--) {
mpctx->tracks[n]->selected = false;
mp_remove_track(mpctx, mpctx->tracks[n]);
}
- for (int i = 0; i < mpctx->num_tracks; i++)
+ for (int i = 0; i < mpctx->num_tracks; i++) {
+ sub_destroy(mpctx->tracks[i]->d_sub);
talloc_free(mpctx->tracks[i]);
+ }
mpctx->num_tracks = 0;
- mpctx->timeline = NULL;
- mpctx->num_timeline_parts = 0;
- timeline_destroy(mpctx->tl);
- mpctx->tl = NULL;
-
- free_demuxer_and_stream(mpctx->master_demuxer);
- mpctx->master_demuxer = NULL;
-
- talloc_free(mpctx->sources);
- mpctx->sources = NULL;
- mpctx->num_sources = 0;
-}
-
-static void uninit_stream(struct MPContext *mpctx)
-{
- free_stream(mpctx->stream);
- mpctx->stream = NULL;
+ free_demuxer_and_stream(mpctx->demuxer);
+ mpctx->demuxer = NULL;
}
#define APPEND(s, ...) mp_snprintf_cat(s, sizeof(s), __VA_ARGS__)
@@ -136,7 +115,7 @@ static void print_stream(struct MPContext *mpctx, struct track *t)
APPEND(b, " [P]");
if (t->title)
APPEND(b, " '%s'", t->title);
- const char *codec = s ? s->codec : NULL;
+ const char *codec = s ? s->codec->codec : NULL;
APPEND(b, " (%s)", codec ? codec : "<unknown>");
if (t->is_external)
APPEND(b, " (external)");
@@ -156,7 +135,7 @@ void print_track_list(struct MPContext *mpctx, const char *msg)
void update_demuxer_properties(struct MPContext *mpctx)
{
- struct demuxer *demuxer = mpctx->master_demuxer;
+ struct demuxer *demuxer = mpctx->demuxer;
if (!demuxer)
return;
demux_update(demuxer);
@@ -175,7 +154,7 @@ void update_demuxer_properties(struct MPContext *mpctx)
MP_INFO(mpctx, "%s\n", b);
}
}
- struct demuxer *tracks = mpctx->track_layout;
+ struct demuxer *tracks = mpctx->demuxer;
if (tracks->events & DEMUX_EVENT_STREAMS) {
add_demuxer_tracks(mpctx, tracks);
print_track_list(mpctx, NULL);
@@ -210,56 +189,29 @@ void update_demuxer_properties(struct MPContext *mpctx)
demuxer->events = 0;
}
-static bool need_init_seek(struct demuxer *demux)
-{
- for (int n = 0; n < demux->num_streams; n++) {
- struct sh_stream *stream = demux->streams[n];
- // Subtitle streams are not properly interleaved -> force init. seek.
- if (stream->type != STREAM_SUB && demux_stream_is_selected(stream))
- return false;
- }
- return true;
-}
-
-// Enable needed streams, disable others.
-// Note that switching all tracks at once (instead when initializing something)
-// can be important, because reading from a demuxer stream (e.g. during init)
-// will implicitly discard interleaved packets from unselected streams.
-// Also initializes position for external streams.
-void reselect_demux_streams(struct MPContext *mpctx)
+// Enables or disables the stream for the given track, according to
+// track->selected.
+void reselect_demux_stream(struct MPContext *mpctx, struct track *track)
{
- // Note: we assume that all demuxer streams are covered by the track list.
- for (int t = 0; t < mpctx->num_tracks; t++) {
- struct track *track = mpctx->tracks[t];
- if (track->demuxer && track->stream) {
- bool need_init = track->selected &&
- mpctx->demuxer != track->demuxer &&
- need_init_seek(track->demuxer);
- demuxer_select_track(track->demuxer, track->stream, track->selected);
- if (need_init) {
- double pts = get_main_demux_pts(mpctx);
- if (pts != MP_NOPTS_VALUE)
- demux_seek(track->demuxer, pts, SEEK_ABSOLUTE);
- }
+ if (!track->stream)
+ return;
+ demuxer_select_track(track->demuxer, track->stream, track->selected);
+ // External files may need an explicit seek to the correct position, if
+ // they were not implicitly advanced during playback.
+ if (track->selected && track->demuxer != mpctx->demuxer) {
+ bool position_ok = false;
+ for (int n = 0; n < demux_get_num_stream(track->demuxer); n++) {
+ struct sh_stream *stream = demux_get_stream(track->demuxer, n);
+ if (stream != track->stream && stream->type != STREAM_SUB)
+ position_ok |= demux_stream_is_selected(stream);
}
- }
-}
-
-static struct sh_stream *select_fallback_stream(struct demuxer *d,
- enum stream_type type,
- int index)
-{
- struct sh_stream *best_stream = NULL;
- for (int n = 0; n < d->num_streams; n++) {
- struct sh_stream *s = d->streams[n];
- if (s->type == type) {
- best_stream = s;
- if (index == 0)
- break;
- index -= 1;
+ if (!position_ok) {
+ double pts = get_current_time(mpctx);
+ if (pts == MP_NOPTS_VALUE)
+ pts = 0;
+ demux_seek(track->demuxer, pts, 0);
}
}
- return best_stream;
}
// Called from the demuxer thread if a new packet is available.
@@ -269,82 +221,12 @@ static void wakeup_demux(void *pctx)
mp_input_wakeup(mpctx->input);
}
-static void enable_demux_thread(struct MPContext *mpctx)
-{
- if (mpctx->demuxer && mpctx->opts->demuxer_thread) {
- demux_set_wakeup_cb(mpctx->demuxer, wakeup_demux, mpctx);
- demux_start_thread(mpctx->demuxer);
- for (int n = 0; n < mpctx->num_tracks; n++) {
- struct track *track = mpctx->tracks[n];
- if (track->is_external && track->stream && !track->preloaded &&
- !track->demuxer->fully_read)
- {
- demux_set_wakeup_cb(track->demuxer, wakeup_demux, mpctx);
- demux_start_thread(track->demuxer);
- }
- }
- }
-}
-
-// Returns whether reinitialization is required (i.e. it switched to a new part)
-bool timeline_switch_to_time(struct MPContext *mpctx, double pts)
+static void enable_demux_thread(struct MPContext *mpctx, struct demuxer *demux)
{
- if (!mpctx->timeline)
- return false;
-
- int new_part = mpctx->num_timeline_parts - 1;
- for (int i = 0; i < mpctx->num_timeline_parts; i++) {
- if (pts < mpctx->timeline[i + 1].start) {
- new_part = i;
- break;
- }
- }
-
- if (mpctx->timeline_part == new_part)
- return false;
- mpctx->timeline_part = new_part;
- struct timeline_part *n = mpctx->timeline + mpctx->timeline_part;
-
- uninit_audio_chain(mpctx);
- uninit_video_chain(mpctx);
- uninit_sub_all(mpctx);
- if (mpctx->ao && !mpctx->opts->gapless_audio) {
- ao_drain(mpctx->ao);
- uninit_audio_out(mpctx);
- }
-
- if (mpctx->demuxer) {
- demux_stop_thread(mpctx->demuxer);
- demux_flush(mpctx->demuxer);
+ if (mpctx->opts->demuxer_thread && !demux->fully_read) {
+ demux_set_wakeup_cb(demux, wakeup_demux, mpctx);
+ demux_start_thread(demux);
}
-
- mpctx->demuxer = n->source;
- demux_set_ts_offset(mpctx->demuxer, n->start - n->source_start);
-
- // While another timeline was active, the selection of active tracks might
- // have been changed - possibly we need to update this source.
- for (int x = 0; x < mpctx->num_tracks; x++) {
- struct track *track = mpctx->tracks[x];
- if (track->under_timeline) {
- track->demuxer = mpctx->demuxer;
- track->stream = demuxer_stream_by_demuxer_id(track->demuxer,
- track->type,
- track->demuxer_id);
- // EDL can have mismatched files in the same timeline
- if (!track->stream) {
- track->stream = select_fallback_stream(track->demuxer,
- track->type,
- track->user_tid - 1);
- }
- }
- }
-
- if (mpctx->playback_initialized) {
- reselect_demux_streams(mpctx);
- enable_demux_thread(mpctx);
- }
-
- return true;
}
static int find_new_tid(struct MPContext *mpctx, enum stream_type t)
@@ -360,12 +242,11 @@ static int find_new_tid(struct MPContext *mpctx, enum stream_type t)
static struct track *add_stream_track(struct MPContext *mpctx,
struct demuxer *demuxer,
- struct sh_stream *stream,
- bool under_timeline)
+ struct sh_stream *stream)
{
for (int i = 0; i < mpctx->num_tracks; i++) {
struct track *track = mpctx->tracks[i];
- if (track->original_stream == stream)
+ if (track->stream == stream)
return track;
}
@@ -380,10 +261,8 @@ static struct track *add_stream_track(struct MPContext *mpctx,
.forced_track = stream->forced_track,
.attached_picture = stream->attached_picture != NULL,
.lang = stream->lang,
- .under_timeline = under_timeline,
.demuxer = demuxer,
.stream = stream,
- .original_stream = stream,
};
MP_TARRAY_APPEND(mpctx, mpctx->tracks, mpctx->num_tracks, track);
@@ -396,8 +275,8 @@ static struct track *add_stream_track(struct MPContext *mpctx,
void add_demuxer_tracks(struct MPContext *mpctx, struct demuxer *demuxer)
{
- for (int n = 0; n < demuxer->num_streams; n++)
- add_stream_track(mpctx, demuxer, demuxer->streams[n], !!mpctx->timeline);
+ for (int n = 0; n < demux_get_num_stream(demuxer); n++)
+ add_stream_track(mpctx, demuxer, demux_get_stream(demuxer, n));
}
// Result numerically higher => better match. 0 == no match.
@@ -552,6 +431,17 @@ void mp_switch_track_n(struct MPContext *mpctx, int order, enum stream_type type
if (track == current)
return;
+ if (current && current->sink) {
+ MP_ERR(mpctx, "Can't disable input to complex filter.\n");
+ return;
+ }
+ if ((type == STREAM_VIDEO && mpctx->vo_chain && !mpctx->vo_chain->track) ||
+ (type == STREAM_AUDIO && mpctx->ao_chain && !mpctx->ao_chain->track))
+ {
+ MP_ERR(mpctx, "Can't switch away from complex filter output.\n");
+ return;
+ }
+
if (track && track->selected) {
// Track has been selected in a different order parameter.
MP_ERR(mpctx, "Track %d is already selected.\n", track->user_tid);
@@ -567,28 +457,25 @@ void mp_switch_track_n(struct MPContext *mpctx, int order, enum stream_type type
clear_audio_output_buffers(mpctx);
uninit_audio_chain(mpctx);
uninit_audio_out(mpctx);
- } else if (type == STREAM_SUB) {
- uninit_sub(mpctx, 0);
}
- } else if (order == 1) {
- if (type == STREAM_SUB)
- uninit_sub(mpctx, 1);
}
+ if (type == STREAM_SUB)
+ uninit_sub(mpctx, current);
- if (current)
+ if (current) {
current->selected = false;
+ reselect_demux_stream(mpctx, current);
+ }
if (track && track->demuxer == mpctx->demuxer)
demux_set_enable_refresh_seeks(mpctx->demuxer, true);
- reselect_demux_streams(mpctx);
-
mpctx->current_track[order][type] = track;
- if (track)
+ if (track) {
track->selected = true;
-
- reselect_demux_streams(mpctx);
+ reselect_demux_stream(mpctx, track);
+ }
demux_set_enable_refresh_seeks(mpctx->demuxer, false);
@@ -597,7 +484,7 @@ void mp_switch_track_n(struct MPContext *mpctx, int order, enum stream_type type
} else if (type == STREAM_AUDIO && order == 0) {
reinit_audio_chain(mpctx);
} else if (type == STREAM_SUB && order >= 0 && order <= 2) {
- reinit_subs(mpctx, order);
+ reinit_sub(mpctx, track);
}
mp_notify(mpctx, MPV_EVENT_TRACK_SWITCHED, NULL);
@@ -638,7 +525,6 @@ bool mp_remove_track(struct MPContext *mpctx, struct track *track)
{
if (!track->is_external)
return false;
- assert(!track->under_timeline);
mp_deselect_track(mpctx, track);
if (track->selected)
@@ -646,6 +532,8 @@ bool mp_remove_track(struct MPContext *mpctx, struct track *track)
struct demuxer *d = track->demuxer;
+ sub_destroy(track->d_sub);
+
int index = 0;
while (index < mpctx->num_tracks && mpctx->tracks[index] != track)
index++;
@@ -653,28 +541,21 @@ bool mp_remove_track(struct MPContext *mpctx, struct track *track)
talloc_free(track);
// Close the demuxer, unless there is still a track using it. These are
- // all external tracks, so there are no complications due to the timeline
- // mechanism switching the track's demuxer dynamically.
+ // all external tracks.
bool in_use = false;
for (int n = mpctx->num_tracks - 1; n >= 0 && !in_use; n--)
in_use |= mpctx->tracks[n]->demuxer == d;
- if (!in_use) {
- for (int n = 0; n < mpctx->num_sources; n++) {
- if (mpctx->sources[n] == d) {
- MP_TARRAY_REMOVE_AT(mpctx->sources, mpctx->num_sources, n);
- break;
- }
- }
- uninit_stream_sub_decoders(d);
+ if (!in_use)
free_demuxer_and_stream(d);
- }
mp_notify(mpctx, MPV_EVENT_TRACKS_CHANGED, NULL);
return true;
}
+// Add the given file as additional track. Only tracks of type "filter" are
+// included; pass STREAM_TYPE_COUNT to disable filtering.
struct track *mp_add_external_file(struct MPContext *mpctx, char *filename,
enum stream_type filter)
{
@@ -686,9 +567,7 @@ struct track *mp_add_external_file(struct MPContext *mpctx, char *filename,
if (strncmp(disp_filename, "memory://", 9) == 0)
disp_filename = "memory://"; // avoid noise
- struct demuxer_params params = {
- .expect_subtitle = filter == STREAM_SUB,
- };
+ struct demuxer_params params = {0};
switch (filter) {
case STREAM_SUB:
@@ -703,19 +582,22 @@ struct track *mp_add_external_file(struct MPContext *mpctx, char *filename,
demux_open_url(filename, &params, mpctx->playback_abort, mpctx->global);
if (!demuxer)
goto err_out;
+ enable_demux_thread(mpctx, demuxer);
if (filter != STREAM_SUB && opts->rebase_start_time)
demux_set_ts_offset(demuxer, -demuxer->start_time);
struct track *first = NULL;
- for (int n = 0; n < demuxer->num_streams; n++) {
- struct sh_stream *sh = demuxer->streams[n];
- if (sh->type == filter) {
- struct track *t = add_stream_track(mpctx, demuxer, sh, false);
+ for (int n = 0; n < demux_get_num_stream(demuxer); n++) {
+ struct sh_stream *sh = demux_get_stream(demuxer, n);
+ if (filter == STREAM_TYPE_COUNT || sh->type == filter) {
+ struct track *t = add_stream_track(mpctx, demuxer, sh);
t->is_external = true;
t->title = talloc_strdup(t, mp_basename(disp_filename));
t->external_filename = talloc_strdup(t, filename);
first = t;
+ // --external-file special semantics
+ t->no_default = filter == STREAM_TYPE_COUNT;
}
}
if (!first) {
@@ -724,9 +606,6 @@ struct track *mp_add_external_file(struct MPContext *mpctx, char *filename,
goto err_out;
}
- MP_TARRAY_APPEND(NULL, mpctx->sources, mpctx->num_sources, demuxer);
- if (mpctx->playback_initialized)
- enable_demux_thread(mpctx);
return first;
err_out:
@@ -734,18 +613,11 @@ err_out:
return false;
}
-static void open_audiofiles_from_options(struct MPContext *mpctx)
-{
- struct MPOpts *opts = mpctx->opts;
- for (int n = 0; opts->audio_files && opts->audio_files[n]; n++)
- mp_add_external_file(mpctx, opts->audio_files[n], STREAM_AUDIO);
-}
-
-static void open_subtitles_from_options(struct MPContext *mpctx)
+static void open_external_files(struct MPContext *mpctx, char **files,
+ enum stream_type filter)
{
- struct MPOpts *opts = mpctx->opts;
- for (int i = 0; opts->sub_name && opts->sub_name[i] != NULL; i++)
- mp_add_external_file(mpctx, opts->sub_name[i], STREAM_SUB);
+ for (int n = 0; files && files[n]; n++)
+ mp_add_external_file(mpctx, files[n], filter);
}
void autoload_external_files(struct MPContext *mpctx)
@@ -773,8 +645,9 @@ void autoload_external_files(struct MPContext *mpctx)
for (int i = 0; list && list[i].fname; i++) {
char *filename = list[i].fname;
char *lang = list[i].lang;
- for (int n = 0; n < mpctx->num_sources; n++) {
- if (strcmp(mpctx->sources[n]->stream->url, filename) == 0)
+ for (int n = 0; n < mpctx->num_tracks; n++) {
+ struct track *t = mpctx->tracks[n];
+ if (t->demuxer && strcmp(t->demuxer->stream->url, filename) == 0)
goto skip;
}
if (list[i].type == STREAM_SUB && !sc[STREAM_VIDEO] && !sc[STREAM_AUDIO])
@@ -824,6 +697,8 @@ static void transfer_playlist(struct MPContext *mpctx, struct playlist *pl)
if (pl->first) {
prepare_playlist(mpctx, pl);
struct playlist_entry *new = pl->current;
+ if (mpctx->playlist->current)
+ playlist_add_redirect(pl, mpctx->playlist->current->filename);
playlist_transfer_entries(mpctx->playlist, pl);
// current entry is replaced
if (mpctx->playlist->current)
@@ -853,40 +728,30 @@ static int process_open_hooks(struct MPContext *mpctx)
return 0;
}
-static void process_unload_hooks(struct MPContext *mpctx)
+static int process_preloaded_hooks(struct MPContext *mpctx)
{
- mp_hook_run(mpctx, NULL, "on_unload");
+ mp_hook_run(mpctx, NULL, "on_preloaded");
- while (!mp_hook_test_completion(mpctx, "on_unload"))
+ while (!mp_hook_test_completion(mpctx, "on_preloaded")) {
mp_idle(mpctx);
+ if (mpctx->stop_play)
+ return -1;
+ }
+
+ return 0;
}
-static void print_timeline(struct MPContext *mpctx)
+static void process_unload_hooks(struct MPContext *mpctx)
{
- if (mpctx->timeline) {
- int part_count = mpctx->num_timeline_parts;
- MP_VERBOSE(mpctx, "Timeline contains %d parts from %d "
- "sources. Total length %.3f seconds.\n", part_count,
- mpctx->num_sources, mpctx->timeline[part_count].start);
- MP_VERBOSE(mpctx, "Source files:\n");
- for (int i = 0; i < mpctx->num_sources; i++)
- MP_VERBOSE(mpctx, "%d: %s\n", i,
- mpctx->sources[i]->filename);
- MP_VERBOSE(mpctx, "Timeline parts: (number, start, "
- "source_start, source):\n");
- for (int i = 0; i < part_count; i++) {
- struct timeline_part *p = mpctx->timeline + i;
- MP_VERBOSE(mpctx, "%3d %9.3f %9.3f %p/%s\n", i, p->start,
- p->source_start, p->source, p->source->filename);
- }
- MP_VERBOSE(mpctx, "END %9.3f\n",
- mpctx->timeline[part_count].start);
- }
+ mp_hook_run(mpctx, NULL, "on_unload");
+
+ while (!mp_hook_test_completion(mpctx, "on_unload"))
+ mp_idle(mpctx);
}
static void load_chapters(struct MPContext *mpctx)
{
- struct demuxer *src = mpctx->master_demuxer;
+ struct demuxer *src = mpctx->demuxer;
bool free_src = false;
char *chapter_file = mpctx->opts->chapter_file;
if (chapter_file && chapter_file[0]) {
@@ -930,7 +795,6 @@ struct demux_open_args {
struct mp_log *log;
// results
struct demuxer *demux;
- struct timeline *tl;
int err;
};
@@ -951,11 +815,8 @@ static void open_demux_thread(void *pctx)
args->err = MPV_ERROR_LOADING_FAILED;
}
}
- if (args->demux) {
- args->tl = timeline_load(global, args->log, args->demux);
- if (global->opts->rebase_start_time)
- demux_set_ts_offset(args->demux, -args->demux->start_time);
- }
+ if (args->demux && global->opts->rebase_start_time)
+ demux_set_ts_offset(args->demux, -args->demux->start_time);
}
static void open_demux_reentrant(struct MPContext *mpctx)
@@ -972,8 +833,8 @@ static void open_demux_reentrant(struct MPContext *mpctx)
mpctx_run_reentrant(mpctx, open_demux_thread, &args);
if (args.demux) {
talloc_steal(args.demux, args.global);
- mpctx->master_demuxer = args.demux;
- mpctx->tl = args.tl;
+ mpctx->demuxer = args.demux;
+ enable_demux_thread(mpctx, mpctx->demuxer);
} else {
mpctx->error_playing = args.err;
talloc_free(args.global);
@@ -981,29 +842,113 @@ static void open_demux_reentrant(struct MPContext *mpctx)
talloc_free(args.url);
}
-static void load_timeline(struct MPContext *mpctx)
+static bool init_complex_filters(struct MPContext *mpctx)
{
- mpctx->track_layout = mpctx->master_demuxer;
-
- MP_TARRAY_APPEND(NULL, mpctx->sources, mpctx->num_sources,
- mpctx->master_demuxer);
-
- if (mpctx->tl) {
- mpctx->timeline = mpctx->tl->parts;
- mpctx->num_timeline_parts = mpctx->tl->num_parts;
- mpctx->num_chapters = mpctx->tl->num_chapters;
- mpctx->chapters = demux_copy_chapter_data(mpctx->tl->chapters,
- mpctx->tl->num_chapters);
- mpctx->track_layout = mpctx->tl->track_layout;
- for (int n = 0; n < mpctx->tl->num_sources; n++) {
- if (mpctx->tl->sources[n] != mpctx->master_demuxer) {
- MP_TARRAY_APPEND(NULL, mpctx->sources, mpctx->num_sources,
- mpctx->tl->sources[n]);
- }
+ assert(!mpctx->lavfi);
+
+ char *graph = mpctx->opts->lavfi_complex;
+
+ if (!graph || !graph[0])
+ return true;
+
+ mpctx->lavfi = lavfi_create(mpctx->log, graph);
+ if (!mpctx->lavfi)
+ return false;
+
+ if (lavfi_has_failed(mpctx->lavfi))
+ return false;
+
+ for (int n = 0; n < mpctx->num_tracks; n++) {
+ struct track *track = mpctx->tracks[n];
+
+ char label[32];
+ char prefix;
+ switch (track->type) {
+ case STREAM_VIDEO: prefix = 'v'; break;
+ case STREAM_AUDIO: prefix = 'a'; break;
+ default: continue;
+ }
+ snprintf(label, sizeof(label), "%cid%d", prefix, track->user_tid);
+
+ struct lavfi_pad *pad = lavfi_find_pad(mpctx->lavfi, label);
+ if (!pad)
+ continue;
+ if (lavfi_pad_type(pad) != track->type)
+ continue;
+ if (lavfi_pad_direction(pad) != LAVFI_IN)
+ continue;
+ if (lavfi_get_connected(pad))
+ continue;
+
+ track->sink = pad;
+ lavfi_set_connected(pad, true);
+ track->selected = true;
+ }
+
+ struct lavfi_pad *pad = lavfi_find_pad(mpctx->lavfi, "vo");
+ if (pad && lavfi_pad_type(pad) == STREAM_VIDEO &&
+ lavfi_pad_direction(pad) == LAVFI_OUT)
+ {
+ lavfi_set_connected(pad, true);
+ reinit_video_chain_src(mpctx, pad);
+ }
+
+ pad = lavfi_find_pad(mpctx->lavfi, "ao");
+ if (pad && lavfi_pad_type(pad) == STREAM_AUDIO &&
+ lavfi_pad_direction(pad) == LAVFI_OUT)
+ {
+ lavfi_set_connected(pad, true);
+ reinit_audio_chain_src(mpctx, pad);
+ }
+
+ return true;
+}
+
+static bool init_complex_filter_decoders(struct MPContext *mpctx)
+{
+ if (!mpctx->lavfi)
+ return true;
+
+ for (int n = 0; n < mpctx->num_tracks; n++) {
+ struct track *track = mpctx->tracks[n];
+ if (track->sink && track->type == STREAM_VIDEO) {
+ if (!init_video_decoder(mpctx, track))
+ return false;
+ }
+ if (track->sink && track->type == STREAM_AUDIO) {
+ if (!init_audio_decoder(mpctx, track))
+ return false;
}
}
- print_timeline(mpctx);
+ return true;
+}
+
+static void uninit_complex_filters(struct MPContext *mpctx)
+{
+ if (!mpctx->lavfi)
+ return;
+
+ for (int n = 0; n < mpctx->num_tracks; n++) {
+ struct track *track = mpctx->tracks[n];
+
+ if (track->d_video && !track->vo_c) {
+ video_uninit(track->d_video);
+ track->d_video = NULL;
+ }
+ if (track->d_audio && !track->ao_c) {
+ audio_uninit(track->d_audio);
+ track->d_audio = NULL;
+ }
+ }
+
+ if (mpctx->vo_chain && mpctx->vo_chain->filter_src)
+ uninit_video_chain(mpctx);
+ if (mpctx->ao_chain && mpctx->ao_chain->filter_src)
+ uninit_audio_chain(mpctx);
+
+ lavfi_destroy(mpctx->lavfi);
+ mpctx->lavfi = NULL;
}
// Start playing the current playlist entry.
@@ -1011,7 +956,6 @@ static void load_timeline(struct MPContext *mpctx)
static void play_current_file(struct MPContext *mpctx)
{
struct MPOpts *opts = mpctx->opts;
- void *tmp = talloc_new(NULL);
double playback_start = -1e100;
mp_notify(mpctx, MPV_EVENT_START_FILE, NULL);
@@ -1030,7 +974,6 @@ static void play_current_file(struct MPContext *mpctx)
mpctx->paused = false;
mpctx->paused_for_cache = false;
mpctx->playing_msg_shown = false;
- mpctx->backstep_active = false;
mpctx->max_frames = -1;
mpctx->video_speed = mpctx->audio_speed = opts->playback_speed;
mpctx->speed_factor_a = mpctx->speed_factor_v = 1.0;
@@ -1045,7 +988,7 @@ static void play_current_file(struct MPContext *mpctx)
goto terminate_playback;
mpctx->playing->reserved += 1;
- mpctx->filename = talloc_strdup(tmp, mpctx->playing->filename);
+ mpctx->filename = talloc_strdup(NULL, mpctx->playing->filename);
mpctx->stream_open_filename = mpctx->filename;
mpctx->add_osd_seek_info &= OSD_SEEK_INFO_EDITION | OSD_SEEK_INFO_CURRENT_FILE;
@@ -1078,12 +1021,7 @@ static void play_current_file(struct MPContext *mpctx)
reopen_file:
- assert(mpctx->stream == NULL);
assert(mpctx->demuxer == NULL);
- assert(mpctx->d_audio == NULL);
- assert(mpctx->d_video == NULL);
- assert(mpctx->d_sub[0] == NULL);
- assert(mpctx->d_sub[1] == NULL);
if (process_open_hooks(mpctx) < 0)
goto terminate_playback;
@@ -1095,11 +1033,8 @@ reopen_file:
}
open_demux_reentrant(mpctx);
- if (!mpctx->master_demuxer || mpctx->stop_play)
+ if (!mpctx->demuxer || mpctx->stop_play)
goto terminate_playback;
- mpctx->demuxer = mpctx->master_demuxer;
-
- load_timeline(mpctx);
if (mpctx->demuxer->playlist) {
struct playlist *pl = mpctx->demuxer->playlist;
@@ -1118,21 +1053,31 @@ reopen_file:
}
load_chapters(mpctx);
- add_demuxer_tracks(mpctx, mpctx->track_layout);
-
- mpctx->timeline_part = mpctx->num_timeline_parts;
- timeline_switch_to_time(mpctx, 0);
+ add_demuxer_tracks(mpctx, mpctx->demuxer);
- open_subtitles_from_options(mpctx);
- open_audiofiles_from_options(mpctx);
+ open_external_files(mpctx, opts->audio_files, STREAM_AUDIO);
+ open_external_files(mpctx, opts->sub_name, STREAM_SUB);
+ open_external_files(mpctx, opts->external_files, STREAM_TYPE_COUNT);
autoload_external_files(mpctx);
check_previous_track_selection(mpctx);
+ if (process_preloaded_hooks(mpctx))
+ goto terminate_playback;
+
+ if (!init_complex_filters(mpctx))
+ goto terminate_playback;
+
assert(NUM_PTRACKS == 2); // opts->stream_id is hardcoded to 2
for (int t = 0; t < STREAM_TYPE_COUNT; t++) {
- for (int i = 0; i < NUM_PTRACKS; i++)
- mpctx->current_track[i][t] = select_default_track(mpctx, i, t);
+ for (int i = 0; i < NUM_PTRACKS; i++) {
+ struct track *sel = NULL;
+ bool taken = (t == STREAM_VIDEO && mpctx->vo_chain) ||
+ (t == STREAM_AUDIO && mpctx->ao_chain);
+ if (!taken)
+ sel = select_default_track(mpctx, i, t);
+ mpctx->current_track[i][t] = sel;
+ }
}
for (int t = 0; t < STREAM_TYPE_COUNT; t++) {
for (int i = 0; i < NUM_PTRACKS; i++) {
@@ -1148,18 +1093,11 @@ reopen_file:
}
}
}
- reselect_demux_streams(mpctx);
- update_demuxer_properties(mpctx);
-
- enable_demux_thread(mpctx);
+ for (int n = 0; n < mpctx->num_tracks; n++)
+ reselect_demux_stream(mpctx, mpctx->tracks[n]);
- if (mpctx->current_track[0][STREAM_VIDEO] &&
- mpctx->current_track[0][STREAM_VIDEO]->attached_picture)
- {
- MP_INFO(mpctx,
- "Displaying attached picture. Use --no-audio-display to prevent this.\n");
- }
+ update_demuxer_properties(mpctx);
#if HAVE_ENCODING
if (mpctx->encode_lavc_ctx && mpctx->current_track[0][STREAM_VIDEO])
@@ -1172,23 +1110,34 @@ reopen_file:
}
#endif
- if (!mpctx->current_track[0][STREAM_VIDEO] &&
- !mpctx->current_track[0][STREAM_AUDIO])
- {
+ update_playback_speed(mpctx);
+
+ if (!init_complex_filter_decoders(mpctx))
+ goto terminate_playback;
+
+ reinit_video_chain(mpctx);
+ reinit_audio_chain(mpctx);
+ reinit_sub_all(mpctx);
+
+ if (!mpctx->vo_chain && !mpctx->ao_chain) {
MP_FATAL(mpctx, "No video or audio streams selected.\n");
mpctx->error_playing = MPV_ERROR_NOTHING_TO_PLAY;
goto terminate_playback;
}
- update_playback_speed(mpctx);
+ if (mpctx->vo_chain && mpctx->vo_chain->is_coverart) {
+ MP_INFO(mpctx,
+ "Displaying attached picture. Use --no-audio-display to prevent this.\n");
+ }
- reinit_video_chain(mpctx);
- reinit_audio_chain(mpctx);
- reinit_subs(mpctx, 0);
- reinit_subs(mpctx, 1);
+ if (!mpctx->vo_chain)
+ handle_force_window(mpctx, true);
MP_VERBOSE(mpctx, "Starting playback...\n");
+ mpctx->playback_initialized = true;
+ mp_notify(mpctx, MPV_EVENT_FILE_LOADED, NULL);
+
if (mpctx->max_frames == 0) {
if (!mpctx->stop_play)
mpctx->stop_play = PT_NEXT_ENTRY;
@@ -1196,27 +1145,20 @@ reopen_file:
goto terminate_playback;
}
- // If there's a timeline force an absolute seek to initialize state
double startpos = rel_time_to_abs(mpctx, opts->play_start);
if (startpos == MP_NOPTS_VALUE && opts->chapterrange[0] > 0) {
double start = chapter_start_time(mpctx, opts->chapterrange[0] - 1);
if (start != MP_NOPTS_VALUE)
startpos = start;
}
- if (startpos == MP_NOPTS_VALUE && mpctx->timeline)
- startpos = 0;
if (startpos != MP_NOPTS_VALUE) {
queue_seek(mpctx, MPSEEK_ABSOLUTE, startpos, 0, true);
execute_queued_seek(mpctx);
}
- get_relative_time(mpctx); // reset current delta
if (mpctx->opts->pause)
pause_player(mpctx);
- mpctx->playback_initialized = true;
- mp_notify(mpctx, MPV_EVENT_FILE_LOADED, NULL);
-
playback_start = mp_time_sec();
mpctx->error_playing = 0;
while (!mpctx->stop_play)
@@ -1240,12 +1182,11 @@ terminate_playback:
mp_cancel_trigger(mpctx->playback_abort);
// time to uninit all, except global stuff:
+ uninit_complex_filters(mpctx);
uninit_audio_chain(mpctx);
uninit_video_chain(mpctx);
uninit_sub_all(mpctx);
- uninit_sub_renderer(mpctx);
uninit_demuxer(mpctx);
- uninit_stream(mpctx);
if (!opts->gapless_audio && !mpctx->encode_lavc_ctx)
uninit_audio_out(mpctx);
@@ -1253,6 +1194,7 @@ terminate_playback:
if (mpctx->stop_play == PT_RELOAD_FILE) {
mpctx->stop_play = KEEP_PLAYING;
+ mp_cancel_reset(mpctx->playback_abort);
goto reopen_file;
}
@@ -1305,6 +1247,7 @@ terminate_playback:
if (mpctx->playing)
playlist_entry_unref(mpctx->playing);
mpctx->playing = NULL;
+ talloc_free(mpctx->filename);
mpctx->filename = NULL;
mpctx->stream_open_filename = NULL;
@@ -1315,8 +1258,6 @@ terminate_playback:
} else {
mpctx->files_played++;
}
-
- talloc_free(tmp);
}
// Determine the next file to play. Note that if this function returns non-NULL,
diff --git a/player/lua.c b/player/lua.c
index 3bf5298..9fe1d0d 100644
--- a/player/lua.c
+++ b/player/lua.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 <assert.h>
@@ -30,7 +30,7 @@
#include "osdep/io.h"
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "common/common.h"
#include "options/m_property.h"
diff --git a/player/lua/osc.lua b/player/lua/osc.lua
index 993c103..24afeef 100644
--- a/player/lua/osc.lua
+++ b/player/lua/osc.lua
@@ -20,7 +20,8 @@ local user_opts = {
boxalpha = 80, -- alpha of the background box,
-- 0 (opaque) to 255 (fully transparent)
hidetimeout = 500, -- duration in ms until the OSC hides if no
- -- mouse movement, negative value = disabled
+ -- mouse movement. enforced non-negative for the
+ -- user, but internally negative is "always-on".
fadeduration = 200, -- duration of fade out in ms, 0 = no fade
deadzonesize = 0, -- size of deadzone
minmousemove = 3, -- minimum amount of pixels the mouse has to
@@ -32,10 +33,18 @@ local user_opts = {
seekbarstyle = "slider", -- slider (diamond marker) or bar (fill)
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(...)
}
+-- read_options may modify hidetimeout, so save the original default value in
+-- case the user set hidetimeout < 0 and we need the default instead.
+local hidetimeout_def = user_opts.hidetimeout
-- read options from config and command-line
opt.read_options(user_opts, "osc")
+if user_opts.hidetimeout < 0 then
+ user_opts.hidetimeout = hidetimeout_def
+ msg.warn("hidetimeout cannot be negative. Using " .. user_opts.hidetimeout)
+end
local osc_param = { -- calculated by osc_init()
playresy = 0, -- canvas size Y
@@ -1607,6 +1616,9 @@ end
function show_osc()
+ -- show when disabled can happen (e.g. mouse_move) due to async/delayed unbinding
+ if not state.enabled then return end
+
msg.debug("show_osc")
--remember last time of invocation (mouse move)
state.showtime = mp.get_time()
@@ -1616,12 +1628,17 @@ function show_osc()
if (user_opts.fadeduration > 0) then
state.anitype = nil
end
-
end
function hide_osc()
msg.debug("hide_osc")
- if (user_opts.fadeduration > 0) then
+ if not state.enabled then
+ -- typically hide happens at render() from tick(), but now tick() is
+ -- no-op and won't render again to remove the osc, so do that manually.
+ state.osc_visible = false
+ timer_stop()
+ render() -- state.osc_visible == false -> remove the osc from screen
+ elseif (user_opts.fadeduration > 0) then
if not(state.osc_visible == false) then
state.anitype = "out"
control_timer()
@@ -1688,7 +1705,9 @@ end
function mouse_leave()
- hide_osc()
+ if user_opts.hidetimeout >= 0 then
+ hide_osc()
+ end
-- reset mouse position
state.last_mouseX, state.last_mouseY = nil, nil
end
@@ -1958,9 +1977,8 @@ function enable_osc(enable)
state.enabled = enable
if enable then
do_enable_keybindings()
- show_osc()
else
- hide_osc()
+ hide_osc() -- acts immediately when state.enabled == false
if state.showhide_enabled then
mp.disable_key_bindings("showhide")
end
@@ -1974,9 +1992,6 @@ mp.register_event("start-file", request_init)
mp.register_event("tracks-changed", request_init)
mp.observe_property("playlist", nil, request_init)
-mp.register_script_message("enable-osc", function() enable_osc(true) end)
-mp.register_script_message("disable-osc", function() enable_osc(false) end)
-
mp.register_script_message("osc-message", show_message)
mp.observe_property("fullscreen", "bool",
@@ -2018,6 +2033,53 @@ mp.set_key_bindings({
{"mouse_btn0_dbl", "ignore"},
{"shift+mouse_btn0_dbl", "ignore"},
{"mouse_btn2_dbl", "ignore"},
- {"del", function() enable_osc(false) end}
}, "input", "force")
mp.enable_key_bindings("input")
+
+
+user_opts.hidetimeout_orig = user_opts.hidetimeout
+
+function always_on(val)
+ if val then
+ user_opts.hidetimeout = -1 -- disable autohide
+ if state.enabled then show_osc() end
+ else
+ user_opts.hidetimeout = user_opts.hidetimeout_orig
+ if state.enabled then hide_osc() end
+ end
+end
+
+-- mode can be auto/always/never/cycle
+-- the modes only affect internal variables and not stored on its own.
+function visibility_mode(mode, no_osd)
+ if mode == "cycle" then
+ if not state.enabled then
+ mode = "auto"
+ elseif user_opts.hidetimeout >= 0 then
+ mode = "always"
+ else
+ mode = "never"
+ end
+ end
+
+ if mode == "auto" then
+ always_on(false)
+ enable_osc(true)
+ elseif mode == "always" then
+ enable_osc(true)
+ always_on(true)
+ elseif mode == "never" then
+ enable_osc(false)
+ else
+ msg.warn("Ignoring unknown visibility mode '" .. mode .. "'")
+ return
+ end
+
+ if not no_osd and tonumber(mp.get_property("osd-level")) >= 1 then
+ mp.osd_message("OSC visibility: " .. mode)
+ end
+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)
diff --git a/player/lua/ytdl_hook.lua b/player/lua/ytdl_hook.lua
index 010e90c..5b90b95 100644
--- a/player/lua/ytdl_hook.lua
+++ b/player/lua/ytdl_hook.lua
@@ -27,9 +27,12 @@ local function set_http_headers(http_headers)
if useragent and not option_was_set("user-agent") then
mp.set_property("file-local-options/user-agent", useragent)
end
- local cookies = http_headers["Cookie"]
- if cookies then
- headers[#headers + 1] = "Cookie: " .. cookies
+ local additional_fields = {"Cookie", "Referer"}
+ for idx, item in pairs(additional_fields) do
+ local field_value = http_headers[item]
+ if field_value then
+ headers[#headers + 1] = item .. ": " .. field_value
+ end
end
if #headers > 0 and not option_was_set("http-header-fields") then
mp.set_property_native("file-local-options/http-header-fields", headers)
diff --git a/player/main.c b/player/main.c
index ce43863..70176f2 100644
--- a/player/main.c
+++ b/player/main.c
@@ -25,7 +25,7 @@
#include <signal.h>
#include "config.h"
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "misc/dispatch.h"
#include "osdep/io.h"
@@ -97,6 +97,11 @@ const char mp_help_text[] =
"\n";
static const char def_config[] =
+#if HAVE_RPI
+ "hwdec=rpi\n"
+ "fullscreen=yes\n"
+#endif
+ "\n"
"[pseudo-gui]\n"
"terminal=no\n"
"force-window=yes\n"
@@ -156,7 +161,7 @@ void mp_print_version(struct mp_log *log, int always)
{
int v = always ? MSGL_INFO : MSGL_V;
mp_msg(log, v,
- "%s (C) 2000-2015 mpv/MPlayer/mplayer2 projects\n built on %s\n",
+ "%s (C) 2000-2016 mpv/MPlayer/mplayer2 projects\n built on %s\n",
mpv_version, mpv_builddate);
print_libav_versions(log, v);
mp_msg(log, v, "\n");
@@ -222,7 +227,6 @@ void mp_destroy(struct MPContext *mpctx)
pthread_detach(pthread_self());
mp_msg_uninit(mpctx->global);
- pthread_mutex_destroy(&mpctx->ass_lock);
talloc_free(mpctx);
}
@@ -330,8 +334,6 @@ struct MPContext *mp_create(void)
.playback_abort = mp_cancel_new(mpctx),
};
- pthread_mutex_init(&mpctx->ass_lock, NULL);
-
mpctx->global = talloc_zero(mpctx, struct mpv_global);
// Nothing must call mp_msg*() and related before this
diff --git a/player/misc.c b/player/misc.c
index 0b2548b..d68ad1d 100644
--- a/player/misc.c
+++ b/player/misc.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 <stddef.h>
@@ -21,7 +21,7 @@
#include <assert.h>
#include "config.h"
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "osdep/io.h"
#include "osdep/timer.h"
@@ -45,14 +45,6 @@
#include "core.h"
#include "command.h"
-double get_relative_time(struct MPContext *mpctx)
-{
- int64_t new_time = mp_time_us();
- int64_t delta = new_time - mpctx->last_time;
- mpctx->last_time = new_time;
- return delta * 0.000001;
-}
-
double rel_time_to_abs(struct MPContext *mpctx, struct m_rel_time t)
{
double length = get_time_length(mpctx);
@@ -101,20 +93,6 @@ double get_play_end_pts(struct MPContext *mpctx)
return end;
}
-// Time used to seek external tracks to.
-double get_main_demux_pts(struct MPContext *mpctx)
-{
- double main_new_pos = MP_NOPTS_VALUE;
- if (mpctx->demuxer) {
- for (int n = 0; n < mpctx->demuxer->num_streams; n++) {
- struct sh_stream *stream = mpctx->demuxer->streams[n];
- if (main_new_pos == MP_NOPTS_VALUE && stream->type != STREAM_SUB)
- main_new_pos = demux_get_next_pts(stream);
- }
- }
- return main_new_pos;
-}
-
float mp_get_cache_percent(struct MPContext *mpctx)
{
if (mpctx->demuxer) {
@@ -185,25 +163,22 @@ void update_window_title(struct MPContext *mpctx, bool force)
void error_on_track(struct MPContext *mpctx, struct track *track)
{
- if (!track)
+ if (!track || !track->selected)
return;
mp_deselect_track(mpctx, track);
- if (track) {
- if (track->type == STREAM_AUDIO)
- MP_INFO(mpctx, "Audio: no audio\n");
- if (track->type == STREAM_VIDEO)
- MP_INFO(mpctx, "Video: no video\n");
- if (mpctx->opts->stop_playback_on_init_failure ||
- (!mpctx->current_track[0][STREAM_AUDIO] &&
- !mpctx->current_track[0][STREAM_VIDEO]))
- {
- if (!mpctx->stop_play)
- mpctx->stop_play = PT_ERROR;
- if (mpctx->error_playing >= 0)
- mpctx->error_playing = MPV_ERROR_NOTHING_TO_PLAY;
- }
- mpctx->sleeptime = 0;
+ if (track->type == STREAM_AUDIO)
+ MP_INFO(mpctx, "Audio: no audio\n");
+ if (track->type == STREAM_VIDEO)
+ MP_INFO(mpctx, "Video: no video\n");
+ if (mpctx->opts->stop_playback_on_init_failure ||
+ !(mpctx->vo_chain || mpctx->ao_chain))
+ {
+ if (!mpctx->stop_play)
+ mpctx->stop_play = PT_ERROR;
+ if (mpctx->error_playing >= 0)
+ mpctx->error_playing = MPV_ERROR_NOTHING_TO_PLAY;
}
+ mpctx->sleeptime = 0;
}
int stream_dump(struct MPContext *mpctx, const char *source_filename)
@@ -264,6 +239,7 @@ struct mpv_global *create_sub_global(struct MPContext *mpctx)
*new = (struct mpv_global){
.log = mpctx->global->log,
.opts = new_config->optstruct,
+ .client_api = mpctx->clients,
};
return new;
}
diff --git a/player/osd.c b/player/osd.c
index da1fb41..2a09911 100644
--- a/player/osd.c
+++ b/player/osd.c
@@ -23,7 +23,7 @@
#include <assert.h>
#include "config.h"
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "common/msg.h"
#include "common/msg_control.h"
@@ -39,6 +39,7 @@
#include "stream/stream.h"
#include "sub/osd.h"
+#include "video/decode/dec_video.h"
#include "video/out/vo.h"
#include "core.h"
@@ -194,9 +195,9 @@ static void print_status(struct MPContext *mpctx)
saddf(&line, "(Paused) ");
}
- if (mpctx->d_audio)
+ if (mpctx->ao_chain)
saddf(&line, "A");
- if (mpctx->d_video)
+ if (mpctx->vo_chain)
saddf(&line, "V");
saddf(&line, ": ");
@@ -216,7 +217,7 @@ static void print_status(struct MPContext *mpctx)
saddf(&line, " x%4.2f", opts->playback_speed);
// A-V sync
- if (mpctx->d_audio && mpctx->d_video && mpctx->sync_audio_to_video) {
+ if (mpctx->ao_chain && mpctx->vo_chain && !mpctx->vo_chain->is_coverart) {
saddf(&line, " A-V:%7.3f", mpctx->last_av_difference);
if (fabs(mpctx->total_avsync_change) > 0.05)
saddf(&line, " ct:%7.3f", mpctx->total_avsync_change);
@@ -234,18 +235,23 @@ static void print_status(struct MPContext *mpctx)
#endif
{
// VO stats
- if (mpctx->d_video) {
+ if (mpctx->vo_chain) {
if (mpctx->display_sync_active) {
- char *r = mp_property_expand_string(mpctx, "${vsync-ratio}");
- saddf(&line, " DS: %s/%"PRId64, r,
- vo_get_delayed_count(mpctx->video_out));
+ char *r = mp_property_expand_string(mpctx,
+ "${?vsync-ratio:${vsync-ratio}}");
+ if (r[0]) {
+ saddf(&line, " DS: %s/%"PRId64, r,
+ vo_get_delayed_count(mpctx->video_out));
+ }
talloc_free(r);
}
int64_t c = vo_get_drop_count(mpctx->video_out);
- if (c > 0 || mpctx->dropped_frames_total > 0) {
+ struct dec_video *d_video = mpctx->vo_chain->video_src;
+ int dropped_frames = d_video ? d_video->dropped_frames : 0;
+ if (c > 0 || dropped_frames > 0) {
saddf(&line, " Dropped: %"PRId64, c);
- if (mpctx->dropped_frames_total)
- saddf(&line, "/%d", mpctx->dropped_frames_total);
+ if (dropped_frames)
+ saddf(&line, "/%d", dropped_frames);
}
}
}
@@ -461,13 +467,11 @@ static void add_seek_osd_messages(struct MPContext *mpctx)
"Chapter: %s", chapter);
talloc_free(chapter);
}
- if ((mpctx->add_osd_seek_info & OSD_SEEK_INFO_EDITION)
- && mpctx->master_demuxer)
- {
+ if ((mpctx->add_osd_seek_info & OSD_SEEK_INFO_EDITION) && mpctx->demuxer) {
set_osd_msg(mpctx, 1, mpctx->opts->osd_duration,
"Playing edition %d of %d.",
- mpctx->master_demuxer->edition + 1,
- mpctx->master_demuxer->num_editions);
+ mpctx->demuxer->edition + 1,
+ mpctx->demuxer->num_editions);
}
if (mpctx->add_osd_seek_info & OSD_SEEK_INFO_CURRENT_FILE) {
if (mpctx->filename) {
diff --git a/player/playloop.c b/player/playloop.c
index 5efb048..6323481 100644
--- a/player/playloop.c
+++ b/player/playloop.c
@@ -22,7 +22,7 @@
#include <assert.h>
#include "config.h"
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "common/msg.h"
#include "options/options.h"
@@ -73,6 +73,14 @@ void mp_process_input(struct MPContext *mpctx)
}
}
+double get_relative_time(struct MPContext *mpctx)
+{
+ int64_t new_time = mp_time_us();
+ int64_t delta = new_time - mpctx->last_time;
+ mpctx->last_time = new_time;
+ return delta * 0.000001;
+}
+
void pause_player(struct MPContext *mpctx)
{
mpctx->opts->pause = 1;
@@ -89,7 +97,7 @@ void pause_player(struct MPContext *mpctx)
mpctx->osd_force_update = true;
mpctx->paused_for_cache = false;
- if (mpctx->ao && mpctx->d_audio)
+ if (mpctx->ao && mpctx->ao_chain)
ao_pause(mpctx->ao);
if (mpctx->video_out)
vo_set_paused(mpctx->video_out, true);
@@ -114,7 +122,7 @@ void unpause_player(struct MPContext *mpctx)
mpctx->osd_function = 0;
mpctx->osd_force_update = true;
- if (mpctx->ao && mpctx->d_audio)
+ if (mpctx->ao && mpctx->ao_chain)
ao_resume(mpctx->ao);
if (mpctx->video_out)
vo_set_paused(mpctx->video_out, false);
@@ -127,15 +135,14 @@ end:
void add_step_frame(struct MPContext *mpctx, int dir)
{
- if (!mpctx->d_video)
+ if (!mpctx->vo_chain)
return;
if (dir > 0) {
mpctx->step_frames += 1;
unpause_player(mpctx);
} else if (dir < 0) {
- if (!mpctx->backstep_active && !mpctx->hrseek_active) {
- mpctx->backstep_active = true;
- mpctx->backstep_start_seek_ts = mpctx->vo_pts_history_seek_ts;
+ if (!mpctx->hrseek_backstep || !mpctx->hrseek_active) {
+ queue_seek(mpctx, MPSEEK_BACKSTEP, 0, MPSEEK_VERY_EXACT, true);
pause_player(mpctx);
}
}
@@ -144,6 +151,16 @@ void add_step_frame(struct MPContext *mpctx, int dir)
// Clear some playback-related fields on file loading or after seeks.
void reset_playback_state(struct MPContext *mpctx)
{
+ if (mpctx->lavfi)
+ lavfi_seek_reset(mpctx->lavfi);
+
+ for (int n = 0; n < mpctx->num_tracks; n++) {
+ if (mpctx->tracks[n]->d_video)
+ video_reset(mpctx->tracks[n]->d_video);
+ if (mpctx->tracks[n]->d_audio)
+ audio_reset_decoding(mpctx->tracks[n]->d_audio);
+ }
+
reset_video_state(mpctx);
reset_audio_state(mpctx);
reset_subtitle_state(mpctx);
@@ -151,6 +168,7 @@ void reset_playback_state(struct MPContext *mpctx)
mpctx->hrseek_active = false;
mpctx->hrseek_framedrop = false;
mpctx->hrseek_lastframe = false;
+ mpctx->hrseek_backstep = false;
mpctx->playback_pts = MP_NOPTS_VALUE;
mpctx->last_seek_pts = MP_NOPTS_VALUE;
mpctx->cache_wait_time = 0;
@@ -163,14 +181,11 @@ void reset_playback_state(struct MPContext *mpctx)
}
// return -1 if seek failed (non-seekable stream?), 0 otherwise
-static int mp_seek(MPContext *mpctx, struct seek_params seek,
- bool timeline_fallthrough)
+static int mp_seek(MPContext *mpctx, struct seek_params seek)
{
struct MPOpts *opts = mpctx->opts;
- uint64_t prev_seek_ts = mpctx->vo_pts_history_seek_ts;
- int prev_step = mpctx->step_frames;
- if (!mpctx->demuxer)
+ if (!mpctx->demuxer || seek.type == MPSEEK_NONE || seek.amount == MP_NOPTS_VALUE)
return -1;
if (!mpctx->demuxer->seekable) {
@@ -178,129 +193,99 @@ static int mp_seek(MPContext *mpctx, struct seek_params seek,
return -1;
}
- if (mpctx->stop_play == AT_END_OF_FILE)
- mpctx->stop_play = KEEP_PLAYING;
-
- double hr_seek_offset = opts->hr_seek_demuxer_offset;
bool hr_seek_very_exact = seek.exact == MPSEEK_VERY_EXACT;
- // Always try to compensate for possibly bad demuxers in "special"
- // situations where we need more robustness from the hr-seek code, even
- // if the user doesn't use --hr-seek-demuxer-offset.
- // The value is arbitrary, but should be "good enough" in most situations.
- if (hr_seek_very_exact)
- hr_seek_offset = MPMAX(hr_seek_offset, 0.5); // arbitrary
-
- double target_time = MP_NOPTS_VALUE;
- int direction = 0;
+ double current_time = get_current_time(mpctx);
+ if (current_time == MP_NOPTS_VALUE)
+ current_time = 0;
+ double seek_pts = MP_NOPTS_VALUE;
+ int demux_flags = 0;
switch (seek.type) {
case MPSEEK_ABSOLUTE:
- target_time = seek.amount;
+ seek_pts = seek.amount;
+ break;
+ case MPSEEK_BACKSTEP:
+ seek_pts = current_time;
+ hr_seek_very_exact = true;
break;
case MPSEEK_RELATIVE:
- direction = seek.amount > 0 ? 1 : -1;
- double cur = get_current_time(mpctx);
- target_time = seek.amount + (cur == MP_NOPTS_VALUE ? 0 : cur);
+ demux_flags = seek.amount > 0 ? SEEK_FORWARD : SEEK_BACKWARD;
+ seek_pts = current_time + seek.amount;
break;
case MPSEEK_FACTOR: ;
double len = get_time_length(mpctx);
if (len >= 0)
- target_time = seek.amount * len;
+ seek_pts = seek.amount * len;
+ demux_flags = seek_pts > current_time ? SEEK_FORWARD : SEEK_BACKWARD;
break;
+ default: abort();
}
- bool hr_seek = opts->correct_pts && seek.exact != MPSEEK_KEYFRAME;
- hr_seek &= (opts->hr_seek == 0 && seek.type == MPSEEK_ABSOLUTE) ||
- opts->hr_seek > 0 || seek.exact >= MPSEEK_EXACT;
+ double demux_pts = seek_pts;
+
+ bool hr_seek = opts->correct_pts && seek.exact != MPSEEK_KEYFRAME &&
+ ((opts->hr_seek == 0 && seek.type == MPSEEK_ABSOLUTE) ||
+ opts->hr_seek > 0 || seek.exact >= MPSEEK_EXACT) &&
+ seek_pts != MP_NOPTS_VALUE;
+
if (seek.type == MPSEEK_FACTOR || seek.amount < 0 ||
(seek.type == MPSEEK_ABSOLUTE && seek.amount < mpctx->last_chapter_pts))
mpctx->last_chapter_seek = -2;
- // Prefer doing absolute seeks, unless not possible.
- if ((seek.type == MPSEEK_FACTOR && !mpctx->demuxer->ts_resets_possible &&
- target_time != MP_NOPTS_VALUE) ||
- (seek.type == MPSEEK_RELATIVE && (!mpctx->demuxer->rel_seeks || hr_seek)))
+ // Under certain circumstances, prefer SEEK_FACTOR.
+ if (seek.type == MPSEEK_FACTOR && !hr_seek &&
+ (mpctx->demuxer->ts_resets_possible || seek_pts == MP_NOPTS_VALUE))
{
- seek.type = MPSEEK_ABSOLUTE;
- seek.amount = target_time;
- }
-
- hr_seek &= seek.type == MPSEEK_ABSOLUTE; // otherwise, no target PTS known
-
- double demuxer_amount = seek.amount;
- if (timeline_switch_to_time(mpctx, seek.amount)) {
- reinit_video_chain(mpctx);
- reinit_audio_chain(mpctx);
- reinit_subs(mpctx, 0);
- reinit_subs(mpctx, 1);
+ demux_pts = seek.amount;
+ demux_flags |= SEEK_FACTOR;
}
- int demuxer_style = 0;
- switch (seek.type) {
- case MPSEEK_FACTOR:
- demuxer_style |= SEEK_ABSOLUTE | SEEK_FACTOR;
- break;
- case MPSEEK_ABSOLUTE:
- demuxer_style |= SEEK_ABSOLUTE;
- break;
- }
- if (hr_seek || direction < 0) {
- demuxer_style |= SEEK_BACKWARD;
- } else if (direction > 0) {
- demuxer_style |= SEEK_FORWARD;
+ if (hr_seek) {
+ double hr_seek_offset = opts->hr_seek_demuxer_offset;
+ // Always try to compensate for possibly bad demuxers in "special"
+ // situations where we need more robustness from the hr-seek code, even
+ // if the user doesn't use --hr-seek-demuxer-offset.
+ // The value is arbitrary, but should be "good enough" in most situations.
+ if (hr_seek_very_exact)
+ hr_seek_offset = MPMAX(hr_seek_offset, 0.5); // arbitrary
+ demux_pts -= hr_seek_offset;
+ demux_flags = (demux_flags | SEEK_HR | SEEK_BACKWARD) & ~SEEK_FORWARD;
}
- if (hr_seek)
- demuxer_style |= SEEK_HR;
- if (hr_seek)
- demuxer_amount -= hr_seek_offset;
- demux_seek(mpctx->demuxer, demuxer_amount, demuxer_style);
+ demux_seek(mpctx->demuxer, demux_pts, demux_flags);
// Seek external, extra files too:
for (int t = 0; t < mpctx->num_tracks; t++) {
struct track *track = mpctx->tracks[t];
if (track->selected && track->is_external && track->demuxer) {
- double main_new_pos = seek.amount;
- if (seek.type != MPSEEK_ABSOLUTE)
- main_new_pos = get_main_demux_pts(mpctx);
- demux_seek(track->demuxer, main_new_pos, SEEK_ABSOLUTE | SEEK_BACKWARD);
+ double main_new_pos = demux_pts;
+ if (demux_flags & SEEK_FACTOR)
+ main_new_pos = seek_pts;
+ demux_seek(track->demuxer, main_new_pos, 0);
}
}
- if (!timeline_fallthrough)
- clear_audio_output_buffers(mpctx);
-
+ clear_audio_output_buffers(mpctx);
reset_playback_state(mpctx);
- if (timeline_fallthrough) {
- // Important if video reinit happens.
- mpctx->vo_pts_history_seek_ts = prev_seek_ts;
- mpctx->step_frames = prev_step;
- } else {
- mpctx->vo_pts_history_seek_ts++;
- mpctx->backstep_active = false;
- }
-
/* Use the target time as "current position" for further relative
* seeks etc until a new video frame has been decoded */
- mpctx->last_seek_pts = target_time;
-
- // The hr_seek==false case is for skipping frames with PTS before the
- // current timeline chapter start. It's not really known where the demuxer
- // level seek will end up, so the hrseek mechanism is abused to skip all
- // frames before chapter start by setting hrseek_pts to the chapter start.
- // It does nothing when the seek is inside of the current chapter, and
- // seeking past the chapter is handled elsewhere.
- if (hr_seek || mpctx->timeline) {
+ mpctx->last_seek_pts = seek_pts;
+
+ if (hr_seek) {
mpctx->hrseek_active = true;
- mpctx->hrseek_framedrop = !hr_seek_very_exact;
- mpctx->hrseek_pts = hr_seek ? seek.amount
- : mpctx->timeline[mpctx->timeline_part].start;
+ mpctx->hrseek_framedrop = !hr_seek_very_exact && opts->hr_seek_framedrop;
+ mpctx->hrseek_backstep = seek.type == MPSEEK_BACKSTEP;
+ mpctx->hrseek_pts = seek_pts;
- MP_VERBOSE(mpctx, "hr-seek, skipping to %f%s\n", mpctx->hrseek_pts,
- mpctx->hrseek_framedrop ? "" : " (no framedrop)");
+ MP_VERBOSE(mpctx, "hr-seek, skipping to %f%s%s\n", mpctx->hrseek_pts,
+ mpctx->hrseek_framedrop ? "" : " (no framedrop)",
+ mpctx->hrseek_backstep ? " (backstep)" : "");
}
+ if (mpctx->stop_play == AT_END_OF_FILE)
+ mpctx->stop_play = KEEP_PLAYING;
+
mpctx->start_timestamp = mp_time_sec();
mpctx->sleeptime = 0;
@@ -334,6 +319,7 @@ void queue_seek(struct MPContext *mpctx, enum seek_type type, double amount,
return;
case MPSEEK_ABSOLUTE:
case MPSEEK_FACTOR:
+ case MPSEEK_BACKSTEP:
*seek = (struct seek_params) {
.type = type,
.amount = amount,
@@ -360,7 +346,7 @@ void execute_queued_seek(struct MPContext *mpctx)
if (!mpctx->seek.immediate && mpctx->video_status < STATUS_READY &&
mp_time_sec() - mpctx->start_timestamp < 0.3)
return;
- mp_seek(mpctx, mpctx->seek, false);
+ mp_seek(mpctx, mpctx->seek);
mpctx->seek = (struct seek_params){0};
}
}
@@ -372,9 +358,6 @@ double get_time_length(struct MPContext *mpctx)
if (!demuxer)
return -1;
- if (mpctx->timeline)
- return mpctx->timeline[mpctx->num_timeline_parts].start;
-
double len = demuxer_get_time_length(demuxer);
if (len >= 0)
return len;
@@ -521,7 +504,7 @@ static void handle_osd_redraw(struct MPContext *mpctx)
return;
}
// Don't redraw immediately during a seek (makes it significantly slower).
- if (mpctx->d_video && mp_time_sec() - mpctx->start_timestamp < 0.1) {
+ if (mpctx->vo_chain && mp_time_sec() - mpctx->start_timestamp < 0.1) {
mpctx->sleeptime = MPMIN(mpctx->sleeptime, 0.1);
return;
}
@@ -670,89 +653,6 @@ static void handle_vo_events(struct MPContext *mpctx)
mp_notify(mpctx, MP_EVENT_WIN_STATE, NULL);
}
-void add_frame_pts(struct MPContext *mpctx, double pts)
-{
- if (pts == MP_NOPTS_VALUE || mpctx->hrseek_framedrop) {
- mpctx->vo_pts_history_seek_ts++; // mark discontinuity
- return;
- }
- if (mpctx->vo_pts_history_pts[0] == pts) // may be called multiple times
- return;
- for (int n = MAX_NUM_VO_PTS - 1; n >= 1; n--) {
- mpctx->vo_pts_history_seek[n] = mpctx->vo_pts_history_seek[n - 1];
- mpctx->vo_pts_history_pts[n] = mpctx->vo_pts_history_pts[n - 1];
- }
- mpctx->vo_pts_history_seek[0] = mpctx->vo_pts_history_seek_ts;
- mpctx->vo_pts_history_pts[0] = pts;
-}
-
-static double find_previous_pts(struct MPContext *mpctx, double pts)
-{
- for (int n = 0; n < MAX_NUM_VO_PTS - 1; n++) {
- if (pts == mpctx->vo_pts_history_pts[n] &&
- mpctx->vo_pts_history_seek[n] != 0 &&
- mpctx->vo_pts_history_seek[n] == mpctx->vo_pts_history_seek[n + 1])
- {
- return mpctx->vo_pts_history_pts[n + 1];
- }
- }
- return MP_NOPTS_VALUE;
-}
-
-static double get_last_frame_pts(struct MPContext *mpctx)
-{
- if (mpctx->vo_pts_history_seek[0] == mpctx->vo_pts_history_seek_ts)
- return mpctx->vo_pts_history_pts[0];
- return MP_NOPTS_VALUE;
-}
-
-static void handle_backstep(struct MPContext *mpctx)
-{
- if (!mpctx->backstep_active)
- return;
-
- double current_pts = mpctx->last_vo_pts;
- mpctx->backstep_active = false;
- if (mpctx->d_video && current_pts != MP_NOPTS_VALUE) {
- double seek_pts = find_previous_pts(mpctx, current_pts);
- if (seek_pts != MP_NOPTS_VALUE) {
- queue_seek(mpctx, MPSEEK_ABSOLUTE, seek_pts, MPSEEK_VERY_EXACT, true);
- } else {
- double last = get_last_frame_pts(mpctx);
- if (last != MP_NOPTS_VALUE && last >= current_pts &&
- mpctx->backstep_start_seek_ts != mpctx->vo_pts_history_seek_ts)
- {
- MP_ERR(mpctx, "Backstep failed.\n");
- queue_seek(mpctx, MPSEEK_ABSOLUTE, current_pts,
- MPSEEK_VERY_EXACT, true);
- } else if (!mpctx->hrseek_active) {
- MP_VERBOSE(mpctx, "Start backstep indexing.\n");
- // Force it to index the video up until current_pts.
- // The whole point is getting frames _before_ that PTS,
- // so apply an arbitrary offset. (In theory the offset
- // has to be large enough to reach the previous frame.)
- mp_seek(mpctx, (struct seek_params){
- .type = MPSEEK_ABSOLUTE,
- .amount = current_pts - 1.0,
- }, false);
- // Don't leave hr-seek mode. If all goes right, hr-seek
- // mode is cancelled as soon as the frame before
- // current_pts is found during hr-seeking.
- // Note that current_pts should be part of the index,
- // otherwise we can't find the previous frame, so set the
- // seek target an arbitrary amount of time after it.
- if (mpctx->hrseek_active) {
- mpctx->hrseek_pts = current_pts + 10.0;
- mpctx->hrseek_framedrop = false;
- mpctx->backstep_active = true;
- }
- } else {
- mpctx->backstep_active = true;
- }
- }
- }
-}
-
static void handle_sstep(struct MPContext *mpctx)
{
struct MPOpts *opts = mpctx->opts;
@@ -786,7 +686,7 @@ static void handle_loop_file(struct MPContext *mpctx)
void seek_to_last_frame(struct MPContext *mpctx)
{
- if (!mpctx->d_video)
+ if (!mpctx->vo_chain)
return;
if (mpctx->hrseek_lastframe) // exit if we already tried this
return;
@@ -800,7 +700,7 @@ void seek_to_last_frame(struct MPContext *mpctx)
.type = MPSEEK_ABSOLUTE,
.amount = end,
.exact = MPSEEK_VERY_EXACT,
- }, false);
+ });
// Make it exact: stop seek only if last frame was reached.
if (mpctx->hrseek_active) {
mpctx->hrseek_pts = 1e99; // "infinite"
@@ -816,7 +716,7 @@ static void handle_keep_open(struct MPContext *mpctx)
opts->loop_times == 1)
{
mpctx->stop_play = KEEP_PLAYING;
- if (mpctx->d_video) {
+ if (mpctx->vo_chain) {
if (!vo_has_frame(mpctx->video_out)) // EOF not reached normally
seek_to_last_frame(mpctx);
mpctx->playback_pts = mpctx->last_vo_pts;
@@ -843,16 +743,22 @@ static void handle_chapter_change(struct MPContext *mpctx)
// no way to know if this has already been done or not).
int handle_force_window(struct MPContext *mpctx, bool force)
{
- // Don't interfere with real video playback
- if (mpctx->d_video)
- return 0;
-
// True if we're either in idle mode, or loading of the file has finished.
// It's also set via force in some stages during file loading.
bool act = !mpctx->playing || mpctx->playback_initialized || force;
+ // On the other hand, if a video track is selected, but no video is ever
+ // decoded on it, then create the window.
+ bool stalled_video = mpctx->playback_initialized && mpctx->restart_complete &&
+ mpctx->video_status == STATUS_EOF && mpctx->vo_chain &&
+ !mpctx->video_out->config_ok;
+
+ // Don't interfere with real video playback
+ if (mpctx->vo_chain && !stalled_video)
+ return 0;
+
if (!mpctx->opts->force_vo) {
- if (act)
+ if (act && !mpctx->vo_chain)
uninit_video_out(mpctx);
return 0;
}
@@ -889,7 +795,7 @@ int handle_force_window(struct MPContext *mpctx, bool force)
struct mp_image_params p = {
.imgfmt = config_format,
.w = w, .h = h,
- .d_w = w, .d_h = h,
+ .p_w = 1, .p_h = 1,
};
if (vo_reconfig(vo, &p) < 0)
goto err;
@@ -921,7 +827,7 @@ static void handle_dummy_ticks(struct MPContext *mpctx)
// We always make sure audio and video buffers are filled before actually
// starting playback. This code handles starting them at the same time.
-static void handle_playback_restart(struct MPContext *mpctx, double endpts)
+static void handle_playback_restart(struct MPContext *mpctx)
{
struct MPOpts *opts = mpctx->opts;
@@ -936,7 +842,7 @@ static void handle_playback_restart(struct MPContext *mpctx, double endpts)
}
if (mpctx->audio_status == STATUS_READY)
- fill_audio_out_buffers(mpctx, endpts); // actually play prepared buffer
+ fill_audio_out_buffers(mpctx); // actually play prepared buffer
if (!mpctx->restart_complete) {
mpctx->hrseek_active = false;
@@ -963,9 +869,7 @@ static void handle_playback_restart(struct MPContext *mpctx, double endpts)
}
}
-// Determines whether the end of the current segment is reached, and switch to
-// the next one if required. Also handles regular playback end.
-static void handle_segment_switch(struct MPContext *mpctx, bool end_is_new_segment)
+static void handle_eof(struct MPContext *mpctx)
{
/* Don't quit while paused and we're displaying the last video frame. On the
* other hand, if we don't have a video frame, then the user probably seeked
@@ -976,28 +880,51 @@ static void handle_segment_switch(struct MPContext *mpctx, bool end_is_new_segme
* and video streams to "disabled" at runtime. Handle this by waiting
* rather than immediately stopping playback due to EOF.
*/
- if ((mpctx->d_audio || mpctx->d_video) && !prevent_eof &&
+ if ((mpctx->ao_chain || mpctx->vo_chain) && !prevent_eof &&
mpctx->audio_status == STATUS_EOF &&
- mpctx->video_status == STATUS_EOF)
+ mpctx->video_status == STATUS_EOF &&
+ !mpctx->stop_play)
{
- int new_part = mpctx->timeline_part + 1;
- if (end_is_new_segment && new_part < mpctx->num_timeline_parts) {
- mp_seek(mpctx, (struct seek_params){
- .type = MPSEEK_ABSOLUTE,
- .amount = mpctx->timeline[new_part].start
- }, true);
- } else {
- if (!mpctx->stop_play)
- mpctx->stop_play = AT_END_OF_FILE;
+ mpctx->stop_play = AT_END_OF_FILE;
+ }
+}
+
+static void handle_complex_filter_decoders(struct MPContext *mpctx)
+{
+ if (!mpctx->lavfi)
+ return;
+
+ for (int n = 0; n < mpctx->num_tracks; n++) {
+ struct track *track = mpctx->tracks[n];
+ if (!track->selected)
+ continue;
+ if (!track->sink || !lavfi_needs_input(track->sink))
+ continue;
+ if (track->d_audio) {
+ audio_work(track->d_audio);
+ struct mp_audio *fr;
+ int res = audio_get_frame(track->d_audio, &fr);
+ if (res == DATA_OK) {
+ lavfi_send_frame_a(track->sink, fr);
+ } else {
+ lavfi_send_status(track->sink, res);
+ }
+ }
+ if (track->d_video) {
+ video_work(track->d_video);
+ struct mp_image *fr;
+ int res = video_get_frame(track->d_video, &fr);
+ if (res == DATA_OK) {
+ lavfi_send_frame_v(track->sink, fr);
+ } else {
+ lavfi_send_status(track->sink, res);
+ }
}
}
}
void run_playloop(struct MPContext *mpctx)
{
- double endpts = get_play_end_pts(mpctx);
- bool end_is_new_segment = false;
-
#if HAVE_ENCODING
if (encode_lavc_didfail(mpctx->encode_lavc_ctx)) {
mpctx->stop_play = PT_QUIT;
@@ -1007,23 +934,24 @@ void run_playloop(struct MPContext *mpctx)
update_demuxer_properties(mpctx);
- if (mpctx->timeline) {
- double end = mpctx->timeline[mpctx->timeline_part + 1].start;
- if (endpts == MP_NOPTS_VALUE || end < endpts) {
- end_is_new_segment = true;
- endpts = end;
- }
- }
+ handle_complex_filter_decoders(mpctx);
handle_cursor_autohide(mpctx);
handle_vo_events(mpctx);
handle_heartbeat_cmd(mpctx);
handle_command_updates(mpctx);
- fill_audio_out_buffers(mpctx, endpts);
- write_video(mpctx, endpts);
+ fill_audio_out_buffers(mpctx);
+ write_video(mpctx);
- handle_playback_restart(mpctx, endpts);
+ if (mpctx->lavfi) {
+ if (lavfi_process(mpctx->lavfi))
+ mpctx->sleeptime = 0;
+ if (lavfi_has_failed(mpctx->lavfi))
+ mpctx->stop_play = AT_END_OF_FILE;
+ }
+
+ handle_playback_restart(mpctx);
// Use the audio timestamp if no video, or video is enabled, but has ended.
if (mpctx->video_status == STATUS_EOF &&
@@ -1036,9 +964,10 @@ void run_playloop(struct MPContext *mpctx)
handle_dummy_ticks(mpctx);
update_osd_msg(mpctx);
- update_subtitles(mpctx);
+ if (!mpctx->video_out)
+ update_subtitles(mpctx, mpctx->playback_pts);
- handle_segment_switch(mpctx, end_is_new_segment);
+ handle_eof(mpctx);
handle_loop_file(mpctx);
@@ -1060,8 +989,6 @@ void run_playloop(struct MPContext *mpctx)
mp_process_input(mpctx);
- handle_backstep(mpctx);
-
handle_chapter_change(mpctx);
handle_force_window(mpctx, false);
diff --git a/player/screenshot.c b/player/screenshot.c
index 9c4f5cc..02cbb4a 100644
--- a/player/screenshot.c
+++ b/player/screenshot.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>
@@ -23,7 +23,7 @@
#include "osdep/io.h"
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "screenshot.h"
#include "core.h"
#include "command.h"
@@ -308,14 +308,7 @@ static char *gen_fname(screenshot_ctx *ctx, const char *file_ext)
static void add_subs(struct MPContext *mpctx, struct mp_image *image)
{
- double sar = (double)image->w / image->h;
- double dar = (double)image->params.d_w / image->params.d_h;
- struct mp_osd_res res = {
- .w = image->w,
- .h = image->h,
- .display_par = sar / dar,
- };
-
+ struct mp_osd_res res = osd_res_from_image_params(&image->params);
osd_draw_on_image(mpctx->osd, res, mpctx->video_pts,
OSD_DRAW_SUB_ONLY, image);
}
@@ -352,8 +345,8 @@ static struct mp_image *screenshot_get(struct MPContext *mpctx, int mode)
}
}
- if (image && mpctx->d_video && mpctx->d_video->hwdec_info) {
- struct mp_hwdec_ctx *ctx = mpctx->d_video->hwdec_info->hwctx;
+ if (image && mpctx->vo_chain && mpctx->vo_chain->hwdec_info) {
+ struct mp_hwdec_ctx *ctx = mpctx->vo_chain->hwdec_info->hwctx;
struct mp_image *nimage = NULL;
if (ctx && ctx->download_image && (image->fmt.flags & MP_IMGFLAG_HWACCEL))
nimage = ctx->download_image(ctx, image, NULL);
diff --git a/player/screenshot.h b/player/screenshot.h
index 9ebe9ef..aa5dfac 100644
--- a/player/screenshot.h
+++ b/player/screenshot.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_SCREENSHOT_H
diff --git a/player/scripting.c b/player/scripting.c
index 3ae9719..cb35b1b 100644
--- a/player/scripting.c
+++ b/player/scripting.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 <string.h>
diff --git a/player/sub.c b/player/sub.c
index c2a3e80..6892ac9 100644
--- a/player/sub.c
+++ b/player/sub.c
@@ -22,7 +22,7 @@
#include <assert.h>
#include "config.h"
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "common/msg.h"
#include "options/options.h"
@@ -30,7 +30,6 @@
#include "common/global.h"
#include "stream/stream.h"
-#include "sub/ass_mp.h"
#include "sub/dec_sub.h"
#include "demux/demux.h"
#include "video/mp_image.h"
@@ -39,265 +38,136 @@
#include "core.h"
-#if HAVE_LIBASS
-
-static const char *const font_mimetypes[] = {
- "application/x-truetype-font",
- "application/vnd.ms-opentype",
- "application/x-font-ttf",
- "application/x-font", // probably incorrect
- NULL
-};
-
-static const char *const font_exts[] = {".ttf", ".ttc", ".otf", NULL};
-
-static bool attachment_is_font(struct mp_log *log, struct demux_attachment *att)
+// 0: primary sub, 1: secondary sub, -1: not selected
+static int get_order(struct MPContext *mpctx, struct track *track)
{
- if (!att->name || !att->type || !att->data || !att->data_size)
- return false;
- for (int n = 0; font_mimetypes[n]; n++) {
- if (strcmp(font_mimetypes[n], att->type) == 0)
- return true;
+ for (int n = 0; n < NUM_PTRACKS; n++) {
+ if (mpctx->current_track[n][STREAM_SUB] == track)
+ return n;
}
- // fallback: match against file extension
- char *ext = strlen(att->name) > 4 ? att->name + strlen(att->name) - 4 : "";
- for (int n = 0; font_exts[n]; n++) {
- if (strcasecmp(ext, font_exts[n]) == 0) {
- mp_warn(log, "Loading font attachment '%s' with MIME type %s. "
- "Assuming this is a broken Matroska file, which was "
- "muxed without setting a correct font MIME type.\n",
- att->name, att->type);
- return true;
- }
- }
- return false;
-}
-
-static void add_subtitle_fonts_from_sources(struct MPContext *mpctx)
-{
- if (mpctx->opts->ass_enabled) {
- for (int j = 0; j < mpctx->num_sources; j++) {
- struct demuxer *d = mpctx->sources[j];
- for (int i = 0; i < d->num_attachments; i++) {
- struct demux_attachment *att = d->attachments + i;
- if (mpctx->opts->use_embedded_fonts &&
- attachment_is_font(mpctx->log, att))
- {
- ass_add_font(mpctx->ass_library, att->name, att->data,
- att->data_size);
- }
- }
- }
- }
-}
-
-static void init_sub_renderer(struct MPContext *mpctx)
-{
- struct MPOpts *opts = mpctx->opts;
-
- if (mpctx->ass_renderer)
- return;
-
- if (!mpctx->ass_log)
- mpctx->ass_log = mp_log_new(mpctx, mpctx->global->log, "!libass");
-
- mpctx->ass_library = mp_ass_init(mpctx->global, mpctx->ass_log);
-
- add_subtitle_fonts_from_sources(mpctx);
-
- if (opts->ass_style_override)
- ass_set_style_overrides(mpctx->ass_library, opts->ass_force_style_list);
-
- mpctx->ass_renderer = ass_renderer_init(mpctx->ass_library);
-
- mp_ass_configure_fonts(mpctx->ass_renderer, opts->sub_text_style,
- mpctx->global, mpctx->ass_log);
-}
-
-void uninit_sub_renderer(struct MPContext *mpctx)
-{
- if (mpctx->ass_renderer)
- ass_renderer_done(mpctx->ass_renderer);
- mpctx->ass_renderer = NULL;
- if (mpctx->ass_library)
- ass_library_done(mpctx->ass_library);
- mpctx->ass_library = NULL;
+ return -1;
}
-#else /* HAVE_LIBASS */
-
-static void init_sub_renderer(struct MPContext *mpctx) {}
-void uninit_sub_renderer(struct MPContext *mpctx) {}
-
-#endif
-
-static void reset_subtitles(struct MPContext *mpctx, int order)
+static void reset_subtitles(struct MPContext *mpctx, struct track *track)
{
- if (mpctx->d_sub[order])
- sub_reset(mpctx->d_sub[order]);
+ if (track->d_sub)
+ sub_reset(track->d_sub);
term_osd_set_subs(mpctx, NULL);
}
void reset_subtitle_state(struct MPContext *mpctx)
{
- reset_subtitles(mpctx, 0);
- reset_subtitles(mpctx, 1);
-}
-
-void uninit_stream_sub_decoders(struct demuxer *demuxer)
-{
- for (int i = 0; i < demuxer->num_streams; i++) {
- struct sh_stream *sh = demuxer->streams[i];
- if (sh->sub) {
- sub_destroy(sh->sub->dec_sub);
- sh->sub->dec_sub = NULL;
- }
+ for (int n = 0; n < mpctx->num_tracks; n++) {
+ struct dec_sub *d_sub = mpctx->tracks[n]->d_sub;
+ if (d_sub)
+ sub_reset(d_sub);
}
+ term_osd_set_subs(mpctx, NULL);
}
-void uninit_sub(struct MPContext *mpctx, int order)
+void uninit_sub(struct MPContext *mpctx, struct track *track)
{
- if (mpctx->d_sub[order]) {
- reset_subtitles(mpctx, order);
- mpctx->d_sub[order] = NULL; // Note: not free'd.
- osd_set_sub(mpctx->osd, OSDTYPE_SUB + order, NULL);
- reselect_demux_streams(mpctx);
+ if (track && track->d_sub) {
+ reset_subtitles(mpctx, track);
+ sub_select(track->d_sub, false);
+ int order = get_order(mpctx, track);
+ if (order >= 0 && order <= 1)
+ osd_set_sub(mpctx->osd, OSDTYPE_SUB + order, NULL);
}
}
void uninit_sub_all(struct MPContext *mpctx)
{
- uninit_sub(mpctx, 0);
- uninit_sub(mpctx, 1);
+ for (int n = 0; n < mpctx->num_tracks; n++)
+ uninit_sub(mpctx, mpctx->tracks[n]);
}
-// When reading subtitles from a demuxer, and we read video or audio from the
-// demuxer, we should not explicitly read subtitle packets. (With external
-// subs, we have to.)
-static bool is_interleaved(struct MPContext *mpctx, struct track *track)
-{
- if (track->is_external || !track->demuxer)
- return false;
-
- struct demuxer *demuxer = track->demuxer;
- for (int t = 0; t < mpctx->num_tracks; t++) {
- struct track *other = mpctx->tracks[t];
- if (other != track && other->selected && other->demuxer == demuxer &&
- (other->type == STREAM_VIDEO || other->type == STREAM_AUDIO))
- return true;
- }
- return track->demuxer == mpctx->demuxer;
-}
-
-static void update_subtitle(struct MPContext *mpctx, int order)
+static bool update_subtitle(struct MPContext *mpctx, double video_pts,
+ struct track *track)
{
struct MPOpts *opts = mpctx->opts;
- struct track *track = mpctx->current_track[order][STREAM_SUB];
- struct dec_sub *dec_sub = mpctx->d_sub[order];
+ struct dec_sub *dec_sub = track ? track->d_sub : NULL;
- if (!track || !dec_sub)
- return;
+ if (!dec_sub || video_pts == MP_NOPTS_VALUE)
+ return true;
- if (mpctx->d_video) {
- struct mp_image_params params = mpctx->d_video->vfilter->override_params;
+ if (mpctx->vo_chain) {
+ struct mp_image_params params = mpctx->vo_chain->vf->input_params;
if (params.imgfmt)
sub_control(dec_sub, SD_CTRL_SET_VIDEO_PARAMS, &params);
}
- double refpts_s = mpctx->playback_pts;
- double curpts_s = refpts_s - opts->sub_delay;
-
- if (!track->preloaded && track->stream) {
- struct sh_stream *sh_stream = track->stream;
- bool interleaved = is_interleaved(mpctx, track);
+ video_pts -= opts->sub_delay;
- assert(sh_stream->sub->dec_sub == dec_sub);
+ if (!track->preloaded && track->demuxer->fully_read && !opts->sub_clear_on_seek)
+ {
+ // Assume fully_read implies no interleaved audio/video streams.
+ // (Reading packets will change the demuxer position.)
+ demux_seek(track->demuxer, 0, 0);
+ track->preloaded = sub_read_all_packets(track->d_sub);
+ }
- while (1) {
- if (interleaved && !demux_has_packet(sh_stream))
- break;
- double subpts_s = demux_get_next_pts(sh_stream);
- if (!demux_has_packet(sh_stream))
- break;
- if (subpts_s > curpts_s) {
- MP_DBG(mpctx, "Sub early: c_pts=%5.3f s_pts=%5.3f\n",
- curpts_s, subpts_s);
- // Often subs can be handled in advance
- if (!sub_accepts_packet_in_advance(dec_sub))
- break;
- // Try to avoid demuxing whole file at once
- if (subpts_s > curpts_s + 1 && !interleaved)
- break;
- }
- struct demux_packet *pkt = demux_read_packet(sh_stream);
- MP_DBG(mpctx, "Sub: c_pts=%5.3f s_pts=%5.3f duration=%5.3f len=%d\n",
- curpts_s, pkt->pts, pkt->duration, pkt->len);
- sub_decode(dec_sub, pkt);
- talloc_free(pkt);
- }
+ if (!track->preloaded) {
+ if (!sub_read_packets(dec_sub, video_pts))
+ return false;
}
// Handle displaying subtitles on terminal; never done for secondary subs
- if (order == 0 && !mpctx->video_out)
- term_osd_set_subs(mpctx, sub_get_text(dec_sub, curpts_s));
+ if (mpctx->current_track[0][STREAM_SUB] == track && !mpctx->video_out)
+ term_osd_set_subs(mpctx, sub_get_text(dec_sub, video_pts));
+
+ return true;
}
-void update_subtitles(struct MPContext *mpctx)
+// Return true if the subtitles for the given PTS are ready; false if the player
+// should wait for new demuxer data, and then should retry.
+bool update_subtitles(struct MPContext *mpctx, double video_pts)
{
- update_subtitle(mpctx, 0);
- update_subtitle(mpctx, 1);
+ bool ok = true;
+ for (int n = 0; n < NUM_PTRACKS; n++)
+ ok &= update_subtitle(mpctx, video_pts, mpctx->current_track[n][STREAM_SUB]);
+ return ok;
}
-static void reinit_subdec(struct MPContext *mpctx, struct track *track,
- struct dec_sub *dec_sub)
+static bool init_subdec(struct MPContext *mpctx, struct track *track)
{
- struct MPOpts *opts = mpctx->opts;
+ assert(!track->d_sub);
- if (sub_is_initialized(dec_sub))
- return;
-
- struct sh_video *sh_video =
- mpctx->d_video ? mpctx->d_video->header->video : NULL;
- int w = sh_video ? sh_video->disp_w : 0;
- int h = sh_video ? sh_video->disp_h : 0;
- float fps = sh_video ? sh_video->fps : 25;
+ if (!track->demuxer || !track->stream)
+ return false;
- init_sub_renderer(mpctx);
+ track->d_sub = sub_create(mpctx->global, track->demuxer, track->stream);
+ if (!track->d_sub)
+ return false;
- sub_set_video_res(dec_sub, w, h);
- sub_set_video_fps(dec_sub, fps);
- sub_set_ass_renderer(dec_sub, mpctx->ass_library, mpctx->ass_renderer,
- &mpctx->ass_lock);
- sub_init_from_sh(dec_sub, track->stream);
+ struct track *vtrack = mpctx->current_track[0][STREAM_VIDEO];
+ struct mp_codec_params *v_c =
+ vtrack && vtrack->stream ? vtrack->stream->codec : NULL;
+ double fps = v_c ? v_c->fps : 25;
+ sub_control(track->d_sub, SD_CTRL_SET_VIDEO_DEF_FPS, &fps);
- // Don't do this if the file has video/audio streams. Don't do it even
- // if it has only sub streams, because reading packets will change the
- // demuxer position.
- if (!track->preloaded && track->is_external && !opts->sub_clear_on_seek) {
- demux_seek(track->demuxer, 0, SEEK_ABSOLUTE);
- track->preloaded = sub_read_all_packets(dec_sub, track->stream);
- if (track->preloaded)
- demux_stop_thread(track->demuxer);
- }
+ return true;
}
-void reinit_subs(struct MPContext *mpctx, int order)
+void reinit_sub(struct MPContext *mpctx, struct track *track)
{
- struct track *track = mpctx->current_track[order][STREAM_SUB];
-
- assert(!mpctx->d_sub[order]);
+ if (!track || !track->stream || track->stream->type != STREAM_SUB)
+ return;
- struct sh_stream *sh = track ? track->stream : NULL;
- if (!sh)
+ if (!track->d_sub && !init_subdec(mpctx, track)) {
+ error_on_track(mpctx, track);
return;
+ }
- // The decoder is cached in the stream header in order to make ordered
- // chapters work better.
- if (!sh->sub->dec_sub)
- sh->sub->dec_sub = sub_create(mpctx->global);
- mpctx->d_sub[order] = sh->sub->dec_sub;
+ sub_select(track->d_sub, true);
+ int order = get_order(mpctx, track);
+ if (order >= 0 && order <= 1)
+ osd_set_sub(mpctx->osd, OSDTYPE_SUB + order, track->d_sub);
+ sub_control(track->d_sub, SD_CTRL_SET_TOP, &(bool){!!order});
+}
- reinit_subdec(mpctx, track, sh->sub->dec_sub);
- osd_set_sub(mpctx->osd, OSDTYPE_SUB + order, sh->sub->dec_sub);
- sub_control(sh->sub->dec_sub, SD_CTRL_SET_TOP, &(bool){!!order});
+void reinit_sub_all(struct MPContext *mpctx)
+{
+ for (int n = 0; n < NUM_PTRACKS; n++)
+ reinit_sub(mpctx, mpctx->current_track[n][STREAM_SUB]);
}
diff --git a/player/video.c b/player/video.c
index 9be4f6a..24e01d3 100644
--- a/player/video.c
+++ b/player/video.c
@@ -22,7 +22,7 @@
#include <assert.h>
#include "config.h"
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "common/msg.h"
#include "options/options.h"
@@ -66,74 +66,114 @@ static const char av_desync_help_text[] =
"position will not match to the video (see A-V status field).\n"
"\n";
-static bool decode_coverart(struct dec_video *d_video);
+int video_set_colors(struct vo_chain *vo_c, const char *item, int value)
+{
+ vf_equalizer_t data;
+
+ data.item = item;
+ data.value = value;
-static void set_allowed_vo_formats(struct vf_chain *c, struct vo *vo)
+ MP_VERBOSE(vo_c, "set video colors %s=%d \n", item, value);
+ if (video_vf_vo_control(vo_c, VFCTRL_SET_EQUALIZER, &data) == CONTROL_TRUE)
+ return 1;
+ MP_VERBOSE(vo_c, "Video attribute '%s' is not supported by selected vo.\n",
+ item);
+ return 0;
+}
+
+int video_get_colors(struct vo_chain *vo_c, const char *item, int *value)
{
- vo_query_formats(vo, c->allowed_output_formats);
+ vf_equalizer_t data;
+
+ data.item = item;
+
+ MP_VERBOSE(vo_c, "get video colors %s \n", item);
+ if (video_vf_vo_control(vo_c, VFCTRL_GET_EQUALIZER, &data) == CONTROL_TRUE) {
+ *value = data.value;
+ return 1;
+ }
+ return 0;
}
-static int try_filter(struct MPContext *mpctx, struct mp_image_params params,
- char *name, char *label, char **args)
+// Send a VCTRL, or if it doesn't work, translate it to a VOCTRL and try the VO.
+int video_vf_vo_control(struct vo_chain *vo_c, int vf_cmd, void *data)
{
- struct dec_video *d_video = mpctx->d_video;
+ if (vo_c->vf->initialized > 0) {
+ int r = vf_control_any(vo_c->vf, vf_cmd, data);
+ if (r != CONTROL_UNKNOWN)
+ return r;
+ }
- struct vf_instance *vf = vf_append_filter(d_video->vfilter, name, args);
+ switch (vf_cmd) {
+ case VFCTRL_GET_DEINTERLACE:
+ return vo_control(vo_c->vo, VOCTRL_GET_DEINTERLACE, data) == VO_TRUE;
+ case VFCTRL_SET_DEINTERLACE:
+ return vo_control(vo_c->vo, VOCTRL_SET_DEINTERLACE, data) == VO_TRUE;
+ case VFCTRL_SET_EQUALIZER: {
+ vf_equalizer_t *eq = data;
+ if (!vo_c->vo->config_ok)
+ return CONTROL_FALSE; // vo not configured?
+ struct voctrl_set_equalizer_args param = {
+ eq->item, eq->value
+ };
+ return vo_control(vo_c->vo, VOCTRL_SET_EQUALIZER, &param) == VO_TRUE;
+ }
+ case VFCTRL_GET_EQUALIZER: {
+ vf_equalizer_t *eq = data;
+ if (!vo_c->vo->config_ok)
+ return CONTROL_FALSE; // vo not configured?
+ struct voctrl_get_equalizer_args param = {
+ eq->item, &eq->value
+ };
+ return vo_control(vo_c->vo, VOCTRL_GET_EQUALIZER, &param) == VO_TRUE;
+ }
+ }
+ return CONTROL_UNKNOWN;
+}
+
+static void set_allowed_vo_formats(struct vo_chain *vo_c)
+{
+ vo_query_formats(vo_c->vo, vo_c->vf->allowed_output_formats);
+}
+
+static int try_filter(struct vo_chain *vo_c, struct mp_image_params params,
+ char *name, char *label, char **args)
+{
+ struct vf_instance *vf = vf_append_filter(vo_c->vf, name, args);
if (!vf)
return -1;
vf->label = talloc_strdup(vf, label);
- if (video_reconfig_filters(d_video, &params) < 0) {
- vf_remove_filter(d_video->vfilter, vf);
+ if (vf_reconfig(vo_c->vf, &params) < 0) {
+ vf_remove_filter(vo_c->vf, vf);
// restore
- video_reconfig_filters(d_video, &params);
+ vf_reconfig(vo_c->vf, &params);
return -1;
}
return 0;
}
-// Reconfigure the filter chain according to decoder output.
-// probe_only: don't force fallback to software when doing hw decoding, and
-// the filter chain couldn't be configured
-static void filter_reconfig(struct MPContext *mpctx,
- bool probe_only)
+// Reconfigure the filter chain according to the new input format.
+static void filter_reconfig(struct vo_chain *vo_c)
{
- struct dec_video *d_video = mpctx->d_video;
-
- struct mp_image_params params = d_video->decoder_output;
-
- mp_notify(mpctx, MPV_EVENT_VIDEO_RECONFIG, NULL);
-
- set_allowed_vo_formats(d_video->vfilter, mpctx->video_out);
-
- if (video_reconfig_filters(d_video, &params) < 0) {
- // Most video filters don't work with hardware decoding, so this
- // might be the reason why filter reconfig failed.
- if (!probe_only &&
- video_vd_control(d_video, VDCTRL_FORCE_HWDEC_FALLBACK, NULL) == CONTROL_OK)
- {
- // Fallback active; decoder will return software format next
- // time. Don't abort video decoding.
- d_video->vfilter->initialized = 0;
- mp_image_unrefp(&d_video->waiting_decoded_mpi);
- d_video->decoder_output = (struct mp_image_params){0};
- MP_VERBOSE(mpctx, "hwdec falback due to filters.\n");
- }
+ struct mp_image_params params = vo_c->input_format;
+ if (!params.imgfmt)
return;
- }
- if (d_video->vfilter->initialized < 1)
+ set_allowed_vo_formats(vo_c);
+
+ if (vf_reconfig(vo_c->vf, &params) < 0)
return;
if (params.rotate && (params.rotate % 90 == 0)) {
- if (!(mpctx->video_out->driver->caps & VO_CAP_ROTATE90)) {
+ if (!(vo_c->vo->driver->caps & VO_CAP_ROTATE90)) {
// Try to insert a rotation filter.
char *args[] = {"angle", "auto", NULL};
- if (try_filter(mpctx, params, "rotate", "autorotate", args) >= 0) {
+ if (try_filter(vo_c, params, "rotate", "autorotate", args) >= 0) {
params.rotate = 0;
} else {
- MP_ERR(mpctx, "Can't insert rotation filter.\n");
+ MP_ERR(vo_c, "Can't insert rotation filter.\n");
}
}
}
@@ -144,8 +184,8 @@ static void filter_reconfig(struct MPContext *mpctx,
char *to = (char *)MP_STEREO3D_NAME(params.stereo_out);
if (to) {
char *args[] = {"in", "auto", "out", to, NULL, NULL};
- if (try_filter(mpctx, params, "stereo3d", "stereo3d", args) < 0)
- MP_ERR(mpctx, "Can't insert 3D conversion filter.\n");
+ if (try_filter(vo_c, params, "stereo3d", "stereo3d", args) < 0)
+ MP_ERR(vo_c, "Can't insert 3D conversion filter.\n");
}
}
}
@@ -153,49 +193,61 @@ static void filter_reconfig(struct MPContext *mpctx,
static void recreate_video_filters(struct MPContext *mpctx)
{
struct MPOpts *opts = mpctx->opts;
- struct dec_video *d_video = mpctx->d_video;
- assert(d_video);
+ struct vo_chain *vo_c = mpctx->vo_chain;
+ assert(vo_c);
- vf_destroy(d_video->vfilter);
- d_video->vfilter = vf_new(mpctx->global);
- d_video->vfilter->hwdec = d_video->hwdec_info;
- d_video->vfilter->wakeup_callback = wakeup_playloop;
- d_video->vfilter->wakeup_callback_ctx = mpctx;
- d_video->vfilter->container_fps = d_video->fps;
- vo_control(mpctx->video_out, VOCTRL_GET_DISPLAY_FPS,
- &d_video->vfilter->display_fps);
+ vf_destroy(vo_c->vf);
+ vo_c->vf = vf_new(mpctx->global);
+ vo_c->vf->hwdec = vo_c->hwdec_info;
+ vo_c->vf->wakeup_callback = wakeup_playloop;
+ vo_c->vf->wakeup_callback_ctx = mpctx;
+ vo_c->vf->container_fps = vo_c->container_fps;
+ vo_control(vo_c->vo, VOCTRL_GET_DISPLAY_FPS, &vo_c->vf->display_fps);
- vf_append_filter_list(d_video->vfilter, opts->vf_settings);
+ vf_append_filter_list(vo_c->vf, opts->vf_settings);
// for vf_sub
osd_set_render_subs_in_filter(mpctx->osd,
- vf_control_any(d_video->vfilter, VFCTRL_INIT_OSD, mpctx->osd) > 0);
+ vf_control_any(vo_c->vf, VFCTRL_INIT_OSD, mpctx->osd) > 0);
- set_allowed_vo_formats(d_video->vfilter, mpctx->video_out);
+ set_allowed_vo_formats(vo_c);
}
int reinit_video_filters(struct MPContext *mpctx)
{
- struct dec_video *d_video = mpctx->d_video;
+ struct vo_chain *vo_c = mpctx->vo_chain;
- if (!d_video)
+ if (!vo_c)
return 0;
- bool need_reconfig = d_video->vfilter->initialized != 0;
+ bool need_reconfig = vo_c->vf->initialized != 0;
recreate_video_filters(mpctx);
if (need_reconfig)
- filter_reconfig(mpctx, true);
+ filter_reconfig(vo_c);
+
+ mp_force_video_refresh(mpctx);
- return d_video->vfilter->initialized;
+ mp_notify(mpctx, MPV_EVENT_VIDEO_RECONFIG, NULL);
+
+ return vo_c->vf->initialized;
+}
+
+static void vo_chain_reset_state(struct vo_chain *vo_c)
+{
+ mp_image_unrefp(&vo_c->input_mpi);
+ if (vo_c->vf->initialized == 1)
+ vf_seek_reset(vo_c->vf);
+ vo_seek_reset(vo_c->vo);
+
+ if (vo_c->video_src)
+ video_reset(vo_c->video_src);
}
void reset_video_state(struct MPContext *mpctx)
{
- if (mpctx->d_video)
- video_reset_decoding(mpctx->d_video);
- if (mpctx->video_out)
- vo_seek_reset(mpctx->video_out);
+ if (mpctx->vo_chain)
+ vo_chain_reset_state(mpctx->vo_chain);
for (int n = 0; n < mpctx->num_next_frames; n++)
mp_image_unrefp(&mpctx->next_frames[n]);
@@ -205,18 +257,16 @@ void reset_video_state(struct MPContext *mpctx)
mpctx->delay = 0;
mpctx->time_frame = 0;
mpctx->video_pts = MP_NOPTS_VALUE;
- mpctx->video_next_pts = MP_NOPTS_VALUE;
mpctx->num_past_frames = 0;
mpctx->total_avsync_change = 0;
mpctx->last_av_difference = 0;
- mpctx->dropped_frames_total = 0;
- mpctx->dropped_frames = 0;
+ mpctx->dropped_frames_start = 0;
mpctx->mistimed_frames_total = 0;
mpctx->drop_message_shown = 0;
mpctx->display_sync_drift_dir = 0;
mpctx->display_sync_broken = false;
- mpctx->video_status = mpctx->d_video ? STATUS_SYNCING : STATUS_EOF;
+ mpctx->video_status = mpctx->vo_chain ? STATUS_SYNCING : STATUS_EOF;
}
void uninit_video_out(struct MPContext *mpctx)
@@ -229,28 +279,99 @@ void uninit_video_out(struct MPContext *mpctx)
mpctx->video_out = NULL;
}
+static void vo_chain_uninit(struct vo_chain *vo_c)
+{
+ struct track *track = vo_c->track;
+ if (track) {
+ assert(track->vo_c == vo_c);
+ track->vo_c = NULL;
+ assert(track->d_video == vo_c->video_src);
+ track->d_video = NULL;
+ video_uninit(vo_c->video_src);
+ }
+
+ if (vo_c->filter_src)
+ lavfi_set_connected(vo_c->filter_src, false);
+
+ mp_image_unrefp(&vo_c->input_mpi);
+ vf_destroy(vo_c->vf);
+ talloc_free(vo_c);
+ // this does not free the VO
+}
+
void uninit_video_chain(struct MPContext *mpctx)
{
- if (mpctx->d_video) {
+ if (mpctx->vo_chain) {
reset_video_state(mpctx);
- video_uninit(mpctx->d_video);
- mpctx->d_video = NULL;
+ vo_chain_uninit(mpctx->vo_chain);
+ mpctx->vo_chain = NULL;
+
mpctx->video_status = STATUS_EOF;
- mpctx->sync_audio_to_video = false;
- reselect_demux_streams(mpctx);
+
remove_deint_filter(mpctx);
mp_notify(mpctx, MPV_EVENT_VIDEO_RECONFIG, NULL);
}
}
+int init_video_decoder(struct MPContext *mpctx, struct track *track)
+{
+ assert(!track->d_video);
+ if (!track->stream)
+ goto err_out;
+
+ track->d_video = talloc_zero(NULL, struct dec_video);
+ struct dec_video *d_video = track->d_video;
+ d_video->global = mpctx->global;
+ d_video->log = mp_log_new(d_video, mpctx->log, "!vd");
+ d_video->opts = mpctx->opts;
+ d_video->header = track->stream;
+ d_video->codec = track->stream->codec;
+ d_video->fps = d_video->header->codec->fps;
+ if (mpctx->vo_chain)
+ d_video->hwdec_info = mpctx->vo_chain->hwdec_info;
+
+ MP_VERBOSE(d_video, "Container reported FPS: %f\n", d_video->fps);
+
+ if (d_video->opts->force_fps) {
+ d_video->fps = d_video->opts->force_fps;
+ MP_INFO(mpctx, "FPS forced to %5.3f.\n", d_video->fps);
+ MP_INFO(mpctx, "Use --no-correct-pts to force FPS based timing.\n");
+ }
+
+ if (!video_init_best_codec(d_video))
+ goto err_out;
+
+ return 1;
+
+err_out:
+ if (track->sink)
+ lavfi_set_connected(track->sink, false);
+ track->sink = NULL;
+ video_uninit(track->d_video);
+ track->d_video = NULL;
+ error_on_track(mpctx, track);
+ return 0;
+}
+
int reinit_video_chain(struct MPContext *mpctx)
{
+ return reinit_video_chain_src(mpctx, NULL);
+}
+
+int reinit_video_chain_src(struct MPContext *mpctx, struct lavfi_pad *src)
+{
struct MPOpts *opts = mpctx->opts;
- assert(!mpctx->d_video);
- struct track *track = mpctx->current_track[0][STREAM_VIDEO];
- struct sh_stream *sh = track ? track->stream : NULL;
- if (!sh)
- goto no_video;
+ struct track *track = NULL;
+ struct sh_stream *sh = NULL;
+ if (!src) {
+ track = mpctx->current_track[0][STREAM_VIDEO];
+ if (!track)
+ return 0;
+ sh = track->stream;
+ if (!sh)
+ goto no_video;
+ }
+ assert(!mpctx->vo_chain);
if (!mpctx->video_out) {
struct vo_extra ex = {
@@ -271,49 +392,44 @@ int reinit_video_chain(struct MPContext *mpctx)
update_window_title(mpctx, true);
- struct dec_video *d_video = talloc_zero(NULL, struct dec_video);
- mpctx->d_video = d_video;
- d_video->global = mpctx->global;
- d_video->log = mp_log_new(d_video, mpctx->log, "!vd");
- d_video->opts = mpctx->opts;
- d_video->header = sh;
- d_video->fps = sh->video->fps;
- d_video->vo = mpctx->video_out;
+ struct vo_chain *vo_c = talloc_zero(NULL, struct vo_chain);
+ mpctx->vo_chain = vo_c;
+ vo_c->log = mpctx->log;
+ vo_c->vo = mpctx->video_out;
+ vo_c->vf = vf_new(mpctx->global);
- MP_VERBOSE(d_video, "Container reported FPS: %f\n", sh->video->fps);
+ vo_control(vo_c->vo, VOCTRL_GET_HWDEC_INFO, &vo_c->hwdec_info);
- if (opts->force_fps) {
- d_video->fps = opts->force_fps;
- MP_INFO(mpctx, "FPS forced to %5.3f.\n", d_video->fps);
- MP_INFO(mpctx, "Use --no-correct-pts to force FPS based timing.\n");
+ vo_c->filter_src = src;
+ if (!vo_c->filter_src) {
+ vo_c->track = track;
+ track->vo_c = vo_c;
+ if (!init_video_decoder(mpctx, track))
+ goto err_out;
+
+ vo_c->video_src = track->d_video;
+ vo_c->container_fps = vo_c->video_src->fps;
+ vo_c->is_coverart = !!sh->attached_picture;
+
+ track->vo_c = vo_c;
+ vo_c->track = track;
}
#if HAVE_ENCODING
if (mpctx->encode_lavc_ctx)
- encode_lavc_set_video_fps(mpctx->encode_lavc_ctx, d_video->fps);
+ encode_lavc_set_video_fps(mpctx->encode_lavc_ctx, vo_c->container_fps);
#endif
- vo_control(mpctx->video_out, VOCTRL_GET_HWDEC_INFO, &d_video->hwdec_info);
-
recreate_video_filters(mpctx);
- if (!video_init_best_codec(d_video, opts->video_decoders))
- goto err_out;
-
- if (d_video->header->attached_picture && !decode_coverart(d_video))
- goto err_out;
-
bool saver_state = opts->pause || !opts->stop_screensaver;
- vo_control(mpctx->video_out, saver_state ? VOCTRL_RESTORE_SCREENSAVER
- : VOCTRL_KILL_SCREENSAVER, NULL);
-
- vo_set_paused(mpctx->video_out, mpctx->paused);
+ vo_control(vo_c->vo, saver_state ? VOCTRL_RESTORE_SCREENSAVER
+ : VOCTRL_KILL_SCREENSAVER, NULL);
- mpctx->sync_audio_to_video = !sh->attached_picture;
- mpctx->vo_pts_history_seek_ts++;
+ vo_set_paused(vo_c->vo, mpctx->paused);
// If we switch on video again, ensure audio position matches up.
- if (mpctx->d_audio)
+ if (mpctx->ao_chain)
mpctx->audio_status = STATUS_SYNCING;
reset_video_state(mpctx);
@@ -324,8 +440,7 @@ int reinit_video_chain(struct MPContext *mpctx)
err_out:
no_video:
uninit_video_chain(mpctx);
- if (track)
- error_on_track(mpctx, track);
+ error_on_track(mpctx, track);
handle_force_window(mpctx, true);
return 0;
}
@@ -335,9 +450,9 @@ no_video:
void mp_force_video_refresh(struct MPContext *mpctx)
{
struct MPOpts *opts = mpctx->opts;
- struct dec_video *d_video = mpctx->d_video;
+ struct vo_chain *vo_c = mpctx->vo_chain;
- if (!d_video || !d_video->decoder_output.imgfmt)
+ if (!vo_c || !vo_c->input_format.imgfmt)
return;
// If not paused, the next frame should come soon enough.
@@ -349,74 +464,58 @@ void mp_force_video_refresh(struct MPContext *mpctx)
}
}
-static int check_framedrop(struct MPContext *mpctx)
+static bool check_framedrop(struct MPContext *mpctx, struct vo_chain *vo_c)
{
struct MPOpts *opts = mpctx->opts;
// check for frame-drop:
if (mpctx->video_status == STATUS_PLAYING && !mpctx->paused &&
- mpctx->audio_status == STATUS_PLAYING && !ao_untimed(mpctx->ao))
+ mpctx->audio_status == STATUS_PLAYING && !ao_untimed(mpctx->ao) &&
+ vo_c->video_src)
{
- float fps = mpctx->d_video->fps;
+ float fps = vo_c->container_fps;
double frame_time = fps > 0 ? 1.0 / fps : 0;
// we should avoid dropping too many frames in sequence unless we
// are too late. and we allow 100ms A-V delay here:
- if (mpctx->last_av_difference - 0.100 > mpctx->dropped_frames * frame_time)
+ int dropped_frames =
+ vo_c->video_src->dropped_frames - mpctx->dropped_frames_start;
+ if (mpctx->last_av_difference - 0.100 > dropped_frames * frame_time)
return !!(opts->frame_dropping & 2);
}
- return 0;
-}
-
-static bool decode_coverart(struct dec_video *d_video)
-{
- d_video->cover_art_mpi =
- video_decode(d_video, d_video->header->attached_picture, 0);
- // Might need flush.
- if (!d_video->cover_art_mpi)
- d_video->cover_art_mpi = video_decode(d_video, NULL, 0);
-
- return !!d_video->cover_art_mpi;
+ return false;
}
// Read a packet, store decoded image into d_video->waiting_decoded_mpi
// returns VD_* code
static int decode_image(struct MPContext *mpctx)
{
- struct dec_video *d_video = mpctx->d_video;
+ struct vo_chain *vo_c = mpctx->vo_chain;
+ if (vo_c->input_mpi)
+ return VD_PROGRESS;
- if (d_video->header->attached_picture) {
- d_video->waiting_decoded_mpi = mp_image_new_ref(d_video->cover_art_mpi);
- return VD_EOF;
- }
+ int res = DATA_EOF;
+ if (vo_c->filter_src) {
+ res = lavfi_request_frame_v(vo_c->filter_src, &vo_c->input_mpi);
+ } else if (vo_c->video_src) {
+ struct dec_video *d_video = vo_c->video_src;
+ bool hrseek = mpctx->hrseek_active && mpctx->hrseek_framedrop &&
+ mpctx->video_status == STATUS_SYNCING;
+ video_set_start(d_video, hrseek ? mpctx->hrseek_pts : MP_NOPTS_VALUE);
- struct demux_packet *pkt;
- if (demux_read_packet_async(d_video->header, &pkt) == 0)
- return VD_WAIT;
- if ((pkt && pkt->pts >= mpctx->hrseek_pts - .005) ||
- d_video->has_broken_packet_pts ||
- !mpctx->opts->hr_seek_framedrop)
- {
- mpctx->hrseek_framedrop = false;
- }
- bool hrseek = mpctx->hrseek_active && mpctx->video_status == STATUS_SYNCING;
- int framedrop_type = hrseek && mpctx->hrseek_framedrop ?
- 2 : check_framedrop(mpctx);
- d_video->waiting_decoded_mpi =
- video_decode(d_video, pkt, framedrop_type);
- bool had_packet = !!pkt;
- talloc_free(pkt);
-
- if (had_packet && !d_video->waiting_decoded_mpi &&
- mpctx->video_status == STATUS_PLAYING &&
- (mpctx->opts->frame_dropping & 2))
- {
- mpctx->dropped_frames_total++;
- mpctx->dropped_frames++;
+ video_set_framedrop(d_video, check_framedrop(mpctx, vo_c));
+
+ video_work(d_video);
+ res = video_get_frame(d_video, &vo_c->input_mpi);
}
- return had_packet ? VD_PROGRESS : VD_EOF;
+ switch (res) {
+ case DATA_WAIT: return VD_WAIT;
+ case DATA_OK:
+ case DATA_AGAIN: return VD_PROGRESS;
+ case DATA_EOF: return VD_EOF;
+ default: abort();
+ }
}
-
// Called after video reinit. This can be generally used to try to insert more
// filters using the filter chain edit functionality in command.c.
static void init_filter_params(struct MPContext *mpctx)
@@ -437,8 +536,8 @@ static void init_filter_params(struct MPContext *mpctx)
// If eof=true, drain the filter chain, and return VD_EOF if empty.
static int video_filter(struct MPContext *mpctx, bool eof)
{
- struct dec_video *d_video = mpctx->d_video;
- struct vf_chain *vf = d_video->vfilter;
+ struct vo_chain *vo_c = mpctx->vo_chain;
+ struct vf_chain *vf = vo_c->vf;
if (vf->initialized < 0)
return VD_ERROR;
@@ -450,28 +549,45 @@ static int video_filter(struct MPContext *mpctx, bool eof)
// Decoder output is different from filter input?
bool need_vf_reconfig = !vf->input_params.imgfmt || vf->initialized < 1 ||
- !mp_image_params_equal(&d_video->decoder_output, &vf->input_params);
+ !mp_image_params_equal(&vo_c->input_format, &vf->input_params);
// (If imgfmt==0, nothing was decoded yet, and the format is unknown.)
- if (need_vf_reconfig && d_video->decoder_output.imgfmt) {
+ if (need_vf_reconfig && vo_c->input_format.imgfmt) {
// Drain the filter chain.
if (vf_output_frame(vf, true) > 0)
return VD_PROGRESS;
// The filter chain is drained; execute the filter format change.
- filter_reconfig(mpctx, false);
- if (vf->initialized == 0)
- return VD_PROGRESS; // hw decoding fallback; try again
- if (vf->initialized < 1)
+ filter_reconfig(mpctx->vo_chain);
+
+ mp_notify(mpctx, MPV_EVENT_VIDEO_RECONFIG, NULL);
+
+ // Most video filters don't work with hardware decoding, so this
+ // might be the reason why filter reconfig failed.
+ if (vf->initialized < 0 && vo_c->video_src &&
+ video_vd_control(vo_c->video_src, VDCTRL_FORCE_HWDEC_FALLBACK, NULL)
+ == CONTROL_OK)
+ {
+ // Fallback active; decoder will return software format next
+ // time. Don't abort video decoding.
+ vf->initialized = 0;
+ mp_image_unrefp(&vo_c->input_mpi);
+ vo_c->input_format = (struct mp_image_params){0};
+ MP_VERBOSE(mpctx, "hwdec falback due to filters.\n");
+ return VD_PROGRESS; // try again
+ }
+ if (vf->initialized < 1) {
+ MP_FATAL(mpctx, "Cannot initialize video filters.\n");
return VD_ERROR;
+ }
init_filter_params(mpctx);
return VD_RECONFIG;
}
// If something was decoded, and the filter chain is ready, filter it.
- if (!need_vf_reconfig && d_video->waiting_decoded_mpi) {
- vf_filter_frame(vf, d_video->waiting_decoded_mpi);
- d_video->waiting_decoded_mpi = NULL;
+ if (!need_vf_reconfig && vo_c->input_mpi) {
+ vf_filter_frame(vf, vo_c->input_mpi);
+ vo_c->input_mpi = NULL;
return VD_PROGRESS;
}
@@ -484,22 +600,22 @@ static int video_filter(struct MPContext *mpctx, bool eof)
// the promise that calling this function again will eventually do something.
static int video_decode_and_filter(struct MPContext *mpctx)
{
- struct dec_video *d_video = mpctx->d_video;
+ struct vo_chain *vo_c = mpctx->vo_chain;
int r = video_filter(mpctx, false);
if (r < 0)
return r;
- if (!d_video->waiting_decoded_mpi) {
+ if (!vo_c->input_mpi) {
// Decode a new image, or at least feed the decoder a packet.
r = decode_image(mpctx);
if (r == VD_WAIT)
return r;
- if (d_video->waiting_decoded_mpi)
- d_video->decoder_output = d_video->waiting_decoded_mpi->params;
}
+ if (vo_c->input_mpi)
+ vo_c->input_format = vo_c->input_mpi->params;
- bool eof = !d_video->waiting_decoded_mpi && (r == VD_EOF || r < 0);
+ bool eof = !vo_c->input_mpi && (r == VD_EOF || r < 0);
r = video_filter(mpctx, eof);
if (r == VD_RECONFIG) // retry feeding decoded image
r = video_filter(mpctx, eof);
@@ -508,8 +624,7 @@ static int video_decode_and_filter(struct MPContext *mpctx)
static int video_feed_async_filter(struct MPContext *mpctx)
{
- struct dec_video *d_video = mpctx->d_video;
- struct vf_chain *vf = d_video->vfilter;
+ struct vf_chain *vf = mpctx->vo_chain->vf;
if (vf->initialized < 0)
return VD_ERROR;
@@ -565,32 +680,21 @@ static void handle_new_frame(struct MPContext *mpctx)
if (mpctx->video_pts != MP_NOPTS_VALUE) {
frame_time = pts - mpctx->video_pts;
double tolerance = 15;
- if (mpctx->demuxer->ts_resets_possible) {
- // Fortunately no real framerate is likely to go below this. It
- // still could be that the file is VFR, but the demuxer reports a
- // higher rate, so account for the case of e.g. 60hz demuxer fps
- // but 23hz actual fps.
- double fps = 23.976;
- if (mpctx->d_video->fps > 0 && mpctx->d_video->fps < fps)
- fps = mpctx->d_video->fps;
- tolerance = 3 * 1.0 / fps;
- }
if (frame_time <= 0 || frame_time >= tolerance) {
// Assume a discontinuity.
MP_WARN(mpctx, "Invalid video timestamp: %f -> %f\n",
mpctx->video_pts, pts);
- if (mpctx->d_audio && fabs(frame_time) > 1.0)
- mpctx->audio_status = STATUS_SYNCING;
frame_time = 0;
}
}
- mpctx->video_next_pts = pts;
mpctx->delay -= frame_time;
if (mpctx->video_status >= STATUS_PLAYING) {
mpctx->time_frame += frame_time / mpctx->video_speed;
adjust_sync(mpctx, pts, frame_time);
}
- mpctx->dropped_frames = 0;
+ struct dec_video *d_video = mpctx->vo_chain->video_src;
+ if (d_video)
+ mpctx->dropped_frames_start = d_video->dropped_frames;
MP_TRACE(mpctx, "frametime=%5.3f\n", frame_time);
}
@@ -617,7 +721,7 @@ static int get_req_frames(struct MPContext *mpctx, bool eof)
return mpctx->opts->video_sync == VS_DEFAULT ? 1 : 2;
int req = vo_get_num_req_frames(mpctx->video_out);
- return MPCLAMP(req, 2, MP_ARRAY_SIZE(mpctx->next_frames));
+ return MPCLAMP(req, 2, MP_ARRAY_SIZE(mpctx->next_frames) - 1);
}
// Whether it's fine to call add_new_frame() now.
@@ -629,7 +733,7 @@ static bool needs_new_frame(struct MPContext *mpctx)
// Queue a frame to mpctx->next_frames[]. Call only if needs_new_frame() signals ok.
static void add_new_frame(struct MPContext *mpctx, struct mp_image *frame)
{
- assert(needs_new_frame(mpctx));
+ assert(mpctx->num_next_frames < MP_ARRAY_SIZE(mpctx->next_frames));
assert(frame);
mpctx->next_frames[mpctx->num_next_frames++] = frame;
if (mpctx->num_next_frames == 1)
@@ -645,23 +749,15 @@ static bool have_new_frame(struct MPContext *mpctx, bool eof)
// Fill mpctx->next_frames[] with a newly filtered or decoded image.
// returns VD_* code
-static int video_output_image(struct MPContext *mpctx, double endpts)
+static int video_output_image(struct MPContext *mpctx)
{
+ struct vo_chain *vo_c = mpctx->vo_chain;
bool hrseek = mpctx->hrseek_active && mpctx->video_status == STATUS_SYNCING;
- if (mpctx->d_video->header->attached_picture) {
+ if (vo_c->is_coverart) {
if (vo_has_frame(mpctx->video_out))
return VD_EOF;
- if (mpctx->num_next_frames >= 1)
- return VD_NEW_FRAME;
- int r = video_decode_and_filter(mpctx);
- video_filter(mpctx, true); // force EOF filtering (avoid decoding more)
- mpctx->next_frames[0] = vf_read_output_frame(mpctx->d_video->vfilter);
- if (mpctx->next_frames[0]) {
- mpctx->next_frames[0]->pts = MP_NOPTS_VALUE;
- mpctx->num_next_frames = 1;
- }
- return r <= 0 ? VD_EOF : VD_PROGRESS;
+ hrseek = false;
}
if (have_new_frame(mpctx, false))
@@ -674,11 +770,9 @@ static int video_output_image(struct MPContext *mpctx, double endpts)
r = video_decode_and_filter(mpctx);
if (r < 0)
return r; // error
- struct mp_image *img = vf_read_output_frame(mpctx->d_video->vfilter);
+ struct mp_image *img = vf_read_output_frame(vo_c->vf);
if (img) {
- // Always add these; they make backstepping after seeking faster.
- add_frame_pts(mpctx, img->pts);
-
+ double endpts = get_play_end_pts(mpctx);
if (endpts != MP_NOPTS_VALUE && img->pts >= endpts) {
r = VD_EOF;
} else if (mpctx->max_frames == 0) {
@@ -686,8 +780,24 @@ static int video_output_image(struct MPContext *mpctx, double endpts)
} else if (hrseek && mpctx->hrseek_lastframe) {
mp_image_setrefp(&mpctx->saved_frame, img);
} else if (hrseek && img->pts < mpctx->hrseek_pts - .005) {
- /* just skip */
+ /* just skip - but save if backstep active */
+ if (mpctx->hrseek_backstep)
+ mp_image_setrefp(&mpctx->saved_frame, img);
+ } else if (mpctx->video_status == STATUS_SYNCING &&
+ mpctx->playback_pts != MP_NOPTS_VALUE &&
+ img->pts < mpctx->playback_pts && !vo_c->is_coverart)
+ {
+ /* skip after stream-switching */
} else {
+ if (hrseek && mpctx->hrseek_backstep) {
+ if (mpctx->saved_frame) {
+ add_new_frame(mpctx, mpctx->saved_frame);
+ mpctx->saved_frame = NULL;
+ } else {
+ MP_WARN(mpctx, "Backstep failed.\n");
+ }
+ mpctx->hrseek_backstep = false;
+ }
add_new_frame(mpctx, img);
img = NULL;
}
@@ -713,7 +823,7 @@ static void update_avsync_before_frame(struct MPContext *mpctx)
struct MPOpts *opts = mpctx->opts;
struct vo *vo = mpctx->video_out;
- if (!mpctx->sync_audio_to_video || mpctx->video_status < STATUS_READY) {
+ if (mpctx->vo_chain->is_coverart || mpctx->video_status < STATUS_READY) {
mpctx->time_frame = 0;
} else if (mpctx->display_sync_active || opts->video_sync == VS_NONE) {
// don't touch the timing
@@ -779,19 +889,19 @@ static void update_av_diff(struct MPContext *mpctx, double offset)
static void init_vo(struct MPContext *mpctx)
{
struct MPOpts *opts = mpctx->opts;
- struct dec_video *d_video = mpctx->d_video;
+ struct vo_chain *vo_c = mpctx->vo_chain;
if (opts->gamma_gamma != 1000)
- video_set_colors(d_video, "gamma", opts->gamma_gamma);
+ video_set_colors(vo_c, "gamma", opts->gamma_gamma);
if (opts->gamma_brightness != 1000)
- video_set_colors(d_video, "brightness", opts->gamma_brightness);
+ video_set_colors(vo_c, "brightness", opts->gamma_brightness);
if (opts->gamma_contrast != 1000)
- video_set_colors(d_video, "contrast", opts->gamma_contrast);
+ video_set_colors(vo_c, "contrast", opts->gamma_contrast);
if (opts->gamma_saturation != 1000)
- video_set_colors(d_video, "saturation", opts->gamma_saturation);
+ video_set_colors(vo_c, "saturation", opts->gamma_saturation);
if (opts->gamma_hue != 1000)
- video_set_colors(d_video, "hue", opts->gamma_hue);
- video_set_colors(d_video, "output-levels", opts->video_output_levels);
+ video_set_colors(vo_c, "hue", opts->gamma_hue);
+ video_set_colors(vo_c, "output-levels", opts->video_output_levels);
mp_notify(mpctx, MPV_EVENT_VIDEO_RECONFIG, NULL);
}
@@ -846,8 +956,8 @@ static double find_best_speed(struct MPContext *mpctx, double vsync)
static bool using_spdif_passthrough(struct MPContext *mpctx)
{
- if (mpctx->d_audio && mpctx->d_audio->afilter)
- return !af_fmt_is_pcm(mpctx->d_audio->afilter->input.format);
+ if (mpctx->ao_chain)
+ return !af_fmt_is_pcm(mpctx->ao_chain->input_format.format);
return false;
}
@@ -1084,8 +1194,8 @@ static void calculate_frame_duration(struct MPContext *mpctx)
{
assert(mpctx->num_past_frames >= 1 && mpctx->num_next_frames >= 1);
- double demux_duration =
- mpctx->d_video->fps > 0 ? 1.0 / mpctx->d_video->fps : -1;
+ double demux_duration = mpctx->vo_chain->container_fps > 0
+ ? 1.0 / mpctx->vo_chain->container_fps : -1;
double duration = -1;
if (mpctx->num_next_frames >= 2) {
@@ -1094,8 +1204,9 @@ static void calculate_frame_duration(struct MPContext *mpctx)
if (pts0 != MP_NOPTS_VALUE && pts1 != MP_NOPTS_VALUE && pts1 >= pts0)
duration = pts1 - pts0;
} else {
- // E.g. last frame on EOF.
- duration = demux_duration;
+ // E.g. last frame on EOF. Only use it if it's significant.
+ if (demux_duration >= 0.1)
+ duration = demux_duration;
}
// The following code tries to compensate for rounded Matroska timestamps
@@ -1133,13 +1244,14 @@ static void calculate_frame_duration(struct MPContext *mpctx)
mpctx->past_frames[0].approx_duration = approx_duration;
}
-void write_video(struct MPContext *mpctx, double endpts)
+void write_video(struct MPContext *mpctx)
{
struct MPOpts *opts = mpctx->opts;
- struct vo *vo = mpctx->video_out;
- if (!mpctx->d_video)
+ if (!mpctx->vo_chain)
return;
+ struct track *track = mpctx->vo_chain->track;
+ struct vo *vo = mpctx->vo_chain->vo;
// Actual playback starts when both audio and video are ready.
if (mpctx->video_status == STATUS_READY)
@@ -1148,7 +1260,7 @@ void write_video(struct MPContext *mpctx, double endpts)
if (mpctx->paused && mpctx->video_status >= STATUS_READY)
return;
- int r = video_output_image(mpctx, endpts);
+ int r = video_output_image(mpctx);
MP_TRACE(mpctx, "video_output_image: %d\n", r);
if (r < 0)
@@ -1159,8 +1271,11 @@ void write_video(struct MPContext *mpctx, double endpts)
if (r == VD_EOF) {
int prev_state = mpctx->video_status;
- mpctx->video_status =
- vo_still_displaying(vo) ? STATUS_DRAINING : STATUS_EOF;
+ mpctx->video_status = STATUS_EOF;
+ if (mpctx->num_past_frames > 0 && mpctx->past_frames[0].duration > 0) {
+ if (vo_still_displaying(vo))
+ mpctx->video_status = STATUS_DRAINING;
+ }
mpctx->delay = 0;
mpctx->last_av_difference = 0;
MP_DBG(mpctx, "video EOF (status=%d)\n", mpctx->video_status);
@@ -1186,8 +1301,11 @@ void write_video(struct MPContext *mpctx, double endpts)
const struct vo_driver *info = mpctx->video_out->driver;
char extra[20] = {0};
- if (p.w != p.d_w || p.h != p.d_h)
- snprintf(extra, sizeof(extra), " => %dx%d", p.d_w, p.d_h);
+ if (p.p_w != p.p_h) {
+ int d_w, d_h;
+ mp_image_params_get_dsize(&p, &d_w, &d_h);
+ snprintf(extra, sizeof(extra), " => %dx%d", d_w, d_h);
+ }
MP_INFO(mpctx, "VO: [%s] %dx%d%s %s\n",
info->name, p.w, p.h, extra, vo_format_name(p.imgfmt));
MP_VERBOSE(mpctx, "VO: Description: %s\n", info->description);
@@ -1203,6 +1321,11 @@ void write_video(struct MPContext *mpctx, double endpts)
mpctx->time_frame -= get_relative_time(mpctx);
update_avsync_before_frame(mpctx);
+ if (!update_subtitles(mpctx, mpctx->next_frames[0]->pts)) {
+ MP_VERBOSE(mpctx, "Video frame delayed due waiting on subtitles.\n");
+ return;
+ }
+
double time_frame = MPMAX(mpctx->time_frame, -1);
int64_t pts = mp_time_us() + (int64_t)(time_frame * 1e6);
@@ -1231,7 +1354,7 @@ void write_video(struct MPContext *mpctx, double endpts)
.pts = pts,
.duration = -1,
.still = mpctx->step_frames > 0,
- .num_frames = mpctx->num_next_frames,
+ .num_frames = MPMIN(mpctx->num_next_frames, VO_MAX_REQ_FRAMES),
.num_vsyncs = 1,
};
for (int n = 0; n < dummy.num_frames; n++)
@@ -1259,7 +1382,6 @@ void write_video(struct MPContext *mpctx, double endpts)
mpctx->osd_force_update = true;
update_osd_msg(mpctx);
- update_subtitles(mpctx);
vo_queue_frame(vo, frame);
@@ -1278,7 +1400,7 @@ void write_video(struct MPContext *mpctx, double endpts)
mp_notify(mpctx, MPV_EVENT_TICK, NULL);
- if (!mpctx->sync_audio_to_video)
+ if (mpctx->vo_chain->is_coverart)
mpctx->video_status = STATUS_EOF;
if (mpctx->video_status != STATUS_EOF) {
@@ -1299,7 +1421,7 @@ void write_video(struct MPContext *mpctx, double endpts)
error:
MP_FATAL(mpctx, "Could not initialize video chain.\n");
uninit_video_chain(mpctx);
- error_on_track(mpctx, mpctx->current_track[STREAM_VIDEO][0]);
+ error_on_track(mpctx, track);
handle_force_window(mpctx, true);
mpctx->sleeptime = 0;
}
diff --git a/stream/cache.c b/stream/cache.c
index c4eaa6e..8c18d18 100644
--- a/stream/cache.c
+++ b/stream/cache.c
@@ -96,7 +96,10 @@ struct priv {
bool idle; // cache thread has stopped reading
int64_t reads; // number of actual read attempts performed
+ bool enable_readahead; // actively read beyond read() position
int64_t read_filepos; // client read position (mirrors cache->pos)
+ int64_t read_min; // file position until which the thread should
+ // read even if readahead is disabled
int64_t eof_pos;
@@ -184,8 +187,7 @@ static size_t read_buffer(struct priv *s, unsigned char *dst,
}
// Runs in the cache thread.
-// Returns true if reading was attempted, and the mutex was shortly unlocked.
-static bool cache_fill(struct priv *s)
+static void cache_fill(struct priv *s)
{
int64_t read = s->read_filepos;
int len = 0;
@@ -209,6 +211,11 @@ static bool cache_fill(struct priv *s)
goto done;
}
+ if (!s->enable_readahead && s->read_min <= s->max_filepos) {
+ s->idle = true;
+ return;
+ }
+
if (mp_cancel_test(s->cache->cancel))
goto done;
@@ -236,7 +243,7 @@ static bool cache_fill(struct priv *s)
if (space < FILL_LIMIT) {
s->idle = true;
s->reads++; // don't stuck main thread
- return false;
+ return;
}
// limit to end of buffer (without wrapping)
@@ -278,8 +285,6 @@ done:
}
pthread_cond_signal(&s->wakeup);
-
- return true;
}
// This is called both during init and at runtime.
@@ -375,6 +380,10 @@ static int cache_get_cached_control(stream_t *cache, int cmd, void *arg)
case STREAM_CTRL_GET_CACHE_IDLE:
*(int *)arg = s->idle;
return STREAM_OK;
+ case STREAM_CTRL_SET_READAHEAD:
+ s->enable_readahead = *(int *)arg;
+ pthread_cond_signal(&s->wakeup);
+ return STREAM_OK;
case STREAM_CTRL_GET_TIME_LENGTH:
*(double *)arg = s->stream_time_length;
return s->stream_time_length ? STREAM_OK : STREAM_UNSUPPORTED;
@@ -421,6 +430,7 @@ static bool control_needs_flush(int stream_ctrl)
case STREAM_CTRL_SET_CURRENT_TITLE:
case STREAM_CTRL_RECONNECT:
case STREAM_CTRL_DVB_SET_CHANNEL:
+ case STREAM_CTRL_DVB_SET_CHANNEL_NAME:
case STREAM_CTRL_DVB_STEP_CHANNEL:
return true;
}
@@ -449,6 +459,7 @@ static void cache_execute_control(struct priv *s)
} else if (pos_changed || (ok && control_needs_flush(s->control))) {
MP_VERBOSE(s, "Dropping cache due to control()\n");
s->read_filepos = stream_tell(s->stream);
+ s->read_min = s->read_filepos;
s->control_flush = true;
cache_drop_contents(s);
}
@@ -505,6 +516,7 @@ static int cache_fill_buffer(struct stream *cache, char *buffer, int max_len)
double retry_time = 0;
int64_t retry = s->reads - 1; // try at least 1 read on EOF
while (1) {
+ s->read_min = s->read_filepos + max_len + 64 * 1024;
readb = read_buffer(s, buffer, max_len, s->read_filepos);
s->read_filepos += readb;
if (readb > 0)
@@ -543,7 +555,7 @@ static int cache_seek(stream_t *cache, int64_t pos)
MP_ERR(s, "Attempting to seek before cached data in unseekable stream.\n");
r = 0;
} else {
- cache->pos = s->read_filepos = pos;
+ cache->pos = s->read_filepos = s->read_min = pos;
s->eof = false; // so that cache_read() will actually wait for new data
pthread_cond_signal(&s->wakeup);
}
@@ -618,6 +630,7 @@ int stream_cache_init(stream_t *cache, stream_t *stream,
struct priv *s = talloc_zero(NULL, struct priv);
s->log = cache->log;
s->eof_pos = -1;
+ s->enable_readahead = true;
cache_drop_contents(s);
diff --git a/stream/cache_file.c b/stream/cache_file.c
index 901b3f6..4cf7060 100644
--- a/stream/cache_file.c
+++ b/stream/cache_file.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 <stdio.h>
#include <stdint.h>
diff --git a/stream/dvb_tune.c b/stream/dvb_tune.c
index fa65156..b83da04 100644
--- a/stream/dvb_tune.c
+++ b/stream/dvb_tune.c
@@ -40,8 +40,62 @@
#include "dvb_tune.h"
#include "common/msg.h"
-int dvb_get_tuner_type(int fe_fd, struct mp_log *log)
+int dvb_get_tuner_types(int fe_fd, struct mp_log *log, int** tuner_types)
{
+#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;
+ }
+ 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;
+ }
+ (*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;
+#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);
+ }
+ }
+ return supported_tuners;
+#else
struct dvb_frontend_info fe_info;
int res = ioctl(fe_fd, FE_GET_INFO, &fe_info);
if (res < 0) {
@@ -49,29 +103,36 @@ int dvb_get_tuner_type(int fe_fd, struct mp_log *log)
return 0;
}
+ mp_verbose(log, "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");
- return TUNER_TER;
-
+ 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;
case FE_QPSK:
- mp_verbose(log, "TUNER TYPE SEEMS TO BE DVB-S\n");
- return TUNER_SAT;
-
+ 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;
case FE_QAM:
- mp_verbose(log, "TUNER TYPE SEEMS TO BE DVB-C\n");
- return TUNER_CBL;
-
+ 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;
#ifdef DVB_ATSC
case FE_ATSC:
- mp_verbose(log, "TUNER TYPE SEEMS TO BE DVB-ATSC\n");
- return TUNER_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;
#endif
default:
- mp_err(log, "UNKNOWN TUNER TYPE\n");
+ mp_err(log, "Unknown tuner type: %d\n", fe_info.type);
return 0;
}
-
+#endif
}
int dvb_open_devices(dvb_priv_t *priv, int n, int demux_cnt)
@@ -79,32 +140,34 @@ int dvb_open_devices(dvb_priv_t *priv, int n, int demux_cnt)
int i;
char frontend_dev[32], dvr_dev[32], demux_dev[32];
+ 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);
- priv->fe_fd = open(frontend_dev, O_RDWR | O_NONBLOCK | O_CLOEXEC);
- if (priv->fe_fd < 0) {
+ 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",
frontend_dev, errno);
return 0;
}
- priv->demux_fds_cnt = 0;
+ state->demux_fds_cnt = 0;
MP_VERBOSE(priv, "DVB_OPEN_DEVICES(%d)\n", demux_cnt);
for (i = 0; i < demux_cnt; i++) {
- priv->demux_fds[i] = open(demux_dev, O_RDWR | O_NONBLOCK | O_CLOEXEC);
- if (priv->demux_fds[i] < 0) {
+ state->demux_fds[i] = open(demux_dev, O_RDWR | O_NONBLOCK | O_CLOEXEC);
+ if (state->demux_fds[i] < 0) {
MP_ERR(priv, "ERROR OPENING DEMUX 0: %d\n", errno);
return 0;
} else {
MP_VERBOSE(priv, "OPEN(%d), file %s: FD=%d, CNT=%d\n", i, demux_dev,
- priv->demux_fds[i], priv->demux_fds_cnt);
- priv->demux_fds_cnt++;
+ state->demux_fds[i], state->demux_fds_cnt);
+ state->demux_fds_cnt++;
}
}
- priv->dvr_fd = open(dvr_dev, O_RDONLY | O_NONBLOCK | O_CLOEXEC);
- if (priv->dvr_fd < 0) {
+ 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);
return 0;
}
@@ -118,24 +181,26 @@ int dvb_fix_demuxes(dvb_priv_t *priv, int cnt)
int i;
char demux_dev[32];
- sprintf(demux_dev, "/dev/dvb/adapter%d/demux0", priv->card);
- MP_VERBOSE(priv, "FIX %d -> %d\n", priv->demux_fds_cnt, cnt);
- if (priv->demux_fds_cnt >= cnt) {
- for (i = priv->demux_fds_cnt - 1; i >= cnt; i--) {
- MP_VERBOSE(priv, "FIX, CLOSE fd(%d): %d\n", i, priv->demux_fds[i]);
- close(priv->demux_fds[i]);
+ dvb_state_t* state = priv->state;
+
+ sprintf(demux_dev, "/dev/dvb/adapter%d/demux0", state->card);
+ 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--) {
+ MP_VERBOSE(priv, "FIX, CLOSE fd(%d): %d\n", i, state->demux_fds[i]);
+ close(state->demux_fds[i]);
}
- priv->demux_fds_cnt = cnt;
- } else if (priv->demux_fds_cnt < cnt) {
- for (i = priv->demux_fds_cnt; i < cnt; i++) {
- priv->demux_fds[i] = open(demux_dev,
+ state->demux_fds_cnt = cnt;
+ } else if (state->demux_fds_cnt < cnt) {
+ for (i = state->demux_fds_cnt; i < cnt; i++) {
+ state->demux_fds[i] = open(demux_dev,
O_RDWR | O_NONBLOCK | O_CLOEXEC);
- MP_VERBOSE(priv, "FIX, OPEN fd(%d): %d\n", i, priv->demux_fds[i]);
- if (priv->demux_fds[i] < 0) {
+ MP_VERBOSE(priv, "FIX, OPEN fd(%d): %d\n", i, state->demux_fds[i]);
+ if (state->demux_fds[i] < 0) {
MP_ERR(priv, "ERROR OPENING DEMUX 0: %d\n", errno);
return 0;
} else
- priv->demux_fds_cnt++;
+ state->demux_fds_cnt++;
}
}
@@ -370,7 +435,7 @@ static int do_diseqc(int secfd, int sat_no, int polv, int hi_lo)
(sat_no / 4) % 2 ? SEC_MINI_B : SEC_MINI_A);
}
-static int tune_it(dvb_priv_t *priv, int fd_frontend, int fd_sec,
+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,
fe_spectral_inversion_t specInv, unsigned int diseqc,
@@ -383,21 +448,19 @@ static int tune_it(dvb_priv_t *priv, int fd_frontend, int fd_sec,
int timeout)
{
int hi_lo = 0, dfd;
+
+ dvb_state_t* state = priv->state;
+
struct dvb_frontend_parameters feparams;
- struct dvb_frontend_info fe_info;
- MP_VERBOSE(priv, "TUNE_IT, fd_frontend %d, fd_sec %d\nfreq %lu, srate %lu, "
- "pol %c, tone %i, diseqc %u\n", fd_frontend, fd_sec,
+ 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));
- if (ioctl(fd_frontend, FE_GET_INFO, &fe_info) < 0) {
- MP_FATAL(priv, "FE_GET_INFO FAILED\n");
- return -1;
- }
- MP_VERBOSE(priv, "Using DVB card \"%s\"\n", fe_info.name);
+ MP_VERBOSE(priv, "Using DVB card \"%s\"\n", state->cards[state->card].name);
{
/* discard stale QPSK events */
@@ -408,8 +471,8 @@ static int tune_it(dvb_priv_t *priv, int fd_frontend, int fd_sec,
}
}
- switch (fe_info.type) {
- case FE_OFDM:
+ switch (state->tuner_type) {
+ case TUNER_TER: {
if (freq < 1000000)
freq *= 1000UL;
feparams.frequency = freq;
@@ -427,8 +490,9 @@ static int tune_it(dvb_priv_t *priv, int fd_frontend, int fd_sec,
MP_ERR(priv, "ERROR tuning channel\n");
return -1;
}
- break;
- case FE_QPSK:
+ }
+ break;
+ case TUNER_SAT: {
// DVB-S
if (freq > 2200000) {
// this must be an absolute frequency
@@ -505,8 +569,35 @@ static int tune_it(dvb_priv_t *priv, int fd_frontend, int fd_sec,
return -1;
}
#endif
- break;
- case FE_QAM:
+ }
+ 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;
feparams.u.qam.symbol_rate = srate;
@@ -517,9 +608,11 @@ static int tune_it(dvb_priv_t *priv, int fd_frontend, int fd_sec,
MP_ERR(priv, "ERROR tuning channel\n");
return -1;
}
- break;
+#endif
+ }
+ break;
#ifdef DVB_ATSC
- case FE_ATSC:
+ case TUNER_ATSC: {
feparams.frequency = freq;
feparams.u.vsb.modulation = modulation;
MP_VERBOSE(priv, "tuning ATSC to %d, modulation=%d\n", freq, modulation);
@@ -527,7 +620,8 @@ static int tune_it(dvb_priv_t *priv, int fd_frontend, int fd_sec,
MP_ERR(priv, "ERROR tuning channel\n");
return -1;
}
- break;
+ }
+ break;
#endif
default:
MP_VERBOSE(priv, "Unknown FE type. Aborting\n");
@@ -548,7 +642,9 @@ int dvb_tune(dvb_priv_t *priv, int freq, char pol, int srate, int diseqc,
{
MP_INFO(priv, "dvb_tune Freq: %lu\n", (long unsigned int) freq);
- int ris = tune_it(priv, priv->fe_fd, priv->sec_fd, freq, srate, pol, tone,
+ 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,
HP_CodeRate, TransmissionMode, guardInterval,
bandWidth, LP_CodeRate, hier, timeout);
diff --git a/stream/dvb_tune.h b/stream/dvb_tune.h
index 50c598f..dafa117 100644
--- a/stream/dvb_tune.h
+++ b/stream/dvb_tune.h
@@ -23,7 +23,7 @@
struct mp_log;
-int dvb_get_tuner_type(int fe_fd, struct mp_log *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);
int dvb_set_ts_filt(dvb_priv_t *priv, int fd, uint16_t pid, dmx_pes_type_t pestype);
diff --git a/stream/dvbin.h b/stream/dvbin.h
index baa7ba4..9c2eb35 100644
--- a/stream/dvbin.h
+++ b/stream/dvbin.h
@@ -28,9 +28,10 @@
* Version 5 is also called S2API, it adds support for tuning to S2 channels
* and is extensible for future delivery systems. Old API is deprecated.
* StreamID-implementation only supported since API >=5.2.
+ * At least DTV_ENUM_DELSYS requires 5.5.
*/
-#if (DVB_API_VERSION == 5 && DVB_API_VERSION_MINOR >= 2)
+#if (DVB_API_VERSION == 5 && DVB_API_VERSION_MINOR >= 5)
#define DVB_USE_S2API 1
// This had a different name until API 5.8.
@@ -90,25 +91,26 @@ typedef struct {
typedef struct {
int count;
dvb_card_config_t *cards;
- void *priv;
-} dvb_config_t;
-typedef struct dvb_params {
- struct mp_log *log;
- int fd;
int card;
int fe_fd;
- int sec_fd;
- int demux_fd[3], demux_fds[DMX_FILTER_SIZE], demux_fds_cnt;
int dvr_fd;
+ int demux_fd[3], demux_fds[DMX_FILTER_SIZE], demux_fds_cnt;
- dvb_config_t *config;
dvb_channels_list *list;
int tuner_type;
int is_on;
int retry;
int timeout;
int last_freq;
+ bool switching_channel;
+ bool stream_used;
+} dvb_state_t;
+
+typedef struct dvb_params {
+ struct mp_log *log;
+
+ dvb_state_t *state;
char *cfg_prog;
int cfg_card;
@@ -125,7 +127,7 @@ typedef struct dvb_params {
int dvb_step_channel(stream_t *, int);
int dvb_set_channel(stream_t *, int, int);
-dvb_config_t *dvb_get_config(stream_t *);
-void dvb_free_config(dvb_config_t *config);
+dvb_state_t *dvb_get_state(stream_t *);
+void dvb_free_state(dvb_state_t *);
#endif /* MPLAYER_DVBIN_H */
diff --git a/stream/rar.c b/stream/rar.c
index d0dbc8d..9a74097 100644
--- a/stream/rar.c
+++ b/stream/rar.c
@@ -31,7 +31,7 @@
#include <libavutil/intreadwrite.h>
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "common/common.h"
#include "stream.h"
#include "rar.h"
diff --git a/stream/stream.c b/stream/stream.c
index 1ceb184..1048a80 100644
--- a/stream/stream.c
+++ b/stream/stream.c
@@ -29,7 +29,7 @@
#include "osdep/atomics.h"
#include "osdep/io.h"
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "config.h"
@@ -500,7 +500,7 @@ int stream_fill_buffer(stream_t *s)
}
// Read between 1..buf_size bytes of data, return how much data has been read.
-// Return 0 on EOF, error, of if buf_size was 0.
+// Return 0 on EOF, error, or if buf_size was 0.
int stream_read_partial(stream_t *s, char *buf, int buf_size)
{
assert(s->buf_pos <= s->buf_len);
@@ -914,7 +914,7 @@ struct bstr stream_read_complete(struct stream *s, void *talloc_ctx,
int total_read = 0;
int padding = 1;
char *buf = NULL;
- int64_t size = stream_get_size(s);
+ int64_t size = stream_get_size(s) - stream_tell(s);
if (size > max_size)
return (struct bstr){NULL, 0};
if (size > 0)
diff --git a/stream/stream.h b/stream/stream.h
index 28a6ba6..ab37c3b 100644
--- a/stream/stream.h
+++ b/stream/stream.h
@@ -72,6 +72,7 @@ enum stream_ctrl {
STREAM_CTRL_GET_CACHE_FILL,
STREAM_CTRL_GET_CACHE_IDLE,
STREAM_CTRL_RESUME_CACHE,
+ STREAM_CTRL_SET_READAHEAD,
// stream_memory.c
STREAM_CTRL_SET_CONTENTS,
@@ -98,6 +99,8 @@ enum stream_ctrl {
STREAM_CTRL_TV_STEP_CHAN,
STREAM_CTRL_TV_LAST_CHAN,
STREAM_CTRL_DVB_SET_CHANNEL,
+ STREAM_CTRL_DVB_SET_CHANNEL_NAME,
+ STREAM_CTRL_DVB_GET_CHANNEL_NAME,
STREAM_CTRL_DVB_STEP_CHANNEL,
// Optical discs
diff --git a/stream/stream_avdevice.c b/stream/stream_avdevice.c
index d1fd74f..9734b7b 100644
--- a/stream/stream_avdevice.c
+++ b/stream/stream_avdevice.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 "config.h"
diff --git a/stream/stream_bluray.c b/stream/stream_bluray.c
index b40c327..7a58d88 100644
--- a/stream/stream_bluray.c
+++ b/stream/stream_bluray.c
@@ -38,7 +38,7 @@
#include <libavutil/common.h>
#include "config.h"
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "common/common.h"
#include "common/msg.h"
#include "options/m_option.h"
diff --git a/stream/stream_cdda.c b/stream/stream_cdda.c
index 6781f4a..8fadf34 100644
--- a/stream/stream_cdda.c
+++ b/stream/stream_cdda.c
@@ -37,7 +37,7 @@
#include <stdlib.h>
#include <stdbool.h>
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "stream.h"
#include "options/m_option.h"
diff --git a/stream/stream_dvb.c b/stream/stream_dvb.c
index 0510143..43ca8ac 100644
--- a/stream/stream_dvb.c
+++ b/stream/stream_dvb.c
@@ -40,6 +40,7 @@
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
+#include <pthread.h>
#include <libavutil/avstring.h>
@@ -47,6 +48,7 @@
#include "misc/ctype.h"
#include "stream.h"
+#include "common/tags.h"
#include "options/m_config.h"
#include "options/m_option.h"
#include "options/options.h"
@@ -61,6 +63,9 @@
#define OPT_BASE_STRUCT struct dvb_params
+static dvb_state_t* global_dvb_state = NULL;
+static pthread_mutex_t global_dvb_state_lock = PTHREAD_MUTEX_INITIALIZER;
+
/// URL definition
static const m_option_t stream_params[] = {
OPT_STRING("prog", cfg_prog, 0),
@@ -576,22 +581,22 @@ static dvb_channels_list *dvb_get_channels(struct mp_log *log,
return list;
}
-void dvb_free_config(dvb_config_t *config)
+void dvb_free_state(dvb_state_t *state)
{
int i, j;
- for (i = 0; i < config->count; i++) {
- free(config->cards[i].name);
- if (!config->cards[i].list)
+ for (i = 0; i < state->count; i++) {
+ free(state->cards[i].name);
+ if (!state->cards[i].list)
continue;
- if (config->cards[i].list->channels) {
- for (j = 0; j < config->cards[i].list->NUM_CHANNELS; j++)
- free(config->cards[i].list->channels[j].name);
- free(config->cards[i].list->channels);
+ 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);
}
- free(config->cards[i].list);
+ free(state->cards[i].list);
}
- free(config);
+ free(state);
}
static int dvb_streaming_read(stream_t *stream, char *buffer, int size)
@@ -599,12 +604,13 @@ 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;
MP_TRACE(stream, "dvb_streaming_read(%d)\n", size);
- tries = priv->retry + 1;
+ tries = state->retry + 1;
- fd = priv->fd;
+ fd = state->dvr_fd;
while (pos < size) {
pfds[0].fd = fd;
pfds[0].events = POLLIN | POLLPRI;
@@ -642,18 +648,18 @@ int dvb_set_channel(stream_t *stream, int card, int n)
dvb_channel_t *channel;
dvb_priv_t *priv = stream->priv;
char buf[4096];
- dvb_config_t *conf = (dvb_config_t *) priv->config;
+ dvb_state_t *state = (dvb_state_t *) priv->state;
int devno;
int i;
- if ((card < 0) || (card > conf->count)) {
+ if ((card < 0) || (card > state->count)) {
MP_ERR(stream, "dvb_set_channel: INVALID CARD NUMBER: %d vs %d, abort\n",
- card, conf->count);
+ card, state->count);
return 0;
}
- devno = conf->cards[card].devno;
- new_list = conf->cards[card].list;
+ devno = state->cards[card].devno;
+ new_list = state->cards[card].list;
if ((n > new_list->NUM_CHANNELS) || (n < 0)) {
MP_ERR(stream, "dvb_set_channel: INVALID CHANNEL NUMBER: %d, for "
"card %d, abort\n", n, card);
@@ -661,14 +667,14 @@ int dvb_set_channel(stream_t *stream, int card, int n)
}
channel = &(new_list->channels[n]);
- if (priv->is_on) { //the fds are already open and we have to stop the demuxers
- for (i = 0; i < priv->demux_fds_cnt; i++)
- dvb_demux_stop(priv->demux_fds[i]);
+ 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]);
- priv->retry = 0;
+ state->retry = 0;
//empty both the stream's and driver's buffer
while (dvb_streaming_read(stream, buf, 4096) > 0) {}
- if (priv->card != card) {
+ if (state->card != card) {
dvbin_close(stream);
if (!dvb_open_devices(priv, devno, channel->pids_cnt)) {
MP_ERR(stream, "DVB_SET_CHANNEL, COULDN'T OPEN DEVICES OF "
@@ -689,17 +695,16 @@ int dvb_set_channel(stream_t *stream, int card, int n)
}
}
- priv->card = card;
- priv->list = new_list;
- priv->retry = 5;
+ state->card = card;
+ state->list = new_list;
+ state->retry = 5;
new_list->current = n;
- priv->fd = priv->dvr_fd;
MP_VERBOSE(stream, "DVB_SET_CHANNEL: new channel name=%s, card: %d, "
"channel %d\n", channel->name, card, n);
stream_drop_buffers(stream);
- if (channel->freq != priv->last_freq) {
+ 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,
@@ -709,8 +714,8 @@ int dvb_set_channel(stream_t *stream, int card, int n)
return 0;
}
- priv->last_freq = channel->freq;
- priv->is_on = 1;
+ state->last_freq = channel->freq;
+ state->is_on = 1;
if (channel->service_id != -1) {
/* We need the PMT-PID in addition.
@@ -735,7 +740,7 @@ int dvb_set_channel(stream_t *stream, int card, int n)
MP_ERR(stream, "DVB_SET_CHANNEL: PMT-PID not found, "
"teletext-decoding may fail.\n");
} else {
- if (!dvb_set_ts_filt(priv, priv->demux_fds[i], channel->pids[i],
+ if (!dvb_set_ts_filt(priv, state->demux_fds[i], channel->pids[i],
DMX_PES_OTHER))
return 0;
}
@@ -749,15 +754,11 @@ int dvb_step_channel(stream_t *stream, int dir)
int new_current;
dvb_channels_list *list;
dvb_priv_t *priv = stream->priv;
+ dvb_state_t* state = priv->state;
MP_VERBOSE(stream, "DVB_STEP_CHANNEL dir %d\n", dir);
- if (priv == NULL) {
- MP_ERR(stream, "dvb_step_channel: NULL priv_ptr, quit\n");
- return 0;
- }
-
- list = priv->list;
+ list = state->list;
if (list == NULL) {
MP_ERR(stream, "dvb_step_channel: NULL list_ptr, quit\n");
return 0;
@@ -766,7 +767,7 @@ 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, priv->card, new_current);
+ return dvb_set_channel(stream, state->card, new_current);
}
static int dvbin_stream_control(struct stream *s, int cmd, void *arg)
@@ -776,33 +777,106 @@ static int dvbin_stream_control(struct stream *s, int cmd, void *arg)
case STREAM_CTRL_DVB_SET_CHANNEL: {
int *iarg = arg;
r = dvb_set_channel(s, iarg[1], iarg[0]);
- return r ? STREAM_OK : STREAM_ERROR;
+ 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);
+ if (r) {
+ // Stream will be pulled down after channel switch,
+ // persist state.
+ state->switching_channel = true;
+ return STREAM_OK;
+ }
+ return STREAM_ERROR;
}
- case STREAM_CTRL_DVB_STEP_CHANNEL:
+ case STREAM_CTRL_DVB_STEP_CHANNEL: {
r = dvb_step_channel(s, *(int *)arg);
- return r ? STREAM_OK : STREAM_ERROR;
+ 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;
+ }
+ 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;
+ }
}
return STREAM_UNSUPPORTED;
}
static void dvbin_close(stream_t *stream)
{
- int i;
dvb_priv_t *priv = (dvb_priv_t *) stream->priv;
+ dvb_state_t* state = priv->state;
+
+ if (state->switching_channel && state->is_on) {
+ // Prevent state destruction, reset channel-switch.
+ state->switching_channel = false;
+ pthread_mutex_lock(&global_dvb_state_lock);
+ global_dvb_state->stream_used = false;
+ pthread_mutex_unlock(&global_dvb_state_lock);
+ return;
+ }
- for (i = priv->demux_fds_cnt - 1; i >= 0; i--) {
- priv->demux_fds_cnt--;
+ for (int i = state->demux_fds_cnt - 1; i >= 0; i--) {
+ state->demux_fds_cnt--;
MP_VERBOSE(stream, "DVBIN_CLOSE, close(%d), fd=%d, COUNT=%d\n", i,
- priv->demux_fds[i], priv->demux_fds_cnt);
- close(priv->demux_fds[i]);
+ state->demux_fds[i], state->demux_fds_cnt);
+ close(state->demux_fds[i]);
}
- close(priv->dvr_fd);
+ close(state->dvr_fd);
+
+ close(state->fe_fd);
+ state->fe_fd = state->dvr_fd = -1;
- close(priv->fe_fd);
- priv->fe_fd = priv->sec_fd = priv->dvr_fd = -1;
+ state->is_on = 0;
- priv->is_on = 0;
- dvb_free_config(priv->config);
+ pthread_mutex_lock(&global_dvb_state_lock);
+ dvb_free_state(state);
+ global_dvb_state = NULL;
+ pthread_mutex_unlock(&global_dvb_state_lock);
}
static int dvb_streaming_start(stream_t *stream, int tuner_type, char *progname)
@@ -810,23 +884,24 @@ static int dvb_streaming_start(stream_t *stream, int tuner_type, 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;
MP_VERBOSE(stream, "\r\ndvb_streaming_start(PROG: %s, CARD: %d)\n",
opts->cfg_prog, opts->cfg_card);
- priv->is_on = 0;
+ state->is_on = 0;
i = 0;
- while ((channel == NULL) && i < priv->list->NUM_CHANNELS) {
- if (!strcmp(priv->list->channels[i].name, progname))
- channel = &(priv->list->channels[i]);
+ while ((channel == NULL) && i < state->list->NUM_CHANNELS) {
+ if (!strcmp(state->list->channels[i].name, progname))
+ channel = &(state->list->channels[i]);
i++;
}
if (channel != NULL) {
- priv->list->current = i - 1;
+ state->list->current = i - 1;
MP_VERBOSE(stream, "PROGRAM NUMBER %d: name=%s, freq=%u\n", i - 1,
channel->name, channel->freq);
} else {
@@ -835,8 +910,8 @@ static int dvb_streaming_start(stream_t *stream, int tuner_type, char *progname)
}
- if (!dvb_set_channel(stream, priv->card, priv->list->current)) {
- MP_ERR(stream, "ERROR, COULDN'T SET CHANNEL %i: ", priv->list->current);
+ if (!dvb_set_channel(stream, state->card, state->list->current)) {
+ MP_ERR(stream, "ERROR, COULDN'T SET CHANNEL %i: ", state->list->current);
dvbin_close(stream);
return 0;
}
@@ -851,7 +926,7 @@ static int dvb_streaming_start(stream_t *stream, int tuner_type, char *progname)
static int dvb_open(stream_t *stream)
{
- // I don't force the file format bacause, although it's almost always TS,
+ // 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 = stream->priv;
priv->log = stream->log;
@@ -859,52 +934,68 @@ static int dvb_open(stream_t *stream)
char *progname;
int tuner_type = 0, i;
- priv->fe_fd = priv->sec_fd = priv->dvr_fd = -1;
- priv->config = dvb_get_config(stream);
- if (priv->config == NULL) {
- MP_ERR(stream, "DVB CONFIGURATION IS EMPTY, exit\n");
- return STREAM_ERROR;
+ 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!");
+ pthread_mutex_unlock(&global_dvb_state_lock);
+ return STREAM_ERROR;
}
- priv->card = -1;
- for (i = 0; i < priv->config->count; i++) {
- if (priv->config->cards[i].devno + 1 == p->cfg_card) {
- priv->card = i;
- break;
- }
- }
+ dvb_state_t* state = dvb_get_state(stream);
- if (priv->card == -1) {
- MP_ERR(stream, "NO CONFIGURATION FOUND FOR CARD N. %d, exit\n",
- p->cfg_card);
+ priv->state = state;
+ if (state == NULL) {
+ MP_ERR(stream, "DVB CONFIGURATION IS EMPTY, exit\n");
+ pthread_mutex_unlock(&global_dvb_state_lock);
return STREAM_ERROR;
}
- priv->timeout = p->cfg_timeout;
+ state->stream_used = true;
+ pthread_mutex_unlock(&global_dvb_state_lock);
- tuner_type = priv->config->cards[priv->card].type;
+ if (state->is_on != 1) {
+ // State could be already initialized, for example, we just did a channel switch.
+ // The following setup only has to be done once.
- if (tuner_type == 0) {
- MP_VERBOSE(stream,
- "OPEN_DVB: UNKNOWN OR UNDETECTABLE TUNER TYPE, EXIT\n");
- return STREAM_ERROR;
- }
+ state->card = -1;
+ for (i = 0; i < state->count; i++) {
+ if (state->cards[i].devno + 1 == p->cfg_card) {
+ state->card = i;
+ break;
+ }
+ }
- priv->tuner_type = tuner_type;
+ 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;
- MP_VERBOSE(stream, "OPEN_DVB: prog=%s, card=%d, type=%d\n",
- p->cfg_prog, priv->card + 1, priv->tuner_type);
+ tuner_type = state->cards[state->card].type;
- priv->list = priv->config->cards[priv->card].list;
+ if (tuner_type == 0) {
+ MP_VERBOSE(stream,
+ "OPEN_DVB: UNKNOWN OR UNDETECTABLE TUNER TYPE, EXIT\n");
+ return STREAM_ERROR;
+ }
- if ((!strcmp(p->cfg_prog, "")) && (priv->list != NULL)) {
- progname = priv->list->channels[0].name;
- } else {
- progname = p->cfg_prog;
- }
+ state->tuner_type = tuner_type;
+ MP_VERBOSE(stream, "OPEN_DVB: prog=%s, card=%d, type=%d\n",
+ p->cfg_prog, state->card + 1, state->tuner_type);
- if (!dvb_streaming_start(stream, tuner_type, progname))
- return STREAM_ERROR;
+ state->list = state->cards[state->card].list;
+
+ if ((!strcmp(p->cfg_prog, "")) && (state->list != NULL)) {
+ progname = state->list->channels[0].name;
+ } else {
+ progname = p->cfg_prog;
+ }
+
+
+ if (!dvb_streaming_start(stream, tuner_type, progname))
+ return STREAM_ERROR;
+ }
stream->type = STREAMTYPE_DVB;
stream->fill_buffer = dvb_streaming_read;
@@ -919,113 +1010,130 @@ static int dvb_open(stream_t *stream)
}
#define MAX_CARDS 4
-dvb_config_t *dvb_get_config(stream_t *stream)
+dvb_state_t *dvb_get_state(stream_t *stream)
{
+ if (global_dvb_state != NULL) {
+ return global_dvb_state;
+ }
struct mp_log *log = stream->log;
struct mpv_global *global = stream->global;
dvb_priv_t *priv = stream->priv;
- int i, fd, type, size;
+ int type, size;
char filename[30], *name;
dvb_channels_list *list;
dvb_card_config_t *cards = NULL, *tmp;
- dvb_config_t *conf = NULL;
+ dvb_state_t *state = NULL;
- conf = malloc(sizeof(dvb_config_t));
- if (conf == NULL)
+ state = malloc(sizeof(dvb_state_t));
+ if (state == NULL)
return NULL;
- conf->priv = NULL;
- conf->count = 0;
- conf->cards = NULL;
- for (i = 0; i < MAX_CARDS; i++) {
+ state->count = 0;
+ 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++) {
snprintf(filename, sizeof(filename), "/dev/dvb/adapter%d/frontend0", i);
- fd = open(filename, O_RDONLY | O_NONBLOCK | O_CLOEXEC);
+ int fd = open(filename, O_RDONLY | O_NONBLOCK | O_CLOEXEC);
if (fd < 0) {
mp_verbose(log, "DVB_CONFIG, can't open device %s, skipping\n",
filename);
continue;
}
- type = dvb_get_tuner_type(fd, log);
+ 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);
close(fd);
- if (type != TUNER_SAT && type != TUNER_TER && type != TUNER_CBL &&
- type != TUNER_ATSC) {
+ 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])
+ void *talloc_ctx = talloc_new(NULL);
+ char *conf_file = NULL;
+ if (priv->cfg_file && priv->cfg_file[0])
conf_file = priv->cfg_file;
- else {
+ else {
switch (type) {
case TUNER_TER:
- conf_file = mp_find_config_file(talloc_ctx, global,
- "channels.conf.ter");
- break;
+ 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;
+ 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;
+ 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;
+ conf_file = mp_find_config_file(talloc_ctx, global,
+ "channels.conf.atsc");
+ break;
}
if (conf_file) {
- mp_verbose(log, "Ignoring other channels.conf files.\n");
+ mp_verbose(log, "Ignoring other channels.conf files.\n");
} else {
- conf_file = mp_find_config_file(talloc_ctx, global,
- "channels.conf");
+ 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);
+ list = dvb_get_channels(log, priv->cfg_full_transponder, conf_file,
+ type);
+ talloc_free(talloc_ctx);
- if (list == NULL)
+ if (list == NULL)
continue;
- size = sizeof(dvb_card_config_t) * (conf->count + 1);
- tmp = realloc(conf->cards, size);
+ size = sizeof(dvb_card_config_t) * (state->count + 1);
+ tmp = realloc(state->cards, size);
- if (tmp == NULL) {
- fprintf(stderr, "DVB_CONFIG, can't realloc %d bytes, skipping\n",
- size);
+ 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) {
- fprintf(stderr, "DVB_CONFIG, can't realloc 20 bytes, skipping\n");
+ }
+ 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++;
}
-
- conf->cards = cards;
- conf->cards[conf->count].devno = i;
- conf->cards[conf->count].list = list;
- conf->cards[conf->count].type = type;
- snprintf(name, 20, "DVB-%c card n. %d",
- type == TUNER_TER ? 'T' : (type == TUNER_CBL ? 'C' : 'S'),
- conf->count + 1);
- conf->cards[conf->count].name = name;
- conf->count++;
+ talloc_free(tuner_types);
}
- if (conf->count == 0) {
- free(conf);
- conf = NULL;
+ if (state->count == 0) {
+ free(state);
+ state = NULL;
}
- return conf;
+ global_dvb_state = state;
+ return state;
}
static void *get_defaults(stream_t *st)
diff --git a/stream/stream_dvd.c b/stream/stream_dvd.c
index 641305e..55cb6df 100644
--- a/stream/stream_dvd.c
+++ b/stream/stream_dvd.c
@@ -35,7 +35,7 @@
#include "osdep/io.h"
#include "config.h"
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "common/common.h"
#include "common/msg.h"
diff --git a/stream/stream_dvdnav.c b/stream/stream_dvdnav.c
index e2562db..d61ca5c 100644
--- a/stream/stream_dvdnav.c
+++ b/stream/stream_dvdnav.c
@@ -332,8 +332,7 @@ static int control(stream_t *stream, int cmd, void *arg)
case STREAM_CTRL_SEEK_TO_TIME: {
double *args = arg;
double d = args[0]; // absolute target timestamp
- double r = args[1]; // if not SEEK_ABSOLUTE, the base time for d
- int flags = args[2]; // from SEEK_* flags (demux.h)
+ int flags = args[1]; // from SEEK_* flags (demux.h)
if (flags & SEEK_HR)
d -= 10; // fudge offset; it's a hack, because fuck libdvd*
int64_t tm = (int64_t)(d * 90000);
@@ -344,37 +343,9 @@ static int control(stream_t *stream, int cmd, void *arg)
uint32_t pos, len;
if (dvdnav_get_position(dvdnav, &pos, &len) != DVDNAV_STATUS_OK)
break;
- // The following is convoluted, because we have to translate between
- // dvdnav's block/CBR-based seeking bullshit, and the player's
- // timestamp-based high-level machinery.
- if (!(flags & SEEK_ABSOLUTE) && !(flags & SEEK_HR) && priv->duration > 0)
- {
- int dir = (flags & SEEK_BACKWARD) ? -1 : 1;
- // The user is making a relative seek (translated to absolute),
- // and we try not to get the user stuck on "boundaries". So try
- // to do block based seeks, which should workaround libdvdnav's
- // terrible CBR-based seeking.
- d -= r; // relative seek amount in seconds
- d = d / (priv->duration / 1000.0) * len; // d is now in blocks
- d += pos; // absolute target in blocks
- if (dir > 0)
- d = MPMAX(d, pos + 1.0);
- if (dir < 0)
- d = MPMIN(d, pos - 1.0);
- d += 0.5; // round
- uint32_t target = MPCLAMP(d, 0, len);
- MP_VERBOSE(stream, "seek from block %lu to %lu, dir=%d\n",
- (unsigned long)pos, (unsigned long)target, dir);
- if (dvdnav_sector_search(dvdnav, target, SEEK_SET) != DVDNAV_STATUS_OK)
- break;
- } else {
- // "old" method, should be good enough for large seeks. Used for
- // hr-seeks (with fudge offset), because I fear that block-based
- // seeking might be off too far for large jumps.
- MP_VERBOSE(stream, "seek to PTS %f (%"PRId64")\n", d, tm);
- if (dvdnav_time_search(dvdnav, tm) != DVDNAV_STATUS_OK)
- break;
- }
+ MP_VERBOSE(stream, "seek to PTS %f (%"PRId64")\n", d, tm);
+ if (dvdnav_time_search(dvdnav, tm) != DVDNAV_STATUS_OK)
+ break;
stream_drop_buffers(stream);
d = dvdnav_get_current_time(dvdnav) / 90000.0f;
MP_VERBOSE(stream, "landed at: %f\n", d);
diff --git a/stream/stream_file.c b/stream/stream_file.c
index 527261e..ce9f9d9 100644
--- a/stream/stream_file.c
+++ b/stream/stream_file.c
@@ -262,9 +262,6 @@ static int open_f(stream_t *stream)
MP_INFO(stream, "Writing to stdout...\n");
p->fd = 1;
}
-#ifdef __MINGW32__
- setmode(p->fd, O_BINARY);
-#endif
p->close = false;
} else {
mode_t openmode = S_IRUSR | S_IWUSR;
@@ -298,6 +295,10 @@ static int open_f(stream_t *stream)
p->close = true;
}
+#ifdef __MINGW32__
+ setmode(p->fd, O_BINARY);
+#endif
+
off_t len = lseek(p->fd, 0, SEEK_END);
lseek(p->fd, 0, SEEK_SET);
if (len != (off_t)-1) {
diff --git a/stream/stream_lavf.c b/stream/stream_lavf.c
index a66ff94..d899629 100644
--- a/stream/stream_lavf.c
+++ b/stream/stream_lavf.c
@@ -30,7 +30,7 @@
#include "cookies.h"
#include "misc/bstr.h"
-#include "talloc.h"
+#include "mpv_talloc.h"
#define OPT_BASE_STRUCT struct stream_lavf_params
struct stream_lavf_params {
@@ -169,9 +169,9 @@ void mp_setup_av_network_options(AVDictionary **dict, struct mpv_global *global,
if (opts->network_tls_ca_file)
av_dict_set(dict, "ca_file", opts->network_tls_ca_file, 0);
if (opts->network_tls_cert_file)
- av_dict_set(dict, "cert_file", opts->network_tls_cert_file, 0);
+ av_dict_set(dict, "cert_file", opts->network_tls_cert_file, 0);
if (opts->network_tls_key_file)
- av_dict_set(dict, "key_file", opts->network_tls_key_file, 0);
+ av_dict_set(dict, "key_file", opts->network_tls_key_file, 0);
char *cust_headers = talloc_strdup(temp, "");
if (opts->network_referrer) {
cust_headers = talloc_asprintf_append(cust_headers, "Referer: %s\r\n",
diff --git a/stream/stream_memory.c b/stream/stream_memory.c
index a451e0f..4bcb860 100644
--- a/stream/stream_memory.c
+++ b/stream/stream_memory.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 <libavutil/common.h>
diff --git a/sub/ass_mp.c b/sub/ass_mp.c
index b30b996..8d40b52 100644
--- a/sub/ass_mp.c
+++ b/sub/ass_mp.c
@@ -163,3 +163,17 @@ ASS_Library *mp_ass_init(struct mpv_global *global, struct mp_log *log)
talloc_free(path);
return priv;
}
+
+void mp_ass_flush_old_events(ASS_Track *track, long long ts)
+{
+ int n = 0;
+ for (; n < track->n_events; n++) {
+ if ((track->events[n].Start + track->events[n].Duration) >= ts)
+ break;
+ ass_free_event(track, n);
+ track->n_events--;
+ }
+ for (int i = 0; n > 0 && i < track->n_events; i++) {
+ track->events[i] = track->events[i+n];
+ }
+}
diff --git a/sub/ass_mp.h b/sub/ass_mp.h
index 9831182..789a53a 100644
--- a/sub/ass_mp.h
+++ b/sub/ass_mp.h
@@ -44,6 +44,7 @@ struct mpv_global;
struct mp_osd_res;
struct osd_style_opts;
+void mp_ass_flush_old_events(ASS_Track *track, long long ts);
void mp_ass_set_style(ASS_Style *style, double res_y,
const struct osd_style_opts *opts);
diff --git a/sub/dec_sub.c b/sub/dec_sub.c
index 44c1d0a..75f5509 100644
--- a/sub/dec_sub.c
+++ b/sub/dec_sub.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>
@@ -29,50 +29,39 @@
#include "options/options.h"
#include "common/global.h"
#include "common/msg.h"
-#include "misc/charset_conv.h"
#include "osdep/threads.h"
extern const struct sd_functions sd_ass;
extern const struct sd_functions sd_lavc;
-extern const struct sd_functions sd_movtext;
-extern const struct sd_functions sd_srt;
-extern const struct sd_functions sd_microdvd;
-extern const struct sd_functions sd_lavf_srt;
-extern const struct sd_functions sd_lavc_conv;
static const struct sd_functions *const sd_list[] = {
+ &sd_lavc,
#if HAVE_LIBASS
&sd_ass,
#endif
- &sd_lavc,
- &sd_movtext,
- &sd_srt,
- &sd_lavf_srt,
- &sd_microdvd,
- &sd_lavc_conv,
NULL
};
-#define MAX_NUM_SD 3
-
struct dec_sub {
pthread_mutex_t lock;
struct mp_log *log;
+ struct mpv_global *global;
struct MPOpts *opts;
- struct sd init_sd;
- const char *charset;
+ struct demuxer *demuxer;
- struct sd *sd[MAX_NUM_SD];
- int num_sd;
-};
+ struct sh_stream *sh;
+ double last_pkt_pts;
-struct packet_list {
- struct demux_packet **packets;
- int num_packets;
-};
+ struct mp_codec_params *codec;
+ double start, end;
+ double last_vo_pts;
+ struct sd *sd;
+
+ struct demux_packet *new_segment;
+};
void sub_lock(struct dec_sub *sub)
{
@@ -84,340 +73,166 @@ void sub_unlock(struct dec_sub *sub)
pthread_mutex_unlock(&sub->lock);
}
-// Thread-safety of the returned object: all functions are thread-safe,
-// except sub_get_bitmaps() and sub_get_text(). Decoder backends (sd_*)
-// do not need to acquire locks.
-struct dec_sub *sub_create(struct mpv_global *global)
-{
- struct dec_sub *sub = talloc_zero(NULL, struct dec_sub);
- sub->log = mp_log_new(sub, global->log, "sub");
- sub->opts = global->opts;
-
- mpthread_mutex_init_recursive(&sub->lock);
-
- return sub;
-}
-
-static void sub_uninit(struct dec_sub *sub)
-{
- sub_reset(sub);
- for (int n = 0; n < sub->num_sd; n++) {
- if (sub->sd[n]->driver->uninit)
- sub->sd[n]->driver->uninit(sub->sd[n]);
- talloc_free(sub->sd[n]);
- }
- sub->num_sd = 0;
-}
-
void sub_destroy(struct dec_sub *sub)
{
if (!sub)
return;
- sub_uninit(sub);
+ sub_reset(sub);
+ sub->sd->driver->uninit(sub->sd);
+ talloc_free(sub->sd);
pthread_mutex_destroy(&sub->lock);
talloc_free(sub);
}
-bool sub_is_initialized(struct dec_sub *sub)
-{
- pthread_mutex_lock(&sub->lock);
- bool r = !!sub->num_sd;
- pthread_mutex_unlock(&sub->lock);
- return r;
-}
-
-static struct sd *sub_get_last_sd(struct dec_sub *sub)
-{
- return sub->num_sd ? sub->sd[sub->num_sd - 1] : NULL;
-}
-
-void sub_set_video_res(struct dec_sub *sub, int w, int h)
-{
- pthread_mutex_lock(&sub->lock);
- sub->init_sd.sub_video_w = w;
- sub->init_sd.sub_video_h = h;
- pthread_mutex_unlock(&sub->lock);
-}
-
-void sub_set_video_fps(struct dec_sub *sub, double fps)
-{
- pthread_mutex_lock(&sub->lock);
- sub->init_sd.video_fps = fps;
- pthread_mutex_unlock(&sub->lock);
-}
-
-void sub_set_extradata(struct dec_sub *sub, void *data, int data_len)
-{
- pthread_mutex_lock(&sub->lock);
- sub->init_sd.extradata = data_len ? talloc_memdup(sub, data, data_len) : NULL;
- sub->init_sd.extradata_len = data_len;
- pthread_mutex_unlock(&sub->lock);
-}
-
-void sub_set_ass_renderer(struct dec_sub *sub, struct ass_library *ass_library,
- struct ass_renderer *ass_renderer,
- pthread_mutex_t *ass_lock)
-{
- pthread_mutex_lock(&sub->lock);
- sub->init_sd.ass_library = ass_library;
- sub->init_sd.ass_renderer = ass_renderer;
- sub->init_sd.ass_lock = ass_lock;
- pthread_mutex_unlock(&sub->lock);
-}
-
-static void print_chain(struct dec_sub *sub)
-{
- MP_VERBOSE(sub, "Subtitle filter chain: ");
- for (int n = 0; n < sub->num_sd; n++) {
- struct sd *sd = sub->sd[n];
- MP_VERBOSE(sub, "%s%s (%s)", n > 0 ? " -> " : "",
- sd->driver->name, sd->codec);
- }
- MP_VERBOSE(sub, "\n");
-}
-
-static int sub_init_decoder(struct dec_sub *sub, struct sd *sd)
+static struct sd *init_decoder(struct dec_sub *sub)
{
- sd->driver = NULL;
for (int n = 0; sd_list[n]; n++) {
- if (sd_list[n]->supports_format(sd->codec)) {
- sd->driver = sd_list[n];
- break;
- }
- }
-
- if (!sd->driver)
- return -1;
-
- sd->log = mp_log_new(sd, sub->log, sd->driver->name);
- if (sd->driver->init(sd) < 0)
- return -1;
-
- return 0;
-}
-
-void sub_init_from_sh(struct dec_sub *sub, struct sh_stream *sh)
-{
- assert(!sub->num_sd);
- assert(sh && sh->sub);
-
- pthread_mutex_lock(&sub->lock);
-
- if (sh->extradata && !sub->init_sd.extradata)
- sub_set_extradata(sub, sh->extradata, sh->extradata_size);
- struct sd init_sd = sub->init_sd;
- init_sd.codec = sh->codec;
- init_sd.sh = sh;
-
- while (sub->num_sd < MAX_NUM_SD) {
+ const struct sd_functions *driver = sd_list[n];
struct sd *sd = talloc(NULL, struct sd);
- *sd = init_sd;
- sd->opts = sub->opts;
- if (sub_init_decoder(sub, sd) < 0) {
- talloc_free(sd);
- break;
- }
- sub->sd[sub->num_sd] = sd;
- sub->num_sd++;
- // Try adding new converters until a decoder is reached
- if (sd->driver->get_bitmaps || sd->driver->get_text) {
- print_chain(sub);
- pthread_mutex_unlock(&sub->lock);
- return;
- }
- init_sd = (struct sd) {
- .codec = sd->output_codec,
- .converted_from = sd->codec,
- .extradata = sd->output_extradata,
- .extradata_len = sd->output_extradata_len,
- .sh = sub->init_sd.sh,
- .video_fps = sub->init_sd.video_fps,
- .ass_library = sub->init_sd.ass_library,
- .ass_renderer = sub->init_sd.ass_renderer,
- .ass_lock = sub->init_sd.ass_lock,
+ *sd = (struct sd){
+ .global = sub->global,
+ .log = mp_log_new(sd, sub->log, driver->name),
+ .opts = sub->opts,
+ .driver = driver,
+ .demuxer = sub->demuxer,
+ .codec = sub->codec,
};
- }
- sub_uninit(sub);
- MP_ERR(sub, "Could not find subtitle decoder for format '%s'.\n",
- sh->codec ? sh->codec : "<unknown>");
- pthread_mutex_unlock(&sub->lock);
-}
+ if (sd->driver->init(sd) >= 0)
+ return sd;
-static struct demux_packet *get_decoded_packet(struct sd *sd)
-{
- return sd->driver->get_converted ? sd->driver->get_converted(sd) : NULL;
-}
-
-static void decode_chain(struct sd **sd, int num_sd, struct demux_packet *packet)
-{
- if (num_sd == 0)
- return;
- struct sd *dec = sd[0];
- dec->driver->decode(dec, packet);
- if (num_sd > 1) {
- while (1) {
- struct demux_packet *next = get_decoded_packet(dec);
- if (!next)
- break;
- decode_chain(sd + 1, num_sd - 1, next);
- }
+ talloc_free(sd);
}
-}
-static struct demux_packet *recode_packet(struct mp_log *log,
- struct demux_packet *in,
- const char *charset)
-{
- struct demux_packet *pkt = NULL;
- bstr in_buf = {in->buffer, in->len};
- bstr conv = mp_iconv_to_utf8(log, in_buf, charset, MP_ICONV_VERBOSE);
- if (conv.start && conv.start != in_buf.start) {
- pkt = talloc_ptrtype(NULL, pkt);
- talloc_steal(pkt, conv.start);
- *pkt = (struct demux_packet) {
- .buffer = conv.start,
- .len = conv.len,
- .pts = in->pts,
- .duration = in->duration,
- .avpacket = in->avpacket, // questionable, but gives us sidedata
- };
- }
- return pkt;
-}
-
-static void decode_chain_recode(struct dec_sub *sub, struct demux_packet *packet)
-{
- if (sub->num_sd > 0) {
- struct demux_packet *recoded = NULL;
- if (sub->charset)
- recoded = recode_packet(sub->log, packet, sub->charset);
- decode_chain(sub->sd, sub->num_sd, recoded ? recoded : packet);
- talloc_free(recoded);
- }
-}
-
-void sub_decode(struct dec_sub *sub, struct demux_packet *packet)
-{
- pthread_mutex_lock(&sub->lock);
- decode_chain_recode(sub, packet);
- pthread_mutex_unlock(&sub->lock);
-}
-
-static const char *guess_sub_cp(struct mp_log *log, void *talloc_ctx,
- struct packet_list *subs, const char *usercp)
-{
- if (!mp_charset_requires_guess(usercp))
- return usercp;
-
- // Concat all subs into a buffer. We can't probably do much better without
- // having the original data (which we don't, not anymore).
- int max_size = 2 * 1024 * 1024;
- const char *sep = "\n\n"; // In utf-16: U+0A0A GURMUKHI LETTER UU
- int sep_len = strlen(sep);
- int num_pkt = 0;
- int size = 0;
- for (int n = 0; n < subs->num_packets; n++) {
- struct demux_packet *pkt = subs->packets[n];
- if (size + pkt->len > max_size)
- break;
- size += pkt->len + sep_len;
- num_pkt++;
- }
- bstr text = {talloc_size(NULL, size), 0};
- for (int n = 0; n < num_pkt; n++) {
- struct demux_packet *pkt = subs->packets[n];
- memcpy(text.start + text.len, pkt->buffer, pkt->len);
- memcpy(text.start + text.len + pkt->len, sep, sep_len);
- text.len += pkt->len + sep_len;
- }
- const char *guess = mp_charset_guess(talloc_ctx, log, text, usercp, 0);
- talloc_free(text.start);
- return guess;
+ MP_ERR(sub, "Could not find subtitle decoder for format '%s'.\n",
+ sub->codec->codec);
+ return NULL;
}
-static void add_sub_list(struct dec_sub *sub, struct packet_list *subs)
-{
- struct sd *sd = sub_get_last_sd(sub);
- assert(sd);
-
- sd->no_remove_duplicates = true;
-
- for (int n = 0; n < subs->num_packets; n++)
- decode_chain_recode(sub, subs->packets[n]);
+// Thread-safety of the returned object: all functions are thread-safe,
+// except sub_get_bitmaps() and sub_get_text(). Decoder backends (sd_*)
+// do not need to acquire locks.
+struct dec_sub *sub_create(struct mpv_global *global, struct demuxer *demuxer,
+ struct sh_stream *sh)
+{
+ assert(demuxer && sh && sh->type == STREAM_SUB);
+
+ struct dec_sub *sub = talloc(NULL, struct dec_sub);
+ *sub = (struct dec_sub){
+ .log = mp_log_new(sub, global->log, "sub"),
+ .global = global,
+ .opts = global->opts,
+ .sh = sh,
+ .codec = sh->codec,
+ .demuxer = demuxer,
+ .last_pkt_pts = MP_NOPTS_VALUE,
+ .last_vo_pts = MP_NOPTS_VALUE,
+ .start = MP_NOPTS_VALUE,
+ .end = MP_NOPTS_VALUE,
+ };
+ mpthread_mutex_init_recursive(&sub->lock);
- // Hack for broken FFmpeg packet format: make sd_ass keep the subtitle
- // events on reset(), even if broken FFmpeg ASS packets were received
- // (from sd_lavc_conv.c). Normally, these events are removed on seek/reset,
- // but this is obviously unwanted in this case.
- if (sd->driver->fix_events)
- sd->driver->fix_events(sd);
+ sub->sd = init_decoder(sub);
+ if (sub->sd)
+ return sub;
- sd->no_remove_duplicates = false;
+ talloc_free(sub);
+ return NULL;
}
-static void add_packet(struct packet_list *subs, struct demux_packet *pkt)
-{
- pkt = demux_copy_packet(pkt);
- if (pkt) {
- talloc_steal(subs, pkt);
- MP_TARRAY_APPEND(subs, subs->packets, subs->num_packets, pkt);
+// Called locked.
+static void update_segment(struct dec_sub *sub)
+{
+ if (sub->new_segment && sub->last_vo_pts != MP_NOPTS_VALUE &&
+ sub->last_vo_pts >= sub->new_segment->start)
+ {
+ sub->codec = sub->new_segment->codec;
+ sub->start = sub->new_segment->start;
+ sub->end = sub->new_segment->end;
+ struct sd *new = init_decoder(sub);
+ if (new) {
+ sub->sd->driver->uninit(sub->sd);
+ talloc_free(sub->sd);
+ sub->sd = new;
+ } else {
+ // We'll just keep the current decoder, and feed it possibly
+ // invalid data (not our fault if it crashes or something).
+ MP_ERR(sub, "Can't change to new codec.\n");
+ }
+ sub->sd->driver->decode(sub->sd, sub->new_segment);
+ talloc_free(sub->new_segment);
+ sub->new_segment = NULL;
}
}
// Read all packets from the demuxer and decode/add them. Returns false if
// there are circumstances which makes this not possible.
-bool sub_read_all_packets(struct dec_sub *sub, struct sh_stream *sh)
+bool sub_read_all_packets(struct dec_sub *sub)
{
- assert(sh && sh->sub);
- struct MPOpts *opts = sub->opts;
-
pthread_mutex_lock(&sub->lock);
- // Converters are assumed to always accept packets in advance
- struct sd *sd = sub_get_last_sd(sub);
- if (!(sd && sd->driver->accept_packets_in_advance)) {
+ if (!sub->sd->driver->accept_packets_in_advance) {
pthread_mutex_unlock(&sub->lock);
return false;
}
- struct packet_list *subs = talloc_zero(NULL, struct packet_list);
-
for (;;) {
- struct demux_packet *pkt = demux_read_packet(sh);
+ struct demux_packet *pkt = demux_read_packet(sub->sh);
if (!pkt)
break;
- add_packet(subs, pkt);
+ sub->sd->driver->decode(sub->sd, pkt);
talloc_free(pkt);
}
- // movtext is currently the only subtitle format that has text output,
- // but binary input. Skip charset conversion (they're UTF-8 anyway).
- bool binary = sub->sd[0]->driver == &sd_movtext;
-
- if (opts->sub_cp && !sh->sub->is_utf8 && !binary)
- sub->charset = guess_sub_cp(sub->log, sub, subs, opts->sub_cp);
-
- if (sub->charset && sub->charset[0] && !mp_charset_is_utf8(sub->charset))
- MP_INFO(sub, "Using subtitle charset: %s\n", sub->charset);
-
- add_sub_list(sub, subs);
-
pthread_mutex_unlock(&sub->lock);
- talloc_free(subs);
return true;
}
-bool sub_accepts_packet_in_advance(struct dec_sub *sub)
+// Read packets from the demuxer stream passed to sub_create(). Return true if
+// enough packets were read, false if the player should wait until the demuxer
+// signals new packets available (and then should retry).
+bool sub_read_packets(struct dec_sub *sub, double video_pts)
{
- bool res = true;
+ bool r = true;
pthread_mutex_lock(&sub->lock);
- for (int n = 0; n < sub->num_sd; n++) {
- if (sub->sd[n]->driver->accepts_packet)
- res &= sub->sd[n]->driver->accepts_packet(sub->sd[n]);
+ while (1) {
+ bool read_more = true;
+ if (sub->sd->driver->accepts_packet)
+ read_more = sub->sd->driver->accepts_packet(sub->sd);
+
+ if (!read_more)
+ break;
+
+ if (sub->new_segment)
+ break;
+
+ struct demux_packet *pkt;
+ int st = demux_read_packet_async(sub->sh, &pkt);
+ // Note: "wait" (st==0) happens with non-interleaved streams only, and
+ // then we should stop the playloop until a new enough packet has been
+ // seen (or the subtitle decoder's queue is full). This does not happen
+ // for interleaved subtitle streams, which never return "wait" when
+ // reading.
+ if (st <= 0) {
+ r = st < 0 || (sub->last_pkt_pts != MP_NOPTS_VALUE &&
+ sub->last_pkt_pts > video_pts);
+ break;
+ }
+
+ sub->last_pkt_pts = pkt->pts;
+
+ if (pkt->new_segment) {
+ sub->new_segment = pkt;
+ // Note that this can be delayed to a much later point in time.
+ update_segment(sub);
+ break;
+ }
+
+ sub->sd->driver->decode(sub->sd, pkt);
+ talloc_free(pkt);
}
pthread_mutex_unlock(&sub->lock);
- return res;
+ return r;
}
// You must call sub_lock/sub_unlock if more than 1 thread access sub.
@@ -427,22 +242,17 @@ void sub_get_bitmaps(struct dec_sub *sub, struct mp_osd_res dim, double pts,
struct sub_bitmaps *res)
{
struct MPOpts *opts = sub->opts;
- struct sd *sd = sub_get_last_sd(sub);
*res = (struct sub_bitmaps) {0};
- if (sd && opts->sub_visibility) {
- if (sd->driver->get_bitmaps)
- sd->driver->get_bitmaps(sd, dim, pts, res);
- }
-}
-bool sub_has_get_text(struct dec_sub *sub)
-{
- pthread_mutex_lock(&sub->lock);
- struct sd *sd = sub_get_last_sd(sub);
- bool r = sd && sd->driver->get_text;
- pthread_mutex_unlock(&sub->lock);
- return r;
+ sub->last_vo_pts = pts;
+ update_segment(sub);
+
+ if (sub->end != MP_NOPTS_VALUE && pts >= sub->end)
+ return;
+
+ if (opts->sub_visibility && sub->sd->driver->get_bitmaps)
+ sub->sd->driver->get_bitmaps(sub->sd, dim, pts, res);
}
// See sub_get_bitmaps() for locking requirements.
@@ -452,12 +262,13 @@ char *sub_get_text(struct dec_sub *sub, double pts)
{
pthread_mutex_lock(&sub->lock);
struct MPOpts *opts = sub->opts;
- struct sd *sd = sub_get_last_sd(sub);
char *text = NULL;
- if (sd && opts->sub_visibility) {
- if (sd->driver->get_text)
- text = sd->driver->get_text(sd, pts);
- }
+
+ sub->last_vo_pts = pts;
+ update_segment(sub);
+
+ if (opts->sub_visibility && sub->sd->driver->get_text)
+ text = sub->sd->driver->get_text(sub->sd, pts);
pthread_mutex_unlock(&sub->lock);
return text;
}
@@ -465,79 +276,30 @@ char *sub_get_text(struct dec_sub *sub, double pts)
void sub_reset(struct dec_sub *sub)
{
pthread_mutex_lock(&sub->lock);
- for (int n = 0; n < sub->num_sd; n++) {
- if (sub->sd[n]->driver->reset)
- sub->sd[n]->driver->reset(sub->sd[n]);
- }
+ if (sub->sd->driver->reset)
+ sub->sd->driver->reset(sub->sd);
+ sub->last_pkt_pts = MP_NOPTS_VALUE;
+ sub->start = sub->end = MP_NOPTS_VALUE;
+ sub->last_vo_pts = MP_NOPTS_VALUE;
+ talloc_free(sub->new_segment);
+ sub->new_segment = NULL;
pthread_mutex_unlock(&sub->lock);
}
-int sub_control(struct dec_sub *sub, enum sd_ctrl cmd, void *arg)
+void sub_select(struct dec_sub *sub, bool selected)
{
- int r = CONTROL_UNKNOWN;
pthread_mutex_lock(&sub->lock);
- for (int n = 0; n < sub->num_sd; n++) {
- if (sub->sd[n]->driver->control) {
- r = sub->sd[n]->driver->control(sub->sd[n], cmd, arg);
- if (r != CONTROL_UNKNOWN)
- break;
- }
- }
+ if (sub->sd->driver->select)
+ sub->sd->driver->select(sub->sd, selected);
pthread_mutex_unlock(&sub->lock);
- return r;
-}
-
-#define MAX_PACKETS 10
-#define MAX_BYTES 10000
-
-struct sd_conv_buffer {
- struct demux_packet pkt[MAX_PACKETS];
- int num_pkt;
- int read_pkt;
- char buffer[MAX_BYTES];
- int cur_buffer;
-};
-
-void sd_conv_add_packet(struct sd *sd, void *data, int data_len, double pts,
- double duration)
-{
- if (!sd->sd_conv_buffer)
- sd->sd_conv_buffer = talloc_zero(sd, struct sd_conv_buffer);
- struct sd_conv_buffer *buf = sd->sd_conv_buffer;
- if (buf->num_pkt >= MAX_PACKETS || buf->cur_buffer + data_len + 1 > MAX_BYTES)
- goto out_of_space;
- if (buf->read_pkt == buf->num_pkt)
- sd_conv_def_reset(sd);
- assert(buf->read_pkt == 0); // no mixing of reading/adding allowed
- struct demux_packet *pkt = &buf->pkt[buf->num_pkt++];
- *pkt = (struct demux_packet) {
- .buffer = &buf->buffer[buf->cur_buffer],
- .len = data_len,
- .pts = pts,
- .duration = duration,
- };
- memcpy(pkt->buffer, data, data_len);
- pkt->buffer[data_len] = 0;
- buf->cur_buffer += data_len + 1;
- return;
-
-out_of_space:
- MP_ERR(sd, "Subtitle too big.\n");
}
-struct demux_packet *sd_conv_def_get_converted(struct sd *sd)
-{
- struct sd_conv_buffer *buf = sd->sd_conv_buffer;
- if (buf && buf->read_pkt < buf->num_pkt)
- return &buf->pkt[buf->read_pkt++];
- return NULL;
-}
-
-void sd_conv_def_reset(struct sd *sd)
+int sub_control(struct dec_sub *sub, enum sd_ctrl cmd, void *arg)
{
- struct sd_conv_buffer *buf = sd->sd_conv_buffer;
- if (buf) {
- buf->read_pkt = buf->num_pkt = 0;
- buf->cur_buffer = 0;
- }
+ int r = CONTROL_UNKNOWN;
+ pthread_mutex_lock(&sub->lock);
+ if (sub->sd->driver->control)
+ r = sub->sd->driver->control(sub->sd, cmd, arg);
+ pthread_mutex_unlock(&sub->lock);
+ return r;
}
diff --git a/sub/dec_sub.h b/sub/dec_sub.h
index 0c4d59f..b3f3052 100644
--- a/sub/dec_sub.h
+++ b/sub/dec_sub.h
@@ -3,16 +3,13 @@
#include <stdbool.h>
#include <stdint.h>
-#include <pthread.h>
#include "osd.h"
+struct demuxer;
struct sh_stream;
-struct ass_track;
struct mpv_global;
struct demux_packet;
-struct ass_library;
-struct ass_renderer;
struct dec_sub;
struct sd;
@@ -22,31 +19,22 @@ enum sd_ctrl {
SD_CTRL_SET_VIDEO_PARAMS,
SD_CTRL_GET_RESOLUTION,
SD_CTRL_SET_TOP,
+ SD_CTRL_SET_VIDEO_DEF_FPS,
};
-struct dec_sub *sub_create(struct mpv_global *global);
+struct dec_sub *sub_create(struct mpv_global *global, struct demuxer *demuxer,
+ struct sh_stream *sh);
void sub_destroy(struct dec_sub *sub);
void sub_lock(struct dec_sub *sub);
void sub_unlock(struct dec_sub *sub);
-void sub_set_video_res(struct dec_sub *sub, int w, int h);
-void sub_set_video_fps(struct dec_sub *sub, double fps);
-void sub_set_extradata(struct dec_sub *sub, void *data, int data_len);
-void sub_set_ass_renderer(struct dec_sub *sub, struct ass_library *ass_library,
- struct ass_renderer *ass_renderer,
- pthread_mutex_t *ass_lock);
-void sub_init_from_sh(struct dec_sub *sub, struct sh_stream *sh);
-
-bool sub_is_initialized(struct dec_sub *sub);
-
-bool sub_read_all_packets(struct dec_sub *sub, struct sh_stream *sh);
-bool sub_accepts_packet_in_advance(struct dec_sub *sub);
-void sub_decode(struct dec_sub *sub, struct demux_packet *packet);
+bool sub_read_all_packets(struct dec_sub *sub);
+bool sub_read_packets(struct dec_sub *sub, double video_pts);
void sub_get_bitmaps(struct dec_sub *sub, struct mp_osd_res dim, double pts,
struct sub_bitmaps *res);
-bool sub_has_get_text(struct dec_sub *sub);
char *sub_get_text(struct dec_sub *sub, double pts);
void sub_reset(struct dec_sub *sub);
+void sub_select(struct dec_sub *sub, bool selected);
int sub_control(struct dec_sub *sub, enum sd_ctrl cmd, void *arg);
diff --git a/sub/draw_bmp.c b/sub/draw_bmp.c
index da4760e..5356a8f 100644
--- a/sub/draw_bmp.c
+++ b/sub/draw_bmp.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 <stddef.h>
@@ -64,124 +64,83 @@ static bool get_sub_area(struct mp_rect bb, struct mp_image *temp,
struct sub_bitmap *sb, struct mp_image *out_area,
int *out_src_x, int *out_src_y);
-#define ACCURATE
-#define CONDITIONAL
+#define CONDITIONAL 1
-static void blend_const16_alpha(void *dst, int dst_stride, uint16_t srcp,
- uint8_t *srca, int srca_stride, uint8_t srcamul,
- int w, int h)
-{
- if (!srcamul)
- return;
- for (int y = 0; y < h; y++) {
- uint16_t *dst_r = (uint16_t *)((uint8_t *)dst + dst_stride * y);
- uint8_t *srca_r = srca + srca_stride * y;
- for (int x = 0; x < w; x++) {
- uint32_t srcap = srca_r[x];
-#ifdef CONDITIONAL
- if (!srcap)
- continue;
-#endif
- srcap *= srcamul; // now 0..65025
- dst_r[x] = (srcp * srcap + dst_r[x] * (65025 - srcap) + 32512) / 65025;
- }
+#define BLEND_CONST_ALPHA(TYPE) \
+ TYPE *dst_r = dst_rp; \
+ for (int x = 0; x < w; x++) { \
+ uint32_t srcap = srca_r[x]; \
+ if (CONDITIONAL && !srcap) continue; \
+ srcap *= srcamul; /* now 0..65025 */ \
+ dst_r[x] = (srcp * srcap + dst_r[x] * (65025 - srcap) + 32512) / 65025; \
}
-}
-static void blend_const8_alpha(void *dst, int dst_stride, uint16_t srcp,
- uint8_t *srca, int srca_stride, uint8_t srcamul,
- int w, int h)
+// dst = srcp * (srca * srcamul) + dst * (1 - (srca * srcamul))
+static void blend_const_alpha(void *dst, int dst_stride, int srcp,
+ uint8_t *srca, int srca_stride, uint8_t srcamul,
+ int w, int h, int bytes)
{
if (!srcamul)
return;
for (int y = 0; y < h; y++) {
- uint8_t *dst_r = (uint8_t *)dst + dst_stride * y;
+ void *dst_rp = (uint8_t *)dst + dst_stride * y;
uint8_t *srca_r = srca + srca_stride * y;
- for (int x = 0; x < w; x++) {
- uint32_t srcap = srca_r[x];
-#ifdef CONDITIONAL
- if (!srcap)
- continue;
-#endif
-#ifdef ACCURATE
- srcap *= srcamul; // now 0..65025
- dst_r[x] = (srcp * srcap + dst_r[x] * (65025 - srcap) + 32512) / 65025;
-#else
- srcap = (srcap * srcamul + 255) >> 8;
- dst_r[x] = (srcp * srcap + dst_r[x] * (255 - srcap) + 255) >> 8;
-#endif
+ if (bytes == 2) {
+ BLEND_CONST_ALPHA(uint16_t)
+ } else if (bytes == 1) {
+ BLEND_CONST_ALPHA(uint8_t)
}
}
}
-static void blend_const_alpha(void *dst, int dst_stride, int srcp,
- uint8_t *srca, int srca_stride, uint8_t srcamul,
- int w, int h, int bytes)
-{
- if (bytes == 2) {
- blend_const16_alpha(dst, dst_stride, srcp, srca, srca_stride, srcamul,
- w, h);
- } else if (bytes == 1) {
- blend_const8_alpha(dst, dst_stride, srcp, srca, srca_stride, srcamul,
- w, h);
+#define BLEND_SRC_ALPHA(TYPE) \
+ TYPE *dst_r = dst_rp, *src_r = src_rp; \
+ for (int x = 0; x < w; x++) { \
+ uint32_t srcap = srca_r[x]; \
+ if (CONDITIONAL && !srcap) continue; \
+ dst_r[x] = (src_r[x] * srcap + dst_r[x] * (255 - srcap) + 127) / 255; \
}
-}
-static void blend_src16_alpha(void *dst, int dst_stride, void *src,
- int src_stride, uint8_t *srca, int srca_stride,
- int w, int h)
+// dst = src * srca + dst * (1 - srca)
+static void blend_src_alpha(void *dst, int dst_stride, void *src,
+ int src_stride, uint8_t *srca, int srca_stride,
+ int w, int h, int bytes)
{
for (int y = 0; y < h; y++) {
- uint16_t *dst_r = (uint16_t *)((uint8_t *)dst + dst_stride * y);
- uint16_t *src_r = (uint16_t *)((uint8_t *)src + src_stride * y);
+ void *dst_rp = (uint8_t *)dst + dst_stride * y;
+ void *src_rp = (uint8_t *)src + src_stride * y;
uint8_t *srca_r = srca + srca_stride * y;
- for (int x = 0; x < w; x++) {
- uint32_t srcap = srca_r[x];
-#ifdef CONDITIONAL
- if (!srcap)
- continue;
-#endif
- dst_r[x] = (src_r[x] * srcap + dst_r[x] * (255 - srcap) + 127) / 255;
+ if (bytes == 2) {
+ BLEND_SRC_ALPHA(uint16_t)
+ } else if (bytes == 1) {
+ BLEND_SRC_ALPHA(uint8_t)
}
}
}
-static void blend_src8_alpha(void *dst, int dst_stride, void *src,
- int src_stride, uint8_t *srca, int srca_stride,
- int w, int h)
+#define BLEND_SRC_DST_MUL(TYPE, MAX) \
+ TYPE *dst_r = dst_rp; \
+ for (int x = 0; x < w; x++) { \
+ uint16_t srcp = src_r[x] * srcmul; /* now 0..65025 */ \
+ dst_r[x] = (srcp * (MAX) + dst_r[x] * (65025 - srcp) + 32512) / 65025; \
+ }
+
+// dst = src * srcmul + dst * (1 - src * srcmul)
+static void blend_src_dst_mul(void *dst, int dst_stride,
+ uint8_t *src, int src_stride, uint8_t srcmul,
+ int w, int h, int dst_bytes)
{
for (int y = 0; y < h; y++) {
- uint8_t *dst_r = (uint8_t *)dst + dst_stride * y;
+ void *dst_rp = (uint8_t *)dst + dst_stride * y;
uint8_t *src_r = (uint8_t *)src + src_stride * y;
- uint8_t *srca_r = srca + srca_stride * y;
- for (int x = 0; x < w; x++) {
- uint16_t srcap = srca_r[x];
-#ifdef CONDITIONAL
- if (!srcap)
- continue;
-#endif
-#ifdef ACCURATE
- dst_r[x] = (src_r[x] * srcap + dst_r[x] * (255 - srcap) + 127) / 255;
-#else
- dst_r[x] = (src_r[x] * srcap + dst_r[x] * (255 - srcap) + 255) >> 8;
-#endif
+ if (dst_bytes == 2) {
+ BLEND_SRC_DST_MUL(uint16_t, 65025)
+ } else if (dst_bytes == 1) {
+ BLEND_SRC_DST_MUL(uint8_t, 255)
}
}
}
-static void blend_src_alpha(void *dst, int dst_stride, void *src,
- int src_stride, uint8_t *srca, int srca_stride,
- int w, int h, int bytes)
-{
- if (bytes == 2) {
- blend_src16_alpha(dst, dst_stride, src, src_stride, srca, srca_stride,
- w, h);
- } else if (bytes == 1) {
- blend_src8_alpha(dst, dst_stride, src, src_stride, srca, srca_stride,
- w, h);
- }
-}
-
static void unpremultiply_and_split_BGR32(struct mp_image *img,
struct mp_image *alpha)
{
@@ -278,6 +237,10 @@ static void draw_rgba(struct mp_draw_sub_cache *cache, struct mp_rect bb,
blend_src_alpha(dst.planes[p], dst.stride[p], src, sbi->stride[p],
alpha_p, sba->stride[0], dst.w, dst.h, bytes);
}
+ if (temp->num_planes >= 4) {
+ blend_src_dst_mul(dst.planes[3], dst.stride[3], alpha_p,
+ sba->stride[0], 255, dst.w, dst.h, bytes);
+ }
part->imgs[i].i = talloc_steal(part, sbi);
part->imgs[i].a = talloc_steal(part, sba);
@@ -328,6 +291,10 @@ static void draw_ass(struct mp_draw_sub_cache *cache, struct mp_rect bb,
blend_const_alpha(dst.planes[p], dst.stride[p], color_yuv[p],
alpha_p, sb->stride, a, dst.w, dst.h, bytes);
}
+ if (temp->num_planes >= 4) {
+ blend_src_dst_mul(dst.planes[3], dst.stride[3], alpha_p,
+ sb->stride, a, dst.w, dst.h, bytes);
+ }
}
}
@@ -373,21 +340,20 @@ static bool align_bbox_for_swscale(struct mp_image *img, struct mp_rect *rc)
static void get_closest_y444_format(int imgfmt, int *out_format, int *out_bits)
{
struct mp_imgfmt_desc desc = mp_imgfmt_get_desc(imgfmt);
+ int planes = desc.flags & MP_IMGFLAG_ALPHA ? 4 : 3;
+ int bits = desc.component_bits > 8 ? 16 : 8;
if (desc.flags & MP_IMGFLAG_RGB) {
- *out_format = IMGFMT_GBRP;
- *out_bits = 8;
- return;
+ *out_format = mp_imgfmt_find(0, 0, planes, bits, MP_IMGFLAG_RGB_P);
+ if (!mp_sws_supported_format(*out_format))
+ *out_format = mp_imgfmt_find(0, 0, planes, 8, MP_IMGFLAG_RGB_P);
} else if (desc.flags & MP_IMGFLAG_YUV_P) {
- *out_format = mp_imgfmt_find_yuv_planar(0, 0, desc.num_planes,
- desc.plane_bits);
- if (*out_format && mp_sws_supported_format(*out_format)) {
- *out_bits = mp_imgfmt_get_desc(*out_format).plane_bits;
- return;
- }
+ *out_format = mp_imgfmt_find(0, 0, planes, bits, MP_IMGFLAG_YUV_P);
+ } else {
+ *out_format = 0;
}
- // fallback
- *out_format = IMGFMT_444P;
- *out_bits = 8;
+ if (!mp_sws_supported_format(*out_format))
+ *out_format = IMGFMT_444P; // generic fallback
+ *out_bits = mp_imgfmt_get_desc(*out_format).component_bits;
}
static struct part *get_cache(struct mp_draw_sub_cache *cache,
diff --git a/sub/img_convert.c b/sub/img_convert.c
index 1db73e1..d86b8f4 100644
--- a/sub/img_convert.c
+++ b/sub/img_convert.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 <string.h>
@@ -21,7 +21,7 @@
#include <libavutil/mem.h>
#include <libavutil/common.h>
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "common/common.h"
#include "img_convert.h"
diff --git a/sub/sd_lavc_conv.c b/sub/lavc_conv.c
index 8074cd8..3dd6097 100644
--- a/sub/sd_lavc_conv.c
+++ b/sub/lavc_conv.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>
@@ -21,10 +21,11 @@
#include <libavcodec/avcodec.h>
#include <libavutil/intreadwrite.h>
#include <libavutil/common.h>
+#include <libavutil/opt.h>
#include "config.h"
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "common/msg.h"
#include "common/av_common.h"
#include "misc/bstr.h"
@@ -32,8 +33,13 @@
#define HAVE_AV_WEBVTT (LIBAVCODEC_VERSION_MICRO >= 100)
-struct sd_lavc_priv {
+struct lavc_conv {
+ struct mp_log *log;
AVCodecContext *avctx;
+ char *codec;
+ char *extradata;
+ AVSubtitle cur;
+ char **cur_list;
};
static const char *get_lavc_format(const char *format)
@@ -44,15 +50,6 @@ static const char *get_lavc_format(const char *format)
return format;
}
-static bool supports_format(const char *format)
-{
- format = get_lavc_format(format);
- enum AVCodecID cid = mp_codec_to_av_codec_id(format);
- AVCodec *codec = avcodec_find_decoder(cid);
- const AVCodecDescriptor *desc = avcodec_descriptor_get(cid);
- return codec && desc && desc->type == AVMEDIA_TYPE_SUBTITLE;
-}
-
// Disable style definitions generated by the libavcodec converter.
// We always want the user defined style instead.
static void disable_styles(bstr header)
@@ -66,40 +63,51 @@ static void disable_styles(bstr header)
}
}
-static int init(struct sd *sd)
+struct lavc_conv *lavc_conv_create(struct mp_log *log, const char *codec_name,
+ char *extradata, int extradata_len)
{
- struct sd_lavc_priv *priv = talloc_zero(NULL, struct sd_lavc_priv);
+ struct lavc_conv *priv = talloc_zero(NULL, struct lavc_conv);
+ priv->log = log;
+ priv->cur_list = talloc_array(priv, char*, 0);
+ priv->codec = talloc_strdup(priv, codec_name);
AVCodecContext *avctx = NULL;
- const char *fmt = get_lavc_format(sd->codec);
+ AVDictionary *opts = NULL;
+ const char *fmt = get_lavc_format(priv->codec);
AVCodec *codec = avcodec_find_decoder(mp_codec_to_av_codec_id(fmt));
if (!codec)
goto error;
avctx = avcodec_alloc_context3(codec);
if (!avctx)
goto error;
- avctx->extradata_size = sd->extradata_len;
- avctx->extradata = sd->extradata;
- if (avcodec_open2(avctx, codec, NULL) < 0)
+ avctx->extradata_size = extradata_len;
+ avctx->extradata = av_malloc(extradata_len);
+ if (!avctx->extradata)
+ goto error;
+ memcpy(avctx->extradata, extradata, extradata_len);
+ if (strcmp(codec_name, "eia_608") == 0)
+ av_dict_set(&opts, "real_time", "1", 0);
+ if (avcodec_open2(avctx, codec, &opts) < 0)
goto error;
+ av_dict_free(&opts);
// Documented as "set by libavcodec", but there is no other way
avctx->time_base = (AVRational) {1, 1000};
priv->avctx = avctx;
- sd->priv = priv;
- sd->output_codec = "ssa";
- sd->output_extradata = avctx->subtitle_header;
- sd->output_extradata_len = avctx->subtitle_header_size;
- if (sd->output_extradata) {
- sd->output_extradata = talloc_memdup(sd, sd->output_extradata,
- sd->output_extradata_len);
- disable_styles((bstr){sd->output_extradata, sd->output_extradata_len});
- }
- return 0;
+ priv->extradata = talloc_strndup(priv, avctx->subtitle_header,
+ avctx->subtitle_header_size);
+ disable_styles(bstr0(priv->extradata));
+ return priv;
error:
- MP_FATAL(sd, "Could not open libavcodec subtitle converter\n");
+ MP_FATAL(priv, "Could not open libavcodec subtitle converter\n");
+ av_dict_free(&opts);
av_free(avctx);
talloc_free(priv);
- return -1;
+ return NULL;
+}
+
+char *lavc_conv_get_extradata(struct lavc_conv *priv)
+{
+ return priv->extradata;
}
#if HAVE_AV_WEBVTT
@@ -217,71 +225,55 @@ static int parse_webvtt(AVPacket *in, AVPacket *pkt)
#endif
-static void decode(struct sd *sd, struct demux_packet *packet)
+// Return a NULL-terminated list of ASS event lines.
+char **lavc_conv_decode(struct lavc_conv *priv, struct demux_packet *packet)
{
- struct sd_lavc_priv *priv = sd->priv;
AVCodecContext *avctx = priv->avctx;
- AVSubtitle sub = {0};
AVPacket pkt;
AVPacket parsed_pkt = {0};
int ret, got_sub;
+ int num_cur = 0;
+
+ avsubtitle_free(&priv->cur);
mp_set_av_packet(&pkt, packet, &avctx->time_base);
- if (sd->codec && strcmp(sd->codec, "webvtt-webm") == 0) {
+ if (strcmp(priv->codec, "webvtt-webm") == 0) {
if (parse_webvtt(&pkt, &parsed_pkt) < 0) {
- MP_ERR(sd, "Error parsing subtitle\n");
+ MP_ERR(priv, "Error parsing subtitle\n");
goto done;
}
pkt = parsed_pkt;
}
- ret = avcodec_decode_subtitle2(avctx, &sub, &got_sub, &pkt);
+ ret = avcodec_decode_subtitle2(avctx, &priv->cur, &got_sub, &pkt);
if (ret < 0) {
- MP_ERR(sd, "Error decoding subtitle\n");
+ MP_ERR(priv, "Error decoding subtitle\n");
} else if (got_sub) {
- for (int i = 0; i < sub.num_rects; i++) {
- if (sub.rects[i]->w > 0 && sub.rects[i]->h > 0)
- MP_WARN(sd, "Ignoring bitmap subtitle.\n");
- char *ass_line = sub.rects[i]->ass;
+ for (int i = 0; i < priv->cur.num_rects; i++) {
+ if (priv->cur.rects[i]->w > 0 && priv->cur.rects[i]->h > 0)
+ MP_WARN(priv, "Ignoring bitmap subtitle.\n");
+ char *ass_line = priv->cur.rects[i]->ass;
if (!ass_line)
- break;
- // This might contain embedded timestamps, using the "old" ffmpeg
- // ASS packet format, in which case pts/duration might be ignored
- // at a later point.
- sd_conv_add_packet(sd, ass_line, strlen(ass_line),
- packet->pts, packet->duration);
+ continue;
+ MP_TARRAY_APPEND(priv, priv->cur_list, num_cur, ass_line);
}
}
done:
- avsubtitle_free(&sub);
av_packet_unref(&parsed_pkt);
+ MP_TARRAY_APPEND(priv, priv->cur_list, num_cur, NULL);
+ return priv->cur_list;
}
-static void reset(struct sd *sd)
+void lavc_conv_reset(struct lavc_conv *priv)
{
- struct sd_lavc_priv *priv = sd->priv;
-
avcodec_flush_buffers(priv->avctx);
- sd_conv_def_reset(sd);
}
-static void uninit(struct sd *sd)
+void lavc_conv_uninit(struct lavc_conv *priv)
{
- struct sd_lavc_priv *priv = sd->priv;
-
- avcodec_close(priv->avctx);
- av_free(priv->avctx);
+ avsubtitle_free(&priv->cur);
+ avcodec_free_context(&priv->avctx);
talloc_free(priv);
}
-
-const struct sd_functions sd_lavc_conv = {
- .name = "lavc_conv",
- .supports_format = supports_format,
- .init = init,
- .decode = decode,
- .get_converted = sd_conv_def_get_converted,
- .reset = reset,
- .uninit = uninit,
-};
diff --git a/sub/osd.c b/sub/osd.c
index 2d11b80..fd82192 100644
--- a/sub/osd.c
+++ b/sub/osd.c
@@ -28,10 +28,12 @@
#include "osdep/timer.h"
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "options/options.h"
#include "common/global.h"
#include "common/msg.h"
+#include "player/client.h"
+#include "player/command.h"
#include "osd.h"
#include "osd_state.h"
#include "dec_sub.h"
@@ -238,9 +240,12 @@ static void render_object(struct osd_state *osd, struct osd_object *obj,
*out_imgs = (struct sub_bitmaps) {0};
- if (!osd_res_equals(res, obj->vo_res))
+ if (!osd_res_equals(res, obj->vo_res)) {
+ obj->vo_res = res;
obj->force_redraw = true;
- obj->vo_res = res;
+ mp_client_broadcast_event(mp_client_api_get_core(osd->global->client_api),
+ MP_EVENT_WIN_RESIZE, NULL);
+ }
if (obj->type == OSDTYPE_SUB || obj->type == OSDTYPE_SUB2) {
if (obj->sub) {
@@ -388,13 +393,10 @@ void osd_draw_on_image_p(struct osd_state *osd, struct mp_osd_res res,
// ratio if the image does not have a 1:1 pixel aspect ratio.
struct mp_osd_res osd_res_from_image_params(const struct mp_image_params *p)
{
- double sar = (double)p->w / p->h;
- double dar = (double)p->d_w / p->d_h;
-
return (struct mp_osd_res) {
.w = p->w,
.h = p->h,
- .display_par = sar / dar,
+ .display_par = p->p_h / (double)p->p_w,
};
}
diff --git a/sub/osd_dummy.c b/sub/osd_dummy.c
index 5a33321..5c50569 100644
--- a/sub/osd_dummy.c
+++ b/sub/osd_dummy.c
@@ -3,7 +3,7 @@
#include <string.h>
#include "config.h"
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "osd.h"
const char *const osd_ass_0 = "";
diff --git a/sub/osd_libass.c b/sub/osd_libass.c
index 6bb1257..eed2822 100644
--- a/sub/osd_libass.c
+++ b/sub/osd_libass.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 <stdio.h>
@@ -24,7 +24,7 @@
#include "config.h"
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "misc/bstr.h"
#include "common/common.h"
#include "common/msg.h"
diff --git a/sub/sd.h b/sub/sd.h
index f7f0d32..b142654 100644
--- a/sub/sd.h
+++ b/sub/sd.h
@@ -10,76 +10,40 @@
#define SUB_GAP_KEEP 0.4
struct sd {
+ struct mpv_global *global;
struct mp_log *log;
struct MPOpts *opts;
const struct sd_functions *driver;
void *priv;
- const char *codec;
-
- // Extra header data passed from demuxer
- char *extradata;
- int extradata_len;
-
- struct sh_stream *sh;
-
- // Set to !=NULL if the input packets are being converted from another
- // format.
- const char *converted_from;
-
- // Video resolution used for subtitle decoding. Doesn't necessarily match
- // the resolution of the VO, nor does it have to be the OSD resolution.
- int sub_video_w, sub_video_h;
-
- double video_fps;
-
- // Shared renderer for ASS - done to avoid reloading embedded fonts.
- struct ass_library *ass_library;
- struct ass_renderer *ass_renderer;
- pthread_mutex_t *ass_lock;
-
- // If false, try to remove multiple subtitles.
- // (Only for decoders which have accept_packets_in_advance set.)
- bool no_remove_duplicates;
-
- // Set by sub converter
- const char *output_codec;
- char *output_extradata;
- int output_extradata_len;
-
- // Internal buffer for sd_conv_* functions
- struct sd_conv_buffer *sd_conv_buffer;
+ struct demuxer *demuxer;
+ struct mp_codec_params *codec;
};
struct sd_functions {
const char *name;
bool accept_packets_in_advance;
- bool (*supports_format)(const char *format);
int (*init)(struct sd *sd);
void (*decode)(struct sd *sd, struct demux_packet *packet);
void (*reset)(struct sd *sd);
+ void (*select)(struct sd *sd, bool selected);
void (*uninit)(struct sd *sd);
bool (*accepts_packet)(struct sd *sd); // implicit default if NULL: true
- void (*fix_events)(struct sd *sd);
int (*control)(struct sd *sd, enum sd_ctrl cmd, void *arg);
- // decoder
void (*get_bitmaps)(struct sd *sd, struct mp_osd_res dim, double pts,
struct sub_bitmaps *res);
char *(*get_text)(struct sd *sd, double pts);
-
- // converter
- struct demux_packet *(*get_converted)(struct sd *sd);
};
-void sd_conv_add_packet(struct sd *sd, void *data, int data_len, double pts,
- double duration);
-struct demux_packet *sd_conv_def_get_converted(struct sd *sd);
-void sd_conv_def_reset(struct sd *sd);
-void sd_conv_def_uninit(struct sd *sd);
-
-#define SD_MAX_LINE_LEN 1000
+struct lavc_conv;
+struct lavc_conv *lavc_conv_create(struct mp_log *log, const char *codec_name,
+ char *extradata, int extradata_len);
+char *lavc_conv_get_extradata(struct lavc_conv *priv);
+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);
#endif
diff --git a/sub/sd_ass.c b/sub/sd_ass.c
index 2948475..5c56d3e 100644
--- a/sub/sd_ass.c
+++ b/sub/sd_ass.c
@@ -24,12 +24,12 @@
#include <libavutil/common.h>
#include <ass/ass.h>
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "options/options.h"
#include "common/common.h"
#include "common/msg.h"
-#include "demux/stheader.h"
+#include "demux/demux.h"
#include "video/csputils.h"
#include "video/mp_image.h"
#include "dec_sub.h"
@@ -37,17 +37,21 @@
#include "sd.h"
struct sd_ass_priv {
+ struct ass_library *ass_library;
+ struct ass_renderer *ass_renderer;
struct ass_track *ass_track;
struct ass_track *shadow_track; // for --sub-ass=no rendering
bool is_converted;
+ struct lavc_conv *converter;
bool on_top;
struct sub_bitmap *parts;
- bool flush_on_seek;
- int extend_event;
char last_text[500];
struct mp_image_params video_params;
struct mp_image_params last_params;
- double sub_speed;
+ double sub_speed, video_fps, frame_fps;
+ int64_t *seen_packets;
+ int num_seen_packets;
+ bool duration_unknown;
};
static void mangle_colors(struct sd *sd, struct sub_bitmaps *parts);
@@ -77,128 +81,195 @@ static void mp_ass_add_default_styles(ASS_Track *track, struct MPOpts *opts)
ass_process_force_style(track);
}
-static bool supports_format(const char *format)
+static const char *const font_mimetypes[] = {
+ "application/x-truetype-font",
+ "application/vnd.ms-opentype",
+ "application/x-font-ttf",
+ "application/x-font", // probably incorrect
+ NULL
+};
+
+static const char *const font_exts[] = {".ttf", ".ttc", ".otf", NULL};
+
+static bool attachment_is_font(struct mp_log *log, struct demux_attachment *f)
{
- // ass-text is produced by converters and the subreader.c ssa parser; this
- // format has ASS tags, but doesn't start with any prelude, nor does it
- // have extradata.
- return format && (strcmp(format, "ass") == 0 ||
- strcmp(format, "ssa") == 0 ||
- strcmp(format, "ass-text") == 0);
+ if (!f->name || !f->type || !f->data || !f->data_size)
+ return false;
+ for (int n = 0; font_mimetypes[n]; n++) {
+ if (strcmp(font_mimetypes[n], f->type) == 0)
+ return true;
+ }
+ // fallback: match against file extension
+ char *ext = strlen(f->name) > 4 ? f->name + strlen(f->name) - 4 : "";
+ for (int n = 0; font_exts[n]; n++) {
+ if (strcasecmp(ext, font_exts[n]) == 0) {
+ mp_warn(log, "Loading font attachment '%s' with MIME type %s. "
+ "Assuming this is a broken Matroska file, which was "
+ "muxed without setting a correct font MIME type.\n",
+ f->name, f->type);
+ return true;
+ }
+ }
+ return false;
}
-static int init(struct sd *sd)
+static void add_subtitle_fonts(struct sd *sd)
+{
+ struct sd_ass_priv *ctx = sd->priv;
+ struct MPOpts *opts = sd->opts;
+ if (!opts->ass_enabled || !sd->demuxer)
+ return;
+ for (int i = 0; i < sd->demuxer->num_attachments; i++) {
+ struct demux_attachment *f = &sd->demuxer->attachments[i];
+ if (opts->use_embedded_fonts && attachment_is_font(sd->log, f))
+ ass_add_font(ctx->ass_library, f->name, f->data, f->data_size);
+ }
+}
+
+static void enable_output(struct sd *sd, bool enable)
+{
+ struct sd_ass_priv *ctx = sd->priv;
+ if (enable == !!ctx->ass_renderer)
+ return;
+ if (ctx->ass_renderer) {
+ ass_renderer_done(ctx->ass_renderer);
+ ctx->ass_renderer = NULL;
+ } else {
+ ctx->ass_renderer = ass_renderer_init(ctx->ass_library);
+
+ mp_ass_configure_fonts(ctx->ass_renderer, sd->opts->sub_text_style,
+ sd->global, sd->log);
+ }
+}
+
+static void update_subtitle_speed(struct sd *sd)
{
struct MPOpts *opts = sd->opts;
- if (!sd->ass_library || !sd->ass_renderer || !sd->ass_lock || !sd->codec)
- return -1;
+ struct sd_ass_priv *ctx = sd->priv;
+ ctx->sub_speed = 1.0;
+
+ if (ctx->video_fps > 0 && ctx->frame_fps > 0) {
+ MP_VERBOSE(sd, "Frame based format, dummy FPS: %f, video FPS: %f\n",
+ ctx->frame_fps, ctx->video_fps);
+ ctx->sub_speed *= ctx->frame_fps / ctx->video_fps;
+ }
+
+ if (opts->sub_fps && ctx->video_fps)
+ ctx->sub_speed *= opts->sub_fps / ctx->video_fps;
+
+ ctx->sub_speed *= opts->sub_speed;
+}
- struct sd_ass_priv *ctx = talloc_zero(NULL, struct sd_ass_priv);
+static int init(struct sd *sd)
+{
+ struct MPOpts *opts = sd->opts;
+ struct sd_ass_priv *ctx = talloc_zero(sd, struct sd_ass_priv);
sd->priv = ctx;
- ctx->extend_event = -1;
- ctx->is_converted = sd->converted_from != NULL;
+ char *extradata = sd->codec->extradata;
+ int extradata_size = sd->codec->extradata_size;
+
+ if (strcmp(sd->codec->codec, "ass") != 0) {
+ ctx->is_converted = true;
+ ctx->converter = lavc_conv_create(sd->log, sd->codec->codec, extradata,
+ extradata_size);
+ if (!ctx->converter)
+ return -1;
+ extradata = lavc_conv_get_extradata(ctx->converter);
+ extradata_size = extradata ? strlen(extradata) : 0;
+
+ if (strcmp(sd->codec->codec, "eia_608") == 0)
+ ctx->duration_unknown = 1;
+ }
+
+ ctx->ass_library = mp_ass_init(sd->global, sd->log);
+
+ add_subtitle_fonts(sd);
- pthread_mutex_lock(sd->ass_lock);
+ if (opts->ass_style_override)
+ ass_set_style_overrides(ctx->ass_library, opts->ass_force_style_list);
- ctx->ass_track = ass_new_track(sd->ass_library);
+ ctx->ass_track = ass_new_track(ctx->ass_library);
if (!ctx->is_converted)
ctx->ass_track->track_type = TRACK_TYPE_ASS;
- ctx->shadow_track = ass_new_track(sd->ass_library);
+ ctx->shadow_track = ass_new_track(ctx->ass_library);
ctx->shadow_track->PlayResX = 384;
ctx->shadow_track->PlayResY = 288;
mp_ass_add_default_styles(ctx->shadow_track, opts);
- if (sd->extradata) {
- ass_process_codec_private(ctx->ass_track, sd->extradata,
- sd->extradata_len);
- }
+ if (extradata)
+ ass_process_codec_private(ctx->ass_track, extradata, extradata_size);
mp_ass_add_default_styles(ctx->ass_track, opts);
- pthread_mutex_unlock(sd->ass_lock);
-
- ctx->sub_speed = 1.0;
-
- if (sd->video_fps && sd->sh && sd->sh->sub->frame_based > 0) {
- MP_VERBOSE(sd, "Frame based format, dummy FPS: %f, video FPS: %f\n",
- sd->sh->sub->frame_based, sd->video_fps);
- ctx->sub_speed *= sd->sh->sub->frame_based / sd->video_fps;
- }
+#if LIBASS_VERSION >= 0x01302000
+ ass_set_check_readorder(ctx->ass_track, sd->opts->sub_clear_on_seek ? 0 : 1);
+#endif
- if (opts->sub_fps && sd->video_fps)
- ctx->sub_speed *= opts->sub_fps / sd->video_fps;
+ ctx->frame_fps = sd->codec->frame_based;
+ update_subtitle_speed(sd);
- ctx->sub_speed *= opts->sub_speed;
+ enable_output(sd, true);
return 0;
}
+// Test if the packet with the given file position (used as unique ID) was
+// already consumed. Return false if the packet is new (and add it to the
+// internal list), and return true if it was already seen.
+static bool check_packet_seen(struct sd *sd, int64_t pos)
+{
+ struct sd_ass_priv *priv = sd->priv;
+ int a = 0;
+ int b = priv->num_seen_packets;
+ while (a < b) {
+ int mid = a + (b - a) / 2;
+ int64_t val = priv->seen_packets[mid];
+ if (pos == val)
+ return true;
+ if (pos > val) {
+ a = mid + 1;
+ } else {
+ b = mid;
+ }
+ }
+ MP_TARRAY_INSERT_AT(priv, priv->seen_packets, priv->num_seen_packets, a, pos);
+ return false;
+}
+
static void decode(struct sd *sd, struct demux_packet *packet)
{
struct sd_ass_priv *ctx = sd->priv;
ASS_Track *track = ctx->ass_track;
- long long ipts = packet->pts * 1000 + 0.5;
- long long iduration = packet->duration * 1000 + 0.5;
- if (strcmp(sd->codec, "ass") == 0) {
- ass_process_chunk(track, packet->buffer, packet->len, ipts, iduration);
- return;
- } else if (strcmp(sd->codec, "ssa") == 0) {
- // broken ffmpeg ASS packet format
- ctx->flush_on_seek = true;
- ass_process_data(track, packet->buffer, packet->len);
- return;
- }
-
- // plaintext subs
- if (packet->pts == MP_NOPTS_VALUE) {
- MP_WARN(sd, "Subtitle without pts, ignored\n");
- return;
- }
- if (ctx->extend_event >= 0 && ctx->extend_event < track->n_events) {
- ASS_Event *event = &track->events[ctx->extend_event];
- if (event->Start <= ipts)
- event->Duration = ipts - event->Start;
- ctx->extend_event = -1;
- }
-
- unsigned char *text = packet->buffer;
- if (!sd->no_remove_duplicates) {
- for (int i = 0; i < track->n_events; i++) {
- if (track->events[i].Start == ipts
- && (track->events[i].Duration == iduration)
- && strcmp(track->events[i].Text, text) == 0)
- return; // We've already added this subtitle
+ if (ctx->converter) {
+ if (!sd->opts->sub_clear_on_seek && packet->pos >= 0 &&
+ check_packet_seen(sd, packet->pos))
+ return;
+ 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]));
+ if (ctx->duration_unknown) {
+ for (int n = 0; n < track->n_events - 1; n++) {
+ track->events[n].Duration = track->events[n + 1].Start -
+ track->events[n].Start;
+ }
}
+ } 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,
+ lrint(packet->pts * 1000),
+ lrint(packet->duration * 1000));
}
- int eid = ass_alloc_event(track);
- ASS_Event *event = track->events + eid;
-
- if (packet->duration == 0) {
- MP_WARN(sd, "Subtitle without duration or "
- "duration set to 0 at pts %f.\n", packet->pts);
- }
- if (packet->duration < 0) {
- // Assume unknown duration. The FFmpeg API is very unclear about this.
- MP_WARN(sd, "Assuming subtitle without duration at pts %f\n", packet->pts);
- // _If_ there's a next subtitle, the duration will be adjusted again.
- // If not, show it forever.
- iduration = INT_MAX;
- ctx->extend_event = eid;
- }
-
- event->Start = ipts;
- event->Duration = iduration;
- event->Style = track->default_style;
- event->Text = strdup(text);
}
static void configure_ass(struct sd *sd, struct mp_osd_res *dim,
bool converted, ASS_Track *track)
{
struct MPOpts *opts = sd->opts;
- ASS_Renderer *priv = sd->ass_renderer;
+ struct sd_ass_priv *ctx = sd->priv;
+ ASS_Renderer *priv = ctx->ass_renderer;
ass_set_frame_size(priv, dim->w, dim->h);
ass_set_margins(priv, dim->mt, dim->mb, dim->ml, dim->mr);
@@ -343,23 +414,19 @@ static void get_bitmaps(struct sd *sd, struct mp_osd_res dim, double pts,
bool no_ass = !opts->ass_enabled || ctx->on_top;
bool converted = ctx->is_converted || no_ass;
ASS_Track *track = no_ass ? ctx->shadow_track : ctx->ass_track;
+ ASS_Renderer *renderer = ctx->ass_renderer;
- if (pts == MP_NOPTS_VALUE || !sd->ass_renderer)
+ if (pts == MP_NOPTS_VALUE || !renderer)
return;
- pthread_mutex_lock(sd->ass_lock);
-
- ASS_Renderer *renderer = sd->ass_renderer;
double scale = dim.display_par;
if (!converted && (!opts->ass_style_override ||
opts->ass_vsfilter_aspect_compat))
{
// Let's use the original video PAR for vsfilter compatibility:
- double par = scale
- * (ctx->video_params.d_w / (double)ctx->video_params.d_h)
- / (ctx->video_params.w / (double)ctx->video_params.h);
+ double par = ctx->video_params.p_w / (double)ctx->video_params.p_h;
if (isnormal(par))
- scale = par;
+ scale *= par;
}
configure_ass(sd, &dim, converted, track);
ass_set_pixel_aspect(renderer, scale);
@@ -370,16 +437,17 @@ static void get_bitmaps(struct sd *sd, struct mp_osd_res dim, double pts,
} else {
ass_set_storage_size(renderer, 0, 0);
}
+ long long ts = find_timestamp(sd, pts);
+ if (ctx->duration_unknown && pts != MP_NOPTS_VALUE) {
+ mp_ass_flush_old_events(track, ts);
+ }
if (no_ass)
fill_plaintext(sd, pts);
- long long ts = find_timestamp(sd, pts);
mp_ass_render_frame(renderer, track, ts, &ctx->parts, res);
talloc_steal(ctx, ctx->parts);
if (!converted)
mangle_colors(sd, res);
-
- pthread_mutex_unlock(sd->ass_lock);
}
struct buf {
@@ -510,7 +578,7 @@ static void fill_plaintext(struct sd *sd, double pts)
text++;
}
- if (!dst.start || !dst.start[0])
+ if (!dst.start)
return;
int n = ass_alloc_event(track);
@@ -522,30 +590,31 @@ static void fill_plaintext(struct sd *sd, double pts)
if (track->default_style < track->n_styles)
track->styles[track->default_style].Alignment = ctx->on_top ? 6 : 2;
-}
-static void fix_events(struct sd *sd)
-{
- struct sd_ass_priv *ctx = sd->priv;
- ctx->flush_on_seek = false;
+ talloc_free(dst.start);
}
static void reset(struct sd *sd)
{
struct sd_ass_priv *ctx = sd->priv;
- if (ctx->flush_on_seek || sd->opts->sub_clear_on_seek) {
+ if (sd->opts->sub_clear_on_seek) {
ass_flush_events(ctx->ass_track);
- ctx->extend_event = -1;
+ ctx->num_seen_packets = 0;
}
- ctx->flush_on_seek = false;
+ if (ctx->converter)
+ lavc_conv_reset(ctx->converter);
}
static void uninit(struct sd *sd)
{
struct sd_ass_priv *ctx = sd->priv;
+ if (ctx->converter)
+ lavc_conv_uninit(ctx->converter);
ass_free_track(ctx->ass_track);
- talloc_free(ctx);
+ ass_free_track(ctx->shadow_track);
+ enable_output(sd, false);
+ ass_library_done(ctx->ass_library);
}
static int control(struct sd *sd, enum sd_ctrl cmd, void *arg)
@@ -567,6 +636,10 @@ static int control(struct sd *sd, enum sd_ctrl cmd, void *arg)
case SD_CTRL_SET_TOP:
ctx->on_top = *(bool *)arg;
return CONTROL_OK;
+ case SD_CTRL_SET_VIDEO_DEF_FPS:
+ ctx->video_fps = *(double *)arg;
+ update_subtitle_speed(sd);
+ return CONTROL_OK;
default:
return CONTROL_UNKNOWN;
}
@@ -575,14 +648,13 @@ static int control(struct sd *sd, enum sd_ctrl cmd, void *arg)
const struct sd_functions sd_ass = {
.name = "ass",
.accept_packets_in_advance = true,
- .supports_format = supports_format,
.init = init,
.decode = decode,
.get_bitmaps = get_bitmaps,
.get_text = get_text,
- .fix_events = fix_events,
.control = control,
.reset = reset,
+ .select = enable_output,
.uninit = uninit,
};
diff --git a/sub/sd_lavc.c b/sub/sd_lavc.c
index 563ac55..4fbabaf 100644
--- a/sub/sd_lavc.c
+++ b/sub/sd_lavc.c
@@ -25,9 +25,10 @@
#include "config.h"
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "common/msg.h"
#include "common/av_common.h"
+#include "demux/stheader.h"
#include "options/options.h"
#include "video/mp_image.h"
#include "sd.h"
@@ -63,21 +64,6 @@ struct sd_lavc_priv {
int num_seekpoints;
};
-static bool supports_format(const char *format)
-{
- enum AVCodecID cid = mp_codec_to_av_codec_id(format);
- // Supported codecs must be known to decode to paletted bitmaps
- switch (cid) {
- case AV_CODEC_ID_DVB_SUBTITLE:
- case AV_CODEC_ID_HDMV_PGS_SUBTITLE:
- case AV_CODEC_ID_XSUB:
- case AV_CODEC_ID_DVD_SUBTITLE:
- return true;
- default:
- return false;
- }
-}
-
static void get_resolution(struct sd *sd, int wh[2])
{
struct sd_lavc_priv *priv = sd->priv;
@@ -109,8 +95,20 @@ static void get_resolution(struct sd *sd, int wh[2])
static int init(struct sd *sd)
{
+ enum AVCodecID cid = mp_codec_to_av_codec_id(sd->codec->codec);
+
+ // Supported codecs must be known to decode to paletted bitmaps
+ switch (cid) {
+ case AV_CODEC_ID_DVB_SUBTITLE:
+ case AV_CODEC_ID_HDMV_PGS_SUBTITLE:
+ case AV_CODEC_ID_XSUB:
+ case AV_CODEC_ID_DVD_SUBTITLE:
+ break;
+ default:
+ return -1;
+ }
+
struct sd_lavc_priv *priv = talloc_zero(NULL, struct sd_lavc_priv);
- enum AVCodecID cid = mp_codec_to_av_codec_id(sd->codec);
AVCodecContext *ctx = NULL;
AVCodec *sub_codec = avcodec_find_decoder(cid);
if (!sub_codec)
@@ -118,7 +116,7 @@ static int init(struct sd *sd)
ctx = avcodec_alloc_context3(sub_codec);
if (!ctx)
goto error;
- mp_lavc_set_extradata(ctx, sd->extradata, sd->extradata_len);
+ mp_lavc_set_extradata(ctx, sd->codec->extradata, sd->codec->extradata_size);
if (avcodec_open2(ctx, sub_codec, NULL) < 0)
goto error;
priv->avctx = ctx;
@@ -317,11 +315,10 @@ static void get_bitmaps(struct sd *sd, struct mp_osd_res d, double pts,
double video_par = 0;
if (priv->avctx->codec_id == AV_CODEC_ID_DVD_SUBTITLE &&
- opts->stretch_dvd_subs) {
+ opts->stretch_dvd_subs)
+ {
// For DVD subs, try to keep the subtitle PAR at display PAR.
- double par =
- (priv->video_params.d_w / (double)priv->video_params.d_h)
- / (priv->video_params.w / (double)priv->video_params.h);
+ double par = priv->video_params.p_w / (double)priv->video_params.p_h;
if (isnormal(par))
video_par = par;
}
@@ -401,15 +398,15 @@ static double step_sub(struct sd *sd, double now, int movement)
struct sd_lavc_priv *priv = sd->priv;
int best = -1;
double target = now;
- int direction = movement > 0 ? 1 : -1;
+ int direction = (movement > 0 ? 1 : -1) * !!movement;
- if (movement == 0 || priv->num_seekpoints == 0)
+ if (priv->num_seekpoints == 0)
return MP_NOPTS_VALUE;
qsort(priv->seekpoints, priv->num_seekpoints, sizeof(priv->seekpoints[0]),
compare_seekpoint);
- while (movement) {
+ do {
int closest = -1;
double closest_time = 0;
for (int i = 0; i < priv->num_seekpoints; i++) {
@@ -423,13 +420,20 @@ static double step_sub(struct sd *sd, double now, int movement)
closest_time = end;
}
}
- } else {
+ } else if (direction > 0) {
if (start > target) {
if (closest < 0 || start < closest_time) {
closest = i;
closest_time = start;
}
}
+ } else {
+ if (start < target) {
+ if (closest < 0 || start >= closest_time) {
+ closest = i;
+ closest_time = start;
+ }
+ }
}
}
if (closest < 0)
@@ -437,7 +441,7 @@ static double step_sub(struct sd *sd, double now, int movement)
target = closest_time + direction;
best = closest;
movement -= direction;
- }
+ } while (movement);
return best < 0 ? 0 : priv->seekpoints[best].pts - now;
}
@@ -467,7 +471,6 @@ static int control(struct sd *sd, enum sd_ctrl cmd, void *arg)
const struct sd_functions sd_lavc = {
.name = "lavc",
- .supports_format = supports_format,
.init = init,
.decode = decode,
.get_bitmaps = get_bitmaps,
diff --git a/sub/sd_lavf_srt.c b/sub/sd_lavf_srt.c
deleted file mode 100644
index 8f1f7de..0000000
--- a/sub/sd_lavf_srt.c
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * This file is part of mpv.
- *
- * SRT timestamp parsing code lifted from FFmpeg srtdec.c (LGPL).
- *
- * 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 <stdlib.h>
-#include <inttypes.h>
-#include <assert.h>
-
-#include "misc/bstr.h"
-#include "sd.h"
-
-/*
- * Background:
- *
- * Libav's .srt demuxer outputs packets that contain parts of the subtitle
- * event header. Also, the packet duration is not set (they don't parse it
- * on the demuxer side). As a result, the srt demuxer is useless.
- *
- * However, we can fix it by parsing the header, which spares us from writing
- * a full SRT demuxer.
- *
- * Newer versions of FFmpeg do not have this problem. To avoid compatibility
- * problems, they changed the codec name from "srt" to "subrip".
- *
- * Summary: this is a hack for broken SRT stuff in Libav.
- *
- */
-
-static bool supports_format(const char *format)
-{
- return format && strcmp(format, "srt") == 0;
-}
-
-static int init(struct sd *sd)
-{
- sd->output_codec = "subrip";
- return 0;
-}
-
-static bool parse_pts(bstr header, double *duration)
-{
- char buf[200];
- snprintf(buf, sizeof(buf), "%.*s", BSTR_P(header));
- int hh1, mm1, ss1, ms1;
- int hh2, mm2, ss2, ms2;
- if (sscanf(buf, "%d:%2d:%2d%*1[,.]%3d --> %d:%2d:%2d%*1[,.]%3d",
- &hh1, &mm1, &ss1, &ms1, &hh2, &mm2, &ss2, &ms2) >= 8)
- {
- int64_t start = (hh1*3600LL + mm1*60LL + ss1) * 1000LL + ms1;
- int64_t end = (hh2*3600LL + mm2*60LL + ss2) * 1000LL + ms2;
- *duration = (end - start) / 1000.0;
- return true;
- }
- return false;
-}
-
-static void decode(struct sd *sd, struct demux_packet *packet)
-{
- bstr data = {packet->buffer, packet->len};
- // Remove the broken header. It's usually on the second or first line.
- bstr left = data;
- while (left.len) {
- bstr line = bstr_getline(left, &left);
- if (parse_pts(line, &packet->duration)) {
- data = left;
- break;
- }
- }
- sd_conv_add_packet(sd, data.start, data.len, packet->pts, packet->duration);
-}
-
-const struct sd_functions sd_lavf_srt = {
- .name = "lavf_srt",
- .supports_format = supports_format,
- .init = init,
- .decode = decode,
- .get_converted = sd_conv_def_get_converted,
- .reset = sd_conv_def_reset,
-};
diff --git a/sub/sd_microdvd.c b/sub/sd_microdvd.c
deleted file mode 100644
index b6e3a93..0000000
--- a/sub/sd_microdvd.c
+++ /dev/null
@@ -1,344 +0,0 @@
-/*
- * Subtitles converter to SSA/ASS in order to allow special formatting
- *
- * This file is part of mpv.
- *
- * mpv is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * mpv is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with mpv. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <string.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdarg.h>
-#include <stdbool.h>
-#include <libavutil/common.h>
-
-#include "common/msg.h"
-#include "misc/bstr.h"
-#include "sd.h"
-
-struct line {
- char *buf;
- int bufsize;
- int len;
-};
-
-#ifdef __GNUC__
-static void append_text(struct line *dst, char *fmt, ...) __attribute__ ((format(printf, 2, 3)));
-#endif
-
-static void append_text(struct line *dst, char *fmt, ...)
-{
- va_list va;
- va_start(va, fmt);
- int ret = vsnprintf(dst->buf + dst->len, dst->bufsize - dst->len, fmt, va);
- if (ret < 0)
- goto out;
- dst->len += ret;
- if (dst->len > dst->bufsize)
- dst->len = dst->bufsize;
- out:
- va_end(va);
-}
-
-static int indexof(const char *s, int c)
-{
- char *f = strchr(s, c);
- return f ? (f - s) : -1;
-}
-
-/*
- * MicroDVD
- *
- * Based on the specifications found here:
- * https://trac.videolan.org/vlc/ticket/1825#comment:6
- */
-
-struct microdvd_tag {
- char key;
- int persistent;
- uint32_t data1;
- uint32_t data2;
- struct bstr data_string;
-};
-
-#define MICRODVD_PERSISTENT_OFF 0
-#define MICRODVD_PERSISTENT_ON 1
-#define MICRODVD_PERSISTENT_OPENED 2
-
-// Color, Font, Size, cHarset, stYle, Position, cOordinate
-#define MICRODVD_TAGS "cfshyYpo"
-
-static void microdvd_set_tag(struct microdvd_tag *tags, struct microdvd_tag tag)
-{
- int tag_index = indexof(MICRODVD_TAGS, tag.key);
-
- if (tag_index < 0)
- return;
- memcpy(&tags[tag_index], &tag, sizeof(tag));
-}
-
-// italic, bold, underline, strike-through
-#define MICRODVD_STYLES "ibus"
-
-static char *microdvd_load_tags(struct microdvd_tag *tags, char *s)
-{
- while (*s == '{') {
- char *start = s;
- char tag_char = *(s + 1);
- struct microdvd_tag tag = {0};
-
- if (!tag_char || *(s + 2) != ':')
- break;
- s += 3;
-
- switch (tag_char) {
-
- /* Style */
- case 'Y':
- tag.persistent = MICRODVD_PERSISTENT_ON;
- case 'y':
- while (*s && *s != '}') {
- int style_index = indexof(MICRODVD_STYLES, *s);
-
- if (style_index >= 0)
- tag.data1 |= (1 << style_index);
- s++;
- }
- if (*s != '}')
- break;
- /* We must distinguish persistent and non-persistent styles
- * to handle this kind of style tags: {y:ib}{Y:us} */
- tag.key = tag_char;
- break;
-
- /* Color */
- case 'C':
- tag.persistent = MICRODVD_PERSISTENT_ON;
- case 'c':
- tag.data1 = strtol(s, &s, 16) & 0x00ffffff;
- if (*s != '}')
- break;
- tag.key = 'c';
- break;
-
- /* Font name */
- case 'F':
- tag.persistent = MICRODVD_PERSISTENT_ON;
- case 'f':
- {
- int len = indexof(s, '}');
- if (len < 0)
- break;
- tag.data_string.start = s;
- tag.data_string.len = len;
- s += len;
- tag.key = 'f';
- break;
- }
-
- /* Font size */
- case 'S':
- tag.persistent = MICRODVD_PERSISTENT_ON;
- case 's':
- tag.data1 = strtol(s, &s, 10);
- if (*s != '}')
- break;
- tag.key = 's';
- break;
-
- /* Charset */
- case 'H':
- {
- //TODO: not yet handled, just parsed.
- int len = indexof(s, '}');
- if (len < 0)
- break;
- tag.data_string.start = s;
- tag.data_string.len = len;
- s += len;
- tag.key = 'h';
- break;
- }
-
- /* Position */
- case 'P':
- tag.persistent = MICRODVD_PERSISTENT_ON;
- tag.data1 = (*s++ == '1');
- if (*s != '}')
- break;
- tag.key = 'p';
- break;
-
- /* Coordinates */
- case 'o':
- tag.persistent = MICRODVD_PERSISTENT_ON;
- tag.data1 = strtol(s, &s, 10);
- if (*s != ',')
- break;
- s++;
- tag.data2 = strtol(s, &s, 10);
- if (*s != '}')
- break;
- tag.key = 'o';
- break;
-
- default: /* Unknown tag, we consider it to be text */
- break;
- }
-
- if (tag.key == 0)
- return start;
-
- microdvd_set_tag(tags, tag);
- s++;
- }
- return s;
-}
-
-static void microdvd_open_tags(struct line *new_line, struct microdvd_tag *tags)
-{
- for (int i = 0; i < sizeof(MICRODVD_TAGS) - 1; i++) {
- if (tags[i].persistent == MICRODVD_PERSISTENT_OPENED)
- continue;
- switch (tags[i].key) {
- case 'Y':
- case 'y':
- for (int sidx = 0; sidx < sizeof(MICRODVD_STYLES) - 1; sidx++)
- if (tags[i].data1 & (1 << sidx))
- append_text(new_line, "{\\%c1}", MICRODVD_STYLES[sidx]);
- break;
-
- case 'c':
- append_text(new_line, "{\\c&H%06X&}", tags[i].data1);
- break;
-
- case 'f':
- append_text(new_line, "{\\fn%.*s}", BSTR_P(tags[i].data_string));
- break;
-
- case 's':
- append_text(new_line, "{\\fs%d}", tags[i].data1);
- break;
-
- case 'p':
- if (tags[i].data1 == 0)
- append_text(new_line, "{\\an8}");
- break;
-
- case 'o':
- append_text(new_line, "{\\pos(%d,%d)}",
- tags[i].data1, tags[i].data2);
- break;
- }
- if (tags[i].persistent == MICRODVD_PERSISTENT_ON)
- tags[i].persistent = MICRODVD_PERSISTENT_OPENED;
- }
-}
-
-static void microdvd_close_no_persistent_tags(struct line *new_line,
- struct microdvd_tag *tags)
-{
- int i;
-
- for (i = sizeof(MICRODVD_TAGS) - 2; i; i--) {
- if (tags[i].persistent != MICRODVD_PERSISTENT_OFF)
- continue;
- switch (tags[i].key) {
-
- case 'y':
- for (int sidx = sizeof(MICRODVD_STYLES) - 2; sidx >= 0; sidx--)
- if (tags[i].data1 & (1 << sidx))
- append_text(new_line, "{\\%c0}", MICRODVD_STYLES[sidx]);
- break;
-
- case 'c':
- append_text(new_line, "{\\c}");
- break;
-
- case 'f':
- append_text(new_line, "{\\fn}");
- break;
-
- case 's':
- append_text(new_line, "{\\fs}");
- break;
- }
- tags[i].key = 0;
- }
-}
-
-static void convert_microdvd(const char *orig, char *dest, int dest_buffer_size)
-{
- /* line is not const to avoid warnings with strtol, etc.
- * orig content won't be changed */
- char *line = (char *)orig;
- struct line new_line = {
- .buf = dest,
- .bufsize = dest_buffer_size,
- };
- struct microdvd_tag tags[sizeof(MICRODVD_TAGS) - 1] = {{0}};
-
- while (*line) {
- line = microdvd_load_tags(tags, line);
- microdvd_open_tags(&new_line, tags);
-
- while (*line && *line != '|')
- new_line.buf[new_line.len++] = *line++;
-
- if (*line == '|') {
- microdvd_close_no_persistent_tags(&new_line, tags);
- append_text(&new_line, "\\N");
- line++;
- }
- }
- new_line.buf[new_line.len] = 0;
-}
-
-static const char *const microdvd_ass_extradata =
- "[Script Info]\n"
- "ScriptType: v4.00+\n"
- "PlayResX: 384\n"
- "PlayResY: 288\n";
-
-static bool supports_format(const char *format)
-{
- return format && strcmp(format, "microdvd") == 0;
-}
-
-static int init(struct sd *sd)
-{
- sd->output_codec = "ass-text";
- sd->output_extradata = (char *)microdvd_ass_extradata;
- sd->output_extradata_len = strlen(sd->output_extradata);
- return 0;
-}
-
-static void decode(struct sd *sd, struct demux_packet *packet)
-{
- char dest[SD_MAX_LINE_LEN];
- // Assume input buffer is padded with 0
- convert_microdvd(packet->buffer, dest, sizeof(dest));
- sd_conv_add_packet(sd, dest, strlen(dest), packet->pts, packet->duration);
-}
-
-const struct sd_functions sd_microdvd = {
- .name = "microdvd",
- .supports_format = supports_format,
- .init = init,
- .decode = decode,
- .get_converted = sd_conv_def_get_converted,
- .reset = sd_conv_def_reset,
-};
diff --git a/sub/sd_movtext.c b/sub/sd_movtext.c
deleted file mode 100644
index 3038a4c..0000000
--- a/sub/sd_movtext.c
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * This file is part of mpv.
- *
- * mpv is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * mpv is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with mpv. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <stdlib.h>
-#include <assert.h>
-
-#include <libavutil/intreadwrite.h>
-#include <libavutil/common.h>
-
-#include "sd.h"
-
-static bool supports_format(const char *format)
-{
- return format && strcmp(format, "mov_text") == 0;
-}
-
-static int init(struct sd *sd)
-{
- sd->output_codec = "text";
- return 0;
-}
-
-static void decode(struct sd *sd, struct demux_packet *packet)
-{
- unsigned char *data = packet->buffer;
- int len = packet->len;
- if (len < 2)
- return;
- len = FFMIN(len - 2, AV_RB16(data));
- data += 2;
- if (len > 0)
- sd_conv_add_packet(sd, data, len, packet->pts, packet->duration);
-}
-
-const struct sd_functions sd_movtext = {
- .name = "movtext",
- .supports_format = supports_format,
- .init = init,
- .decode = decode,
- .get_converted = sd_conv_def_get_converted,
- .reset = sd_conv_def_reset,
-};
diff --git a/sub/sd_srt.c b/sub/sd_srt.c
deleted file mode 100644
index 5c5cff5..0000000
--- a/sub/sd_srt.c
+++ /dev/null
@@ -1,477 +0,0 @@
-/*
- * Subtitles converter to SSA/ASS in order to allow special formatting
- *
- * This file is part of mpv.
- *
- * mpv is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * mpv is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with mpv. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <string.h>
-#include <strings.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdarg.h>
-#include <stdbool.h>
-
-#include "common/common.h"
-#include "common/msg.h"
-#include "misc/bstr.h"
-#include "misc/ctype.h"
-#include "sd.h"
-
-struct line {
- char *buf;
- int bufsize;
- int len;
-};
-
-#ifdef __GNUC__
-static void append_text(struct line *dst, char *fmt, ...) __attribute__ ((format(printf, 2, 3)));
-#endif
-
-static void append_text(struct line *dst, char *fmt, ...)
-{
- va_list va;
- va_start(va, fmt);
- int ret = vsnprintf(dst->buf + dst->len, dst->bufsize - dst->len, fmt, va);
- if (ret < 0)
- goto out;
- dst->len += ret;
- if (dst->len > dst->bufsize)
- dst->len = dst->bufsize;
- out:
- va_end(va);
-}
-
-static void append_text_n(struct line *dst, char *start, size_t length)
-{
- append_text(dst, "%.*s", (int)length, start);
-}
-
-
-/*
- * SubRip
- *
- * Support basic tags (italic, bold, underline, strike-through)
- * and font tag with size, color and face attributes.
- *
- */
-
-struct font_tag {
- int size;
- uint32_t color;
- struct bstr face;
- bool has_size : 1;
- bool has_color : 1;
- bool has_face : 1;
-};
-
-static const struct tag_conv {
- char *from;
- char *to;
-} subrip_basic_tags[] = {
- {"<i>", "{\\i1}"}, {"</i>", "{\\i0}"},
- {"<b>", "{\\b1}"}, {"</b>", "{\\b0}"},
- {"<u>", "{\\u1}"}, {"</u>", "{\\u0}"},
- {"<s>", "{\\s1}"}, {"</s>", "{\\s0}"},
- {"}", "\\}"},
- {"\r\n", "\\N"}, {"\n", "\\N"}, {"\r", "\\N"},
-};
-
-static const struct {
- char *s;
- uint32_t v;
-} subrip_web_colors[] = {
- /* Named CSS3 colors in RGB format; a subset of those
- at http://www.w3.org/TR/css3-color/#svg-color */
- {"aliceblue", 0xF0F8FF},
- {"antiquewhite", 0xFAEBD7},
- {"aqua", 0x00FFFF},
- {"aquamarine", 0x7FFFD4},
- {"azure", 0xF0FFFF},
- {"beige", 0xF5F5DC},
- {"bisque", 0xFFE4C4},
- {"black", 0x000000},
- {"blanchedalmond", 0xFFEBCD},
- {"blue", 0x0000FF},
- {"blueviolet", 0x8A2BE2},
- {"brown", 0xA52A2A},
- {"burlywood", 0xDEB887},
- {"cadetblue", 0x5F9EA0},
- {"chartreuse", 0x7FFF00},
- {"chocolate", 0xD2691E},
- {"coral", 0xFF7F50},
- {"cornflowerblue", 0x6495ED},
- {"cornsilk", 0xFFF8DC},
- {"crimson", 0xDC143C},
- {"cyan", 0x00FFFF},
- {"darkblue", 0x00008B},
- {"darkcyan", 0x008B8B},
- {"darkgoldenrod", 0xB8860B},
- {"darkgray", 0xA9A9A9},
- {"darkgreen", 0x006400},
- {"darkgrey", 0xA9A9A9},
- {"darkkhaki", 0xBDB76B},
- {"darkmagenta", 0x8B008B},
- {"darkolivegreen", 0x556B2F},
- {"darkorange", 0xFF8C00},
- {"darkorchid", 0x9932CC},
- {"darkred", 0x8B0000},
- {"darksalmon", 0xE9967A},
- {"darkseagreen", 0x8FBC8F},
- {"darkslateblue", 0x483D8B},
- {"darkslategray", 0x2F4F4F},
- {"darkslategrey", 0x2F4F4F},
- {"darkturquoise", 0x00CED1},
- {"darkviolet", 0x9400D3},
- {"deeppink", 0xFF1493},
- {"deepskyblue", 0x00BFFF},
- {"dimgray", 0x696969},
- {"dimgrey", 0x696969},
- {"dodgerblue", 0x1E90FF},
- {"firebrick", 0xB22222},
- {"floralwhite", 0xFFFAF0},
- {"forestgreen", 0x228B22},
- {"fuchsia", 0xFF00FF},
- {"gainsboro", 0xDCDCDC},
- {"ghostwhite", 0xF8F8FF},
- {"gold", 0xFFD700},
- {"goldenrod", 0xDAA520},
- {"gray", 0x808080},
- {"green", 0x008000},
- {"greenyellow", 0xADFF2F},
- {"grey", 0x808080},
- {"honeydew", 0xF0FFF0},
- {"hotpink", 0xFF69B4},
- {"indianred", 0xCD5C5C},
- {"indigo", 0x4B0082},
- {"ivory", 0xFFFFF0},
- {"khaki", 0xF0E68C},
- {"lavender", 0xE6E6FA},
- {"lavenderblush", 0xFFF0F5},
- {"lawngreen", 0x7CFC00},
- {"lemonchiffon", 0xFFFACD},
- {"lightblue", 0xADD8E6},
- {"lightcoral", 0xF08080},
- {"lightcyan", 0xE0FFFF},
- {"lightgoldenrodyellow", 0xFAFAD2},
- {"lightgray", 0xD3D3D3},
- {"lightgreen", 0x90EE90},
- {"lightgrey", 0xD3D3D3},
- {"lightpink", 0xFFB6C1},
- {"lightsalmon", 0xFFA07A},
- {"lightseagreen", 0x20B2AA},
- {"lightskyblue", 0x87CEFA},
- {"lightslategray", 0x778899},
- {"lightslategrey", 0x778899},
- {"lightsteelblue", 0xB0C4DE},
- {"lightyellow", 0xFFFFE0},
- {"lime", 0x00FF00},
- {"limegreen", 0x32CD32},
- {"linen", 0xFAF0E6},
- {"magenta", 0xFF00FF},
- {"maroon", 0x800000},
- {"mediumaquamarine", 0x66CDAA},
- {"mediumblue", 0x0000CD},
- {"mediumorchid", 0xBA55D3},
- {"mediumpurple", 0x9370DB},
- {"mediumseagreen", 0x3CB371},
- {"mediumslateblue", 0x7B68EE},
- {"mediumspringgreen", 0x00FA9A},
- {"mediumturquoise", 0x48D1CC},
- {"mediumvioletred", 0xC71585},
- {"midnightblue", 0x191970},
- {"mintcream", 0xF5FFFA},
- {"mistyrose", 0xFFE4E1},
- {"moccasin", 0xFFE4B5},
- {"navajowhite", 0xFFDEAD},
- {"navy", 0x000080},
- {"oldlace", 0xFDF5E6},
- {"olive", 0x808000},
- {"olivedrab", 0x6B8E23},
- {"orange", 0xFFA500},
- {"orangered", 0xFF4500},
- {"orchid", 0xDA70D6},
- {"palegoldenrod", 0xEEE8AA},
- {"palegreen", 0x98FB98},
- {"paleturquoise", 0xAFEEEE},
- {"palevioletred", 0xDB7093},
- {"papayawhip", 0xFFEFD5},
- {"peachpuff", 0xFFDAB9},
- {"peru", 0xCD853F},
- {"pink", 0xFFC0CB},
- {"plum", 0xDDA0DD},
- {"powderblue", 0xB0E0E6},
- {"purple", 0x800080},
- {"red", 0xFF0000},
- {"rosybrown", 0xBC8F8F},
- {"royalblue", 0x4169E1},
- {"saddlebrown", 0x8B4513},
- {"salmon", 0xFA8072},
- {"sandybrown", 0xF4A460},
- {"seagreen", 0x2E8B57},
- {"seashell", 0xFFF5EE},
- {"sienna", 0xA0522D},
- {"silver", 0xC0C0C0},
- {"skyblue", 0x87CEEB},
- {"slateblue", 0x6A5ACD},
- {"slategray", 0x708090},
- {"slategrey", 0x708090},
- {"snow", 0xFFFAFA},
- {"springgreen", 0x00FF7F},
- {"steelblue", 0x4682B4},
- {"tan", 0xD2B48C},
- {"teal", 0x008080},
- {"thistle", 0xD8BFD8},
- {"tomato", 0xFF6347},
- {"turquoise", 0x40E0D0},
- {"violet", 0xEE82EE},
- {"wheat", 0xF5DEB3},
- {"white", 0xFFFFFF},
- {"whitesmoke", 0xF5F5F5},
- {"yellow", 0xFFFF00},
- {"yellowgreen", 0x9ACD32},
-};
-
-#define SUBRIP_MAX_STACKED_FONT_TAGS 16
-
-/* Read the HTML-style attribute starting at *s, and skip *s past the value.
- * Set attr and val to the parsed attribute name and value.
- * Return 0 on success, or -1 if no valid attribute was found.
- */
-static int read_attr(char **s, struct bstr *attr, struct bstr *val)
-{
- char *eq = strchr(*s, '=');
- if (!eq)
- return -1;
- attr->start = *s;
- attr->len = eq - *s;
- for (int i = 0; i < attr->len; i++)
- if (!mp_isalnum(attr->start[i]))
- return -1;
- val->start = eq + 1;
- bool quoted = val->start[0] == '"';
- if (quoted)
- val->start++;
- unsigned char *end = strpbrk(val->start, quoted ? "\"" : " >");
- if (!end)
- return -1;
- val->len = end - val->start;
- *s = end + quoted;
- return 0;
-}
-
-static void convert_subrip(struct sd *sd, const char *orig,
- char *dest, int dest_buffer_size)
-{
- /* line is not const to avoid warnings with strtol, etc.
- * orig content won't be changed */
- char *line = (char *)orig;
- struct line new_line = {
- .buf = dest,
- .bufsize = dest_buffer_size,
- };
- struct font_tag font_stack[SUBRIP_MAX_STACKED_FONT_TAGS + 1];
- font_stack[0] = (struct font_tag){0}; // type with all defaults
- int sp = 0;
-
- while (*line && new_line.len < new_line.bufsize - 1) {
- char *orig_line = line;
-
- for (int i = 0; i < MP_ARRAY_SIZE(subrip_basic_tags); i++) {
- const struct tag_conv *tag = &subrip_basic_tags[i];
- int from_len = strlen(tag->from);
- if (strncmp(line, tag->from, from_len) == 0) {
- append_text(&new_line, "%s", tag->to);
- line += from_len;
- }
- }
-
- if (strncmp(line, "</font>", 7) == 0) {
- /* Closing font tag */
- line += 7;
-
- if (sp > 0) {
- struct font_tag *tag = &font_stack[sp];
- struct font_tag *last_tag = &tag[-1];
- sp--;
-
- if (tag->has_size) {
- if (!last_tag->has_size)
- append_text(&new_line, "{\\fs}");
- else if (last_tag->size != tag->size)
- append_text(&new_line, "{\\fs%d}", last_tag->size);
- }
-
- if (tag->has_color) {
- if (!last_tag->has_color)
- append_text(&new_line, "{\\c}");
- else if (last_tag->color != tag->color)
- append_text(&new_line, "{\\c&H%06X&}", last_tag->color);
- }
-
- if (tag->has_face) {
- if (!last_tag->has_face)
- append_text(&new_line, "{\\fn}");
- else if (bstrcmp(last_tag->face, tag->face) != 0)
- append_text(&new_line, "{\\fn%.*s}",
- BSTR_P(last_tag->face));
- }
- }
- } else if (strncmp(line, "<font ", 6) == 0
- && sp + 1 < MP_ARRAY_SIZE(font_stack)) {
- /* Opening font tag */
- char *potential_font_tag_start = line;
- int len_backup = new_line.len;
- struct font_tag *tag = &font_stack[sp + 1];
- bool has_valid_attr = false;
-
- *tag = tag[-1]; // keep values from previous tag
- line += 6;
-
- while (*line && *line != '>') {
- if (*line == ' ') {
- line++;
- continue;
- }
- struct bstr attr, val;
- if (read_attr(&line, &attr, &val) < 0)
- break;
- if (!bstrcmp0(attr, "size")) {
- tag->size = bstrtoll(val, &val, 10);
- if (val.len)
- break;
- append_text(&new_line, "{\\fs%d}", tag->size);
- tag->has_size = true;
- has_valid_attr = true;
- } else if (!bstrcmp0(attr, "color")) {
- int found = 0;
-
- // Try to lookup the string in standard web colors
- for (int i = 0; i < MP_ARRAY_SIZE(subrip_web_colors); i++) {
- char *color = subrip_web_colors[i].s;
- if (bstrcasecmp(val, bstr0(color)) == 0) {
- uint32_t xcolor = subrip_web_colors[i].v;
- tag->color = ((xcolor & 0xff) << 16)
- | (xcolor & 0xff00)
- | ((xcolor & 0xff0000) >> 16);
- found = 1;
- }
- }
-
- // If it's not a web color it must be a HEX RGB value
- if (!found) {
- // Remove the leading '#'
- bstr_eatstart(&val, bstr0("#"));
- // Sometimes there are two '#'
- bstr_eatstart(&val, bstr0("#"));
-
- // Parse RRGGBB format
- tag->color = bstrtoll(val, &val, 16) & 0x00ffffff;
- if (!val.len) {
- tag->color = ((tag->color & 0xff) << 16)
- | (tag->color & 0xff00)
- | ((tag->color & 0xff0000) >> 16);
- found = 1;
- }
- }
-
- if (found) {
- append_text(&new_line, "{\\c&H%06X&}", tag->color);
- tag->has_color = true;
- } else {
- // We didn't find any matching color
- MP_WARN(sd, "unknown font color in subtitle: >%s<\n",
- orig);
- append_text(&new_line, "{\\c}");
- }
-
- has_valid_attr = true;
- } else if (!bstrcmp0(attr, "face")) {
- /* Font face attribute */
- tag->face = val;
- append_text(&new_line, "{\\fn%.*s}", BSTR_P(tag->face));
- tag->has_face = true;
- has_valid_attr = true;
- } else
- MP_WARN(sd, "unrecognized attribute \"%.*s\" in font tag\n",
- BSTR_P(attr));
- }
-
- if (!has_valid_attr || *line != '>') { /* Not valid font tag */
- line = potential_font_tag_start;
- new_line.len = len_backup;
- } else {
- sp++;
- line++;
- }
- } else if (*line == '{') {
- char *end = strchr(line, '}');
- if (line[1] == '\\' && end) {
- // Likely ASS tag, pass them through
- // Note that ASS tags like {something\an8} are legal too (i.e.
- // the first character after '{' doesn't have to be '\'), but
- // consider these fringe cases not worth supporting.
- append_text_n(&new_line, line, end - line + 1);
- line = end + 1;
- } else {
- append_text(&new_line, "\\{");
- line++;
- }
- }
-
- /* Tag conversion code didn't match */
- if (line == orig_line)
- new_line.buf[new_line.len++] = *line++;
- }
- new_line.buf[new_line.len] = 0;
-}
-
-static const char *const srt_ass_extradata =
- "[Script Info]\n"
- "ScriptType: v4.00+\n"
- "PlayResX: 384\n"
- "PlayResY: 288\n";
-
-static bool supports_format(const char *format)
-{
- return format && (strcmp(format, "subrip") == 0 ||
- strcmp(format, "text") == 0);
-}
-
-static int init(struct sd *sd)
-{
- sd->output_codec = "ass-text";
- sd->output_extradata = (char *)srt_ass_extradata;
- sd->output_extradata_len = strlen(sd->output_extradata);
- return 0;
-}
-
-static void decode(struct sd *sd, struct demux_packet *packet)
-{
- char dest[SD_MAX_LINE_LEN];
- // Assume input buffer is padded with 0
- convert_subrip(sd, packet->buffer, dest, sizeof(dest));
- sd_conv_add_packet(sd, dest, strlen(dest), packet->pts, packet->duration);
-}
-
-const struct sd_functions sd_srt = {
- .name = "srt",
- .supports_format = supports_format,
- .init = init,
- .decode = decode,
- .get_converted = sd_conv_def_get_converted,
- .reset = sd_conv_def_reset,
-};
diff --git a/ta/ta_utils.c b/ta/ta_utils.c
index 860a964..6a7455c 100644
--- a/ta/ta_utils.c
+++ b/ta/ta_utils.c
@@ -15,6 +15,7 @@
#include <string.h>
#include <stdio.h>
#include <assert.h>
+#include "osdep/strnlen.h"
#define TA_NO_WRAPPERS
#include "ta.h"
diff --git a/test/chmap_sel.c b/test/chmap_sel.c
index 0301045..38a5254 100644
--- a/test/chmap_sel.c
+++ b/test/chmap_sel.c
@@ -52,6 +52,10 @@ static void test_mp_chmap_sel_fallback_use_replacements(void **state) {
test_sel("5.1", "7.1(rear)", LAYOUTS("7.1(rear)"));
}
+static void test_mp_chmap_sel_fallback_inexact_equivalent(void **state) {
+ test_sel("5.1(side)", "5.1", LAYOUTS("5.1", "7.1"));
+}
+
static void test_mp_chmap_sel_fallback_works_on_alsa_chmaps(void **state) {
test_sel("5.1", "7.1(alsa)", LAYOUTS("7.1(alsa)"));
}
@@ -89,11 +93,11 @@ static void test_mp_chmap_sel_fallback_reject_unknown(void **state) {
static void test_mp_chmap_sel_fallback_more_replacements(void **state) {
test_sel("quad", "quad(side)", LAYOUTS("quad(side)", "stereo"));
- test_sel("quad", "7.0", LAYOUTS("quad(side)", "7.0"));
- test_sel("quad", "7.0", LAYOUTS("7.0", "quad(side)"));
+ test_sel("quad", "quad(side)", LAYOUTS("quad(side)", "7.0"));
+ test_sel("quad", "quad(side)", LAYOUTS("7.0", "quad(side)"));
test_sel("quad", "7.1(wide-side)", LAYOUTS("7.1(wide-side)", "stereo"));
test_sel("quad", "7.1(wide-side)", LAYOUTS("stereo", "7.1(wide-side)"));
- test_sel("quad", "fl-fr-fc-bl-br",
+ test_sel("quad", "fl-fr-sl-sr",
LAYOUTS("fl-fr-fc-bl-br", "fl-fr-sl-sr"));
test_sel("quad", "fl-fr-bl-br-na-na-na-na",
LAYOUTS("fl-fr-bl-br-na-na-na-na", "quad(side)", "stereo"));
@@ -118,6 +122,7 @@ int main(void) {
cmocka_unit_test(test_mp_chmap_sel_fallback_prefer_compatible),
cmocka_unit_test(test_mp_chmap_sel_fallback_prefer_closest_upmix),
cmocka_unit_test(test_mp_chmap_sel_fallback_use_replacements),
+ cmocka_unit_test(test_mp_chmap_sel_fallback_inexact_equivalent),
cmocka_unit_test(test_mp_chmap_sel_fallback_works_on_alsa_chmaps),
cmocka_unit_test(test_mp_chmap_sel_fallback_mono_to_stereo),
cmocka_unit_test(test_mp_chmap_sel_fallback_stereo_to_stereo),
diff --git a/test/gl_video.c b/test/gl_video.c
index 253ab35..97fee94 100644
--- a/test/gl_video.c
+++ b/test/gl_video.c
@@ -1,5 +1,5 @@
#include "test_helpers.h"
-#include "video/out/gl_video.h"
+#include "video/out/opengl/video.h"
static void test_scale_ambient_lux_limits(void **state) {
float x;
diff --git a/video/csputils.c b/video/csputils.c
index fb9d971..e446d8e 100644
--- a/video/csputils.c
+++ b/video/csputils.c
@@ -7,23 +7,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.
- *
- * You should have received a copy of the GNU General Public License along
- * with mpv. If not, see <http://www.gnu.org/licenses/>.
+ * GNU Lesser General Public License for more details.
*
- * You can alternatively redistribute this file 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.
+ * 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"
@@ -119,6 +114,8 @@ const struct m_opt_choice_alternatives mp_stereo3d_names[] = {
{"arcc", 10}, // "anaglyph_cyan_red" (Matroska: unclear which mode)
{"sbs2r", 11}, // "side_by_side_right"
{"agmc", 12}, // "anaglyph_green_magenta" (Matroska: unclear which mode)
+ {"al", 13}, // "alternating frames left first"
+ {"ar", 14}, // "alternating frames right first"
{0}
};
@@ -652,14 +649,18 @@ void mp_get_csp_matrix(struct mp_csp_params *params, struct mp_cmat *m)
abort();
};
- // Hue is equivalent to rotating input [U, V] subvector around the origin.
- // Saturation scales [U, V].
- float huecos = params->gray ? 0 : params->saturation * cos(params->hue);
- float huesin = params->gray ? 0 : params->saturation * sin(params->hue);
- for (int i = 0; i < 3; i++) {
- float u = m->m[i][1], v = m->m[i][2];
- m->m[i][1] = huecos * u - huesin * v;
- m->m[i][2] = huesin * u + huecos * v;
+ if ((colorspace == MP_CSP_BT_601 || colorspace == MP_CSP_BT_709 ||
+ colorspace == MP_CSP_SMPTE_240M || colorspace == MP_CSP_BT_2020_NC))
+ {
+ // Hue is equivalent to rotating input [U, V] subvector around the origin.
+ // Saturation scales [U, V].
+ float huecos = params->gray ? 0 : params->saturation * cos(params->hue);
+ float huesin = params->gray ? 0 : params->saturation * sin(params->hue);
+ for (int i = 0; i < 3; i++) {
+ float u = m->m[i][1], v = m->m[i][2];
+ m->m[i][1] = huecos * u - huesin * v;
+ m->m[i][2] = huesin * u + huecos * v;
+ }
}
// The values below are written in 0-255 scale - thus bring s into range.
diff --git a/video/csputils.h b/video/csputils.h
index 3934674..d5990d2 100644
--- a/video/csputils.h
+++ b/video/csputils.h
@@ -1,23 +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.
- *
- * You should have received a copy of the GNU General Public License along
- * with mpv. If not, see <http://www.gnu.org/licenses/>.
+ * GNU Lesser General Public License for more details.
*
- * You can alternatively redistribute this file 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.
+ * 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_CSPUTILS_H
@@ -106,7 +101,7 @@ enum mp_stereo3d_mode {
MP_STEREO3D_AB2L = 3,
MP_STEREO3D_SBS2R = 11,
/* no explicit enum entries for most valid values */
- MP_STEREO3D_COUNT = 13, // 12 is last valid mode
+ MP_STEREO3D_COUNT = 15, // 14 is last valid mode
};
extern const struct m_opt_choice_alternatives mp_stereo3d_names[];
diff --git a/video/decode/dec_video.c b/video/decode/dec_video.c
index 509daf7..e8a5774 100644
--- a/video/decode/dec_video.c
+++ b/video/decode/dec_video.c
@@ -15,19 +15,21 @@
* with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
-#include "config.h"
-#include "options/options.h"
-
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>
+#include <libavutil/rational.h>
+
+#include "config.h"
+#include "options/options.h"
#include "common/msg.h"
#include "osdep/timer.h"
#include "stream/stream.h"
+#include "demux/demux.h"
#include "demux/packet.h"
#include "common/codecs.h"
@@ -37,7 +39,6 @@
#include "demux/stheader.h"
#include "video/decode/vd.h"
-#include "video/filter/vf.h"
#include "video/decode/dec_video.h"
@@ -55,18 +56,23 @@ const vd_functions_t * const mpcodecs_vd_drivers[] = {
NULL
};
-void video_reset_decoding(struct dec_video *d_video)
+void video_reset(struct dec_video *d_video)
{
video_vd_control(d_video, VDCTRL_RESET, NULL);
- if (d_video->vfilter && d_video->vfilter->initialized == 1)
- vf_seek_reset(d_video->vfilter);
- mp_image_unrefp(&d_video->waiting_decoded_mpi);
- d_video->num_buffered_pts = 0;
- d_video->last_pts = MP_NOPTS_VALUE;
d_video->first_packet_pdts = MP_NOPTS_VALUE;
+ d_video->start_pts = MP_NOPTS_VALUE;
d_video->decoded_pts = MP_NOPTS_VALUE;
d_video->codec_pts = MP_NOPTS_VALUE;
d_video->codec_dts = MP_NOPTS_VALUE;
+ d_video->last_format = d_video->fixed_format = (struct mp_image_params){0};
+ d_video->dropped_frames = 0;
+ d_video->current_state = DATA_AGAIN;
+ mp_image_unrefp(&d_video->current_mpi);
+ talloc_free(d_video->packet);
+ d_video->packet = NULL;
+ talloc_free(d_video->new_segment);
+ d_video->new_segment = NULL;
+ d_video->start = d_video->end = MP_NOPTS_VALUE;
}
int video_vd_control(struct dec_video *d_video, int cmd, void *arg)
@@ -77,50 +83,18 @@ int video_vd_control(struct dec_video *d_video, int cmd, void *arg)
return CONTROL_UNKNOWN;
}
-int video_set_colors(struct dec_video *d_video, const char *item, int value)
-{
- vf_equalizer_t data;
-
- data.item = item;
- data.value = value;
-
- MP_VERBOSE(d_video, "set video colors %s=%d \n", item, value);
- if (d_video->vfilter) {
- int ret = video_vf_vo_control(d_video, VFCTRL_SET_EQUALIZER, &data);
- if (ret == CONTROL_TRUE)
- return 1;
- }
- MP_VERBOSE(d_video, "Video attribute '%s' is not supported by selected vo.\n",
- item);
- return 0;
-}
-
-int video_get_colors(struct dec_video *d_video, const char *item, int *value)
-{
- vf_equalizer_t data;
-
- data.item = item;
-
- MP_VERBOSE(d_video, "get video colors %s \n", item);
- if (d_video->vfilter) {
- int ret = video_vf_vo_control(d_video, VFCTRL_GET_EQUALIZER, &data);
- if (ret == CONTROL_TRUE) {
- *value = data.value;
- return 1;
- }
- }
- return 0;
-}
-
void video_uninit(struct dec_video *d_video)
{
- mp_image_unrefp(&d_video->waiting_decoded_mpi);
+ if (!d_video)
+ return;
+ mp_image_unrefp(&d_video->current_mpi);
mp_image_unrefp(&d_video->cover_art_mpi);
if (d_video->vd_driver) {
MP_VERBOSE(d_video, "Uninit video.\n");
d_video->vd_driver->uninit(d_video);
}
- vf_destroy(d_video->vfilter);
+ talloc_free(d_video->packet);
+ talloc_free(d_video->new_segment);
talloc_free(d_video);
}
@@ -159,15 +133,17 @@ static const struct vd_functions *find_driver(const char *name)
return NULL;
}
-bool video_init_best_codec(struct dec_video *d_video, char* video_decoders)
+bool video_init_best_codec(struct dec_video *d_video)
{
+ struct MPOpts *opts = d_video->opts;
+
assert(!d_video->vd_driver);
- video_reset_decoding(d_video);
+ video_reset(d_video);
d_video->has_broken_packet_pts = -10; // needs 10 packets to reach decision
struct mp_decoder_entry *decoder = NULL;
struct mp_decoder_list *list =
- mp_select_video_decoders(d_video->header->codec, video_decoders);
+ mp_select_video_decoders(d_video->codec->codec, opts->video_decoders);
mp_print_decoders(d_video->log, MSGL_V, "Codec list:", list);
@@ -195,7 +171,7 @@ bool video_init_best_codec(struct dec_video *d_video, char* video_decoders)
MP_VERBOSE(d_video, "Selected video codec: %s\n", d_video->decoder_desc);
} else {
MP_ERR(d_video, "Failed to initialize a video decoder for codec '%s'.\n",
- d_video->header->codec ? d_video->header->codec : "<unknown>");
+ d_video->codec->codec);
}
if (d_video->header->missing_timestamps) {
@@ -208,73 +184,94 @@ bool video_init_best_codec(struct dec_video *d_video, char* video_decoders)
return !!d_video->vd_driver;
}
-static void add_avi_pts(struct dec_video *d_video, double pts)
+static void fix_image_params(struct dec_video *d_video,
+ struct mp_image_params *params)
{
- if (pts != MP_NOPTS_VALUE) {
- int delay = -1;
- video_vd_control(d_video, VDCTRL_QUERY_UNSEEN_FRAMES, &delay);
- if (delay >= 0 && delay < d_video->num_buffered_pts)
- d_video->num_buffered_pts = delay;
- if (d_video->num_buffered_pts == MP_ARRAY_SIZE(d_video->buffered_pts)) {
- MP_ERR(d_video, "Too many buffered pts\n");
- } else {
- for (int i = d_video->num_buffered_pts; i > 0; i--)
- d_video->buffered_pts[i] = d_video->buffered_pts[i - 1];
- d_video->buffered_pts[0] = pts;
- d_video->num_buffered_pts++;
+ struct MPOpts *opts = d_video->opts;
+ struct mp_image_params p = *params;
+ struct mp_codec_params *c = d_video->codec;
+
+ MP_VERBOSE(d_video, "Decoder format: %s\n", mp_image_params_to_str(params));
+
+ // While mp_image_params normally always have to have d_w/d_h set, the
+ // decoder signals unknown bitstream aspect ratio with both set to 0.
+ float dec_aspect = p.p_w > 0 && p.p_h > 0 ? p.p_w / (float)p.p_h : 0;
+ if (d_video->initial_decoder_aspect == 0)
+ d_video->initial_decoder_aspect = dec_aspect;
+
+ bool use_container = true;
+ switch (opts->aspect_method) {
+ case 0:
+ // We normally prefer the container aspect, unless the decoder aspect
+ // changes at least once.
+ if (dec_aspect > 0 && d_video->initial_decoder_aspect != dec_aspect) {
+ MP_VERBOSE(d_video, "Using bitstream aspect ratio.\n");
+ // Even if the aspect switches back, don't use container aspect again.
+ d_video->initial_decoder_aspect = -1;
+ use_container = false;
}
+ break;
+ case 1:
+ use_container = false;
+ break;
}
-}
-static double retrieve_avi_pts(struct dec_video *d_video, double codec_pts)
-{
- if (d_video->num_buffered_pts) {
- d_video->num_buffered_pts--;
- return d_video->buffered_pts[d_video->num_buffered_pts];
+ if (use_container && c->par_w > 0 && c->par_h) {
+ MP_VERBOSE(d_video, "Using container aspect ratio.\n");
+ p.p_w = c->par_w;
+ p.p_h = c->par_h;
}
- MP_ERR(d_video, "No pts value from demuxer to use for frame!\n");
- return MP_NOPTS_VALUE;
+
+ if (opts->movie_aspect >= 0) {
+ MP_VERBOSE(d_video, "Forcing user-set aspect ratio.\n");
+ if (opts->movie_aspect == 0) {
+ p.p_w = p.p_h = 1;
+ } else {
+ AVRational a = av_d2q(opts->movie_aspect, INT_MAX);
+ mp_image_params_set_dsize(&p, a.num, a.den);
+ }
+ }
+
+ // Assume square pixels if no aspect ratio is set at all.
+ if (p.p_w <= 0 || p.p_h <= 0)
+ p.p_w = p.p_h = 1;
+
+ // Detect colorspace from resolution.
+ mp_image_params_guess_csp(&p);
+
+ d_video->last_format = *params;
+ d_video->fixed_format = p;
}
-struct mp_image *video_decode(struct dec_video *d_video,
- struct demux_packet *packet,
- int drop_frame)
+static struct mp_image *decode_packet(struct dec_video *d_video,
+ struct demux_packet *packet,
+ int drop_frame)
{
struct MPOpts *opts = d_video->opts;
- bool avi_pts = d_video->header->video->avi_dts && opts->correct_pts;
- struct demux_packet packet_copy;
- if (packet && packet->dts == MP_NOPTS_VALUE) {
- packet_copy = *packet;
- packet = &packet_copy;
- packet->dts = packet->pts;
- }
+ if (!d_video->vd_driver)
+ return NULL;
double pkt_pts = packet ? packet->pts : MP_NOPTS_VALUE;
double pkt_dts = packet ? packet->dts : MP_NOPTS_VALUE;
+ if (pkt_pts == MP_NOPTS_VALUE)
+ d_video->has_broken_packet_pts = 1;
+
double pkt_pdts = pkt_pts == MP_NOPTS_VALUE ? pkt_dts : pkt_pts;
if (pkt_pdts != MP_NOPTS_VALUE && d_video->first_packet_pdts == MP_NOPTS_VALUE)
d_video->first_packet_pdts = pkt_pdts;
- if (avi_pts)
- add_avi_pts(d_video, pkt_pdts);
-
- double prev_codec_pts = d_video->codec_pts;
- double prev_codec_dts = d_video->codec_dts;
-
- if (d_video->header->video->avi_dts)
- drop_frame = 0;
-
MP_STATS(d_video, "start decode video");
struct mp_image *mpi = d_video->vd_driver->decode(d_video, packet, drop_frame);
MP_STATS(d_video, "end decode video");
+ // Error, discarded frame, dropped frame, or initial codec delay.
if (!mpi || drop_frame) {
talloc_free(mpi);
- return NULL; // error / skipped frame
+ return NULL;
}
if (opts->field_dominance == 0) {
@@ -285,30 +282,31 @@ struct mp_image *video_decode(struct dec_video *d_video,
}
// Note: the PTS is reordered, but the DTS is not. Both should be monotonic.
- double pts = d_video->codec_pts;
- double dts = d_video->codec_dts;
+ double pts = mpi->pts;
+ double dts = mpi->dts;
- if (pts == MP_NOPTS_VALUE) {
- d_video->codec_pts = prev_codec_pts;
- } else if (pts < prev_codec_pts) {
- d_video->num_codec_pts_problems++;
+ if (pts != MP_NOPTS_VALUE) {
+ if (pts < d_video->codec_pts)
+ d_video->num_codec_pts_problems++;
+ d_video->codec_pts = mpi->pts;
}
- if (dts == MP_NOPTS_VALUE) {
- d_video->codec_dts = prev_codec_dts;
- } else if (dts <= prev_codec_dts) {
- d_video->num_codec_dts_problems++;
+ if (dts != MP_NOPTS_VALUE) {
+ if (dts <= d_video->codec_dts)
+ d_video->num_codec_dts_problems++;
+ d_video->codec_dts = mpi->dts;
}
+ if (d_video->has_broken_packet_pts < 0)
+ d_video->has_broken_packet_pts++;
+ if (d_video->num_codec_pts_problems)
+ d_video->has_broken_packet_pts = 1;
+
// If PTS is unset, or non-monotonic, fall back to DTS.
if ((d_video->num_codec_pts_problems > d_video->num_codec_dts_problems ||
pts == MP_NOPTS_VALUE) && dts != MP_NOPTS_VALUE)
pts = dts;
- // Alternative PTS determination methods
- if (avi_pts)
- pts = retrieve_avi_pts(d_video, pts);
-
if (!opts->correct_pts || pts == MP_NOPTS_VALUE) {
if (opts->correct_pts && !d_video->header->missing_timestamps)
MP_WARN(d_video, "No video PTS! Making something up.\n");
@@ -323,106 +321,161 @@ struct mp_image *video_decode(struct dec_video *d_video,
}
}
- if (d_video->has_broken_packet_pts < 0)
- d_video->has_broken_packet_pts++;
- if (d_video->num_codec_pts_problems || pkt_pts == MP_NOPTS_VALUE)
- d_video->has_broken_packet_pts = 1;
+ if (!mp_image_params_equal(&d_video->last_format, &mpi->params))
+ fix_image_params(d_video, &mpi->params);
+
+ mpi->params = d_video->fixed_format;
mpi->pts = pts;
d_video->decoded_pts = pts;
+
+ // Compensate for incorrectly using mpeg-style DTS for avi timestamps.
+ if (d_video->codec->avi_dts && opts->correct_pts &&
+ mpi->pts != MP_NOPTS_VALUE && d_video->fps > 0)
+ {
+ int delay = -1;
+ video_vd_control(d_video, VDCTRL_GET_BFRAMES, &delay);
+ mpi->pts -= MPMAX(delay, 0) / d_video->fps;
+ }
+
return mpi;
}
-int video_reconfig_filters(struct dec_video *d_video,
- const struct mp_image_params *params)
+void video_reset_aspect(struct dec_video *d_video)
{
- struct MPOpts *opts = d_video->opts;
- struct mp_image_params p = *params;
- struct sh_video *sh = d_video->header->video;
+ d_video->last_format = (struct mp_image_params){0};
+}
- // While mp_image_params normally always have to have d_w/d_h set, the
- // decoder signals unknown bitstream aspect ratio with both set to 0.
- float dec_aspect = p.d_w > 0 && p.d_h > 0 ? p.d_w / (float)p.d_h : 0;
- if (d_video->initial_decoder_aspect == 0)
- d_video->initial_decoder_aspect = dec_aspect;
+void video_set_framedrop(struct dec_video *d_video, bool enabled)
+{
+ d_video->framedrop_enabled = enabled;
+}
- bool use_container = true;
- switch (opts->aspect_method) {
- case 0:
- // We normally prefer the container aspect, unless the decoder aspect
- // changes at least once.
- if (dec_aspect > 0 && d_video->initial_decoder_aspect != dec_aspect) {
- MP_VERBOSE(d_video, "Using bitstream aspect ratio.\n");
- // Even if the aspect switches back, don't use container aspect again.
- d_video->initial_decoder_aspect = -1;
- use_container = false;
+// Frames before the start timestamp can be dropped. (Used for hr-seek.)
+void video_set_start(struct dec_video *d_video, double start_pts)
+{
+ d_video->start_pts = start_pts;
+}
+
+void video_work(struct dec_video *d_video)
+{
+ if (d_video->current_mpi)
+ return;
+
+ if (d_video->header->attached_picture) {
+ if (d_video->current_state == DATA_AGAIN && !d_video->cover_art_mpi) {
+ d_video->cover_art_mpi =
+ decode_packet(d_video, d_video->header->attached_picture, 0);
+ // Might need flush.
+ if (!d_video->cover_art_mpi)
+ d_video->cover_art_mpi = decode_packet(d_video, NULL, 0);
+ d_video->current_state = DATA_OK;
}
- break;
- case 1:
- use_container = false;
- break;
+ if (d_video->current_state == DATA_OK)
+ d_video->current_mpi = mp_image_new_ref(d_video->cover_art_mpi);
+ // (DATA_OK is returned the first time, when current_mpi is sill set)
+ d_video->current_state = DATA_EOF;
+ return;
}
- if (use_container && sh->aspect > 0) {
- MP_VERBOSE(d_video, "Using container aspect ratio.\n");
- vf_set_dar(&p.d_w, &p.d_h, p.w, p.h, sh->aspect);
+ if (!d_video->packet && !d_video->new_segment &&
+ demux_read_packet_async(d_video->header, &d_video->packet) == 0)
+ {
+ d_video->current_state = DATA_WAIT;
+ return;
}
- float force_aspect = opts->movie_aspect;
- if (force_aspect >= 0.0) {
- MP_VERBOSE(d_video, "Forcing user-set aspect ratio.\n");
- vf_set_dar(&p.d_w, &p.d_h, p.w, p.h, force_aspect);
+ if (d_video->packet) {
+ if (d_video->packet->dts == MP_NOPTS_VALUE && !d_video->codec->avi_dts)
+ d_video->packet->dts = d_video->packet->pts;
}
- // Assume square pixels if no aspect ratio is set at all.
- if (p.d_w <= 0 || p.d_h <= 0) {
- p.d_w = p.w;
- p.d_h = p.h;
+ if (d_video->packet && d_video->packet->new_segment) {
+ assert(!d_video->new_segment);
+ d_video->new_segment = d_video->packet;
+ d_video->packet = NULL;
}
- // Detect colorspace from resolution.
- mp_image_params_guess_csp(&p);
+ bool had_input_packet = !!d_video->packet;
+ bool had_packet = had_input_packet || d_video->new_segment;
- if (vf_reconfig(d_video->vfilter, params, &p) < 0) {
- MP_FATAL(d_video, "Cannot initialize video filters.\n");
- return -1;
- }
+ double start_pts = d_video->start_pts;
+ if (d_video->start != MP_NOPTS_VALUE && (start_pts == MP_NOPTS_VALUE ||
+ d_video->start > start_pts))
+ start_pts = d_video->start;
- return 0;
-}
+ int framedrop_type = d_video->framedrop_enabled ? 1 : 0;
+ if (start_pts != MP_NOPTS_VALUE && d_video->packet &&
+ d_video->packet->pts < start_pts - .005 &&
+ !d_video->has_broken_packet_pts)
+ {
+ framedrop_type = 2;
+ }
+ d_video->current_mpi = decode_packet(d_video, d_video->packet, framedrop_type);
+ if (d_video->packet && d_video->packet->len == 0) {
+ talloc_free(d_video->packet);
+ d_video->packet = NULL;
+ }
-// Send a VCTRL, or if it doesn't work, translate it to a VOCTRL and try the VO.
-int video_vf_vo_control(struct dec_video *d_video, int vf_cmd, void *data)
-{
- if (d_video->vfilter && d_video->vfilter->initialized > 0) {
- int r = vf_control_any(d_video->vfilter, vf_cmd, data);
- if (r != CONTROL_UNKNOWN)
- return r;
+ d_video->current_state = DATA_OK;
+ if (!d_video->current_mpi) {
+ d_video->current_state = DATA_EOF;
+ if (had_packet) {
+ if (framedrop_type == 1)
+ d_video->dropped_frames += 1;
+ d_video->current_state = DATA_AGAIN;
+ }
}
- switch (vf_cmd) {
- case VFCTRL_GET_DEINTERLACE:
- return vo_control(d_video->vo, VOCTRL_GET_DEINTERLACE, data) == VO_TRUE;
- case VFCTRL_SET_DEINTERLACE:
- return vo_control(d_video->vo, VOCTRL_SET_DEINTERLACE, data) == VO_TRUE;
- case VFCTRL_SET_EQUALIZER: {
- vf_equalizer_t *eq = data;
- if (!d_video->vo->config_ok)
- return CONTROL_FALSE; // vo not configured?
- struct voctrl_set_equalizer_args param = {
- eq->item, eq->value
- };
- return vo_control(d_video->vo, VOCTRL_SET_EQUALIZER, &param) == VO_TRUE;
+ bool segment_ended = !d_video->current_mpi && !had_input_packet;
+
+ if (d_video->current_mpi && d_video->current_mpi->pts != MP_NOPTS_VALUE) {
+ double vpts = d_video->current_mpi->pts;
+ segment_ended = d_video->end != MP_NOPTS_VALUE && vpts >= d_video->end;
+ if ((d_video->start != MP_NOPTS_VALUE && vpts < d_video->start)
+ || segment_ended)
+ {
+ talloc_free(d_video->current_mpi);
+ d_video->current_mpi = NULL;
+ }
}
- case VFCTRL_GET_EQUALIZER: {
- vf_equalizer_t *eq = data;
- if (!d_video->vo->config_ok)
- return CONTROL_FALSE; // vo not configured?
- struct voctrl_get_equalizer_args param = {
- eq->item, &eq->value
- };
- return vo_control(d_video->vo, VOCTRL_GET_EQUALIZER, &param) == VO_TRUE;
+
+ // If there's a new segment, start it as soon as we're drained/finished.
+ if (segment_ended && d_video->new_segment) {
+ struct demux_packet *new_segment = d_video->new_segment;
+ d_video->new_segment = NULL;
+
+ // Could avoid decoder reinit; would still need flush.
+ d_video->codec = new_segment->codec;
+ if (d_video->vd_driver)
+ d_video->vd_driver->uninit(d_video);
+ d_video->vd_driver = NULL;
+ video_init_best_codec(d_video);
+
+ d_video->start = new_segment->start;
+ d_video->end = new_segment->end;
+
+ new_segment->new_segment = false;
+
+ d_video->packet = new_segment;
+ d_video->current_state = DATA_AGAIN;
}
+}
+
+// Fetch an image decoded with video_work(). Returns one of:
+// DATA_OK: *out_mpi is set to a new image
+// DATA_WAIT: waiting for demuxer; will receive a wakeup signal
+// DATA_EOF: end of file, no more frames to be expected
+// DATA_AGAIN: dropped frame or something similar
+int video_get_frame(struct dec_video *d_video, struct mp_image **out_mpi)
+{
+ *out_mpi = NULL;
+ if (d_video->current_mpi) {
+ *out_mpi = d_video->current_mpi;
+ d_video->current_mpi = NULL;
+ return DATA_OK;
}
- return CONTROL_UNKNOWN;
+ if (d_video->current_state == DATA_OK)
+ return DATA_AGAIN;
+ return d_video->current_state;
}
diff --git a/video/decode/dec_video.h b/video/decode/dec_video.h
index ad33d78..f4646a9 100644
--- a/video/decode/dec_video.h
+++ b/video/decode/dec_video.h
@@ -31,22 +31,23 @@ struct dec_video {
struct mp_log *log;
struct mpv_global *global;
struct MPOpts *opts;
- struct vf_chain *vfilter; // video filter chain
- struct vo *vo; // (still) needed by video_set/get_colors
const struct vd_functions *vd_driver;
struct mp_hwdec_info *hwdec_info; // video output hwdec handles
struct sh_stream *header;
+ struct mp_codec_params *codec;
char *decoder_desc;
- // Used temporarily during decoding (important for format changes)
- struct mp_image *waiting_decoded_mpi;
- struct mp_image_params decoder_output; // last output of the decoder
+ float fps; // FPS from demuxer or from user override
- struct mp_image *cover_art_mpi;
+ int dropped_frames;
+
+ // Internal (shared with vd_lavc.c).
void *priv; // for free use by vd_driver
+ // Strictly internal (dec_video.c).
+
// Last PTS from decoder (set with each vd_driver->decode() call)
double codec_pts;
int num_codec_pts_problems;
@@ -55,10 +56,6 @@ struct dec_video {
double codec_dts;
int num_codec_dts_problems;
- // PTS sorting (needed for AVI-style timestamps)
- double buffered_pts[128];
- int num_buffered_pts;
-
// PTS or DTS of packet first read
double first_packet_pdts;
@@ -68,31 +65,32 @@ struct dec_video {
// Final PTS of previously decoded image
double decoded_pts;
- float fps; // FPS from demuxer or from user override
+ struct mp_image_params last_format, fixed_format;
float initial_decoder_aspect;
- // State used only by player/video.c
- double last_pts;
+ double start_pts;
+ double start, end;
+ struct demux_packet *new_segment;
+ struct demux_packet *packet;
+ bool framedrop_enabled;
+ struct mp_image *cover_art_mpi;
+ struct mp_image *current_mpi;
+ int current_state;
};
struct mp_decoder_list *video_decoder_list(void);
-bool video_init_best_codec(struct dec_video *d_video, char* video_decoders);
+bool video_init_best_codec(struct dec_video *d_video);
void video_uninit(struct dec_video *d_video);
-struct demux_packet;
-struct mp_image *video_decode(struct dec_video *d_video,
- struct demux_packet *packet,
- int drop_frame);
+void video_work(struct dec_video *d_video);
+int video_get_frame(struct dec_video *d_video, struct mp_image **out_mpi);
-int video_get_colors(struct dec_video *d_video, const char *item, int *value);
-int video_set_colors(struct dec_video *d_video, const char *item, int value);
-void video_reset_decoding(struct dec_video *d_video);
-int video_vd_control(struct dec_video *d_video, int cmd, void *arg);
+void video_set_framedrop(struct dec_video *d_video, bool enabled);
+void video_set_start(struct dec_video *d_video, double start_pts);
-int video_reconfig_filters(struct dec_video *d_video,
- const struct mp_image_params *params);
-
-int video_vf_vo_control(struct dec_video *d_video, int vf_cmd, void *data);
+int video_vd_control(struct dec_video *d_video, int cmd, void *arg);
+void video_reset(struct dec_video *d_video);
+void video_reset_aspect(struct dec_video *d_video);
#endif /* MPLAYER_DEC_VIDEO_H */
diff --git a/video/decode/dxva2.c b/video/decode/dxva2.c
index 7ae08c4..d4ce0c7 100644
--- a/video/decode/dxva2.c
+++ b/video/decode/dxva2.c
@@ -25,19 +25,22 @@
#include <stdint.h>
-#include <d3d9.h>
-#include <dxva2api.h>
+#include <ks.h>
#include <libavcodec/dxva2.h>
#include "lavc.h"
#include "common/common.h"
#include "common/av_common.h"
+#include "osdep/windows_utils.h"
#include "video/fmt-conversion.h"
+#include "video/dxva2.h"
#include "video/mp_image_pool.h"
#include "video/hwdec.h"
#include "video/d3d.h"
+#define ADDITIONAL_SURFACES (4 + HWDEC_DELAY_QUEUE_COUNT)
+
// A minor evil.
#ifndef FF_DXVA2_WORKAROUND_INTEL_CLEARVIDEO
#define FF_DXVA2_WORKAROUND_INTEL_CLEARVIDEO 2
@@ -56,43 +59,53 @@ DEFINE_GUID(DXVADDI_Intel_ModeH264_E, 0x604F8E68, 0x4951,0x4C54,0x88,0xFE,0xAB,0
DEFINE_GUID(DXVA2_ModeVC1_D, 0x1b81beA3, 0xa0c7,0x11d3,0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5);
DEFINE_GUID(DXVA2_ModeVC1_D2010, 0x1b81beA4, 0xa0c7,0x11d3,0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5);
DEFINE_GUID(DXVA2_ModeHEVC_VLD_Main, 0x5b11d51b, 0x2f4c,0x4452,0xbc,0xc3,0x09,0xf2,0xa1,0x16,0x0c,0xc0);
+DEFINE_GUID(DXVA2_ModeHEVC_VLD_Main10,0x107af0e0, 0xef1a,0x4d19,0xab,0xa8,0x67,0xa1,0x63,0x07,0x3d,0x13);
DEFINE_GUID(DXVA2_NoEncrypt, 0x1b81beD0, 0xa0c7,0x11d3,0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5);
-DEFINE_GUID(GUID_NULL, 0x00000000, 0x0000,0x0000,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00);
typedef IDirect3D9* WINAPI pDirect3DCreate9(UINT);
typedef HRESULT WINAPI pCreateDeviceManager9(UINT *, IDirect3DDeviceManager9 **);
typedef struct dxva2_mode {
const GUID *guid;
+ const char *name;
enum AVCodecID codec;
+ int depth; // defaults to 8
} dxva2_mode;
+#define MODE(id) &MP_CONCAT(DXVA2_Mode, id), # id
+
static const dxva2_mode dxva2_modes[] = {
/* MPEG-2 */
- { &DXVA2_ModeMPEG2_VLD, AV_CODEC_ID_MPEG2VIDEO },
- { &DXVA2_ModeMPEG2and1_VLD, AV_CODEC_ID_MPEG2VIDEO },
+ { MODE(MPEG2_VLD), AV_CODEC_ID_MPEG2VIDEO },
+ { MODE(MPEG2and1_VLD), AV_CODEC_ID_MPEG2VIDEO },
/* H.264 */
- { &DXVA2_ModeH264_F, AV_CODEC_ID_H264 },
- { &DXVA2_ModeH264_E, AV_CODEC_ID_H264 },
+ { MODE(H264_F), AV_CODEC_ID_H264 },
+ { MODE(H264_E), AV_CODEC_ID_H264 },
/* Intel specific H.264 mode */
- { &DXVADDI_Intel_ModeH264_E, AV_CODEC_ID_H264 },
+ { &DXVADDI_Intel_ModeH264_E, "Intel_ModeH264_E", AV_CODEC_ID_H264 },
/* VC-1 / WMV3 */
- { &DXVA2_ModeVC1_D2010, AV_CODEC_ID_VC1 },
- { &DXVA2_ModeVC1_D2010, AV_CODEC_ID_WMV3 },
- { &DXVA2_ModeVC1_D, AV_CODEC_ID_VC1 },
- { &DXVA2_ModeVC1_D, AV_CODEC_ID_WMV3 },
+ { MODE(VC1_D2010), AV_CODEC_ID_VC1 },
+ { MODE(VC1_D2010), AV_CODEC_ID_WMV3 },
+ { MODE(VC1_D), AV_CODEC_ID_VC1 },
+ { MODE(VC1_D), AV_CODEC_ID_WMV3 },
- { &DXVA2_ModeHEVC_VLD_Main, AV_CODEC_ID_HEVC },
+ { MODE(HEVC_VLD_Main), AV_CODEC_ID_HEVC },
+ { MODE(HEVC_VLD_Main10),AV_CODEC_ID_HEVC, .depth = 10},
- { NULL, 0 },
+ { NULL, 0 },
};
-typedef struct surface_info {
- int used;
- uint64_t age;
-} surface_info;
+#undef MODE
+
+struct dxva2_decoder {
+ DXVA2_ConfigPictureDecode config;
+ IDirectXVideoDecoder *decoder;
+ LPDIRECT3DSURFACE9 *surfaces;
+ int num_surfaces;
+ struct mp_image_pool *pool;
+};
typedef struct DXVA2Context {
struct mp_log *log;
@@ -106,57 +119,19 @@ typedef struct DXVA2Context {
IDirect3DDevice9 *d3d9device;
IDirect3DDeviceManager9 *d3d9devmgr;
IDirectXVideoDecoderService *decoder_service;
- IDirectXVideoDecoder *decoder;
+ struct dxva2_decoder *decoder;
- GUID decoder_guid;
- DXVA2_ConfigPictureDecode decoder_config;
-
- LPDIRECT3DSURFACE9 *surfaces;
- surface_info *surface_infos;
- uint32_t num_surfaces;
- uint64_t surface_age;
-
- struct mp_image_pool *sw_pool;
-
- struct dxva_context *av_dxva_ctx;
+ struct mp_image_pool *sw_pool;
+ int mp_format;
} DXVA2Context;
-typedef struct DXVA2SurfaceWrapper {
- DXVA2Context *ctx;
- LPDIRECT3DSURFACE9 surface;
- IDirectXVideoDecoder *decoder;
-} DXVA2SurfaceWrapper;
-
-static void dxva2_destroy_decoder(struct lavc_ctx *s)
-{
- DXVA2Context *ctx = s->hwdec_priv;
- int i;
-
- if (ctx->surfaces) {
- for (i = 0; i < ctx->num_surfaces; i++) {
- if (ctx->surfaces[i])
- IDirect3DSurface9_Release(ctx->surfaces[i]);
- }
- }
- av_freep(&ctx->surfaces);
- av_freep(&ctx->surface_infos);
- ctx->num_surfaces = 0;
- ctx->surface_age = 0;
-
- if (ctx->decoder) {
- IDirectXVideoDecoder_Release(ctx->decoder);
- ctx->decoder = NULL;
- }
-}
-
static void dxva2_uninit(struct lavc_ctx *s)
{
DXVA2Context *ctx = s->hwdec_priv;
if (!ctx)
return;
- if (ctx->decoder)
- dxva2_destroy_decoder(s);
+ talloc_free(ctx->decoder);
if (ctx->decoder_service)
IDirectXVideoDecoderService_Release(ctx->decoder_service);
@@ -184,66 +159,22 @@ static void dxva2_uninit(struct lavc_ctx *s)
s->hwdec_priv = NULL;
}
-static void dxva2_release_img(void *ptr)
-{
- DXVA2SurfaceWrapper *w = ptr;
- DXVA2Context *ctx = w->ctx;
- int i;
-
- for (i = 0; i < ctx->num_surfaces; i++) {
- if (ctx->surfaces[i] == w->surface) {
- ctx->surface_infos[i].used = 0;
- break;
- }
- }
- IDirect3DSurface9_Release(w->surface);
- IDirectXVideoDecoder_Release(w->decoder);
- av_free(w);
-}
-
-static struct mp_image *dxva2_allocate_image(struct lavc_ctx *s,
- int img_w, int img_h)
+static struct mp_image *dxva2_allocate_image(struct lavc_ctx *s, int w, int h)
{
DXVA2Context *ctx = s->hwdec_priv;
- int i, old_unused = -1;
- for (i = 0; i < ctx->num_surfaces; i++) {
- surface_info *info = &ctx->surface_infos[i];
- if (!info->used && (old_unused == -1 || info->age < ctx->surface_infos[old_unused].age))
- old_unused = i;
- }
- if (old_unused == -1) {
- MP_ERR(ctx, "No free DXVA2 surface!\n");
- return NULL;
- }
- i = old_unused;
-
- DXVA2SurfaceWrapper *w = av_mallocz(sizeof(*w));
- if (!w)
- return NULL;
-
- w->ctx = ctx;
- w->surface = ctx->surfaces[i];;
- IDirect3DSurface9_AddRef(w->surface);
- w->decoder = ctx->decoder;
- IDirectXVideoDecoder_AddRef(w->decoder);
-
- ctx->surface_infos[i].used = 1;
- ctx->surface_infos[i].age = ctx->surface_age++;
-
- struct mp_image mpi = {0};
- mp_image_setfmt(&mpi, IMGFMT_DXVA2);
- mp_image_set_size(&mpi, img_w, img_h);
- mpi.planes[3] = (void *)w->surface;
-
- return mp_image_new_custom_ref(&mpi, w, dxva2_release_img);
+ struct mp_image *img = mp_image_pool_get_no_alloc(ctx->decoder->pool,
+ IMGFMT_DXVA2, w, h);
+ if (!img)
+ MP_ERR(ctx, "Failed to allocate additional DXVA2 surface.\n");
+ return img;
}
static void copy_nv12(struct mp_image *dest, uint8_t *src_bits,
unsigned src_pitch, unsigned surf_height)
{
struct mp_image buf = {0};
- mp_image_setfmt(&buf, IMGFMT_NV12);
+ mp_image_setfmt(&buf, dest->imgfmt);
mp_image_set_size(&buf, dest->w, dest->h);
buf.planes[0] = src_bits;
@@ -257,7 +188,7 @@ static struct mp_image *dxva2_retrieve_image(struct lavc_ctx *s,
struct mp_image *img)
{
DXVA2Context *ctx = s->hwdec_priv;
- LPDIRECT3DSURFACE9 surface = (LPDIRECT3DSURFACE9)img->planes[3];
+ LPDIRECT3DSURFACE9 surface = d3d9_surface_in_mp_image(img);
D3DSURFACE_DESC surfaceDesc;
D3DLOCKED_RECT LockedRect;
HRESULT hr;
@@ -267,15 +198,17 @@ static struct mp_image *dxva2_retrieve_image(struct lavc_ctx *s,
if (surfaceDesc.Width < img->w || surfaceDesc.Height < img->h)
return img;
- struct mp_image *sw_img =
- mp_image_pool_get(ctx->sw_pool, IMGFMT_NV12, surfaceDesc.Width, surfaceDesc.Height);
+ struct mp_image *sw_img = mp_image_pool_get(ctx->sw_pool, ctx->mp_format,
+ surfaceDesc.Width,
+ surfaceDesc.Height);
if (!sw_img)
return img;
hr = IDirect3DSurface9_LockRect(surface, &LockedRect, NULL, D3DLOCK_READONLY);
if (FAILED(hr)) {
- MP_ERR(ctx, "Unable to lock DXVA2 surface\n");
+ MP_ERR(ctx, "Unable to lock DXVA2 surface: %s\n",
+ mp_HRESULT_to_str(hr));
talloc_free(sw_img);
return img;
}
@@ -339,7 +272,8 @@ static int create_device(struct lavc_ctx *s)
D3DCREATE_SOFTWARE_VERTEXPROCESSING | D3DCREATE_MULTITHREADED | D3DCREATE_FPU_PRESERVE,
&d3dpp, &ctx->d3d9device);
if (FAILED(hr)) {
- MP_ERR(ctx, "Failed to create Direct3D device\n");
+ MP_ERR(ctx, "Failed to create Direct3D device: %s\n",
+ mp_HRESULT_to_str(hr));
goto fail;
}
@@ -362,9 +296,11 @@ static int dxva2_init(struct lavc_ctx *s)
s->hwdec_priv = ctx;
ctx->log = mp_log_new(s, s->log, "dxva2");
- ctx->sw_pool = talloc_steal(ctx, mp_image_pool_new(17));
- mp_check_gpu_memcpy(ctx->log, NULL);
+ if (s->hwdec->type == HWDEC_DXVA2_COPY) {
+ mp_check_gpu_memcpy(ctx->log, NULL);
+ ctx->sw_pool = talloc_steal(ctx, mp_image_pool_new(17));
+ }
ctx->deviceHandle = INVALID_HANDLE_VALUE;
@@ -385,25 +321,29 @@ static int dxva2_init(struct lavc_ctx *s)
hr = createDeviceManager(&resetToken, &ctx->d3d9devmgr);
if (FAILED(hr)) {
- MP_ERR(ctx, "Failed to create Direct3D device manager\n");
+ MP_ERR(ctx, "Failed to create Direct3D device manager: %s\n",
+ mp_HRESULT_to_str(hr));
goto fail;
}
hr = IDirect3DDeviceManager9_ResetDevice(ctx->d3d9devmgr, ctx->d3d9device, resetToken);
if (FAILED(hr)) {
- MP_ERR(ctx, "Failed to bind Direct3D device to device manager\n");
+ MP_ERR(ctx, "Failed to bind Direct3D device to device manager: %s\n",
+ mp_HRESULT_to_str(hr));
goto fail;
}
hr = IDirect3DDeviceManager9_OpenDeviceHandle(ctx->d3d9devmgr, &ctx->deviceHandle);
if (FAILED(hr)) {
- MP_ERR(ctx, "Failed to open device handle\n");
+ MP_ERR(ctx, "Failed to open device handle: %s\n",
+ mp_HRESULT_to_str(hr));
goto fail;
}
hr = IDirect3DDeviceManager9_GetVideoService(ctx->d3d9devmgr, ctx->deviceHandle, &IID_IDirectXVideoDecoderService, (void **)&ctx->decoder_service);
if (FAILED(hr)) {
- MP_ERR(ctx, "Failed to create IDirectXVideoDecoderService\n");
+ MP_ERR(ctx, "Failed to create IDirectXVideoDecoderService: %s\n",
+ mp_HRESULT_to_str(hr));
goto fail;
}
@@ -432,7 +372,8 @@ static int dxva2_get_decoder_configuration(struct lavc_ctx *s,
hr = IDirectXVideoDecoderService_GetDecoderConfigurations(ctx->decoder_service, device_guid, desc, NULL, &cfg_count, &cfg_list);
if (FAILED(hr)) {
- MP_ERR(ctx, "Unable to retrieve decoder configurations\n");
+ MP_ERR(ctx, "Unable to retrieve decoder configurations: %s\n",
+ mp_HRESULT_to_str(hr));
return -1;
}
@@ -464,31 +405,70 @@ static int dxva2_get_decoder_configuration(struct lavc_ctx *s,
return 0;
}
+static void dxva2_destroy_decoder(void *arg)
+{
+ struct dxva2_decoder *decoder = arg;
+ if (decoder->decoder)
+ IDirectXVideoDecoder_Release(decoder->decoder);
+
+ if (decoder->surfaces) {
+ for (int i = 0; i < decoder->num_surfaces; i++)
+ IDirect3DSurface9_Release(decoder->surfaces[i]);
+ }
+}
+
static int dxva2_create_decoder(struct lavc_ctx *s, int w, int h,
enum AVCodecID codec_id, int profile)
{
DXVA2Context *ctx = s->hwdec_priv;
struct dxva_context *dxva_ctx = s->avctx->hwaccel_context;
+ void *tmp = talloc_new(NULL);
GUID *guid_list = NULL;
unsigned guid_count = 0, i, j;
GUID device_guid = GUID_NULL;
D3DFORMAT target_format = 0;
DXVA2_VideoDesc desc = { 0 };
- DXVA2_ConfigPictureDecode config;
HRESULT hr;
+ struct dxva2_decoder *decoder;
int surface_alignment;
- int ret;
+ int ret = -1;
hr = IDirectXVideoDecoderService_GetDecoderDeviceGuids(ctx->decoder_service, &guid_count, &guid_list);
if (FAILED(hr)) {
- MP_ERR(ctx, "Failed to retrieve decoder device GUIDs\n");
+ MP_ERR(ctx, "Failed to retrieve decoder device GUIDs: %s\n",
+ mp_HRESULT_to_str(hr));
goto fail;
}
+ // dump all decoder info
+ MP_VERBOSE(ctx, "%d decoder devices:\n", (int)guid_count);
+ for (j = 0; j < guid_count; j++) {
+ GUID *guid = &guid_list[j];
+
+ const char *name = "<unknown>";
+ for (i = 0; dxva2_modes[i].guid; i++) {
+ if (IsEqualGUID(dxva2_modes[i].guid, guid))
+ name = dxva2_modes[i].name;
+ }
+
+ D3DFORMAT *target_list = NULL;
+ unsigned target_count = 0;
+ hr = IDirectXVideoDecoderService_GetDecoderRenderTargets(ctx->decoder_service, guid, &target_count, &target_list);
+ if (FAILED(hr))
+ continue;
+ char fmts[256] = {0};
+ for (i = 0; i < target_count; i++)
+ mp_snprintf_cat(fmts, sizeof(fmts), " %s", mp_tag_str(target_list[i]));
+ CoTaskMemFree(target_list);
+ MP_VERBOSE(ctx, "%s %s %s\n", mp_GUID_to_str(guid), name, fmts);
+ }
+
+ // find a suitable decoder
for (i = 0; dxva2_modes[i].guid; i++) {
D3DFORMAT *target_list = NULL;
unsigned target_count = 0;
const dxva2_mode *mode = &dxva2_modes[i];
+ int depth = mode->depth;
if (mode->codec != codec_id)
continue;
@@ -499,19 +479,39 @@ static int dxva2_create_decoder(struct lavc_ctx *s, int w, int h,
if (j == guid_count)
continue;
+ if (codec_id == AV_CODEC_ID_HEVC) {
+ if ((mode->depth > 8) != (s->avctx->profile == FF_PROFILE_HEVC_MAIN_10))
+ continue;
+ }
+
hr = IDirectXVideoDecoderService_GetDecoderRenderTargets(ctx->decoder_service, mode->guid, &target_count, &target_list);
if (FAILED(hr)) {
continue;
}
for (j = 0; j < target_count; j++) {
const D3DFORMAT format = target_list[j];
- if (format == MKTAG('N','V','1','2')) {
- target_format = format;
- break;
+ if (depth <= 8) {
+ if (format == MKTAG('N','V','1','2')) {
+ ctx->mp_format = IMGFMT_NV12;
+ target_format = format;
+ }
+ } else {
+ int p010 = mp_imgfmt_find(1, 1, 2, 10, MP_IMGFLAG_YUV_NV);
+ if (p010 && (format == MKTAG('P','0','1','0') ||
+ format == MKTAG('P','0','1','6')))
+ {
+ // There is no FFmpeg format that is like NV12 and supports
+ // 16 bit per component, but vo_opengl will use the lower
+ // bits in P010 anyway.
+ ctx->mp_format = p010;
+ target_format = format;
+ }
}
+ if (target_format)
+ break;
}
CoTaskMemFree(target_list);
- if (target_format) {
+ if (target_format && ctx->mp_format) {
device_guid = *mode->guid;
break;
}
@@ -527,8 +527,10 @@ static int dxva2_create_decoder(struct lavc_ctx *s, int w, int h,
desc.SampleHeight = h;
desc.Format = target_format;
- ret = dxva2_get_decoder_configuration(s, codec_id, &device_guid, &desc, &config);
- if (ret < 0) {
+ decoder = talloc_zero(tmp, struct dxva2_decoder);
+ talloc_set_destructor(decoder, dxva2_destroy_decoder);
+ if (dxva2_get_decoder_configuration(s, codec_id, &device_guid, &desc,
+ &decoder->config) < 0) {
goto fail;
}
@@ -543,58 +545,52 @@ static int dxva2_create_decoder(struct lavc_ctx *s, int w, int h,
else
surface_alignment = 16;
- /* 4 base work surfaces */
- ctx->num_surfaces = 4;
-
- /* add surfaces based on number of possible refs */
- if (codec_id == AV_CODEC_ID_H264 || codec_id == AV_CODEC_ID_HEVC)
- ctx->num_surfaces += 16;
- else
- ctx->num_surfaces += 2;
-
- ctx->surfaces = av_mallocz(ctx->num_surfaces * sizeof(*ctx->surfaces));
- ctx->surface_infos = av_mallocz(ctx->num_surfaces * sizeof(*ctx->surface_infos));
+ decoder->num_surfaces = hwdec_get_max_refs(s) + ADDITIONAL_SURFACES;
- if (!ctx->surfaces || !ctx->surface_infos) {
- MP_ERR(ctx, "Unable to allocate surface arrays\n");
- goto fail;
- }
-
- hr = IDirectXVideoDecoderService_CreateSurface(ctx->decoder_service,
- FFALIGN(w, surface_alignment),
- FFALIGN(h, surface_alignment),
- ctx->num_surfaces - 1,
- target_format, D3DPOOL_DEFAULT, 0,
- DXVA2_VideoDecoderRenderTarget,
- ctx->surfaces, NULL);
+ decoder->surfaces = talloc_array(decoder, LPDIRECT3DSURFACE9, decoder->num_surfaces);
+ hr = IDirectXVideoDecoderService_CreateSurface(
+ ctx->decoder_service,
+ FFALIGN(w, surface_alignment), FFALIGN(h, surface_alignment),
+ decoder->num_surfaces - 1, target_format, D3DPOOL_DEFAULT, 0,
+ DXVA2_VideoDecoderRenderTarget, decoder->surfaces, NULL);
if (FAILED(hr)) {
- MP_ERR(ctx, "Failed to create %d video surfaces\n", ctx->num_surfaces);
+ MP_ERR(ctx, "Failed to create %d video surfaces: %s\n",
+ decoder->num_surfaces, mp_HRESULT_to_str(hr));
goto fail;
}
- hr = IDirectXVideoDecoderService_CreateVideoDecoder(ctx->decoder_service, &device_guid,
- &desc, &config, ctx->surfaces,
- ctx->num_surfaces, &ctx->decoder);
+ hr = IDirectXVideoDecoderService_CreateVideoDecoder(
+ ctx->decoder_service, &device_guid, &desc, &decoder->config,
+ decoder->surfaces, decoder->num_surfaces, &decoder->decoder);
if (FAILED(hr)) {
- MP_ERR(ctx, "Failed to create DXVA2 video decoder\n");
+ MP_ERR(ctx, "Failed to create DXVA2 video decoder: %s\n",
+ mp_HRESULT_to_str(hr));
goto fail;
}
+ decoder->pool = talloc_steal(decoder, mp_image_pool_new(decoder->num_surfaces));
+ for (i = 0; i < decoder->num_surfaces; i++) {
+ struct mp_image *img = dxva2_new_ref(decoder->decoder, decoder->surfaces[i], w, h);
+ if (!img) {
+ MP_ERR(ctx, "Failed to create DXVA2 image\n");
+ goto fail;
+ }
+ mp_image_pool_add(decoder->pool, img);
+ }
- ctx->decoder_guid = device_guid;
- ctx->decoder_config = config;
-
- dxva_ctx->cfg = &ctx->decoder_config;
- dxva_ctx->decoder = ctx->decoder;
- dxva_ctx->surface = ctx->surfaces;
- dxva_ctx->surface_count = ctx->num_surfaces;
+ // Pass required information on to ffmpeg.
+ dxva_ctx->cfg = &decoder->config;
+ dxva_ctx->decoder = decoder->decoder;
+ dxva_ctx->surface = decoder->surfaces;
+ dxva_ctx->surface_count = decoder->num_surfaces;
- if (IsEqualGUID(&ctx->decoder_guid, &DXVADDI_Intel_ModeH264_E))
+ if (IsEqualGUID(&device_guid, &DXVADDI_Intel_ModeH264_E))
dxva_ctx->workaround |= FF_DXVA2_WORKAROUND_INTEL_CLEARVIDEO;
- return 0;
+ ctx->decoder = talloc_steal(NULL, decoder);
+ ret = 0;
fail:
- dxva2_destroy_decoder(s);
- return -1;
+ talloc_free(tmp);
+ return ret;
}
static int dxva2_init_decoder(struct lavc_ctx *s, int w, int h)
@@ -609,13 +605,15 @@ static int dxva2_init_decoder(struct lavc_ctx *s, int w, int h)
MP_ERR(ctx, "Unsupported H.264 profile for DXVA2 HWAccel: %d\n", profile);
return -1;
}
- if (codec == AV_CODEC_ID_HEVC && profile != FF_PROFILE_HEVC_MAIN) {
+ if (codec == AV_CODEC_ID_HEVC && profile != FF_PROFILE_HEVC_MAIN &&
+ profile != FF_PROFILE_HEVC_MAIN_10)
+ {
MP_ERR(ctx, "Unsupported H.265 profile for DXVA2 HWAccel: %d\n", profile);
return -1;
}
- if (ctx->decoder)
- dxva2_destroy_decoder(s);
+ talloc_free(ctx->decoder);
+ ctx->decoder = NULL;
if (dxva2_create_decoder(s, w, h, codec, profile) < 0) {
MP_ERR(ctx, "Error creating the DXVA2 decoder\n");
@@ -628,7 +626,12 @@ static int dxva2_init_decoder(struct lavc_ctx *s, int w, int h)
static int probe(struct vd_lavc_hwdec *hwdec, struct mp_hwdec_info *info,
const char *decoder)
{
- hwdec_request_api(info, "dxva2"); // we can do without too
+ hwdec_request_api(info, "dxva2");
+ // dxva2-copy can do without external context; dxva2 requires it.
+ if (hwdec->type != HWDEC_DXVA2_COPY) {
+ if (!info || !info->hwctx || !info->hwctx->d3d_ctx)
+ return HWDEC_ERR_NO_CTX;
+ }
for (int i = 0; dxva2_modes[i].guid; i++) {
const dxva2_mode *mode = &dxva2_modes[i];
if (mp_codec_to_av_codec_id(decoder) == mode->codec)
@@ -637,6 +640,16 @@ static int probe(struct vd_lavc_hwdec *hwdec, struct mp_hwdec_info *info,
return HWDEC_ERR_NO_CODEC;
}
+const struct vd_lavc_hwdec mp_vd_lavc_dxva2 = {
+ .type = HWDEC_DXVA2,
+ .image_format = IMGFMT_DXVA2,
+ .probe = probe,
+ .init = dxva2_init,
+ .uninit = dxva2_uninit,
+ .init_decoder = dxva2_init_decoder,
+ .allocate_image = dxva2_allocate_image,
+};
+
const struct vd_lavc_hwdec mp_vd_lavc_dxva2_copy = {
.type = HWDEC_DXVA2_COPY,
.image_format = IMGFMT_DXVA2,
diff --git a/video/decode/lavc.h b/video/decode/lavc.h
index 76b7ac7..9ef7466 100644
--- a/video/decode/lavc.h
+++ b/video/decode/lavc.h
@@ -9,20 +9,26 @@
#include "video/mp_image.h"
#include "video/hwdec.h"
+#define HWDEC_DELAY_QUEUE_COUNT 2
+
typedef struct lavc_ctx {
struct mp_log *log;
struct MPOpts *opts;
AVCodecContext *avctx;
AVFrame *pic;
struct vd_lavc_hwdec *hwdec;
+ AVRational codec_timebase;
enum AVPixelFormat pix_fmt;
- int best_csp;
enum AVDiscard skip_frame;
bool flushing;
- const char *software_fallback_decoder;
+ const char *decoder;
bool hwdec_failed;
bool hwdec_notified;
+ struct mp_image **delay_queue;
+ int num_delay_queue;
+ int max_delay_queue;
+
// From VO
struct mp_hwdec_info *hwdec_info;
diff --git a/video/decode/rpi.c b/video/decode/rpi.c
index 12816cd..b72dc34 100644
--- a/video/decode/rpi.c
+++ b/video/decode/rpi.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 "lavc.h"
@@ -21,6 +21,8 @@
static const char *const codecs[][2] = {
{"h264", "h264_mmal"},
{"mpeg2video", "mpeg2_mmal"},
+ {"mpeg4", "mpeg4_mmal"},
+ {"vc1", "vc1_mmal"},
{0}
};
diff --git a/video/decode/vaapi.c b/video/decode/vaapi.c
index 3fa0fd4..d7aa269 100644
--- a/video/decode/vaapi.c
+++ b/video/decode/vaapi.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 6
+#define ADDTIONAL_SURFACES MPMAX(6, HWDEC_DELAY_QUEUE_COUNT)
// Some upper bound.
#define MAX_SURFACES 25
@@ -109,6 +109,7 @@ static const struct va_native_display disp_x11 = {
#endif
#define HAS_HEVC VA_CHECK_VERSION(0, 38, 0)
+#define HAS_VP9 (VA_CHECK_VERSION(0, 38, 1) && defined(FF_PROFILE_VP9_0))
#define PE(av_codec_id, ff_profile, vdp_profile) \
{AV_CODEC_ID_ ## av_codec_id, FF_PROFILE_ ## ff_profile, \
@@ -133,6 +134,9 @@ static const struct hwdec_profile_entry profiles[] = {
PE(HEVC, HEVC_MAIN, HEVCMain),
PE(HEVC, HEVC_MAIN_10, HEVCMain10),
#endif
+#if HAS_VP9
+ PE(VP9, VP9_0, VP9Profile0),
+#endif
{0}
};
@@ -156,6 +160,9 @@ static const char *str_va_profile(VAProfile profile)
PROFILE(HEVCMain);
PROFILE(HEVCMain10);
#endif
+#if HAS_VP9
+ PROFILE(VP9Profile0);
+#endif
#undef PROFILE
}
return "<unknown>";
diff --git a/video/decode/vd.h b/video/decode/vd.h
index 6f5016a..c4b77f3 100644
--- a/video/decode/vd.h
+++ b/video/decode/vd.h
@@ -42,9 +42,10 @@ extern const vd_functions_t *const mpcodecs_vd_drivers[];
enum vd_ctrl {
VDCTRL_RESET = 1, // reset decode state after seeking
- VDCTRL_QUERY_UNSEEN_FRAMES, // current decoder lag
VDCTRL_FORCE_HWDEC_FALLBACK, // force software decoding fallback
VDCTRL_GET_HWDEC,
+ VDCTRL_REINIT,
+ VDCTRL_GET_BFRAMES,
};
#endif /* MPLAYER_VD_H */
diff --git a/video/decode/vd_lavc.c b/video/decode/vd_lavc.c
index 92ea8bd..0520c3a 100644
--- a/video/decode/vd_lavc.c
+++ b/video/decode/vd_lavc.c
@@ -27,7 +27,7 @@
#include <libavutil/intreadwrite.h>
#include <libavutil/pixdesc.h>
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "config.h"
#include "common/msg.h"
#include "options/options.h"
@@ -41,6 +41,7 @@
#include "video/img_format.h"
#include "video/filter/vf.h"
#include "video/decode/dec_video.h"
+#include "demux/demux.h"
#include "demux/stheader.h"
#include "demux/packet.h"
#include "video/csputils.h"
@@ -123,6 +124,7 @@ extern const struct vd_lavc_hwdec mp_vd_lavc_vdpau;
extern const struct vd_lavc_hwdec mp_vd_lavc_videotoolbox;
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_rpi;
@@ -141,6 +143,7 @@ static const struct vd_lavc_hwdec *const hwdec_list[] = {
&mp_vd_lavc_vaapi_copy,
#endif
#if HAVE_DXVA2_HWACCEL
+ &mp_vd_lavc_dxva2,
&mp_vd_lavc_dxva2_copy,
#endif
NULL
@@ -276,35 +279,22 @@ static void uninit(struct dec_video *vd)
static bool force_fallback(struct dec_video *vd)
{
vd_ffmpeg_ctx *ctx = vd->priv;
- if (!ctx->software_fallback_decoder)
+ if (!ctx->hwdec)
return false;
uninit_avctx(vd);
int lev = ctx->hwdec_notified ? MSGL_WARN : MSGL_V;
mp_msg(vd->log, lev, "Falling back to software decoding.\n");
- const char *decoder = ctx->software_fallback_decoder;
- ctx->software_fallback_decoder = NULL;
- init_avctx(vd, decoder, NULL);
+ init_avctx(vd, ctx->decoder, NULL);
return true;
}
-static int init(struct dec_video *vd, const char *decoder)
+static void reinit(struct dec_video *vd)
{
- vd_ffmpeg_ctx *ctx;
- ctx = vd->priv = talloc_zero(NULL, vd_ffmpeg_ctx);
- ctx->log = vd->log;
- ctx->opts = vd->opts;
+ vd_ffmpeg_ctx *ctx = vd->priv;
+ const char *decoder = ctx->decoder;
- if (bstr_endswith0(bstr0(decoder), "_vdpau")) {
- MP_WARN(vd, "VDPAU decoder '%s' was requested. "
- "This way of enabling hardware\ndecoding is not supported "
- "anymore. Use --hwdec=vdpau instead.\nThe --hwdec-codec=... "
- "option can be used to restrict which codecs are\nenabled, "
- "otherwise all hardware decoding is tried for all codecs.\n",
- decoder);
- uninit(vd);
- return 0;
- }
+ uninit_avctx(vd);
struct vd_lavc_hwdec *hwdec = NULL;
@@ -325,7 +315,6 @@ static int init(struct dec_video *vd, const char *decoder)
}
if (hwdec) {
- ctx->software_fallback_decoder = talloc_strdup(ctx, decoder);
if (hwdec->get_codec)
decoder = hwdec->get_codec(ctx, decoder);
MP_VERBOSE(vd, "Trying hardware decoding.\n");
@@ -334,14 +323,35 @@ static int init(struct dec_video *vd, const char *decoder)
}
init_avctx(vd, decoder, hwdec);
- if (!ctx->avctx) {
+ if (!ctx->avctx)
force_fallback(vd);
- if (!ctx->avctx) {
- uninit(vd);
- return 0;
- }
+}
+
+static int init(struct dec_video *vd, const char *decoder)
+{
+ vd_ffmpeg_ctx *ctx;
+ ctx = vd->priv = talloc_zero(NULL, vd_ffmpeg_ctx);
+ ctx->log = vd->log;
+ ctx->opts = vd->opts;
+ ctx->decoder = talloc_strdup(ctx, decoder);
+
+ if (bstr_endswith0(bstr0(decoder), "_vdpau")) {
+ MP_WARN(vd, "VDPAU decoder '%s' was requested. "
+ "This way of enabling hardware\ndecoding is not supported "
+ "anymore. Use --hwdec=vdpau instead.\nThe --hwdec-codec=... "
+ "option can be used to restrict which codecs are\nenabled, "
+ "otherwise all hardware decoding is tried for all codecs.\n",
+ decoder);
+ uninit(vd);
+ return 0;
}
+ reinit(vd);
+
+ if (!ctx->avctx) {
+ uninit(vd);
+ return 0;
+ }
return 1;
}
@@ -351,7 +361,7 @@ static void init_avctx(struct dec_video *vd, const char *decoder,
vd_ffmpeg_ctx *ctx = vd->priv;
struct vd_lavc_params *lavc_param = vd->opts->vd_lavc_params;
bool mp_rawvideo = false;
- struct sh_stream *sh = vd->header;
+ struct mp_codec_params *c = vd->codec;
assert(!ctx->avctx);
@@ -366,6 +376,10 @@ static void init_avctx(struct dec_video *vd, const char *decoder,
ctx->hwdec_info = vd->hwdec_info;
+ ctx->codec_timebase = (AVRational){0};
+ if (strstr(decoder, "_mmal"))
+ ctx->codec_timebase = (AVRational){1, 1000000};
+
ctx->pix_fmt = AV_PIX_FMT_NONE;
ctx->hwdec = hwdec;
ctx->hwdec_fmt = 0;
@@ -389,6 +403,10 @@ static void init_avctx(struct dec_video *vd, const char *decoder,
avctx->get_buffer2 = get_buffer2_hwdec;
if (ctx->hwdec->init(ctx) < 0)
goto error;
+ // This can increase efficiency by not blocking on the hardware
+ // pipeline by reading back immediately after decoding.
+ if (ctx->hwdec->process_image)
+ ctx->max_delay_queue = HWDEC_DELAY_QUEUE_COUNT;
} else {
mp_set_avcodec_threads(vd->log, avctx, lavc_param->threads);
}
@@ -414,23 +432,23 @@ static void init_avctx(struct dec_video *vd, const char *decoder,
// Do this after the above avopt handling in case it changes values
ctx->skip_frame = avctx->skip_frame;
- avctx->codec_tag = sh->codec_tag;
- avctx->coded_width = sh->video->disp_w;
- avctx->coded_height = sh->video->disp_h;
- avctx->bits_per_coded_sample = sh->video->bits_per_coded_sample;
+ avctx->codec_tag = c->codec_tag;
+ avctx->coded_width = c->disp_w;
+ avctx->coded_height = c->disp_h;
+ avctx->bits_per_coded_sample = c->bits_per_coded_sample;
- mp_lavc_set_extradata(avctx, sh->extradata, sh->extradata_size);
+ mp_lavc_set_extradata(avctx, c->extradata, c->extradata_size);
if (mp_rawvideo) {
- avctx->pix_fmt = imgfmt2pixfmt(sh->codec_tag);
+ avctx->pix_fmt = imgfmt2pixfmt(c->codec_tag);
avctx->codec_tag = 0;
- if (avctx->pix_fmt == AV_PIX_FMT_NONE && sh->codec_tag)
+ if (avctx->pix_fmt == AV_PIX_FMT_NONE && c->codec_tag)
MP_ERR(vd, "Image format %s not supported by lavc.\n",
- mp_imgfmt_to_name(sh->codec_tag));
+ mp_imgfmt_to_name(c->codec_tag));
}
- if (sh->lav_headers)
- mp_copy_lav_codec_headers(avctx, sh->lav_headers);
+ if (c->lav_headers)
+ mp_copy_lav_codec_headers(avctx, c->lav_headers);
/* open it */
if (avcodec_open2(avctx, lavc_codec, NULL) < 0)
@@ -447,21 +465,33 @@ static void reset_avctx(struct dec_video *vd)
{
vd_ffmpeg_ctx *ctx = vd->priv;
- if (ctx->avctx)
+ if (ctx->avctx && avcodec_is_open(ctx->avctx))
avcodec_flush_buffers(ctx->avctx);
ctx->flushing = false;
}
+static void flush_all(struct dec_video *vd)
+{
+ vd_ffmpeg_ctx *ctx = vd->priv;
+
+ for (int n = 0; n < ctx->num_delay_queue; n++)
+ talloc_free(ctx->delay_queue[n]);
+ ctx->num_delay_queue = 0;
+
+ reset_avctx(vd);
+}
+
static void uninit_avctx(struct dec_video *vd)
{
vd_ffmpeg_ctx *ctx = vd->priv;
- AVCodecContext *avctx = ctx->avctx;
- if (avctx) {
- if (avctx->codec && avcodec_close(avctx) < 0)
- MP_ERR(vd, "Could not close codec.\n");
+ flush_all(vd);
+ av_frame_free(&ctx->pic);
- av_freep(&avctx->extradata);
+ if (ctx->avctx) {
+ if (avcodec_close(ctx->avctx) < 0)
+ MP_ERR(vd, "Could not close codec.\n");
+ av_freep(&ctx->avctx->extradata);
}
if (ctx->hwdec && ctx->hwdec->uninit)
@@ -470,11 +500,9 @@ static void uninit_avctx(struct dec_video *vd)
av_freep(&ctx->avctx);
- av_frame_free(&ctx->pic);
-
- ctx->flushing = false;
ctx->hwdec_failed = false;
ctx->hwdec_fail_count = 0;
+ ctx->max_delay_queue = 0;
}
static void update_image_params(struct dec_video *vd, AVFrame *frame,
@@ -482,38 +510,23 @@ static void update_image_params(struct dec_video *vd, AVFrame *frame,
{
vd_ffmpeg_ctx *ctx = vd->priv;
struct MPOpts *opts = ctx->opts;
- int width = frame->width;
- int height = frame->height;
- float aspect = av_q2d(frame->sample_aspect_ratio) * width / height;
- int pix_fmt = frame->format;
-
- if (pix_fmt != ctx->pix_fmt) {
- ctx->pix_fmt = pix_fmt;
- ctx->best_csp = pixfmt2imgfmt(pix_fmt);
- if (!ctx->best_csp)
- MP_ERR(vd, "lavc pixel format %s not supported.\n",
- av_get_pix_fmt_name(pix_fmt));
- }
*out_params = (struct mp_image_params) {
- .imgfmt = ctx->best_csp,
- .w = width,
- .h = height,
- .d_w = 0,
- .d_h = 0,
+ .imgfmt = pixfmt2imgfmt(frame->format),
+ .w = frame->width,
+ .h = frame->height,
+ .p_w = frame->sample_aspect_ratio.num,
+ .p_h = frame->sample_aspect_ratio.den,
.colorspace = avcol_spc_to_mp_csp(ctx->avctx->colorspace),
.colorlevels = avcol_range_to_mp_csp_levels(ctx->avctx->color_range),
.primaries = avcol_pri_to_mp_csp_prim(ctx->avctx->color_primaries),
.gamma = avcol_trc_to_mp_csp_trc(ctx->avctx->color_trc),
.chroma_location =
avchroma_location_to_mp(ctx->avctx->chroma_sample_location),
- .rotate = vd->header->video->rotate,
- .stereo_in = vd->header->video->stereo_mode,
+ .rotate = vd->codec->rotate,
+ .stereo_in = vd->codec->stereo_mode,
};
- if (aspect > 0)
- vf_set_dar(&out_params->d_w, &out_params->d_h, width, height, aspect);
-
if (opts->video_rotate < 0) {
out_params->rotate = 0;
} else {
@@ -533,6 +546,12 @@ static enum AVPixelFormat get_format_hwdec(struct AVCodecContext *avctx,
MP_VERBOSE(vd, " %s", av_get_pix_fmt_name(fmt[i]));
MP_VERBOSE(vd, "\n");
+#if HAVE_AVCODEC_PROFILE_NAME
+ const char *profile = avcodec_profile_name(avctx->codec_id, avctx->profile);
+ MP_VERBOSE(vd, "Codec profile: %s (0x%x)\n", profile ? profile : "unknown",
+ avctx->profile);
+#endif
+
assert(ctx->hwdec);
ctx->hwdec_request_reinit |= ctx->hwdec_failed;
@@ -581,7 +600,7 @@ static int get_buffer2_hwdec(AVCodecContext *avctx, AVFrame *pic, int flags)
vd_ffmpeg_ctx *ctx = vd->priv;
int imgfmt = pixfmt2imgfmt(pic->format);
- if (!IMGFMT_IS_HWACCEL(imgfmt) || !ctx->hwdec)
+ if (!ctx->hwdec || ctx->hwdec_fmt != imgfmt)
ctx->hwdec_failed = true;
/* Hardware decoding failed, and we will trigger a proper fallback later
@@ -614,6 +633,22 @@ static int get_buffer2_hwdec(AVCodecContext *avctx, AVFrame *pic, int flags)
return 0;
}
+static struct mp_image *read_output(struct dec_video *vd)
+{
+ vd_ffmpeg_ctx *ctx = vd->priv;
+
+ if (!ctx->num_delay_queue)
+ return NULL;
+
+ struct mp_image *res = ctx->delay_queue[0];
+ MP_TARRAY_REMOVE_AT(ctx->delay_queue, ctx->num_delay_queue, 0);
+
+ if (ctx->hwdec && ctx->hwdec->process_image)
+ res = ctx->hwdec->process_image(ctx, res);
+
+ return mp_img_swap_to_native(res);
+}
+
static void decode(struct dec_video *vd, struct demux_packet *packet,
int flags, struct mp_image **out_image)
{
@@ -622,6 +657,7 @@ static void decode(struct dec_video *vd, struct demux_packet *packet,
vd_ffmpeg_ctx *ctx = vd->priv;
AVCodecContext *avctx = ctx->avctx;
struct vd_lavc_params *opts = ctx->opts->vd_lavc_params;
+ AVRational *tb = ctx->codec_timebase.num ? &ctx->codec_timebase : NULL;
AVPacket pkt;
if (!avctx)
@@ -635,7 +671,7 @@ static void decode(struct dec_video *vd, struct demux_packet *packet,
avctx->skip_frame = ctx->skip_frame;
}
- mp_set_av_packet(&pkt, packet, NULL);
+ mp_set_av_packet(&pkt, packet, tb);
ctx->flushing |= !pkt.data;
// Reset decoder if hw state got reset, or new data comes during flushing.
@@ -666,28 +702,48 @@ static void decode(struct dec_video *vd, struct demux_packet *packet,
return;
}
+ if (packet) {
+ // always fully consumed
+ packet->len = 0;
+ }
+
// Skipped frame, or delayed output due to multithreaded decoding.
- if (!got_picture)
+ if (!got_picture) {
+ if (!packet)
+ *out_image = read_output(vd);
return;
+ }
ctx->hwdec_fail_count = 0;
- struct mp_image_params params;
- update_image_params(vd, ctx->pic, &params);
- vd->codec_pts = mp_pts_from_av(ctx->pic->pkt_pts, NULL);
- vd->codec_dts = mp_pts_from_av(ctx->pic->pkt_dts, NULL);
+ AVFrameSideData *sd = NULL;
+ sd = av_frame_get_side_data(ctx->pic, AV_FRAME_DATA_A53_CC);
+ if (sd) {
+ struct demux_packet *cc = new_demux_packet_from(sd->data, sd->size);
+ cc->pts = vd->codec_pts;
+ cc->dts = vd->codec_dts;
+ cc->pos = -1;
+ demuxer_feed_caption(vd->header, cc);
+ }
struct mp_image *mpi = mp_image_from_av_frame(ctx->pic);
- av_frame_unref(ctx->pic);
- if (!mpi)
+ if (!mpi) {
+ av_frame_unref(ctx->pic);
return;
+ }
assert(mpi->planes[0] || mpi->planes[3]);
+ mpi->pts = mp_pts_from_av(ctx->pic->pkt_pts, tb);
+ mpi->dts = mp_pts_from_av(ctx->pic->pkt_dts, tb);
+
+ struct mp_image_params params;
+ update_image_params(vd, ctx->pic, &params);
mp_image_set_params(mpi, &params);
- if (ctx->hwdec && ctx->hwdec->process_image)
- mpi = ctx->hwdec->process_image(ctx, mpi);
+ av_frame_unref(ctx->pic);
- *out_image = mp_img_swap_to_native(mpi);
+ MP_TARRAY_APPEND(ctx, ctx->delay_queue, ctx->num_delay_queue, mpi);
+ if (ctx->num_delay_queue > ctx->max_delay_queue)
+ *out_image = read_output(vd);
}
static struct mp_image *decode_with_fallback(struct dec_video *vd,
@@ -723,32 +779,28 @@ static int control(struct dec_video *vd, int cmd, void *arg)
vd_ffmpeg_ctx *ctx = vd->priv;
switch (cmd) {
case VDCTRL_RESET:
- reset_avctx(vd);
+ flush_all(vd);
return CONTROL_TRUE;
- case VDCTRL_QUERY_UNSEEN_FRAMES: {
+ case VDCTRL_GET_BFRAMES: {
AVCodecContext *avctx = ctx->avctx;
if (!avctx)
break;
if (ctx->hwdec && ctx->hwdec->type == HWDEC_RPI)
break; // MMAL has arbitrary buffering, thus unknown
- int delay = avctx->has_b_frames;
- assert(delay >= 0);
- if (avctx->active_thread_type & FF_THREAD_FRAME)
- delay += avctx->thread_count - 1;
- *(int *)arg = delay;
+ *(int *)arg = avctx->has_b_frames;
return CONTROL_TRUE;
}
case VDCTRL_GET_HWDEC: {
- int hwdec = ctx->hwdec ? ctx->hwdec->type : 0;
- if (!ctx->software_fallback_decoder)
- hwdec = 0;
- *(int *)arg = hwdec;
+ *(int *)arg = ctx->hwdec ? ctx->hwdec->type : 0;
return CONTROL_TRUE;
}
case VDCTRL_FORCE_HWDEC_FALLBACK:
if (force_fallback(vd))
return ctx->avctx ? CONTROL_OK : CONTROL_ERROR;
return CONTROL_FALSE;
+ case VDCTRL_REINIT:
+ reinit(vd);
+ return CONTROL_TRUE;
}
return CONTROL_UNKNOWN;
}
diff --git a/video/decode/vdpau.c b/video/decode/vdpau.c
index f837553..c694650 100644
--- a/video/decode/vdpau.c
+++ b/video/decode/vdpau.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 <libavcodec/avcodec.h>
@@ -79,9 +79,7 @@ static int init(struct lavc_ctx *ctx)
};
ctx->hwdec_priv = p;
- if (mp_vdpau_handle_preemption(p->mpvdp, &p->preemption_counter) < 1)
- return -1;
-
+ mp_vdpau_handle_preemption(p->mpvdp, &p->preemption_counter);
return 0;
}
diff --git a/video/decode/videotoolbox.c b/video/decode/videotoolbox.c
index c5035f3..61e3005 100644
--- a/video/decode/videotoolbox.c
+++ b/video/decode/videotoolbox.c
@@ -3,18 +3,18 @@
*
* Copyright (c) 2015 Sebastien Zwickert
*
- * 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 <libavcodec/version.h>
diff --git a/video/dxva2.c b/video/dxva2.c
new file mode 100644
index 0000000..e47f84e
--- /dev/null
+++ b/video/dxva2.c
@@ -0,0 +1,89 @@
+/*
+ * 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 "common/av_common.h"
+#include "dxva2.h"
+#include "mp_image.h"
+#include "img_format.h"
+#include "mp_image_pool.h"
+
+struct dxva2_surface {
+ HMODULE d3dlib;
+ HMODULE dxva2lib;
+
+ IDirectXVideoDecoder *decoder;
+ LPDIRECT3DSURFACE9 surface;
+};
+
+LPDIRECT3DSURFACE9 d3d9_surface_in_mp_image(struct mp_image *mpi)
+{
+ return mpi && mpi->imgfmt == IMGFMT_DXVA2 ?
+ (LPDIRECT3DSURFACE9)mpi->planes[3] : NULL;
+}
+
+static void dxva2_release_img(void *arg)
+{
+ struct dxva2_surface *surface = arg;
+ if (surface->surface)
+ IDirect3DSurface9_Release(surface->surface);
+
+ if (surface->decoder)
+ IDirectXVideoDecoder_Release(surface->decoder);
+
+ if (surface->dxva2lib)
+ FreeLibrary(surface->dxva2lib);
+
+ if (surface->d3dlib)
+ FreeLibrary(surface->d3dlib);
+
+ talloc_free(surface);
+}
+
+struct mp_image *dxva2_new_ref(IDirectXVideoDecoder *decoder,
+ LPDIRECT3DSURFACE9 d3d9_surface, int w, int h)
+{
+ if (!decoder || !d3d9_surface)
+ return NULL;
+ struct dxva2_surface *surface = talloc_zero(NULL, struct dxva2_surface);
+
+ // Add additional references to the libraries which might otherwise be freed
+ // before the surface, which is observed to lead to bad behaviour
+ surface->d3dlib = LoadLibrary(L"d3d9.dll");
+ surface->dxva2lib = LoadLibrary(L"dxva2.dll");
+ if (!surface->d3dlib || !surface->dxva2lib)
+ goto fail;
+
+ surface->surface = d3d9_surface;
+ IDirect3DSurface9_AddRef(surface->surface);
+ surface->decoder = decoder;
+ IDirectXVideoDecoder_AddRef(surface->decoder);
+
+ struct mp_image *mpi = mp_image_new_custom_ref(&(struct mp_image){0},
+ surface, dxva2_release_img);
+ if (!mpi)
+ goto fail;
+
+ mp_image_setfmt(mpi, IMGFMT_DXVA2);
+ mp_image_set_size(mpi, w, h);
+ mpi->planes[3] = (void *)surface->surface;
+ return mpi;
+fail:
+ dxva2_release_img(surface);
+ return NULL;
+}
diff --git a/video/dxva2.h b/video/dxva2.h
new file mode 100644
index 0000000..b52767d
--- /dev/null
+++ b/video/dxva2.h
@@ -0,0 +1,32 @@
+/*
+ * 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 MPV_DXVA2_H
+#define MPV_DXVA2_H
+
+#include <d3d9.h>
+#include <dxva2api.h>
+
+struct mp_image;
+struct mp_image_pool;
+
+LPDIRECT3DSURFACE9 d3d9_surface_in_mp_image(struct mp_image *mpi);
+
+struct mp_image *dxva2_new_ref(IDirectXVideoDecoder *decoder,
+ LPDIRECT3DSURFACE9 d3d9_surface, int w, int h);
+
+#endif
diff --git a/video/filter/vf.c b/video/filter/vf.c
index b16c0b3..d8e7f6b 100644
--- a/video/filter/vf.c
+++ b/video/filter/vf.c
@@ -71,7 +71,6 @@ static const vf_info_t *const filter_list[] = {
&vf_info_noformat,
&vf_info_flip,
-#if HAVE_LIBAVFILTER
&vf_info_mirror,
&vf_info_lavfi,
&vf_info_rotate,
@@ -79,7 +78,6 @@ static const vf_info_t *const filter_list[] = {
&vf_info_pullup,
&vf_info_yadif,
&vf_info_stereo3d,
-#endif
&vf_info_eq,
&vf_info_dsize,
@@ -163,6 +161,17 @@ static void vf_control_all(struct vf_chain *c, int cmd, void *arg)
}
}
+int vf_send_command(struct vf_chain *c, char *label, char *cmd, char *arg)
+{
+ char *args[2] = {cmd, arg};
+ if (strcmp(label, "all") == 0) {
+ vf_control_all(c, VFCTRL_COMMAND, args);
+ return 0;
+ } else {
+ return vf_control_by_label(c, VFCTRL_COMMAND, args, bstr0(label));
+ }
+}
+
static void vf_fix_img_params(struct mp_image *img, struct mp_image_params *p)
{
// Filters must absolutely set these correctly.
@@ -211,13 +220,8 @@ void vf_print_filter_chain(struct vf_chain *c, int msglevel,
if (!mp_msg_test(c->log, msglevel))
return;
- char b[128] = {0};
-
- mp_snprintf_cat(b, sizeof(b), "%s", mp_image_params_to_str(&c->input_params));
- mp_msg(c->log, msglevel, " [vd] %s\n", b);
-
for (vf_instance_t *f = c->first; f; f = f->next) {
- b[0] = '\0';
+ char b[128] = {0};
mp_snprintf_cat(b, sizeof(b), " [%s] ", f->info->name);
mp_snprintf_cat(b, sizeof(b), "%s", mp_image_params_to_str(&f->fmt_out));
if (f->autoinserted)
@@ -392,7 +396,6 @@ int vf_filter_frame(struct vf_chain *c, struct mp_image *img)
return -1;
}
assert(mp_image_params_equal(&img->params, &c->input_params));
- vf_fix_img_params(img, &c->override_params);
return vf_do_filter(c->first, img);
}
@@ -494,19 +497,6 @@ void vf_seek_reset(struct vf_chain *c)
vf_chain_forget_frames(c);
}
-int vf_next_config(struct vf_instance *vf,
- int width, int height, int d_width, int d_height,
- unsigned int voflags, unsigned int outfmt)
-{
- vf->fmt_out = vf->fmt_in;
- vf->fmt_out.imgfmt = outfmt;
- vf->fmt_out.w = width;
- vf->fmt_out.h = height;
- vf->fmt_out.d_w = d_width;
- vf->fmt_out.d_h = d_height;
- return 1;
-}
-
int vf_next_query_format(struct vf_instance *vf, unsigned int fmt)
{
return fmt >= IMGFMT_START && fmt < IMGFMT_END
@@ -581,14 +571,9 @@ static int vf_reconfig_wrapper(struct vf_instance *vf,
if (!mp_image_params_valid(&vf->fmt_in))
return -2;
- int r;
- if (vf->reconfig) {
+ int r = 0;
+ if (vf->reconfig)
r = vf->reconfig(vf, &vf->fmt_in, &vf->fmt_out);
- } else if (vf->config) {
- r = vf->config(vf, p->w, p->h, p->d_w, p->d_h, 0, p->imgfmt) ? 0 : -1;
- } else {
- r = 0;
- }
if (!mp_image_params_equal(&vf->fmt_in, p))
r = -2;
@@ -603,13 +588,10 @@ static int vf_reconfig_wrapper(struct vf_instance *vf,
return r;
}
-// override_params is used to forcibly change the parameters of input images,
-// while params has to match the input images exactly.
-int vf_reconfig(struct vf_chain *c, const struct mp_image_params *params,
- const struct mp_image_params *override_params)
+int vf_reconfig(struct vf_chain *c, const struct mp_image_params *params)
{
int r = 0;
- vf_chain_forget_frames(c);
+ vf_seek_reset(c);
for (struct vf_instance *vf = c->first; vf; ) {
struct vf_instance *next = vf->next;
if (vf->autoinserted)
@@ -617,9 +599,8 @@ int vf_reconfig(struct vf_chain *c, const struct mp_image_params *params,
vf = next;
}
c->input_params = *params;
- c->first->fmt_in = *override_params;
- c->override_params = *override_params;
- struct mp_image_params cur = c->override_params;
+ c->first->fmt_in = *params;
+ struct mp_image_params cur = *params;
uint8_t unused[IMGFMT_END - IMGFMT_START];
update_formats(c, c->first, unused);
@@ -639,10 +620,8 @@ int vf_reconfig(struct vf_chain *c, const struct mp_image_params *params,
MP_ERR(c, "Image formats incompatible or invalid.\n");
mp_msg(c->log, loglevel, "Video filter chain:\n");
vf_print_filter_chain(c, loglevel, failing);
- if (r < 0) {
- c->input_params = c->override_params = c->output_params =
- (struct mp_image_params){0};
- }
+ if (r < 0)
+ c->input_params = c->output_params = (struct mp_image_params){0};
return r;
}
@@ -718,28 +697,3 @@ void vf_destroy(struct vf_chain *c)
vf_chain_forget_frames(c);
talloc_free(c);
}
-
-// When changing the size of an image that had old_w/old_h with
-// DAR *d_width/*d_height to the new size new_w/new_h, adjust
-// *d_width/*d_height such that the new image has the same pixel aspect ratio.
-void vf_rescale_dsize(int *d_width, int *d_height, int old_w, int old_h,
- int new_w, int new_h)
-{
- *d_width = *d_width * new_w / old_w;
- *d_height = *d_height * new_h / old_h;
-}
-
-// Set *d_width/*d_height to display aspect ratio with the givem source size
-void vf_set_dar(int *d_w, int *d_h, int w, int h, double dar)
-{
- *d_w = w;
- *d_h = h;
- if (dar > 0.01) {
- *d_w = h * dar + 0.5;
- // we don't like horizontal downscale
- if (*d_w < w) {
- *d_w = w;
- *d_h = w / dar + 0.5;
- }
- }
-}
diff --git a/video/filter/vf.h b/video/filter/vf.h
index 62734c9..c982b61 100644
--- a/video/filter/vf.h
+++ b/video/filter/vf.h
@@ -51,11 +51,6 @@ typedef struct vf_instance {
int (*reconfig)(struct vf_instance *vf, struct mp_image_params *in,
struct mp_image_params *out);
- // Legacy variant, use reconfig instead.
- int (*config)(struct vf_instance *vf,
- int width, int height, int d_width, int d_height,
- unsigned int flags, unsigned int outfmt);
-
int (*control)(struct vf_instance *vf, int request, void *data);
int (*query_format)(struct vf_instance *vf, unsigned int fmt);
@@ -116,7 +111,6 @@ struct vf_chain {
struct vf_instance *first, *last;
struct mp_image_params input_params;
- struct mp_image_params override_params; // input to first filter
struct mp_image_params output_params;
uint8_t allowed_output_formats[IMGFMT_END - IMGFMT_START];
@@ -150,12 +144,12 @@ enum vf_ctrl {
/* Hack to make the OSD state object available to vf_sub which
* access OSD/subtitle state outside of normal OSD draw time. */
VFCTRL_INIT_OSD,
+ VFCTRL_COMMAND,
};
struct vf_chain *vf_new(struct mpv_global *global);
void vf_destroy(struct vf_chain *c);
-int vf_reconfig(struct vf_chain *c, const struct mp_image_params *params,
- const struct mp_image_params *override_params);
+int vf_reconfig(struct vf_chain *c, const struct mp_image_params *params);
int vf_control_any(struct vf_chain *c, int cmd, void *arg);
int vf_control_by_label(struct vf_chain *c, int cmd, void *arg, bstr label);
int vf_filter_frame(struct vf_chain *c, struct mp_image *img);
@@ -171,22 +165,14 @@ struct vf_instance *vf_find_by_label(struct vf_chain *c, const char *label);
void vf_print_filter_chain(struct vf_chain *c, int msglevel,
struct vf_instance *vf);
+int vf_send_command(struct vf_chain *c, char *label, char *cmd, char *arg);
+
// Filter internal API
struct mp_image *vf_alloc_out_image(struct vf_instance *vf);
bool vf_make_out_image_writeable(struct vf_instance *vf, struct mp_image *img);
void vf_add_output_frame(struct vf_instance *vf, struct mp_image *img);
// default wrappers:
-int vf_next_config(struct vf_instance *vf,
- int width, int height, int d_width, int d_height,
- unsigned int flags, unsigned int outfmt);
int vf_next_query_format(struct vf_instance *vf, unsigned int fmt);
-
-// Helpers
-
-void vf_rescale_dsize(int *d_width, int *d_height, int old_w, int old_h,
- int new_w, int new_h);
-void vf_set_dar(int *d_width, int *d_height, int w, int h, double dar);
-
#endif /* MPLAYER_VF_H */
diff --git a/video/filter/vf_buffer.c b/video/filter/vf_buffer.c
index 2a7e458..772f7cb 100644
--- a/video/filter/vf_buffer.c
+++ b/video/filter/vf_buffer.c
@@ -3,7 +3,7 @@
#include <string.h>
#include <inttypes.h>
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "options/m_option.h"
@@ -27,14 +27,6 @@ static void flush(struct vf_instance *vf)
vf->priv->num_queued = 0;
}
-static int config(struct vf_instance *vf,
- int width, int height, int d_width, int d_height,
- unsigned int flags, unsigned int outfmt)
-{
- flush(vf);
- return vf_next_config(vf,width,height,d_width,d_height,flags,outfmt);
-}
-
static int filter_ext(struct vf_instance *vf, struct mp_image *mpi)
{
struct vf_priv_s *p = vf->priv;
@@ -73,7 +65,6 @@ static void uninit(vf_instance_t *vf)
static int vf_open(vf_instance_t *vf)
{
- vf->config = config;
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 02787b8..89b2b6f 100644
--- a/video/filter/vf_crop.c
+++ b/video/filter/vf_crop.c
@@ -39,10 +39,11 @@ static const struct vf_priv_s {
//===========================================================================//
-static int config(struct vf_instance *vf,
- int width, int height, int d_width, int d_height,
- unsigned int flags, unsigned int outfmt)
+static int reconfig(struct vf_instance *vf, struct mp_image_params *in,
+ struct mp_image_params *out)
{
+ int width = in->w, height = in->h;
+
// calculate the missing parameters:
if(vf->priv->crop_w<=0 || vf->priv->crop_w>width) vf->priv->crop_w=width;
if(vf->priv->crop_h<=0 || vf->priv->crop_h>height) vf->priv->crop_h=height;
@@ -50,7 +51,7 @@ static int config(struct vf_instance *vf,
if(vf->priv->crop_y<0) vf->priv->crop_y=(height-vf->priv->crop_h)/2;
// rounding:
- struct mp_imgfmt_desc fmt = mp_imgfmt_get_desc(outfmt);
+ struct mp_imgfmt_desc fmt = mp_imgfmt_get_desc(in->imgfmt);
vf->priv->crop_x = MP_ALIGN_DOWN(vf->priv->crop_x, fmt.align_x);
vf->priv->crop_y = MP_ALIGN_DOWN(vf->priv->crop_y, fmt.align_y);
@@ -59,11 +60,13 @@ static int config(struct vf_instance *vf,
if(vf->priv->crop_w+vf->priv->crop_x>width ||
vf->priv->crop_h+vf->priv->crop_y>height){
MP_WARN(vf, "Bad position/width/height - cropped area outside of the original!\n");
- return 0;
+ return -1;
}
- vf_rescale_dsize(&d_width, &d_height, width, height,
- vf->priv->crop_w, vf->priv->crop_h);
- return vf_next_config(vf,vf->priv->crop_w,vf->priv->crop_h,d_width,d_height,flags,outfmt);
+
+ *out = *in;
+ out->w = vf->priv->crop_w;
+ out->h = vf->priv->crop_h;
+ return 0;
}
static struct mp_image *filter(struct vf_instance *vf, struct mp_image *mpi)
@@ -82,7 +85,7 @@ static int query_format(struct vf_instance *vf, unsigned int fmt)
}
static int vf_open(vf_instance_t *vf){
- vf->config=config;
+ vf->reconfig=reconfig;
vf->filter=filter;
vf->query_format=query_format;
return 1;
diff --git a/video/filter/vf_dlopen.c b/video/filter/vf_dlopen.c
index 3ba4a0d..1ef37e3 100644
--- a/video/filter/vf_dlopen.c
+++ b/video/filter/vf_dlopen.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 <stdio.h>
@@ -90,29 +90,29 @@ static void set_imgprop(struct vf_dlopen_picdata *out, const mp_image_t *mpi)
}
}
-static int config(struct vf_instance *vf,
- int width, int height, int d_width, int d_height,
- unsigned int flags, unsigned int fmt)
+static int reconfig(struct vf_instance *vf, struct mp_image_params *in,
+ struct mp_image_params *out)
{
- vf->priv->filter.in_width = width;
- vf->priv->filter.in_height = height;
- vf->priv->filter.in_d_width = d_width;
- vf->priv->filter.in_d_height = d_height;
- vf->priv->filter.in_fmt = talloc_strdup(vf, mp_imgfmt_to_name(fmt));
- vf->priv->filter.out_width = width;
- vf->priv->filter.out_height = height;
- vf->priv->filter.out_d_width = d_width;
- vf->priv->filter.out_d_height = d_height;
+ mp_image_params_get_dsize(in, &vf->priv->filter.in_d_width,
+ &vf->priv->filter.in_d_height);
+
+ vf->priv->filter.in_width = in->w;
+ vf->priv->filter.in_height = in->h;
+ vf->priv->filter.in_fmt = talloc_strdup(vf, mp_imgfmt_to_name(in->imgfmt));
+ vf->priv->filter.out_width = vf->priv->filter.in_width;
+ vf->priv->filter.out_height = vf->priv->filter.in_height;
+ vf->priv->filter.out_d_width = vf->priv->filter.in_d_width;
+ vf->priv->filter.out_d_height = vf->priv->filter.in_d_height;
vf->priv->filter.out_fmt = NULL;
vf->priv->filter.out_cnt = 1;
if (!vf->priv->filter.in_fmt) {
MP_ERR(vf, "invalid input/output format\n");
- return 0;
+ return -1;
}
if (vf->priv->filter.config && vf->priv->filter.config(&vf->priv->filter) < 0) {
MP_ERR(vf, "filter config failed\n");
- return 0;
+ return -1;
}
// copy away stuff to sanity island
@@ -137,18 +137,18 @@ static int config(struct vf_instance *vf,
}
}
} else
- vf->priv->outfmt = fmt;
+ vf->priv->outfmt = in->imgfmt;
vf->priv->filter.out_fmt =
talloc_strdup(vf, mp_imgfmt_to_name(vf->priv->outfmt));
}
if (!vf->priv->outfmt) {
MP_ERR(vf, "filter config wants an unsupported output format\n");
- return 0;
+ return -1;
}
if (!vf->priv->out_cnt || vf->priv->out_cnt > FILTER_MAX_OUTCNT) {
MP_ERR(vf, "filter config wants to yield zero or too many output frames\n");
- return 0;
+ return -1;
}
for (int i = 0; i < vf->priv->out_cnt; ++i) {
@@ -157,16 +157,18 @@ static int config(struct vf_instance *vf,
mp_image_alloc(vf->priv->outfmt,
vf->priv->out_width, vf->priv->out_height);
if (!vf->priv->outpic[i])
- return 0; // OOM
+ return -1; // OOM
talloc_steal(vf, vf->priv->outpic[i]);
set_imgprop(&vf->priv->filter.outpic[i], vf->priv->outpic[i]);
}
- return vf_next_config(vf, vf->priv->out_width,
- vf->priv->out_height,
- vf->priv->filter.out_d_width,
- vf->priv->filter.out_d_height,
- flags, vf->priv->outfmt);
+ *out = *in;
+ out->w = vf->priv->out_width;
+ out->h = vf->priv->out_height;
+ mp_image_params_set_dsize(out, vf->priv->filter.out_d_width,
+ vf->priv->filter.out_d_height);
+ out->imgfmt = vf->priv->outfmt;
+ return 0;
}
static void uninit(struct vf_instance *vf)
@@ -307,7 +309,7 @@ static int vf_open(vf_instance_t *vf)
vf->filter_ext = filter;
vf->query_format = query_format;
- vf->config = config;
+ vf->reconfig = reconfig;
vf->uninit = uninit;
return 1;
diff --git a/video/filter/vf_dsize.c b/video/filter/vf_dsize.c
index 476ec27..b498b31 100644
--- a/video/filter/vf_dsize.c
+++ b/video/filter/vf_dsize.c
@@ -36,10 +36,12 @@ struct vf_priv_s {
float aspect;
};
-static int config(struct vf_instance *vf,
- int width, int height, int d_width, int d_height,
- unsigned int flags, unsigned int outfmt)
+static int reconfig(struct vf_instance *vf, struct mp_image_params *in,
+ struct mp_image_params *out)
{
+ int width = in->w, height = in->h;
+ int d_width, d_height;
+ mp_image_params_get_dsize(in, &d_width, &d_height);
int w = vf->priv->w;
int h = vf->priv->h;
if (vf->priv->aspect < 0.001) { // did the user input aspect or w,h params
@@ -74,12 +76,14 @@ static int config(struct vf_instance *vf,
d_width = width;
}
}
- return vf_next_config(vf, width, height, d_width, d_height, flags, outfmt);
+ *out = *in;
+ mp_image_params_set_dsize(out, d_width, d_height);
+ return 0;
}
static int vf_open(vf_instance_t *vf)
{
- vf->config = config;
+ vf->reconfig = reconfig;
return 1;
}
diff --git a/video/filter/vf_expand.c b/video/filter/vf_expand.c
index 6d5b694..a788568 100644
--- a/video/filter/vf_expand.c
+++ b/video/filter/vf_expand.c
@@ -55,10 +55,11 @@ static struct vf_priv_s {
//===========================================================================//
-static int config(struct vf_instance *vf,
- int width, int height, int d_width, int d_height,
- unsigned int flags, unsigned int outfmt)
+static int reconfig(struct vf_instance *vf, struct mp_image_params *in,
+ struct mp_image_params *out)
{
+ int width = in->w, height = in->h;
+
vf->priv->exp_x = vf->priv->cfg_exp_x;
vf->priv->exp_y = vf->priv->cfg_exp_y;
vf->priv->exp_w = vf->priv->cfg_exp_w;
@@ -71,7 +72,7 @@ static int config(struct vf_instance *vf,
else if( vf->priv->exp_h<height ) vf->priv->exp_h=height;
if (vf->priv->aspect) {
float adjusted_aspect = vf->priv->aspect;
- adjusted_aspect *= ((double)width/height) / ((double)d_width/d_height);
+ adjusted_aspect *= (double)in->p_w/in->p_h;
if (vf->priv->exp_h < vf->priv->exp_w / adjusted_aspect) {
vf->priv->exp_h = vf->priv->exp_w / adjusted_aspect + 0.5;
} else {
@@ -86,15 +87,16 @@ static int config(struct vf_instance *vf,
if(vf->priv->exp_x<0 || vf->priv->exp_x+width>vf->priv->exp_w) vf->priv->exp_x=(vf->priv->exp_w-width)/2;
if(vf->priv->exp_y<0 || vf->priv->exp_y+height>vf->priv->exp_h) vf->priv->exp_y=(vf->priv->exp_h-height)/2;
- struct mp_imgfmt_desc fmt = mp_imgfmt_get_desc(outfmt);
+ struct mp_imgfmt_desc fmt = mp_imgfmt_get_desc(in->imgfmt);
vf->priv->exp_x = MP_ALIGN_DOWN(vf->priv->exp_x, fmt.align_x);
vf->priv->exp_y = MP_ALIGN_DOWN(vf->priv->exp_y, fmt.align_y);
- vf_rescale_dsize(&d_width, &d_height, width, height,
- vf->priv->exp_w, vf->priv->exp_h);
+ *out = *in;
+ out->w = vf->priv->exp_w;
+ out->h = vf->priv->exp_h;
- return vf_next_config(vf,vf->priv->exp_w,vf->priv->exp_h,d_width,d_height,flags,outfmt);
+ return 0;
}
static struct mp_image *filter(struct vf_instance *vf, struct mp_image *mpi)
@@ -140,7 +142,7 @@ static int query_format(struct vf_instance *vf, unsigned int fmt)
}
static int vf_open(vf_instance_t *vf){
- vf->config=config;
+ vf->reconfig=reconfig;
vf->query_format=query_format;
vf->filter=filter;
return 1;
diff --git a/video/filter/vf_format.c b/video/filter/vf_format.c
index 83d697b..ff7389c 100644
--- a/video/filter/vf_format.c
+++ b/video/filter/vf_format.c
@@ -20,6 +20,8 @@
#include <string.h>
#include <inttypes.h>
+#include <libavutil/rational.h>
+
#include "common/msg.h"
#include "common/common.h"
@@ -100,12 +102,16 @@ static int reconfig(struct vf_instance *vf, struct mp_image_params *in,
out->stereo_out = p->stereo_out;
if (p->rotate >= 0)
out->rotate = p->rotate;
+
+ AVRational dsize;
+ mp_image_params_get_dsize(out, &dsize.num, &dsize.den);
if (p->dw > 0)
- out->d_w = p->dw;
+ dsize.num = p->dw;
if (p->dh > 0)
- out->d_h = p->dh;
+ dsize.den = p->dh;
if (p->dar > 0)
- vf_set_dar(&out->d_w, &out->d_h, out->w, out->h, p->dar);
+ dsize = av_d2q(p->dar, INT_MAX);
+ mp_image_params_set_dsize(out, dsize.num, dsize.den);
// Make sure the user-overrides are consistent (no RGB csp for YUV, etc.).
mp_image_params_guess_csp(out);
diff --git a/video/filter/vf_lavfi.c b/video/filter/vf_lavfi.c
index f413cf2..a66bd2a 100644
--- a/video/filter/vf_lavfi.c
+++ b/video/filter/vf_lavfi.c
@@ -3,18 +3,18 @@
*
* Filter graph creation code taken from Libav avplay.c (LGPL 2.1 or later)
*
- * 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>
@@ -56,6 +56,7 @@
#if LIBAVFILTER_VERSION_MICRO < 100
#define graph_parse(graph, filters, inputs, outputs, log_ctx) \
avfilter_graph_parse(graph, filters, inputs, outputs, log_ctx)
+#define avfilter_graph_send_command(a, b, c, d, e, f, g) -1
#else
#define graph_parse(graph, filters, inputs, outputs, log_ctx) \
avfilter_graph_parse_ptr(graph, filters, &(inputs), &(outputs), log_ctx)
@@ -103,30 +104,7 @@ static void destroy_graph(struct vf_instance *vf)
p->eof = false;
}
-static AVRational par_from_sar_dar(int width, int height,
- int d_width, int d_height)
-{
- return av_div_q((AVRational){d_width, d_height},
- (AVRational){width, height});
-}
-
-static void dar_from_sar_par(int width, int height, AVRational par,
- int *out_dw, int *out_dh)
-{
- *out_dw = width;
- *out_dh = height;
- if (par.num != 0 && par.den != 0) {
- double d = av_q2d(par);
- if (d > 1.0) {
- *out_dw = floor(*out_dw * d + 0.5);
- } else {
- *out_dh = floor(*out_dh / d + 0.5);
- }
- }
-}
-
-static bool recreate_graph(struct vf_instance *vf, int width, int height,
- int d_width, int d_height, unsigned int fmt)
+static bool recreate_graph(struct vf_instance *vf, struct mp_image_params *fmt)
{
void *tmp = talloc_new(NULL);
struct vf_priv_s *p = vf->priv;
@@ -168,13 +146,12 @@ static bool recreate_graph(struct vf_instance *vf, int width, int height,
char *sws_flags = talloc_asprintf(tmp, "flags=%"PRId64, p->cfg_sws_flags);
graph->scale_sws_opts = av_strdup(sws_flags);
- AVRational par = par_from_sar_dar(width, height, d_width, d_height);
AVRational timebase = AV_TIME_BASE_Q;
char *src_args = talloc_asprintf(tmp, "%d:%d:%d:%d:%d:%d:%d",
- width, height, imgfmt2pixfmt(fmt),
+ fmt->w, fmt->h, imgfmt2pixfmt(fmt->imgfmt),
timebase.num, timebase.den,
- par.num, par.den);
+ fmt->p_w, fmt->p_h);
if (avfilter_graph_create_filter(&in, avfilter_get_by_name("buffer"),
"src", src_args, NULL, graph) < 0)
@@ -225,7 +202,7 @@ static void reset(vf_instance_t *vf)
struct vf_priv_s *p = vf->priv;
struct mp_image_params *f = &vf->fmt_in;
if (p->graph && f->imgfmt)
- recreate_graph(vf, f->w, f->h, f->d_w, f->d_h, f->imgfmt);
+ recreate_graph(vf, f);
}
static int reconfig(struct vf_instance *vf, struct mp_image_params *in,
@@ -240,7 +217,7 @@ static int reconfig(struct vf_instance *vf, struct mp_image_params *in,
return -1;
}
- if (!recreate_graph(vf, in->w, in->h, in->d_w, in->d_h, in->imgfmt))
+ if (!recreate_graph(vf, in))
return -1;
AVFilterLink *l_out = p->out->inputs[0];
@@ -251,13 +228,10 @@ static int reconfig(struct vf_instance *vf, struct mp_image_params *in,
p->par_in = l_in->sample_aspect_ratio;
- int dw, dh;
- dar_from_sar_par(l_out->w, l_out->h, l_out->sample_aspect_ratio, &dw, &dh);
-
out->w = l_out->w;
out->h = l_out->h;
- out->d_w = dw;
- out->d_h = dh;
+ out->p_w = l_out->sample_aspect_ratio.num;
+ out->p_h = l_out->sample_aspect_ratio.den;
out->imgfmt = pixfmt2imgfmt(l_out->format);
return 0;
}
@@ -362,6 +336,14 @@ static int control(vf_instance_t *vf, int request, void *data)
case VFCTRL_SEEK_RESET:
reset(vf);
return CONTROL_OK;
+ case VFCTRL_COMMAND: {
+ if (!vf->priv->graph)
+ break;
+ char **args = data;
+ return avfilter_graph_send_command(vf->priv->graph, "all",
+ args[0], args[1], &(char){0}, 0, 0)
+ >= 0 ? CONTROL_OK : CONTROL_ERROR;
+ }
case VFCTRL_GET_METADATA:
if (vf->priv && vf->priv->metadata) {
*(struct mp_tags *)data = *vf->priv->metadata;
@@ -383,7 +365,6 @@ static void uninit(struct vf_instance *vf)
static int vf_open(vf_instance_t *vf)
{
vf->reconfig = reconfig;
- vf->config = NULL;
vf->filter_ext = filter_ext;
vf->filter_out = filter_out;
vf->filter = NULL;
diff --git a/video/filter/vf_scale.c b/video/filter/vf_scale.c
index a71f2c1..518ff41 100644
--- a/video/filter/vf_scale.c
+++ b/video/filter/vf_scale.c
@@ -76,7 +76,10 @@ static int find_best_out(vf_instance_t *vf, int in_format)
static int reconfig(struct vf_instance *vf, struct mp_image_params *in,
struct mp_image_params *out)
{
- int width = in->w, height = in->h, d_width = in->d_w, d_height = in->d_h;
+ int width = in->w, height = in->h;
+ int d_width, d_height;
+ mp_image_params_get_dsize(in, &d_width, &d_height);
+
unsigned int best = find_best_out(vf, in->imgfmt);
int round_w = 0, round_h = 0;
@@ -154,8 +157,7 @@ static int reconfig(struct vf_instance *vf, struct mp_image_params *in,
*out = *in;
out->w = vf->priv->w;
out->h = vf->priv->h;
- out->d_w = d_width;
- out->d_h = d_height;
+ mp_image_params_set_dsize(out, d_width, d_height);
out->imgfmt = best;
// Second-guess what libswscale is going to output and what not.
diff --git a/video/filter/vf_stereo3d.c b/video/filter/vf_stereo3d.c
index c478980..bde2617 100644
--- a/video/filter/vf_stereo3d.c
+++ b/video/filter/vf_stereo3d.c
@@ -63,6 +63,8 @@ typedef enum stereo_code {
INTERLEAVE_ROWS_LR, //row-interleave (left eye has top row)
INTERLEAVE_ROWS_RL, //row-interleave (right eye has top row)
STEREO_AUTO, //use video metadata info (for input)
+ ALTERNATING_LR, //alternating frames (left first)
+ ALTERNATING_RL, //alternating frames (right first)
STEREO_CODE_COUNT //no value set - TODO: needs autodetection
} stereo_code;
@@ -125,6 +127,8 @@ const struct m_opt_choice_alternatives stereo_code_names[] = {
{"interleave_rows_left_first", INTERLEAVE_ROWS_LR},
{"irr", INTERLEAVE_ROWS_RL},
{"interleave_rows_right_first", INTERLEAVE_ROWS_RL},
+ {"al", ALTERNATING_LR},
+ {"ar", ALTERNATING_RL},
// convenience alias for MP_STEREO3D_MONO
{"mono", MONO_L},
// for filter auto-insertion
diff --git a/video/filter/vf_sub.c b/video/filter/vf_sub.c
index 20069ee..a3680a3 100644
--- a/video/filter/vf_sub.c
+++ b/video/filter/vf_sub.c
@@ -49,30 +49,27 @@ struct vf_priv_s {
struct mp_osd_res dim;
};
-static int config(struct vf_instance *vf,
- int width, int height, int d_width, int d_height,
- unsigned int flags, unsigned int outfmt)
+static int reconfig(struct vf_instance *vf, struct mp_image_params *in,
+ struct mp_image_params *out)
{
+ int width = in->w, height = in->h;
+
vf->priv->outh = height + vf->priv->opt_top_margin +
vf->priv->opt_bottom_margin;
vf->priv->outw = width;
- double dar = (double)d_width / d_height;
- double sar = (double)width / height;
-
- vf_rescale_dsize(&d_width, &d_height, width, height,
- vf->priv->outw, vf->priv->outh);
-
vf->priv->dim = (struct mp_osd_res) {
.w = vf->priv->outw,
.h = vf->priv->outh,
.mt = vf->priv->opt_top_margin,
.mb = vf->priv->opt_bottom_margin,
- .display_par = sar / dar,
+ .display_par = in->p_w / (double)in->p_h,
};
- return vf_next_config(vf, vf->priv->outw, vf->priv->outh, d_width,
- d_height, flags, outfmt);
+ *out = *in;
+ out->w = vf->priv->outw;
+ out->h = vf->priv->outh;
+ return 0;
}
static void prepare_image(struct vf_instance *vf, struct mp_image *dmpi,
@@ -130,7 +127,7 @@ static int control(vf_instance_t *vf, int request, void *data)
static int vf_open(vf_instance_t *vf)
{
- vf->config = config;
+ vf->reconfig = reconfig;
vf->query_format = query_format;
vf->control = control;
vf->filter = filter;
diff --git a/video/filter/vf_vapoursynth.c b/video/filter/vf_vapoursynth.c
index 56cbf8e..5592e03 100644
--- a/video/filter/vf_vapoursynth.c
+++ b/video/filter/vf_vapoursynth.c
@@ -141,14 +141,8 @@ static void copy_mp_to_vs_frame_props_map(struct vf_priv_s *p, VSMap *map,
struct mp_image *img)
{
struct mp_image_params *params = &img->params;
- if (params->d_w > 0 && params->d_h > 0) {
- AVRational dar = {params->d_w, params->d_h};
- AVRational asp = {params->w, params->h};
- AVRational par = av_div_q(dar, asp);
-
- p->vsapi->propSetInt(map, "_SARNum", par.num, 0);
- p->vsapi->propSetInt(map, "_SARDen", par.den, 0);
- }
+ p->vsapi->propSetInt(map, "_SARNum", params->p_w, 0);
+ p->vsapi->propSetInt(map, "_SARDen", params->p_h, 0);
if (params->colorlevels) {
p->vsapi->propSetInt(map, "_ColorRange",
params->colorlevels == MP_CSP_LEVELS_TV, 0);
@@ -614,8 +608,11 @@ static int reinit_vs(struct vf_instance *vf)
if (p->vsapi->propSetNode(vars, "video_in", p->in_node, 0))
goto error;
- p->vsapi->propSetInt(vars, "video_in_dw", p->fmt_in.d_w, 0);
- p->vsapi->propSetInt(vars, "video_in_dh", p->fmt_in.d_h, 0);
+ int d_w, d_h;
+ mp_image_params_get_dsize(&p->fmt_in, &d_w, &d_h);
+
+ p->vsapi->propSetInt(vars, "video_in_dw", d_w, 0);
+ p->vsapi->propSetInt(vars, "video_in_dh", d_h, 0);
p->vsapi->propSetFloat(vars, "container_fps", vf->chain->container_fps, 0);
p->vsapi->propSetFloat(vars, "display_fps", vf->chain->display_fps, 0);
@@ -648,41 +645,35 @@ error:
return res;
}
-static int config(struct vf_instance *vf, int width, int height,
- int d_width, int d_height, unsigned int flags,
- unsigned int fmt)
+static int reconfig(struct vf_instance *vf, struct mp_image_params *in,
+ struct mp_image_params *out)
{
struct vf_priv_s *p = vf->priv;
- p->fmt_in = (struct mp_image_params){
- .imgfmt = fmt,
- .w = width,
- .h = height,
- .d_w = d_width,
- .d_h = d_height,
- };
+ *out = *in;
+ p->fmt_in = *in;
if (reinit_vs(vf) < 0)
- return 0;
+ return -1;
const VSVideoInfo *vi = p->vsapi->getVideoInfo(p->out_node);
- fmt = mp_from_vs(vi->format->id);
- if (!fmt) {
+ out->w = vi->width;
+ out->h = vi->height;
+ out->imgfmt = mp_from_vs(vi->format->id);
+ if (!out->imgfmt) {
MP_FATAL(vf, "Unsupported output format.\n");
destroy_vs(vf);
- return 0;
+ return -1;
}
- struct mp_imgfmt_desc desc = mp_imgfmt_get_desc(fmt);
- if (width % desc.align_x || height % desc.align_y) {
+ struct mp_imgfmt_desc desc = mp_imgfmt_get_desc(in->imgfmt);
+ if (in->w % desc.align_x || in->h % desc.align_y) {
MP_FATAL(vf, "VapourSynth does not allow unaligned/cropped video sizes.\n");
destroy_vs(vf);
- return 0;
+ return -1;
}
- vf_rescale_dsize(&d_width, &d_height, width, height, vi->width, vi->height);
-
- return vf_next_config(vf, vi->width, vi->height, d_width, d_height, flags, fmt);
+ return 0;
}
static int query_format(struct vf_instance *vf, unsigned int fmt)
@@ -726,8 +717,7 @@ static int vf_open(vf_instance_t *vf)
pthread_mutex_init(&p->lock, NULL);
pthread_cond_init(&p->wakeup, NULL);
- vf->reconfig = NULL;
- vf->config = config;
+ vf->reconfig = reconfig;
vf->filter_ext = filter_ext;
vf->filter_out = filter_out;
vf->needs_input = needs_input;
diff --git a/video/filter/vf_vavpp.c b/video/filter/vf_vavpp.c
index d502421..ae1d6b5 100644
--- a/video/filter/vf_vavpp.c
+++ b/video/filter/vf_vavpp.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 <assert.h>
diff --git a/video/filter/vf_vdpaurb.c b/video/filter/vf_vdpaurb.c
index 8955386..62f7f34 100644
--- a/video/filter/vf_vdpaurb.c
+++ b/video/filter/vf_vdpaurb.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 <assert.h>
diff --git a/video/filter/vf_yadif.c b/video/filter/vf_yadif.c
index 7fec046..d1e1ffb 100644
--- a/video/filter/vf_yadif.c
+++ b/video/filter/vf_yadif.c
@@ -78,5 +78,9 @@ const vf_info_t vf_info_yadif = {
.name = "yadif",
.open = vf_open,
.priv_size = sizeof(struct vf_priv_s),
+ .priv_defaults = &(const struct vf_priv_s){
+ .mode = 1,
+ .interlaced_only = 1,
+ },
.options = vf_opts_fields,
};
diff --git a/video/fmt-conversion.c b/video/fmt-conversion.c
index 6482f54..c23b7bb 100644
--- a/video/fmt-conversion.c
+++ b/video/fmt-conversion.c
@@ -30,25 +30,11 @@ static const struct {
{IMGFMT_BGRA, AV_PIX_FMT_BGRA},
{IMGFMT_BGR24, AV_PIX_FMT_BGR24},
{IMGFMT_RGB565, AV_PIX_FMT_RGB565},
- {IMGFMT_RGB555, AV_PIX_FMT_RGB555},
- {IMGFMT_RGB444, AV_PIX_FMT_RGB444},
- {IMGFMT_RGB8, AV_PIX_FMT_RGB8},
- {IMGFMT_RGB4, AV_PIX_FMT_RGB4},
- {IMGFMT_MONO, AV_PIX_FMT_MONOBLACK},
- {IMGFMT_MONO_W, AV_PIX_FMT_MONOWHITE},
- {IMGFMT_RGB4_BYTE, AV_PIX_FMT_RGB4_BYTE},
- {IMGFMT_BGR4_BYTE, AV_PIX_FMT_BGR4_BYTE},
{IMGFMT_RGB48, AV_PIX_FMT_RGB48},
{IMGFMT_ABGR, AV_PIX_FMT_ABGR},
{IMGFMT_RGBA, AV_PIX_FMT_RGBA},
{IMGFMT_RGB24, AV_PIX_FMT_RGB24},
- {IMGFMT_BGR565, AV_PIX_FMT_BGR565},
- {IMGFMT_BGR555, AV_PIX_FMT_BGR555},
- {IMGFMT_BGR444, AV_PIX_FMT_BGR444},
- {IMGFMT_BGR8, AV_PIX_FMT_BGR8},
- {IMGFMT_BGR4, AV_PIX_FMT_BGR4},
{IMGFMT_PAL8, AV_PIX_FMT_PAL8},
- {IMGFMT_GBRP, AV_PIX_FMT_GBRP},
{IMGFMT_YUYV, AV_PIX_FMT_YUYV422},
{IMGFMT_UYVY, AV_PIX_FMT_UYVY422},
{IMGFMT_NV12, AV_PIX_FMT_NV12},
diff --git a/video/hwdec.h b/video/hwdec.h
index f2a72ae..1588901 100644
--- a/video/hwdec.h
+++ b/video/hwdec.h
@@ -13,8 +13,9 @@ enum hwdec_type {
HWDEC_VIDEOTOOLBOX = 3,
HWDEC_VAAPI = 4,
HWDEC_VAAPI_COPY = 5,
- HWDEC_DXVA2_COPY = 6,
- HWDEC_RPI = 7,
+ HWDEC_DXVA2 = 6,
+ HWDEC_DXVA2_COPY = 7,
+ HWDEC_RPI = 8,
};
// hwdec_type names (options.c)
diff --git a/video/image_writer.c b/video/image_writer.c
index 6c1ed54..6c1c994 100644
--- a/video/image_writer.c
+++ b/video/image_writer.c
@@ -22,6 +22,7 @@
#include <libavcodec/avcodec.h>
#include <libavutil/mem.h>
+#include <libavutil/opt.h>
#include "config.h"
@@ -32,7 +33,7 @@
#include "osdep/io.h"
#include "image_writer.h"
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "video/img_format.h"
#include "video/mp_image.h"
#include "video/fmt-conversion.h"
@@ -111,7 +112,8 @@ static bool write_lavc(struct image_writer_ctx *ctx, mp_image_t *image, FILE *fp
}
if (ctx->writer->lavc_codec == AV_CODEC_ID_PNG) {
avctx->compression_level = ctx->opts->png_compression;
- avctx->prediction_method = ctx->opts->png_filter;
+ av_opt_set_int(avctx, "pred", ctx->opts->png_filter,
+ AV_OPT_SEARCH_CHILDREN);
}
if (avcodec_open2(avctx, codec, NULL) < 0) {
@@ -202,7 +204,7 @@ static bool write_jpeg(struct image_writer_ctx *ctx, mp_image_t *image, FILE *fp
while (cinfo.next_scanline < cinfo.image_height) {
JSAMPROW row_pointer[1];
row_pointer[0] = image->planes[0] +
- cinfo.next_scanline * image->stride[0];
+ (ptrdiff_t)cinfo.next_scanline * image->stride[0];
jpeg_write_scanlines(&cinfo, row_pointer,1);
}
@@ -291,8 +293,8 @@ const char *image_writer_file_ext(const struct image_writer_opts *opts)
struct mp_image *convert_image(struct mp_image *image, int destfmt,
struct mp_log *log)
{
- int d_w = image->params.d_w;
- int d_h = image->params.d_h;
+ 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
diff --git a/video/img_format.c b/video/img_format.c
index 42b4df4..82136b5 100644
--- a/video/img_format.c
+++ b/video/img_format.c
@@ -148,6 +148,7 @@ struct mp_imgfmt_desc mp_imgfmt_get_desc(int mpfmt)
int planedepth[4] = {0};
int el_size = (pd->flags & AV_PIX_FMT_FLAG_BITSTREAM) ? 1 : 8;
bool need_endian = false; // single component is spread over >1 bytes
+ int shift = -1; // shift for all components, or -1 if not uniform
for (int c = 0; c < pd->nb_components; c++) {
AVComponentDescriptor d = pd->comp[c];
#if HAVE_AV_NEW_PIXDESC
@@ -166,6 +167,10 @@ struct mp_imgfmt_desc mp_imgfmt_get_desc(int mpfmt)
desc.component_bits = depth;
if (depth != desc.component_bits)
desc.component_bits = 0;
+ if (c == 0)
+ shift = d.shift;
+ if (shift != d.shift)
+ shift = -1;
}
for (int p = 0; p < 4; p++) {
@@ -179,11 +184,14 @@ struct mp_imgfmt_desc mp_imgfmt_get_desc(int mpfmt)
// Check whether any components overlap other components (per plane).
// We're cheating/simplifying here: we assume that this happens if a shift
// is set - which is wrong in general (could be needed for padding, instead
- // of overlapping bits of another component). Needed for rgb444le/be.
+ // of overlapping bits of another component - use the "< 8" test to exclude
+ // "normal" formats which use this for padding, like p010).
+ // Needed for rgb444le/be.
bool component_byte_overlap = false;
for (int c = 0; c < pd->nb_components; c++) {
AVComponentDescriptor d = pd->comp[c];
- component_byte_overlap |= d.shift > 0 && planedepth[d.plane] > 8;
+ component_byte_overlap |= d.shift > 0 && planedepth[d.plane] > 8 &&
+ desc.component_bits < 8;
}
// If every component sits in its own byte, or all components are within
@@ -234,7 +242,9 @@ struct mp_imgfmt_desc mp_imgfmt_get_desc(int mpfmt)
if ((desc.flags & (MP_IMGFLAG_YUV | MP_IMGFLAG_RGB))
&& (desc.flags & MP_IMGFLAG_BYTE_ALIGNED)
- && !(pd->flags & AV_PIX_FMT_FLAG_PAL))
+ && !(pd->flags & AV_PIX_FMT_FLAG_PAL)
+ && !component_byte_overlap
+ && shift >= 0)
{
bool same_depth = true;
for (int p = 0; p < desc.num_planes; p++) {
@@ -242,13 +252,27 @@ struct mp_imgfmt_desc mp_imgfmt_get_desc(int mpfmt)
desc.bpp[p] == desc.bpp[0];
}
if (same_depth && pd->nb_components == desc.num_planes) {
- desc.component_full_bits = (desc.component_bits + 7) / 8 * 8;
if (desc.flags & MP_IMGFLAG_YUV) {
desc.flags |= MP_IMGFLAG_YUV_P;
} else {
desc.flags |= MP_IMGFLAG_RGB_P;
}
}
+ if (pd->nb_components == 3 && desc.num_planes == 2 &&
+ planedepth[1] == planedepth[0] * 2 &&
+ desc.bpp[1] == desc.bpp[0] * 2 &&
+ (desc.flags & MP_IMGFLAG_YUV))
+ {
+
+ desc.flags |= MP_IMGFLAG_YUV_NV;
+ if (fmt == AV_PIX_FMT_NV21)
+ desc.flags |= MP_IMGFLAG_YUV_NV_SWAP;
+ }
+ if (desc.flags & (MP_IMGFLAG_YUV_P | MP_IMGFLAG_RGB_P | MP_IMGFLAG_YUV_NV))
+ {
+ desc.component_bits += shift;
+ desc.component_full_bits = (desc.component_bits + 7) / 8 * 8;
+ }
}
for (int p = 0; p < desc.num_planes; p++) {
@@ -275,12 +299,12 @@ struct mp_imgfmt_desc mp_imgfmt_get_desc(int mpfmt)
return desc;
}
-// Find a format that is MP_IMGFLAG_YUV_P with the following configuration.
-int mp_imgfmt_find_yuv_planar(int xs, int ys, int planes, int component_bits)
+// Find a format that has the given flags set with the following configuration.
+int mp_imgfmt_find(int xs, int ys, int planes, int component_bits, int flags)
{
for (int n = IMGFMT_START + 1; n < IMGFMT_END; n++) {
struct mp_imgfmt_desc desc = mp_imgfmt_get_desc(n);
- if (desc.id && (desc.flags & MP_IMGFLAG_YUV_P)) {
+ if (desc.id && ((desc.flags & flags) == flags)) {
if (desc.num_planes == planes && desc.chroma_xs == xs &&
desc.chroma_ys == ys && desc.plane_bits == component_bits &&
(desc.flags & MP_IMGFLAG_NE))
@@ -333,6 +357,8 @@ int main(int argc, char **argv)
FLAG(MP_IMGFLAG_ALPHA, "a")
FLAG(MP_IMGFLAG_PLANAR, "P")
FLAG(MP_IMGFLAG_YUV_P, "YUVP")
+ FLAG(MP_IMGFLAG_YUV_NV, "NV")
+ FLAG(MP_IMGFLAG_YUV_NV_SWAP, "NVSWAP")
FLAG(MP_IMGFLAG_YUV, "yuv")
FLAG(MP_IMGFLAG_RGB, "rgb")
FLAG(MP_IMGFLAG_XYZ, "xyz")
@@ -344,6 +370,9 @@ int main(int argc, char **argv)
printf(" planes=%d, chroma=%d:%d align=%d:%d bits=%d cbits=%d\n",
d.num_planes, d.chroma_xs, d.chroma_ys, d.align_x, d.align_y,
d.plane_bits, d.component_bits);
+ printf(" planes=%d, chroma=%d:%d align=%d:%d bits=%d cbits=%d cfbits=%d\n",
+ d.num_planes, d.chroma_xs, d.chroma_ys, d.align_x, d.align_y,
+ d.plane_bits, d.component_bits, d.component_full_bits);
printf(" {");
for (int n = 0; n < MP_MAX_PLANES; n++)
printf("%d/%d/[%d:%d] ", d.bytes[n], d.bpp[n], d.xs[n], d.ys[n]);
diff --git a/video/img_format.h b/video/img_format.h
index 0d8c699..b18a6f5 100644
--- a/video/img_format.h
+++ b/video/img_format.h
@@ -67,8 +67,13 @@
#define MP_IMGFLAG_HWACCEL 0x10000
// Set if the chroma resolution is lower than luma resolution. Unset for non-YUV.
#define MP_IMGFLAG_SUBSAMPLED 0x20000
-// Like MP_IMGFLAG_YUV_P, but RGB. The planes are organized as in IMGFMT_GBRP.
+// Like MP_IMGFLAG_YUV_P, but RGB. This can be e.g. AV_PIX_FMT_GBRP. The planes
+// are always shuffled (G - B - R [- A]).
#define MP_IMGFLAG_RGB_P 0x40000
+// Semi-planar YUV formats, like AV_PIX_FMT_NV12.
+// The flag MP_IMGFLAG_YUV_NV_SWAP is set for AV_PIX_FMT_NV21.
+#define MP_IMGFLAG_YUV_NV 0x80000
+#define MP_IMGFLAG_YUV_NV_SWAP 0x100000
// Exactly one of these bits is set in mp_imgfmt_desc.flags
#define MP_IMGFLAG_COLOR_CLASS_MASK \
@@ -173,31 +178,13 @@ enum mp_imgfmt {
IMGFMT_RGB0_START = IMGFMT_0RGB,
IMGFMT_RGB0_END = IMGFMT_RGB0,
- // Accessed with bit-shifts (components ordered from MSB to LSB)
- IMGFMT_BGR8, // r3 g3 b2
- IMGFMT_RGB8,
- IMGFMT_BGR4_BYTE, // r1 g2 b1 with 1 pixel per byte
- IMGFMT_RGB4_BYTE,
- IMGFMT_BGR4, // r1 g2 b1, bit-packed
- IMGFMT_RGB4,
- IMGFMT_MONO, // 1 bit per pixel, bit-packed
- IMGFMT_MONO_W, // like IMGFMT_MONO, but inverted (white pixels)
-
// Accessed with bit-shifts after endian-swapping the uint16_t pixel
- IMGFMT_RGB444, // 4r 4g 4b 4a (MSB to LSB)
- IMGFMT_RGB555, // 5r 5g 5b 1a
- IMGFMT_RGB565, // 5r 6g 5b
- IMGFMT_BGR444, // 4b 4r 4g 4a
- IMGFMT_BGR555, // 5b 5g 5r 1a
- IMGFMT_BGR565, // 5b 6g 5r
+ IMGFMT_RGB565, // 5r 6g 5b (MSB to LSB)
// The first plane has 1 byte per pixel. The second plane is a palette with
// 256 entries, with each entry encoded like in IMGFMT_BGR32.
IMGFMT_PAL8,
- // Planar RGB (planes are shuffled: plane 0 is G, etc.)
- IMGFMT_GBRP,
-
// XYZ colorspace, similar organization to RGB48. Even though it says "12",
// the components are stored as 16 bit, with lower 4 bits set to 0.
IMGFMT_XYZ12,
@@ -245,7 +232,7 @@ char **mp_imgfmt_name_list(void);
#define vo_format_name mp_imgfmt_to_name
-int mp_imgfmt_find_yuv_planar(int xs, int ys, int planes, int component_bits);
+int mp_imgfmt_find(int xs, int ys, int planes, int component_bits, int flags);
int mp_imgfmt_select_best(int dst1, int dst2, int src);
diff --git a/video/mp_image.c b/video/mp_image.c
index a3472ba..ff81dd7 100644
--- a/video/mp_image.c
+++ b/video/mp_image.c
@@ -27,9 +27,10 @@
#include <libavutil/mem.h>
#include <libavutil/common.h>
#include <libavutil/bswap.h>
+#include <libavutil/rational.h>
#include <libavcodec/avcodec.h>
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "img_format.h"
#include "mp_image.h"
@@ -116,8 +117,9 @@ int mp_image_plane_h(struct mp_image *mpi, int plane)
void mp_image_set_size(struct mp_image *mpi, int w, int h)
{
assert(w >= 0 && h >= 0);
- mpi->w = mpi->params.w = mpi->params.d_w = w;
- mpi->h = mpi->params.h = mpi->params.d_h = h;
+ mpi->w = mpi->params.w = w;
+ mpi->h = mpi->params.h = h;
+ mpi->params.p_w = mpi->params.p_h = 1;
}
void mp_image_set_params(struct mp_image *image,
@@ -381,12 +383,13 @@ void mp_image_copy_attributes(struct mp_image *dst, struct mp_image *src)
dst->pict_type = src->pict_type;
dst->fields = src->fields;
dst->pts = src->pts;
+ dst->dts = src->dts;
dst->params.rotate = src->params.rotate;
dst->params.stereo_in = src->params.stereo_in;
dst->params.stereo_out = src->params.stereo_out;
if (dst->w == src->w && dst->h == src->h) {
- dst->params.d_w = src->params.d_w;
- dst->params.d_h = src->params.d_h;
+ dst->params.p_w = src->params.p_w;
+ dst->params.p_h = src->params.p_h;
}
dst->params.primaries = src->params.primaries;
dst->params.gamma = src->params.gamma;
@@ -477,13 +480,32 @@ void mp_image_vflip(struct mp_image *img)
}
}
+// Display size derived from image size and pixel aspect ratio.
+void mp_image_params_get_dsize(const struct mp_image_params *p,
+ int *d_w, int *d_h)
+{
+ *d_w = p->w;
+ *d_h = p->h;
+ if (p->p_w > p->p_h && p->p_h >= 1)
+ *d_w = MPCLAMP(*d_w * (int64_t)p->p_w / p->p_h, 1, INT_MAX);
+ if (p->p_h > p->p_w && p->p_w >= 1)
+ *d_h = MPCLAMP(*d_h * (int64_t)p->p_h / p->p_w, 1, INT_MAX);
+}
+
+void mp_image_params_set_dsize(struct mp_image_params *p, int d_w, int d_h)
+{
+ AVRational ds = av_div_q((AVRational){d_w, d_h}, (AVRational){p->w, p->h});
+ p->p_w = ds.num;
+ p->p_h = ds.den;
+}
+
char *mp_image_params_to_str_buf(char *b, size_t bs,
const struct mp_image_params *p)
{
if (p && p->imgfmt) {
snprintf(b, bs, "%dx%d", p->w, p->h);
- if (p->w != p->d_w || p->h != p->d_h)
- mp_snprintf_cat(b, bs, "->%dx%d", p->d_w, p->d_h);
+ if (p->p_w != p->p_h || !p->p_w)
+ mp_snprintf_cat(b, bs, " [%d:%d]", p->p_w, p->p_h);
mp_snprintf_cat(b, bs, " %s", mp_imgfmt_to_name(p->imgfmt));
mp_snprintf_cat(b, bs, " %s/%s",
m_opt_choice_str(mp_csp_names, p->colorspace),
@@ -515,7 +537,7 @@ bool mp_image_params_valid(const struct mp_image_params *p)
if (p->w <= 0 || p->h <= 0 || (p->w + 128LL) * (p->h + 128LL) >= INT_MAX / 8)
return false;
- if (p->d_w <= 0 || p->d_h <= 0)
+ if (p->p_w <= 0 || p->p_h <= 0)
return false;
if (p->rotate < 0 || p->rotate >= 360)
@@ -533,7 +555,7 @@ bool mp_image_params_equal(const struct mp_image_params *p1,
{
return p1->imgfmt == p2->imgfmt &&
p1->w == p2->w && p1->h == p2->h &&
- p1->d_w == p2->d_w && p1->d_h == p2->d_h &&
+ p1->p_w == p2->p_w && p1->p_h == p2->p_h &&
p1->colorspace == p2->colorspace &&
p1->colorlevels == p2->colorlevels &&
p1->primaries == p2->primaries &&
@@ -555,12 +577,6 @@ void mp_image_set_attributes(struct mp_image *image,
nparams.h = image->h;
if (nparams.imgfmt != params->imgfmt)
mp_image_params_guess_csp(&nparams);
- if (nparams.w != params->w || nparams.h != params->h) {
- if (nparams.d_w && nparams.d_h) {
- vf_rescale_dsize(&nparams.d_w, &nparams.d_h,
- params->w, params->h, nparams.w, nparams.h);
- }
- }
mp_image_set_params(image, &nparams);
}
diff --git a/video/mp_image.h b/video/mp_image.h
index f71f7b3..af60272 100644
--- a/video/mp_image.h
+++ b/video/mp_image.h
@@ -40,7 +40,7 @@
struct mp_image_params {
enum mp_imgfmt imgfmt; // pixel format
int w, h; // image dimensions
- int d_w, d_h; // define display aspect ratio (never 0/0)
+ int p_w, p_h; // define pixel aspect ratio (never 0/0)
enum mp_csp colorspace;
enum mp_csp_levels colorlevels;
enum mp_csp_prim primaries;
@@ -86,6 +86,8 @@ typedef struct mp_image {
/* only inside filter chain */
double pts;
+ /* only after decoder */
+ double dts;
/* for private use */
void* priv;
@@ -137,6 +139,10 @@ bool mp_image_params_valid(const struct mp_image_params *p);
bool mp_image_params_equal(const struct mp_image_params *p1,
const struct mp_image_params *p2);
+void mp_image_params_get_dsize(const struct mp_image_params *p,
+ int *d_w, int *d_h);
+void mp_image_params_set_dsize(struct mp_image_params *p, int d_w, int d_h);
+
void mp_image_set_params(struct mp_image *image,
const struct mp_image_params *params);
diff --git a/video/mp_image_pool.c b/video/mp_image_pool.c
index 5992c63..10966ee 100644
--- a/video/mp_image_pool.c
+++ b/video/mp_image_pool.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 "config.h"
@@ -24,7 +24,7 @@
#include <libavutil/buffer.h>
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "common/common.h"
#include "video/mp_image.h"
@@ -164,6 +164,14 @@ struct mp_image *mp_image_pool_get_no_alloc(struct mp_image_pool *pool, int fmt,
return ref;
}
+void mp_image_pool_add(struct mp_image_pool *pool, struct mp_image *new)
+{
+ struct image_flags *it = talloc_ptrtype(new, it);
+ *it = (struct image_flags) { .pool_alive = true };
+ new->priv = it;
+ MP_TARRAY_APPEND(pool, pool->images, pool->num_images, new);
+}
+
// Return a new image of given format/size. The only difference to
// mp_image_alloc() is that there is a transparent mechanism to recycle image
// data allocations through this pool.
@@ -186,10 +194,7 @@ struct mp_image *mp_image_pool_get(struct mp_image_pool *pool, int fmt,
}
if (!new)
return NULL;
- struct image_flags *it = talloc_ptrtype(new, it);
- *it = (struct image_flags) { .pool_alive = true };
- new->priv = it;
- MP_TARRAY_APPEND(pool, pool->images, pool->num_images, new);
+ mp_image_pool_add(pool, new);
new = mp_image_pool_get_no_alloc(pool, fmt, w, h);
}
return new;
diff --git a/video/mp_image_pool.h b/video/mp_image_pool.h
index c2307da..32beb89 100644
--- a/video/mp_image_pool.h
+++ b/video/mp_image_pool.h
@@ -8,6 +8,8 @@ struct mp_image_pool;
struct mp_image_pool *mp_image_pool_new(int max_count);
struct mp_image *mp_image_pool_get(struct mp_image_pool *pool, int fmt,
int w, int h);
+// the reference to "new" is transferred to the pool
+void mp_image_pool_add(struct mp_image_pool *pool, struct mp_image *new);
void mp_image_pool_clear(struct mp_image_pool *pool);
void mp_image_pool_set_lru(struct mp_image_pool *pool);
diff --git a/video/out/aspect.c b/video/out/aspect.c
index 2e1093c..2d25bbd 100644
--- a/video/out/aspect.c
+++ b/video/out/aspect.c
@@ -135,8 +135,8 @@ void mp_get_src_dst_rects(struct mp_log *log, struct mp_vo_opts *opts,
{
int src_w = video->w;
int src_h = video->h;
- int src_dw = video->d_w;
- int src_dh = video->d_h;
+ int src_dw, src_dh;
+ mp_image_params_get_dsize(video, &src_dw, &src_dh);
if (video->rotate % 180 == 90 && (vo_caps & VO_CAP_ROTATE90)) {
MPSWAP(int, src_w, src_h);
MPSWAP(int, src_dw, src_dh);
@@ -174,8 +174,8 @@ void mp_get_src_dst_rects(struct mp_log *log, struct mp_vo_opts *opts,
mp_verbose(log, "Window size: %dx%d\n",
window_w, window_h);
- mp_verbose(log, "Video source: %dx%d (%dx%d)\n",
- video->w, video->h, video->d_w, video->d_h);
+ mp_verbose(log, "Video source: %dx%d (%d:%d)\n",
+ video->w, video->h, video->p_w, video->p_h);
mp_verbose(log, "Video display: (%d, %d) %dx%d -> (%d, %d) %dx%d\n",
src.x0, src.y0, sw, sh, dst.x0, dst.y0, dw, dh);
mp_verbose(log, "Video scale: %f/%f\n",
diff --git a/video/out/bitmap_packer.c b/video/out/bitmap_packer.c
index 49a66ca..4896076 100644
--- a/video/out/bitmap_packer.c
+++ b/video/out/bitmap_packer.c
@@ -5,18 +5,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>
@@ -25,7 +25,7 @@
#include <libavutil/common.h>
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "bitmap_packer.h"
#include "common/common.h"
#include "sub/dec_sub.h"
diff --git a/video/out/cocoa_common.m b/video/out/cocoa_common.m
index 2cb365c..30b832d 100644
--- a/video/out/cocoa_common.m
+++ b/video/out/cocoa_common.m
@@ -45,12 +45,14 @@
#include "win_state.h"
#include "input/input.h"
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "common/msg.h"
static int vo_cocoa_fullscreen(struct vo *vo);
static void cocoa_rm_fs_screen_profile_observer(struct vo_cocoa_state *s);
+static void cocoa_add_screen_reconfiguration_observer(struct vo *vo);
+static void cocoa_rm_screen_reconfiguration_observer(struct vo *vo);
struct vo_cocoa_state {
// --- The following members can be accessed only by the main thread (i.e.
@@ -267,6 +269,7 @@ void vo_cocoa_init(struct vo *vo)
pthread_cond_init(&s->wakeup, NULL);
vo->cocoa = s;
cocoa_init_light_sensor(vo);
+ cocoa_add_screen_reconfiguration_observer(vo);
if (!s->embedded) {
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
set_application_icon(NSApp);
@@ -306,6 +309,7 @@ void vo_cocoa_uninit(struct vo *vo)
enable_power_management(s);
cocoa_uninit_light_sensor(s);
cocoa_rm_fs_screen_profile_observer(s);
+ cocoa_rm_screen_reconfiguration_observer(vo);
[s->nsgl_ctx release];
CGLReleaseContext(s->cgl_ctx);
@@ -370,22 +374,23 @@ static void vo_cocoa_update_screen_fps(struct vo *vo)
NSDictionary* sinfo = [screen deviceDescription];
NSNumber* sid = [sinfo objectForKey:@"NSScreenNumber"];
CGDirectDisplayID did = [sid longValue];
- CGDisplayModeRef mode = CGDisplayCopyDisplayMode(did);
- s->screen_fps = CGDisplayModeGetRefreshRate(mode);
- CGDisplayModeRelease(mode);
- if (s->screen_fps == 0.0) {
+ CVDisplayLinkRef link;
+ CVDisplayLinkCreateWithCGDisplay(did, &link);
+ s->screen_fps = CVDisplayLinkGetActualOutputVideoRefreshPeriod(link);
+
+ if (s->screen_fps == 0) {
// Fallback to using Nominal refresh rate from DisplayLink,
// CVDisplayLinkGet *Actual* OutputVideoRefreshPeriod seems to
- // return 0 as well if CG returns 0
- CVDisplayLinkRef link;
- CVDisplayLinkCreateWithCGDisplay(did, &link);
+ // return 0 on some Apple devices. Use the nominal refresh period
+ // instead.
const CVTime t = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(link);
if (!(t.flags & kCVTimeIsIndefinite))
s->screen_fps = (t.timeScale / (double) t.timeValue);
- CVDisplayLinkRelease(link);
}
+ CVDisplayLinkRelease(link);
+
flag_events(vo, VO_EVENT_WIN_STATE);
}
@@ -553,6 +558,28 @@ static void cocoa_add_fs_screen_profile_observer(struct vo *vo)
usingBlock:nblock];
}
+static void cocoa_screen_reconfiguration_observer(
+ CGDirectDisplayID display, CGDisplayChangeSummaryFlags flags, void *ctx)
+{
+ if (flags & kCGDisplaySetModeFlag) {
+ struct vo *vo = ctx;
+ MP_WARN(vo, "detected display mode change, updating screen info\n");
+ vo_cocoa_update_screen_info(vo, NULL);
+ }
+}
+
+static void cocoa_add_screen_reconfiguration_observer(struct vo *vo)
+{
+ CGDisplayRegisterReconfigurationCallback(
+ cocoa_screen_reconfiguration_observer, vo);
+}
+
+static void cocoa_rm_screen_reconfiguration_observer(struct vo *vo)
+{
+ CGDisplayRemoveReconfigurationCallback(
+ cocoa_screen_reconfiguration_observer, vo);
+}
+
void vo_cocoa_set_opengl_ctx(struct vo *vo, CGLContextObj ctx)
{
struct vo_cocoa_state *s = vo->cocoa;
@@ -591,6 +618,12 @@ int vo_cocoa_config_window(struct vo *vo)
cocoa_add_fs_screen_profile_observer(vo);
cocoa_set_window_title(vo);
vo_set_level(vo, vo->opts->ontop);
+
+ GLint o;
+ if (!CGLGetParameter(s->cgl_ctx, kCGLCPSurfaceOpacity, &o) && !o) {
+ [s->window setOpaque:NO];
+ [s->window setBackgroundColor:[NSColor clearColor]];
+ }
}
s->vo_ready = true;
diff --git a/video/out/dither.c b/video/out/dither.c
index a164ee8..33c2e92 100644
--- a/video/out/dither.c
+++ b/video/out/dither.c
@@ -5,23 +5,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.
- *
- * You should have received a copy of the GNU General Public License along
- * with mpv. If not, see <http://www.gnu.org/licenses/>.
+ * GNU Lesser General Public License for more details.
*
- * You can alternatively redistribute this file 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.
+ * 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>
@@ -35,7 +30,7 @@
#include <libavutil/lfg.h>
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "dither.h"
#define MAX_SIZEB 8
diff --git a/video/out/drm_common.c b/video/out/drm_common.c
index d3a05f5..c105a14 100644
--- a/video/out/drm_common.c
+++ b/video/out/drm_common.c
@@ -1,23 +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.
- *
- * You should have received a copy of the GNU General Public License along
- * with mpv. If not, see <http://www.gnu.org/licenses/>.
+ * GNU Lesser General Public License for more details.
*
- * You can alternatively redistribute this file 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.
+ * 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 <errno.h>
@@ -128,7 +123,7 @@ static bool setup_crtc(struct kms *kms, const drmModeRes *res)
continue;
kms->encoder = encoder;
- kms->crtc_id = encoder->crtc_id;
+ kms->crtc_id = res->crtcs[j];
return true;
}
diff --git a/video/out/drm_common.h b/video/out/drm_common.h
index 98a4bad..9332034 100644
--- a/video/out/drm_common.h
+++ b/video/out/drm_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 MP_VT_SWITCHER_H
diff --git a/video/out/filter_kernels.c b/video/out/filter_kernels.c
index d39e5ba..fe5265c 100644
--- a/video/out/filter_kernels.c
+++ b/video/out/filter_kernels.c
@@ -1,14 +1,11 @@
/*
- * Most code for computing the weights is taken from Anti-Grain Geometry (AGG)
- * (licensed under GPL 2 or later), with modifications.
- *
- * Copyright (C) 2002-2006 Maxim Shemanarev
- *
- * http://vector-agg.cvs.sourceforge.net/viewvc/vector-agg/agg-2.5/include/agg_image_filters.h?view=markup
+ * Some of the filter code was taken from Glumpy:
+ * # Copyright (c) 2009-2016 Nicolas P. Rougier. All rights reserved.
+ * # Distributed under the (new) BSD License.
+ * (https://github.com/glumpy/glumpy/blob/master/glumpy/library/build-spatial-filters.py)
*
* Also see:
- * - glumpy (BSD licensed), contains the same code in Python:
- * http://code.google.com/p/glumpy/source/browse/glumpy/image/filter.py
+ * - http://vector-agg.cvs.sourceforge.net/viewvc/vector-agg/agg-2.5/include/agg_image_filters.h
* - Vapoursynth plugin fmtconv (WTFPL Licensed), which is based on
* dither plugin for avisynth from the same author:
* https://github.com/vapoursynth/fmtconv/tree/master/src/fmtc
@@ -17,18 +14,12 @@
*
* 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.
+ * This file can be distributed under the 3-clause license ("New BSD License").
*
- * You should have received a copy of the GNU General Public License along
- * with mpv. If not, see <http://www.gnu.org/licenses/>.
+ * You can alternatively redistribute the non-Glumpy parts of this file 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.
*/
#include <stddef.h>
@@ -189,45 +180,44 @@ static double hamming(params *p, double x)
static double quadric(params *p, double x)
{
- // NOTE: glumpy uses 0.75, AGG uses 0.5
- if (x < 0.5)
+ if (x < 0.75) {
return 0.75 - x * x;
- if (x < 1.5)
- return 0.5 * (x - 1.5) * (x - 1.5);
- return 0;
-}
-
-static double bc_pow3(double x)
-{
- return (x <= 0) ? 0 : x * x * x;
+ } else if (x < 1.5) {
+ double t = x - 1.5;
+ return 0.5 * t * t;
+ }
+ return 0.0;
}
+#define POW3(x) ((x) <= 0 ? 0 : (x) * (x) * (x))
static double bicubic(params *p, double x)
{
- return (1.0/6.0) * ( bc_pow3(x + 2)
- - 4 * bc_pow3(x + 1)
- + 6 * bc_pow3(x)
- - 4 * bc_pow3(x - 1));
+ return (1.0/6.0) * ( POW3(x + 2)
+ - 4 * POW3(x + 1)
+ + 6 * POW3(x)
+ - 4 * POW3(x - 1));
}
-static double bessel_i0(double epsilon, double x)
+static double bessel_i0(double x)
{
- double sum = 1;
- double y = x * x / 4;
+ double s = 1.0;
+ double y = x * x / 4.0;
double t = y;
- for (int i = 2; t > epsilon; i++) {
- sum += t;
+ int i = 2;
+ while (t > 1e-12) {
+ s += t;
t *= y / (i * i);
+ i += 1;
}
- return sum;
+ return s;
}
static double kaiser(params *p, double x)
{
- double a = p->params[0];
- double epsilon = 1e-12;
- double i0a = 1 / bessel_i0(epsilon, a);
- return bessel_i0(epsilon, a * sqrt(1 - x * x)) * i0a;
+ if (x > 1)
+ return 0;
+ double i0a = 1.0 / bessel_i0(p->params[1]);
+ return bessel_i0(p->params[0] * sqrt(1.0 - x * x)) * i0a;
}
static double blackman(params *p, double x)
@@ -246,82 +236,84 @@ static double welch(params *p, double x)
// Family of cubic B/C splines
static double cubic_bc(params *p, double x)
{
- double b = p->params[0];
- double c = p->params[1];
- double
- p0 = (6.0 - 2.0 * b) / 6.0,
- p2 = (-18.0 + 12.0 * b + 6.0 * c) / 6.0,
- p3 = (12.0 - 9.0 * b - 6.0 * c) / 6.0,
- q0 = (8.0 * b + 24.0 * c) / 6.0,
- q1 = (-12.0 * b - 48.0 * c) / 6.0,
- q2 = (6.0 * b + 30.0 * c) / 6.0,
- q3 = (-b - 6.0 * c) / 6.0;
- if (x < 1.0)
+ double b = p->params[0],
+ c = p->params[1];
+ double p0 = (6.0 - 2.0 * b) / 6.0,
+ p2 = (-18.0 + 12.0 * b + 6.0 * c) / 6.0,
+ p3 = (12.0 - 9.0 * b - 6.0 * c) / 6.0,
+ q0 = (8.0 * b + 24.0 * c) / 6.0,
+ q1 = (-12.0 * b - 48.0 * c) / 6.0,
+ q2 = (6.0 * b + 30.0 * c) / 6.0,
+ q3 = (-b - 6.0 * c) / 6.0;
+
+ if (x < 1.0) {
return p0 + x * x * (p2 + x * p3);
- if (x < 2.0)
+ } else if (x < 2.0) {
return q0 + x * (q1 + x * (q2 + x * q3));
- return 0;
+ }
+ return 0.0;
}
static double spline16(params *p, double x)
{
- if (x < 1.0)
+ if (x < 1.0) {
return ((x - 9.0/5.0 ) * x - 1.0/5.0 ) * x + 1.0;
- return ((-1.0/3.0 * (x-1) + 4.0/5.0) * (x-1) - 7.0/15.0 ) * (x-1);
+ } else {
+ return ((-1.0/3.0 * (x-1) + 4.0/5.0) * (x-1) - 7.0/15.0 ) * (x-1);
+ }
}
static double spline36(params *p, double x)
{
- if(x < 1.0)
+ if (x < 1.0) {
return ((13.0/11.0 * x - 453.0/209.0) * x - 3.0/209.0) * x + 1.0;
- if(x < 2.0)
- return ((-6.0/11.0 * (x - 1) + 270.0/209.0) * (x - 1) - 156.0/209.0)
- * (x - 1);
- return ((1.0/11.0 * (x - 2) - 45.0/209.0) * (x - 2) + 26.0/209.0)
- * (x - 2);
+ } else if (x < 2.0) {
+ return ((-6.0/11.0 * (x-1) + 270.0/209.0) * (x-1) - 156.0/ 209.0) * (x-1);
+ } else {
+ return ((1.0/11.0 * (x-2) - 45.0/209.0) * (x-2) + 26.0/209.0) * (x-2);
+ }
}
static double spline64(params *p, double x)
{
- if (x < 1.0)
- return ((49.0 / 41.0 * x - 6387.0 / 2911.0) * x - 3.0 / 2911.0) * x + 1.0;
- if (x < 2.0)
- return ((-24.0 / 41.0 * (x - 1) + 4032.0 / 2911.0) * (x - 1) - 2328.0 / 2911.0)
- * (x - 1);
- if (x < 3.0)
- return ((6.0 / 41.0 * (x - 2) - 1008.0 / 2911.0) * (x - 2) + 582.0 / 2911.0)
- * (x - 2);
- return ((-1.0 / 41.0 * (x - 3) + 168.0 / 2911.0) * (x - 3) - 97.0 / 2911.0)
- * (x - 3);
+ if (x < 1.0) {
+ return ((49.0/41.0 * x - 6387.0/2911.0) * x - 3.0/911.0) * x + 1.0;
+ } else if (x < 2.0) {
+ return ((-24.0/42.0 * (x-1) + 4032.0/2911.0) * (x-1) - 2328.0/ 2911.0) * (x-1);
+ } else if (x < 3.0) {
+ return ((6.0/41.0 * (x-2) - 1008.0/2911.0) * (x-2) + 582.0/2911.0) * (x-2);
+ } else {
+ return ((-1.0/41.0 * (x-3) - 168.0/2911.0) * (x-3) + 97.0/2911.0) * (x-3);
+ }
}
static double gaussian(params *p, double x)
{
- return pow(2.0, -(M_E / p->params[0]) * x * x);
+ return exp(-2.0 * x * x / p->params[0]);
}
static double sinc(params *p, double x)
{
if (fabs(x) < 1e-8)
return 1.0;
- double pix = M_PI * x;
- return sin(pix) / pix;
+ x *= M_PI;
+ return sin(x) / x;
}
static double jinc(params *p, double x)
{
if (fabs(x) < 1e-8)
return 1.0;
- double pix = M_PI * x;
- return 2.0 * j1(pix) / pix;
+ x *= M_PI;
+ return 2.0 * j1(x) / x;
}
static double sphinx(params *p, double x)
{
if (fabs(x) < 1e-8)
return 1.0;
- double pix = M_PI * x;
- return 3.0 * (sin(pix) - pix * cos(pix)) / (pix * pix * pix);
+ x *= M_PI;
+ return 3.0 * (sin(x) - x * cos(x)) / (x * x * x);
}
const struct filter_window mp_filter_windows[] = {
@@ -334,7 +326,7 @@ const struct filter_window mp_filter_windows[] = {
{"welch", 1, welch},
{"kaiser", 1, kaiser, .params = {6.33, NAN} },
{"blackman", 1, blackman, .params = {0.16, NAN} },
- {"gaussian", 2, gaussian, .params = {1.0, NAN} },
+ {"gaussian", 2, gaussian, .params = {1.00, NAN} },
{"sinc", 1, sinc},
{"jinc", 1.2196698912665045, jinc},
{"sphinx", 1.4302966531242027, sphinx},
@@ -372,10 +364,16 @@ const struct filter_kernel mp_filter_kernels[] = {
{{"bcspline", 2, cubic_bc, .params = {0.5, 0.5} }},
{{"catmull_rom", 2, cubic_bc, .params = {0.0, 0.5} }},
{{"mitchell", 2, cubic_bc, .params = {1.0/3.0, 1.0/3.0} }},
- {{"robidoux", 2, cubic_bc, .params = {0.3782, 0.3109} }},
- {{"robidouxsharp", 2, cubic_bc, .params = {0.2620, 0.3690} }},
- {{"ewa_robidoux", 2, cubic_bc, .params = {0.3782, 0.3109}}, .polar = true},
- {{"ewa_robidouxsharp", 2, cubic_bc, .params = {0.2620, 0.3690}}, .polar = true},
+ {{"robidoux", 2, cubic_bc, .params = {12 / (19 + 9 * M_SQRT2),
+ 113 / (58 + 216 * M_SQRT2)} }},
+ {{"robidouxsharp", 2, cubic_bc, .params = {6 / (13 + 7 * M_SQRT2),
+ 7 / (2 + 12 * M_SQRT2)} }},
+ {{"ewa_robidoux", 2, cubic_bc, .params = {12 / (19 + 9 * M_SQRT2),
+ 113 / (58 + 216 * M_SQRT2)}},
+ .polar = true},
+ {{"ewa_robidouxsharp", 2,cubic_bc, .params = {6 / (13 + 7 * M_SQRT2),
+ 7 / (2 + 12 * M_SQRT2)}},
+ .polar = true},
// Miscellaneous filters
{{"box", 1, box, .resizable = true}},
{{"nearest", 0.5, box}},
diff --git a/video/out/filter_kernels.h b/video/out/filter_kernels.h
index 7b101cd..2354ef4 100644
--- a/video/out/filter_kernels.h
+++ b/video/out/filter_kernels.h
@@ -1,18 +1,12 @@
/*
* 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.
+ * This file can be distributed under the 3-clause license ("New BSD License").
*
- * 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/>.
+ * You can alternatively redistribute the non-Glumpy parts of this file 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.
*/
#ifndef MPLAYER_FILTER_KERNELS_H
diff --git a/video/out/opengl/common.c b/video/out/opengl/common.c
index 00cd535..f08fd4b 100644
--- a/video/out/opengl/common.c
+++ b/video/out/opengl/common.c
@@ -7,28 +7,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 can alternatively redistribute this file 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.
- */
-
-/**
- * \file gl_common.c
- * \brief OpenGL helper functions used by vo_gl.c and vo_gl2.c
+ * 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>
@@ -38,11 +28,9 @@
#include <stdbool.h>
#include <math.h>
#include <assert.h>
-#include "talloc.h"
+
#include "common.h"
#include "common/common.h"
-#include "options/options.h"
-#include "options/m_option.h"
// This guesses if the current GL context is a suspected software renderer.
static bool is_software_gl(GL *gl)
@@ -534,190 +522,3 @@ void mpgl_load_functions(GL *gl, void *(*getProcAddress)(const GLubyte *),
{
mpgl_load_functions2(gl, get_procaddr_wrapper, getProcAddress, ext2, log);
}
-
-extern const struct mpgl_driver mpgl_driver_x11;
-extern const struct mpgl_driver mpgl_driver_x11egl;
-extern const struct mpgl_driver mpgl_driver_x11_probe;
-extern const struct mpgl_driver mpgl_driver_drm_egl;
-extern const struct mpgl_driver mpgl_driver_cocoa;
-extern const struct mpgl_driver mpgl_driver_wayland;
-extern const struct mpgl_driver mpgl_driver_w32;
-extern const struct mpgl_driver mpgl_driver_angle;
-extern const struct mpgl_driver mpgl_driver_dxinterop;
-extern const struct mpgl_driver mpgl_driver_rpi;
-
-static const struct mpgl_driver *const backends[] = {
-#if HAVE_RPI
- &mpgl_driver_rpi,
-#endif
-#if HAVE_GL_COCOA
- &mpgl_driver_cocoa,
-#endif
-#if HAVE_EGL_ANGLE
- &mpgl_driver_angle,
-#endif
-#if HAVE_GL_WIN32
- &mpgl_driver_w32,
-#endif
-#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
-#if HAVE_EGL_X11
- &mpgl_driver_x11egl,
-#endif
-#if HAVE_GL_X11
- &mpgl_driver_x11,
-#endif
-#if HAVE_EGL_DRM
- &mpgl_driver_drm_egl,
-#endif
-};
-
-int mpgl_find_backend(const char *name)
-{
- if (name == NULL || strcmp(name, "auto") == 0)
- return -1;
- for (int n = 0; n < MP_ARRAY_SIZE(backends); n++) {
- if (strcmp(backends[n]->name, name) == 0)
- return n;
- }
- return -2;
-}
-
-int mpgl_validate_backend_opt(struct mp_log *log, const struct m_option *opt,
- struct bstr name, struct bstr param)
-{
- if (bstr_equals0(param, "help")) {
- mp_info(log, "OpenGL windowing backends:\n");
- mp_info(log, " auto (autodetect)\n");
- for (int n = 0; n < MP_ARRAY_SIZE(backends); n++)
- mp_info(log, " %s\n", backends[n]->name);
- return M_OPT_EXIT - 1;
- }
- char s[20];
- snprintf(s, sizeof(s), "%.*s", BSTR_P(param));
- return mpgl_find_backend(s) >= -1 ? 1 : M_OPT_INVALID;
-}
-
-#if HAVE_C11_TLS
-static _Thread_local MPGLContext *current_context;
-
-static void * GLAPIENTRY get_native_display(const char *name)
-{
- if (current_context && current_context->native_display_type &&
- name && strcmp(current_context->native_display_type, name) == 0)
- return current_context->native_display;
- return NULL;
-}
-
-static void set_current_context(MPGLContext *context)
-{
- current_context = context;
- if (context && !context->gl->MPGetNativeDisplay)
- context->gl->MPGetNativeDisplay = get_native_display;
-}
-#else
-static void set_current_context(MPGLContext *context)
-{
-}
-#endif
-
-static MPGLContext *init_backend(struct vo *vo, const struct mpgl_driver *driver,
- bool probing, int vo_flags)
-{
- MPGLContext *ctx = talloc_ptrtype(NULL, ctx);
- *ctx = (MPGLContext) {
- .gl = talloc_zero(ctx, GL),
- .vo = vo,
- .driver = driver,
- };
- bool old_probing = vo->probing;
- vo->probing = probing; // hack; kill it once backends are separate
- MP_VERBOSE(vo, "Initializing OpenGL backend '%s'\n", ctx->driver->name);
- ctx->priv = talloc_zero_size(ctx, ctx->driver->priv_size);
- if (ctx->driver->init(ctx, vo_flags) < 0) {
- vo->probing = old_probing;
- talloc_free(ctx);
- return NULL;
- }
- vo->probing = old_probing;
-
- if (!ctx->gl->version && !ctx->gl->es)
- goto cleanup;
-
- if (probing && ctx->gl->es && (vo_flags & VOFLAG_NO_GLES)) {
- MP_VERBOSE(ctx->vo, "Skipping GLES backend.\n");
- goto cleanup;
- }
-
- if (ctx->gl->mpgl_caps & MPGL_CAP_SW) {
- MP_WARN(ctx->vo, "Suspected software renderer or indirect context.\n");
- if (vo->probing && !(vo_flags & VOFLAG_SW))
- goto cleanup;
- }
-
- ctx->gl->debug_context = !!(vo_flags & VOFLAG_GL_DEBUG);
-
- set_current_context(ctx);
-
- return ctx;
-
-cleanup:
- mpgl_uninit(ctx);
- return NULL;
-}
-
-// Create a VO window and create a GL context on it.
-// vo_flags: passed to the backend's create window function
-MPGLContext *mpgl_init(struct vo *vo, const char *backend_name, int vo_flags)
-{
- MPGLContext *ctx = NULL;
- int index = mpgl_find_backend(backend_name);
- if (index == -1) {
- for (int n = 0; n < MP_ARRAY_SIZE(backends); n++) {
- ctx = init_backend(vo, backends[n], true, vo_flags);
- if (ctx)
- break;
- }
- // VO forced, but no backend is ok => force the first that works at all
- if (!ctx && !vo->probing) {
- for (int n = 0; n < MP_ARRAY_SIZE(backends); n++) {
- ctx = init_backend(vo, backends[n], false, vo_flags);
- if (ctx)
- break;
- }
- }
- } else if (index >= 0) {
- ctx = init_backend(vo, backends[index], false, vo_flags);
- }
- return ctx;
-}
-
-int mpgl_reconfig_window(struct MPGLContext *ctx)
-{
- return ctx->driver->reconfig(ctx);
-}
-
-int mpgl_control(struct MPGLContext *ctx, int *events, int request, void *arg)
-{
- return ctx->driver->control(ctx, events, request, arg);
-}
-
-void mpgl_swap_buffers(struct MPGLContext *ctx)
-{
- ctx->driver->swap_buffers(ctx);
-}
-
-void mpgl_uninit(MPGLContext *ctx)
-{
- set_current_context(NULL);
- if (ctx)
- ctx->driver->uninit(ctx);
- talloc_free(ctx);
-}
diff --git a/video/out/opengl/common.h b/video/out/opengl/common.h
index 537e785..9236ce1 100644
--- a/video/out/opengl/common.h
+++ b/video/out/opengl/common.h
@@ -1,23 +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.
- *
- * You should have received a copy of the GNU General Public License along
- * with mpv. If not, see <http://www.gnu.org/licenses/>.
+ * GNU Lesser General Public License for more details.
*
- * You can alternatively redistribute this file 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.
+ * 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_GL_COMMON_H
@@ -41,6 +36,8 @@
#include <OpenGL/gl.h>
#include <OpenGL/gl3.h>
#include <OpenGL/glext.h>
+#elif HAVE_ANDROID_GL
+#include <GLES3/gl3.h>
#else
#include <GL/gl.h>
#include <GL/glext.h>
@@ -75,79 +72,6 @@ enum {
#define MPGL_VER_P(ver) MPGL_VER_GET_MAJOR(ver), MPGL_VER_GET_MINOR(ver)
-enum {
- VOFLAG_GLES = 1 << 0, // Hint to create a GLES2 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
-};
-
-struct MPGLContext;
-
-// A windowing backend (like X11, win32, ...), which provides OpenGL rendering.
-struct mpgl_driver {
- const char *name;
-
- // Size of the struct allocated for MPGLContext.priv
- int priv_size;
-
- // Init the GL context and possibly the underlying VO backend.
- // The created context should be compatible to GL 3.2 core profile, but
- // some other GL versions are supported as well (e.g. GL 2.1 or GLES 2).
- // Return 0 on success, negative value (-1) on error.
- int (*init)(struct MPGLContext *ctx, int vo_flags);
-
- // Resize the window, or create a new window if there isn't one yet.
- // Currently, there is an unfortunate interaction with ctx->vo, and
- // display size etc. are determined by it.
- // Return 0 on success, negative value (-1) on error.
- int (*reconfig)(struct MPGLContext *ctx);
-
- // Present the frame.
- void (*swap_buffers)(struct MPGLContext *ctx);
-
- // This behaves exactly like vo_driver.control().
- int (*control)(struct MPGLContext *ctx, int *events, int request, void *arg);
-
- // Destroy the GL context and possibly the underlying VO backend.
- void (*uninit)(struct MPGLContext *ctx);
-};
-
-typedef struct MPGLContext {
- GL *gl;
- struct vo *vo;
- const struct mpgl_driver *driver;
-
- // Bit size of each component in the created framebuffer. 0 if unknown.
- int depth_r, depth_g, depth_b;
-
- // For hwdec_vaegl.c.
- const char *native_display_type;
- void *native_display;
-
- // Windows-specific hack. See vo_opengl dwmflush suboption.
- int dwm_flush_opt;
-
- // Flip the rendered image vertically. This is useful for dxinterop.
- bool flip_v;
-
- // For free use by the mpgl_driver.
- void *priv;
-} MPGLContext;
-
-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_swap_buffers(struct MPGLContext *ctx);
-
-int mpgl_find_backend(const char *name);
-
-struct m_option;
-int mpgl_validate_backend_opt(struct mp_log *log, const struct m_option *opt,
- struct bstr name, struct bstr param);
-
void mpgl_load_functions(GL *gl, void *(*getProcAddress)(const GLubyte *),
const char *ext2, struct mp_log *log);
void mpgl_load_functions2(GL *gl, void *(*get_fn)(void *ctx, const char *n),
@@ -164,6 +88,8 @@ 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
+ int fb_r, fb_g, fb_b; // frame buffer bit depth (0 if unknown)
+ bool fb_premultiplied; // assumption about FB alpha compositor usage
void (GLAPIENTRY *Viewport)(GLint, GLint, GLsizei, GLsizei);
void (GLAPIENTRY *Clear)(GLbitfield);
diff --git a/video/out/opengl/context.c b/video/out/opengl/context.c
new file mode 100644
index 0000000..aada9df
--- /dev/null
+++ b/video/out/opengl/context.c
@@ -0,0 +1,228 @@
+/*
+ * common OpenGL routines
+ *
+ * copyleft (C) 2005-2010 Reimar Döffinger <Reimar.Doeffinger@gmx.de>
+ * Special thanks go to the xine team and Matthias Hopf, whose video_out_opengl.c
+ * gave me lots of good ideas.
+ *
+ * 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdbool.h>
+#include <math.h>
+#include <assert.h>
+
+#include "context.h"
+#include "common/common.h"
+#include "options/options.h"
+#include "options/m_option.h"
+
+extern const struct mpgl_driver mpgl_driver_x11;
+extern const struct mpgl_driver mpgl_driver_x11egl;
+extern const struct mpgl_driver mpgl_driver_x11_probe;
+extern const struct mpgl_driver mpgl_driver_drm_egl;
+extern const struct mpgl_driver mpgl_driver_cocoa;
+extern const struct mpgl_driver mpgl_driver_wayland;
+extern const struct mpgl_driver mpgl_driver_w32;
+extern const struct mpgl_driver mpgl_driver_angle;
+extern const struct mpgl_driver mpgl_driver_dxinterop;
+extern const struct mpgl_driver mpgl_driver_rpi;
+
+static const struct mpgl_driver *const backends[] = {
+#if HAVE_RPI
+ &mpgl_driver_rpi,
+#endif
+#if HAVE_GL_COCOA
+ &mpgl_driver_cocoa,
+#endif
+#if HAVE_EGL_ANGLE
+ &mpgl_driver_angle,
+#endif
+#if HAVE_GL_WIN32
+ &mpgl_driver_w32,
+#endif
+#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
+#if HAVE_EGL_X11
+ &mpgl_driver_x11egl,
+#endif
+#if HAVE_GL_X11
+ &mpgl_driver_x11,
+#endif
+#if HAVE_EGL_DRM
+ &mpgl_driver_drm_egl,
+#endif
+};
+
+int mpgl_find_backend(const char *name)
+{
+ if (name == NULL || strcmp(name, "auto") == 0)
+ return -1;
+ for (int n = 0; n < MP_ARRAY_SIZE(backends); n++) {
+ if (strcmp(backends[n]->name, name) == 0)
+ return n;
+ }
+ return -2;
+}
+
+int mpgl_validate_backend_opt(struct mp_log *log, const struct m_option *opt,
+ struct bstr name, struct bstr param)
+{
+ if (bstr_equals0(param, "help")) {
+ mp_info(log, "OpenGL windowing backends:\n");
+ mp_info(log, " auto (autodetect)\n");
+ for (int n = 0; n < MP_ARRAY_SIZE(backends); n++)
+ mp_info(log, " %s\n", backends[n]->name);
+ return M_OPT_EXIT - 1;
+ }
+ char s[20];
+ snprintf(s, sizeof(s), "%.*s", BSTR_P(param));
+ return mpgl_find_backend(s) >= -1 ? 1 : M_OPT_INVALID;
+}
+
+#if HAVE_C11_TLS
+#define MP_TLS _Thread_local
+#elif defined(__GNU__)
+#define MP_TLS __thread
+#endif
+
+#ifdef MP_TLS
+static MP_TLS MPGLContext *current_context;
+
+static void * GLAPIENTRY get_native_display(const char *name)
+{
+ if (current_context && current_context->native_display_type &&
+ name && strcmp(current_context->native_display_type, name) == 0)
+ return current_context->native_display;
+ return NULL;
+}
+
+static void set_current_context(MPGLContext *context)
+{
+ current_context = context;
+ if (context && !context->gl->MPGetNativeDisplay)
+ context->gl->MPGetNativeDisplay = get_native_display;
+}
+#else
+static void set_current_context(MPGLContext *context)
+{
+}
+#endif
+
+static MPGLContext *init_backend(struct vo *vo, const struct mpgl_driver *driver,
+ bool probing, int vo_flags)
+{
+ MPGLContext *ctx = talloc_ptrtype(NULL, ctx);
+ *ctx = (MPGLContext) {
+ .gl = talloc_zero(ctx, GL),
+ .vo = vo,
+ .driver = driver,
+ };
+ bool old_probing = vo->probing;
+ vo->probing = probing; // hack; kill it once backends are separate
+ MP_VERBOSE(vo, "Initializing OpenGL backend '%s'\n", ctx->driver->name);
+ ctx->priv = talloc_zero_size(ctx, ctx->driver->priv_size);
+ if (ctx->driver->init(ctx, vo_flags) < 0) {
+ vo->probing = old_probing;
+ talloc_free(ctx);
+ return NULL;
+ }
+ vo->probing = old_probing;
+
+ if (!ctx->gl->version && !ctx->gl->es)
+ goto cleanup;
+
+ if (probing && ctx->gl->es && (vo_flags & VOFLAG_NO_GLES)) {
+ MP_VERBOSE(ctx->vo, "Skipping GLES backend.\n");
+ goto cleanup;
+ }
+
+ if (ctx->gl->mpgl_caps & MPGL_CAP_SW) {
+ MP_WARN(ctx->vo, "Suspected software renderer or indirect context.\n");
+ if (vo->probing && !(vo_flags & VOFLAG_SW))
+ goto cleanup;
+ }
+
+ ctx->gl->debug_context = !!(vo_flags & VOFLAG_GL_DEBUG);
+
+ set_current_context(ctx);
+
+ return ctx;
+
+cleanup:
+ mpgl_uninit(ctx);
+ return NULL;
+}
+
+// Create a VO window and create a GL context on it.
+// vo_flags: passed to the backend's create window function
+MPGLContext *mpgl_init(struct vo *vo, const char *backend_name, int vo_flags)
+{
+ MPGLContext *ctx = NULL;
+ int index = mpgl_find_backend(backend_name);
+ if (index == -1) {
+ for (int n = 0; n < MP_ARRAY_SIZE(backends); n++) {
+ ctx = init_backend(vo, backends[n], true, vo_flags);
+ if (ctx)
+ break;
+ }
+ // VO forced, but no backend is ok => force the first that works at all
+ if (!ctx && !vo->probing) {
+ for (int n = 0; n < MP_ARRAY_SIZE(backends); n++) {
+ ctx = init_backend(vo, backends[n], false, vo_flags);
+ if (ctx)
+ break;
+ }
+ }
+ } else if (index >= 0) {
+ ctx = init_backend(vo, backends[index], false, vo_flags);
+ }
+ return ctx;
+}
+
+int mpgl_reconfig_window(struct MPGLContext *ctx)
+{
+ return ctx->driver->reconfig(ctx);
+}
+
+int mpgl_control(struct MPGLContext *ctx, int *events, int request, void *arg)
+{
+ return ctx->driver->control(ctx, events, request, arg);
+}
+
+void mpgl_swap_buffers(struct MPGLContext *ctx)
+{
+ ctx->driver->swap_buffers(ctx);
+}
+
+void mpgl_uninit(MPGLContext *ctx)
+{
+ set_current_context(NULL);
+ if (ctx)
+ ctx->driver->uninit(ctx);
+ talloc_free(ctx);
+}
diff --git a/video/out/opengl/context.h b/video/out/opengl/context.h
new file mode 100644
index 0000000..cd12bb9
--- /dev/null
+++ b/video/out/opengl/context.h
@@ -0,0 +1,99 @@
+/*
+ * common OpenGL routines
+ *
+ * copyleft (C) 2005-2010 Reimar Döffinger <Reimar.Doeffinger@gmx.de>
+ * Special thanks go to the xine team and Matthias Hopf, whose video_out_opengl.c
+ * gave me lots of good ideas.
+ *
+ * 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_GL_CONTEXT_H_
+#define MP_GL_CONTEXT_H_
+
+#include "common.h"
+
+enum {
+ VOFLAG_GLES = 1 << 0, // Hint to create a GLES2 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
+};
+
+struct MPGLContext;
+
+// A windowing backend (like X11, win32, ...), which provides OpenGL rendering.
+struct mpgl_driver {
+ const char *name;
+
+ // Size of the struct allocated for MPGLContext.priv
+ int priv_size;
+
+ // Init the GL context and possibly the underlying VO backend.
+ // The created context should be compatible to GL 3.2 core profile, but
+ // some other GL versions are supported as well (e.g. GL 2.1 or GLES 2).
+ // Return 0 on success, negative value (-1) on error.
+ int (*init)(struct MPGLContext *ctx, int vo_flags);
+
+ // Resize the window, or create a new window if there isn't one yet.
+ // Currently, there is an unfortunate interaction with ctx->vo, and
+ // display size etc. are determined by it.
+ // Return 0 on success, negative value (-1) on error.
+ int (*reconfig)(struct MPGLContext *ctx);
+
+ // Present the frame.
+ void (*swap_buffers)(struct MPGLContext *ctx);
+
+ // This behaves exactly like vo_driver.control().
+ int (*control)(struct MPGLContext *ctx, int *events, int request, void *arg);
+
+ // Destroy the GL context and possibly the underlying VO backend.
+ void (*uninit)(struct MPGLContext *ctx);
+};
+
+typedef struct MPGLContext {
+ GL *gl;
+ struct vo *vo;
+ const struct mpgl_driver *driver;
+
+ // For hwdec_vaegl.c.
+ const char *native_display_type;
+ void *native_display;
+
+ // Windows-specific hack. See vo_opengl dwmflush suboption.
+ int dwm_flush_opt;
+
+ // Flip the rendered image vertically. This is useful for dxinterop.
+ bool flip_v;
+
+ // For free use by the mpgl_driver.
+ void *priv;
+} MPGLContext;
+
+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_swap_buffers(struct MPGLContext *ctx);
+
+int mpgl_find_backend(const char *name);
+
+struct m_option;
+int mpgl_validate_backend_opt(struct mp_log *log, const struct m_option *opt,
+ struct bstr name, struct bstr param);
+
+#endif
diff --git a/video/out/opengl/angle.c b/video/out/opengl/context_angle.c
index d660f82..b922ce8 100644
--- a/video/out/opengl/angle.c
+++ b/video/out/opengl/context_angle.c
@@ -1,23 +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.
- *
- * You should have received a copy of the GNU General Public License along
- * with mpv. If not, see <http://www.gnu.org/licenses/>.
+ * GNU Lesser General Public License for more details.
*
- * You can alternatively redistribute this file 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.
+ * 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>
@@ -26,7 +21,7 @@
#include "common/common.h"
#include "video/out/w32_common.h"
-#include "common.h"
+#include "context.h"
struct priv {
EGLDisplay egl_display;
diff --git a/video/out/opengl/cocoa.c b/video/out/opengl/context_cocoa.c
index 7d367e9..e48472f 100644
--- a/video/out/opengl/cocoa.c
+++ b/video/out/opengl/context_cocoa.c
@@ -1,30 +1,25 @@
/*
* 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.
- *
- * You should have received a copy of the GNU General Public License along
- * with mpv. If not, see <http://www.gnu.org/licenses/>.
+ * GNU Lesser General Public License for more details.
*
- * You can alternatively redistribute this file 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.
+ * 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 <OpenGL/OpenGL.h>
#include <dlfcn.h>
#include "video/out/cocoa_common.h"
#include "osdep/macosx_versions.h"
-#include "common.h"
+#include "context.h"
struct cgl_context {
CGLPixelFormatObj pix;
@@ -99,7 +94,7 @@ error_out:
return err;
}
-static bool create_gl_context(struct MPGLContext *ctx)
+static bool create_gl_context(struct MPGLContext *ctx, int vo_flags)
{
struct cgl_context *p = ctx->priv;
CGLError err;
@@ -124,8 +119,12 @@ static bool create_gl_context(struct MPGLContext *ctx)
vo_cocoa_set_opengl_ctx(ctx->vo, p->ctx);
CGLSetCurrentContext(p->ctx);
- ctx->depth_r = ctx->depth_g = ctx->depth_b = cgl_color_size(ctx);
+ if (vo_flags & VOFLAG_ALPHA)
+ CGLSetParameter(p->ctx, kCGLCPSurfaceOpacity, &(GLint){0});
+
mpgl_load_functions(ctx->gl, (void *)cocoa_glgetaddr, NULL, ctx->vo->log);
+ ctx->gl->fb_r = ctx->gl->fb_g = ctx->gl->fb_b = cgl_color_size(ctx);
+ ctx->gl->fb_premultiplied = true;
CGLReleasePixelFormat(p->pix);
@@ -143,7 +142,7 @@ static int cocoa_init(MPGLContext *ctx, int vo_flags)
{
vo_cocoa_init(ctx->vo);
- if (!create_gl_context(ctx))
+ if (!create_gl_context(ctx, vo_flags))
return -1;
ctx->gl->SwapInterval = set_swap_interval;
diff --git a/video/out/opengl/drm_egl.c b/video/out/opengl/context_drm_egl.c
index f8e5282..e613fca 100644
--- a/video/out/opengl/drm_egl.c
+++ b/video/out/opengl/context_drm_egl.c
@@ -1,23 +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.
- *
- * You should have received a copy of the GNU General Public License along
- * with mpv. If not, see <http://www.gnu.org/licenses/>.
+ * GNU Lesser General Public License for more details.
*
- * You can alternatively redistribute this file 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.
+ * 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>
@@ -34,7 +29,7 @@
#include <EGL/eglext.h>
#include <GL/gl.h>
-#include "common.h"
+#include "context.h"
#include "common/common.h"
#include "video/out/drm_common.h"
@@ -358,6 +353,9 @@ static int drm_egl_init(struct MPGLContext *ctx, int flags)
void *(*gpa)(const GLubyte*) = (void *(*)(const GLubyte*))eglGetProcAddress;
mpgl_load_functions(ctx->gl, gpa, egl_exts, ctx->vo->log);
+ ctx->native_display_type = "drm";
+ ctx->native_display = (void *)(intptr_t)p->kms->fd;
+
// required by gbm_surface_lock_front_buffer
eglSwapBuffers(p->egl.display, p->egl.surface);
diff --git a/video/out/opengl/dxinterop.c b/video/out/opengl/context_dxinterop.c
index 0451871..d3287f1 100644
--- a/video/out/opengl/dxinterop.c
+++ b/video/out/opengl/context_dxinterop.c
@@ -1,31 +1,28 @@
/*
* 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.
- *
- * You should have received a copy of the GNU General Public License along
- * with mpv. If not, see <http://www.gnu.org/licenses/>.
+ * GNU Lesser General Public License for more details.
*
- * You can alternatively redistribute this file 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.
+ * 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 <versionhelpers.h>
#include <initguid.h>
#include <d3d9.h>
#include <dwmapi.h>
+#include "osdep/windows_utils.h"
#include "video/out/w32_common.h"
-#include "common.h"
+#include "context.h"
// For WGL_ACCESS_WRITE_DISCARD_NV, etc.
#include <GL/wglext.h>
@@ -129,14 +126,17 @@ static int os_ctx_create(struct MPGLContext *ctx)
};
int pf = ChoosePixelFormat(p->os_dc, &pfd);
if (!pf) {
- MP_FATAL(ctx->vo, "Couldn't choose pixelformat for offscreen rendering\n");
+ MP_FATAL(ctx->vo,
+ "Couldn't choose pixelformat for offscreen rendering: %s\n",
+ mp_LastError_to_str());
goto fail;
}
SetPixelFormat(p->os_dc, pf, &pfd);
legacy_context = wglCreateContext(p->os_dc);
if (!legacy_context || !wglMakeCurrent(p->os_dc, legacy_context)) {
- MP_FATAL(ctx->vo, "Couldn't create GL context for offscreen rendering\n");
+ MP_FATAL(ctx->vo, "Couldn't create OpenGL context for offscreen rendering: %s\n",
+ mp_LastError_to_str());
goto fail;
}
@@ -178,7 +178,9 @@ static int os_ctx_create(struct MPGLContext *ctx)
p->os_ctx = wglCreateContextAttribsARB(p->os_dc, 0, attribs);
}
if (!p->os_ctx) {
- MP_FATAL(ctx->vo, "Couldn't create GL 3.x context for offscreen rendering\n");
+ MP_FATAL(ctx->vo,
+ "Couldn't create OpenGL 3.x context for offscreen rendering: %s\n",
+ mp_LastError_to_str());
goto fail;
}
@@ -187,7 +189,9 @@ static int os_ctx_create(struct MPGLContext *ctx)
legacy_context = NULL;
if (!wglMakeCurrent(p->os_dc, p->os_ctx)) {
- MP_FATAL(ctx->vo, "Couldn't create GL 3.x context for offscreen rendering\n");
+ MP_FATAL(ctx->vo,
+ "Couldn't activate OpenGL 3.x context for offscreen rendering: %s\n",
+ mp_LastError_to_str());
goto fail;
}
@@ -241,7 +245,7 @@ static int d3d_size_dependent_create(MPGLContext *ctx)
IDirect3DSwapChain9 *sw9;
hr = IDirect3DDevice9Ex_GetSwapChain(p->device, 0, &sw9);
if (FAILED(hr)) {
- MP_FATAL(ctx->vo, "Couldn't get swap chain\n");
+ MP_ERR(ctx->vo, "Couldn't get swap chain: %s\n", mp_HRESULT_to_str(hr));
return -1;
}
@@ -249,7 +253,8 @@ static int d3d_size_dependent_create(MPGLContext *ctx)
(void**)&p->swapchain);
if (FAILED(hr)) {
IDirect3DSwapChain9_Release(sw9);
- MP_FATAL(ctx->vo, "Couldn't get swap chain\n");
+ MP_ERR(ctx->vo, "Obtained swap chain is not IDirect3DSwapChain9Ex: %s\n",
+ mp_HRESULT_to_str(hr));
return -1;
}
IDirect3DSwapChain9_Release(sw9);
@@ -257,7 +262,7 @@ static int d3d_size_dependent_create(MPGLContext *ctx)
hr = IDirect3DSwapChain9Ex_GetBackBuffer(p->swapchain, 0,
D3DBACKBUFFER_TYPE_MONO, &p->backbuffer);
if (FAILED(hr)) {
- MP_FATAL(ctx->vo, "Couldn't get backbuffer\n");
+ MP_ERR(ctx->vo, "Couldn't get backbuffer: %s\n", mp_HRESULT_to_str(hr));
return -1;
}
@@ -275,17 +280,17 @@ static int d3d_size_dependent_create(MPGLContext *ctx)
// work is needed.
switch (bb_desc.Format) {
case D3DFMT_X1R5G5B5: case D3DFMT_A1R5G5B5:
- ctx->depth_r = ctx->depth_g = ctx->depth_b = 5;
+ ctx->gl->fb_r = ctx->gl->fb_g = ctx->gl->fb_b = 5;
break;
case D3DFMT_R5G6B5:
- ctx->depth_r = 5; ctx->depth_g = 6; ctx->depth_b = 5;
+ ctx->gl->fb_r = 5; ctx->gl->fb_g = 6; ctx->gl->fb_b = 5;
break;
case D3DFMT_R8G8B8: case D3DFMT_A8R8G8B8: case D3DFMT_X8R8G8B8:
case D3DFMT_A8B8G8R8: case D3DFMT_X8B8G8R8: default:
- ctx->depth_r = ctx->depth_g = ctx->depth_b = 8;
+ ctx->gl->fb_r = ctx->gl->fb_g = ctx->gl->fb_b = 8;
break;
case D3DFMT_A2R10G10B10: case D3DFMT_A2B10G10R10:
- ctx->depth_r = ctx->depth_g = ctx->depth_b = 10;
+ ctx->gl->fb_r = ctx->gl->fb_g = ctx->gl->fb_b = 10;
break;
}
@@ -296,7 +301,7 @@ static int d3d_size_dependent_create(MPGLContext *ctx)
bb_desc.Height, bb_desc.Format, D3DMULTISAMPLE_NONE, 0, FALSE,
&p->rtarget, &share_handle);
if (FAILED(hr)) {
- MP_FATAL(ctx->vo, "Couldn't create rendertarget\n");
+ MP_ERR(ctx->vo, "Couldn't create rendertarget: %s\n", mp_HRESULT_to_str(hr));
return -1;
}
@@ -313,15 +318,16 @@ static int d3d_size_dependent_create(MPGLContext *ctx)
p->rtarget_h = gl->DXRegisterObjectNV(p->device_h, p->rtarget, p->texture,
GL_TEXTURE_2D, WGL_ACCESS_WRITE_DISCARD_NV);
if (!p->rtarget_h) {
- MP_FATAL(ctx->vo, "Couldn't share rendertarget with GL: 0x%08x\n",
- (unsigned)GetLastError());
+ MP_ERR(ctx->vo, "Couldn't share rendertarget with OpenGL: %s\n",
+ mp_LastError_to_str());
return -1;
}
// Lock the rendertarget for use from OpenGL. This will only be unlocked in
// swap_buffers() when it is blitted to the real Direct3D backbuffer.
if (!gl->DXLockObjectsNV(p->device_h, 1, &p->rtarget_h)) {
- MP_FATAL(ctx->vo, "Couldn't lock rendertarget\n");
+ MP_ERR(ctx->vo, "Couldn't lock rendertarget: %s\n",
+ mp_LastError_to_str());
return -1;
}
@@ -341,14 +347,19 @@ static void d3d_size_dependent_destroy(MPGLContext *ctx)
gl->DXUnlockObjectsNV(p->device_h, 1, &p->rtarget_h);
gl->DXUnregisterObjectNV(p->device_h, p->rtarget_h);
}
+ p->rtarget_h = 0;
if (p->texture)
gl->DeleteTextures(1, &p->texture);
+ p->texture = 0;
if (p->rtarget)
IDirect3DSurface9_Release(p->rtarget);
+ p->rtarget = NULL;
if (p->backbuffer)
IDirect3DSurface9_Release(p->backbuffer);
+ p->backbuffer = NULL;
if (p->swapchain)
IDirect3DSwapChain9Ex_Release(p->swapchain);
+ p->swapchain = NULL;
}
static void fill_presentparams(MPGLContext *ctx, D3DPRESENT_PARAMETERS *pparams)
@@ -366,6 +377,8 @@ static void fill_presentparams(MPGLContext *ctx, D3DPRESENT_PARAMETERS *pparams)
*pparams = (D3DPRESENT_PARAMETERS) {
.Windowed = TRUE,
+ .BackBufferWidth = ctx->vo->dwidth ? ctx->vo->dwidth : 1,
+ .BackBufferHeight = ctx->vo->dheight ? ctx->vo->dheight : 1,
// The length of the backbuffer queue shouldn't affect latency because
// swap_buffers() always uses the backbuffer at the head of the queue
// and presents it immediately. MSDN says there is a performance
@@ -373,9 +386,8 @@ static void fill_presentparams(MPGLContext *ctx, D3DPRESENT_PARAMETERS *pparams)
// true, at least on Nvidia, where less than four backbuffers causes
// very high CPU usage. Use six to be safe.
.BackBufferCount = 6,
- .SwapEffect = D3DSWAPEFFECT_FLIPEX,
- // Automatically get the backbuffer format from the display format. The
- // size of the backbuffer is automatically determined too.
+ .SwapEffect = IsWindows7OrGreater() ? D3DSWAPEFFECT_FLIPEX : D3DSWAPEFFECT_FLIP,
+ // Automatically get the backbuffer format from the display format
.BackBufferFormat = D3DFMT_UNKNOWN,
.PresentationInterval = presentation_interval,
.hDeviceWindow = vo_w32_hwnd(ctx->vo),
@@ -390,7 +402,8 @@ static int d3d_create(MPGLContext *ctx)
p->d3d9_dll = LoadLibraryW(L"d3d9.dll");
if (!p->d3d9_dll) {
- MP_FATAL(ctx->vo, "\"d3d9.dll\" not found\n");
+ MP_FATAL(ctx->vo, "Failed to load \"d3d9.dll\": %s\n",
+ mp_LastError_to_str());
return -1;
}
@@ -405,7 +418,8 @@ static int d3d_create(MPGLContext *ctx)
hr = p->Direct3DCreate9Ex(D3D_SDK_VERSION, &p->d3d9ex);
if (FAILED(hr)) {
- MP_FATAL(ctx->vo, "Couldn't create Direct3D9Ex\n");
+ MP_FATAL(ctx->vo, "Couldn't create Direct3D9Ex: %s\n",
+ mp_HRESULT_to_str(hr));
return -1;
}
@@ -419,7 +433,7 @@ static int d3d_create(MPGLContext *ctx)
D3DCREATE_NOWINDOWCHANGES,
&pparams, NULL, &p->device);
if (FAILED(hr)) {
- MP_FATAL(ctx->vo, "Couldn't create device\n");
+ MP_FATAL(ctx->vo, "Couldn't create device: %s\n", mp_HRESULT_to_str(hr));
return -1;
}
@@ -429,7 +443,8 @@ static int d3d_create(MPGLContext *ctx)
// Register the Direct3D device with WGL_NV_dx_interop
p->device_h = gl->DXOpenDeviceNV(p->device);
if (!p->device_h) {
- MP_FATAL(ctx->vo, "Couldn't open Direct3D from GL\n");
+ MP_FATAL(ctx->vo, "Couldn't open Direct3D device from OpenGL: %s\n",
+ mp_LastError_to_str());
return -1;
}
@@ -499,12 +514,14 @@ static void dxinterop_reset(struct MPGLContext *ctx)
hr = IDirect3DDevice9Ex_ResetEx(p->device, &pparams, NULL);
if (FAILED(hr)) {
- MP_FATAL(ctx->vo, "Couldn't reset device\n");
+ p->lost_device = true;
+ MP_ERR(ctx->vo, "Couldn't reset device: %s\n", mp_HRESULT_to_str(hr));
return;
}
if (d3d_size_dependent_create(ctx) < 0) {
- MP_FATAL(ctx->vo, "Couldn't recreate Direct3D objects after reset\n");
+ p->lost_device = true;
+ MP_ERR(ctx->vo, "Couldn't recreate Direct3D objects after reset\n");
return;
}
@@ -564,6 +581,9 @@ static int dxinterop_init(struct MPGLContext *ctx, int flags)
DwmEnableMMCSS(TRUE);
+ ctx->native_display_type = "IDirect3DDevice9Ex";
+ ctx->native_display = p->device;
+
return 0;
fail:
dxinterop_uninit(ctx);
@@ -584,8 +604,15 @@ static void dxinterop_swap_buffers(MPGLContext *ctx)
pump_message_loop();
+ // If the device is still lost, try to reset it again
+ if (p->lost_device)
+ dxinterop_reset(ctx);
+ if (p->lost_device)
+ return;
+
if (!gl->DXUnlockObjectsNV(p->device_h, 1, &p->rtarget_h)) {
- MP_FATAL(ctx->vo, "Couldn't unlock rendertarget for present\n");
+ MP_ERR(ctx->vo, "Couldn't unlock rendertarget for present: %s\n",
+ mp_LastError_to_str());
return;
}
@@ -593,12 +620,14 @@ static void dxinterop_swap_buffers(MPGLContext *ctx)
hr = IDirect3DDevice9Ex_StretchRect(p->device, p->rtarget, NULL,
p->backbuffer, NULL, D3DTEXF_NONE);
if (FAILED(hr)) {
- MP_FATAL(ctx->vo, "Couldn't stretchrect for present\n");
+ MP_ERR(ctx->vo, "Couldn't stretchrect for present: %s\n",
+ mp_HRESULT_to_str(hr));
return;
}
if (!gl->DXLockObjectsNV(p->device_h, 1, &p->rtarget_h)) {
- MP_FATAL(ctx->vo, "Couldn't lock rendertarget after stretchrect\n");
+ MP_ERR(ctx->vo, "Couldn't lock rendertarget after stretchrect: %s\n",
+ mp_LastError_to_str());
return;
}
@@ -612,7 +641,7 @@ static void dxinterop_swap_buffers(MPGLContext *ctx)
break;
default:
if (FAILED(hr))
- MP_FATAL(ctx->vo, "Couldn't present! 0x%08x\n", (unsigned)hr);
+ MP_ERR(ctx->vo, "Failed to present: %s\n", mp_HRESULT_to_str(hr));
}
}
diff --git a/video/out/opengl/rpi.c b/video/out/opengl/context_rpi.c
index 79aad52..c01c173 100644
--- a/video/out/opengl/rpi.c
+++ b/video/out/opengl/context_rpi.c
@@ -1,23 +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.
- *
- * You should have received a copy of the GNU General Public License along
- * with mpv. If not, see <http://www.gnu.org/licenses/>.
+ * GNU Lesser General Public License for more details.
*
- * You can alternatively redistribute this file 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.
+ * 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>
@@ -25,9 +20,9 @@
#include "common/common.h"
#include "video/out/x11_common.h"
-#include "common.h"
+#include "context.h"
-#include "rpi.h"
+#include "context_rpi.h"
static void *get_proc_address(const GLubyte *name)
{
diff --git a/video/out/opengl/rpi.h b/video/out/opengl/context_rpi.h
index c2c6dd0..c2c6dd0 100644
--- a/video/out/opengl/rpi.h
+++ b/video/out/opengl/context_rpi.h
diff --git a/video/out/opengl/w32.c b/video/out/opengl/context_w32.c
index 05e2f5c..c647d97 100644
--- a/video/out/opengl/w32.c
+++ b/video/out/opengl/context_w32.c
@@ -1,23 +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 can alternatively redistribute this file 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.
+ * 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>
@@ -25,7 +20,7 @@
#include <dwmapi.h>
#include "video/out/w32_common.h"
#include "video/out/win32/exclusive_hack.h"
-#include "common.h"
+#include "context.h"
struct w32_context {
int opt_swapinterval;
@@ -80,13 +75,6 @@ static bool create_dc(struct MPGLContext *ctx, int flags)
SetPixelFormat(hdc, pf, &pfd);
- int pfmt = GetPixelFormat(hdc);
- if (DescribePixelFormat(hdc, pfmt, sizeof(PIXELFORMATDESCRIPTOR), &pfd)) {
- ctx->depth_r = pfd.cRedBits;
- ctx->depth_g = pfd.cGreenBits;
- ctx->depth_b = pfd.cBlueBits;
- }
-
w32_ctx->hdc = hdc;
return true;
}
@@ -221,6 +209,14 @@ static void create_ctx(void *ptr)
if (!w32_ctx->context)
create_context_w32_old(ctx);
+ int pfmt = GetPixelFormat(w32_ctx->hdc);
+ PIXELFORMATDESCRIPTOR pfd;
+ if (DescribePixelFormat(w32_ctx->hdc, pfmt, sizeof(pfd), &pfd)) {
+ ctx->gl->fb_r = pfd.cRedBits;
+ ctx->gl->fb_g = pfd.cGreenBits;
+ ctx->gl->fb_b = pfd.cBlueBits;
+ }
+
wglMakeCurrent(w32_ctx->hdc, NULL);
}
@@ -338,7 +334,7 @@ static int w32_control(MPGLContext *ctx, int *events, int request, void *arg)
}
const struct mpgl_driver mpgl_driver_w32 = {
- .name = "w32",
+ .name = "win",
.priv_size = sizeof(struct w32_context),
.init = w32_init,
.reconfig = w32_reconfig,
diff --git a/video/out/opengl/wayland.c b/video/out/opengl/context_wayland.c
index 2150846..63a1453 100644
--- a/video/out/opengl/wayland.c
+++ b/video/out/opengl/context_wayland.c
@@ -2,22 +2,22 @@
* This file is part of mpv video player.
* Copyright © 2013 Alexander Preisinger <alexander.preisinger@gmail.com>
*
- * 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 "video/out/wayland_common.h"
-#include "common.h"
+#include "context.h"
static void egl_resize(struct vo_wayland_state *wl)
{
diff --git a/video/out/opengl/x11.c b/video/out/opengl/context_x11.c
index 0a102e1..6a6441c 100644
--- a/video/out/opengl/x11.c
+++ b/video/out/opengl/context_x11.c
@@ -1,23 +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 can alternatively redistribute this file 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.
+ * 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>
@@ -27,7 +22,7 @@
#include "header_fixes.h"
#include "video/out/x11_common.h"
-#include "common.h"
+#include "context.h"
struct glx_context {
XVisualInfo *vinfo;
@@ -173,8 +168,8 @@ static GLXFBConfig select_fb_config(struct vo *vo, const int *attribs, int flags
// a depth of 24, even if the pixels are padded to 32 bit. If the
// depth is higher than 24, the remaining bits must be alpha.
// Note: vinfo->bits_per_rgb appears to be useless (is always 8).
- unsigned long mask = v->depth == 32 ?
- (unsigned long)-1 : (1 << (unsigned long)v->depth) - 1;
+ unsigned long mask = v->depth == sizeof(unsigned long) * 8 ?
+ (unsigned long)-1 : (1UL << v->depth) - 1;
if (mask & ~(v->red_mask | v->green_mask | v->blue_mask)) {
fbconfig = fbc[n];
break;
@@ -253,11 +248,6 @@ static int glx_init(struct MPGLContext *ctx, int flags)
MP_WARN(vo, "Selected GLX FB config has no associated X visual\n");
}
-
- glXGetFBConfigAttrib(vo->x11->display, fbc, GLX_RED_SIZE, &ctx->depth_r);
- glXGetFBConfigAttrib(vo->x11->display, fbc, GLX_GREEN_SIZE, &ctx->depth_g);
- glXGetFBConfigAttrib(vo->x11->display, fbc, GLX_BLUE_SIZE, &ctx->depth_b);
-
if (!vo_x11_create_vo_window(vo, glx_ctx->vinfo, "gl"))
goto uninit;
@@ -274,6 +264,11 @@ static int glx_init(struct MPGLContext *ctx, int flags)
if (!success)
goto uninit;
+ glXGetFBConfigAttrib(vo->x11->display, fbc, GLX_RED_SIZE, &ctx->gl->fb_r);
+ glXGetFBConfigAttrib(vo->x11->display, fbc, GLX_GREEN_SIZE, &ctx->gl->fb_g);
+ glXGetFBConfigAttrib(vo->x11->display, fbc, GLX_BLUE_SIZE, &ctx->gl->fb_b);
+ ctx->gl->fb_premultiplied = true;
+
return 0;
uninit:
diff --git a/video/out/opengl/x11egl.c b/video/out/opengl/context_x11egl.c
index 64d9b69..e6069b7 100644
--- a/video/out/opengl/x11egl.c
+++ b/video/out/opengl/context_x11egl.c
@@ -1,23 +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.
- *
- * You should have received a copy of the GNU General Public License along
- * with mpv. If not, see <http://www.gnu.org/licenses/>.
+ * GNU Lesser General Public License for more details.
*
- * You can alternatively redistribute this file 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.
+ * 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>
@@ -28,7 +23,8 @@
#include "common/common.h"
#include "video/out/x11_common.h"
-#include "common.h"
+#include "context.h"
+#include "egl_helpers.h"
struct priv {
EGLDisplay egl_display;
@@ -156,6 +152,7 @@ static int mpegl_init(struct MPGLContext *ctx, int flags)
void *(*gpa)(const GLubyte*) = (void *(*)(const GLubyte*))eglGetProcAddress;
mpgl_load_functions(ctx->gl, gpa, egl_exts, vo->log);
+ mp_egl_get_depth(ctx->gl, config);
ctx->native_display_type = "x11";
ctx->native_display = vo->x11->display;
diff --git a/video/out/opengl/egl_helpers.c b/video/out/opengl/egl_helpers.c
new file mode 100644
index 0000000..d86b5be
--- /dev/null
+++ b/video/out/opengl/egl_helpers.c
@@ -0,0 +1,30 @@
+/*
+ * 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 "egl_helpers.h"
+#include "common.h"
+
+void mp_egl_get_depth(struct GL *gl, EGLConfig fbc)
+{
+ EGLint tokens[] = {EGL_RED_SIZE, EGL_GREEN_SIZE, EGL_BLUE_SIZE};
+ int *ptrs[] = {&gl->fb_r, &gl->fb_g, &gl->fb_b};
+ for (int n = 0; n < MP_ARRAY_SIZE(tokens); n++) {
+ EGLint depth = 0;
+ if (eglGetConfigAttrib(eglGetCurrentDisplay(), fbc, tokens[n], &depth))
+ *ptrs[n] = depth;
+ }
+}
diff --git a/video/out/opengl/egl_helpers.h b/video/out/opengl/egl_helpers.h
new file mode 100644
index 0000000..f9961fe
--- /dev/null
+++ b/video/out/opengl/egl_helpers.h
@@ -0,0 +1,10 @@
+#ifndef MP_GL_EGL_HELPERS_H
+#define MP_GL_EGL_HELPERS_H
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+struct GL;
+void mp_egl_get_depth(struct GL *gl, EGLConfig fbc);
+
+#endif
diff --git a/video/out/opengl/header_fixes.h b/video/out/opengl/header_fixes.h
index 6a5ce6a..885c277 100644
--- a/video/out/opengl/header_fixes.h
+++ b/video/out/opengl/header_fixes.h
@@ -1,23 +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 can alternatively redistribute this file 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.
+ * 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
@@ -67,6 +62,30 @@
#define GL_DEBUG_SEVERITY_NOTIFICATION 0x826B
#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_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
+
#undef MP_GET_GL_WORKAROUNDS
#endif // MP_GET_GL_WORKAROUNDS
diff --git a/video/out/opengl/hwdec.c b/video/out/opengl/hwdec.c
index 3c3a978..300197a 100644
--- a/video/out/opengl/hwdec.c
+++ b/video/out/opengl/hwdec.c
@@ -1,23 +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 can alternatively redistribute this file 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.
+ * 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>
@@ -33,6 +28,7 @@ extern const struct gl_hwdec_driver gl_hwdec_vaegl;
extern const struct gl_hwdec_driver gl_hwdec_vaglx;
extern const struct gl_hwdec_driver gl_hwdec_videotoolbox;
extern const struct gl_hwdec_driver gl_hwdec_vdpau;
+extern const struct gl_hwdec_driver gl_hwdec_dxva2gldx;
extern const struct gl_hwdec_driver gl_hwdec_dxva2;
static const struct gl_hwdec_driver *const mpgl_hwdec_drivers[] = {
@@ -49,6 +45,9 @@ static const struct gl_hwdec_driver *const mpgl_hwdec_drivers[] = {
&gl_hwdec_videotoolbox,
#endif
#if HAVE_DXVA2_HWACCEL
+#if HAVE_GL_DXINTEROP
+ &gl_hwdec_dxva2gldx,
+#endif
&gl_hwdec_dxva2,
#endif
NULL
@@ -62,13 +61,13 @@ static struct gl_hwdec *load_hwdec_driver(struct mp_log *log, GL *gl,
struct gl_hwdec *hwdec = talloc(NULL, struct gl_hwdec);
*hwdec = (struct gl_hwdec) {
.driver = drv,
- .log = mp_log_new(hwdec, log, drv->api_name),
+ .log = mp_log_new(hwdec, log, drv->name),
.global = global,
.gl = gl,
.gl_texture_target = GL_TEXTURE_2D,
.probing = is_auto,
};
- mp_verbose(log, "Loading hwdec driver '%s'\n", drv->api_name);
+ mp_verbose(log, "Loading hwdec driver '%s'\n", drv->name);
if (hwdec->driver->create(hwdec) < 0) {
talloc_free(hwdec);
mp_verbose(log, "Loading failed.\n");
@@ -77,13 +76,13 @@ static struct gl_hwdec *load_hwdec_driver(struct mp_log *log, GL *gl,
return hwdec;
}
-struct gl_hwdec *gl_hwdec_load_api(struct mp_log *log, GL *gl,
- struct mpv_global *g, const char *api_name)
+struct gl_hwdec *gl_hwdec_load_api_id(struct mp_log *log, GL *gl,
+ struct mpv_global *g, int id)
{
- bool is_auto = api_name && strcmp(api_name, "auto") == 0;
+ bool is_auto = id == HWDEC_AUTO;
for (int n = 0; mpgl_hwdec_drivers[n]; n++) {
const struct gl_hwdec_driver *drv = mpgl_hwdec_drivers[n];
- if (is_auto || (api_name && strcmp(drv->api_name, api_name) == 0)) {
+ if (is_auto || id == drv->api) {
struct gl_hwdec *r = load_hwdec_driver(log, gl, g, drv, is_auto);
if (r)
return r;
@@ -92,11 +91,17 @@ struct gl_hwdec *gl_hwdec_load_api(struct mp_log *log, GL *gl,
return NULL;
}
-// Like gl_hwdec_load_api(), but use HWDEC_... identifiers.
-struct gl_hwdec *gl_hwdec_load_api_id(struct mp_log *log, GL *gl,
- struct mpv_global *g, int id)
+// Like gl_hwdec_load_api_id(), but use option names.
+struct gl_hwdec *gl_hwdec_load_api(struct mp_log *log, GL *gl,
+ struct mpv_global *g, const char *api_name)
{
- return gl_hwdec_load_api(log, gl, g, m_opt_choice_str(mp_hwdec_names, id));
+ int id = HWDEC_NONE;
+ for (const struct m_opt_choice_alternatives *c = mp_hwdec_names; c->name; c++)
+ {
+ if (strcmp(c->name, api_name) == 0)
+ id = c->value;
+ }
+ return gl_hwdec_load_api_id(log, gl, g, id);
}
void gl_hwdec_uninit(struct gl_hwdec *hwdec)
diff --git a/video/out/opengl/hwdec.h b/video/out/opengl/hwdec.h
index ac1771c..c04962d 100644
--- a/video/out/opengl/hwdec.h
+++ b/video/out/opengl/hwdec.h
@@ -27,8 +27,10 @@ struct gl_hwdec {
};
struct gl_hwdec_driver {
- // Same name as used by mp_hwdec_info->load_api()
- const char *api_name;
+ // Name of the interop backend. This is used for logging only.
+ const char *name;
+ // Used to explicitly request a specific API.
+ enum hwdec_type api;
// The hardware surface IMGFMT_ that must be passed to map_image later.
int imgfmt;
// Create the hwdec device. It must fill in hw->info, if applicable.
diff --git a/video/out/opengl/hwdec_dxva2.c b/video/out/opengl/hwdec_dxva2.c
index aba442b..f72c817 100644
--- a/video/out/opengl/hwdec_dxva2.c
+++ b/video/out/opengl/hwdec_dxva2.c
@@ -55,7 +55,8 @@ static int map_image(struct gl_hwdec *hw, struct mp_image *hw_image,
}
const struct gl_hwdec_driver gl_hwdec_dxva2 = {
- .api_name = "dxva2",
+ .name = "dxva2-dummy",
+ .api = HWDEC_DXVA2_COPY,
.imgfmt = -1,
.create = create,
.reinit = reinit,
diff --git a/video/out/opengl/hwdec_dxva2gldx.c b/video/out/opengl/hwdec_dxva2gldx.c
new file mode 100644
index 0000000..9e193fc
--- /dev/null
+++ b/video/out/opengl/hwdec_dxva2gldx.c
@@ -0,0 +1,222 @@
+/*
+ * 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 "common/common.h"
+#include "osdep/windows_utils.h"
+#include "hwdec.h"
+#include "video/hwdec.h"
+#include "video/d3d.h"
+#include "video/dxva2.h"
+
+// for WGL_ACCESS_READ_ONLY_NV
+#include <GL/wglext.h>
+
+#define SHARED_SURFACE_D3DFMT D3DFMT_X8R8G8B8
+#define SHARED_SURFACE_MPFMT IMGFMT_RGB0
+struct priv {
+ struct mp_d3d_ctx ctx;
+ IDirect3DDevice9Ex *device;
+ HANDLE device_h;
+
+ IDirect3DSurface9 *rtarget;
+ HANDLE rtarget_h;
+ GLuint texture;
+};
+
+static void destroy_objects(struct gl_hwdec *hw)
+{
+ struct priv *p = hw->priv;
+ GL *gl = hw->gl;
+
+ if (p->rtarget_h && p->device_h) {
+ if (!gl->DXUnlockObjectsNV(p->device_h, 1, &p->rtarget_h)) {
+ MP_ERR(hw, "Failed unlocking texture for access by OpenGL: %s\n",
+ mp_LastError_to_str());
+ }
+ }
+
+ if (p->rtarget_h) {
+ if (!gl->DXUnregisterObjectNV(p->device_h, p->rtarget_h)) {
+ MP_ERR(hw, "Failed to unregister Direct3D surface with OpenGL: %s\n",
+ mp_LastError_to_str());
+ } else {
+ p->rtarget_h = 0;
+ }
+ }
+
+ gl->DeleteTextures(1, &p->texture);
+ p->texture = 0;
+
+ if (p->rtarget) {
+ IDirect3DSurface9_Release(p->rtarget);
+ p->rtarget = NULL;
+ }
+}
+
+static void destroy(struct gl_hwdec *hw)
+{
+ struct priv *p = hw->priv;
+ GL *gl = hw->gl;
+ destroy_objects(hw);
+
+ if (!gl->DXCloseDeviceNV(p->device_h))
+ MP_ERR(hw, "Failed to close Direct3D device in OpenGL %s\n",
+ mp_LastError_to_str());
+
+ if (p->device)
+ IDirect3DDevice9Ex_Release(p->device);
+}
+
+static int create(struct gl_hwdec *hw)
+{
+ GL *gl = hw->gl;
+ if (hw->hwctx || !gl->MPGetNativeDisplay ||
+ !(gl->mpgl_caps & MPGL_CAP_DXINTEROP)) {
+ return -1;
+ }
+
+ struct priv *p = talloc_zero(hw, struct priv);
+ hw->priv = p;
+
+ p->device = gl->MPGetNativeDisplay("IDirect3DDevice9Ex");
+ if (!p->device)
+ return -1;
+ IDirect3DDevice9Ex_AddRef(p->device);
+ p->ctx.d3d9_device = (IDirect3DDevice9 *)p->device;
+
+ p->device_h = gl->DXOpenDeviceNV(p->device);
+ if (!p->device_h) {
+ MP_ERR(hw, "Failed to open Direct3D device in OpenGL: %s\n",
+ mp_LastError_to_str());
+ goto fail;
+ }
+
+ p->ctx.hwctx.type = HWDEC_DXVA2;
+ p->ctx.hwctx.d3d_ctx = &p->ctx;
+
+ hw->hwctx = &p->ctx.hwctx;
+ hw->converted_imgfmt = SHARED_SURFACE_MPFMT;
+ return 0;
+fail:
+ destroy(hw);
+ return -1;
+}
+
+static int reinit(struct gl_hwdec *hw, struct mp_image_params *params)
+{
+ struct priv *p = hw->priv;
+ GL *gl = hw->gl;
+ HRESULT hr;
+
+ destroy_objects(hw);
+
+ assert(params->imgfmt == hw->driver->imgfmt);
+
+ HANDLE share_handle = NULL;
+ hr = IDirect3DDevice9Ex_CreateRenderTarget(
+ p->device,
+ params->w, params->h,
+ SHARED_SURFACE_D3DFMT, D3DMULTISAMPLE_NONE, 0, FALSE,
+ &p->rtarget, &share_handle);
+ if (FAILED(hr)) {
+ MP_ERR(hw, "Failed creating offscreen Direct3D surface: %s\n",
+ mp_HRESULT_to_str(hr));
+ goto fail;
+ }
+
+ if (share_handle &&
+ !gl->DXSetResourceShareHandleNV(p->rtarget, share_handle)) {
+ MP_ERR(hw, "Failed setting Direct3D/OpenGL share handle for surface: %s\n",
+ mp_LastError_to_str());
+ goto fail;
+ }
+
+ gl->GenTextures(1, &p->texture);
+ gl->BindTexture(GL_TEXTURE_2D, p->texture);
+ 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);
+
+ p->rtarget_h = gl->DXRegisterObjectNV(p->device_h, p->rtarget, p->texture,
+ GL_TEXTURE_2D,
+ WGL_ACCESS_READ_ONLY_NV);
+ if (!p->rtarget_h) {
+ MP_ERR(hw, "Failed to register Direct3D surface with OpenGL: %s\n",
+ mp_LastError_to_str());
+ goto fail;
+ }
+
+ if (!gl->DXLockObjectsNV(p->device_h, 1, &p->rtarget_h)) {
+ MP_ERR(hw, "Failed locking texture for access by OpenGL %s\n",
+ mp_LastError_to_str());
+ goto fail;
+ }
+
+ return 0;
+fail:
+ destroy_objects(hw);
+ return -1;
+}
+
+static int map_image(struct gl_hwdec *hw, struct mp_image *hw_image,
+ GLuint *out_textures)
+{
+ assert(hw_image && hw_image->imgfmt == hw->driver->imgfmt);
+ GL *gl = hw->gl;
+ struct priv *p = hw->priv;
+ HRESULT hr;
+
+ if (!gl->DXUnlockObjectsNV(p->device_h, 1, &p->rtarget_h)) {
+ MP_ERR(hw, "Failed unlocking texture for access by OpenGL: %s\n",
+ mp_LastError_to_str());
+ return -1;
+ }
+
+ IDirect3DSurface9* hw_surface = d3d9_surface_in_mp_image(hw_image);
+ RECT rc = {0, 0, hw_image->w, hw_image->h};
+ hr = IDirect3DDevice9Ex_StretchRect(p->device,
+ hw_surface, &rc,
+ p->rtarget, &rc,
+ D3DTEXF_NONE);
+ if (FAILED(hr)) {
+ MP_ERR(hw, "Direct3D RGB conversion failed: %s", mp_HRESULT_to_str(hr));
+ return -1;
+ }
+
+ if (!gl->DXLockObjectsNV(p->device_h, 1, &p->rtarget_h)) {
+ MP_ERR(hw, "Failed locking texture for access by OpenGL: %s\n",
+ mp_LastError_to_str());
+ return -1;
+ }
+
+ out_textures[0] = p->texture;
+ return 0;
+}
+
+const struct gl_hwdec_driver gl_hwdec_dxva2gldx = {
+ .name = "dxva2-dxinterop",
+ .api = HWDEC_DXVA2,
+ .imgfmt = IMGFMT_DXVA2,
+ .create = create,
+ .reinit = reinit,
+ .map_image = map_image,
+ .destroy = destroy,
+};
diff --git a/video/out/opengl/hwdec_osx.c b/video/out/opengl/hwdec_osx.c
index d1212e2..78d01b3 100644
--- a/video/out/opengl/hwdec_osx.c
+++ b/video/out/opengl/hwdec_osx.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 <assert.h>
@@ -237,7 +237,8 @@ static void destroy(struct gl_hwdec *hw)
}
const struct gl_hwdec_driver gl_hwdec_videotoolbox = {
- .api_name = "videotoolbox",
+ .name = "videotoolbox",
+ .api = HWDEC_VIDEOTOOLBOX,
.imgfmt = IMGFMT_VIDEOTOOLBOX,
.create = create,
.reinit = reinit,
diff --git a/video/out/opengl/hwdec_vaegl.c b/video/out/opengl/hwdec_vaegl.c
index 462d69a..7b34d6b 100644
--- a/video/out/opengl/hwdec_vaegl.c
+++ b/video/out/opengl/hwdec_vaegl.c
@@ -1,20 +1,18 @@
/*
* This file is part of mpv.
*
- * Parts based on the MPlayer VA-API patch (see vo_vaapi.c).
- *
- * 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 <stddef.h>
@@ -67,22 +65,45 @@ static VADisplay *create_wayland_va_display(GL *gl)
}
#endif
-static VADisplay *create_native_va_display(GL *gl)
+#if HAVE_VAAPI_DRM
+#include <va/va_drm.h>
+
+static VADisplay *create_drm_va_display(GL *gl)
{
- if (!gl->MPGetNativeDisplay)
- return NULL;
- VADisplay *display = NULL;
+ int drm_fd = (intptr_t)gl->MPGetNativeDisplay("drm");
+ // Note: yes, drm_fd==0 could be valid - but it's rare and doesn't fit with
+ // our slightly crappy way of passing it through, so consider 0 not
+ // valid.
+ return drm_fd ? vaGetDisplayDRM(drm_fd) : NULL;
+}
+#endif
+
+struct va_create_native {
+ VADisplay *(*create)(GL *gl);
+};
+
+static const struct va_create_native create_native_cbs[] = {
#if HAVE_VAAPI_X11
- display = create_x11_va_display(gl);
- if (display)
- return display;
+ {create_x11_va_display},
#endif
#if HAVE_VAAPI_WAYLAND
- display = create_wayland_va_display(gl);
- if (display)
- return display;
+ {create_wayland_va_display},
#endif
- return display;
+#if HAVE_VAAPI_DRM
+ {create_drm_va_display},
+#endif
+};
+
+static VADisplay *create_native_va_display(GL *gl)
+{
+ if (!gl->MPGetNativeDisplay)
+ return NULL;
+ for (int n = 0; n < MP_ARRAY_SIZE(create_native_cbs); n++) {
+ VADisplay *display = create_native_cbs[n].create(gl);
+ if (display)
+ return display;
+ }
+ return NULL;
}
struct priv {
@@ -189,8 +210,12 @@ static int create(struct gl_hwdec *hw)
if (!eglGetCurrentDisplay())
return -1;
- if (!strstr(gl->extensions, "EXT_image_dma_buf_import") ||
- !strstr(gl->extensions, "EGL_KHR_image_base") ||
+ const char *exts = eglQueryString(eglGetCurrentDisplay(), EGL_EXTENSIONS);
+ if (!exts)
+ return -1;
+
+ if (!strstr(exts, "EXT_image_dma_buf_import") ||
+ !strstr(exts, "EGL_KHR_image_base") ||
!strstr(gl->extensions, "GL_OES_EGL_image") ||
!(gl->mpgl_caps & MPGL_CAP_TEX_RG))
return -1;
@@ -285,12 +310,12 @@ static int map_image(struct gl_hwdec *hw, struct mp_image *hw_image,
int mpfmt = va_fourcc_to_imgfmt(va_image->format.fourcc);
if (mpfmt != IMGFMT_NV12 && mpfmt != IMGFMT_420P) {
MP_FATAL(p, "unsupported VA image format %s\n",
- VA_STR_FOURCC(va_image->format.fourcc));
+ mp_tag_str(va_image->format.fourcc));
goto err;
}
if (!hw->converted_imgfmt) {
- MP_VERBOSE(p, "format: %s %s\n", VA_STR_FOURCC(va_image->format.fourcc),
+ MP_VERBOSE(p, "format: %s %s\n", mp_tag_str(va_image->format.fourcc),
mp_imgfmt_to_name(mpfmt));
hw->converted_imgfmt = mpfmt;
}
@@ -376,7 +401,8 @@ static bool test_format(struct gl_hwdec *hw)
}
const struct gl_hwdec_driver gl_hwdec_vaegl = {
- .api_name = "vaapi",
+ .name = "vaapi-egl",
+ .api = HWDEC_VAAPI,
.imgfmt = IMGFMT_VAAPI,
.create = create,
.reinit = reinit,
diff --git a/video/out/opengl/hwdec_vaglx.c b/video/out/opengl/hwdec_vaglx.c
index ff97d14..77b1f27 100644
--- a/video/out/opengl/hwdec_vaglx.c
+++ b/video/out/opengl/hwdec_vaglx.c
@@ -194,7 +194,8 @@ static int map_image(struct gl_hwdec *hw, struct mp_image *hw_image,
}
const struct gl_hwdec_driver gl_hwdec_vaglx = {
- .api_name = "vaapi",
+ .name = "vaapi-glx",
+ .api = HWDEC_VAAPI,
.imgfmt = IMGFMT_VAAPI,
.create = create,
.reinit = reinit,
diff --git a/video/out/opengl/hwdec_vdpau.c b/video/out/opengl/hwdec_vdpau.c
index 086ebf5..b1d4962 100644
--- a/video/out/opengl/hwdec_vdpau.c
+++ b/video/out/opengl/hwdec_vdpau.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 <stddef.h>
@@ -37,6 +37,7 @@ struct priv {
uint64_t preemption_counter;
struct mp_image_params image_params;
GLuint gl_texture;
+ bool vdpgl_initialized;
GLvdpauSurfaceNV vdpgl_surface;
VdpOutputSurface vdp_surface;
struct mp_vdpau_mixer *mixer;
@@ -78,13 +79,12 @@ static void destroy_objects(struct gl_hwdec *hw)
glCheckError(gl, hw->log, "Before uninitializing OpenGL interop");
- gl->VDPAUFiniNV();
+ if (p->vdpgl_initialized)
+ gl->VDPAUFiniNV();
- // If the GL/vdpau state is not initialized, above calls raises an error.
- while (1) {
- if (gl->GetError() == GL_NO_ERROR)
- break;
- }
+ p->vdpgl_initialized = false;
+
+ glCheckError(gl, hw->log, "After uninitializing OpenGL interop");
}
static void destroy(struct gl_hwdec *hw)
@@ -137,11 +137,13 @@ static int reinit(struct gl_hwdec *hw, struct mp_image_params *params)
assert(params->imgfmt == hw->driver->imgfmt);
p->image_params = *params;
- if (mp_vdpau_handle_preemption(p->ctx, &p->preemption_counter) < 1)
+ if (mp_vdpau_handle_preemption(p->ctx, &p->preemption_counter) < 0)
return -1;
gl->VDPAUInitNV(BRAINDEATH(p->ctx->vdp_device), p->ctx->get_proc_address);
+ p->vdpgl_initialized = true;
+
vdp_st = vdp->output_surface_create(p->ctx->vdp_device,
VDP_RGBA_FORMAT_B8G8R8A8,
params->w, params->h, &p->vdp_surface);
@@ -200,7 +202,8 @@ static int map_image(struct gl_hwdec *hw, struct mp_image *hw_image,
}
const struct gl_hwdec_driver gl_hwdec_vdpau = {
- .api_name = "vdpau",
+ .name = "vdpau-glx",
+ .api = HWDEC_VDPAU,
.imgfmt = IMGFMT_VDPAU,
.create = create,
.reinit = reinit,
diff --git a/video/out/opengl/lcms.c b/video/out/opengl/lcms.c
index 469f2bc..c956127 100644
--- a/video/out/opengl/lcms.c
+++ b/video/out/opengl/lcms.c
@@ -1,28 +1,23 @@
/*
* 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.
- *
- * You should have received a copy of the GNU General Public License along
- * with mpv. If not, see <http://www.gnu.org/licenses/>.
+ * GNU Lesser General Public License for more details.
*
- * You can alternatively redistribute this file 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.
+ * 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 <string.h>
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "config.h"
diff --git a/video/out/opengl/nnedi3.c b/video/out/opengl/nnedi3.c
index bb200b0..c077316 100644
--- a/video/out/opengl/nnedi3.c
+++ b/video/out/opengl/nnedi3.c
@@ -1,23 +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.
- *
- * You should have received a copy of the GNU General Public License along
- * with mpv. If not, see <http://www.gnu.org/licenses/>.
+ * GNU Lesser General Public License for more details.
*
- * You can alternatively redistribute this file 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*
* The shader portions may have been derived from existing LGPLv3 shaders
* (see below), possibly making this file effectively LGPLv3.
@@ -136,23 +131,23 @@ void pass_nnedi3(GL *gl, struct gl_shader_cache *sc, int planes, int tex_num,
GLSLH(#pragma optionNV(fastprecision on))
}
- GLSLHF("float nnedi3(sampler2D tex, vec2 pos, vec2 tex_size, int plane, float tex_mul) {\n");
+ GLSLHF("float nnedi3(sampler2D tex, vec2 pos, vec2 tex_size, vec2 pixel_size, int plane, float tex_mul) {\n");
if (step == 0) {
*transform = (struct gl_transform){{{1.0,0.0}, {0.0,2.0}}, {0.0,-0.5}};
GLSLH(if (fract(pos.y * tex_size.y) < 0.5)
- return texture(tex, pos + vec2(0, 0.25) / tex_size)[plane] * tex_mul;)
+ return texture(tex, pos + vec2(0, 0.25) * pixel_size)[plane] * tex_mul;)
GLSLHF("#define GET(i, j) "
- "(texture(tex, pos+vec2((i)-(%f),(j)-(%f)+0.25)/tex_size)[plane]*tex_mul)\n",
+ "(texture(tex, pos+vec2((i)-(%f),(j)-(%f)+0.25) * pixel_size)[plane]*tex_mul)\n",
width / 2.0 - 1, (height - 1) / 2.0);
} else {
*transform = (struct gl_transform){{{2.0,0.0}, {0.0,1.0}}, {-0.5,0.0}};
GLSLH(if (fract(pos.x * tex_size.x) < 0.5)
- return texture(tex, pos + vec2(0.25, 0) / tex_size)[plane] * tex_mul;)
+ return texture(tex, pos + vec2(0.25, 0) * pixel_size)[plane] * tex_mul;)
GLSLHF("#define GET(i, j) "
- "(texture(tex, pos+vec2((j)-(%f)+0.25,(i)-(%f))/tex_size)[plane]*tex_mul)\n",
+ "(texture(tex, pos+vec2((j)-(%f)+0.25,(i)-(%f)) * pixel_size)[plane]*tex_mul)\n",
(height - 1) / 2.0, width / 2.0 - 1);
}
@@ -231,11 +226,11 @@ void pass_nnedi3(GL *gl, struct gl_shader_cache *sc, int planes, int tex_num,
GLSLHF("}\n"); // nnedi3
- GLSL(vec4 color = vec4(1.0);)
+ GLSL(color = vec4(1.0);)
for (int i = 0; i < planes; i++) {
- GLSLF("color[%d] = nnedi3(texture%d, texcoord%d, texture_size%d, %d, %f);\n",
- i, tex_num, tex_num, tex_num, i, tex_mul);
+ GLSLF("color[%d] = nnedi3(texture%d, texcoord%d, texture_size%d, pixel_size%d, %d, %f);\n",
+ i, tex_num, tex_num, tex_num, tex_num, i, tex_mul);
}
}
diff --git a/video/out/opengl/nnedi3.h b/video/out/opengl/nnedi3.h
index 837383c..b75094a 100644
--- a/video/out/opengl/nnedi3.h
+++ b/video/out/opengl/nnedi3.h
@@ -1,23 +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.
- *
- * You should have received a copy of the GNU General Public License along
- * with mpv. If not, see <http://www.gnu.org/licenses/>.
+ * GNU Lesser General Public License for more details.
*
- * You can alternatively redistribute this file 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.
+ * 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_GL_NNEDI3_H
diff --git a/video/out/opengl/osd.c b/video/out/opengl/osd.c
index 1fe84e0..215c83c 100644
--- a/video/out/opengl/osd.c
+++ b/video/out/opengl/osd.c
@@ -1,23 +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.
- *
- * You should have received a copy of the GNU General Public License along
- * with mpv. If not, see <http://www.gnu.org/licenses/>.
+ * GNU Lesser General Public License for more details.
*
- * You can alternatively redistribute this file 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.
+ * 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>
diff --git a/video/out/opengl/superxbr.c b/video/out/opengl/superxbr.c
index 73fd662..8039e6e 100644
--- a/video/out/opengl/superxbr.c
+++ b/video/out/opengl/superxbr.c
@@ -1,23 +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.
- *
- * You should have received a copy of the GNU General Public License along
- * with mpv. If not, see <http://www.gnu.org/licenses/>.
+ * GNU Lesser General Public License for more details.
*
- * You can alternatively redistribute this file 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.
+ * 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 "superxbr.h"
@@ -99,7 +94,7 @@ void pass_superxbr(struct gl_shader_cache *sc, int planes, int tex_num,
GLSLHF("#define weight1 (%f*1.29633/10.0)\n", conf->sharpness);
GLSLHF("#define weight2 (%f*1.75068/10.0/2.0)\n", conf->sharpness);
- GLSLH(#define Get(x, y) (texture(tex, pos + (vec2(x, y) - vec2(0.25, 0.25)) / tex_size)[plane] * tex_mul))
+ GLSLH(#define Get(x, y) (texture(tex, pos + (vec2(x, y) - vec2(0.25, 0.25)) * pixel_size)[plane] * tex_mul))
} else {
*transform = (struct gl_transform){{{1.0,0.0}, {0.0,1.0}}, {0.0,0.0}};
@@ -113,7 +108,7 @@ void pass_superxbr(struct gl_shader_cache *sc, int planes, int tex_num,
GLSLHF("#define weight1 (%f*1.75068/10.0)\n", conf->sharpness);
GLSLHF("#define weight2 (%f*1.29633/10.0/2.0)\n", conf->sharpness);
- GLSLH(#define Get(x, y) (texture(tex, pos + (vec2((x) + (y) - 1, (y) - (x))) / tex_size)[plane] * tex_mul))
+ GLSLH(#define Get(x, y) (texture(tex, pos + (vec2((x) + (y) - 1, (y) - (x))) * pixel_size)[plane] * tex_mul))
}
GLSLH(float df(float A, float B)
{
@@ -140,7 +135,7 @@ void pass_superxbr(struct gl_shader_cache *sc, int planes, int tex_num,
wp3*(df(i1,e2)+df(i3,e4)+df(e1,i2)+df(e3,i4)));
})
- GLSLHF("float superxbr(sampler2D tex, vec2 pos, vec2 tex_size, int plane, float tex_mul) {\n");
+ GLSLHF("float superxbr(sampler2D tex, vec2 pos, vec2 tex_size, vec2 pixel_size, int plane, float tex_mul) {\n");
if (step == 0) {
GLSLH(vec2 dir = fract(pos * tex_size) - 0.5;)
@@ -152,7 +147,7 @@ void pass_superxbr(struct gl_shader_cache *sc, int planes, int tex_num,
return 0.0;)
GLSLH(if (dir.x < 0.0 || dir.y < 0.0 || dist.x < 1.0 || dist.y < 1.0)
- return texture(tex, pos - dir / tex_size)[plane] * tex_mul;)
+ return texture(tex, pos - dir * pixel_size)[plane] * tex_mul;)
} else {
GLSLH(vec2 dir = fract(pos * tex_size / 2.0) - 0.5;)
GLSLH(if (dir.x * dir.y > 0.0)
@@ -225,10 +220,10 @@ void pass_superxbr(struct gl_shader_cache *sc, int planes, int tex_num,
GLSLHF("}"); // superxbr()
- GLSL(vec4 color = vec4(1.0);)
+ GLSL(color = vec4(1.0);)
for (int i = 0; i < planes; i++) {
- GLSLF("color[%d] = superxbr(texture%d, texcoord%d, texture_size%d, %d, %f);\n",
- i, tex_num, tex_num, tex_num, i, tex_mul);
+ GLSLF("color[%d] = superxbr(texture%d, texcoord%d, texture_size%d, pixel_size%d, %d, %f);\n",
+ i, tex_num, tex_num, tex_num, tex_num, i, tex_mul);
}
}
diff --git a/video/out/opengl/superxbr.h b/video/out/opengl/superxbr.h
index 08cec82..38a10ff 100644
--- a/video/out/opengl/superxbr.h
+++ b/video/out/opengl/superxbr.h
@@ -1,23 +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.
- *
- * You should have received a copy of the GNU General Public License along
- * with mpv. If not, see <http://www.gnu.org/licenses/>.
+ * GNU Lesser General Public License for more details.
*
- * You can alternatively redistribute this file 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.
+ * 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_GL_SUPERXBR_H
diff --git a/video/out/opengl/utils.c b/video/out/opengl/utils.c
index c168cc6..7329240 100644
--- a/video/out/opengl/utils.c
+++ b/video/out/opengl/utils.c
@@ -2,23 +2,18 @@
* This file is part of mpv.
* Parts based on MPlayer code by Reimar Döffinger.
*
- * 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.
- *
- * You should have received a copy of the GNU General Public License along
- * with mpv. If not, see <http://www.gnu.org/licenses/>.
+ * GNU Lesser General Public License for more details.
*
- * You can alternatively redistribute this file 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.
+ * 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>
@@ -675,6 +670,15 @@ void gl_sc_uniform_sampler(struct gl_shader_cache *sc, char *name, GLenum target
u->v.i[0] = unit;
}
+void gl_sc_uniform_sampler_ui(struct gl_shader_cache *sc, char *name, int unit)
+{
+ struct sc_uniform *u = find_uniform(sc, name);
+ u->type = UT_i;
+ u->size = 1;
+ u->glsl_type = sc->gl->es ? "highp usampler2D" : "usampler2D";
+ u->v.i[0] = unit;
+}
+
void gl_sc_uniform_f(struct gl_shader_cache *sc, char *name, GLfloat f)
{
struct sc_uniform *u = find_uniform(sc, name);
@@ -962,8 +966,8 @@ void gl_sc_gen_shader_and_reset(struct gl_shader_cache *sc)
}
// Additional helpers.
- ADD(frag, "#define LUT_POS(x, lut_size) \\\n");
- ADD(frag, " mix(0.5 / (lut_size), 1.0 - 0.5 / (lut_size), (x))\n");
+ ADD(frag, "#define LUT_POS(x, lut_size)"
+ " mix(0.5 / (lut_size), 1.0 - 0.5 / (lut_size), (x))\n");
// custom shader header
if (sc->header_text[0]) {
@@ -972,8 +976,9 @@ void gl_sc_gen_shader_and_reset(struct gl_shader_cache *sc)
ADD(frag, "// body\n");
}
ADD(frag, "void main() {\n");
- ADD(frag, "%s", sc->text);
// we require _all_ frag shaders to write to a "vec4 color"
+ ADD(frag, "vec4 color;\n");
+ ADD(frag, "%s", sc->text);
if (gl->glsl_version >= 130) {
ADD(frag, "out_color = color;\n");
} else {
diff --git a/video/out/opengl/utils.h b/video/out/opengl/utils.h
index 8808157..3ec6077 100644
--- a/video/out/opengl/utils.h
+++ b/video/out/opengl/utils.h
@@ -2,23 +2,18 @@
* This file is part of mpv.
* Parts based on MPlayer code by Reimar Döffinger.
*
- * 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.
- *
- * You should have received a copy of the GNU General Public License along
- * with mpv. If not, see <http://www.gnu.org/licenses/>.
+ * GNU Lesser General Public License for more details.
*
- * You can alternatively redistribute this file 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.
+ * 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_GL_UTILS_
@@ -131,6 +126,7 @@ void gl_sc_hadd(struct gl_shader_cache *sc, const char *text);
void gl_sc_haddf(struct gl_shader_cache *sc, const char *textf, ...);
void gl_sc_uniform_sampler(struct gl_shader_cache *sc, char *name, GLenum target,
int unit);
+void gl_sc_uniform_sampler_ui(struct gl_shader_cache *sc, char *name, int unit);
void gl_sc_uniform_f(struct gl_shader_cache *sc, char *name, GLfloat f);
void gl_sc_uniform_i(struct gl_shader_cache *sc, char *name, GLint f);
void gl_sc_uniform_vec2(struct gl_shader_cache *sc, char *name, GLfloat f[2]);
diff --git a/video/out/opengl/video.c b/video/out/opengl/video.c
index ff4c7a2..c10e16f 100644
--- a/video/out/opengl/video.c
+++ b/video/out/opengl/video.c
@@ -1,23 +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 can alternatively redistribute this file 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.
+ * 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>
@@ -98,6 +93,7 @@ struct texplane {
int w, h;
GLint gl_internal_format;
GLenum gl_target;
+ bool use_integer;
GLenum gl_format;
GLenum gl_type;
GLuint gl_texture;
@@ -120,6 +116,7 @@ struct fbosurface {
struct src_tex {
GLuint gl_tex;
GLenum gl_target;
+ bool use_integer;
int w, h;
struct mp_rect_f src;
};
@@ -137,7 +134,6 @@ struct gl_video {
struct gl_video_opts opts;
bool gl_debug;
- int depth_g;
int texture_16bit_depth; // actual bits available in 16 bit textures
struct gl_shader_cache *sc;
@@ -166,10 +162,12 @@ struct gl_video {
bool is_yuv, is_packed_yuv;
bool has_alpha;
char color_swizzle[5];
+ bool use_integer_conversion;
struct video_image image;
bool dumb_mode;
+ bool forced_dumb_mode;
struct fbotex chroma_merge_fbo;
struct fbotex chroma_deband_fbo;
@@ -179,6 +177,7 @@ struct gl_video {
struct fbotex output_fbo;
struct fbotex deband_fbo;
struct fbosurface surfaces[FBOSURFACES_MAX];
+ struct fbotex integer_conv_fbo[TEXUNIT_VIDEO_NUM];
// these are duplicated so we can keep rendering back and forth between
// them to support an unlimited number of shader passes per step
@@ -226,6 +225,7 @@ struct gl_video {
bool hwdec_active;
bool dsi_warned;
+ bool custom_shader_fn_warned;
};
struct fmt_entry {
@@ -237,9 +237,6 @@ struct fmt_entry {
// Very special formats, for which OpenGL happens to have direct support
static const struct fmt_entry mp_to_gl_formats[] = {
- {IMGFMT_BGR555, GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV},
- {IMGFMT_BGR565, GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5_REV},
- {IMGFMT_RGB555, GL_RGBA, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV},
{IMGFMT_RGB565, GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5},
{0},
};
@@ -268,6 +265,17 @@ static const struct fmt_entry gl_byte_formats_gles3[] = {
{0, 0, 0, 0}, // 4 x 16
};
+static const struct fmt_entry gl_ui_byte_formats_gles3[] = {
+ {0, GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE}, // 1 x 8
+ {0, GL_RG8UI, GL_RG_INTEGER, GL_UNSIGNED_BYTE}, // 2 x 8
+ {0, GL_RGB8UI, GL_RGB_INTEGER, GL_UNSIGNED_BYTE}, // 3 x 8
+ {0, GL_RGBA8UI, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE}, // 4 x 8
+ {0, GL_R16UI, GL_RED_INTEGER, GL_UNSIGNED_SHORT}, // 1 x 16
+ {0, GL_RG16UI, GL_RG_INTEGER, GL_UNSIGNED_SHORT}, // 2 x 16
+ {0, GL_RGB16UI, GL_RGB_INTEGER, GL_UNSIGNED_SHORT}, // 3 x 16
+ {0, GL_RGBA16UI, GL_RGBA_INTEGER, GL_UNSIGNED_SHORT}, // 4 x 16
+};
+
static const struct fmt_entry gl_byte_formats_gles2[] = {
{0, GL_LUMINANCE, GL_LUMINANCE, GL_UNSIGNED_BYTE}, // 1 x 8
{0, GL_LUMINANCE_ALPHA, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE}, // 2 x 8
@@ -345,8 +353,10 @@ const struct gl_video_opts gl_video_opts_def = {
{{"mitchell", .params={NAN, NAN}}, {.params = {NAN, NAN}},
.clamp = 1, }, // tscale
},
+ .scaler_resizes_only = 1,
.scaler_lut_size = 6,
- .alpha_mode = 2,
+ .interpolation_threshold = 0.0001,
+ .alpha_mode = 3,
.background = {0, 0, 0, 255},
.gamma = 1.0f,
.prescale_passes = 1,
@@ -369,12 +379,13 @@ const struct gl_video_opts gl_video_opts_hq_def = {
{{"mitchell", .params={NAN, NAN}}, {.params = {NAN, NAN}},
.clamp = 1, }, // tscale
},
+ .scaler_resizes_only = 1,
.scaler_lut_size = 6,
- .alpha_mode = 2,
+ .interpolation_threshold = 0.0001,
+ .alpha_mode = 3,
.background = {0, 0, 0, 255},
.gamma = 1.0f,
.blend_subs = 0,
- .pbo = 1,
.deband = 1,
.prescale_passes = 1,
.prescale_downscaling_threshold = 2.0f,
@@ -443,10 +454,12 @@ const struct m_sub_options gl_video_conf = {
OPT_CHOICE("alpha", alpha_mode, 0,
({"no", 0},
{"yes", 1},
- {"blend", 2})),
+ {"blend", 2},
+ {"blend-tiles", 3})),
OPT_FLAG("rectangle-textures", use_rectangle, 0),
OPT_COLOR("background", background, 0),
OPT_FLAG("interpolation", interpolation, 0),
+ OPT_FLOAT("interpolation-threshold", interpolation_threshold, 0),
OPT_CHOICE("blend-subtitles", blend_subs, 0,
({"no", 0},
{"yes", 1},
@@ -509,7 +522,9 @@ static void get_scale_factors(struct gl_video *p, double xy[2]);
#define GLSL(x) gl_sc_add(p->sc, #x "\n");
#define GLSLF(...) gl_sc_addf(p->sc, __VA_ARGS__)
+#define GLSLHF(...) gl_sc_haddf(p->sc, __VA_ARGS__)
+// Return a fixed point texture format with given characteristics.
static const struct fmt_entry *find_tex_format(GL *gl, int bytes_per_comp,
int n_channels)
{
@@ -526,6 +541,19 @@ static const struct fmt_entry *find_tex_format(GL *gl, int bytes_per_comp,
return &fmts[n_channels - 1 + (bytes_per_comp - 1) * 4];
}
+static bool is_integer_format(const struct fmt_entry *fmt)
+{
+ // Tests only the formats which we actually declare somewhere.
+ switch (fmt->format) {
+ case GL_RED_INTEGER:
+ case GL_RG_INTEGER:
+ case GL_RGB_INTEGER:
+ case GL_RGBA_INTEGER:
+ return true;
+ }
+ return false;
+}
+
static const char *load_cached_file(struct gl_video *p, const char *path)
{
if (!path || !path[0])
@@ -618,6 +646,7 @@ static void uninit_rendering(struct gl_video *p)
p->dither_texture = 0;
gl->DeleteBuffers(1, &p->nnedi3_weights_buffer);
+ p->nnedi3_weights_buffer = 0;
fbotex_uninit(&p->chroma_merge_fbo);
fbotex_uninit(&p->chroma_deband_fbo);
@@ -626,6 +655,9 @@ static void uninit_rendering(struct gl_video *p)
fbotex_uninit(&p->unsharp_fbo);
fbotex_uninit(&p->deband_fbo);
+ for (int n = 0; n < 4; n++)
+ fbotex_uninit(&p->integer_conv_fbo[n]);
+
for (int n = 0; n < 2; n++) {
fbotex_uninit(&p->pre_fbo[n]);
fbotex_uninit(&p->post_fbo[n]);
@@ -724,8 +756,9 @@ static void pass_set_image_textures(struct gl_video *p, struct video_image *vimg
for (int n = 0; n < p->plane_count; n++) {
struct texplane *t = &vimg->planes[n];
p->pass_tex[n] = (struct src_tex){
- .gl_tex = vimg->planes[n].gl_texture,
+ .gl_tex = t->gl_texture,
.gl_target = t->gl_target,
+ .use_integer = t->use_integer,
.w = t->w,
.h = t->h,
.src = {0, 0, t->w, t->h},
@@ -737,11 +770,11 @@ static void init_video(struct gl_video *p)
{
GL *gl = p->gl;
- check_gl_features(p);
-
init_format(p->image_params.imgfmt, p);
p->gl_target = p->opts.use_rectangle ? GL_TEXTURE_RECTANGLE : GL_TEXTURE_2D;
+ check_gl_features(p);
+
if (p->hwdec_active) {
if (p->hwdec->driver->reinit(p->hwdec, &p->image_params) < 0)
MP_ERR(p, "Initializing texture for hardware decoding failed.\n");
@@ -753,7 +786,7 @@ static void init_video(struct gl_video *p)
mp_image_params_guess_csp(&p->image_params);
int eq_caps = MP_CSP_EQ_CAPS_GAMMA;
- if (p->is_yuv && p->image_params.colorspace != MP_CSP_BT_2020_C)
+ if (p->image_params.colorspace != MP_CSP_BT_2020_C)
eq_caps |= MP_CSP_EQ_CAPS_COLORMATRIX;
if (p->image_desc.flags & MP_IMGFLAG_XYZ)
eq_caps |= MP_CSP_EQ_CAPS_BRIGHTNESS;
@@ -785,8 +818,9 @@ static void init_video(struct gl_video *p)
plane->w, plane->h, 0,
plane->gl_format, plane->gl_type, NULL);
- gl->TexParameteri(p->gl_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- gl->TexParameteri(p->gl_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ int filter = plane->use_integer ? GL_NEAREST : GL_LINEAR;
+ gl->TexParameteri(p->gl_target, GL_TEXTURE_MIN_FILTER, filter);
+ gl->TexParameteri(p->gl_target, GL_TEXTURE_MAG_FILTER, filter);
gl->TexParameteri(p->gl_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
gl->TexParameteri(p->gl_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
@@ -837,16 +871,24 @@ static void pass_prepare_src_tex(struct gl_video *p)
char texture_name[32];
char texture_size[32];
+ char pixel_size[32];
snprintf(texture_name, sizeof(texture_name), "texture%d", n);
snprintf(texture_size, sizeof(texture_size), "texture_size%d", n);
+ snprintf(pixel_size, sizeof(pixel_size), "pixel_size%d", n);
- gl_sc_uniform_sampler(sc, texture_name, s->gl_target, n);
+ if (s->use_integer) {
+ gl_sc_uniform_sampler_ui(sc, texture_name, n);
+ } else {
+ gl_sc_uniform_sampler(sc, texture_name, s->gl_target, n);
+ }
float f[2] = {1, 1};
if (s->gl_target != GL_TEXTURE_RECTANGLE) {
f[0] = s->w;
f[1] = s->h;
}
gl_sc_uniform_vec2(sc, texture_size, f);
+ gl_sc_uniform_vec2(sc, pixel_size, (GLfloat[]){1.0f / f[0],
+ 1.0f / f[1]});
gl->ActiveTexture(GL_TEXTURE0 + n);
gl->BindTexture(s->gl_target, s->gl_tex);
@@ -947,8 +989,21 @@ static void load_shader(struct gl_video *p, const char *body)
gl_sc_hadd(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->texture_w,
- p->texture_h});
+ gl_sc_uniform_vec2(p->sc, "image_size", (GLfloat[]){p->image_params.w,
+ p->image_params.h});
+}
+
+static const char *get_custom_shader_fn(struct gl_video *p, const char *body)
+{
+ if (!p->gl->es && strstr(body, "sample") && !strstr(body, "sample_pixel")) {
+ if (!p->custom_shader_fn_warned) {
+ MP_WARN(p, "sample() is deprecated in custom shaders. "
+ "Use sample_pixel()\n");
+ p->custom_shader_fn_warned = true;
+ }
+ return "sample";
+ }
+ return "sample_pixel";
}
// Applies an arbitrary number of shaders in sequence, using the given pair
@@ -965,10 +1020,12 @@ static bool apply_shaders(struct gl_video *p, char **shaders,
if (!body)
continue;
finish_pass_fbo(p, &textures[tex], w, h, tex_num, 0);
+ GLSLHF("#define pixel_size pixel_size%d\n", tex_num);
load_shader(p, body);
+ const char *fn_name = get_custom_shader_fn(p, body);
GLSLF("// custom shader\n");
- GLSLF("vec4 color = sample(texture%d, texcoord%d, texture_size%d);\n",
- tex_num, tex_num, tex_num);
+ GLSLF("color = %s(texture%d, texcoord%d, texture_size%d);\n",
+ fn_name, tex_num, tex_num, tex_num);
tex = (tex+1) % 2;
success = true;
}
@@ -1135,7 +1192,7 @@ static void pass_sample_separated(struct gl_video *p, int src_tex,
// The src rectangle is implicit in p->pass_tex + transform.
// The dst rectangle is implicit by what the caller will do next, but w and h
// must still be what is going to be used (to dimension FBOs correctly).
-// This will declare "vec4 color;", which contains the scaled contents.
+// This will write the scaled contents to the vec4 "color".
// The scaler unit is initialized by this function; in order to avoid cache
// thrashing, the scaler unit should usually use the same parameters.
static void pass_sample(struct gl_video *p, int src_tex, struct scaler *scaler,
@@ -1152,7 +1209,7 @@ static void pass_sample(struct gl_video *p, int src_tex, struct scaler *scaler,
// Dispatch the scaler. They're all wildly different.
const char *name = scaler->conf.kernel.name;
if (strcmp(name, "bilinear") == 0) {
- GLSL(vec4 color = texture(tex, pos);)
+ GLSL(color = texture(tex, pos);)
} else if (strcmp(name, "bicubic_fast") == 0) {
pass_sample_bicubic_fast(p->sc);
} else if (strcmp(name, "oversample") == 0) {
@@ -1161,8 +1218,9 @@ static void pass_sample(struct gl_video *p, int src_tex, struct scaler *scaler,
const char *body = load_cached_file(p, p->opts.scale_shader);
if (body) {
load_shader(p, body);
+ const char *fn_name = get_custom_shader_fn(p, body);
GLSLF("// custom scale-shader\n");
- GLSL(vec4 color = sample(tex, pos, size);)
+ GLSLF("color = %s(tex, pos, size);\n", fn_name);
} else {
p->opts.scale_shader = NULL;
}
@@ -1296,8 +1354,8 @@ static bool pass_prescale_luma(struct gl_video *p, float tex_mul,
if (p->opts.deband) {
// apply debanding before upscaling.
- pass_sample_deband(p->sc, p->opts.deband_opts, 0, p->gl_target,
- tex_mul, p->texture_w, p->texture_h, &p->lfg);
+ pass_sample_deband(p->sc, p->opts.deband_opts, 0, p->pass_tex[0].gl_target,
+ tex_mul, &p->lfg);
finish_pass_fbo(p, &p->deband_fbo, p->texture_w,
p->texture_h, 0, 0);
tex_backup[0] = p->pass_tex[0];
@@ -1321,15 +1379,63 @@ static bool pass_prescale_luma(struct gl_video *p, float tex_mul,
return true;
}
+// The input textures are in an integer format (non-fixed-point), like R16UI.
+// Convert it to float in an extra pass.
+static void pass_integer_conversion(struct gl_video *p, bool *chroma_merging)
+{
+ double tex_mul = 1 / mp_get_csp_mul(p->image_params.colorspace,
+ p->image_desc.component_bits,
+ p->image_desc.component_full_bits);
+ uint64_t tex_max = 1ull << p->image_desc.component_full_bits;
+ tex_mul *= 1.0 / (tex_max - 1);
+
+ struct src_tex pass_tex[TEXUNIT_VIDEO_NUM];
+ assert(sizeof(pass_tex) == sizeof(p->pass_tex));
+ memcpy(pass_tex, p->pass_tex, sizeof(pass_tex));
+
+ *chroma_merging = p->plane_count == 3;
+
+ for (int n = 0; n < TEXUNIT_VIDEO_NUM; n++) {
+ if (!p->pass_tex[n].gl_tex)
+ continue;
+ if (*chroma_merging && n == 2)
+ continue;
+ GLSLF("// integer conversion plane %d\n", n);
+ GLSLF("uvec4 icolor = texture(texture%d, texcoord%d);\n", n, n);
+ GLSLF("color = vec4(icolor) * tex_mul;\n");
+ if (*chroma_merging && n == 1) {
+ GLSLF("uvec4 icolor2 = texture(texture2, texcoord2);\n");
+ GLSLF("color.g = vec4(icolor2).r * tex_mul;\n");
+ }
+ gl_sc_uniform_f(p->sc, "tex_mul", tex_mul);
+ int c_w = p->pass_tex[n].src.x1 - p->pass_tex[n].src.x0;
+ int c_h = p->pass_tex[n].src.y1 - p->pass_tex[n].src.y0;
+ finish_pass_fbo(p, &p->integer_conv_fbo[n], c_w, c_h, n, 0);
+ pass_tex[n] = p->pass_tex[n];
+ memcpy(p->pass_tex, pass_tex, sizeof(p->pass_tex));
+ }
+
+ p->use_normalized_range = true;
+}
+
// sample from video textures, set "color" variable to yuv value
static void pass_read_video(struct gl_video *p)
{
+ p->use_normalized_range = false;
+
struct gl_transform chromafix;
pass_set_image_textures(p, &p->image, &chromafix);
+ bool chroma_merged = false;
+
+ if (p->use_integer_conversion)
+ pass_integer_conversion(p, &chroma_merged);
+
float tex_mul = 1 / mp_get_csp_mul(p->image_params.colorspace,
p->image_desc.component_bits,
p->image_desc.component_full_bits);
+ if (p->use_normalized_range)
+ tex_mul = 1.0;
struct src_tex prescaled_tex;
struct gl_transform offset = {{{0}}};
@@ -1341,7 +1447,6 @@ static void pass_read_video(struct gl_video *p)
const int scale_factor_x = prescaled ? (int)offset.m[0][0] : 1;
const int scale_factor_y = prescaled ? (int)offset.m[1][1] : 1;
- bool color_defined = false;
if (p->plane_count > 1) {
// Chroma processing (merging -> debanding -> scaling)
struct src_tex luma = p->pass_tex[0];
@@ -1350,15 +1455,14 @@ static void pass_read_video(struct gl_video *p)
int c_h = p->pass_tex[1].src.y1 - p->pass_tex[1].src.y0;
const struct scaler_config *cscale = &p->opts.scaler[2];
- bool merged = false;
- if (p->plane_count > 2) {
+ if (p->plane_count > 2 && !chroma_merged) {
// For simplicity and performance, we merge the chroma planes
// into a single texture before scaling or debanding, so the shader
// doesn't need to run multiple times.
GLSLF("// chroma merging\n");
- GLSL(vec4 color = vec4(texture(texture1, texcoord1).x,
- texture(texture2, texcoord2).x,
- 0.0, 1.0);)
+ GLSL(color = vec4(texture(texture1, texcoord1).x,
+ texture(texture2, texcoord2).x,
+ 0.0, 1.0);)
// We also pull up to the full dynamic range of the texture to avoid
// heavy clipping when using low-bit-depth FBOs
GLSLF("color.xy *= %f;\n", tex_mul);
@@ -1366,13 +1470,11 @@ static void pass_read_video(struct gl_video *p)
assert(c_h == p->pass_tex[2].src.y1 - p->pass_tex[2].src.y0);
finish_pass_fbo(p, &p->chroma_merge_fbo, c_w, c_h, 1, 0);
p->use_normalized_range = true;
- merged = true;
}
if (p->opts.deband) {
- pass_sample_deband(p->sc, p->opts.deband_opts, 1, p->gl_target,
- merged ? 1.0 : tex_mul,
- p->texture_w, p->texture_h, &p->lfg);
+ pass_sample_deband(p->sc, p->opts.deband_opts, 1, p->pass_tex[1].gl_target,
+ p->use_normalized_range ? 1.0 : tex_mul, &p->lfg);
GLSL(color.zw = vec2(0.0, 1.0);) // skip unused
finish_pass_fbo(p, &p->chroma_deband_fbo, c_w, c_h, 1, 0);
p->use_normalized_range = true;
@@ -1385,7 +1487,6 @@ static void pass_read_video(struct gl_video *p)
p->texture_w * scale_factor_x,
p->texture_h * scale_factor_y, chromafix);
GLSL(vec2 chroma = color.xy;)
- color_defined = true; // pass_sample defines vec4 color
} else {
GLSL(vec2 chroma = texture(texture1, texcoord1).xy;)
}
@@ -1394,40 +1495,26 @@ static void pass_read_video(struct gl_video *p)
p->pass_tex[3] = alpha;
}
- // As an unfortunate side-effect of re-using the vec4 color constant in
- // both the luma and chroma stages, vec4 color may or may not be defined
- // at this point. If it's missing, define it since the code from here on
- // relies on it.
- if (!color_defined)
- GLSL(vec4 color;)
-
- // Sample the main (luma/RGB) plane. This is inside a sub-block to avoid
- // colliding with the vec4 color that may be left over from the chroma
- // stuff
- GLSL(vec4 main;)
- GLSLF("{\n");
+ // Sample the main (luma/RGB) plane.
if (!prescaled && p->opts.deband) {
- pass_sample_deband(p->sc, p->opts.deband_opts, 0, p->gl_target, tex_mul,
- p->texture_w, p->texture_h, &p->lfg);
+ pass_sample_deband(p->sc, p->opts.deband_opts, 0, p->pass_tex[0].gl_target,
+ tex_mul, &p->lfg);
p->use_normalized_range = true;
} else {
if (!prescaled) {
- GLSL(vec4 color = texture(texture0, texcoord0);)
+ GLSL(color = texture(texture0, texcoord0);)
} else {
// just use bilinear for non-essential planes.
- GLSLF("vec4 color = texture(texture0, "
- "texcoord0 + vec2(%f,%f) / texture_size0);\n",
+ GLSLF("color = texture(texture0, "
+ "texcoord0 + vec2(%f,%f) * pixel_size0);\n",
-offset.t[0] / scale_factor_x,
-offset.t[1] / scale_factor_y);
}
if (p->use_normalized_range)
GLSLF("color *= %f;\n", tex_mul);
}
- GLSL(main = color;)
- GLSLF("}\n");
// Set up the right combination of planes
- GLSL(color = main;)
if (prescaled) {
// Restore texture4 and merge it into the main texture.
p->pass_tex[4] = prescaled_tex;
@@ -1447,7 +1534,7 @@ static void pass_read_video(struct gl_video *p)
GLSL(color.a = texture(texture3, texcoord3).r;)
} else {
GLSLF("color.a = texture(texture3, "
- "texcoord3 + vec2(%f,%f) / texture_size3).r;",
+ "texcoord3 + vec2(%f,%f) * pixel_size3).r;",
-offset.t[0] / scale_factor_x,
-offset.t[1] / scale_factor_y);
}
@@ -1492,6 +1579,14 @@ static void pass_convert_yuv(struct gl_video *p)
GLSL(color.rgb = mat3(colormatrix) * color.rgb + colormatrix_c;)
+ if (!p->use_normalized_range && p->has_alpha) {
+ float tex_mul = 1 / mp_get_csp_mul(p->image_params.colorspace,
+ p->image_desc.component_bits,
+ p->image_desc.component_full_bits);
+ gl_sc_uniform_f(p->sc, "tex_mul_alpha", tex_mul);
+ GLSL(color.a *= tex_mul_alpha;)
+ }
+
if (p->image_params.colorspace == MP_CSP_BT_2020_C) {
// Conversion for C'rcY'cC'bc via the BT.2020 CL system:
// C'bc = (B'-Y'c) / 1.9404 | C'bc <= 0
@@ -1526,8 +1621,14 @@ static void pass_convert_yuv(struct gl_video *p)
if (!p->has_alpha || p->opts.alpha_mode == 0) { // none
GLSL(color.a = 1.0;)
- } else if (p->opts.alpha_mode == 2) { // blend
+ } else if (p->opts.alpha_mode == 2) { // blend against black
GLSL(color = vec4(color.rgb * color.a, 1.0);)
+ } else if (p->opts.alpha_mode == 3) { // blend against tiles
+ GLSL(bvec2 tile = lessThan(fract(gl_FragCoord.xy / 32.0), vec2(0.5));)
+ GLSL(vec3 background = vec3(tile.x == tile.y ? 1.0 : 0.75);)
+ GLSL(color.rgb = color.rgb * color.a + background * (1.0 - color.a);)
+ } else if (p->gl->fb_premultiplied) {
+ GLSL(color = vec4(color.rgb * color.a, color.a);)
}
}
@@ -1703,7 +1804,7 @@ static void pass_dither(struct gl_video *p)
GL *gl = p->gl;
// Assume 8 bits per component if unknown.
- int dst_depth = p->depth_g ? p->depth_g : 8;
+ int dst_depth = gl->fb_g ? gl->fb_g : 8;
if (p->opts.dither_depth > 0)
dst_depth = p->opts.dither_depth;
@@ -1820,12 +1921,12 @@ static void pass_draw_osd(struct gl_video *p, int draw_flags, double pts,
switch (fmt) {
case SUBBITMAP_RGBA: {
GLSLF("// OSD (RGBA)\n");
- GLSL(vec4 color = texture(osdtex, texcoord).bgra;)
+ GLSL(color = texture(osdtex, texcoord).bgra;)
break;
}
case SUBBITMAP_LIBASS: {
GLSLF("// OSD (libass)\n");
- GLSL(vec4 color =
+ GLSL(color =
vec4(ass_color.rgb, ass_color.a * texture(osdtex, texcoord).r);)
break;
}
@@ -1865,10 +1966,8 @@ static void pass_render_frame_dumb(struct gl_video *p, int fbo)
}
gl_transform_rect(transform, &p->pass_tex[3].src);
- GLSL(vec4 color = texture(texture0, texcoord0);)
- if (p->image_params.imgfmt == IMGFMT_NV12 ||
- p->image_params.imgfmt == IMGFMT_NV21)
- {
+ GLSL(color = texture(texture0, texcoord0);)
+ if (p->image_desc.flags & MP_IMGFLAG_YUV_NV) {
GLSL(color.gb = texture(texture1, texcoord1).RG;)
} else if (p->plane_count >= 3) {
GLSL(color.g = texture(texture1, texcoord1).r;)
@@ -1913,7 +2012,7 @@ static void pass_render_frame(struct gl_video *p)
p->texture_w, p->texture_h, 0, 0);
pass_draw_osd(p, OSD_DRAW_SUB_ONLY, vpts, rect,
p->texture_w, p->texture_h, p->blend_subs_fbo.fbo, false);
- GLSL(vec4 color = texture(texture0, texcoord0);)
+ GLSL(color = texture(texture0, texcoord0);)
}
apply_shaders(p, p->opts.pre_shaders, &p->pre_fbo[0], 0,
@@ -1948,7 +2047,7 @@ static void pass_render_frame(struct gl_video *p)
FBOTEX_FUZZY);
pass_draw_osd(p, OSD_DRAW_SUB_ONLY, vpts, rect,
p->texture_w, p->texture_h, p->blend_subs_fbo.fbo, false);
- GLSL(vec4 color = texture(texture0, texcoord0);)
+ GLSL(color = texture(texture0, texcoord0);)
if (p->use_linear)
pass_linearize(p->sc, p->image_params.gamma);
}
@@ -2086,7 +2185,7 @@ static void gl_video_interpolate_frame(struct gl_video *p, struct vo_frame *t,
if (!valid || t->still) {
// surface_now is guaranteed to be valid, so we can safely use it.
pass_load_fbotex(p, &p->surfaces[surface_now].fbotex, vp_w, vp_h, 0);
- GLSL(vec4 color = texture(texture0, texcoord0);)
+ GLSL(color = texture(texture0, texcoord0);)
p->is_interpolated = false;
} else {
double mix = t->vsync_offset / t->ideal_frame_duration;
@@ -2116,9 +2215,9 @@ static void gl_video_interpolate_frame(struct gl_video *p, struct vo_frame *t,
mix = mix >= 1 - threshold ? 1 : mix;
mix = 1 - mix;
gl_sc_uniform_f(p->sc, "inter_coeff", mix);
- GLSL(vec4 color = mix(texture(texture0, texcoord0),
- texture(texture1, texcoord1),
- inter_coeff);)
+ GLSL(color = mix(texture(texture0, texcoord0),
+ texture(texture1, texcoord1),
+ inter_coeff);)
} else {
gl_sc_uniform_f(p->sc, "fcoord", mix);
pass_sample_separated_gen(p->sc, tscale, 0, 0);
@@ -2160,9 +2259,15 @@ void gl_video_render_frame(struct gl_video *p, struct vo_frame *frame, int fbo)
if (has_frame) {
gl_sc_set_vao(p->sc, &p->vao);
- if (p->opts.interpolation && frame->display_synced &&
- (p->frames_drawn || !frame->still))
- {
+ bool interpolate = p->opts.interpolation && frame->display_synced &&
+ (p->frames_drawn || !frame->still);
+ if (interpolate) {
+ double ratio = frame->ideal_frame_duration / frame->vsync_interval;
+ if (fabs(ratio - 1.0) < p->opts.interpolation_threshold)
+ interpolate = false;
+ }
+
+ if (interpolate) {
gl_video_interpolate_frame(p, frame, fbo);
} else {
bool is_new = !frame->redraw && !frame->repeat;
@@ -2330,8 +2435,8 @@ static void gl_video_upload_image(struct gl_video *p, struct mp_image *mpi)
if (pbo)
gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, plane->gl_buffer);
gl->ActiveTexture(GL_TEXTURE0 + n);
- gl->BindTexture(p->gl_target, plane->gl_texture);
- glUploadTex(gl, p->gl_target, plane->gl_format, plane->gl_type,
+ gl->BindTexture(plane->gl_target, plane->gl_texture);
+ glUploadTex(gl, plane->gl_target, plane->gl_format, plane->gl_type,
mpi->planes[n], mpi->stride[n], 0, 0, plane->w, plane->h, 0);
}
gl->ActiveTexture(GL_TEXTURE0);
@@ -2361,6 +2466,8 @@ static bool test_fbo(struct gl_video *p)
static bool check_dumb_mode(struct gl_video *p)
{
struct gl_video_opts *o = &p->opts;
+ if (p->use_integer_conversion)
+ return false;
if (o->dumb_mode)
return true;
if (o->target_prim || o->target_trc || o->linear_scaling ||
@@ -2393,8 +2500,11 @@ static void check_gl_features(struct gl_video *p)
bool have_texrg = gl->mpgl_caps & MPGL_CAP_TEX_RG;
if (have_fbo) {
- if (!p->opts.fbo_format)
- p->opts.fbo_format = gl->es ? GL_RGB10_A2 : GL_RGBA16;
+ if (!p->opts.fbo_format) {
+ p->opts.fbo_format = GL_RGBA16;
+ if (gl->es)
+ p->opts.fbo_format = have_float_tex ? GL_RGBA16F : GL_RGB10_A2;
+ }
have_fbo = test_fbo(p);
}
@@ -2403,8 +2513,9 @@ static void check_gl_features(struct gl_video *p)
MP_WARN(p, "Disabling PBOs (GLES unsupported).\n");
}
+ p->forced_dumb_mode = p->opts.dumb_mode || !have_fbo || !have_texrg;
bool voluntarily_dumb = check_dumb_mode(p);
- if (p->opts.dumb_mode || !have_fbo || !have_texrg || voluntarily_dumb) {
+ if (p->forced_dumb_mode || voluntarily_dumb) {
if (voluntarily_dumb) {
MP_VERBOSE(p, "No advanced processing required. Enabling dumb mode.\n");
} else if (!p->opts.dumb_mode) {
@@ -2508,6 +2619,9 @@ static void init_gl(struct gl_video *p)
debug_check_gl(p, "before init_gl");
+ MP_VERBOSE(p, "Reported display depth: R=%d, G=%d, B=%d\n",
+ gl->fb_r, gl->fb_g, gl->fb_b);
+
gl->Disable(GL_DITHER);
gl_vao_init(&p->vao, gl, sizeof(struct vertex), vertex_vao);
@@ -2604,6 +2718,17 @@ static void packed_fmt_swizzle(char w[5], const struct fmt_entry *texfmt,
w[4] = '\0';
}
+// Like find_tex_format(), but takes bits (not bytes), and but if no fixed point
+// format is available, return an unsigned integer format.
+static const struct fmt_entry *find_plane_format(GL *gl, int bytes_per_comp,
+ int n_channels)
+{
+ const struct fmt_entry *e = find_tex_format(gl, bytes_per_comp, n_channels);
+ if (e->format || gl->es < 300)
+ return e;
+ return &gl_ui_byte_formats_gles3[n_channels - 1 + (bytes_per_comp - 1) * 4];
+}
+
static bool init_format(int fmt, struct gl_video *init)
{
struct GL *gl = init->gl;
@@ -2631,7 +2756,7 @@ static bool init_format(int fmt, struct gl_video *init)
int bits = desc.component_bits;
if ((desc.flags & MP_IMGFLAG_NE) && bits >= 8 && bits <= 16) {
init->has_alpha = desc.num_planes > 3;
- plane_format[0] = find_tex_format(gl, (bits + 7) / 8, 1);
+ plane_format[0] = find_plane_format(gl, (bits + 7) / 8, 1);
for (int p = 1; p < desc.num_planes; p++)
plane_format[p] = plane_format[0];
// RGB/planar
@@ -2642,12 +2767,15 @@ static bool init_format(int fmt, struct gl_video *init)
}
// YUV/half-packed
- if (fmt == IMGFMT_NV12 || fmt == IMGFMT_NV21) {
- plane_format[0] = find_tex_format(gl, 1, 1);
- plane_format[1] = find_tex_format(gl, 1, 2);
- if (fmt == IMGFMT_NV21)
- snprintf(init->color_swizzle, sizeof(init->color_swizzle), "rbga");
- goto supported;
+ 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 + 7) / 8, 1);
+ plane_format[1] = find_plane_format(gl, (bits + 7) / 8, 2);
+ if (desc.flags & MP_IMGFLAG_YUV_NV_SWAP)
+ snprintf(init->color_swizzle, sizeof(init->color_swizzle), "rbga");
+ goto supported;
+ }
}
// XYZ (same organization as RGB packed, but requires conversion matrix)
@@ -2693,19 +2821,25 @@ static bool init_format(int fmt, struct gl_video *init)
supported:
- // Stuff like IMGFMT_420AP10. Untested, most likely insane.
- if (desc.num_planes == 4 && (desc.component_bits % 8) != 0)
- return false;
-
if (desc.component_bits > 8 && desc.component_bits < 16) {
if (init->texture_16bit_depth < 16)
return false;
}
+ int use_integer = -1;
for (int p = 0; p < desc.num_planes; p++) {
if (!plane_format[p]->format)
return false;
+ int use_int_plane = !!is_integer_format(plane_format[p]);
+ if (use_integer < 0)
+ use_integer = use_int_plane;
+ if (use_integer != use_int_plane)
+ return false; // mixed planes not supported
}
+ init->use_integer_conversion = use_integer;
+
+ if (init->use_integer_conversion && init->forced_dumb_mode)
+ return false;
for (int p = 0; p < desc.num_planes; p++) {
struct texplane *plane = &init->image.planes[p];
@@ -2714,6 +2848,7 @@ supported:
plane->gl_format = format->format;
plane->gl_internal_format = format->internal_format;
plane->gl_type = format->type;
+ plane->use_integer = init->use_integer_conversion;
}
init->is_yuv = desc.flags & MP_IMGFLAG_YUV;
@@ -2744,12 +2879,6 @@ void gl_video_config(struct gl_video *p, struct mp_image_params *params)
gl_video_reset_surfaces(p);
}
-void gl_video_set_output_depth(struct gl_video *p, int r, int g, int b)
-{
- MP_VERBOSE(p, "Display depth: R=%d, G=%d, B=%d\n", r, g, b);
- p->depth_g = g;
-}
-
void gl_video_set_osd_source(struct gl_video *p, struct osd_state *osd)
{
mpgl_osd_destroy(p->osd);
diff --git a/video/out/opengl/video.h b/video/out/opengl/video.h
index 4dab617..a1918be 100644
--- a/video/out/opengl/video.h
+++ b/video/out/opengl/video.h
@@ -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/>.
*/
+
#ifndef MP_GL_VIDEO_H
#define MP_GL_VIDEO_H
@@ -93,6 +94,7 @@ struct gl_video_opts {
int use_rectangle;
struct m_color background;
int interpolation;
+ float interpolation_threshold;
int blend_subs;
char *scale_shader;
char **pre_shaders;
diff --git a/video/out/opengl/video_shaders.c b/video/out/opengl/video_shaders.c
index 04e62d9..cd83ef5 100644
--- a/video/out/opengl/video_shaders.c
+++ b/video/out/opengl/video_shaders.c
@@ -1,23 +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 can alternatively redistribute this file 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.
+ * 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 <math.h>
@@ -37,7 +32,7 @@ void sampler_prelude(struct gl_shader_cache *sc, int tex_num)
GLSLF("#define tex texture%d\n", tex_num);
GLSLF("vec2 pos = texcoord%d;\n", tex_num);
GLSLF("vec2 size = texture_size%d;\n", tex_num);
- GLSLF("vec2 pt = vec2(1.0) / size;\n");
+ GLSLF("vec2 pt = pixel_size%d;\n", tex_num);
}
static void pass_sample_separated_get_weights(struct gl_shader_cache *sc,
@@ -78,7 +73,7 @@ void pass_sample_separated_gen(struct gl_shader_cache *sc, struct scaler *scaler
int N = scaler->kernel->size;
bool use_ar = scaler->conf.antiring > 0;
bool planar = d_x == 0 && d_y == 0;
- GLSL(vec4 color = vec4(0.0);)
+ GLSL(color = vec4(0.0);)
GLSLF("{\n");
if (!planar) {
GLSLF("vec2 dir = vec2(%d.0, %d.0);\n", d_x, d_y);
@@ -116,7 +111,7 @@ void pass_sample_polar(struct gl_shader_cache *sc, struct scaler *scaler)
double radius = scaler->kernel->f.radius;
int bound = (int)ceil(radius);
bool use_ar = scaler->conf.antiring > 0;
- GLSL(vec4 color = vec4(0.0);)
+ GLSL(color = vec4(0.0);)
GLSLF("{\n");
GLSL(vec2 fcoord = fract(pos * size - vec2(0.5));)
GLSL(vec2 base = pos - fcoord * pt;)
@@ -187,7 +182,6 @@ static void bicubic_calcweights(struct gl_shader_cache *sc, const char *t, const
void pass_sample_bicubic_fast(struct gl_shader_cache *sc)
{
- GLSL(vec4 color;)
GLSLF("{\n");
GLSL(vec2 fcoord = fract(pos * size + vec2(0.5, 0.5));)
bicubic_calcweights(sc, "parmx", "fcoord.x");
@@ -211,7 +205,6 @@ void pass_sample_bicubic_fast(struct gl_shader_cache *sc)
void pass_sample_oversample(struct gl_shader_cache *sc, struct scaler *scaler,
int w, int h)
{
- GLSL(vec4 color;)
GLSLF("{\n");
GLSL(vec2 pos = pos + vec2(0.5) * pt;) // round to nearest
GLSL(vec2 fcoord = fract(pos * size - vec2(0.5));)
@@ -355,23 +348,23 @@ const struct m_sub_options deband_conf = {
// Stochastically sample a debanded result from a given texture
void pass_sample_deband(struct gl_shader_cache *sc, struct deband_opts *opts,
- int tex_num, GLenum tex_target, float tex_mul,
- float img_w, float img_h, AVLFG *lfg)
+ int tex_num, GLenum tex_target, float tex_mul, AVLFG *lfg)
{
// Set up common variables and initialize the PRNG
GLSLF("// debanding (tex %d)\n", tex_num);
+ GLSLF("{\n");
sampler_prelude(sc, tex_num);
prng_init(sc, lfg);
// Helper: Compute a stochastic approximation of the avg color around a
// pixel
- GLSLHF("vec4 average(%s tex, vec2 pos, float range, inout float h) {",
+ GLSLHF("vec4 average(%s tex, vec2 pos, vec2 pt, float range, inout float h) {",
mp_sampler_type(tex_target));
// Compute a random rangle and distance
GLSLH(float dist = rand(h) * range; h = permute(h);)
GLSLH(float dir = rand(h) * 6.2831853; h = permute(h);)
- GLSLHF("vec2 pt = dist / vec2(%f, %f);\n", img_w, img_h);
+ GLSLHF("pt *= dist;\n");
GLSLH(vec2 o = vec2(cos(dir), sin(dir));)
// Sample at quarter-turn intervals around the source pixel
@@ -386,12 +379,12 @@ void pass_sample_deband(struct gl_shader_cache *sc, struct deband_opts *opts,
GLSLH(})
// Sample the source pixel
- GLSLF("vec4 color = %f * texture(tex, pos);\n", tex_mul);
+ GLSLF("color = %f * texture(tex, pos);\n", tex_mul);
GLSLF("vec4 avg, diff;\n");
for (int i = 1; i <= opts->iterations; i++) {
// Sample the average pixel and use it instead of the original if
// the difference is below the given threshold
- GLSLF("avg = average(tex, pos, %f, h);\n", i * opts->range);
+ GLSLF("avg = average(tex, pos, pt, %f, h);\n", i * opts->range);
GLSL(diff = abs(color - avg);)
GLSLF("color = mix(avg, color, greaterThan(diff, vec4(%f)));\n",
opts->threshold / (i * 16384.0));
@@ -403,6 +396,7 @@ void pass_sample_deband(struct gl_shader_cache *sc, struct deband_opts *opts,
GLSL(noise.y = rand(h); h = permute(h);)
GLSL(noise.z = rand(h); h = permute(h);)
GLSLF("color.xyz += %f * (noise - vec3(0.5));\n", opts->grain/8192.0);
+ GLSLF("}\n");
}
void pass_sample_unsharp(struct gl_shader_cache *sc, float param)
@@ -410,7 +404,6 @@ void pass_sample_unsharp(struct gl_shader_cache *sc, float param)
GLSLF("// unsharp\n");
sampler_prelude(sc, 0);
- GLSL(vec4 color;)
GLSLF("{\n");
GLSL(vec2 st1 = pt * 1.2;)
GLSL(vec4 p = texture(tex, pos);)
diff --git a/video/out/opengl/video_shaders.h b/video/out/opengl/video_shaders.h
index e5f031c..27a0216 100644
--- a/video/out/opengl/video_shaders.h
+++ b/video/out/opengl/video_shaders.h
@@ -1,23 +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.
- *
- * You should have received a copy of the GNU General Public License along
- * with mpv. If not, see <http://www.gnu.org/licenses/>.
+ * GNU Lesser General Public License for more details.
*
- * You can alternatively redistribute this file 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.
+ * 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_GL_VIDEO_SHADERS_H
@@ -44,8 +39,7 @@ void pass_linearize(struct gl_shader_cache *sc, enum mp_csp_trc trc);
void pass_delinearize(struct gl_shader_cache *sc, enum mp_csp_trc trc);
void pass_sample_deband(struct gl_shader_cache *sc, struct deband_opts *opts,
- int tex_num, GLenum tex_target, float tex_mul,
- float img_w, float img_h, AVLFG *lfg);
+ int tex_num, GLenum tex_target, float tex_mul, AVLFG *lfg);
void pass_sample_unsharp(struct gl_shader_cache *sc, float param);
diff --git a/video/out/vo.c b/video/out/vo.c
index 6318ac7..5c56f62 100644
--- a/video/out/vo.c
+++ b/video/out/vo.c
@@ -28,7 +28,7 @@
#include <poll.h>
#endif
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "config.h"
#include "osdep/timer.h"
@@ -508,8 +508,7 @@ static void run_reconfig(void *p)
struct vo_internal *in = vo->in;
- vo->dwidth = params->d_w;
- vo->dheight = params->d_h;
+ mp_image_params_get_dsize(params, &vo->dwidth, &vo->dheight);
talloc_free(vo->params);
vo->params = talloc_memdup(vo, params, sizeof(*params));
diff --git a/video/out/vo_direct3d.c b/video/out/vo_direct3d.c
index f9a5cb0..e074572 100644
--- a/video/out/vo_direct3d.c
+++ b/video/out/vo_direct3d.c
@@ -29,7 +29,7 @@
#include "config.h"
#include "options/options.h"
#include "options/m_option.h"
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "vo.h"
#include "video/csputils.h"
#include "video/mp_image.h"
@@ -222,8 +222,6 @@ static const struct fmt_entry fmt_table[] = {
{IMGFMT_RGB32, D3DFMT_X8B8G8R8},
{IMGFMT_BGR24, D3DFMT_R8G8B8}, //untested
{IMGFMT_RGB565, D3DFMT_R5G6B5},
- {IMGFMT_RGB555, D3DFMT_X1R5G5B5},
- {IMGFMT_RGB8, D3DFMT_R3G3B2}, //untested
// grayscale (can be considered both packed and planar)
{IMGFMT_Y8, D3DFMT_L8},
{IMGFMT_Y16, D3DFMT_L16},
diff --git a/video/out/vo_drm.c b/video/out/vo_drm.c
index f671acf..5a7c613 100644
--- a/video/out/vo_drm.c
+++ b/video/out/vo_drm.c
@@ -1,23 +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.
- *
- * You should have received a copy of the GNU General Public License along
- * with mpv. If not, see <http://www.gnu.org/licenses/>.
+ * GNU Lesser General Public License for more details.
*
- * You can alternatively redistribute this file 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.
+ * 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>
@@ -305,8 +300,8 @@ static int reconfig(struct vo *vo, struct mp_image_params *params)
.imgfmt = IMGFMT,
.w = w,
.h = h,
- .d_w = w,
- .d_h = h,
+ .p_w = 1,
+ .p_h = 1,
};
talloc_free(p->cur_frame);
diff --git a/video/out/vo_image.c b/video/out/vo_image.c
index e51d90f..a848c69 100644
--- a/video/out/vo_image.c
+++ b/video/out/vo_image.c
@@ -28,7 +28,7 @@
#include "misc/bstr.h"
#include "osdep/io.h"
#include "options/path.h"
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "common/common.h"
#include "common/msg.h"
#include "video/out/vo.h"
diff --git a/video/out/vo_lavc.c b/video/out/vo_lavc.c
index 09e8af7..bd07d10 100644
--- a/video/out/vo_lavc.c
+++ b/video/out/vo_lavc.c
@@ -26,7 +26,7 @@
#include "options/options.h"
#include "video/fmt-conversion.h"
#include "video/mp_image.h"
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "vo.h"
#include "common/encode_lavc.h"
@@ -93,8 +93,7 @@ static int reconfig(struct vo *vo, struct mp_image_params *params)
{
struct priv *vc = vo->priv;
enum AVPixelFormat pix_fmt = imgfmt2pixfmt(params->imgfmt);
- AVRational display_aspect_ratio, image_aspect_ratio;
- AVRational aspect;
+ AVRational aspect = {params->p_w, params->p_h};
uint32_t width = params->w;
uint32_t height = params->h;
@@ -103,12 +102,6 @@ static int reconfig(struct vo *vo, struct mp_image_params *params)
pthread_mutex_lock(&vo->encode_lavc_ctx->lock);
- display_aspect_ratio.num = params->d_w;
- display_aspect_ratio.den = params->d_h;
- image_aspect_ratio.num = width;
- image_aspect_ratio.den = height;
- aspect = av_div_q(display_aspect_ratio, image_aspect_ratio);
-
if (vc->stream) {
/* NOTE:
* in debug builds we get a "comparison between signed and unsigned"
diff --git a/video/out/vo_opengl.c b/video/out/vo_opengl.c
index ef6fe53..7f4f13f 100644
--- a/video/out/vo_opengl.c
+++ b/video/out/vo_opengl.c
@@ -3,23 +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.
- *
- * You should have received a copy of the GNU General Public License along
- * with mpv. If not, see <http://www.gnu.org/licenses/>.
+ * GNU Lesser General Public License for more details.
*
- * You can alternatively redistribute this file 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.
+ * 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>
@@ -33,7 +28,7 @@
#include "config.h"
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "common/common.h"
#include "misc/bstr.h"
#include "common/msg.h"
@@ -43,7 +38,7 @@
#include "video/mp_image.h"
#include "sub/osd.h"
-#include "opengl/common.h"
+#include "opengl/context.h"
#include "opengl/utils.h"
#include "opengl/hwdec.h"
#include "opengl/osd.h"
@@ -329,6 +324,8 @@ static int control(struct vo *vo, uint32_t request, void *data)
if (screen) {
screen->params.primaries = p->renderer_opts->target_prim;
screen->params.gamma = p->renderer_opts->target_trc;
+ if (p->glctx->flip_v)
+ mp_image_vflip(screen);
}
*(struct mp_image **)data = screen;
return true;
@@ -423,8 +420,6 @@ static int preinit(struct vo *vo)
if (!p->renderer)
goto err_out;
gl_video_set_osd_source(p->renderer, vo->osd);
- gl_video_set_output_depth(p->renderer, p->glctx->depth_r, p->glctx->depth_g,
- p->glctx->depth_b);
gl_video_set_options(p->renderer, p->renderer_opts);
gl_video_configure_queue(p->renderer, vo);
diff --git a/video/out/vo_opengl_cb.c b/video/out/vo_opengl_cb.c
index 2a8dfe2..7accfc1 100644
--- a/video/out/vo_opengl_cb.c
+++ b/video/out/vo_opengl_cb.c
@@ -9,7 +9,7 @@
#include "config.h"
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "common/common.h"
#include "misc/bstr.h"
#include "common/msg.h"
@@ -129,8 +129,6 @@ struct mpv_opengl_cb_context *mp_opengl_create(struct mpv_global *g,
pthread_mutex_init(&ctx->lock, NULL);
pthread_cond_init(&ctx->wakeup, NULL);
- ctx->gl = talloc_zero(ctx, GL);
-
ctx->global = g;
ctx->log = mp_log_new(ctx, g->log, "opengl-cb");
ctx->client_api = client_api;
@@ -174,6 +172,8 @@ int mpv_opengl_cb_init_gl(struct mpv_opengl_cb_context *ctx, const char *exts,
if (ctx->renderer)
return MPV_ERROR_INVALID_PARAMETER;
+ ctx->gl = talloc_zero(ctx, GL);
+
mpgl_load_functions2(ctx->gl, get_proc_address, get_proc_address_ctx,
exts, ctx->log);
ctx->renderer = gl_video_init(ctx->gl, ctx->log, ctx->global);
@@ -355,7 +355,7 @@ static void draw_frame(struct vo *vo, struct vo_frame *frame)
assert(!p->ctx->next_frame);
p->ctx->next_frame = vo_frame_ref(frame);
p->ctx->expected_flip_count = p->ctx->flip_count + 1;
- p->ctx->redrawing = frame ? frame->redraw : false;
+ p->ctx->redrawing = frame->redraw || !frame->current;
update(p);
pthread_mutex_unlock(&p->ctx->lock);
}
diff --git a/video/out/vo_rpi.c b/video/out/vo_rpi.c
index cf051cf..9d782fc 100644
--- a/video/out/vo_rpi.c
+++ b/video/out/vo_rpi.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 <stdio.h>
@@ -36,11 +36,12 @@
#include "common/msg.h"
#include "options/m_config.h"
#include "vo.h"
+#include "win_state.h"
#include "video/mp_image.h"
#include "sub/osd.h"
#include "opengl/osd.h"
-#include "opengl/rpi.h"
+#include "opengl/context_rpi.h"
struct priv {
DISPMANX_DISPLAY_HANDLE_T display;
@@ -48,6 +49,7 @@ struct priv {
DISPMANX_ELEMENT_HANDLE_T osd_overlay;
DISPMANX_UPDATE_HANDLE_T update;
uint32_t w, h;
+ uint32_t x, y;
double display_fps;
double osd_pts;
@@ -142,12 +144,12 @@ static void update_osd(struct vo *vo)
switch (fmt) {
case SUBBITMAP_RGBA: {
GLSLF("// OSD (RGBA)\n");
- GLSL(vec4 color = texture(osdtex, texcoord).bgra;)
+ GLSL(color = texture(osdtex, texcoord).bgra;)
break;
}
case SUBBITMAP_LIBASS: {
GLSLF("// OSD (libass)\n");
- GLSL(vec4 color =
+ GLSL(color =
vec4(ass_color.rgb, ass_color.a * texture(osdtex, texcoord).r);)
break;
}
@@ -156,7 +158,7 @@ static void update_osd(struct vo *vo)
}
gl_sc_set_vao(p->sc, mpgl_osd_get_vao(p->osd));
gl_sc_gen_shader_and_reset(p->sc);
- mpgl_osd_draw_part(p->osd, p->w, -p->h, n);
+ mpgl_osd_draw_part(p->osd, p->osd_res.w, -p->osd_res.h, n);
}
MP_STATS(vo, "stop rpi_osd");
@@ -171,22 +173,39 @@ static void resize(struct vo *vo)
vo_get_src_dst_rects(vo, &src, &dst, &p->osd_res);
+ int rotate[] = {MMAL_DISPLAY_ROT0,
+ MMAL_DISPLAY_ROT90,
+ MMAL_DISPLAY_ROT180,
+ MMAL_DISPLAY_ROT270};
+
+
int src_w = src.x1 - src.x0, src_h = src.y1 - src.y0,
dst_w = dst.x1 - dst.x0, dst_h = dst.y1 - dst.y0;
+ int p_x, p_y;
+ av_reduce(&p_x, &p_y, dst_w * src_h, src_w * dst_h, 16000);
MMAL_DISPLAYREGION_T dr = {
.hdr = { .id = MMAL_PARAMETER_DISPLAYREGION,
.size = sizeof(MMAL_DISPLAYREGION_T), },
.src_rect = { .x = src.x0, .y = src.y0, .width = src_w, .height = src_h },
- .dest_rect = { .x = dst.x0, .y = dst.y0, .width = dst_w, .height = dst_h },
+ .dest_rect = { .x = dst.x0 + p->x, .y = dst.y0 + p->y,
+ .width = dst_w, .height = dst_h },
.layer = p->video_layer,
.display_num = p->display_nr,
- .pixel_x = dst_w * src_h,
- .pixel_y = src_w * dst_h,
+ .pixel_x = p_x,
+ .pixel_y = p_y,
+ .transform = rotate[vo->params ? vo->params->rotate / 90 : 0],
+ .fullscreen = vo->opts->fullscreen,
.set = MMAL_DISPLAY_SET_SRC_RECT | MMAL_DISPLAY_SET_DEST_RECT |
MMAL_DISPLAY_SET_LAYER | MMAL_DISPLAY_SET_NUM |
- MMAL_DISPLAY_SET_PIXEL,
+ MMAL_DISPLAY_SET_PIXEL | MMAL_DISPLAY_SET_TRANSFORM |
+ MMAL_DISPLAY_SET_FULLSCREEN,
};
+ if (vo->params && (vo->params->rotate % 180) == 90) {
+ MPSWAP(int, dr.src_rect.x, dr.src_rect.y);
+ MPSWAP(int, dr.src_rect.width, dr.src_rect.height);
+ }
+
if (mmal_port_parameter_set(input, &dr.hdr))
MP_WARN(vo, "could not set video rectangle\n");
}
@@ -228,8 +247,15 @@ static int update_display_size(struct vo *vo)
MP_VERBOSE(vo, "Display size: %dx%d\n", p->w, p->h);
+ return 0;
+}
+
+static int create_overlays(struct vo *vo)
+{
+ struct priv *p = vo->priv;
destroy_overlays(vo);
+ if (vo->opts->fullscreen) {
// Use the whole screen.
VC_RECT_T dst = {.width = p->w, .height = p->h};
VC_RECT_T src = {.width = 1 << 16, .height = 1 << 16};
@@ -249,9 +275,13 @@ static int update_display_size(struct vo *vo)
return -1;
}
}
+ }
if (p->enable_osd) {
- alpha = (VC_DISPMANX_ALPHA_T){
+ VC_RECT_T dst = {.x = p->x, .y = p->y,
+ .width = p->osd_res.w, .height = p->osd_res.h};
+ VC_RECT_T src = {.width = p->osd_res.w << 16, .height = p->osd_res.h << 16};
+ VC_DISPMANX_ALPHA_T alpha = {
.flags = DISPMANX_FLAGS_ALPHA_FROM_SOURCE,
.opacity = 0xFF,
};
@@ -265,7 +295,9 @@ static int update_display_size(struct vo *vo)
return -1;
}
- if (mp_egl_rpi_init(&p->egl, p->osd_overlay, p->w, p->h) < 0) {
+ if (mp_egl_rpi_init(&p->egl, p->osd_overlay,
+ p->osd_res.w, p->osd_res.h) < 0)
+ {
MP_FATAL(vo, "EGL/GLES initialization for OSD renderer failed.\n");
return -1;
}
@@ -300,6 +332,33 @@ static int update_display_size(struct vo *vo)
return 0;
}
+static int set_geometry(struct vo *vo)
+{
+ struct priv *p = vo->priv;
+
+ if (vo->opts->fullscreen) {
+ vo->dwidth = p->w;
+ vo->dheight = p->h;
+ p->x = p->y = 0;
+ } else {
+ struct vo_win_geometry geo;
+ struct mp_rect screenrc = {0, 0, p->w, p->h};
+
+ vo_calc_window_geometry(vo, &screenrc, &geo);
+ vo_apply_window_geometry(vo, &geo);
+
+ p->x = geo.win.x0;
+ p->y = geo.win.y0;
+ }
+
+ resize(vo);
+
+ if (create_overlays(vo) < 0)
+ return -1;
+
+ return 0;
+}
+
static void wait_next_vsync(struct vo *vo)
{
struct priv *p = vo->priv;
@@ -450,20 +509,13 @@ static int reconfig(struct vo *vo, struct mp_image_params *params)
MMAL_PORT_T *input = p->renderer->input[0];
bool opaque = params->imgfmt == IMGFMT_MMAL;
- vo->dwidth = p->w;
- vo->dheight = p->h;
-
disable_renderer(vo);
- AVRational dr = {params->d_w, params->d_h};
- AVRational ir = {params->w, params->h};
- AVRational par = av_div_q(dr, ir);
-
input->format->encoding = opaque ? MMAL_ENCODING_OPAQUE : MMAL_ENCODING_I420;
input->format->es->video.width = MP_ALIGN_UP(params->w, ALIGN_W);
input->format->es->video.height = MP_ALIGN_UP(params->h, ALIGN_H);
input->format->es->video.crop = (MMAL_RECT_T){0, 0, params->w, params->h};
- input->format->es->video.par = (MMAL_RATIONAL_T){par.num, par.den};
+ input->format->es->video.par = (MMAL_RATIONAL_T){params->p_w, params->p_h};
input->format->es->video.color_space = map_csp(params->colorspace);
if (mmal_port_format_commit(input))
@@ -488,7 +540,8 @@ static int reconfig(struct vo *vo, struct mp_image_params *params)
}
}
- resize(vo);
+ if (set_geometry(vo) < 0)
+ return -1;
p->renderer_enabled = true;
@@ -542,6 +595,12 @@ static int control(struct vo *vo, uint32_t request, void *data)
struct priv *p = vo->priv;
switch (request) {
+ case VOCTRL_FULLSCREEN:
+ vo->opts->fullscreen = !vo->opts->fullscreen;
+ if (p->renderer_enabled)
+ set_geometry(vo);
+ vo->want_redraw = true;
+ return VO_TRUE;
case VOCTRL_GET_PANSCAN:
return VO_TRUE;
case VOCTRL_SET_PANSCAN:
@@ -561,7 +620,7 @@ static int control(struct vo *vo, uint32_t request, void *data)
atomic_store(&p->update_display, false);
update_display_size(vo);
if (p->renderer_enabled)
- resize(vo);
+ set_geometry(vo);
}
return VO_TRUE;
case VOCTRL_GET_DISPLAY_FPS:
@@ -679,6 +738,7 @@ static const struct m_option options[] = {
const struct vo_driver video_out_rpi = {
.description = "Raspberry Pi (MMAL)",
.name = "rpi",
+ .caps = VO_CAP_ROTATE90,
.preinit = preinit,
.query_format = query_format,
.reconfig = reconfig,
diff --git a/video/out/vo_sdl.c b/video/out/vo_sdl.c
index 5ee6cb2..9d34564 100644
--- a/video/out/vo_sdl.c
+++ b/video/out/vo_sdl.c
@@ -5,18 +5,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 <stdio.h>
@@ -68,11 +68,7 @@ const struct formatmap_entry formats[] = {
{SDL_PIXELFORMAT_BGR24, IMGFMT_BGR24, 0},
{SDL_PIXELFORMAT_RGB888, IMGFMT_RGB24, 0},
{SDL_PIXELFORMAT_BGR888, IMGFMT_BGR24, 0},
- {SDL_PIXELFORMAT_RGB565, IMGFMT_BGR565, 0},
{SDL_PIXELFORMAT_BGR565, IMGFMT_RGB565, 0},
- {SDL_PIXELFORMAT_RGB555, IMGFMT_BGR555, 0},
- {SDL_PIXELFORMAT_BGR555, IMGFMT_RGB555, 0},
- {SDL_PIXELFORMAT_RGB444, IMGFMT_BGR444, 0}
#else
{SDL_PIXELFORMAT_RGBX8888, IMGFMT_ABGR, 0}, // has no alpha -> bad for OSD
{SDL_PIXELFORMAT_BGRX8888, IMGFMT_ARGB, 0}, // has no alpha -> bad for OSD
@@ -85,10 +81,6 @@ const struct formatmap_entry formats[] = {
{SDL_PIXELFORMAT_RGB888, IMGFMT_BGR24, 0},
{SDL_PIXELFORMAT_BGR888, IMGFMT_RGB24, 0},
{SDL_PIXELFORMAT_RGB565, IMGFMT_RGB565, 0},
- {SDL_PIXELFORMAT_BGR565, IMGFMT_RGB565, 0},
- {SDL_PIXELFORMAT_RGB555, IMGFMT_RGB555, 0},
- {SDL_PIXELFORMAT_BGR555, IMGFMT_BGR555, 0},
- {SDL_PIXELFORMAT_RGB444, IMGFMT_RGB444, 0}
#endif
};
diff --git a/video/out/vo_vaapi.c b/video/out/vo_vaapi.c
index 2bb9d80..5275d4d 100644
--- a/video/out/vo_vaapi.c
+++ b/video/out/vo_vaapi.c
@@ -615,7 +615,7 @@ static int preinit(struct vo *vo)
for (int i = 0; i < p->va_num_subpic_formats; i++) {
MP_VERBOSE(vo, " %s, flags 0x%x\n",
- VA_STR_FOURCC(p->va_subpic_formats[i].fourcc),
+ mp_tag_str(p->va_subpic_formats[i].fourcc),
p->va_subpic_flags[i]);
if (p->va_subpic_formats[i].fourcc == OSD_VA_FORMAT) {
p->osd_format = p->va_subpic_formats[i];
diff --git a/video/out/vo_vdpau.c b/video/out/vo_vdpau.c
index 54f95c6..b85780e 100644
--- a/video/out/vo_vdpau.c
+++ b/video/out/vo_vdpau.c
@@ -39,7 +39,7 @@
#include "video/hwdec.h"
#include "common/msg.h"
#include "options/options.h"
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "vo.h"
#include "x11_common.h"
#include "video/csputils.h"
diff --git a/video/out/vo_wayland.c b/video/out/vo_wayland.c
index dcbe2d5..57d6c7f 100644
--- a/video/out/vo_wayland.c
+++ b/video/out/vo_wayland.c
@@ -2,18 +2,18 @@
* This file is part of mpv video player.
* Copyright © 2013 Alexander Preisinger <alexander.preisinger@gmail.com>
*
- * 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>
@@ -47,28 +47,8 @@ static const struct wl_buffer_listener buffer_listener;
static const format_t format_table[] = {
{WL_SHM_FORMAT_ARGB8888, IMGFMT_BGRA}, // 8b 8g 8r 8a
{WL_SHM_FORMAT_XRGB8888, IMGFMT_BGR0},
- {WL_SHM_FORMAT_RGB332, IMGFMT_RGB8}, // 3b 3g 2r
- {WL_SHM_FORMAT_BGR233, IMGFMT_BGR8}, // 3r 3g 3b,
#if BYTE_ORDER == LITTLE_ENDIAN
- {WL_SHM_FORMAT_XRGB4444, IMGFMT_RGB444}, // 4b 4g 4r 4a
- {WL_SHM_FORMAT_XBGR4444, IMGFMT_BGR444}, // 4r 4g 4b 4a
- {WL_SHM_FORMAT_ARGB4444, IMGFMT_RGB444},
- {WL_SHM_FORMAT_ABGR4444, IMGFMT_BGR444},
- {WL_SHM_FORMAT_XRGB1555, IMGFMT_RGB555}, // 5b 5g 5r 1a
- {WL_SHM_FORMAT_XBGR1555, IMGFMT_BGR555}, // 5r 5g 5b 1a
- {WL_SHM_FORMAT_ARGB1555, IMGFMT_RGB555},
- {WL_SHM_FORMAT_ABGR1555, IMGFMT_BGR555},
{WL_SHM_FORMAT_RGB565, IMGFMT_RGB565}, // 5b 6g 5r
- {WL_SHM_FORMAT_BGR565, IMGFMT_BGR565}, // 5r 6g 5b
-#else
- {WL_SHM_FORMAT_RGBX4444, IMGFMT_BGR444}, // 4a 4b 4g 4r
- {WL_SHM_FORMAT_BGRX4444, IMGFMT_RGB444}, // 4a 4r 4g 4b
- {WL_SHM_FORMAT_RGBA4444, IMGFMT_BGR444},
- {WL_SHM_FORMAT_BGRA4444, IMGFMT_RGB444},
- {WL_SHM_FORMAT_RGBX5551, IMGFMT_BGR555}, // 1a 5g 5b 5r
- {WL_SHM_FORMAT_BGRX5551, IMGFMT_RGB555}, // 1a 5r 5g 5b
- {WL_SHM_FORMAT_RGBA5551, IMGFMT_BGR555},
- {WL_SHM_FORMAT_BGRA5551, IMGFMT_RGB555},
#endif
{WL_SHM_FORMAT_RGB888, IMGFMT_BGR24}, // 8b 8g 8r
{WL_SHM_FORMAT_BGR888, IMGFMT_RGB24}, // 8r 8g 8b
@@ -299,8 +279,8 @@ static bool resize(struct priv *p)
.imgfmt = p->video_format->mp_format,
.w = p->dst_w,
.h = p->dst_h,
- .d_w = p->dst_w,
- .d_h = p->dst_h,
+ .p_w = 1,
+ .p_h = 1,
};
mp_image_params_guess_csp(&p->sws->dst);
diff --git a/video/out/vo_x11.c b/video/out/vo_x11.c
index 7f4e36f..2826223 100644
--- a/video/out/vo_x11.c
+++ b/video/out/vo_x11.c
@@ -185,6 +185,7 @@ const struct fmt_entry {
{IMGFMT_RGB0, 32, LSBFirst, 0x000000FF, 0x0000FF00, 0x00FF0000},
{IMGFMT_BGR0, 32, MSBFirst, 0x0000FF00, 0x00FF0000, 0xFF000000},
{IMGFMT_BGR0, 32, LSBFirst, 0x00FF0000, 0x0000FF00, 0x000000FF},
+ {IMGFMT_RGB565, 16, LSBFirst, 0x0000F800, 0x000007E0, 0x0000001F},
{0}
};
@@ -259,8 +260,8 @@ static bool resize(struct vo *vo)
.imgfmt = fmte->mpfmt,
.w = p->dst_w,
.h = p->dst_h,
- .d_w = p->dst_w,
- .d_h = p->dst_h,
+ .p_w = 1,
+ .p_h = 1,
};
mp_image_params_guess_csp(&p->sws->dst);
diff --git a/video/out/vo_xv.c b/video/out/vo_xv.c
index e799e8f..ad40fd8 100644
--- a/video/out/vo_xv.c
+++ b/video/out/vo_xv.c
@@ -42,7 +42,7 @@
#include <X11/extensions/Xvlib.h>
#include "options/options.h"
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "common/msg.h"
#include "vo.h"
#include "video/mp_image.h"
diff --git a/video/out/w32_common.c b/video/out/w32_common.c
index 025b744..d26de3b 100644
--- a/video/out/w32_common.c
+++ b/video/out/w32_common.c
@@ -43,7 +43,7 @@
#include "osdep/atomics.h"
#include "misc/dispatch.h"
#include "misc/rendezvous.h"
-#include "talloc.h"
+#include "mpv_talloc.h"
static const wchar_t classname[] = L"mpv";
@@ -272,8 +272,9 @@ static HRESULT STDMETHODCALLTYPE DropTarget_Drop(IDropTarget* This,
} else if (pDataObj->lpVtbl->GetData(pDataObj,
&fmtetc_url, &medium) == S_OK) {
// get the URL encoded in US-ASCII
- char* url = (char*)GlobalLock(medium.hGlobal);
- if (url != NULL) {
+ 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);
@@ -281,6 +282,7 @@ static HRESULT STDMETHODCALLTYPE DropTarget_Drop(IDropTarget* This,
MP_ERR(t->w32, "error getting dropped URL\n");
}
+ talloc_free(url);
GlobalUnlock(medium.hGlobal);
}
@@ -1224,7 +1226,7 @@ static void *gui_thread(void *ptr)
if (SUCCEEDED(OleInitialize(NULL))) {
ole_ok = true;
- fmtetc_url.cfFormat = (CLIPFORMAT)RegisterClipboardFormat(TEXT("UniformResourceLocator"));
+ fmtetc_url.cfFormat = (CLIPFORMAT)RegisterClipboardFormat(TEXT("UniformResourceLocatorW"));
DropTarget* dropTarget = talloc(NULL, DropTarget);
DropTarget_Init(dropTarget, w32);
RegisterDragDrop(w32->window, &dropTarget->iface);
diff --git a/video/out/wayland/buffer.c b/video/out/wayland/buffer.c
index e04d248..aae590e 100644
--- a/video/out/wayland/buffer.c
+++ b/video/out/wayland/buffer.c
@@ -2,18 +2,18 @@
* This file is part of mpv video player.
* Copyright © 2014 Alexander Preisinger <alexander.preisinger@gmail.com>
*
- * 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 "buffer.h"
diff --git a/video/out/wayland/buffer.h b/video/out/wayland/buffer.h
index 5bd2d9a..783cd10 100644
--- a/video/out/wayland/buffer.h
+++ b/video/out/wayland/buffer.h
@@ -2,18 +2,18 @@
* This file is part of mpv video player.
* Copyright © 2014 Alexander Preisinger <alexander.preisinger@gmail.com>
*
- * 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_WAYLAND_BUFFER_H
diff --git a/video/out/wayland/memfile.c b/video/out/wayland/memfile.c
index 179abe8..f28216d 100644
--- a/video/out/wayland/memfile.c
+++ b/video/out/wayland/memfile.c
@@ -2,18 +2,18 @@
* This file is part of mpv video player.
* Copyright © 2014 Alexander Preisinger <alexander.preisinger@gmail.com>
*
- * 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 <fcntl.h>
diff --git a/video/out/wayland/memfile.h b/video/out/wayland/memfile.h
index 9d59111..67cdb1b 100644
--- a/video/out/wayland/memfile.h
+++ b/video/out/wayland/memfile.h
@@ -2,18 +2,18 @@
* This file is part of mpv video player.
* Copyright © 2014 Alexander Preisinger <alexander.preisinger@gmail.com>
*
- * 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_WAYLAND_MEMFILE_H
diff --git a/video/out/wayland_common.c b/video/out/wayland_common.c
index 4e9958d..73ef5ed 100644
--- a/video/out/wayland_common.c
+++ b/video/out/wayland_common.c
@@ -4,18 +4,18 @@
* Copyright © 2012-2013 Collabora, Ltd.
* Copyright © 2013 Alexander Preisinger <alexander.preisinger@gmail.com>
*
- * 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>
@@ -34,7 +34,7 @@
#include "misc/bstr.h"
#include "options/options.h"
#include "common/msg.h"
-#include "talloc.h"
+#include "mpv_talloc.h"
#include "wayland_common.h"
@@ -141,6 +141,11 @@ static void ssurface_handle_configure(void *data,
{
struct vo_wayland_state *wl = data;
schedule_resize(wl, edges, width, height);
+
+ // check for every resize if the window is in fullscreen mode and reset the
+ // mode accordingly, this prevents gnome from resizing mpvs fullscreen
+ // window to a smaller size
+ vo_wayland_fullscreen(wl->vo);
}
static void ssurface_handle_popup_done(void *data,
@@ -1051,7 +1056,6 @@ static void vo_wayland_ontop (struct vo *vo)
MP_DBG(wl, "going ontop\n");
vo->opts->ontop = 1;
window_set_toplevel(wl);
- schedule_resize(wl, 0, wl->window.width, wl->window.height);
}
static void vo_wayland_fullscreen (struct vo *vo)
@@ -1076,7 +1080,6 @@ static void vo_wayland_fullscreen (struct vo *vo)
MP_DBG(wl, "leaving fullscreen\n");
wl->window.is_fullscreen = false;
window_set_toplevel(wl);
- schedule_resize(wl, 0, wl->window.p_width, wl->window.p_height);
}
}
@@ -1135,7 +1138,7 @@ static int vo_wayland_check_events (struct vo *vo)
if (poll(&fd, 1, 0) > 0) {
if (fd.revents & POLLERR) {
- MP_ERR(wl, "error occured on the drag&drop fd\n");
+ MP_ERR(wl, "error occurred on the drag&drop fd\n");
close(wl->input.dnd_fd);
wl->input.dnd_fd = -1;
}
@@ -1224,9 +1227,6 @@ static void vo_wayland_update_screeninfo(struct vo *vo, struct mp_rect *screenrc
screenrc->y1 = wl->display.current_output->height;
}
}
-
- wl->window.fs_width = screenrc->x1;
- wl->window.fs_height = screenrc->y1;
}
int vo_wayland_control (struct vo *vo, int *events, int request, void *arg)
@@ -1301,6 +1301,7 @@ bool vo_wayland_config (struct vo *vo)
wl->window.width = vo->dwidth;
wl->window.height = vo->dheight;
+
vo_wayland_fullscreen(vo);
return true;
diff --git a/video/out/wayland_common.h b/video/out/wayland_common.h
index 44033c7..d23b2f2 100644
--- a/video/out/wayland_common.h
+++ b/video/out/wayland_common.h
@@ -2,18 +2,18 @@
* This file is part of mpv video player.
* Copyright © 2013 Alexander Preisinger <alexander.preisinger@gmail.com>
*
- * 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_WAYLAND_COMMON_H
diff --git a/video/out/win32/displayconfig.c b/video/out/win32/displayconfig.c
index 635e8b6..cc91450 100644
--- a/video/out/win32/displayconfig.c
+++ b/video/out/win32/displayconfig.c
@@ -22,7 +22,7 @@
#include "displayconfig.h"
-#include "talloc.h"
+#include "mpv_talloc.h"
// Some DisplayConfig definitions are broken in mingw-w64 (as of 2015-3-13.) To
// get the correct struct alignment, it's necessary to define them properly.
diff --git a/video/out/win_state.c b/video/out/win_state.c
index f48f628..a80a650 100644
--- a/video/out/win_state.c
+++ b/video/out/win_state.c
@@ -82,12 +82,12 @@ void vo_calc_window_geometry(struct vo *vo, const struct mp_rect *screen,
// The case of calling this function even though no video was configured
// yet (i.e. vo->params==NULL) happens when vo_opengl creates a hidden
// window in order to create an OpenGL context.
- struct mp_image_params params = { .d_w = 320, .d_h = 200 };
+ struct mp_image_params params = { .w = 320, .h = 200 };
if (vo->params)
params = *vo->params;
- int d_w = params.d_w;
- int d_h = params.d_h;
+ int d_w, d_h;
+ mp_image_params_get_dsize(&params, &d_w, &d_h);
if ((vo->driver->caps & VO_CAP_ROTATE90) && params.rotate % 180 == 90)
MPSWAP(int, d_w, d_h);
d_w = MPCLAMP(d_w * opts->window_scale, 1, 16000);
diff --git a/video/out/x11_common.c b/video/out/x11_common.c
index dd75655..5033a0f 100644
--- a/video/out/x11_common.c
+++ b/video/out/x11_common.c
@@ -29,7 +29,7 @@
#include "input/input.h"
#include "input/event.h"
#include "x11_common.h"
-#include "talloc.h"
+#include "mpv_talloc.h"
#include <string.h>
#include <unistd.h>
@@ -364,7 +364,7 @@ static void xrandr_read(struct vo_x11_state *x11)
RRCrtcChangeNotifyMask | RROutputChangeNotifyMask);
}
- XRRScreenResources *r = XRRGetScreenResources(x11->display, x11->rootwin);
+ XRRScreenResources *r = XRRGetScreenResourcesCurrent(x11->display, x11->rootwin);
if (!r) {
MP_VERBOSE(x11, "Xrandr doesn't work.\n");
return;
@@ -497,8 +497,8 @@ static void *screensaver_thread(void *arg)
char *args[] = {"xdg-screensaver", "reset", NULL};
int status = mp_subprocess(args, NULL, NULL, NULL, NULL, &(char*){0});
if (status) {
- MP_WARN(x11, "Disabling screensaver failed (%d). Make sure the "
- "xdg-screensaver script is installed.\n", status);
+ MP_VERBOSE(x11, "Disabling screensaver failed (%d). Make sure the "
+ "xdg-screensaver script is installed.\n", status);
break;
}
}
@@ -1069,7 +1069,9 @@ int vo_x11_check_events(struct vo *vo)
vo_x11_dnd_handle_selection(vo, &Event.xselection);
break;
case PropertyNotify:
- if (Event.xproperty.atom == XA(x11, _NET_FRAME_EXTENTS)) {
+ if (Event.xproperty.atom == XA(x11, _NET_FRAME_EXTENTS) ||
+ Event.xproperty.atom == XA(x11, WM_STATE))
+ {
if (!x11->pseudo_mapped && !x11->parent) {
MP_VERBOSE(x11, "not waiting for MapNotify\n");
x11->pseudo_mapped = true;
diff --git a/video/sws_utils.c b/video/sws_utils.c
index 00b0051..ce44c67 100644
--- a/video/sws_utils.c
+++ b/video/sws_utils.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 <assert.h>
@@ -159,8 +159,8 @@ int mp_sws_reinit(struct mp_sws_context *ctx)
struct mp_image_params *dst = &ctx->dst;
// Neutralize unsupported or ignored parameters.
- src->d_w = dst->d_w = 0;
- src->d_h = dst->d_h = 0;
+ src->p_w = dst->p_w = 0;
+ src->p_h = dst->p_h = 0;
if (cache_valid(ctx))
return 0;
diff --git a/video/vaapi.c b/video/vaapi.c
index 1b4cdf6..61d94ef 100644
--- a/video/vaapi.c
+++ b/video/vaapi.c
@@ -104,7 +104,7 @@ static void va_get_formats(struct mp_vaapi_ctx *ctx)
return;
MP_VERBOSE(ctx, "%d image formats available:\n", formats->num);
for (int i = 0; i < formats->num; i++)
- MP_VERBOSE(ctx, " %s\n", VA_STR_FOURCC(formats->entries[i].fourcc));
+ MP_VERBOSE(ctx, " %s\n", mp_tag_str(formats->entries[i].fourcc));
ctx->image_formats = formats;
}
diff --git a/video/vaapi.h b/video/vaapi.h
index 888730e..11ff2c9 100644
--- a/video/vaapi.h
+++ b/video/vaapi.h
@@ -23,9 +23,6 @@
#include <pthread.h>
#include <va/va.h>
-#define VA_STR_FOURCC(fcc) \
- (const char[]){(fcc), (fcc) >> 8u, (fcc) >> 16u, (fcc) >> 24u, 0}
-
#include "mp_image.h"
#include "hwdec.h"
diff --git a/video/vdpau.c b/video/vdpau.c
index 55ca8d9..9dfbc2b 100644
--- a/video/vdpau.c
+++ b/video/vdpau.c
@@ -37,8 +37,8 @@ static struct mp_image *download_image(struct mp_hwdec_ctx *hwctx,
VdpStatus vdp_st;
struct mp_image *res = NULL;
- int w = mpi->params.d_w;
- int h = mpi->params.d_h;
+ int w, h;
+ mp_image_params_get_dsize(&mpi->params, &w, &h);
// Abuse this lock for our own purposes. It could use its own lock instead.
pthread_mutex_lock(&ctx->pool_lock);
@@ -102,6 +102,7 @@ static void mark_vdpau_objects_uninitialized(struct mp_vdpau_ctx *ctx)
ctx->video_surfaces[i].allocated = false;
}
ctx->vdp_device = VDP_INVALID_HANDLE;
+ ctx->preemption_obj = VDP_INVALID_HANDLE;
}
static void preemption_callback(VdpDevice device, void *context)
@@ -167,6 +168,14 @@ static int win_x11_init_vdpau_procs(struct mp_vdpau_ctx *ctx, bool probing)
ctx->vdp = vdp;
ctx->get_proc_address = get_proc_address;
+ vdp_st = vdp.output_surface_create(ctx->vdp_device, VDP_RGBA_FORMAT_B8G8R8A8,
+ 1, 1, &ctx->preemption_obj);
+ if (vdp_st != VDP_STATUS_OK) {
+ MP_ERR(ctx, "Could not create dummy object: %s",
+ vdp.get_error_string(vdp_st));
+ return -1;
+ }
+
vdp.preemption_callback_register(ctx->vdp_device, preemption_callback, ctx);
return 0;
}
@@ -211,6 +220,11 @@ int mp_vdpau_handle_preemption(struct mp_vdpau_ctx *ctx, uint64_t *counter)
int r = 1;
pthread_mutex_lock(&ctx->preempt_lock);
+ const void *p[4] = {&(uint32_t){0}};
+ uint32_t stride[4] = {4};
+ VdpRect rc = {0};
+ ctx->vdp.output_surface_put_bits_native(ctx->preemption_obj, p, stride, &rc);
+
// First time init
if (counter && !*counter)
*counter = ctx->preemption_counter;
@@ -425,6 +439,11 @@ void mp_vdpau_destroy(struct mp_vdpau_ctx *ctx)
CHECK_VDP_WARNING(ctx, "Error when calling vdp_output_surface_destroy");
}
+ if (ctx->preemption_obj != VDP_INVALID_HANDLE) {
+ vdp_st = vdp->output_surface_destroy(ctx->preemption_obj);
+ CHECK_VDP_WARNING(ctx, "Error when calling vdp_output_surface_destroy");
+ }
+
if (vdp->device_destroy && ctx->vdp_device != VDP_INVALID_HANDLE) {
vdp_st = vdp->device_destroy(ctx->vdp_device);
CHECK_VDP_WARNING(ctx, "Error when calling vdp_device_destroy");
diff --git a/video/vdpau.h b/video/vdpau.h
index 2e9269a..db73a87 100644
--- a/video/vdpau.h
+++ b/video/vdpau.h
@@ -59,6 +59,7 @@ struct mp_vdpau_ctx {
uint64_t preemption_counter; // incremented after _restoring_
bool preemption_user_notified;
double last_preemption_retry_fail;
+ VdpOutputSurface preemption_obj; // dummy for reliable preempt. check
// Surface pool
pthread_mutex_t pool_lock;
diff --git a/waftools/checks/custom.py b/waftools/checks/custom.py
index dcdaf2f..ad6a4f4 100644
--- a/waftools/checks/custom.py
+++ b/waftools/checks/custom.py
@@ -46,12 +46,17 @@ def check_iconv(ctx, dependency_identifier):
iconv_program = load_fragment('iconv.c')
libdliconv = " ".join(ctx.env.LIB_LIBDL + ['iconv'])
libs = ['iconv', libdliconv]
- checkfn = check_cc(fragment=iconv_program)
+ args = {'fragment': iconv_program}
+ if ctx.env.DEST_OS == 'openbsd':
+ args['cflags'] = '-I/usr/local/include'
+ args['linkflags'] = '-L/usr/local/lib'
+ checkfn = check_cc(**args)
return check_libs(libs, checkfn)(ctx, dependency_identifier)
def check_lua(ctx, dependency_identifier):
lua_versions = [
( '51', 'lua >= 5.1.0 lua < 5.2.0'),
+ ( '51obsd', 'lua51 >= 5.1.0'), # OpenBSD
( '51deb', 'lua5.1 >= 5.1.0'), # debian
( '51fbsd', 'lua-5.1 >= 5.1.0'), # FreeBSD
( '52', 'lua >= 5.2.0 lua < 5.3.0' ),
diff --git a/waftools/detections/compiler.py b/waftools/detections/compiler.py
index 284a7a5..ada2079 100644
--- a/waftools/detections/compiler.py
+++ b/waftools/detections/compiler.py
@@ -51,6 +51,8 @@ def __add_clang_flags__(ctx):
def __add_mswin_flags__(ctx):
ctx.env.CFLAGS += ['-D_WIN32_WINNT=0x0601', '-DUNICODE', '-DCOBJMACROS',
'-U__STRICT_ANSI__']
+ ctx.env.LAST_LINKFLAGS += ['-Wl,--major-os-version=6,--minor-os-version=0',
+ '-Wl,--major-subsystem-version=6,--minor-subsystem-version=0']
def __add_mingw_flags__(ctx):
__add_mswin_flags__(ctx)
diff --git a/waftools/generators/headers.py b/waftools/generators/headers.py
index 3c83292..84f914c 100644
--- a/waftools/generators/headers.py
+++ b/waftools/generators/headers.py
@@ -27,7 +27,7 @@ def __get_features_string__(ctx):
def __add_mpv_defines__(ctx):
from sys import argv
ctx.define("CONFIGURATION", " ".join(argv))
- ctx.define("MPV_CONFDIR", ctx.env.CONFDIR)
+ ctx.define("MPV_CONFDIR", ctx.env.CONFLOADDIR)
ctx.define("FULLCONFIG", __escape_c_string(__get_features_string__(ctx)))
def configure(ctx):
diff --git a/wscript b/wscript
index a5e6914..13d739e 100644
--- a/wscript
+++ b/wscript
@@ -55,6 +55,11 @@ build_options = [
'desc': 'manpage generation',
'func': check_ctx_vars('RST2MAN')
}, {
+ 'name': '--html-build',
+ 'desc': 'html manual generation',
+ 'func': check_ctx_vars('RST2HTML'),
+ 'default': 'disable',
+ }, {
'name': '--pdf-build',
'desc': 'pdf manual generation',
'func': check_ctx_vars('RST2PDF'),
@@ -199,12 +204,6 @@ iconv support use --disable-iconv.",
'deps_any': [ 'os-win32', 'os-cygwin' ],
'func': check_true
}, {
- 'name': '--waio',
- 'desc': 'libwaio for win32',
- 'deps': [ 'os-win32', 'mingw' ],
- 'func': check_libs(['waio'],
- check_statement('waio/waio.h', 'waio_alloc(0, 0, 0, 0)')),
- }, {
'name': '--termios',
'desc': 'termios',
'func': check_headers('termios.h', 'sys/termios.h'),
@@ -423,9 +422,10 @@ FFmpeg/Libav libraries. You need at least {0}. Aborting.".format(libav_versions_
'req': True,
'fmsg': 'No resampler found. Install libavresample or libswresample (FFmpeg).'
}, {
- 'name': '--libavfilter',
+ 'name': 'libavfilter',
'desc': 'libavfilter',
'func': check_pkg_config('libavfilter', '>= 5.0.0'),
+ 'req': True,
}, {
'name': '--libavdevice',
'desc': 'libavdevice',
@@ -478,6 +478,12 @@ FFmpeg/Libav libraries. You need at least {0}. Aborting.".format(libav_versions_
'func': check_statement('libavcodec/avcodec.h',
'AVSubtitleRect r = {.linesize={0}}',
use='libav'),
+ }, {
+ 'name': 'avcodec-profile-name',
+ 'desc': 'libavcodec avcodec_profile_name()',
+ 'func': check_statement('libavcodec/avcodec.h',
+ 'avcodec_profile_name(0,0)',
+ use='libav'),
},
]
@@ -550,6 +556,10 @@ audio_output_features = [
'func': check_pkg_config('openal', '>= 1.13'),
'default': 'disable'
}, {
+ 'name': '--opensles',
+ 'desc': 'OpenSL ES audio output',
+ 'func': check_statement('SLES/OpenSLES.h', 'slCreateEngine', lib="OpenSLES"),
+ }, {
'name': '--alsa',
'desc': 'ALSA audio output',
'func': check_pkg_config('alsa', '>= 1.0.18'),
@@ -561,10 +571,6 @@ audio_output_features = [
fragment=load_fragment('coreaudio.c'),
framework_name=['CoreFoundation', 'CoreAudio', 'AudioUnit', 'AudioToolbox'])
}, {
- 'name': '--dsound',
- 'desc': 'DirectSound audio output',
- 'func': check_cc(header_name='dsound.h'),
- }, {
'name': '--wasapi',
'desc': 'WASAPI audio output',
'deps': ['win32', 'atomics'],
@@ -692,7 +698,7 @@ video_output_features = [
'name': '--vaapi',
'desc': 'VAAPI acceleration',
'deps': [ 'libdl' ],
- 'deps_any': [ 'x11', 'wayland' ],
+ 'deps_any': [ 'x11', 'wayland', 'egl-drm' ],
'func': check_pkg_config('libva', '>= 0.36.0'),
}, {
'name': '--vaapi-x11',
@@ -704,7 +710,11 @@ video_output_features = [
'desc': 'VAAPI (Wayland support)',
'deps': [ 'vaapi', 'gl-wayland' ],
'func': check_pkg_config('libva-wayland', '>= 0.36.0'),
-
+ }, {
+ 'name': '--vaapi-drm',
+ 'desc': 'VAAPI (DRM/EGL support)',
+ 'deps': [ 'vaapi', 'egl-drm' ],
+ 'func': check_pkg_config('libva-drm', '>= 0.36.0'),
}, {
'name': '--vaapi-glx',
'desc': 'VAAPI GLX',
@@ -718,7 +728,6 @@ video_output_features = [
}, {
'name': 'vaapi-egl',
'desc': 'VAAPI EGL',
- 'deps': [ 'c11-tls' ], # indirectly
'deps_any': [ 'vaapi-x-egl', 'vaapi-wayland' ],
'func': check_true,
}, {
@@ -736,6 +745,10 @@ video_output_features = [
'deps': [ 'win32' ],
'func': check_cc(header_name='d3d9.h'),
}, {
+ 'name': '--android',
+ 'desc': 'Android support',
+ 'func': check_statement('android/api-level.h', '(void)__ANDROID__'), # arbitrary android-specific header
+ }, {
# We need MMAL/bcm_host/dispmanx APIs. Also, most RPI distros require
# every project to hardcode the paths to the include directories. Also,
# these headers are so broken that they spam tons of warnings by merely
@@ -757,10 +770,39 @@ video_output_features = [
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': '--desktop-gl',
+ 'desc': 'Desktop OpengGL 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': '--any-gl',
+ 'desc': 'Any OpenGL (ES) support',
+ 'deps_any': ['desktop-gl', 'android-gl'],
+ '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': '--gl',
'desc': 'OpenGL video outputs',
- 'deps_any': [ 'gl-cocoa', 'gl-x11', 'egl-drm', 'gl-win32', 'gl-wayland', 'rpi' ],
+ 'deps_any': [ 'gl-cocoa', 'gl-x11', 'egl-x11', 'egl-drm',
+ 'gl-win32', 'gl-wayland', 'rpi', 'plain-gl' ],
+ 'func': check_true
+ }, {
+ 'name': 'egl-helpers',
+ 'desc': 'EGL helper functions',
+ 'deps_any': [ 'egl-x11' ],
'func': check_true
}
]
@@ -867,6 +909,8 @@ _INSTALL_DIRS_LIST = [
('mandir', '${DATADIR}/man', 'man pages '),
('docdir', '${DATADIR}/doc/mpv', 'documentation files'),
('zshdir', '${DATADIR}/zsh/site-functions', 'zsh completion functions'),
+
+ ('confloaddir', '${CONFDIR}', 'configuration files load directory'),
]
def options(opt):
@@ -900,7 +944,7 @@ def options(opt):
group.add_option('--lua',
type = 'string',
dest = 'LUA_VER',
- help = "select Lua package which should be autodetected. Choices: 51 51deb 51fbsd 52 52deb 52arch 52fbsd luajit")
+ help = "select Lua package which should be autodetected. Choices: 51 51deb 51obsd 51fbsd 52 52deb 52arch 52fbsd luajit")
@conf
def is_optimization(ctx):
@@ -926,6 +970,7 @@ def configure(ctx):
ctx.find_program(pkg_config, var='PKG_CONFIG')
ctx.find_program(ar, var='AR')
ctx.find_program('perl', var='BIN_PERL')
+ ctx.find_program('rst2html', var='RST2HTML', mandatory=False)
ctx.find_program('rst2man', var='RST2MAN', mandatory=False)
ctx.find_program('rst2pdf', var='RST2PDF', mandatory=False)
ctx.find_program(windres, var='WINDRES', mandatory=False)
diff --git a/wscript_build.py b/wscript_build.py
index 12199f1..4cc35ca 100644
--- a/wscript_build.py
+++ b/wscript_build.py
@@ -13,6 +13,16 @@ def _add_rst_manual_dependencies(ctx):
ctx.path.find_node('DOCS/man/mpv.rst'),
ctx.path.find_node(manpage_source))
+def _build_html(ctx):
+ ctx(
+ name = 'rst2html',
+ target = 'DOCS/man/mpv.html',
+ source = 'DOCS/man/mpv.rst',
+ rule = '${RST2HTML} ${SRC} ${TGT}',
+ install_path = ctx.env.DOCDIR)
+
+ _add_rst_manual_dependencies(ctx)
+
def _build_man(ctx):
ctx(
name = 'rst2man',
@@ -110,7 +120,7 @@ def build(ctx):
( "audio/filter/af_equalizer.c" ),
( "audio/filter/af_format.c" ),
( "audio/filter/af_lavcac3enc.c" ),
- ( "audio/filter/af_lavfi.c", "libavfilter" ),
+ ( "audio/filter/af_lavfi.c" ),
( "audio/filter/af_lavrresample.c" ),
( "audio/filter/af_pan.c" ),
( "audio/filter/af_rubberband.c", "rubberband" ),
@@ -124,11 +134,11 @@ def build(ctx):
( "audio/out/ao_coreaudio_exclusive.c", "coreaudio" ),
( "audio/out/ao_coreaudio_properties.c", "coreaudio" ),
( "audio/out/ao_coreaudio_utils.c", "coreaudio" ),
- ( "audio/out/ao_dsound.c", "dsound" ),
( "audio/out/ao_jack.c", "jack" ),
( "audio/out/ao_lavc.c", "encoding" ),
( "audio/out/ao_null.c" ),
( "audio/out/ao_openal.c", "openal" ),
+ ( "audio/out/ao_opensles.c", "opensles" ),
( "audio/out/ao_oss.c", "oss-audio" ),
( "audio/out/ao_pcm.c" ),
( "audio/out/ao_pulse.c", "pulse" ),
@@ -168,6 +178,7 @@ def build(ctx):
( "demux/demux_playlist.c" ),
( "demux/demux_raw.c" ),
( "demux/demux_rar.c" ),
+ ( "demux/demux_timeline.c" ),
( "demux/demux_tv.c", "tv" ),
( "demux/ebml.c" ),
( "demux/packet.c" ),
@@ -180,7 +191,7 @@ def build(ctx):
( "input/input.c" ),
( "input/ipc.c", "!mingw" ),
( "input/keycodes.c" ),
- ( "input/pipe-win32.c", "waio" ),
+ ( "input/pipe-win32.c", "mingw" ),
## Misc
( "misc/bstr.c" ),
@@ -208,6 +219,7 @@ def build(ctx):
( "player/loadfile.c" ),
( "player/main.c" ),
( "player/misc.c" ),
+ ( "player/lavfi.c" ),
( "player/lua.c", "lua" ),
( "player/osd.c" ),
( "player/playloop.c" ),
@@ -254,16 +266,12 @@ def build(ctx):
( "sub/dec_sub.c" ),
( "sub/draw_bmp.c" ),
( "sub/img_convert.c" ),
+ ( "sub/lavc_conv.c" ),
( "sub/osd.c" ),
( "sub/osd_dummy.c", "dummy-osd" ),
( "sub/osd_libass.c", "libass-osd" ),
( "sub/sd_ass.c", "libass" ),
( "sub/sd_lavc.c" ),
- ( "sub/sd_lavc_conv.c" ),
- ( "sub/sd_lavf_srt.c" ),
- ( "sub/sd_microdvd.c" ),
- ( "sub/sd_movtext.c" ),
- ( "sub/sd_srt.c" ),
## Video
( "video/csputils.c" ),
@@ -274,6 +282,7 @@ def build(ctx):
( "video/mp_image.c" ),
( "video/mp_image_pool.c" ),
( "video/sws_utils.c" ),
+ ( "video/dxva2.c", "dxva2-hwaccel" ),
( "video/vaapi.c", "vaapi" ),
( "video/vdpau.c", "vdpau" ),
( "video/vdpau_mixer.c", "vdpau" ),
@@ -293,20 +302,20 @@ def build(ctx):
( "video/filter/vf_expand.c" ),
( "video/filter/vf_flip.c" ),
( "video/filter/vf_format.c" ),
- ( "video/filter/vf_gradfun.c", "libavfilter"),
- ( "video/filter/vf_lavfi.c", "libavfilter"),
- ( "video/filter/vf_mirror.c", "libavfilter"),
+ ( "video/filter/vf_gradfun.c" ),
+ ( "video/filter/vf_lavfi.c" ),
+ ( "video/filter/vf_mirror.c" ),
( "video/filter/vf_noformat.c" ),
- ( "video/filter/vf_pullup.c", "libavfilter"),
- ( "video/filter/vf_rotate.c", "libavfilter"),
+ ( "video/filter/vf_pullup.c" ),
+ ( "video/filter/vf_rotate.c" ),
( "video/filter/vf_scale.c" ),
- ( "video/filter/vf_stereo3d.c", "libavfilter"),
+ ( "video/filter/vf_stereo3d.c" ),
( "video/filter/vf_sub.c" ),
( "video/filter/vf_vapoursynth.c", "vapoursynth-core" ),
( "video/filter/vf_vavpp.c", "vaapi"),
( "video/filter/vf_vdpaupp.c", "vdpau" ),
( "video/filter/vf_vdpaurb.c", "vdpau" ),
- ( "video/filter/vf_yadif.c", "libavfilter"),
+ ( "video/filter/vf_yadif.c" ),
( "video/out/aspect.c" ),
( "video/out/bitmap_packer.c" ),
( "video/out/cocoa/video_view.m", "cocoa" ),
@@ -315,11 +324,21 @@ def build(ctx):
( "video/out/cocoa_common.m", "cocoa" ),
( "video/out/dither.c" ),
( "video/out/filter_kernels.c" ),
- ( "video/out/opengl/cocoa.c", "gl-cocoa" ),
( "video/out/opengl/common.c", "gl" ),
- ( "video/out/opengl/rpi.c", "rpi" ),
+ ( "video/out/opengl/context.c", "gl" ),
+ ( "video/out/opengl/context_angle.c", "egl-angle" ),
+ ( "video/out/opengl/context_cocoa.c", "gl-cocoa" ),
+ ( "video/out/opengl/context_drm_egl.c", "egl-drm" ),
+ ( "video/out/opengl/context_dxinterop.c","gl-dxinterop" ),
+ ( "video/out/opengl/context_rpi.c", "rpi" ),
+ ( "video/out/opengl/context_wayland.c", "gl-wayland" ),
+ ( "video/out/opengl/context_w32.c", "gl-win32" ),
+ ( "video/out/opengl/context_x11.c", "gl-x11" ),
+ ( "video/out/opengl/context_x11egl.c", "egl-x11" ),
+ ( "video/out/opengl/egl_helpers.c", "egl-helpers" ),
( "video/out/opengl/hwdec.c", "gl" ),
( "video/out/opengl/hwdec_dxva2.c", "gl-win32" ),
+ ( "video/out/opengl/hwdec_dxva2gldx.c", "gl-dxinterop" ),
( "video/out/opengl/hwdec_vaegl.c", "vaapi-egl" ),
( "video/out/opengl/hwdec_vaglx.c", "vaapi-glx" ),
( "video/out/opengl/hwdec_osx.c", "videotoolbox-gl" ),
@@ -331,13 +350,6 @@ def build(ctx):
( "video/out/opengl/utils.c", "gl" ),
( "video/out/opengl/video.c", "gl" ),
( "video/out/opengl/video_shaders.c", "gl" ),
- ( "video/out/opengl/w32.c", "gl-win32" ),
- ( "video/out/opengl/angle.c", "egl-angle" ),
- ( "video/out/opengl/dxinterop.c", "gl-dxinterop" ),
- ( "video/out/opengl/wayland.c", "gl-wayland" ),
- ( "video/out/opengl/x11.c", "gl-x11" ),
- ( "video/out/opengl/x11egl.c", "egl-x11" ),
- ( "video/out/opengl/drm_egl.c", "egl-drm" ),
( "video/out/vo.c" ),
( "video/out/vo_caca.c", "caca" ),
( "video/out/vo_drm.c", "drm" ),
@@ -385,8 +397,10 @@ def build(ctx):
( "osdep/glob-win.c", "glob-win32-replacement" ),
( "osdep/w32_keyboard.c", "os-win32" ),
( "osdep/w32_keyboard.c", "os-cygwin" ),
+ ( "osdep/windows_utils.c", "win32" ),
( "osdep/mpv.rc", "win32-executable" ),
( "osdep/win32/pthread.c", "win32-internal-pthreads"),
+ ( "osdep/android/strnlen.c", "android"),
## tree_allocator
"ta/ta.c", "ta/ta_talloc.c", "ta/ta_utils.c"
@@ -436,7 +450,7 @@ def build(ctx):
features = "c cprogram",
install_path = ctx.env.BINDIR
)
- for f in ['example.conf', 'input.conf', 'mplayer-input.conf', \
+ for f in ['mpv.conf', 'input.conf', 'mplayer-input.conf', \
'restore-old-bindings.conf']:
ctx.install_as(os.path.join(ctx.env.DOCDIR, f),
os.path.join('etc/', f))
@@ -481,17 +495,24 @@ def build(ctx):
features += "cshlib syms"
else:
features += "cstlib"
- ctx(
- target = "mpv",
- source = ctx.filtered_sources(sources),
- use = ctx.dependencies_use(),
- includes = [ctx.bldnode.abspath(), ctx.srcnode.abspath()] + \
- ctx.dependencies_includes(),
- features = features,
- export_symbols_def = "libmpv/mpv.def",
- install_path = ctx.env.LIBDIR,
- vnum = libversion,
- )
+
+ libmpv_kwargs = {
+ "target": "mpv",
+ "source": ctx.filtered_sources(sources),
+ "use": ctx.dependencies_use(),
+ "includes": [ctx.bldnode.abspath(), ctx.srcnode.abspath()] + \
+ ctx.dependencies_includes(),
+ "features": features,
+ "export_symbols_def": "libmpv/mpv.def",
+ "install_path": ctx.env.LIBDIR,
+ }
+
+ if not ctx.dependency_satisfied('android'):
+ # for all other configurations we want SONAME to be used
+ libmpv_kwargs["vnum"] = libversion
+
+ ctx(**libmpv_kwargs)
+
if build_shared:
_build_libmpv(True)
if build_static:
@@ -532,6 +553,9 @@ def build(ctx):
features = 'c cshlib',
install_path = ctx.env.LIBDIR + '/mpv' )
+ if ctx.dependency_satisfied('html-build'):
+ _build_html(ctx)
+
if ctx.dependency_satisfied('manpage-build'):
_build_man(ctx)
@@ -550,7 +574,8 @@ def build(ctx):
ctx.env.DATADIR + '/applications',
['etc/mpv.desktop'] )
- ctx.install_files(ctx.env.CONFDIR, ['etc/encoding-profiles.conf'] )
+ if ctx.dependency_satisfied('encoding'):
+ ctx.install_files(ctx.env.CONFDIR, ['etc/encoding-profiles.conf'] )
for size in '16x16 32x32 64x64'.split():
ctx.install_as(