summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlessandro Ghedini <alessandro@ghedini.me>2015-04-25 15:06:27 +0200
committerAlessandro Ghedini <alessandro@ghedini.me>2015-04-25 15:06:27 +0200
commit01321227f04467e2d4d94f340641bbac0e5f1a13 (patch)
tree5e3bd188c6a06dfa5c9bc142f005fa006db697af
parent598a1188df1c06e2f108de0a61b13c908ffd9369 (diff)
Imported Upstream version 0.9.0
-rw-r--r--.gitignore1
-rw-r--r--.travis.yml2
-rw-r--r--DOCS/client-api-changes.rst9
-rw-r--r--DOCS/client_api_examples/qml/main.cpp3
-rw-r--r--DOCS/client_api_examples/qml_direct/main.cpp13
-rw-r--r--DOCS/client_api_examples/qml_direct/main.h1
-rw-r--r--DOCS/compile-windows.md1
-rw-r--r--DOCS/contribute.md3
-rw-r--r--DOCS/man/af.rst51
-rw-r--r--DOCS/man/ao.rst38
-rw-r--r--DOCS/man/input.rst131
-rw-r--r--DOCS/man/ipc.rst3
-rw-r--r--DOCS/man/lua.rst26
-rw-r--r--DOCS/man/mpv.rst87
-rw-r--r--DOCS/man/options.rst274
-rw-r--r--DOCS/man/vf.rst159
-rw-r--r--DOCS/man/vo.rst331
-rw-r--r--DOCS/mplayer-changes.rst7
-rw-r--r--DOCS/waf-buildsystem.rst5
-rw-r--r--README.md68
-rw-r--r--RELEASE_NOTES374
-rw-r--r--TOOLS/lua/README.md2
-rw-r--r--TOOLS/lua/audio-hotplug-test.lua13
-rw-r--r--TOOLS/lua/autoload.lua41
-rw-r--r--TOOLS/lua/ontop-playback.lua19
-rw-r--r--TOOLS/lua/pause-when-minimize.lua2
-rw-r--r--TOOLS/lua/youtube-starttime.lua34
-rw-r--r--TOOLS/osxbundle/mpv.app/Contents/Resources/mpv.conf3
-rwxr-xr-xTOOLS/umpv2
-rwxr-xr-xTOOLS/youtube-dl_mpv.sh22
-rwxr-xr-xTOOLS/zsh.pl97
-rw-r--r--VERSION2
-rw-r--r--audio/audio.c4
-rw-r--r--audio/chmap.c2
-rw-r--r--audio/decode/ad.h9
-rw-r--r--audio/decode/ad_lavc.c16
-rw-r--r--audio/decode/ad_mpg123.c304
-rw-r--r--audio/decode/ad_spdif.c19
-rw-r--r--audio/decode/dec_audio.c17
-rw-r--r--audio/decode/dec_audio.h9
-rw-r--r--audio/filter/af.c59
-rw-r--r--audio/filter/af.h14
-rw-r--r--audio/filter/af_bs2b.c12
-rw-r--r--audio/filter/af_center.c9
-rw-r--r--audio/filter/af_channels.c9
-rw-r--r--audio/filter/af_delay.c9
-rw-r--r--audio/filter/af_drc.c9
-rw-r--r--audio/filter/af_dummy.c9
-rw-r--r--audio/filter/af_equalizer.c9
-rw-r--r--audio/filter/af_export.c9
-rw-r--r--audio/filter/af_extrastereo.c9
-rw-r--r--audio/filter/af_forcespeed.c71
-rw-r--r--audio/filter/af_hrtf.c9
-rw-r--r--audio/filter/af_hrtf.h9
-rw-r--r--audio/filter/af_karaoke.c9
-rw-r--r--audio/filter/af_ladspa.c9
-rw-r--r--audio/filter/af_lavcac3enc.c9
-rw-r--r--audio/filter/af_lavfi.c28
-rw-r--r--audio/filter/af_lavrresample.c119
-rw-r--r--audio/filter/af_pan.c9
-rw-r--r--audio/filter/af_rubberband.c225
-rw-r--r--audio/filter/af_scaletempo.c70
-rw-r--r--audio/filter/af_sinesuppress.c9
-rw-r--r--audio/filter/af_sub.c9
-rw-r--r--audio/filter/af_surround.c11
-rw-r--r--audio/filter/af_sweep.c9
-rw-r--r--audio/filter/af_volume.c11
-rw-r--r--audio/filter/dsp.h9
-rw-r--r--audio/filter/equalizer.h9
-rw-r--r--audio/filter/filter.c9
-rw-r--r--audio/filter/filter.h9
-rw-r--r--audio/filter/tools.c41
-rw-r--r--audio/filter/window.c9
-rw-r--r--audio/filter/window.h9
-rw-r--r--audio/fmt-conversion.c11
-rw-r--r--audio/fmt-conversion.h9
-rw-r--r--audio/format.c38
-rw-r--r--audio/format.h16
-rw-r--r--audio/mixer.c59
-rw-r--r--audio/mixer.h9
-rw-r--r--audio/out/ao.c137
-rw-r--r--audio/out/ao.h21
-rw-r--r--audio/out/ao_alsa.c55
-rw-r--r--audio/out/ao_coreaudio.c125
-rw-r--r--audio/out/ao_coreaudio_exclusive.c163
-rw-r--r--audio/out/ao_coreaudio_utils.c36
-rw-r--r--audio/out/ao_coreaudio_utils.h3
-rw-r--r--audio/out/ao_dsound.c9
-rw-r--r--audio/out/ao_jack.c9
-rw-r--r--audio/out/ao_lavc.c11
-rw-r--r--audio/out/ao_null.c9
-rw-r--r--audio/out/ao_openal.c9
-rw-r--r--audio/out/ao_oss.c11
-rw-r--r--audio/out/ao_pcm.c11
-rw-r--r--audio/out/ao_pulse.c58
-rw-r--r--audio/out/ao_rsound.c9
-rw-r--r--audio/out/ao_sdl.c7
-rw-r--r--audio/out/ao_sndio.c22
-rw-r--r--audio/out/ao_wasapi.c233
-rwxr-xr-xaudio/out/ao_wasapi.h43
-rwxr-xr-xaudio/out/ao_wasapi_changenotify.c143
-rwxr-xr-xaudio/out/ao_wasapi_utils.c873
-rwxr-xr-xaudio/out/ao_wasapi_utils.h13
-rw-r--r--audio/out/internal.h32
-rw-r--r--common/av_log.c9
-rw-r--r--common/common.c9
-rw-r--r--common/common.h9
-rw-r--r--common/encode_lavc.c7
-rw-r--r--common/encode_lavc.h7
-rw-r--r--common/msg.c20
-rw-r--r--common/msg.h9
-rw-r--r--common/msg_control.h1
-rw-r--r--common/playlist.c24
-rw-r--r--common/playlist.h8
-rw-r--r--common/version.c9
-rw-r--r--demux/codec_tags.c3
-rw-r--r--demux/demux.c140
-rw-r--r--demux/demux.h34
-rw-r--r--demux/demux_cue.c19
-rw-r--r--demux/demux_disc.c7
-rw-r--r--demux/demux_edl.c35
-rw-r--r--demux/demux_lavf.c157
-rw-r--r--demux/demux_libass.c1
-rw-r--r--demux/demux_mf.c9
-rw-r--r--demux/demux_mkv.c110
-rw-r--r--demux/demux_mkv_timeline.c71
-rw-r--r--demux/demux_playlist.c81
-rw-r--r--demux/demux_rar.c62
-rw-r--r--demux/demux_raw.c10
-rw-r--r--demux/demux_subreader.c10
-rw-r--r--demux/ebml.c9
-rw-r--r--demux/ebml.h9
-rw-r--r--demux/matroska.h9
-rw-r--r--demux/packet.h9
-rw-r--r--demux/stheader.h13
-rw-r--r--demux/timeline.c8
-rw-r--r--demux/timeline.h1
-rw-r--r--etc/input.conf28
-rw-r--r--etc/mplayer-input.conf16
-rw-r--r--etc/mpv.desktop3
-rw-r--r--input/cmd_list.c65
-rw-r--r--input/cmd_list.h12
-rw-r--r--input/cmd_parse.c9
-rw-r--r--input/cmd_parse.h10
-rw-r--r--input/input.c37
-rw-r--r--input/input.h11
-rw-r--r--input/ipc.c38
-rw-r--r--input/joystick.c216
-rw-r--r--input/keycodes.c46
-rw-r--r--input/keycodes.h59
-rw-r--r--input/lirc.c167
-rw-r--r--libmpv/client.h37
-rw-r--r--libmpv/mpv.def2
-rw-r--r--libmpv/opengl_cb.h57
-rw-r--r--misc/bstr.c9
-rw-r--r--misc/bstr.h9
-rwxr-xr-xold-configure80
-rw-r--r--old-makefile16
-rw-r--r--options/m_config.c23
-rw-r--r--options/m_config.h9
-rw-r--r--options/m_option.c290
-rw-r--r--options/m_option.h28
-rw-r--r--options/m_property.c12
-rw-r--r--options/m_property.h9
-rw-r--r--options/options.c121
-rw-r--r--options/options.h13
-rw-r--r--options/parse_commandline.c40
-rw-r--r--options/parse_commandline.h14
-rw-r--r--options/parse_configfile.c364
-rw-r--r--options/parse_configfile.h12
-rw-r--r--options/path.c9
-rw-r--r--options/path.h9
-rw-r--r--osdep/io.c36
-rw-r--r--osdep/io.h10
-rw-r--r--osdep/macosx_application.h3
-rw-r--r--osdep/macosx_application.m8
-rw-r--r--osdep/macosx_application_objc.h3
-rw-r--r--osdep/macosx_events.h4
-rw-r--r--osdep/mpv.rc11
-rw-r--r--osdep/path-macosx.m3
-rw-r--r--osdep/subprocess-posix.c13
-rw-r--r--osdep/subprocess-win.c22
-rw-r--r--osdep/subprocess.c57
-rw-r--r--osdep/subprocess.h3
-rw-r--r--osdep/terminal-unix.c3
-rw-r--r--osdep/terminal-win.c59
-rw-r--r--osdep/terminal.h12
-rw-r--r--osdep/timer-linux.c9
-rw-r--r--osdep/timer-win2.c9
-rw-r--r--osdep/timer.h9
-rw-r--r--osdep/win32-console-wrapper.c4
-rw-r--r--player/audio.c101
-rw-r--r--player/client.c33
-rw-r--r--player/client.h1
-rw-r--r--player/command.c511
-rw-r--r--player/command.h16
-rw-r--r--player/configfiles.c32
-rw-r--r--player/core.h28
-rw-r--r--player/discnav.c23
-rw-r--r--player/loadfile.c71
-rw-r--r--player/lua.c44
-rw-r--r--player/lua/defaults.lua4
-rw-r--r--player/lua/osc.lua12
-rw-r--r--player/lua/ytdl_hook.lua38
-rw-r--r--player/main-fn-unix.c (renamed from player/main_fn.c)0
-rw-r--r--player/main-fn-win.c55
-rw-r--r--player/main.c133
-rw-r--r--player/misc.c9
-rw-r--r--player/osd.c9
-rw-r--r--player/playloop.c42
-rw-r--r--player/screenshot.c33
-rw-r--r--player/screenshot.h12
-rw-r--r--player/sub.c11
-rw-r--r--player/video.c167
-rw-r--r--stream/ai_alsa1x.c9
-rw-r--r--stream/ai_oss.c9
-rw-r--r--stream/audio_in.c9
-rw-r--r--stream/audio_in.h9
-rw-r--r--stream/cache.c50
-rw-r--r--stream/cookies.c9
-rw-r--r--stream/cookies.h9
-rw-r--r--stream/discnav.h10
-rw-r--r--stream/frequencies.c9
-rw-r--r--stream/frequencies.h9
-rw-r--r--stream/stream.c11
-rw-r--r--stream/stream.h10
-rw-r--r--stream/stream_avdevice.c3
-rw-r--r--stream/stream_bluray.c17
-rw-r--r--stream/stream_cdda.c19
-rw-r--r--stream/stream_dvb.c2
-rw-r--r--stream/stream_dvd.c11
-rw-r--r--stream/stream_dvd_common.c9
-rw-r--r--stream/stream_dvd_common.h9
-rw-r--r--stream/stream_dvdnav.c19
-rw-r--r--stream/stream_file.c38
-rw-r--r--stream/stream_lavf.c20
-rw-r--r--stream/stream_mf.c9
-rw-r--r--stream/stream_null.c11
-rw-r--r--stream/stream_pvr.c9
-rw-r--r--stream/stream_rar.c6
-rw-r--r--stream/stream_smb.c11
-rw-r--r--stream/stream_tv.c9
-rw-r--r--stream/tv.c9
-rw-r--r--stream/tv.h9
-rw-r--r--stream/tvi_def.h9
-rw-r--r--stream/tvi_dummy.c9
-rw-r--r--stream/tvi_v4l2.c11
-rw-r--r--sub/ass_mp.c83
-rw-r--r--sub/ass_mp.h12
-rw-r--r--sub/dec_sub.c15
-rw-r--r--sub/draw_bmp.c28
-rw-r--r--sub/find_subfiles.c2
-rw-r--r--sub/find_subfiles.h9
-rw-r--r--sub/img_convert.c9
-rw-r--r--sub/osd.c106
-rw-r--r--sub/osd.h17
-rw-r--r--sub/osd_libass.c13
-rw-r--r--sub/osd_state.h3
-rw-r--r--sub/sd.h3
-rw-r--r--sub/sd_ass.c109
-rw-r--r--sub/sd_lavc.c45
-rw-r--r--sub/sd_microdvd.c9
-rw-r--r--sub/sd_srt.c9
-rw-r--r--ta/ta_utils.c4
-rw-r--r--test/gl_video.c42
-rw-r--r--test/test_helpers.h4
-rwxr-xr-xtravis-deps6
-rw-r--r--video/csputils.c199
-rw-r--r--video/csputils.h48
-rw-r--r--video/decode/dec_video.c43
-rw-r--r--video/decode/dec_video.h10
-rw-r--r--video/decode/dxva2.c5
-rw-r--r--video/decode/lavc.h2
-rw-r--r--video/decode/rpi.c56
-rw-r--r--video/decode/vd.h9
-rw-r--r--video/decode/vd_lavc.c30
-rw-r--r--video/decode/vdpau.c27
-rw-r--r--video/filter/vf.c16
-rw-r--r--video/filter/vf.h10
-rw-r--r--video/filter/vf_crop.c16
-rw-r--r--video/filter/vf_delogo.c9
-rw-r--r--video/filter/vf_dlopen.c16
-rw-r--r--video/filter/vf_dsize.c9
-rw-r--r--video/filter/vf_eq.c13
-rw-r--r--video/filter/vf_expand.c28
-rw-r--r--video/filter/vf_flip.c9
-rw-r--r--video/filter/vf_format.c129
-rw-r--r--video/filter/vf_gradfun.c9
-rw-r--r--video/filter/vf_hqdn3d.c9
-rw-r--r--video/filter/vf_lavfi.c5
-rw-r--r--video/filter/vf_mirror.c97
-rw-r--r--video/filter/vf_noformat.c9
-rw-r--r--video/filter/vf_noise.c9
-rw-r--r--video/filter/vf_pullup.c9
-rw-r--r--video/filter/vf_rotate.c9
-rw-r--r--video/filter/vf_scale.c9
-rw-r--r--video/filter/vf_screenshot.c75
-rw-r--r--video/filter/vf_stereo3d.c23
-rw-r--r--video/filter/vf_sub.c10
-rw-r--r--video/filter/vf_unsharp.c9
-rw-r--r--video/filter/vf_vapoursynth.c53
-rw-r--r--video/filter/vf_vdpaupp.c4
-rw-r--r--video/filter/vf_yadif.c9
-rw-r--r--video/fmt-conversion.c12
-rw-r--r--video/fmt-conversion.h9
-rw-r--r--video/hwdec.h1
-rw-r--r--video/image_writer.c159
-rw-r--r--video/image_writer.h19
-rw-r--r--video/img_format.c12
-rw-r--r--video/img_format.h12
-rw-r--r--video/memcpy_pic.h79
-rw-r--r--video/mp_image.c185
-rw-r--r--video/mp_image.h37
-rw-r--r--video/mp_image_pool.c3
-rw-r--r--video/out/aspect.c9
-rw-r--r--video/out/aspect.h9
-rw-r--r--video/out/bitmap_packer.c12
-rw-r--r--video/out/cocoa/events_view.m3
-rw-r--r--video/out/cocoa/window.m10
-rw-r--r--video/out/cocoa_common.m114
-rw-r--r--video/out/d3d_shader_420p.h (renamed from video/out/d3d_shader_yuv.h)284
-rw-r--r--video/out/d3d_shader_nv12.h123
-rw-r--r--video/out/d3d_shader_yuv.hlsl21
-rw-r--r--video/out/dither.c50
-rw-r--r--video/out/drm_common.c149
-rw-r--r--video/out/drm_common.h36
-rw-r--r--video/out/filter_kernels.c281
-rw-r--r--video/out/filter_kernels.h34
-rw-r--r--video/out/gl_cocoa.c9
-rw-r--r--video/out/gl_common.c84
-rw-r--r--video/out/gl_common.h21
-rw-r--r--video/out/gl_header_fixes.h9
-rw-r--r--video/out/gl_hwdec.c9
-rw-r--r--video/out/gl_hwdec.h7
-rw-r--r--video/out/gl_hwdec_vaglx.c110
-rw-r--r--video/out/gl_hwdec_vda.c3
-rw-r--r--video/out/gl_hwdec_vdpau.c19
-rw-r--r--video/out/gl_osd.c220
-rw-r--r--video/out/gl_osd.h39
-rw-r--r--video/out/gl_rpi.c219
-rw-r--r--video/out/gl_utils.c513
-rw-r--r--video/out/gl_utils.h61
-rw-r--r--video/out/gl_video.c2992
-rw-r--r--video/out/gl_video.h43
-rw-r--r--video/out/gl_video_shaders.glsl529
-rw-r--r--video/out/gl_w32.c51
-rw-r--r--video/out/gl_wayland.c12
-rw-r--r--video/out/gl_x11.c10
-rw-r--r--video/out/gl_x11egl.c10
-rw-r--r--video/out/vo.c104
-rw-r--r--video/out/vo.h20
-rw-r--r--video/out/vo_caca.c10
-rw-r--r--video/out/vo_direct3d.c163
-rw-r--r--video/out/vo_drm.c641
-rw-r--r--video/out/vo_image.c8
-rw-r--r--video/out/vo_lavc.c27
-rw-r--r--video/out/vo_null.c9
-rw-r--r--video/out/vo_opengl.c81
-rw-r--r--video/out/vo_opengl_cb.c58
-rw-r--r--video/out/vo_rpi.c575
-rw-r--r--video/out/vo_sdl.c90
-rw-r--r--video/out/vo_vaapi.c22
-rw-r--r--video/out/vo_vdpau.c57
-rw-r--r--video/out/vo_wayland.c66
-rw-r--r--video/out/vo_x11.c11
-rw-r--r--video/out/vo_xv.c21
-rw-r--r--video/out/w32_common.c81
-rw-r--r--video/out/w32_common.h9
-rw-r--r--video/out/wayland_common.c42
-rw-r--r--video/out/wayland_common.h5
-rw-r--r--video/out/win_state.c2
-rw-r--r--video/out/x11_common.c83
-rw-r--r--video/out/x11_common.h15
-rw-r--r--video/sws_utils.c9
-rw-r--r--video/vdpau.c5
-rw-r--r--waftools/checks/custom.py2
-rw-r--r--waftools/detections/compiler.py6
-rw-r--r--waftools/generators/headers.py12
-rw-r--r--waftools/generators/sources.py6
-rw-r--r--wscript188
-rw-r--r--wscript_build.py29
381 files changed, 12053 insertions, 8717 deletions
diff --git a/.gitignore b/.gitignore
index b8ff3a8..159e409 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,7 +14,6 @@
/input/input_conf.h
/tags
/TAGS
-/video/out/gl_video_shaders.h
/video/out/x11_icon.inc
/demux/ebml_defs.c
/demux/ebml_types.h
diff --git a/.travis.yml b/.travis.yml
index 31a5492..dab4b6d 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,7 +2,7 @@ language: c
os:
- linux
-# - osx
+ - osx
env:
matrix:
- LIBAV=libav-stable
diff --git a/DOCS/client-api-changes.rst b/DOCS/client-api-changes.rst
index 842dd85..9b3f21c 100644
--- a/DOCS/client-api-changes.rst
+++ b/DOCS/client-api-changes.rst
@@ -25,6 +25,15 @@ API changes
::
+ 1.17 - add MPV_FORMAT_BYTE_ARRAY
+ --- mpv 0.9.0 will be released ---
+ 1.16 - add mpv_opengl_cb_report_flip()
+ - introduce mpv_opengl_cb_draw() and deprecate mpv_opengl_cb_render()
+ 1.15 - mpv_initialize() will now load config files. This requires setting
+ the "config" and "config-dir" options. In particular, it will load
+ mpv.conf.
+ - minor backwards-compatible change to the "seek" and "screenshot"
+ commands (new flag syntax, old additional args deprecated)
--- mpv 0.8.0 is released ---
1.14 - add mpv_wait_async_requests()
- the --msg-level option changes its native type from a flat string to
diff --git a/DOCS/client_api_examples/qml/main.cpp b/DOCS/client_api_examples/qml/main.cpp
index 69552e9..2122cd8 100644
--- a/DOCS/client_api_examples/qml/main.cpp
+++ b/DOCS/client_api_examples/qml/main.cpp
@@ -46,9 +46,8 @@ public:
void render()
{
QOpenGLFramebufferObject *fbo = framebufferObject();
- int vp[4] = {0, 0, fbo->width(), fbo->height()};
window->resetOpenGLState();
- mpv_opengl_cb_render(mpv_gl, fbo->handle(), vp);
+ mpv_opengl_cb_draw(mpv_gl, fbo->handle(), fbo->width(), fbo->height());
window->resetOpenGLState();
}
};
diff --git a/DOCS/client_api_examples/qml_direct/main.cpp b/DOCS/client_api_examples/qml_direct/main.cpp
index 5f1f146..37c6119 100644
--- a/DOCS/client_api_examples/qml_direct/main.cpp
+++ b/DOCS/client_api_examples/qml_direct/main.cpp
@@ -39,15 +39,13 @@ void MpvRenderer::paint()
{
window->resetOpenGLState();
- // Render to the whole window.
- int vp[4] = {0, 0, size.width(), -size.height()};
-
// This uses 0 as framebuffer, which indicates that mpv will render directly
// to the frontbuffer. Note that mpv will always switch framebuffers
// explicitly. Some QWindow setups (such as using QQuickWidget) actually
// want you to render into a FBO in the beforeRendering() signal, and this
// code won't work there.
- mpv_opengl_cb_render(mpv_gl, 0, vp);
+ // The negation is used for rendering with OpenGL's flipped coordinates.
+ mpv_opengl_cb_draw(mpv_gl, 0, size.width(), -size.height());
window->resetOpenGLState();
}
@@ -96,6 +94,8 @@ void MpvObject::handleWindowChanged(QQuickWindow *win)
this, &MpvObject::sync, Qt::DirectConnection);
connect(win, &QQuickWindow::sceneGraphInvalidated,
this, &MpvObject::cleanup, Qt::DirectConnection);
+ connect(win, &QQuickWindow::frameSwapped,
+ this, &MpvObject::swapped, Qt::DirectConnection);
win->setClearBeforeRendering(false);
}
@@ -110,6 +110,11 @@ void MpvObject::sync()
renderer->size = window()->size() * window()->devicePixelRatio();
}
+void MpvObject::swapped()
+{
+ mpv_opengl_cb_report_flip(mpv_gl, 0);
+}
+
void MpvObject::cleanup()
{
if (renderer) {
diff --git a/DOCS/client_api_examples/qml_direct/main.h b/DOCS/client_api_examples/qml_direct/main.h
index 66fe8c9..b0310ff 100644
--- a/DOCS/client_api_examples/qml_direct/main.h
+++ b/DOCS/client_api_examples/qml_direct/main.h
@@ -37,6 +37,7 @@ public:
public slots:
void command(const QVariant& params);
void sync();
+ void swapped();
void cleanup();
signals:
void onUpdate();
diff --git a/DOCS/compile-windows.md b/DOCS/compile-windows.md
index fbcda1a..af161fd 100644
--- a/DOCS/compile-windows.md
+++ b/DOCS/compile-windows.md
@@ -79,6 +79,7 @@ export PATH=/opt/mxe/usr/bin/:$PATH
cd ..
git clone https://github.com/mpv-player/mpv.git
cd mpv
+python ./bootstrap.py
DEST_OS=win32 TARGET=i686-w64-mingw32.static ./waf configure
# Or, if 64 bit version,
# DEST_OS=win32 TARGET=x86_64-w64-mingw32.static ./waf configure
diff --git a/DOCS/contribute.md b/DOCS/contribute.md
index 224c524..11b5e33 100644
--- a/DOCS/contribute.md
+++ b/DOCS/contribute.md
@@ -14,6 +14,9 @@ Sending patches
``git format-patch``. diffs posted as pastebins (especially if the http link
returns HTML) just cause extra work for everyone, because they lack commit
message and authorship information.
+- When creating pull requests, be sure to test your changes. If you didn't, please
+ say so in the pull request message.
+
- Write informative commit messages. Use present tense to describe the
situation with the patch applied, and past tense for the situation before
the change.
diff --git a/DOCS/man/af.rst b/DOCS/man/af.rst
index a569794..96e5068 100644
--- a/DOCS/man/af.rst
+++ b/DOCS/man/af.rst
@@ -63,7 +63,7 @@ Available filters are:
Set AVOptions on the SwrContext or AVAudioResampleContext. These should
be documented by FFmpeg or Libav.
-``lavcac3enc[=tospdif[:bitrate[:minchn]]]``
+``lavcac3enc[=tospdif[:bitrate[:minch]]]``
Encode multi-channel audio to AC-3 at runtime using libavcodec. Supports
16-bit native-endian input format, maximum 6 channels. The output is
big-endian when outputting a raw AC-3 stream, native-endian when
@@ -92,8 +92,8 @@ Available filters are:
:5ch: 448
:6ch: 448
- ``minchn=<n>``
- If the input channel number is less than ``<minchn>``, the filter will
+ ``minch=<n>``
+ If the input channel number is less than ``<minch>``, the filter will
detach itself (default: 3).
``sweep[=speed]``
@@ -225,14 +225,21 @@ Available filters are:
automatically up- and downmix standard channel layouts.
``format=format:srate:channels:out-format:out-srate:out-channels``
- Force a specific audio format/configuration without actually changing the
- audio data. Keep in mind that the filter system might auto-insert actual
- conversion filters before or after this filter if needed.
+ Does not do any format conversion itself. Rather, it may cause the
+ filter system to insert necessary conversion filters before or after this
+ filter if needed. It is primarily useful for controlling the audio format
+ going into other filters. To specify the format for audio output, see
+ ``--audio-format``, ``--audio-samplerate``, and ``--audio-channels``. This
+ filter is able to force a particular format, whereas ``--audio-*``
+ may be overridden by the ao based on output compatibility.
All parameters are optional. The first 3 parameters restrict what the filter
- accepts as input. The ``out-`` parameters change the audio format, without
- actually doing a conversion. The data will be 'reinterpreted' by the
- filters or audio outputs following this filter.
+ accepts as input. They will therefore cause conversion filters to be
+ inserted before this one. The ``out-`` parameters tell the filters or audio
+ outputs following this filter how to interpret the data without actually
+ doing a conversion. Setting these will probably just break things unless you
+ really know you want this for some reason, such as testing or dealing with
+ broken media.
``<format>``
Force conversion to this format. Use ``--af=format=format=help`` to get
@@ -252,14 +259,9 @@ Available filters are:
``<out-channels>``
- See also ``--audio-format``, ``--audio-samplerate``, and
- ``--audio-channels`` for related options. Keep in mind that
- ``--audio-channels`` does not actually force the number of
- channels in most cases, while this filter can do this.
-
- *NOTE*: this filter used to be named ``force``. Also, unlike the old
- ``format`` filter, this does not do any actual conversion anymore.
- Conversion is done by other, automatically inserted filters.
+ *NOTE*: this filter used to be named ``force``. The old ``format`` filter
+ used to do conversion itself, unlike this one which lets the filter system
+ handle the conversion.
``convert24``
Filter for internal use only. Converts between 24-bit and 32-bit sample
@@ -601,6 +603,20 @@ Available filters are:
Changing playback speed would change pitch, leaving audio tempo at
1.2x.
+``rubberband``
+ High quality pitch correction with librubberband. This can be used in place
+ of ``scaletempo``, and will be used to adjust audio pitch when playing
+ at speed different from normal.
+
+ This filter has a number of sub-options. You can list them with
+ ``mpv --af=rubberband=help``. This will also show the default values
+ for each option. The options are not documented here, because they are
+ merely passed to librubberband. Look at the librubberband documentation
+ to learn what each option does:
+ http://breakfastquay.com/rubberband/code-doc/classRubberBand_1_1RubberBandStretcher.html
+ (The mapping of the mpv rubberband filter sub-option names and values to
+ those of librubberband follows a simple pattern: ``"Option" + Name + Value``.)
+
``lavfi=graph``
Filter audio using FFmpeg's libavfilter.
@@ -615,4 +631,3 @@ Available filters are:
``o=<string>``
AVOptions.
-
diff --git a/DOCS/man/ao.rst b/DOCS/man/ao.rst
index fdbefbb..534f625 100644
--- a/DOCS/man/ao.rst
+++ b/DOCS/man/ao.rst
@@ -55,6 +55,14 @@ Available audio output drivers are:
Allow output of non-interleaved formats (if the audio decoder uses
this format). Currently disabled by default, because some popular
ALSA plugins are utterly broken with non-interleaved formats.
+ ``ingore-chmap``
+ Don't read or set the channel map of the ALSA device - only request the
+ required number of channels, and then pass the audio as-is to it. This
+ option most likely should not be used. It can be useful for debugging,
+ or for static setups with a specially engineered ALSA configuration (in
+ this case you should always force the same layout with ``--audio-channels``,
+ or it will work only for files which use the layout implicit to your
+ ALSA device).
.. note::
@@ -99,8 +107,10 @@ Available audio output drivers are:
``--audio-device`` to select the device (use ``--audio-device=help``
to get a list of all devices and their mpv name).
- You can also try
- `Using the upmix plugin <https://github.com/mpv-player/mpv/wiki/ALSA:-Surround-Sound-and-Upmixing>`_.
+ You can also try `using the upmix plugin <http://git.io/vfuAy>`_.
+ This setup enables multichannel audio on the ``default`` device
+ with automatic upmixing with shared access, so playing stereo
+ and multichannel audio at the same time will work as expected.
``oss``
OSS audio output driver
@@ -294,19 +304,19 @@ Available audio output drivers are:
``wasapi``
Audio output to the Windows Audio Session API.
+ ``exclusive``
+ Requests exclusive, direct hardware access. By definition prevents
+ sound playback of any other program until mpv exits.
``device=<id>``
Uses the requested endpoint instead of the system's default audio
- endpoint. Both the number and the ID String are valid; the ID String
- is guaranteed to not change unless the driver is uninstalled.
+ endpoint. Both an ordinal number (0,1,2,...) and the GUID
+ String are valid; the GUID string is guaranteed to not change
+ unless the driver is uninstalled.
- Also supports searching active devices by name. If more than one
- device matches the name, refuses loading it.
+ Also supports searching active devices by human readable name. If more
+ than one device matches the name, refuses loading it.
- To get a list of the valid devices, give ``help`` as the id. The
- list is the same as the ``list`` suboption, but stops the player
- initialization.
- ``exclusive``
- Requests exclusive, direct hardware access. By definition prevents
- sound playback of any other program until mpv exits.
- ``list``
- Lists all audio endpoints (output devices) present in the system.
+ This option is mostly deprecated in favour of the more general
+ ``--audio-device`` option. That said, ``--audio-device=help`` will give
+ a list of valid device GUIDs (prefixed with ``wasapi/``), as well as
+ their human readable names, which should work here.
diff --git a/DOCS/man/input.rst b/DOCS/man/input.rst
index 77b13ed..633f6a8 100644
--- a/DOCS/man/input.rst
+++ b/DOCS/man/input.rst
@@ -86,11 +86,11 @@ List of Input Commands
disabling default bindings, without disabling all bindings with
``--no-input-default-bindings``.
-``seek <seconds> [relative|absolute|absolute-percent|- [default-precise|exact|keyframes]]``
+``seek <seconds> [relative|absolute|absolute-percent|exact|keyframes]``
Change the playback position. By default, seeks by a relative amount of
seconds.
- The second argument sets the seek mode:
+ The second argument consists of flags controlling the seek mode:
relative (default)
Seek relative to current position (a negative value seeks backwards).
@@ -98,16 +98,19 @@ List of Input Commands
Seek to a given time.
absolute-percent
Seek to a given percent position.
-
- The third argument defines how exact the seek is:
-
- default-precise (default)
- Follow the default behavior as set by ``--hr-seek``, which by default
- does imprecise seeks (like ``keyframes``).
- exact
- Always do exact/hr/precise seeks (slow).
keyframes
Always restart playback at keyframe boundaries (fast).
+ exact
+ Always do exact/hr/precise seeks (slow).
+
+ Multiple flags can be combined, e.g.: ``absolute+keyframes``.
+
+ By default, ``keyframes`` is used for relative seeks, and ``exact`` is used
+ for absolute seeks.
+
+ Before mpv 0.9, the ``keyframes`` and ``exact`` flags had to be passed as
+ 3rd parameter (essentially using a space instead of ``+``). The 3rd
+ parameter is still parsed, but is considered deprecated.
``revert_seek [mode]``
Undoes the ``seek`` command, and some other commands that seek (but not
@@ -170,16 +173,12 @@ List of Input Commands
Save the contents of the mpv window. Typically scaled, with OSD and
subtitles. The exact behavior depends on the selected video output, and
if no support is available, this will act like ``video``.
-
- Second argument:
-
- <single> (default)
- Take a single screenshot.
<each-frame>
Take a screenshot each frame. Issue this command again to stop taking
screenshots. Note that you should disable frame-dropping when using
this mode - or you might receive duplicate images in cases when a
- frame was dropped.
+ frame was dropped. This flag can be combined with the other flags,
+ e.g. ``video+each-frame``.
``screenshot_to_file "<filename>" [subtitles|video|window]``
Take a screenshot and save it to a given file. The format of the file will
@@ -188,8 +187,7 @@ List of Input Commands
The second argument is like the first argument to ``screenshot``.
- This command tries to never overwrite files. If the file already exists,
- it fails.
+ If the file already exists, it's overwritten.
Like all input command parameters, the filename is subject to property
expansion as described in `Property Expansion`_.
@@ -400,6 +398,21 @@ List of Input Commands
``audio_reload [<id>]``
Reload the given audio tracks. See ``sub_reload`` command.
+``rescan_external_files [<mode>]``
+ Rescan external files according to the current ``--sub-auto`` and
+ ``--audio-file-auto`` settings. This can be used to auto-load external
+ files *after* the file was loaded.
+
+ The ``mode`` argument is one of the following:
+
+ <keep-selection> (default)
+ Do not change current track selections.
+
+ <reselect>
+ Select the default audio and video streams, which typically selects
+ external files with highest preference. (The implementation is not
+ perfect, and could be improved on request.)
+
Input Commands that are Possibly Subject to Change
--------------------------------------------------
@@ -618,6 +631,15 @@ Input Commands that are Possibly Subject to Change
unseekable streams that are going out of sync.
This command might be changed or removed in the future.
+``screenshot_raw [subtitles|video|window]``
+ Return a screenshot in memory. This can be used only through the client
+ API. The MPV_FORMAT_NODE_MAP returned by this command has the ``w``, ``h``,
+ ``stride`` fields set to obvious contents. A ``format`` field is set to
+ ``bgr0`` by default. This format is organized as ``B8G8R8X8`` (where ``B``
+ is the LSB). The contents of the padding ``X`` is undefined. The ``data``
+ field is of type MPV_FORMAT_BYTE_ARRAY with the actual image data. The image
+ is freed as soon as the result node is freed.
+
Undocumented commands: ``tv_last_channel`` (TV/DVB only),
``get_property`` (deprecated), ``ao_reload`` (experimental/internal).
@@ -906,6 +928,10 @@ Property list
a BD or DVD. Use the ``discnav menu`` command to actually enter or leave
menu mode.
+``disc-mouse-on-button``
+ Return ``yes`` when the mouse cursor is located on a button, or ``no``
+ when cursor is outside of any button for disc navigation.
+
``chapters``
Number of chapters.
@@ -1064,6 +1090,11 @@ Property list
guess is very unreliable, and often the property will not be available
at all, even if data is buffered.
+``demuxer-cache-time``
+ Approximate time of video buffered in the demuxer, in seconds. Same as
+ ``demuxer-cache-duration`` but returns the last timestamp of bufferred
+ data in demuxer.
+
``demuxer-cache-idle``
Returns ``yes`` if the demuxer is idle, which means the demuxer cache is
filled to the requested amount, and is currently not reading more data.
@@ -1109,9 +1140,6 @@ Property list
``audio-codec``
Audio codec selected for decoding.
-``audio-bitrate``
- Audio bitrate. This is probably a very bad guess in most cases.
-
``audio-samplerate``
Audio samplerate.
@@ -1208,9 +1236,6 @@ Property list
``video-codec``
Video codec selected for decoding.
-``video-bitrate``
- Video bitrate (a bad guess).
-
``width``, ``height``
Video size. This uses the size of the video as decoded, or if no video
frame has been decoded yet, the (possibly incorrect) container indicated
@@ -1255,6 +1280,9 @@ Property list
``video-params/primaries``
The primaries in use as string. (Exact values subject to change.)
+ ``video-params/gamma``
+ The gamma function in use as string. (Exact values subject to change.)
+
``video-params/chroma-location``
Chroma location as string. (Exact values subject to change.)
@@ -1325,6 +1353,13 @@ Property list
Names of the displays that the mpv window covers. On X11, these
are the xrandr names (LVDS1, HDMI1, DP1, VGA1, etc.).
+``display-fps``
+ The refresh rate of the current display. Currently, this is the lowest FPS
+ of any display covered by the video, as retrieved by the underlying system
+ APIs (e.g. xrandr on X11). It is not the measured FPS. It's not necessarily
+ available on all platforms. Note that any of the listed facts may change
+ any time without a warning.
+
``video-aspect`` (RW)
Video aspect, see ``--video-aspect``.
@@ -1381,8 +1416,11 @@ Property list
``sub-scale`` (RW)
Subtitle font size multiplier.
-``ass-use-margins`` (RW)
- See ``--ass-use-margins``.
+``ass-force-margins`` (RW)
+ See ``--ass-force-margins``.
+
+``sub-use-margins`` (RW)
+ See ``--sub-use-margins``.
``ass-vsfilter-aspect-compat`` (RW)
See ``--ass-vsfilter-aspect-compat``.
@@ -1486,6 +1524,13 @@ Property list
``track-list/N/selected``
``yes`` if the track is currently decoded, ``no`` otherwise.
+ ``track-list/N/ff-index``
+ The stream index as usually used by the FFmpeg utilities. Note that
+ this can be potentially wrong if a demuxer other than libavformat
+ (``--demuxer=lavf``) is used. For mkv files, the index will usually
+ match even if the default (builtin) demuxer is used, but there is
+ no hard guarantee.
+
When querying the property with the client API using ``MPV_FORMAT_NODE``,
or with Lua ``mp.get_property_native``, this will return a mpv_node with
the following contents:
@@ -1607,7 +1652,7 @@ Property list
whether the video window is visible. If the ``--force-window`` option is
used, this is usually always returns ``yes``.
-``packet-video-bitrate``, ``packet-audio-bitrate``, ``packet-sub-bitrate``
+``video-bitrate``, ``audio-bitrate``, ``sub-bitrate``
Bitrate values calculated on the packet level. This works by dividing the
bit size of all packets between two keyframes by their presentation
timestamp distance. (This uses the timestamps are stored in the file, so
@@ -1616,8 +1661,29 @@ Property list
bitrate. To make the property more UI friendly, updates to these properties
are throttled in a certain way.
+ The unit is bits per second. OSD formatting turns these values in kilobits
+ (or megabits, if appropriate), which can be prevented by using the
+ raw property value, e.g. with ``${=video-bitrate}``.
+
+ Note that the accuracy of these properties is influenced by a few factors.
+ If the underlying demuxer rewrites the packets on demuxing (done for some
+ file formats), the bitrate might be slightly off. If timestamps are bad
+ or jittery (like in Matroska), even constant bitrate streams might show
+ fluctuating bitrate.
+
How exactly these values are calculated might change in the future.
+ In earlier versions of mpv, these properties returned a static (but bad)
+ guess using a completely different method.
+
+``packet-video-bitrate``, ``packet-audio-bitrate``, ``packet-sub-bitrate``
+ Old and deprecated properties for ``video-bitrate``, ``audio-bitrate``,
+ ``sub-bitrate``. They behave exactly the same, but return a value in
+ kilobits. Also, they don't have any OSD formatting, though the same can be
+ achieved with e.g. ``${=video-bitrate}``.
+
+ These properties shouldn't be used anymore.
+
``audio-device-list``
Return the list of discovered audio devices. This is mostly for use with
the client API, and reflects what ``--audio-device=help`` with the command
@@ -1639,6 +1705,13 @@ Property list
human readable free form text. The description is an empty string if none
was received.
+ The special entry with the name set to ``auto`` selects the default audio
+ output driver and the default device.
+
+ The property can be watched with the property observation mechanism in
+ the client API and in Lua scripts. (Technically, change notification is
+ enabled the first time this property is read.)
+
``audio-device`` (RW)
Set the audio device. This directly reads/writes the ``--audio-device``
option, but on write accesses, the audio output will be scheduled for
@@ -1662,6 +1735,10 @@ Property list
Return the audio device selected by the AO driver (only implemented for
some drivers: currently only ``coreaudio``).
+``working-directory``
+ Return the working directory of the mpv process. Can be useful for JSON IPC
+ users, because the command line player usually works with relative paths.
+
``mpv-version``
Return the mpv version/copyright string. Depending on how the binary was
built, it might contain either a release version, or just a git hash.
diff --git a/DOCS/man/ipc.rst b/DOCS/man/ipc.rst
index 0edec05..bbe07f0 100644
--- a/DOCS/man/ipc.rst
+++ b/DOCS/man/ipc.rst
@@ -79,6 +79,9 @@ mpv will also send events to clients with JSON messages of the following form:
where ``event_name`` is the name of the event. Additional event-specific fields
can also be present. See `List of events`_ for a list of all supported events.
+All commands, replies, and events are separated from each other with a line
+break character (``\n``).
+
If the first character (after skipping whitespace) is not ``{``, the command
will be interpreted as non-JSON text command, as they are used in input.conf
(or ``mpv_command_string()`` in the client API). Additionally, line starting
diff --git a/DOCS/man/lua.rst b/DOCS/man/lua.rst
index 8f7ce48..ff70600 100644
--- a/DOCS/man/lua.rst
+++ b/DOCS/man/lua.rst
@@ -326,7 +326,6 @@ The ``mp`` module is preloaded, although it can be loaded manually with
the timer is re-added after the function fn is run.
Returns a timer object. The timer object provides the following methods:
-
``stop()``
Disable the timer. Does nothing if the timer is already disabled.
This will remember the current elapsed time when stopping, so that
@@ -357,6 +356,23 @@ The ``mp`` module is preloaded, although it can be loaded manually with
(``true``). This value is used when the timer expires (but before
the timer callback function fn is run).
+ Note that these are method, and you have to call them using ``:`` instead
+ of ``.`` (Refer to http://www.lua.org/manual/5.2/manual.html#3.4.9 .)
+
+ Example:
+
+ ::
+
+ seconds = 0
+ timer = mp.add_periodic_timer(1, function()
+ print("called every second")
+ # stop it after 10 seconds
+ seconds = seconds + 1
+ if seconds >= 10 then
+ timer:kill()
+ end
+ end)
+
``mp.get_opt(key)``
Return a setting from the ``--script-opts`` option. It's up to the user and
@@ -623,6 +639,14 @@ strictly part of the guaranteed API.
trailing text is returned as 3rd return value. (The 3rd return value is
always there, but with ``trail`` set, no error is raised.)
+``utils.format_json(v)``
+ Format the given Lua table (or value) as a JSON string and return it. On
+ error, returns ``nil, error``. (Errors usually only happen on value types
+ incompatible with JSON.)
+
+ The argument value uses similar conventions as ``mp.set_property_native()``
+ to distinguish empty objects and arrays.
+
``utils.to_string(v)``
Turn the given value into a string. Formats tables and their contents. This
doesn't do anything special; it is only needed because Lua is terrible.
diff --git a/DOCS/man/mpv.rst b/DOCS/man/mpv.rst
index c547584..751b2b2 100644
--- a/DOCS/man/mpv.rst
+++ b/DOCS/man/mpv.rst
@@ -32,8 +32,10 @@ INTERACTIVE CONTROL
===================
mpv has a fully configurable, command-driven control layer which allows you
-to control mpv using keyboard, mouse, joystick or remote control (with
-LIRC). See the ``--input-`` options for ways to customize it.
+to control mpv using keyboard, mouse, or remote control (there is no
+LIRC support - configure remotes as input devices instead).
+
+See the ``--input-`` options for ways to customize it.
Keyboard Control
----------------
@@ -135,6 +137,11 @@ l
Ctrl + and Ctrl -
Adjust audio delay by +/- 0.1 seconds.
+u
+ Switch between applying no style overrides to SSA/ASS subtitles, and
+ overriding them almost completely with the normal subtitle style. See
+ ``--ass-style-override`` for more info.
+
V
Toggle subtitle VSFilter aspect compatibility mode. See
``--ass-vsfilter-aspect-compat`` for more info.
@@ -269,14 +276,16 @@ Shells may actually strip some quotes from the string passed to the commandline,
so the example quotes the string twice, ensuring that mpv recieves the ``"``
quotes.
-The ``[...]`` from of quotes wraps everything between ``[`` and ``]``. It's
+The ``[...]`` form of quotes wraps everything between ``[`` and ``]``. It's
useful with shells that don't interpret these characters in the middle of
-an argument (like bash).
+an argument (like bash). These quotes are balanced (since mpv 0.9.0): the ``[``
+and ``]`` nest, and the quote terminates on the last ``]`` that has no matching
+``[`` within the string. (For example, ``[a[b]c]`` results in ``a[b]c``.)
-A special kind of string-escaping intended for use with external scripts and
-programs is started with ``%``.
+The fixed-length quoting syntax is intended for use with external
+scripts and programs.
-It has the following format::
+It is started with ``%`` and has the following format::
%n%string_of_length_n
@@ -312,7 +321,8 @@ console controls. (Which makes it suitable for playing data piped to stdin.)
For paths passed to suboptions, the situation is further complicated by the
need to escape special characters. To work this around, the path can be
-additionally wrapped in the ``%n%string_of_length_n`` syntax (see above).
+additionally wrapped in the fixed-length syntax, e.g. ``%n%string_of_length_n``
+(see above).
Some mpv options interpret paths starting with ``~``. Currently, the prefix
``~~/`` expands to the mpv configuration directory (usually ``~/.config/mpv/``).
@@ -390,10 +400,10 @@ Escaping spaces and special characters
This is done like with command line options. The shell is not involved here,
but option values still need to be quoted as a whole if it contains certain
characters like spaces. A config entry can be quoted with ``"`` and ``'``,
-as well as with the ``%n%`` syntax mentioned before. This is like passing
-the exact contents of the quoted string as command line option. C-style escapes
-are currently _not_ interpreted on this level, although some options to this
-manually. (This is a mess and should probably be changed at some point.)
+as well as with the fixed-length syntax (``%n%``) mentioned before. This is like
+passing the exact contents of the quoted string as command line option. C-style
+escapes are currently _not_ interpreted on this level, although some options to
+this manually. (This is a mess and should probably be changed at some point.)
Putting Command Line Options into the Configuration File
--------------------------------------------------------
@@ -468,11 +478,8 @@ A screenshot will usually contain the unscaled video contents at the end of the
video filter chain and subtitles. By default, ``S`` takes screenshots without
subtitles, while ``s`` includes subtitles.
-The ``screenshot`` video filter is not required when using a recommended GUI
-video output driver. It should normally not be added to the config file, as
-taking screenshots is handled by the VOs, and adding the screenshot filter will
-break hardware decoding. (The filter may still be useful for taking screenshots
-at a certain point within the video chain when using multiple video filters.)
+Unlike with MPlayer, the ``screenshot`` video filter is not required. This
+filter was never required in mpv, and has been removed.
TERMINAL STATUS LINE
====================
@@ -510,10 +517,9 @@ listed.
rendering is too slow. Also can be incremented on "hiccups" and when the video
frame couldn't be displayed on time. (``vo-drop-frame-count`` property.)
If the decoder drops frames, the number of decoder-dropped frames is appended
- to the display as well, e.g.: ``Dropped: 4/34``. This should almost never
- happen, unless decoder-framedropping is enabled with one of the
- ``--framedrop`` options, the stream contains errors, or a weird codec is in
- use. (``drop-frame-count`` property.)
+ to the display as well, e.g.: ``Dropped: 4/34``. This happens only if
+ decoder-framedropping is enabled with the ``--framedrop`` options.
+ (``drop-frame-count`` property.)
- Cache state, e.g. ``Cache: 2s+134KB``. Visible if the stream cache is enabled.
The first value shows the amount of video buffered in the demuxer in seconds,
the second value shows *additional* data buffered in the stream cache in
@@ -603,6 +609,39 @@ PROTOCOLS
``memory://data``
Use the ``data`` part as source data.
+PSEUDO GUI MODE
+===============
+
+mpv has no official GUI, other than the OSC (`ON SCREEN CONTROLLER`_), which
+is not a full GUI and is not meant to be. However, to compensate for the lack
+of expected GUI behavior, mpv will in some cases start with some settings
+changed to behave slightly more like a GUI mode.
+
+Currently this happens only in the following cases:
+
+- if started using the ``mpv.desktop`` file on Linux (e.g. started from menus
+ or file associations provided by desktop environments)
+- if started from explorer.exe on Windows (technically, if it was started on
+ Windows, and all of the stdout/stderr/stdin handles are unset)
+- manually adding ``--profile=pseudo-gui`` to the command line
+
+This mode implicitly adds ``--profile=pseudo-gui`` to the command line, with
+the ``pseudo-gui`` profile being predefined with the following contents:
+
+::
+
+ [pseudo-gui]
+ terminal=no
+ force-window=yes
+ idle=once
+
+This follows the mpv config file format. To customize pseudo-GUI mode, you can
+put your own ``pseudo-gui`` profile into your ``mpv.conf``. This profile will
+enhance the default profile, rather than overwrite it.
+
+The profile always overrides other settings in ``mpv.conf``.
+
+
.. include:: options.rst
.. include:: ao.rst
@@ -758,10 +797,12 @@ FILES
=====
``/usr/local/etc/mpv/mpv.conf``
- mpv system-wide settings (depends on ``--prefix`` passed to configure)
+ mpv system-wide settings (depends on ``--prefix`` passed to configure - mpv
+ in default configuration will use ``/usr/local/etc/mpv/`` as config
+ directory, while most Linux distributions will set it to ``/etc/mpv/``).
``~/.config/mpv/mpv.conf``
- mpv user settings
+ mpv user settings (see `CONFIGURATION FILES`_ section)
``~/.config/mpv/input.conf``
key bindings (see `INPUT.CONF`_ section)
diff --git a/DOCS/man/options.rst b/DOCS/man/options.rst
index aa08a10..dd85fbf 100644
--- a/DOCS/man/options.rst
+++ b/DOCS/man/options.rst
@@ -181,6 +181,7 @@ Playback Control
file, such as a chapter seek, but not for relative seeks like
the default behavior of arrow keys (default).
:yes: Use precise seeks whenever possible.
+ :always: Same as ``yes`` (for compatibility).
``--hr-seek-demuxer-offset=<seconds>``
This option exists to work around failures to do precise seeks (as in
@@ -439,7 +440,8 @@ Program Behavior
Enable the youtube-dl hook-script. It will look at the input URL, and will
play the video located on the website. This works with many streaming sites,
not just the one that the script is named after. This requires a recent
- version of youtube-dl to be installed on the system. (Enabled by default.)
+ version of youtube-dl to be installed on the system. (Enabled by default,
+ except when the client API / libmpv is used.)
If the script can't do anything with an URL, it will do nothing.
@@ -454,6 +456,18 @@ Program Behavior
``bestvideo+bestaudio``.
(Default: ``best``)
+``--ytdl-raw-options=<key>=<value>[,<key>=<value>[,...]]``
+ Pass arbitraty options to youtube-dl. Parameter and argument should be
+ passed as a key-value pair. Options without argument must include ``=``.
+
+ There is no sanity checking so it's possible to break things (i.e.
+ passing invalid parameters to youtube-dl).
+
+ .. admonition:: Example
+
+ ``--ytdl-raw-options=username=user,password=pass``
+ ``--ytdl-raw-options=force-ipv6=``
+
Video
-----
@@ -541,7 +555,8 @@ Video
:vaapi: requires ``--vo=opengl`` or ``--vo=vaapi`` (Linux with Intel GPUs only)
:vaapi-copy: copies video back into system RAM (Linux with Intel GPUs only)
:vda: requires ``--vo=opengl`` (OS X only)
- :dxva2-copy: copies video back to system RAM (Windows only) (experimental)
+ :dxva2-copy: copies video back to system RAM (Windows only)
+ :rpi: requires ``--vo=rpi`` (Raspberry Pi only - default if available)
``auto`` tries to automatically enable hardware decoding using the first
available method. This still depends what VO you are using. For example,
@@ -552,8 +567,7 @@ Video
The ``vaapi-copy`` mode allows you to use vaapi with any VO. Because
this copies the decoded video back to system RAM, it's likely less efficient
- than the ``vaapi`` mode. But there are reports that this is actually faster
- as well, and avoids many issues with ``vaapi``.
+ than the ``vaapi`` mode.
.. note::
@@ -614,7 +628,7 @@ Video
This option is disabled if the ``--no-keepaspect`` option is used.
-``--video-rotate=<0-359|no>``
+``--video-rotate=<0-360|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,
@@ -801,7 +815,7 @@ Audio
-----
``--audio-pitch-correction=<yes|no>``
- If this is enabled (default), playing with a speed higher than normal
+ If this is enabled (default), playing with a speed different from normal
automatically inserts the ``scaletempo`` audio filter. For details, see
audio filter section.
@@ -873,8 +887,7 @@ Audio
delay the audio, and negative values delay the video.
``--no-audio``
- Do not play sound. With some demuxers this may not work. In those cases
- you can try ``--ao=null`` instead.
+ Do not play sound.
``--mute=<auto|yes|no>``
Set startup audio mute status. ``auto`` (default) will not change the mute
@@ -903,11 +916,14 @@ Audio
``--ad-lavc-ac3drc=<level>``
Select the Dynamic Range Compression level for AC-3 audio streams.
``<level>`` is a float value ranging from 0 to 1, where 0 means no
- compression and 1 (which is the default) means full compression (make loud
- passages more silent and vice versa). Values up to 2 are also accepted, but
+ compression (which is the default) and 1 means full compression (make loud
+ passages more silent and vice versa). Values up to 6 are also accepted, but
are purely experimental. This option only shows an effect if the AC-3 stream
contains the required range compression information.
+ The standard mandates that DRC is enabled by default, but mpv (and some
+ other players) ignore this for the sake of better audio quality.
+
``--ad-lavc-downmix=<yes|no>``
Whether to request audio channel downmixing from the decoder (default: yes).
Some decoders, like AC-3, AAC and DTS, can remix audio on decoding. The
@@ -967,8 +983,9 @@ Audio
``--audio-display=<no|attachment>``
Setting this option to ``attachment`` (default) will display image
- attachments when playing audio files. It will display the first image
- found, and additional images are available as video tracks.
+ attachments (e.g. album cover art) when playing audio files. It will
+ display the first image found, and additional images are available as
+ video tracks.
Setting this option to ``no`` disables display of video entirely when
playing audio files.
@@ -1157,13 +1174,22 @@ Subtitles
doesn't covert the window fully, e.g. because screen aspect and window
aspect mismatch (and the player adds black bars).
+ Default: yes.
+
This option is misnamed. The difference to the confusingly similar sounding
option ``--sub-scale-by-window`` is that ``--sub-scale-with-window`` still
scales with the approximate window size, while the other option disables
this scaling.
+ Affects plain text subtitles only (or ASS if ``--ass-style-override`` is
+ set high enough).
+
+``--ass-scale-with-window=<yes|no>``
+ Like ``--sub-scale-with-window``, but affects subtitles in ASS format only.
Like ``--sub-scale``, this can break ASS subtitles.
+ Default: no.
+
``--embeddedfonts``, ``--no-embeddedfonts``
Use fonts embedded in Matroska container files and ASS scripts (default:
enabled). These fonts can be used for SSA/ASS subtitle rendering.
@@ -1244,12 +1270,23 @@ Subtitles
:signfs: like ``yes``, but apply ``--sub-scale`` only to signs
:no: Render subtitles as forced by subtitle scripts.
:force: Try to force the font style as defined by the ``--sub-text-*``
- options. Requires a modified libass, can break rendering easily.
- Probably more reliable than ``force``.
+ options. Can break rendering easily.
+
+``--ass-force-margins``
+ Enables placing toptitles and subtitles in black borders when they are
+ available, if the subtitles are in the ASS format.
+
+ Default: no.
-``--ass-use-margins``
+``--sub-use-margins``
Enables placing toptitles and subtitles in black borders when they are
- available.
+ available, if the subtitles are in a plain text format (or ASS if
+ ``--ass-style-override`` is set high enough).
+
+ Default: yes.
+
+ Renamed from ``--ass-use-margins``. To place ASS subtitles in the borders
+ too (like the old option did), also add ``--ass-force-margins``.
``--ass-vsfilter-aspect-compat=<yes|no>``
Stretch SSA/ASS subtitles when playing anamorphic videos for compatibility
@@ -1687,19 +1724,13 @@ Window
(depending on the video aspect ratio, the width or height will be
larger than 500 in order to keep the aspect ratio the same).
-``--autosync=<factor>``
- Gradually adjusts the A/V sync based on audio delay measurements.
- Specifying ``--autosync=0``, the default, will cause frame timing to be
- based entirely on audio delay measurements. Specifying ``--autosync=1``
- will do the same, but will subtly change the A/V correction algorithm. An
- uneven video framerate in a video which plays fine with ``--no-audio`` can
- often be helped by setting this to an integer value greater than 1. The
- higher the value, the closer the timing will be to ``--no-audio``. Try
- ``--autosync=30`` to smooth out problems with sound drivers which do not
- implement a perfect audio delay measurement. With this value, if large A/V
- sync offsets occur, they will only take about 1 or 2 seconds to settle
- out. This delay in reaction time to sudden A/V offsets should be the only
- side-effect of turning this option on, for all sound drivers.
+``--window-scale=<factor>``
+ Resize the video window to a multiple (or fraction) of the video size. This
+ option is applied before ``--autofit`` and other options are applied (so
+ they override this option).
+
+ For example, ``--window-scale=0.5`` would show the window at half the
+ video size.
``--cursor-autohide=<number|no|always>``
Make mouse cursor automatically hide after given number of milliseconds.
@@ -1911,6 +1942,10 @@ Disc Devices
``--cdda-skip=<yes|no>``
(Never) accept imperfect data reconstruction.
+``--cdda-cdtext=<yes|no>``
+ Print CD text. This is disabled by default, because it ruins perfomance
+ with CD-ROM drives for unknown reasons.
+
``--dvd-speed=<speed>``
Try to limit DVD speed (default: 0, no change). DVD base speed is 1385
kB/s, so an 8x drive can read at speeds up to 11080 kB/s. Slower speeds
@@ -1954,101 +1989,6 @@ Equalizer
negative of the image with this option. Not supported by all video output
drivers.
-``--colormatrix=<colorspace>``
- Controls the YUV to RGB color space conversion when playing video. There
- are various standards. Normally, BT.601 should be used for SD video, and
- BT.709 for HD video. (This is done by default.) Using incorrect color space
- results in slightly under or over saturated and shifted colors.
-
- The color space conversion is additionally influenced by the related
- options --colormatrix-input-range and --colormatrix-output-range.
-
- These options are not always supported. Different video outputs provide
- varying degrees of support. The ``opengl`` and ``vdpau`` video output
- drivers usually offer full support. The ``xv`` output can set the color
- space if the system video driver supports it, but not input and output
- levels. The ``scale`` video filter can configure color space and input
- levels, but only if the output format is RGB (if the video output driver
- supports RGB output, you can force this with ``-vf scale,format=rgba``).
-
- If this option is set to ``auto`` (which is the default), the video's
- color space flag will be used. If that flag is unset, the color space
- will be selected automatically. This is done using a simple heuristic that
- attempts to distinguish SD and HD video. If the video is larger than
- 1279x576 pixels, BT.709 (HD) will be used; otherwise BT.601 (SD) is
- selected.
-
- Available color spaces are:
-
- :auto: automatic selection (default)
- :BT.601: ITU-R BT.601 (SD)
- :BT.709: ITU-R BT.709 (HD)
- :BT.2020-NCL: ITU-R BT.2020 non-constant luminance system
- :BT.2020-CL: ITU-R BT.2020 constant luminance system
- :SMPTE-240M: SMPTE-240M
-
-``--colormatrix-input-range=<color-range>``
- YUV color levels used with YUV to RGB conversion. This option is only
- necessary when playing broken files which do not follow standard color
- levels or which are flagged wrong. If the video does not specify its
- color range, it is assumed to be limited range.
-
- The same limitations as with --colormatrix apply.
-
- Available color ranges are:
-
- :auto: automatic selection (normally limited range) (default)
- :limited: limited range (16-235 for luma, 16-240 for chroma)
- :full: full range (0-255 for both luma and chroma)
-
-``--colormatrix-output-range=<color-range>``
- RGB color levels used with YUV to RGB conversion. Normally, output devices
- such as PC monitors use full range color levels. However, some TVs and
- video monitors expect studio RGB levels. Providing full range output to a
- device expecting studio level input results in crushed blacks and whites,
- the reverse in dim gray blacks and dim whites.
-
- The same limitations as with ``--colormatrix`` apply.
-
- Available color ranges are:
-
- :auto: automatic selection (equals to full range) (default)
- :limited: limited range (16-235 per component), studio levels
- :full: full range (0-255 per component), PC levels
-
- .. note::
-
- It is advisable to use your graphics driver's color range option
- instead, if available.
-
-``--colormatrix-primaries=<primaries>``
- RGB primaries 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. By default, when unset, BT.709 is used for
- all files except those tagged with a BT.2020 color matrix.
-
- This option only affects video output drivers that perform color
- management, for example ``opengl`` with the ``srgb`` or ``icc-profile``
- suboptions set.
-
- If this option is set to ``auto`` (which is the default), the video's
- primaries flag will be used. If that flag is unset, the color space will
- be selected automatically, using the following heuristics: If the
- ``--colormatrix`` is set or determined as BT.2020 or BT.709, the
- corresponding primaries are used. Otherwise, if the video height is
- exactly 576 (PAL), BT.601-625 is used. If it's exactly 480 or 486 (NTSC),
- BT.601-525 is used. If the video resolution is anything else, BT.709 is
- used.
-
- Available primaries are:
-
- :auto: automatic selection (default)
- :BT.601-525: ITU-R BT.601 (SD) 525-line systems (NTSC, SMPTE-C)
- :BT.601-625: ITU-R BT.601 (SD) 625-line systems (PAL, SECAM)
- :BT.709: ITU-R BT.709 (HD) (same primaries as sRGB)
- :BT.2020: ITU-R BT.2020 (UHD)
-
-
Demuxer
-------
@@ -2076,6 +2016,12 @@ Demuxer
``--demuxer-lavf-format=<name>``
Force a specific libavformat demuxer.
+``--demuxer-lavf-hacks=<yes|no>``
+ By default, some formats will be handled differently from other formats
+ by explicitly checking for them. Most of these compensate for weird or
+ imperfect behavior from libavformat demuxers. Passing ``no`` disables
+ these. For debugging and testing only.
+
``--demuxer-lavf-genpts-mode=<no|lavf>``
Mode for deriving missing packet PTS values from packet DTS. ``lavf``
enables libavformat's ``genpts`` option. ``no`` disables it. This used
@@ -2160,6 +2106,18 @@ Demuxer
will be slower (especially when playing over http), or that behavior with
broken files is much worse. So don't use this option.
+``--demuxer-mkv-fix-timestamps=<yes|no>``
+ Fix rounded Matroska timestamps (enabled by default). Matroska usually
+ stores timestamps rounded to milliseconds. This means timestamps jitter
+ by some amount around the intended timestamp. mpv can correct the timestamps
+ based on the framerate value stored in the file: the timestamp is rounded
+ to the next frame (according to the framerate), unless the new timestamp
+ would deviate more than 1ms from the old one. This should undo the rounding
+ done by the muxer.
+
+ (The allowed deviation can be less than 1ms if the file uses a non-standard
+ timecode scale.)
+
``--demuxer-rawaudio-channels=<value>``
Number of channels (or channel layout) if ``--demuxer=rawaudio`` is used
(default: stereo).
@@ -2208,7 +2166,7 @@ Demuxer
``--demuxer-readahead-secs=<seconds>``
If ``--demuxer-thread`` is enabled, this controls how much the demuxer
- should buffer ahead in seconds (default: 0.2). As long as no packet has
+ should buffer ahead in seconds (default: 1). As long as no packet has
a timestamp difference higher than the readahead amount relative to the
last packet returned to the decoder, the demuxer keeps reading.
@@ -2286,6 +2244,9 @@ Input
get replies or events. Use ``--input-unix-socket`` for something
bi-directional. On MS Windows, JSON commands are not available.
+ This can also specify a direct file descriptor with ``fd://N`` (UNIX only).
+ In this case, JSON replies will be written if the FD is writable.
+
See also ``--slave-broken``.
.. note::
@@ -2318,19 +2279,6 @@ Input
driver. Necessary to use the OSC, or to select the buttons in DVD menus.
Support depends on the VO in use.
-``--input-joystick``, ``--no-input-joystick``
- Enable/disable joystick support. Disabled by default.
-
-``--input-js-dev``
- Specifies the joystick device to use (default: ``/dev/input/js0``).
-
-``--input-lirc``, ``--no-input-lirc``
- Enable/disable LIRC support. Enabled by default.
-
-``--input-lirc-conf=<filename>``
- (LIRC only)
- Specifies a configuration file for LIRC (default: ``~/.lircrc``).
-
``--input-media-keys=<yes|no>``
(OS X only)
Enable/disable media keys support. Enabled by default (except for libmpv).
@@ -2469,6 +2417,9 @@ OSD
``--osd-blur=<0..20.0>``, ``--sub-text-blur=<0..20.0>``
Gaussian blur factor. 0 means no blur applied (default).
+``--osd-bold=<yes|no>``, ``--sub-text-bold=<yes|no>``
+ Format text on bold.
+
``--osd-border-color=<color>``, ``--sub-text-border-color=<color>``
See ``--osd-color``. Color used for the OSD/sub font border.
@@ -2513,7 +2464,8 @@ OSD
- ``--osd-color='#C0808080'`` set OSD to 50% gray with 75% alpha
``--osd-fractions``
- Show OSD times with fractions of seconds.
+ Show OSD times with fractions of seconds (in millisecond precision). Useful
+ to see the exact timestamp of a video frame.
``--osd-level=<0-3>``
Specifies which mode the OSD should start in.
@@ -2542,6 +2494,17 @@ OSD
Default: 22.
+``--osd-align-x=<left|center|right>``, ``--sub-text-align-x=...``
+ Control to which corner of the screen OSD or text subtitles should be
+ aligned to (default: ``center`` for subs, ``left`` for OSD).
+
+ Never applied to ASS subtitles, except in ``--no-sub-ass`` mode. Likewise,
+ this does not apply to image subtitles.
+
+``--osd-align-y=<top|center|bottom>`` ``--sub-text-align-y=...``
+ Vertical position (default: ``bottom`` for subs, ``top`` for OSD).
+ Details see ``--osd-align-x``.
+
``--osd-scale=<factor>``
OSD font size multiplier, multiplied with ``--osd-font-size`` value.
@@ -2594,6 +2557,13 @@ Screenshot
:jpg: JPEG (default)
:jpeg: JPEG (same as jpg, but with .jpeg file ending)
+``--screenshot-tag-colorspace=<yes|no>``
+ Tag screenshots with the appropriate colorspace.
+
+ Note that not all formats are supported.
+
+ Default: ``yes``.
+
``--screenshot-template=<template>``
Specify the filename template used to save screenshots. The template
specifies the filename without file extension, and can contain format
@@ -2892,7 +2862,7 @@ TV
Use _ for spaces in names (or play with quoting ;-) ). The channel
names will then be written using OSD, and the slave commands
``tv_step_channel``, ``tv_set_channel`` and ``tv_last_channel``
- will be usable for a remote control (see LIRC). Not compatible with
+ will be usable for a remote control. Not compatible with
the ``frequency`` parameter.
.. note::
@@ -3001,11 +2971,13 @@ TV
Cache
-----
-``--cache=<kBytes|no|auto>``
+``--cache=<kBytes|yes|no|auto>``
Set the size of the cache in kilobytes, disable it with ``no``, or
automatically enable it if needed with ``auto`` (default: ``auto``).
With ``auto``, the cache will usually be enabled for network streams,
- using the size set by ``--cache-default``.
+ using the size set by ``--cache-default``. With ``yes``, the cache will
+ always be enabled with the size set by ``--cache-default`` (unless the
+ stream can not be cached, or ``--cache-default`` disables caching).
May be useful when playing files from slow media, but can also have
negative effects, especially with file formats that require a lot of
@@ -3018,7 +2990,7 @@ Cache
because no space is reserved for seeking back yet.
``--cache-default=<kBytes|no>``
- Set the size of the cache in kilobytes (default: 25000 KB). Using ``no``
+ Set the size of the cache in kilobytes (default: 150000 KB). Using ``no``
will not automatically enable the cache e.g. when playing from a network
stream. Note that using ``--cache`` will always override this option.
@@ -3091,7 +3063,7 @@ Cache
``--cache-secs=<seconds>``
How many seconds of audio/video to prefetch if the cache is active. This
overrides the ``--demuxer-readahead-secs`` option if and only if the cache
- is enabled and the value is larger. (Default: 2.)
+ is enabled and the value is larger. (Default: 10.)
``--cache-pause``, ``--no-cache-pause``
Whether the player should automatically pause when the cache runs low,
@@ -3272,6 +3244,20 @@ Miscellaneous
``--mc=<seconds/frame>``
Maximum A-V sync correction per frame (in seconds)
+``--autosync=<factor>``
+ Gradually adjusts the A/V sync based on audio delay measurements.
+ Specifying ``--autosync=0``, the default, will cause frame timing to be
+ based entirely on audio delay measurements. Specifying ``--autosync=1``
+ will do the same, but will subtly change the A/V correction algorithm. An
+ uneven video framerate in a video which plays fine with ``--no-audio`` can
+ often be helped by setting this to an integer value greater than 1. The
+ higher the value, the closer the timing will be to ``--no-audio``. Try
+ ``--autosync=30`` to smooth out problems with sound drivers which do not
+ implement a perfect audio delay measurement. With this value, if large A/V
+ sync offsets occur, they will only take about 1 or 2 seconds to settle
+ out. This delay in reaction time to sudden A/V offsets should be the only
+ side-effect of turning this option on, for all sound drivers.
+
``--mf-fps=<value>``
Framerate used when decoding from multiple PNG or JPEG files with ``mf://``
(default: 1).
diff --git a/DOCS/man/vf.rst b/DOCS/man/vf.rst
index 8880d3b..3739716 100644
--- a/DOCS/man/vf.rst
+++ b/DOCS/man/vf.rst
@@ -207,19 +207,148 @@ Available filters are:
For a list of available formats, see ``format=fmt=help``.
``<fmt>``
- Format name, e.g. rgb15, bgr24, 420p, etc. (default: yuyv).
+ Format name, e.g. rgb15, bgr24, 420p, etc. (default: don't change).
``<outfmt>``
- Format name that should be substituted for the output. If this is not
- 100% compatible with the ``<fmt>`` value, it will crash.
+ Format name that should be substituted for the output. If they do not
+ have the same bytes per pixel and chroma subsamplimg, it will fail.
+ ``<colormatrix>``
+ Controls the YUV to RGB color space conversion when playing video. There
+ are various standards. Normally, BT.601 should be used for SD video, and
+ BT.709 for HD video. (This is done by default.) Using incorrect color space
+ results in slightly under or over saturated and shifted colors.
+
+ These options are not always supported. Different video outputs provide
+ varying degrees of support. The ``opengl`` and ``vdpau`` video output
+ drivers usually offer full support. The ``xv`` output can set the color
+ space if the system video driver supports it, but not input and output
+ levels. The ``scale`` video filter can configure color space and input
+ levels, but only if the output format is RGB (if the video output driver
+ supports RGB output, you can force this with ``-vf scale,format=rgba``).
+
+ If this option is set to ``auto`` (which is the default), the video's
+ color space flag will be used. If that flag is unset, the color space
+ will be selected automatically. This is done using a simple heuristic that
+ attempts to distinguish SD and HD video. If the video is larger than
+ 1279x576 pixels, BT.709 (HD) will be used; otherwise BT.601 (SD) is
+ selected.
+
+ Available color spaces are:
+
+ :auto: automatic selection (default)
+ :bt.601: ITU-R BT.601 (SD)
+ :bt.709: ITU-R BT.709 (HD)
+ :bt.2020-ncl: ITU-R BT.2020 non-constant luminance system
+ :bt.2020-cl: ITU-R BT.2020 constant luminance system
+ :smpte-240m: SMPTE-240M
+
+ ``<colorlevels>``
+ YUV color levels used with YUV to RGB conversion. This option is only
+ necessary when playing broken files which do not follow standard color
+ levels or which are flagged wrong. If the video does not specify its
+ color range, it is assumed to be limited range.
+
+ The same limitations as with ``<colormatrix>`` apply.
+
+ Available color ranges are:
+
+ :auto: automatic selection (normally limited range) (default)
+ :limited: limited range (16-235 for luma, 16-240 for chroma)
+ :full: full range (0-255 for both luma and chroma)
+
+ ``<outputlevels>``
+ RGB color levels used with YUV to RGB conversion. Normally, output devices
+ such as PC monitors use full range color levels. However, some TVs and
+ video monitors expect studio RGB levels. Providing full range output to a
+ device expecting studio level input results in crushed blacks and whites,
+ the reverse in dim gray blacks and dim whites.
+
+ The same limitations as with ``<colormatrix>`` apply.
+
+ Available color ranges are:
+
+ :auto: automatic selection (equals to full range) (default)
+ :limited: limited range (16-235 per component), studio levels
+ :full: full range (0-255 per component), PC levels
+
+ .. note::
+
+ It is advisable to use your graphics driver's color range option
+ instead, if available.
+
+ ``<primaries>``
+ RGB primaries 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, for example ``opengl`` with the ``target-prim`` or
+ ``icc-profile`` suboptions set.
+
+ If this option is set to ``auto`` (which is the default), the video's
+ primaries flag will be used. If that flag is unset, the color space will
+ be selected automatically, using the following heuristics: If the
+ ``<colormatrix>`` is set or determined as BT.2020 or BT.709, the
+ corresponding primaries are used. Otherwise, if the video height is
+ exactly 576 (PAL), BT.601-625 is used. If it's exactly 480 or 486 (NTSC),
+ BT.601-525 is used. If the video resolution is anything else, BT.709 is
+ used.
+
+ Available primaries are:
+
+ :auto: automatic selection (default)
+ :bt.601-525: ITU-R BT.601 (SD) 525-line systems (NTSC, SMPTE-C)
+ :bt.601-625: ITU-R BT.601 (SD) 625-line systems (PAL, SECAM)
+ :bt.709: ITU-R BT.709 (HD) (same primaries as sRGB)
+ :bt.2020: ITU-R BT.2020 (UHD)
+ :apple: Apple RGB
+ :adobe: Adobe RGB (1998)
+ :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.
+
+ 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.
+
+ 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
+
+ ``<stereo-in>``
+ Set the stereo mode the video is assumed to be encoded in. Takes the
+ same values as the ``--video-stereo-mode`` option.
+
+ ``<stereo-out>``
+ Set the stereo mode the video should be displayed as. Takes the
+ same values as the ``--video-stereo-mode`` option.
+
+ ``<rotate>``
+ Set the rotation the video is assumed to be encoded with in degrees.
+ The special value ``-1`` uses the input format.
+
+ ``<dw>``, ``<dh>``
+ Set the display size. Note that setting the display size such that
+ the video is scaled in both directions instead of just changing the
+ aspect ratio is an implementation detail, and might change later.
- .. admonition:: Examples
+ ``<dar>``
+ Set the display aspect ratio of the video frame. This is a float,
+ but values such as ``[16:9]`` can be passed too (``[...]`` for quoting
+ to prevent the option parser from interpreting the ``:`` character).
- ====================== =====================
- Valid Invalid (will crash)
- ====================== =====================
- ``format=rgb24:bgr24`` ``format=rgb24:420p``
- ``format=yuyv:uyvy``
- ====================== =====================
``noformat[=fmt]``
Restricts the color space for the next filter without doing any conversion.
@@ -432,12 +561,6 @@ Available filters are:
``show``
Draw a rectangle showing the area defined by x/y/w/h.
-``screenshot``
- Optional filter for screenshot support. This is only needed if the video
- output does not provide working direct screenshot support. Note that it is
- not always safe to insert this filter by default. See `TAKING SCREENSHOTS`_
- for details.
-
``sub=[=bottom-margin:top-margin]``
Moves subtitle rendering to an arbitrary point in the filter chain, or force
subtitle rendering in the video filter as opposed to using video output OSD
@@ -445,9 +568,9 @@ Available filters are:
``<bottom-margin>``
Adds a black band at the bottom of the frame. The SSA/ASS renderer can
- place subtitles there (with ``--ass-use-margins``).
+ place subtitles there (with ``--sub-use-margins``).
``<top-margin>``
- Black band on the top for toptitles (with ``--ass-use-margins``).
+ Black band on the top for toptitles (with ``--sub-use-margins``).
.. admonition:: Examples
diff --git a/DOCS/man/vo.rst b/DOCS/man/vo.rst
index eb922eb..b398d4f 100644
--- a/DOCS/man/vo.rst
+++ b/DOCS/man/vo.rst
@@ -308,36 +308,72 @@ Available video output drivers are:
This filter corresponds to the old ``lanczos3`` alias if the default
radius is used, while ``lanczos2`` corresponds to a radius of 2.
+ (This filter is an alias for ``sinc``-windowed ``sinc``)
+
``ewa_lanczos``
Elliptic weighted average Lanczos scaling. Also known as Jinc.
- Relatively slow, but very good quality. The number of taps can
- be controlled with ``scale-radius``. Adding extra taps makes the
+ Relatively slow, but very good quality. The radius can be
+ controlled with ``scale-radius``. Increasing the radius makes the
filter sharper but adds more ringing.
- This filter supports antiringing (see ``scale-antiring``).
+ (This filter is an alias for ``jinc``-windowed ``jinc``)
+
+ ``ewa_lanczossharp``
+ A slightly sharpened version of ewa_lanczos, preconfigured to use
+ an ideal radius and parameter. If your hardware can run it, this is
+ probably what you should use by default.
``mitchell``
- Mitchell-Netravali. The ``b`` and ``c`` parameters can be set with
- ``scale-param1`` and ``scale-param2``. Both are set to 1/3 by default.
- This filter is very good at downscaling (see ``scale-down``).
+ Mitchell-Netravali. The ``B`` and ``C`` parameters can be set with
+ ``scale-param1`` and ``scale-param2``. This filter is very good at
+ downscaling (see ``dscale``).
+
+ ``oversample``
+ A version of nearest neighbour that (naively) oversamples pixels,
+ so that pixels overlapping edges get linearly interpolated instead
+ of rounded. This essentially removes the small imperfections and
+ judder artifacts caused by nearest-neighbour interpolation, in
+ exchange for adding some blur. This filter is good at temporal
+ interpolation, and also known as "smoothmotion" (see ``tscale``).
There are some more filters, but most are not as useful. For a complete
list, pass ``help`` as value, e.g.::
mpv --vo=opengl:scale=help
- ``scale-param1=<value>``
- Set filter parameters. Ignored if the filter is not tunable. These are
- unset by default, and use the filter specific default if applicable.
+ ``scale-param1=<value>``, ``scale-param2=<value>``
+ Set filter parameters. Ignored if the filter is not tunable.
+ Currently, this affects the following filter parameters:
+
+ bcspline
+ Spline parameters (``B`` and ``C``). Defaults to 0.5 for both.
+
+ gaussian
+ Scale parameter (``t``). Increasing this makes the result blurrier.
+ Defaults to 1.
- ``scale-param2=<value>``
- See ``scale-param1``.
+ sharpen3, sharpen5
+ Sharpening strength. Increasing this makes the image sharper but
+ adds more ringing and aliasing. Defaults to 0.5.
- ``scale-radius=<r>``
- Set radius for filters listed below, must be a float number between 1.0
- and 16.0. Defaults to be 3.0 if not specified.
+ oversample
+ Minimum distance to an edge before interpolation is used. Setting
+ this to 0 will always interpolate edges, whereas setting it to 0.5
+ will never interpolate, thus behaving as if the regular nearest
+ neighbour algorithm was used. Defaults to 0.0.
- ``sinc``, ``lanczos``, ``ewa_lanczos``, ``blackman``, ``gaussian``
+ ``scale-blur=<value>``
+ Kernel scaling factor (also known as a blur factor). Decreasing this
+ makes the result sharper, increasing it makes it blurrier (default 0).
+ If set to 0, the kernel's preferred blur factor is used. Note that
+ setting this too low (eg. 0.5) leads to bad results. It's generally
+ recommended to stick to values between 0.8 and 1.2.
+
+ ``scale-radius=<value>``
+ Set radius for filters listed below, must be a float number between 0.5
+ and 16.0. Defaults to the filter's preferred radius if not specified.
+
+ ``sinc`` and derivatives, ``jinc`` and derivatives, ``gaussian``, ``box`` and ``triangle``
Note that depending on filter implementation details and video scaling
ratio, the radius that actually being used might be different
@@ -349,7 +385,26 @@ Available video output drivers are:
between 0.0 and 1.0. The default value of 0.0 disables antiringing
entirely.
- Note that this currently only affects ``ewa_lanczos``.
+ Note that this doesn't affect the special filters ``bilinear``,
+ ``bicubic_fast`` or ``sharpen``.
+
+ ``scale-window=<window>``
+ (Advanced users only) Choose a custom windowing function for the kernel.
+ Defaults to the filter's preferred window if unset. Use
+ ``scale-window=help`` to get a list of supported windowing functions.
+
+ ``scale-wparam=<window>``
+ (Advanced users only) Configure the parameter for the window function
+ given by ``scale-window``. Ignored if the window is not tunable.
+ Currently, this affects the following window parameters:
+
+ kaiser
+ Window parameter (alpha). Defaults to 6.33.
+ blackman
+ Window parameter (alpha). Defaults to 0.16.
+ gaussian
+ Scale parameter (t). Increasing this makes the window wider.
+ Defaults to 1.
``scaler-resizes-only``
Disable the scaler if the video image is not resized. In that case,
@@ -357,21 +412,6 @@ Available video output drivers are:
will reproduce the source image perfectly if no scaling is performed.
Note that this option never affects ``cscale``.
- ``srgb``
- Convert and color correct the output to sRGB before displaying it on
- the screen. This option enables ``linear-scaling``.
-
- This option is equivalent to using ``icc-profile`` with an sRGB ICC
- profile, but it is implemented without a 3DLUT and does not require
- LittleCMS 2. If both ``srgb`` and ``icc-profile`` are present, the
- latter takes precedence, as they are somewhat redundant.
-
- Note: When playing back BT.2020 content with this option enabled, out
- of gamut colors will be numerically clipped, which can potentially
- change the hue and/or luminance. If this is not desired, it is
- recommended to use ``icc-profile`` with an sRGB ICC profile instead,
- when playing back wide-gamut BT.2020 content.
-
``pbo``
Enable use of PBOs. This is slightly faster, but can sometimes lead to
sporadic and temporary image corruption (in theory, because reupload
@@ -417,32 +457,59 @@ Available video output drivers are:
debug OpenGL context (which does nothing with current graphics drivers
as of this writing).
+ ``interpolation``
+ Reduce stuttering caused by mismatches in the video fps and display
+ refresh rate (also known as judder).
+
+ This essentially attempts to interpolate the missing frames by
+ convoluting the video along the temporal axis. The filter used can be
+ controlled using the ``tscale`` setting.
+
+ Note that this relies on vsync to work, see ``swapinterval`` for more
+ information.
+
``swapinterval=<n>``
Interval in displayed frames between two buffer swaps.
1 is equivalent to enable VSYNC, 0 to disable VSYNC. Defaults to 1 if
not specified.
- ``cscale=<filter>``
- As ``scale``, but for interpolating chroma information. If the image
- is not subsampled, this option is ignored entirely. Note that the
- implementation is currently always done as a single pass, so using
- it with separable filters will result in slow performance for very
- little visible benefit.
+ Note that this depends on proper OpenGL vsync support. On some platforms
+ and drivers, this only works reliably when in fullscreen mode. It may
+ also require driver-specific hacks if using multiple monitors, to
+ ensure mpv syncs to the right one. Compositing window managers can
+ also lead to bad results, as can missing or incorrect display FPS
+ information (see ``--display-fps``).
- ``scale-down=<filter>``
+ ``dscale=<filter>``
Like ``scale``, but apply these filters on downscaling instead. If this
option is unset, the filter implied by ``scale`` will be applied.
- ``cscale-param1``, ``cscale-param2``, ``cscale-radius``, ``cscale-antiring``
- Set filter parameters and radius for ``cscale``.
+ ``cscale=<filter>``
+ As ``scale``, but for interpolating chroma information. If the image
+ is not subsampled, this option is ignored entirely.
+
+ ``tscale=<filter>``
+ The filter used for interpolating the temporal axis (frames). This is
+ only used if ``interpolation`` is enabled. The only valid choices
+ for ``tscale`` are separable convolution filters (use ``tscale=help``
+ to get a list). The default is ``oversample``.
+
+ Note that the maximum supported filter radius is currently 3, and that
+ using filters with larger radius may introduce issues when pausing or
+ framestepping, proportional to the radius used. It is recommended to
+ stick to a radius of 1 or 2.
+
+ ``dscale-radius``, ``cscale-radius``, ``tscale-radius``, etc.
+ Set filter parameters for ``dscale``, ``cscale`` and ``tscale``,
+ respectively.
- See ``scale-param1``, ``scale-param2``, ``scale-radius`` and
- ``scale-antiring``.
+ See the corresponding options for ``scale``.
``linear-scaling``
- Scale in linear light. This is automatically enabled if ``srgb``,
- ``icc-profile`` or ``sigmoid-upscaling`` is set. It should only
- be used with a ``fbo-format`` that has at least 16 bit precision.
+ Scale in linear light. This is automatically enabled if
+ ``target-prim``, ``target-trc``, ``icc-profile`` or
+ ``sigmoid-upscaling`` is set. It should only be used with a
+ ``fbo-format`` that has at least 16 bit precision.
``fancy-downscaling``
When using convolution based filters, extend the filter size
@@ -481,6 +548,17 @@ Available video output drivers are:
X11/GLX only.
+ ``dwmflush=<no|windowed|yes>``
+ Calls ``DwmFlush`` after swapping buffers on Windows (default: no).
+ It also sets ``SwapInterval(0)`` to ignore the OpenGL timing. Values
+ are: no (disabled), windowed (only in windowed mode), yes (also in
+ full screen).
+ This may help getting more consistent frame intervals, especially with
+ high-fps clips - which might also reduce dropped frames. Typically a
+ value of 1 should be enough since full screen may bypass the DWM.
+
+ Windows only.
+
``sw``
Continue even if a software renderer is detected.
@@ -524,13 +602,67 @@ Available video output drivers are:
0.8
Pitch black room
+ ``gamma-auto``
+ Automatically corrects the gamma value depending on ambient lighting
+ conditions (adding a gamma boost for dark rooms).
+
+ With ambient illuminance of 64lux, mpv will pick the 1.0 gamma value
+ (no boost), and slightly increase the boost up until 0.8 for 16lux.
+
+ NOTE: Only implemented on OS X.
+
+ ``target-prim=<value>``
+ Specifies the primaries of the display. Video colors will be adapted
+ to this colorspace if necessary. Valid values are:
+
+ auto
+ Disable any adaptation (default)
+ bt.470m
+ ITU-R BT.470 M
+ bt.601-525
+ ITU-R BT.601 (525-line SD systems, eg. NTSC), SMPTE 170M/240M
+ bt.601-625
+ ITU-R BT.601 (625-line SD systems, eg. PAL/SECAM), ITU-R BT.470 B/G
+ bt.709
+ ITU-R BT.709 (HD), IEC 61966-2-4 (sRGB), SMPTE RP177 Annex B
+ bt.2020
+ ITU-R BT.2020 (UHD)
+ apple
+ Apple RGB
+ adobe
+ Adobe RGB (1998)
+ prophoto
+ ProPhoto RGB (ROMM)
+ cie1931
+ CIE 1931 RGB (not to be confused with CIE XYZ)
+
+ ``target-trc=<value>``
+ Specifies the transfer characteristics (gamma) of the display. Video
+ colors will be adjusted to this curve. Valid values are:
+
+ auto
+ Disable any adaptation (default)
+ bt.1886
+ ITU-R BT.1886 curve, without the brightness drop (approx. 1.961)
+ srgb
+ IEC 61966-2-4 (sRGB)
+ linear
+ Linear light output
+ gamma1.8
+ Pure power curve (gamma 1.8), also used for Apple RGB
+ gamma2.2
+ Pure power curve (gamma 2.2)
+ gamma2.8
+ Pure power curve (gamma 2.8), also used for BT.470-BG
+ prophoto
+ ProPhoto RGB (ROMM)
+
``icc-profile=<file>``
Load an ICC profile and use it to transform linear RGB to screen output.
- Needs LittleCMS 2 support compiled in. This option overrides the ``srgb``
- property, as using both is somewhat redundant. It also enables
+ Needs LittleCMS 2 support compiled in. This option overrides the
+ ``target-prim`` and ``target-trc`` options. It also enables
``linear-scaling``.
-
``icc-profile-auto``
Automatically select the ICC display profile currently specified by
the display settings of the operating system.
@@ -544,9 +676,8 @@ Available video output drivers are:
Its size depends on the ``3dlut-size``, and can be very big.
``icc-intent=<value>``
- Specifies the ICC Intent used for transformations between color spaces.
- This affects the rendering when using ``icc-profile`` or ``srgb`` and
- also affects the way DCP XYZ content gets converted to RGB.
+ Specifies the ICC intent used for the color transformation (when using
+ ``icc-profile``).
0
perceptual
@@ -562,6 +693,28 @@ Available video output drivers are:
Default is 128x256x64.
Sizes must be a power of two, and 512 at most.
+ ``blend-subtitles=<yes|video|no>``
+ Blend subtitles directly onto upscaled video frames, before
+ interpolation and/or color management (default: no). Enabling this
+ causes subtitles to be affected by ``icc-profile``, ``target-prim``,
+ ``target-trc``, ``interpolation``, ``gamma`` and ``linear-scaling``.
+ It also increases subtitle performance when using ``interpolation``.
+
+ The downside of enabling this is that it restricts subtitles to the
+ visible portion of the video, so you can't have subtitles exist in the
+ black margins below a video (for example).
+
+ If ``video`` is selected, the behavior is similar to ``yes``, but subs
+ are drawn at the video's native resolution, and scaled along with the
+ video.
+
+ .. warning:: This changes the way subtitle colors are handled. Normally,
+ subtitle colors are assumed to be in sRGB and color managed
+ as such. Enabling this makes them treated as being in the
+ video's color space instead. This is good if you want
+ 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).
@@ -577,10 +730,6 @@ Available video output drivers are:
no
Ignore alpha component.
- ``chroma-location=<auto|center|left>``
- Set the YUV chroma sample location. auto means use the bitstream
- flags (default: auto).
-
``rectangle-textures``
Force use of rectangle textures (default: no). Normally this shouldn't
have any advantages over normal textures. Note that hardware decoding
@@ -590,54 +739,12 @@ Available video output drivers are:
Color used to draw parts of the mpv window not covered by video.
See ``--osd-color`` option how colors are defined.
- ``smoothmotion``
- Reduce stuttering caused by mismatches in video fps and display
- refresh rate (also known as judder).
-
- Instead of drawing source frames for variable durations, smoothmotion
- will blend frames that overlap the transition between two frames in
- the source material.
-
- For example, a 24 Hz clip played back on a 60 Hz display would normally
- result in a pattern like this::
-
- A A A B B C C C D D E E E F F
-
- which has different lengths, alternating between 3 and 2. This
- difference in frame duration is what causes judder.
-
- With smoothmotion enabled, the pattern changes to::
-
- A A A+B B B C C C+D D D E E E+F F F
-
- where A+B is a blend of A and B. In this pattern, each frame gets a
- (consistent) duration of roughly 2.5 - resulting in smooth motion.
-
- GPU drivers or compositing window managers overriding vsync behavior
- can lead to bad results. If the framerate is close to or over the
- display refresh rate, results can be bad as well.
-
- .. note:: On systems other than Linux or OS X, you currently must set
- the ``--display-fps`` option, or the results will be bad.
-
- ``smoothmotion-threshold=<0.0-0.5>``
- Mix threshold at which interpolation is skipped (default: 0.0 – never
- skip).
-
- For example, with a ``smoothmotion-threshold`` of 0.1, if the
- smoothmotion algorithm would try to blend two frames by a ratio of
- 95% A + 5% B, it would simply display A instead. (Since the
- distance, 0.05, is lower than the threshold)
-
- Setting this to 0.5 would be similar to disabling smoothmotion
- completely, since it would always just display the nearest frame.
-
``opengl-hq``
Same as ``opengl``, but with default settings for high quality rendering.
This is equivalent to::
- --vo=opengl:scale=spline36:scale-down=mitchell:dither-depth=auto:fbo-format=rgba16:fancy-downscaling:sigmoid-upscaling
+ --vo=opengl:scale=spline36:cscale=spline36:dscale=mitchell:dither-depth=auto:fbo-format=rgba16:fancy-downscaling:sigmoid-upscaling
Note that some cheaper LCDs do dithering that gravely interferes with
``opengl``'s dithering. Disabling dithering with ``dither-depth=no`` helps.
@@ -796,7 +903,33 @@ Available video output drivers are:
clear
Drop all frames in the frame queue.
- This also supports many of the suboptions the ``opengl`` VO has. Runs
+ This also supports many of the suboptions the ``opengl`` VO has. Run
``mpv --vo=opengl-cb:help`` for a list.
This also supports the ``vo_cmdline`` command.
+
+``rpi`` (Raspberry Pi)
+ Native video output on the Raspberry Pi using the MMAL API.
+
+ ``display=<number>``
+ Select the display number on which the video overlay should be shown
+ (default: 0).
+
+ ``layer=<number>``
+ Select the dispmanx layer on which the video overlay should be shown
+ (default: -10). Note that mpv will also use the 2 layers above the
+ selected layer, to handle the window background and OSD. Actual video
+ rendering will happen on the layer above the selected layer.
+
+``drm`` (Direct Rendering Manager)
+ Video output driver using Kernel Mode Setting / Direct Rendering Manager.
+ Does not support hardware acceleration. Should be used when one doesn't
+ want to install full-blown graphical environment (e.g. no X).
+
+ ``connector=<number>``
+ Select the connector to use (usually this is a monitor.) If set to -1,
+ mpv renders the output on the first available connector. (default: -1)
+
+ ``devpath=<filename>``
+ Path to graphic card device.
+ (default: /dev/dri/card0)
diff --git a/DOCS/mplayer-changes.rst b/DOCS/mplayer-changes.rst
index a4f3467..e98daf4 100644
--- a/DOCS/mplayer-changes.rst
+++ b/DOCS/mplayer-changes.rst
@@ -46,6 +46,11 @@ Input
* Allow customizing whether a key binding for seeking shows the video time, the
OSD bar, or nothing (see the `Input Command Prefixes`_ section).
* Support mapping multiple commands to one key.
+* Classic LIRC support was removed. Install remotes as input devices instead.
+ This way they will send X11 key events to the mpv window, which can be bound
+ using the normal ``input.conf``.
+* Joystick support was removed. It was considered useless and was the cause
+ of some problems (e.g. a laptop's accelerator being recognized as joystick).
Audio
~~~~~
@@ -227,7 +232,6 @@ Command Line Switches
``-idx`` ``--index``
``-lavdopts ...`` ``--vd-lavc-...``
``-lavfdopts`` ``--demuxer-lavf-...``
- ``-lircconf`` ``--input-lirc-conf``
``-loop 0`` ``--loop=inf``
``-mixer-channel`` AO suboptions (``alsa``, ``oss``)
``-mixer`` AO suboptions (``alsa``, ``oss``)
@@ -239,7 +243,6 @@ Command Line Switches
``-noar`` ``--no-input-appleremote``
``-noautosub`` ``--no-sub-auto``
``-noconsolecontrols`` ``--no-input-terminal``
- ``-nojoystick`` ``--no-input-joystick``
``-nosound`` ``--no-audio``
``-osdlevel`` ``--osd-level``
``-panscanrange`` ``--video-zoom``, ``--video-pan-x/y``
diff --git a/DOCS/waf-buildsystem.rst b/DOCS/waf-buildsystem.rst
index cc05511..fa71cbb 100644
--- a/DOCS/waf-buildsystem.rst
+++ b/DOCS/waf-buildsystem.rst
@@ -20,8 +20,9 @@ shortcomings:
part is this pieces are spread apart in the configure and copy pasted for
any single case. That brings us to..
-2) --enable-feature has to override the user and help him understand that he
- has libraries missing and should install them for the feature to be enabled.
+2) --enable-feature has to be overridden by the user and helps them understand that
+ they have libraries missing and should install them for the feature to be
+ enabled.
3) Must be customizable, hackable, pleasant to the developer eyes and to work
with in general.
diff --git a/README.md b/README.md
index 619d779..4f86765 100644
--- a/README.md
+++ b/README.md
@@ -1,22 +1,39 @@
-mpv
-===
+![http://mpv.io/](https://raw.githubusercontent.com/mpv-player/mpv.io/master/source/images/mpv-logo-128.png)
+
+## mpv
+
+--------------
+
+
+* [Overview](#overview)
+* [Downloads](#downloads)
+* [Compilation](#compilation)
+* [FFmpeg vs. Libav](#ffmpeg-vs-libav)
+* [Release cycle](#release-cycle)
+* [Bug reports](#bug-reports)
+* [Contributing](#contributing)
+* [Relation to MPlayer and mplayer2](#relation-to-mplayer-and-mplayer2)
+* [Wiki](https://github.com/mpv-player/mpv/wiki)
+* [Man pages](http://mpv.io/manual/master/)
+* [Contact](#contact)
+* [License](#license)
+
+## Overview
-Overview
---------
**mpv** is a media player based on MPlayer and mplayer2. It supports a wide
variety of video file formats, audio and video codecs, and subtitle types.
Releases can be found on the [release list][releases].
-Downloads
----------
+## Downloads
+
For semi-official builds and third-party packages please see
[mpv.io](http://mpv.io/installation/).
-Compilation
------------
+## Compilation
+
Compiling with full features requires development files for several
external libraries. Below is a list of some important requirements.
@@ -50,10 +67,10 @@ Essential dependencies (incomplete list):
- Audio output development headers (libasound/ALSA, pulseaudio)
- FFmpeg libraries (libavutil libavcodec libavformat libswscale libavfilter
and either libswresample or libavresample)
- At least FFmpeg 2.1.4 or Libav 10 is required.
+ At least FFmpeg 2.4.0 or Libav 11 is required.
- zlib
- iconv (normally provided by the system libc)
-- libass
+- libass (OSD, OSC, text subtitles)
- Lua (optional, required for the OSC pseudo-GUI and youtube-dl integration)
- libjpeg (optional, used for screenshots only)
- Enca (optional, for subtitle charset detection)
@@ -89,16 +106,16 @@ 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].
-FFmpeg vs. Libav
-----------------
+## FFmpeg vs. Libav
+
Generally, mpv should work with the latest release as well as the git version
of both FFmpeg and Libav. But FFmpeg is preferred, and some mpv features work
-with FFmpeg only. See the [wiki article][ffmpeg_vs_libav] about the issue.
+with FFmpeg only (subtitle formats in particular).
-Release cycle
--------------
+## Release cycle
+
Every few months, a new release is cut off of the master branch and is assigned
a 0.X.0 version number.
@@ -116,21 +133,21 @@ Releases other than the latest release are unsupported and unmaintained.
See the [release policy document][release-policy] for more information.
-Bug reports
------------
+## Bug reports
+
Please use the [issue tracker][issue-tracker] provided by GitHub to send us bug
reports or feature requests.
-Contributing
-------------
+## Contributing
+
For small changes you can just send us pull requests through GitHub. For bigger
changes come and talk to us on IRC before you start working on them. It will
make code review easier for both parties later on.
-Relation to MPlayer and mplayer2
---------------------------------
+## Relation to MPlayer and mplayer2
+
mpv is based on mplayer2, which in turn is based on the original MPlayer
(also called mplayer, mplayer-svn, mplayer1). Many changes have been made, a
@@ -151,8 +168,8 @@ MPlayer drop-in replacement.
If you are wondering what's different from mplayer2 and MPlayer, an incomplete
list of changes is located [here][mplayer-changes].
-Contact
--------
+## Contact
+
Most activity happens on the IRC channel and the github issue tracker. The
mailing lists are mostly unused.
@@ -176,3 +193,8 @@ only if discretion is required.
[release-policy]: https://github.com/mpv-player/mpv/blob/master/DOCS/release-policy.md
[windows_compilation]: https://github.com/mpv-player/mpv/blob/master/DOCS/compile-windows.md
[mplayer-changes]: https://github.com/mpv-player/mpv/blob/master/DOCS/mplayer-changes.rst
+
+## License
+
+
+[GPLv2](https://github.com/mpv-player/mpv/blob/master/LICENSE)
diff --git a/RELEASE_NOTES b/RELEASE_NOTES
index bd999f5..56537ab 100644
--- a/RELEASE_NOTES
+++ b/RELEASE_NOTES
@@ -1,3 +1,159 @@
+Release 0.9.0
+=============
+
+Changes
+-------
+
+Changes that may break users' config files have been annotated with a `(!)`.
+
+- Note: mpv is not compatible with Lua 5.3. Lua 5.1 or 5.2 is required.
+- The minimum required libass version is now 0.12.1 or newer.
+- The minimum required FFmpeg version is now 2.4.0 (equiv. Libav 11) or newer.
+- The internal libmpg123 support was removed. This was already not used by
+ default in the previous release.
+- `(!)` The LIRC support was removed. Configure LIRC remotes as input devices
+ instead.
+- `(!)` The Linux Joystick support was removed.
+- `(!)` `vf_screenshot` was removed, as they are now handled at a VO level and
+ is compatible with all VOs.
+- `(!)` `--ass-use-margins` has been renamed to `--sub-use-margins` and applies
+ only to plain-text (non-ASS) subtitles (enabled by default). The new
+ `--ass-force-margins` option applies only to ASS subtitles (disabled by
+ default). To get the old behaviour back, enable both at the same time.
+- `(!)` The `--sub-scale-with-window` option now only applies to plain text
+ (non-ASS) subtitles (enabled by default). The new `--ass-scale-with-window`
+ option does the same but only with ASS subtitles (disabled by default).
+- `(!)` The range for the `param1` for the `gaussian` `vo_opengl` scaler has
+ been redefined. Instead of being an arbitrary 1-100 range, have a default
+ value of 1.0, and anything higher is blurrier.
+- `(!)` The `seek`, `playlist_next, `playlist_prev`, `loadfile` and `loadlist`
+ parameters no longer accept numerical parameters where symbolic parameter
+ names exist.
+- `(!)` `vo_opengl` changes:
+ - `(!)` The `smoothmotion` suboption has been renamed to `interpolation`.
+ The old name is still supported for now.
+ - `(!)` The `bilinear_slow` scaler has been renamed to `triangle`.
+ - `(!)` `scale-down` has been renamed to `dscale` and now has its own set of
+ config options (e.g. `dscale-radius`).
+ - `(!)` Scaler radius no longer defaults to `3` but to a preferred value
+ that may be different for each filter.
+ - The `scale-radius` option may now go down as low as `0.5`, which is the
+ value used by the `nearest` filter.
+ - `spline36` is the new `cscale` default for `opengl-hq`. This might break
+ setups that use `fbo-format=rgb8`. To work around it, leave `fbo-format`
+ as its default, or set to something higher than 8, or set `cscale=bilinear`,
+ the previous default.
+- `(!)` `vf_format` no longer converts video to YUYV if there is no parameter.
+ Video is now passed unchanged unless a format is specifically requested.
+- `(!)` The `--colormatrix`, `--colormatrix-input-range`,
+ `--colormatrix-output-range` and `--colormatrix-primaries` options have been
+ converted into `vf_format` suboptions. See commit 27715b7 and the manual for
+ details.
+- `vf_mirror`'s implementation was replaced with calling into `libavfilter`'s
+ `vf_hflip` filter, thus depending on `libavfilter` to function.
+- The `device` subption to `ao_wasapi` has been deprecated in favor of
+ `--audio-device`.
+- `--video-rotate` now allows 360 as an argument instead of stopping at 359.
+- Several improvements to `af_scaletempo`.
+- Options that have multiple options and also include a "yes" option now
+ default to that if specified with no arguments.
+- The default value of `--cache-default` is now 150000 (153.6 MB, ~146 MiB).
+- JPEG screenshots now use the same subsampling as the source video. The images
+ are still RGB regardless of source format though.
+
+Bug fixes
+---------
+
+- mpv no longer saves position on files that can't be resumed (issue #1701).
+- (X11) Fix the player thinking the mouse has left the window in some WMs /
+ embeddings (issue #1672).
+- mpv no longer freezes on wayland when the compositor stops asking it to draw
+ itself (e.g. when minimized) (issue #249).
+- `.ac3` files are no longer rejected by `--audio-file-auto` (issue #1759).
+- `ao_wasapi` now automatically enables `exclusive` when passthrough is
+ attempted (issue #1742).
+- Attempt to fix flickering on Intel VAAPI drivers with `--hwdec=vaapi` and
+ `--vo=opengl` (issue #1765).
+- `youtube-dl` will no longer download video streams when video playback
+ is disabled with `--no-video`.
+- (Windows) mpv now prevents system sleep when playing a video-only file.
+ Previously, only files with an opened audio track would prevent sleep.
+
+New features
+------------
+
+- `vo_opengl` features:
+ - Added `ewa_ginseng`, `ewa_hanning`, `robidoux`, `robidouxsharp`,
+ `oversample` and `haasnsoft` scalers.
+ - There are now `ewa_lanczossoft` and `ewa_lanczossharp` aliases to
+ `ewa_lanczos` that are tuned to be blurrier and sharper, respectively.
+ - Added `gamma-auto` option that uses ambient light sensors to automatically
+ adjust the video gamma. See commit c028d78 for details.
+ - Added `blend-subtitles` option to draw subtitles directly into the video
+ instead of rendered afterwards. Potentially necessary for correct rendering
+ with files that use ASS subtitles for typesetting in combination with an
+ `icc-profile`. There is a default option for drawing after upscaling,
+ and a `video` option for drawing before upscaling. See details and warning
+ on the manual.
+ - There is now a `tscale` option, used to choose the temporal scaler used
+ in the `interpolation` mode (previously `smoothmotion`).
+ - There is a new `scale-blur` parameter to adjust the amount of blur that
+ most of the filters produce. Deviating from the default may introduce
+ artifacts in EWA filters.
+ - (Windows) There is now a `dwmflush` option that might help improve
+ rendering of high-fps video. Disabled by default. See manual for options.
+- New Linux-only `vo_drm` video output driver. Uses the direct rendering /
+ kernel modesetting drivers to draw directly to the framebuffer, but with
+ no hardware acceleration. See manual for details.
+- New `pseudo-gui` builtin profile, automatically used when launched from
+ `mpv.desktop` by opening `mpv.exe` on windows (`mpv.com` still works as
+ usual), or by opening the `mpv.app` bundle. The `pseudo-gui` tries to make
+ the player window behave closer to what a desktop player would do, by not
+ immediately closing and allowing the user to drag&drop files for playback.
+ See manual for details.
+- mpv can now play directories by automatically playing their contents instead.
+ Works everywhere but on Windows, due to issues with Windows' C runtime.
+- Add support to pitch correct stretched audio with librubberband.
+- Add support for the Raspberry Pi 2's hardware decoder when FFmpeg (or Libav)
+ is built with `--enable-mmal`. See commit 8fff125 for details.
+- The `--cache` option now accepts a "yes" option, that always enables a
+ `--cache-default`-sized cache on all cases a cache can be used, unless
+ `--cache-default` disables caching.
+- `ao_pulse`, `ao_coreaudio` and `ao_wasapi` now support device hotplugging.
+- New `--osd-align-x` and `--osd-align-y` options can be used to align the OSD
+ independently from subtitles.
+- New `--osd-bold` and `--sub-text-bold` options can be enabled to bold all
+ OSD or plain-text subtitle text, respectively.
+- Added a default keybind to the `u` key that enables/disables ASS style
+ overriding. When enabled, is equivalent to `--ass-style-override=force`.
+- There is now a `MOUSE_ENTER` keybind, that is called when the mouse cursor
+ enters the VO from outside.
+- The new `--ytdl-params` option can be set to arguments that are always given
+ to `youtube-dl` invocations. There is no sanity checking, so invalid options
+ can prevent `mpv` from working with http URLs.
+- There's a new `--demuxer-mkv-fix-timestamps` option, enabled by default, that
+ tries to guess more accurate video timestamps by using FPS information, if
+ available. See manual for details.
+- The new `--window-scale` option can be used to scale the video window by the
+ specified multiplier, before other options such as `--autofit` are applied.
+- `vo_direct3d_shaders` now supports NV12 colorspace without using stretchrect,
+ but it seems no drivers actually support that.
+- (Client API) Added a `rescan_external_files` command, as requested on issue
+ #1586.
+- (Client API) If enabled, initializing the Client API will now also load the
+ user's `mpv.conf`.
+- (Client API) There's a new `mpv_opengl_cb_report_flip()` call for API users
+ to call to inform mpv of when exactly a frame was displayed.
+- (Client API) The `mpv_opengl_cb_render()` function was deprecated in favor
+ of the new, simpler `mpv_opengl_cb_draw()` function.
+- (Client API) There is a new `screenshot_raw` command. See `input.rst` for
+ details.
+- The `--input-file` argument may now also be a file descriptor in the form
+ "fd://N", where N is the FD number.
+
+This listing is not complete. A complete changelog can be seen by running
+`git log v0.8.0..v0.9.0` in the git repository.
+
Release 0.8.3
=============
@@ -11,6 +167,9 @@ Bug fixes
---------
- (X11) Fix crash on `vo_xv:no-colorkey` with the Overlay adapter. (bug #1629)
+- (X11) `--stop-screensaver` is now implemented by calling into
+ `xdg-screensaver`, fixing some compatibility issues that prevented it from
+ actually stopping screensavers.
- Make the video equalizer work correctly on some VAAPI drivers. (bug #1647)
- Prevent OSD from disappearing when clicking on mozplugger. (bug #1672)
- The new DVB-S2 support code, using S2API, now builds on FreeBSD.
@@ -232,3 +391,218 @@ New features
This listing is not complete. A complete changelog can be seen by running
`git log v0.7.0..v0.8.0` in the git repository.
+
+Release 0.7.3
+=============
+
+Changes
+-------
+
+- Several documentation updates and typo fixes.
+- Don't show "0%" position when the stream is infinite / has unknown length.
+- Fixes the config file loading order so that lower priority `mpv.conf` files
+ don't override higher priority `config` files.
+- (OSX) Remove coreaudio_exclusive from the "auto" codec list.
+- Failing to create a GL3 context is now a warning instead of error, to reduce
+ user confusion.
+- The subtitle decoder now gets reset when cycling subtitles. This makes the
+ currently shown subtitle event disappear even if cycling back to the current
+ subtitle track.
+- `--shuffle` and `--merge-files` now affects the contents of playlist files,
+ instead of just the list of files given as arguments to mpv.
+- `./waf install` avoids installing a few additional data/config files if only
+ libmpv was built.
+- Improved channel mapping when the file's channel map doesn't match the AO's
+ available channel maps.
+- (OSX) VDA now gracefully refuses to run on non-OpenGL3-capable systems instead
+ of trying and failing to build its shaders.
+- Add workaround for vf_vapoursynth filters that expect an FPS.
+- The default value for `--screenshot-template` now shows up in
+ `--list-options`.
+- `ao_pulse`'s `latency-hacks` suboption is now off by default, as it causes
+ issues with newer pulseaudio releases. If A/V Sync issues happen, either
+ add the `latency-hacks=on` pulse suboption in mpv.conf, or update the
+ pulseaudio daemon (bug #1430).
+- (Linux) `ao_alsa` now deals better with audio device disconnection.
+- Client API: timestamp properties that have no value return "no" instead of
+ magic number.
+
+Bug fixes
+---------
+
+- (Windows, OSX) Many `ao_wasapi` and `ao_coreaudio` fixes.
+- (OSX) Make the window title update correctly on OS X Yosemite.
+- Fixes for handling mono audio on various AOs.
+- (Linux) Fixes resuming from suspend on ao_alsa.
+- Fixes for playlist file parsing.
+- Overly long options in the --list-options output now break the column layout
+ instead of getting truncated; fixes shell completion scripts.
+- Fix rendering resolution of certain DVB subtitles (bug #1425).
+- Fix EDL or --merge-files breaking timestamps with .avi files.
+- Workaround for libavcodec bug with the VP9 codec parser (bug #1448),
+ fixes possible crash.
+- Improve robustness of the matroska parser with broken files (bugs #1457,
+ #1461).
+- Improve 10bit video compatibility with older GPUs (specially Intel) (bug
+ #1383).
+- Fixes flashing the VO window when playing a list of files that includes
+ non-media files (bug #1459).
+- Workaround for window embedding in OpenBox (bug #1235)
+- Fixes for several crashes and lockups (bugs #838, #1389, #1408, #1463, #1473,
+ #1474).
+- Fixes for the zsh completion script (bugs #997)
+
+New features
+------------
+
+- mpv now prints the contents of its config.h when running in verbose mode, to
+ aid with debugging.
+- The `include=` option now accepts `~` to refer to the HOME dir (bug #1406).
+- `af_volume` now prints ReplayGain values in verbose mode.
+- m3u playlists that don't have the `#EXTM3U` header are now accepted if they
+ "look like" ASCII or UTF-8 text and have the .m3u extension.
+- Chapter marks in the seek bar now update when switching files.
+- Supports embedded cover art in MKV files (bug #1374).
+- (Windows) Video window can now be resized even with --no-border.
+- (Windows) Client API: the "run" command now works on Windows too.
+- (Linux) vo_wayland now supports key modifiers (Meta, Alt, Control, Shift)
+- Client API: "display-names" property is now observable.
+
+This listing is not complete. A complete changelog can be seen by running
+`git log v0.7.2..v0.7.3` in the git repository.
+
+Release 0.7.2
+=============
+
+Changes
+-------
+
+- Give precedence to the DVD menu navigation keyboard bindings so that user
+ defined LEFT/RIGHT/... bindings don't break DVD menu navigation.
+- Try to fallback to the "default" device if the selected device is busy in the
+ alsa AO.
+- Don't create Dock icon for audio only files on OS X.
+- Save screenshots to desktop when using the app bundle on OS X.
+- Restore ab-loop settings with playback resume.
+- Bump required youtube-dl version to 2014.11.26 and enable the ytdl_hook Lua
+ script by default (now playing videos from YouTube and the like will work
+ out of the box without any configuration change needed).
+
+Bug fixes
+---------
+
+- Don't signal an error if --stream-dump is used.
+- Fix removing key bindings from Lua scripts.
+- Reject channel descriptions with too many channels in the coreaudio AO.
+- Don't async redraw when waiting for VO redraw on OS X (this fixes the very
+ annoying glitch where the black bars disappear for a single frame when going
+ fullscreen).
+- Fix mono playback with the also AO.
+- Don't crash if framebuffers are not available in the opengl VO.
+
+New features
+------------
+
+- Try to handle multi-arc videos in the ytdl_hook Lua script.
+
+This listing is not complete. There are many more bug fixes and changes. The
+complete change log can be viewed by running ``git log 47ec404..c7d6b21`` in
+the git repository.
+
+Release 0.7.1
+=============
+
+Changes
+-------
+
+- Don't show the volume neutral marker on the OSD if softvol is disabled.
+- Don't select a subtitle track when executing the sub_add input command in
+ "auto" mode.
+
+Bug fixes
+---------
+
+- Fix busy loop when seeking while paused (this fixes a problem with pulseaudio
+ that caused mpv and the pulseaudio daemon to use 100% CPU).
+- Fix Lua function utils.subprocess() in Windows versions older than Vista.
+- Avoid creating a window bigger than the screen on Windows.
+- Don't ignore the last line in m3u playlists.
+- Don't crash if a codec could not be opened.
+- Dynamically allocate audio channel map entries (this should fix a crash in
+ the alsa and coreaudio AOs with audio devices that support more than 20
+ channel maps).
+- Ignore the "srgb" option in the opengl VO if hardware decoding is enabled.
+- Linearize non-RGB sRGB files correctly (eg. JPEG).
+- Fix opening reference URLs (.file/id=) on OS X.
+
+This listing is not complete. There are many more bug fixes and changes. The
+complete change log can be viewed by running ``git log 8d8b36d..6583ad6`` in
+the git repository.
+
+Release 0.7.0
+=============
+
+Changes
+-------
+
+- Buffer partial log messages in the client API (the client API will now only
+ pass full log messages to clients).
+- Remove ncurses/terminfo/termcap support (it was disabled by default and
+ replaced by new code since v0.6.0).
+- Enable cdda:// support by default again (it was disabled since v0.6.0).
+- Cascade-load input.conf (if there are several input.confs in the set of valid
+ config paths, load them all).
+- Draw the OSD twice in 3D mode (this fixes subtitles display in 3D mode).
+- Make wasapi the default AO on Windows again since many of its problems have
+ been solved.
+- Use "site-functions" subdir to install the zsh completion script instead of
+ the Debian-specific "vendor-completions" (also provide the --zshdir waf
+ configure option for changing this value).
+- Improve synchronization between the Cocoa GUI and the player (this fixes some
+ long standing deadlock issues on Mac OS X).
+- Remove --fs-missioncontrol option (only relevant to Mac OS X).
+
+New features
+------------
+
+- Enable pitch correction by default when playing at higher speeds (this can be
+ controlled with the --audio-pitch-correction option).
+- Open stream and demuxer asynchronously (this should avoid having the player
+ get blocked on network streams).
+- Add cache-buffering-state property for querying the cache fill status until
+ the player unpauses.
+- Add support for listing and selecting the audio device (note that it is not
+ implemented for all AOs, see the --audio-device option for more information).
+- Add support for a JSON-based IPC mechanism (note that this is not currently
+ supported on Windows, see the JSON IPC section in the manpage for more
+ information).
+- Add Lua utility function for starting processes (see utils.subprocess() in the
+ manpage).
+- Add Lua utility function for parsing JSON (see utils.parse_json() in the
+ manpage).
+- Add field-dominance property (see --field-dominance option).
+- Add video-rotate property (see --video-rotate option).
+- Add playback-abort property for querying whether playback is stopped or is to
+ be stopped.
+- Add cursor-autohide property (see --cursor-autohide option).
+- Add vo-configured property for querying whether a window is created.
+- Add support for dxva2 hardware acceleration on Windows.
+- Drop libquvi support (this has been replaced by a built-in Lua script that
+ invokes the youtube-dl tool, which needs to be installed, see the --ytdl
+ option).
+- Add support for loading chapters from an external file (see the
+ --chapters-file option).
+- Add window-minimized property for querying whether the window is minimized
+ (works for X11 only).
+- Make it possible to configure the OSC seekbar style (see the "seekbarstyle"
+ OSC option).
+- Add support for libmpv on Mac OS X (it used to be broken, now it's fully
+ functional and also provides support for embedding the mpv window inside a
+ Cocoa/Qt application).
+- Try to use the audio channel map reported by ALSA in the alsa AO.
+- Add option to disable text OSD rendering completely (useful for working around
+ certain fontconfig issues, see the --use-text-osd option).
+
+This listing is not complete. There are many more bug fixes and changes. The
+complete change log can be viewed by running ``git log 7759c18..9479daa`` in
+the git repository.
diff --git a/TOOLS/lua/README.md b/TOOLS/lua/README.md
index f7cdf2a..e904482 100644
--- a/TOOLS/lua/README.md
+++ b/TOOLS/lua/README.md
@@ -10,3 +10,5 @@ to mpv's command line.
Where appropriate, they may also be placed in ~/.config/mpv/scripts/ from
where they will be automatically loaded when mpv starts.
+
+Some of these are just for testing mpv internals.
diff --git a/TOOLS/lua/audio-hotplug-test.lua b/TOOLS/lua/audio-hotplug-test.lua
new file mode 100644
index 0000000..f27b793
--- /dev/null
+++ b/TOOLS/lua/audio-hotplug-test.lua
@@ -0,0 +1,13 @@
+local utils = require("mp.utils")
+
+mp.observe_property("audio-device-list", "native", function(name, val)
+ print("Audio device list changed:")
+ for index, e in ipairs(val) do
+ print(" - '" .. e.name .. "' (" .. e.description .. ")")
+ end
+end)
+
+mp.observe_property("audio-out-detected-device", "native", function(name, val)
+ print("Detected audio device changed:")
+ print(" - '" .. val)
+end)
diff --git a/TOOLS/lua/autoload.lua b/TOOLS/lua/autoload.lua
index 433b213..0730397 100644
--- a/TOOLS/lua/autoload.lua
+++ b/TOOLS/lua/autoload.lua
@@ -7,6 +7,17 @@
-- Add at most 5 * 2 files when starting a file (before + after).
MAXENTRIES = 5
+function Set (t)
+ local set = {}
+ for _, v in pairs(t) do set[v] = true end
+ return set
+end
+
+EXTENSIONS = Set {
+ 'mkv', 'avi', 'mp4', 'ogv', 'webm', 'rmvb', 'flv', 'wmv', 'mpeg', 'mpg', 'm4v', '3gp',
+ 'mp3', 'wav', 'ogv', 'flac', 'm4a', 'wma',
+}
+
mputils = require 'mp.utils'
function add_files_at(index, files)
@@ -18,20 +29,44 @@ function add_files_at(index, files)
end
end
+function get_extension(path)
+ return string.match(path, "%.([^%.]+)$" )
+end
+
+table.filter = function(t, iter)
+ for i = #t, 1, -1 do
+ if not iter(t[i]) then
+ table.remove(t, i)
+ end
+ end
+end
+
function find_and_add_entries()
local path = mp.get_property("path", "")
local dir, filename = mputils.split_path(path)
if #dir == 0 then
return
end
+
local files = mputils.readdir(dir, "files")
if files == nil then
return
end
- table.sort(files)
+ table.filter(files, function (v, k)
+ local ext = get_extension(v)
+ if ext == nil then
+ return false
+ end
+ return EXTENSIONS[string.lower(ext)]
+ end)
+ table.sort(files, function (a, b)
+ return string.lower(a) < string.lower(b)
+ end)
+
if dir == "." then
dir = ""
end
+
local pl = mp.get_property_native("playlist", {})
local pl_current = mp.get_property_number("playlist-pos", 0) + 1
-- Find the current pl entry (dir+"/"+filename) in the sorted dir list
@@ -45,6 +80,7 @@ function find_and_add_entries()
if current == nil then
return
end
+
local append = {[-1] = {}, [1] = {}}
for direction = -1, 1, 2 do -- 2 iterations, with direction = -1 and +1
for i = 1, MAXENTRIES do
@@ -53,6 +89,7 @@ function find_and_add_entries()
if file == nil or file[1] == "." then
break
end
+
local filepath = dir .. file
if pl_e then
-- If there's a playlist entry, and it's the same file, stop.
@@ -60,6 +97,7 @@ function find_and_add_entries()
break
end
end
+
if direction == -1 then
if pl_current == 1 then -- never add additional entries in the middle
mp.msg.info("Prepending " .. file)
@@ -71,6 +109,7 @@ function find_and_add_entries()
end
end
end
+
add_files_at(pl_current + 1, append[1])
add_files_at(pl_current, append[-1])
end
diff --git a/TOOLS/lua/ontop-playback.lua b/TOOLS/lua/ontop-playback.lua
new file mode 100644
index 0000000..b02716c
--- /dev/null
+++ b/TOOLS/lua/ontop-playback.lua
@@ -0,0 +1,19 @@
+--makes mpv disable ontop when pausing and re-enable it again when resuming playback
+--please note that this won't do anything if ontop was not enabled before pausing
+
+local was_ontop = false
+
+mp.observe_property("pause", "bool", function(name, value)
+ local ontop = mp.get_property_native("ontop")
+ if value then
+ if ontop then
+ mp.set_property_native("ontop", false)
+ was_ontop = true
+ end
+ else
+ if was_ontop and not ontop then
+ mp.set_property_native("ontop", true)
+ end
+ was_ontop = false
+ end
+end)
diff --git a/TOOLS/lua/pause-when-minimize.lua b/TOOLS/lua/pause-when-minimize.lua
index 62e3ff5..99add70 100644
--- a/TOOLS/lua/pause-when-minimize.lua
+++ b/TOOLS/lua/pause-when-minimize.lua
@@ -2,8 +2,6 @@
-- if it's brought back again. If the player was already paused when minimizing,
-- then try not to mess with the pause state.
--- Note: currently works with X11 only.
-
local did_minimize = false
mp.observe_property("window-minimized", "bool", function(name, value)
diff --git a/TOOLS/lua/youtube-starttime.lua b/TOOLS/lua/youtube-starttime.lua
new file mode 100644
index 0000000..ea8e9ab
--- /dev/null
+++ b/TOOLS/lua/youtube-starttime.lua
@@ -0,0 +1,34 @@
+--sets the startime of a youtube video as specified in the "t=HHhMMmSSs" part of the url
+--NOTE: This might become obsolete once youtube-dl adds the functionality
+
+local msg = require 'mp.msg'
+
+function youtube_starttime()
+ url = mp.get_property("path", "")
+ start = 0
+
+ if string.find(url, "youtu%.?be") and
+ ((url:find("http://") == 1) or (url:find("https://") == 1)) then
+ time = string.match(url, "[#&%?]t=%d*h?%d*m?%d+s?m?h?")
+ --the time-string can start with #, & or ? followed by t= and the timing parameters
+ --at least one number needs to be present after t=, followed by h, m, s or nothing (>implies s)
+
+ if time then
+ for pos in string.gmatch(time,"%d+%a?") do
+ if string.match(pos,"%d+h") then --find out multiplier for
+ multiplier = 60*60 --hours
+ elseif string.match(pos,"%d+m") then
+ multiplier = 60 --minutes
+ else multiplier = 1 end --seconds
+
+ start = start + (string.match(pos,"%d+") * multiplier)
+ end
+
+ msg.info("parsed '" .. time .. "' into '" .. start .. "' seconds")
+ end
+
+ mp.set_property("file-local-options/start",start)
+ end
+end
+
+mp.add_hook("on_load", 50, youtube_starttime)
diff --git a/TOOLS/osxbundle/mpv.app/Contents/Resources/mpv.conf b/TOOLS/osxbundle/mpv.app/Contents/Resources/mpv.conf
index ff78dfa..7527de0 100644
--- a/TOOLS/osxbundle/mpv.app/Contents/Resources/mpv.conf
+++ b/TOOLS/osxbundle/mpv.app/Contents/Resources/mpv.conf
@@ -1,3 +1,2 @@
screenshot-template=~/Desktop/shot%n
-quiet
-idle=once
+profile=pseudo-gui
diff --git a/TOOLS/umpv b/TOOLS/umpv
index 4757e4b..0080b44 100755
--- a/TOOLS/umpv
+++ b/TOOLS/umpv
@@ -21,7 +21,7 @@ script after that will start a new mpv instance.
Note that you can control the mpv instance by writing to the command fifo:
- echo "cycle fullscreen" > ~/.umpv-fifo
+ echo "cycle fullscreen" > ~/.umpv_fifo
Note: you can supply custom mpv path and options with the MPV environment
variable. The environment variable will be split on whitespace, and the
diff --git a/TOOLS/youtube-dl_mpv.sh b/TOOLS/youtube-dl_mpv.sh
deleted file mode 100755
index 3f4c02f..0000000
--- a/TOOLS/youtube-dl_mpv.sh
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/bin/sh
-# Example of script for using mpv with youtube-dl
-set -e
-
-cookies_dir="$(mktemp -d /tmp/youtube-dl_mpv.XXXX)"
-cookies_file="${cookies_dir}/cookies"
-user_agent="$(youtube-dl --dump-user-agent)" # or set whatever you want
-
-video_url=$(youtube-dl \
- --user-agent="${user_agent}" \
- --cookies="${cookies_file}" \
- --get-url \
- "$@")
-
-set -f
-${MPV:-mpv} \
- --cookies \
- --cookies-file="${cookies_file}" \
- --user-agent="${user_agent}" \
- -- ${video_url}
-
-rm -rf "${cookies_dir}"
diff --git a/TOOLS/zsh.pl b/TOOLS/zsh.pl
index 35592fe..6548d85 100755
--- a/TOOLS/zsh.pl
+++ b/TOOLS/zsh.pl
@@ -7,15 +7,15 @@ use warnings;
my $mpv = $ARGV[0] || 'mpv';
-my @opts = parse_main_opts("$mpv --list-options", '^ (\-\-[^\s\*]*)\*?\s*(.*)');
+my @opts = parse_main_opts('--list-options', '^ (\-\-[^\s\*]*)\*?\s*(.*)');
-my @ao = parse_opts("$mpv --ao=help", '^ ([^\s\:]*)\s*: (.*)');
-my @vo = parse_opts("$mpv --vo=help", '^ ([^\s\:]*)\s*: (.*)');
+my @ao = parse_opts('--ao=help', '^ ([^\s\:]*)\s*: (.*)');
+my @vo = parse_opts('--vo=help', '^ ([^\s\:]*)\s*: (.*)');
-my @af = parse_opts("$mpv --af=help", '^ ([^\s\:]*)\s*: (.*)');
-my @vf = parse_opts("$mpv --vf=help", '^ ([^\s\:]*)\s*: (.*)');
+my @af = parse_opts('--af=help', '^ ([^\s\:]*)\s*: (.*)');
+my @vf = parse_opts('--vf=help', '^ ([^\s\:]*)\s*: (.*)');
-my @protos = parse_opts("$mpv --list-protocols", '^ ([^\s]*)');
+my @protos = parse_opts('--list-protocols', '^ ([^\s]*)');
my ($opts_str, $ao_str, $vo_str, $af_str, $vf_str, $protos_str);
@@ -34,36 +34,35 @@ chomp $af_str;
$vf_str .= qq{ '$_' \\\n} foreach (@vf);
chomp $vf_str;
-$protos_str .= qq{$_ } foreach (@protos);
-chomp $protos_str;
+$protos_str = join(' ', @protos);
my $profile_comp = <<'EOS';
- local -a profiles
- local current
- for current in "${(@f)$($words[1] --profile=help)}"; do
- current=${current//\*/\\\*}
- current=${current//\:/\\\:}
- current=${current//\[/\\\[}
- current=${current//\]/\\\]}
- if [[ $current =~ $'\t'([^$'\t']*)$'\t'(.*) ]]; then
- if [[ -n $match[2] ]]; then
- current="$match[1][$match[2]]"
- else
- current="$match[1]"
- fi
- profiles=($profiles $current)
+ local -a profiles
+ local current
+ for current in "${(@f)$($words[1] --profile=help)}"; do
+ current=${current//\*/\\\*}
+ current=${current//\:/\\\:}
+ current=${current//\[/\\\[}
+ current=${current//\]/\\\]}
+ if [[ $current =~ $'\t'([^$'\t']*)$'\t'(.*) ]]; then
+ if [[ -n $match[2] ]]; then
+ current="$match[1][$match[2]]"
+ else
+ current="$match[1]"
fi
- done
- if [[ $state == show-profile ]]; then
- # For --show-profile, only one allowed
- if (( ${#profiles} > 0 )); then
- _values 'profile' $profiles && rc=0
- fi
- else
- # For --profile, multiple allowed
- profiles=($profiles 'help[list profiles]')
- _values -s , 'profile(s)' $profiles && rc=0
+ profiles=($profiles $current)
+ fi
+ done
+ if [[ $state == show-profile ]]; then
+ # For --show-profile, only one allowed
+ if (( ${#profiles} > 0 )); then
+ _values 'profile' $profiles && rc=0
fi
+ else
+ # For --profile, multiple allowed
+ profiles=($profiles 'help[list profiles]')
+ _values -s , 'profile(s)' $profiles && rc=0
+ fi
EOS
chomp $profile_comp;
@@ -83,39 +82,27 @@ $opts_str
case \$state in
ao)
- local -a values
- values=(
+ _values -s , 'audio outputs' \\
$ao_str
- )
-
- _describe -t values 'audio outputs' values && rc=0
+ && rc=0
;;
vo)
- local -a values
- values=(
+ _values -s , 'video outputs' \\
$vo_str
- )
-
- _describe -t values 'video outputs' values && rc=0
+ && rc=0
;;
af)
- local -a values
- values=(
+ _values -s , 'audio filters' \\
$af_str
- )
-
- _describe -t values 'audio filters' values && rc=0
+ && rc=0
;;
vf)
- local -a values
- values=(
+ _values -s , 'video filters' \\
$vf_str
- )
-
- _describe -t values 'video filters' values && rc=0
+ && rc=0
;;
profile|show-profile)
@@ -154,7 +141,7 @@ sub parse_main_opts {
my ($cmd, $regex) = @_;
my @list;
- my @lines = split /\n/, `$cmd`;
+ my @lines = split /\n/, `"$mpv" --no-config $cmd`;
foreach my $line (@lines) {
my ($name, $desc) = ($line =~ /^$regex/) or next;
@@ -219,7 +206,7 @@ sub parse_opts {
my ($cmd, $regex) = @_;
my @list;
- my @lines = split /\n/, `$cmd`;
+ my @lines = split /\n/, `"$mpv" --no-config $cmd`;
foreach my $line (@lines) {
if ($line !~ /^$regex/) {
@@ -231,7 +218,7 @@ sub parse_opts {
if (defined $2) {
my $desc = $2;
$desc =~ s/\:/\\:/g;
- $entry .= ':' . $desc;
+ $entry .= "[$desc]";
}
push @list, $entry
diff --git a/VERSION b/VERSION
index ee94dd8..899f24f 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.8.3
+0.9.0 \ No newline at end of file
diff --git a/audio/audio.c b/audio/audio.c
index e5df25a..ea0b620 100644
--- a/audio/audio.c
+++ b/audio/audio.c
@@ -35,7 +35,7 @@ static void update_redundant_info(struct mp_audio *mpa)
mp_chmap_is_valid(&mpa->channels));
mpa->nch = mpa->channels.num;
mpa->bps = af_fmt2bps(mpa->format);
- if (af_fmt_is_planar(mpa->format)) {
+ if (AF_FORMAT_IS_PLANAR(mpa->format)) {
mpa->spf = 1;
mpa->num_planes = mpa->nch;
mpa->sstride = mpa->bps;
@@ -101,7 +101,7 @@ char *mp_audio_config_to_str_buf(char *buf, size_t buf_sz, struct mp_audio *mpa)
void mp_audio_force_interleaved_format(struct mp_audio *mpa)
{
- if (af_fmt_is_planar(mpa->format))
+ if (AF_FORMAT_IS_PLANAR(mpa->format))
mp_audio_set_format(mpa, af_fmt_from_planar(mpa->format));
}
diff --git a/audio/chmap.c b/audio/chmap.c
index 497b910..1991d15 100644
--- a/audio/chmap.c
+++ b/audio/chmap.c
@@ -57,7 +57,9 @@ static const char *const speaker_names[MP_SPEAKER_ID_COUNT][2] = {
static const char *const std_layout_names[][2] = {
{"empty", ""}, // not in lavc
{"mono", "fc"},
+ {"1.0", "fc"}, // not in lavc
{"stereo", "fl-fr"},
+ {"2.0", "fl-fr"}, // not in lavc
{"2.1", "fl-fr-lfe"},
{"3.0", "fl-fr-fc"},
{"3.0(back)", "fl-fr-bc"},
diff --git a/audio/decode/ad.h b/audio/decode/ad.h
index 852df7c..0513954 100644
--- a/audio/decode/ad.h
+++ b/audio/decode/ad.h
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_AD_H
diff --git a/audio/decode/ad_lavc.c b/audio/decode/ad_lavc.c
index fd09eca..5a2392e 100644
--- a/audio/decode/ad_lavc.c
+++ b/audio/decode/ad_lavc.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
@@ -59,7 +58,7 @@ struct ad_lavc_params {
const struct m_sub_options ad_lavc_conf = {
.opts = (const m_option_t[]) {
- OPT_FLOATRANGE("ac3drc", ac3drc, 0, 0, 2),
+ OPT_FLOATRANGE("ac3drc", ac3drc, 0, 0, 6),
OPT_FLAG("downmix", downmix, 0),
OPT_INTRANGE("threads", threads, 0, 1, 16),
OPT_KEYVALUELIST("o", avopts, 0),
@@ -67,7 +66,7 @@ const struct m_sub_options ad_lavc_conf = {
},
.size = sizeof(struct ad_lavc_params),
.defaults = &(const struct ad_lavc_params){
- .ac3drc = 1.,
+ .ac3drc = 0,
.downmix = 1,
.threads = 1,
},
@@ -145,9 +144,6 @@ static int init(struct dec_audio *da, const char *decoder)
return 0;
}
- if (lavc_context->bit_rate != 0)
- da->bitrate = lavc_context->bit_rate;
-
return 1;
}
diff --git a/audio/decode/ad_mpg123.c b/audio/decode/ad_mpg123.c
deleted file mode 100644
index 2fbbec6..0000000
--- a/audio/decode/ad_mpg123.c
+++ /dev/null
@@ -1,304 +0,0 @@
-/*
- * MPEG 1.0/2.0/2.5 audio layer I, II, III decoding with libmpg123
- *
- * Copyright (C) 2010-2013 Thomas Orgis <thomas@orgis.org>
- *
- * 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 <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-
-#include "config.h"
-
-#include "ad.h"
-#include "common/msg.h"
-
-#include <mpg123.h>
-
-#if (defined MPG123_API_VERSION) && (MPG123_API_VERSION < 33)
-#error "This should not happen"
-#endif
-
-struct ad_mpg123_context {
- mpg123_handle *handle;
- int sample_size;
- /* Running mean for bit rate, stream length estimation. */
- float mean_rate;
- unsigned int mean_count;
- /* Time delay for updates. */
- short delay;
- /* If the stream is actually VBR. */
- char vbr;
- struct mp_audio frame;
-};
-
-static void uninit(struct dec_audio *da)
-{
- struct ad_mpg123_context *con = da->priv;
-
- mpg123_close(con->handle);
- mpg123_delete(con->handle);
- mpg123_exit();
-}
-
-/* This initializes libmpg123 and prepares the handle, including funky
- * parameters. */
-static int init(struct dec_audio *da, const char *decoder)
-{
- int err;
- struct ad_mpg123_context *con;
- /* Assumption: You always call preinit + init + uninit, on every file.
- * But you stop at preinit in case it fails.
- * If that is not true, one must ensure not to call mpg123_init / exit
- * twice in a row. */
- if (mpg123_init() != MPG123_OK)
- return 0;
-
- da->priv = talloc_zero(NULL, struct ad_mpg123_context);
- con = da->priv;
- /* Auto-choice of optimized decoder (first argument NULL). */
- con->handle = mpg123_new(NULL, &err);
- if (!con->handle)
- goto bad_end;
-
- /* Basic settings.
- * Don't spill messages, enable better resync with non-seekable streams.
- * Give both flags individually without error checking to keep going with
- * old libmpg123. Generally, it is not fatal if the flags are not
- * honored */
- mpg123_param(con->handle, MPG123_ADD_FLAGS, MPG123_QUIET, 0.0);
- /* Do not bail out on malformed streams at all.
- * MPlayer does not handle a decoder throwing the towel on crappy input. */
- mpg123_param(con->handle, MPG123_RESYNC_LIMIT, -1, 0.0);
-
- /* Open decisions: Configure libmpg123 to force encoding (or stay open about
- * library builds that support only float or int32 output), (de)configure
- * gapless decoding (won't work with seeking in MPlayer, though).
- * Don't forget to eventually enable ReplayGain/RVA support, too.
- * Let's try to run with the default for now. */
-
- /* That would produce floating point output.
- * You can get 32 and 24 bit ints, even 8 bit via format matrix.
- * If wanting a specific encoding here, configure format matrix and
- * make sure it is in set_format(). */
- /* mpg123_param(con->handle, MPG123_ADD_FLAGS, MPG123_FORCE_FLOAT, 0.); */
-
- /* Example for RVA choice (available since libmpg123 1.0.0):
- mpg123_param(con->handle, MPG123_RVA, MPG123_RVA_MIX, 0.0) */
-
- /* Prevent funky automatic resampling.
- * This way, we can be sure that one frame will never produce
- * more than 1152 stereo samples.
- * Background:
- * Going to decode directly to the output buffer. It is important to have
- * MPG123_AUTO_RESAMPLE disabled for the buffer size being an all-time
- * limit.
- * We need at least 1152 samples. dec_audio.c normally guarantees this. */
- mpg123_param(con->handle, MPG123_REMOVE_FLAGS, MPG123_AUTO_RESAMPLE, 0.);
-
- err = mpg123_open_feed(con->handle);
- if (err != MPG123_OK)
- goto bad_end;
-
- return 1;
-
- bad_end:
- if (!con->handle) {
- MP_ERR(da, "mpg123 preinit error: %s\n", mpg123_plain_strerror(err));
- } else {
- MP_ERR(da, "mpg123 preinit error: %s\n", mpg123_strerror(con->handle));
- }
-
- uninit(da);
- return 0;
-}
-
-static int mpg123_format_to_af(int mpg123_encoding)
-{
- /* Without external force, mpg123 will always choose signed encoding,
- * and non-16-bit only on builds that don't support it.
- * Be reminded that it doesn't matter to the MPEG file what encoding
- * is produced from it. */
- switch (mpg123_encoding) {
- case MPG123_ENC_SIGNED_8: return AF_FORMAT_S8;
- case MPG123_ENC_SIGNED_16: return AF_FORMAT_S16;
- case MPG123_ENC_SIGNED_32: return AF_FORMAT_S32;
- case MPG123_ENC_FLOAT_32: return AF_FORMAT_FLOAT;
- }
- return 0;
-}
-
-/* Compute bitrate from frame size. */
-static int compute_bitrate(struct mpg123_frameinfo *i)
-{
- static const int samples_per_frame[4][4] = {
- {-1, 384, 1152, 1152}, /* MPEG 1 */
- {-1, 384, 1152, 576}, /* MPEG 2 */
- {-1, 384, 1152, 576}, /* MPEG 2.5 */
- {-1, -1, -1, -1}, /* Unknown */
- };
- return (int) ((i->framesize + 4) * 8 * i->rate /
- samples_per_frame[i->version][i->layer] + 0.5);
-}
-
-/* Update mean bitrate. This could be dropped if accurate time display
- * on audio file playback is not desired. */
-static void update_info(struct dec_audio *da)
-{
- struct ad_mpg123_context *con = da->priv;
- struct mpg123_frameinfo finfo;
- if (mpg123_info(con->handle, &finfo) != MPG123_OK)
- return;
-
- /* finfo.bitrate is expressed in kilobits */
- const int bitrate = finfo.bitrate * 1000;
-
- if (finfo.vbr != MPG123_CBR) {
- if (--con->delay < 1) {
- if (++con->mean_count > ((unsigned int) -1) / 2)
- con->mean_count = ((unsigned int) -1) / 4;
-
- /* Might not be numerically optimal, but works fine enough. */
- con->mean_rate = ((con->mean_count - 1) * con->mean_rate +
- bitrate) / con->mean_count;
- da->bitrate = (int) (con->mean_rate + 0.5);
-
- con->delay = 10;
- }
- } else {
- da->bitrate = bitrate ? bitrate : compute_bitrate(&finfo);
- con->delay = 1;
- con->mean_rate = 0.;
- con->mean_count = 0;
- }
-}
-
-/* libmpg123 has a new format ready; query and store, return return value
- of mpg123_getformat() */
-static int set_format(struct dec_audio *da)
-{
- struct ad_mpg123_context *con = da->priv;
- int ret;
- long rate;
- int channels;
- int encoding;
- ret = mpg123_getformat(con->handle, &rate, &channels, &encoding);
- if (ret == MPG123_OK) {
- mp_audio_set_num_channels(&con->frame, channels);
- con->frame.rate = rate;
- int af = mpg123_format_to_af(encoding);
- if (!af) {
- /* This means we got a funny custom build of libmpg123 that only supports an unknown format. */
- MP_ERR(da, "Bad encoding from mpg123: %i.\n", encoding);
- return MPG123_ERR;
- }
- mp_audio_set_format(&con->frame, af);
- con->sample_size = channels * af_fmt2bps(af);
- }
- return ret;
-}
-
-static int decode_packet(struct dec_audio *da, struct mp_audio **out)
-{
- struct ad_mpg123_context *con = da->priv;
- int ret;
-
- struct demux_packet *pkt;
- if (demux_read_packet_async(da->header, &pkt) == 0)
- return AD_WAIT;
- if (!pkt)
- return AD_EOF;
-
- /* Next bytes from that presentation time. */
- if (pkt->pts != MP_NOPTS_VALUE) {
- da->pts = pkt->pts;
- da->pts_offset = 0;
- }
-
- /* Have to use mpg123_feed() to avoid decoding here. */
- ret = mpg123_feed(con->handle, pkt->buffer, pkt->len);
- talloc_free(pkt);
-
- if (ret != MPG123_OK)
- goto mpg123_fail;
-
- unsigned char *audio = NULL;
- size_t bytes = 0;
- ret = mpg123_decode_frame(con->handle, NULL, &audio, &bytes);
-
- if (ret == MPG123_NEED_MORE)
- return 0;
-
- if (ret != MPG123_OK && ret != MPG123_DONE && ret != MPG123_NEW_FORMAT)
- goto mpg123_fail;
-
- ret = set_format(da);
- if (ret != MPG123_OK)
- goto mpg123_fail;
-
- if (con->sample_size < 1) {
- MP_ERR(da, "no sample size\n");
- return AD_ERR;
- }
-
- int got_samples = bytes / con->sample_size;
- *out = mp_audio_pool_get(da->pool, &con->frame, got_samples);
- if (!*out)
- return AD_ERR;
-
- memcpy((*out)->planes[0], audio, bytes);
-
- update_info(da);
- return 0;
-
-mpg123_fail:
- MP_ERR(da, "mpg123 decoding error: %s\n", mpg123_strerror(con->handle));
- return AD_ERR;
-}
-
-static int control(struct dec_audio *da, int cmd, void *arg)
-{
- struct ad_mpg123_context *con = da->priv;
-
- switch (cmd) {
- case ADCTRL_RESET:
- mpg123_close(con->handle);
-
- if (mpg123_open_feed(con->handle) != MPG123_OK) {
- MP_ERR(da, "mpg123 failed to reopen stream: %s\n",
- mpg123_strerror(con->handle));
- return CONTROL_FALSE;
- }
- return CONTROL_TRUE;
- }
- return CONTROL_UNKNOWN;
-}
-
-static void add_decoders(struct mp_decoder_list *list)
-{
- mp_add_decoder(list, "mpg123", "mp3", "mp3",
- "High-performance decoder using libmpg123");
-}
-
-const struct ad_functions ad_mpg123 = {
- .name = "mpg123",
- .add_decoders = add_decoders,
- .init = init,
- .uninit = uninit,
- .control = control,
- .decode_packet = decode_packet,
-};
diff --git a/audio/decode/ad_spdif.c b/audio/decode/ad_spdif.c
index f789176..5265f4f 100644
--- a/audio/decode/ad_spdif.c
+++ b/audio/decode/ad_spdif.c
@@ -1,21 +1,20 @@
/*
- * This file is part of MPlayer.
- *
* Copyright (C) 2012 Naoya OYAMA
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
@@ -36,7 +35,6 @@
struct spdifContext {
struct mp_log *log;
AVFormatContext *lavf_ctx;
- int iec61937_packet_size;
int out_buffer_len;
uint8_t out_buffer[OUTBUF_SIZE];
bool need_close;
@@ -117,13 +115,11 @@ static int init(struct dec_audio *da, const char *decoder)
int samplerate = 0;
switch (stream->codec->codec_id) {
case AV_CODEC_ID_AAC:
- spdif_ctx->iec61937_packet_size = 16384;
sample_format = AF_FORMAT_S_AAC;
samplerate = 48000;
num_channels = 2;
break;
case AV_CODEC_ID_AC3:
- spdif_ctx->iec61937_packet_size = 6144;
sample_format = AF_FORMAT_S_AC3;
samplerate = 48000;
num_channels = 2;
@@ -131,31 +127,26 @@ static int init(struct dec_audio *da, const char *decoder)
case AV_CODEC_ID_DTS:
if (da->opts->dtshd) {
av_dict_set(&format_opts, "dtshd_rate", "768000", 0); // 4*192000
- spdif_ctx->iec61937_packet_size = 32768;
sample_format = AF_FORMAT_S_DTSHD;
samplerate = 192000;
num_channels = 2*4;
} else {
- spdif_ctx->iec61937_packet_size = 32768;
sample_format = AF_FORMAT_S_DTS;
samplerate = 48000;
num_channels = 2;
}
break;
case AV_CODEC_ID_EAC3:
- spdif_ctx->iec61937_packet_size = 24576;
sample_format = AF_FORMAT_S_EAC3;
samplerate = 192000;
num_channels = 2;
break;
case AV_CODEC_ID_MP3:
- spdif_ctx->iec61937_packet_size = 4608;
sample_format = AF_FORMAT_S_MP3;
samplerate = 48000;
num_channels = 2;
break;
case AV_CODEC_ID_TRUEHD:
- spdif_ctx->iec61937_packet_size = 61440;
sample_format = AF_FORMAT_S_TRUEHD;
samplerate = 192000;
num_channels = 8;
diff --git a/audio/decode/dec_audio.c b/audio/decode/dec_audio.c
index ab4cd2e..dfbe32c 100644
--- a/audio/decode/dec_audio.c
+++ b/audio/decode/dec_audio.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
@@ -25,7 +24,6 @@
#include "demux/codec_tags.h"
-#include "config.h"
#include "common/codecs.h"
#include "common/msg.h"
#include "misc/bstr.h"
@@ -43,15 +41,11 @@
#include "audio/filter/af.h"
-extern const struct ad_functions ad_mpg123;
extern const struct ad_functions ad_lavc;
extern const struct ad_functions ad_spdif;
static const struct ad_functions * const ad_drivers[] = {
&ad_lavc,
-#if HAVE_MPG123
- &ad_mpg123,
-#endif
&ad_spdif,
NULL
};
@@ -164,6 +158,9 @@ static int decode_new_frame(struct dec_audio *da)
if (ret < 0)
return ret;
+ if (da->pts == MP_NOPTS_VALUE && da->header->missing_timestamps)
+ da->pts = 0;
+
if (da->waiting) {
da->pts_offset += da->waiting->samples;
da->decode_format = *da->waiting;
diff --git a/audio/decode/dec_audio.h b/audio/decode/dec_audio.h
index ab7ae33..11e4a24 100644
--- a/audio/decode/dec_audio.h
+++ b/audio/decode/dec_audio.h
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_DEC_AUDIO_H
diff --git a/audio/filter/af.c b/audio/filter/af.c
index 32e4900..7a6aef0 100644
--- a/audio/filter/af.c
+++ b/audio/filter/af.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
@@ -54,11 +53,11 @@ extern const struct af_info af_info_center;
extern const struct af_info af_info_sinesuppress;
extern const struct af_info af_info_karaoke;
extern const struct af_info af_info_scaletempo;
-extern const struct af_info af_info_forcespeed;
extern const struct af_info af_info_bs2b;
extern const struct af_info af_info_lavfi;
extern const struct af_info af_info_convert24;
extern const struct af_info af_info_convertsignendian;
+extern const struct af_info af_info_rubberband;
static const struct af_info *const filter_list[] = {
&af_info_dummy,
@@ -80,10 +79,12 @@ static const struct af_info *const filter_list[] = {
#if HAVE_LADSPA
&af_info_ladspa,
#endif
+#if HAVE_RUBBERBAND
+ &af_info_rubberband,
+#endif
&af_info_center,
&af_info_sinesuppress,
&af_info_karaoke,
- &af_info_forcespeed,
&af_info_scaletempo,
#if HAVE_LIBBS2B
&af_info_bs2b,
@@ -503,6 +504,15 @@ static int af_fix_rate(struct af_stream *s, struct af_instance **p_af,
return AF_OK;
}
+static void reset_formats(struct af_stream *s)
+{
+ for (struct af_instance *af = s->first; af; af = af->next) {
+ af->control(af, AF_CONTROL_SET_RESAMPLE_RATE, &(int){0});
+ af->control(af, AF_CONTROL_SET_CHANNELS, &(struct mp_chmap){0});
+ af->control(af, AF_CONTROL_SET_FORMAT, &(int){0});
+ }
+}
+
// Return AF_OK on success or AF_ERROR on failure.
// Warning:
// A failed af_reinit() leaves the audio chain behind in a useless, broken
@@ -513,6 +523,7 @@ static int af_reinit(struct af_stream *s)
{
remove_auto_inserted_filters(s);
af_chain_forget_frames(s);
+ reset_formats(s);
s->first->fmt_in = s->first->fmt_out = s->input;
// Start with the second filter, as the first filter is the special input
// filter which needs no initialization.
@@ -559,6 +570,23 @@ static int af_reinit(struct af_stream *s)
retry++;
continue;
}
+ // If the format conversion is (probably) caused by spdif, then
+ // (as a feature) drop the filter, instead of failing hard.
+ int fmt_in1 = af->prev->data->format;
+ int fmt_in2 = in.format;
+ if (af_fmt_is_valid(fmt_in1) && af_fmt_is_valid(fmt_in2)) {
+ bool spd1 = AF_FORMAT_IS_IEC61937(fmt_in1);
+ bool spd2 = AF_FORMAT_IS_IEC61937(fmt_in2);
+ if (spd1 != spd2) {
+ MP_WARN(af, "Filter %s apparently cannot be used due to "
+ "spdif passthrough - removing it.\n",
+ af->info->name);
+ struct af_instance *aft = af->prev;
+ af_remove(s, af);
+ af = aft->next;
+ break;
+ }
+ }
goto negotiate_error;
}
case AF_DETACH: { // Filter is redundant and wants to be unloaded
@@ -687,8 +715,14 @@ int af_init(struct af_stream *s)
to the stream s. The filter will be inserted somewhere nice in the
list of filters. The return value is a pointer to the new filter,
If the filter couldn't be added the return value is NULL. */
-struct af_instance *af_add(struct af_stream *s, char *name, char **args)
+struct af_instance *af_add(struct af_stream *s, char *name, char *label,
+ char **args)
{
+ assert(label);
+
+ if (af_find_by_label(s, label))
+ return NULL;
+
struct af_instance *new;
// Insert the filter somewhere nice
if (af_is_conversion_filter(s->first->next))
@@ -697,17 +731,14 @@ struct af_instance *af_add(struct af_stream *s, char *name, char **args)
new = af_prepend(s, s->first->next, name, args);
if (!new)
return NULL;
+ new->label = talloc_strdup(new, label);
// Reinitalize the filter list
if (af_reinit(s) != AF_OK) {
- af_remove(s, new);
- if (af_reinit(s) != AF_OK) {
- af_uninit(s);
- af_init(s);
- }
+ af_remove_by_label(s, label);
return NULL;
}
- return new;
+ return af_find_by_label(s, label);
}
struct af_instance *af_find_by_label(struct af_stream *s, char *label)
diff --git a/audio/filter/af.h b/audio/filter/af.h
index cecc389..fb5f74b 100644
--- a/audio/filter/af.h
+++ b/audio/filter/af.h
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_AF_H
@@ -141,7 +140,8 @@ struct af_stream *af_new(struct mpv_global *global);
void af_destroy(struct af_stream *s);
int af_init(struct af_stream *s);
void af_uninit(struct af_stream *s);
-struct af_instance *af_add(struct af_stream *s, char *name, char **args);
+struct af_instance *af_add(struct af_stream *s, char *name, char *label,
+ char **args);
int af_remove_by_label(struct af_stream *s, char *label);
struct af_instance *af_find_by_label(struct af_stream *s, char *label);
struct af_instance *af_control_any_rev(struct af_stream *s, int cmd, void *arg);
@@ -159,9 +159,7 @@ double af_calc_delay(struct af_stream *s);
int af_test_output(struct af_instance *af, struct mp_audio *out);
int af_from_dB(int n, float *in, float *out, float k, float mi, float ma);
-int af_to_dB(int n, float *in, float *out, float k);
int af_from_ms(int n, float *in, int *out, int rate, float mi, float ma);
-int af_to_ms(int n, int *in, float *out, int rate);
float af_softclip(float a);
#endif /* MPLAYER_AF_H */
diff --git a/audio/filter/af_bs2b.c b/audio/filter/af_bs2b.c
index 874dbc6..10d6f4e 100644
--- a/audio/filter/af_bs2b.c
+++ b/audio/filter/af_bs2b.c
@@ -4,21 +4,20 @@
*
* Copyright (c) 2009 Andrew Savchenko
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <bs2b.h>
@@ -164,7 +163,8 @@ const struct af_info af_info_bs2b = {
OPT_INTRANGE("fcut", fcut, 0, BS2B_MINFCUT, BS2B_MAXFCUT),
OPT_INTRANGE("feed", feed, 0, BS2B_MINFEED, BS2B_MAXFEED),
OPT_CHOICE("profile", profile, 0,
- ({"default", BS2B_DEFAULT_CLEVEL},
+ ({"unset", 0},
+ {"default", BS2B_DEFAULT_CLEVEL},
{"cmoy", BS2B_CMOY_CLEVEL},
{"jmeier", BS2B_JMEIER_CLEVEL})),
{0}
diff --git a/audio/filter/af_center.c b/audio/filter/af_center.c
index ff2a37d..69e54e8 100644
--- a/audio/filter/af_center.c
+++ b/audio/filter/af_center.c
@@ -8,21 +8,20 @@
*
* copyright (c) 2005 Alex Beregszaszi
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
diff --git a/audio/filter/af_channels.c b/audio/filter/af_channels.c
index 22be38a..57fe487 100644
--- a/audio/filter/af_channels.c
+++ b/audio/filter/af_channels.c
@@ -5,21 +5,20 @@
*
* Original author: Anders
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
diff --git a/audio/filter/af_delay.c b/audio/filter/af_delay.c
index 8de26ec..8d1cca8 100644
--- a/audio/filter/af_delay.c
+++ b/audio/filter/af_delay.c
@@ -5,21 +5,20 @@
*
* Original author: Anders
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
diff --git a/audio/filter/af_drc.c b/audio/filter/af_drc.c
index 2560658..4344766 100644
--- a/audio/filter/af_drc.c
+++ b/audio/filter/af_drc.c
@@ -1,21 +1,20 @@
/*
* Copyright (C) 2004 Alex Beregszaszi & Pierre Lombard
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
diff --git a/audio/filter/af_dummy.c b/audio/filter/af_dummy.c
index 3d3ec27..341ec7e 100644
--- a/audio/filter/af_dummy.c
+++ b/audio/filter/af_dummy.c
@@ -4,21 +4,20 @@
*
* Original author: Anders
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
diff --git a/audio/filter/af_equalizer.c b/audio/filter/af_equalizer.c
index f5d47c8..781239c 100644
--- a/audio/filter/af_equalizer.c
+++ b/audio/filter/af_equalizer.c
@@ -6,21 +6,20 @@
*
* Copyright (C) 2001 Anders Johansson ajh@atri.curtin.edu.au
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
diff --git a/audio/filter/af_export.c b/audio/filter/af_export.c
index fb5503f..f261353 100644
--- a/audio/filter/af_export.c
+++ b/audio/filter/af_export.c
@@ -9,21 +9,20 @@
*
* Authors: Anders; Gustavo Sverzut Barbieri <gustavo.barbieri@ic.unicamp.br>
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
diff --git a/audio/filter/af_extrastereo.c b/audio/filter/af_extrastereo.c
index 4b39793..49222eb 100644
--- a/audio/filter/af_extrastereo.c
+++ b/audio/filter/af_extrastereo.c
@@ -1,21 +1,20 @@
/*
* Copyright (C) 2004 Alex Beregszaszi & Pierre Lombard
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
diff --git a/audio/filter/af_forcespeed.c b/audio/filter/af_forcespeed.c
deleted file mode 100644
index 3f2ca83..0000000
--- a/audio/filter/af_forcespeed.c
+++ /dev/null
@@ -1,71 +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 "af.h"
-
-struct priv {
- double speed;
-};
-
-static int control(struct af_instance *af, int cmd, void *arg)
-{
- struct priv *priv = af->priv;
-
- switch (cmd) {
- case AF_CONTROL_REINIT: {
- struct mp_audio *in = arg;
- struct mp_audio orig_in = *in;
- struct mp_audio *out = af->data;
-
- mp_audio_copy_config(out, in);
- out->rate = in->rate * priv->speed;
-
- return mp_audio_config_equals(in, &orig_in) ? AF_OK : AF_FALSE;
- }
- case AF_CONTROL_SET_PLAYBACK_SPEED_RESAMPLE: {
- priv->speed = *(double *)arg;
- return AF_OK;
- }
- }
- return AF_UNKNOWN;
-}
-
-static int filter(struct af_instance *af, struct mp_audio *data)
-{
- if (data)
- mp_audio_copy_config(data, af->data);
- af_add_output_frame(af, data);
- return 0;
-}
-
-static int af_open(struct af_instance *af)
-{
- struct priv *priv = af->priv;
- af->control = control;
- af->filter_frame = filter;
- priv->speed = 1.0;
- return AF_OK;
-}
-
-#define OPT_BASE_STRUCT struct priv
-
-const struct af_info af_info_forcespeed = {
- .info = "Force audio speed",
- .name = "forcespeed",
- .open = af_open,
- .priv_size = sizeof(struct priv),
-};
diff --git a/audio/filter/af_hrtf.c b/audio/filter/af_hrtf.c
index 1496f8b..94a1599 100644
--- a/audio/filter/af_hrtf.c
+++ b/audio/filter/af_hrtf.c
@@ -5,21 +5,20 @@
*
* Author: ylai
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
//#include <stdio.h>
diff --git a/audio/filter/af_hrtf.h b/audio/filter/af_hrtf.h
index a654dc4..9ba9858 100644
--- a/audio/filter/af_hrtf.h
+++ b/audio/filter/af_hrtf.h
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_AF_HRTF_H
diff --git a/audio/filter/af_karaoke.c b/audio/filter/af_karaoke.c
index f69b936..0c8dc62 100644
--- a/audio/filter/af_karaoke.c
+++ b/audio/filter/af_karaoke.c
@@ -4,21 +4,20 @@
* copyright (c) 2006 Reynaldo H. Verdejo Pinochet
* Based on code by Alex Beregszaszi for his 'center' filter.
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
diff --git a/audio/filter/af_ladspa.c b/audio/filter/af_ladspa.c
index 6a8cd80..bd54dbb 100644
--- a/audio/filter/af_ladspa.c
+++ b/audio/filter/af_ladspa.c
@@ -4,21 +4,20 @@
* Written by Ivo van Poorten <ivop@euronet.nl>
* Copyright (C) 2004, 2005
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
/* ------------------------------------------------------------------------- */
diff --git a/audio/filter/af_lavcac3enc.c b/audio/filter/af_lavcac3enc.c
index 3ced241..a1651a0 100644
--- a/audio/filter/af_lavcac3enc.c
+++ b/audio/filter/af_lavcac3enc.c
@@ -3,21 +3,20 @@
*
* Copyright (C) 2007 Ulion <ulion A gmail P com>
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
diff --git a/audio/filter/af_lavfi.c b/audio/filter/af_lavfi.c
index 902fe76..249a560 100644
--- a/audio/filter/af_lavfi.c
+++ b/audio/filter/af_lavfi.c
@@ -61,6 +61,8 @@ struct priv {
AVRational timebase_out;
+ bool eof;
+
// options
char *cfg_graph;
char **cfg_avopts;
@@ -71,6 +73,8 @@ static void destroy_graph(struct af_instance *af)
struct priv *p = af->priv;
avfilter_graph_free(&p->graph);
p->in = p->out = NULL;
+ p->samples_in = 0;
+ p->eof = false;
}
static bool recreate_graph(struct af_instance *af, struct mp_audio *config)
@@ -167,6 +171,12 @@ error:
return false;
}
+static void reset(struct af_instance *af)
+{
+ if (!recreate_graph(af, &af->fmt_in))
+ MP_FATAL(af, "Can't recreate libavfilter filter after a seek reset.\n");
+}
+
static int control(struct af_instance *af, int cmd, void *arg)
{
struct priv *p = af->priv;
@@ -203,6 +213,9 @@ 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_RESET:
+ reset(af);
+ return AF_OK;
}
return AF_UNKNOWN;
}
@@ -210,9 +223,16 @@ static int control(struct af_instance *af, int cmd, void *arg)
static int filter_frame(struct af_instance *af, struct mp_audio *data)
{
struct priv *p = af->priv;
+ AVFrame *frame = NULL;
+
+ if (p->eof && data)
+ reset(af);
+
+ if (!p->graph)
+ goto error;
+
AVFilterLink *l_in = p->in->outputs[0];
- AVFrame *frame = NULL;
if (data) {
frame = av_frame_alloc();
if (!frame)
@@ -255,6 +275,9 @@ static int filter_out(struct af_instance *af)
{
struct priv *p = af->priv;
+ if (!p->graph)
+ goto error;
+
AVFrame *frame = av_frame_alloc();
if (!frame)
goto error;
@@ -262,7 +285,10 @@ static int filter_out(struct af_instance *af)
int err = av_buffersink_get_frame(p->out, frame);
if (err == AVERROR(EAGAIN) || err == AVERROR_EOF) {
// Not an error situation - no more output buffers in queue.
+ // AVERROR_EOF means we shouldn't even give the filter more
+ // input, but we don't handle that completely correctly.
av_frame_free(&frame);
+ p->eof |= err == AVERROR_EOF;
return 0;
}
diff --git a/audio/filter/af_lavrresample.c b/audio/filter/af_lavrresample.c
index dc2f062..412846a 100644
--- a/audio/filter/af_lavrresample.c
+++ b/audio/filter/af_lavrresample.c
@@ -2,28 +2,29 @@
* Copyright (c) 2004 Michael Niedermayer <michaelni@gmx.at>
* Copyright (c) 2013 Stefano Pigozzi <stefano.pigozzi@gmail.com>
*
- * This file is part of mpv.
* Based on Michael Niedermayer's lavcresample.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
+#include <math.h>
#include <assert.h>
#include <libavutil/opt.h>
@@ -32,7 +33,7 @@
#include <libavutil/samplefmt.h>
#include <libavutil/mathematics.h>
-#include "talloc.h"
+#include "common/common.h"
#include "config.h"
#if HAVE_LIBAVRESAMPLE
@@ -64,7 +65,8 @@ struct af_resample_opts {
int linear;
double cutoff;
- int in_rate;
+ int in_rate_af; // filter input sample rate
+ int in_rate; // actual rate (used by lavr), adjusted for playback speed
int in_format;
struct mp_chmap in_channels;
int out_rate;
@@ -75,6 +77,9 @@ struct af_resample_opts {
struct af_resample {
int allow_detach;
char **avopts;
+ double playback_speed;
+ struct mp_audio *pending;
+ bool avrctx_ok;
struct AVAudioResampleContext *avrctx;
struct AVAudioResampleContext *avrctx_out; // for output channel reordering
struct af_resample_opts ctx; // opts in the context
@@ -94,6 +99,10 @@ static void drop_all_output(struct af_resample *s)
{
while (avresample_read(s->avrctx, NULL, 1000) > 0) {}
}
+static int get_drain_samples(struct af_resample *s)
+{
+ return avresample_get_out_samples(s->avrctx, 0);
+}
#else
static int get_delay(struct af_resample *s)
{
@@ -103,18 +112,39 @@ static void drop_all_output(struct af_resample *s)
{
while (swr_drop_output(s->avrctx, 1000) > 0) {}
}
+static int get_drain_samples(struct af_resample *s)
+{
+ return 4096; // libswscale does not have this
+}
#endif
+static int resample_frame(struct AVAudioResampleContext *r,
+ struct mp_audio *out, struct mp_audio *in)
+{
+ return avresample_convert(r,
+ out ? (uint8_t **)out->planes : NULL,
+ out ? mp_audio_get_allocated_size(out) : 0,
+ out ? out->samples : 0,
+ in ? (uint8_t **)in->planes : NULL,
+ in ? mp_audio_get_allocated_size(in) : 0,
+ in ? in->samples : 0);
+}
+
static double af_resample_default_cutoff(int filter_size)
{
return FFMAX(1.0 - 6.5 / (filter_size + 8), 0.80);
}
+static int rate_from_speed(int rate, double speed)
+{
+ return lrint(rate * speed);
+}
+
static bool needs_lavrctx_reconfigure(struct af_resample *s,
struct mp_audio *in,
struct mp_audio *out)
{
- return s->ctx.in_rate != in->rate ||
+ return s->ctx.in_rate_af != in->rate ||
s->ctx.in_format != in->format ||
!mp_chmap_equals(&s->ctx.in_channels, &in->channels) ||
s->ctx.out_rate != out->rate ||
@@ -138,6 +168,8 @@ static int configure_lavrr(struct af_instance *af, struct mp_audio *in,
{
struct af_resample *s = af->priv;
+ s->avrctx_ok = false;
+
enum AVSampleFormat in_samplefmt = af_to_avformat(in->format);
enum AVSampleFormat out_samplefmt = af_to_avformat(out->format);
@@ -147,8 +179,12 @@ static int configure_lavrr(struct af_instance *af, struct mp_audio *in,
avresample_close(s->avrctx);
avresample_close(s->avrctx_out);
+ talloc_free(s->pending);
+ s->pending = NULL;
+
s->ctx.out_rate = out->rate;
- s->ctx.in_rate = in->rate;
+ s->ctx.in_rate_af = in->rate;
+ s->ctx.in_rate = rate_from_speed(in->rate, s->playback_speed);
s->ctx.out_format = out->format;
s->ctx.in_format = in->format;
s->ctx.out_channels= out->channels;
@@ -164,6 +200,10 @@ static int configure_lavrr(struct af_instance *af, struct mp_audio *in,
av_opt_set_double(s->avrctx, "cutoff", s->ctx.cutoff, 0);
+#if HAVE_LIBSWRESAMPLE
+ av_opt_set_double(s->avrctx, "rematrix_maxval", 1.0, 0);
+#endif
+
if (mp_set_avopts(af->log, s->avrctx, s->avopts) < 0)
return AF_ERROR;
@@ -217,24 +257,25 @@ static int configure_lavrr(struct af_instance *af, struct mp_audio *in,
MP_ERR(af, "Cannot open Libavresample Context. \n");
return AF_ERROR;
}
+ s->avrctx_ok = true;
return AF_OK;
}
static int control(struct af_instance *af, int cmd, void *arg)
{
- struct af_resample *s = (struct af_resample *) af->priv;
- struct mp_audio *in = (struct mp_audio *) arg;
- struct mp_audio *out = (struct mp_audio *) af->data;
+ struct af_resample *s = af->priv;
switch (cmd) {
case AF_CONTROL_REINIT: {
+ struct mp_audio *in = arg;
+ struct mp_audio *out = af->data;
struct mp_audio orig_in = *in;
if (((out->rate == in->rate) || (out->rate == 0)) &&
(out->format == in->format) &&
(mp_chmap_equals(&out->channels, &in->channels) || out->nch == 0) &&
- s->allow_detach)
+ s->allow_detach && s->playback_speed == 1.0)
return AF_DETACH;
if (out->rate == 0)
@@ -257,10 +298,11 @@ static int control(struct af_instance *af, int cmd, void *arg)
return r;
}
case AF_CONTROL_SET_FORMAT: {
- if (af_to_avformat(*(int*)arg) == AV_SAMPLE_FMT_NONE)
+ int format = *(int *)arg;
+ if (format && af_to_avformat(format) == AV_SAMPLE_FMT_NONE)
return AF_FALSE;
- mp_audio_set_format(af->data, *(int*)arg);
+ mp_audio_set_format(af->data, format);
return AF_OK;
}
case AF_CONTROL_SET_CHANNELS: {
@@ -268,8 +310,28 @@ static int control(struct af_instance *af, int cmd, void *arg)
return AF_OK;
}
case AF_CONTROL_SET_RESAMPLE_RATE:
- out->rate = *(int *)arg;
+ af->data->rate = *(int *)arg;
+ return AF_OK;
+ case AF_CONTROL_SET_PLAYBACK_SPEED_RESAMPLE: {
+ s->playback_speed = *(double *)arg;
+ int new_rate = rate_from_speed(s->ctx.in_rate_af, s->playback_speed);
+ if (new_rate != s->ctx.in_rate && s->avrctx_ok && af->fmt_out.format) {
+ // Before reconfiguring, drain the audio that is still buffered
+ // in the resampler.
+ struct mp_audio *pending = talloc_zero(NULL, struct mp_audio);
+ mp_audio_copy_config(pending, &af->fmt_out);
+ pending->samples = get_drain_samples(s);
+ if (pending->samples > 0) {
+ mp_audio_realloc_min(pending, pending->samples);
+ int r = resample_frame(s->avrctx, pending, NULL);
+ pending->samples = MPMAX(r, 0);
+ }
+ // Reinitialize resampler.
+ configure_lavrr(af, &af->fmt_in, &af->fmt_out);
+ s->pending = pending;
+ }
return AF_OK;
+ }
case AF_CONTROL_RESET:
drop_all_output(s);
return AF_OK;
@@ -289,6 +351,7 @@ static void uninit(struct af_instance *af)
if (s->avrctx_out)
avresample_close(s->avrctx_out);
avresample_free(&s->avrctx_out);
+ talloc_free(s->pending);
}
static bool needs_reorder(int *reorder, int num_ch)
@@ -309,22 +372,19 @@ static void reorder_planes(struct mp_audio *mpa, int *reorder)
}
}
-static int resample_frame(struct AVAudioResampleContext *r,
- struct mp_audio *out, struct mp_audio *in)
-{
- return avresample_convert(r,
- out ? (uint8_t **)out->planes : NULL,
- out ? mp_audio_get_allocated_size(out) : 0,
- out ? out->samples : 0,
- in ? (uint8_t **)in->planes : NULL,
- in ? mp_audio_get_allocated_size(in) : 0,
- in ? in->samples : 0);
-}
-
static int filter(struct af_instance *af, struct mp_audio *in)
{
struct af_resample *s = af->priv;
+ if (s->pending) {
+ if (s->pending->samples) {
+ af_add_output_frame(af, s->pending);
+ } else {
+ talloc_free(s->pending);
+ }
+ s->pending = NULL;
+ }
+
int samples = avresample_available(s->avrctx) +
av_rescale_rnd(get_delay(s) + (in ? in->samples : 0),
s->ctx.out_rate, s->ctx.in_rate, AV_ROUND_UP);
@@ -345,7 +405,7 @@ static int filter(struct af_instance *af, struct mp_audio *in)
}
if (needs_reorder(s->reorder_out, out->nch)) {
- if (af_fmt_is_planar(out->format)) {
+ if (AF_FORMAT_IS_PLANAR(out->format)) {
reorder_planes(out, s->reorder_out);
} else if (out->samples) {
struct mp_audio *new = mp_audio_pool_get(s->reorder_buffer, out,
@@ -412,6 +472,7 @@ const struct af_info af_info_lavrresample = {
.cutoff = 0.0,
.phase_shift = 10,
},
+ .playback_speed = 1.0,
.allow_detach = 1,
},
.options = (const struct m_option[]) {
diff --git a/audio/filter/af_pan.c b/audio/filter/af_pan.c
index 979e472..900d10c 100644
--- a/audio/filter/af_pan.c
+++ b/audio/filter/af_pan.c
@@ -1,21 +1,20 @@
/*
* Copyright (C) 2002 Anders Johansson ajh@atri.curtin.edu.au
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
diff --git a/audio/filter/af_rubberband.c b/audio/filter/af_rubberband.c
new file mode 100644
index 0000000..2619e4b
--- /dev/null
+++ b/audio/filter/af_rubberband.c
@@ -0,0 +1,225 @@
+/*
+ * 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 <rubberband/rubberband-c.h>
+
+#include "common/common.h"
+#include "af.h"
+
+struct priv {
+ RubberBandState rubber;
+ double speed;
+ struct mp_audio *pending;
+ bool needs_reset;
+ // Estimate how much librubberband has buffered internally.
+ // I could not find a way to do this with the librubberband API.
+ double rubber_delay;
+ // command line options
+ int opt_transients, opt_detector, opt_phase, opt_window,
+ opt_smoothing, opt_formant, opt_pitch, opt_channels;
+};
+
+static void update_speed(struct af_instance *af, double new_speed)
+{
+ struct priv *p = af->priv;
+
+ p->speed = new_speed;
+ rubberband_set_time_ratio(p->rubber, 1.0 / p->speed);
+}
+
+static int control(struct af_instance *af, int cmd, void *arg)
+{
+ struct priv *p = af->priv;
+
+ switch (cmd) {
+ case AF_CONTROL_REINIT: {
+ struct mp_audio *in = arg;
+ struct mp_audio orig_in = *in;
+ struct mp_audio *out = af->data;
+
+ in->format = AF_FORMAT_FLOATP;
+ mp_audio_copy_config(out, in);
+
+ if (p->rubber)
+ rubberband_delete(p->rubber);
+
+ int opts = p->opt_transients | p->opt_detector | p->opt_phase |
+ p->opt_window | p->opt_smoothing | p->opt_formant |
+ p->opt_pitch | p-> opt_channels |
+ RubberBandOptionProcessRealTime;
+
+ p->rubber = rubberband_new(in->rate, in->channels.num, opts, 1.0, 1.0);
+ if (!p->rubber) {
+ MP_FATAL(af, "librubberband initialization failed.\n");
+ return AF_ERROR;
+ }
+
+ update_speed(af, p->speed);
+ control(af, AF_CONTROL_RESET, NULL);
+
+ return mp_audio_config_equals(in, &orig_in) ? AF_OK : AF_FALSE;
+ }
+ case AF_CONTROL_SET_PLAYBACK_SPEED: {
+ update_speed(af, *(double *)arg);
+ return AF_OK;
+ }
+ case AF_CONTROL_RESET:
+ if (p->rubber)
+ rubberband_reset(p->rubber);
+ talloc_free(p->pending);
+ p->pending = NULL;
+ p->rubber_delay = 0;
+ return AF_OK;
+ }
+ return AF_UNKNOWN;
+}
+
+static int filter_frame(struct af_instance *af, struct mp_audio *data)
+{
+ struct priv *p = af->priv;
+
+ talloc_free(p->pending);
+ p->pending = data;
+
+ return 0;
+}
+
+static int filter_out(struct af_instance *af)
+{
+ struct priv *p = af->priv;
+
+ while (rubberband_available(p->rubber) <= 0) {
+ const float *dummy[MP_NUM_CHANNELS] = {0};
+ const float **in_data = dummy;
+ size_t in_samples = 0;
+ if (p->pending) {
+ if (!p->pending->samples)
+ break;
+
+ // recover from previous EOF
+ if (p->needs_reset) {
+ rubberband_reset(p->rubber);
+ p->rubber_delay = 0;
+ }
+ p->needs_reset = false;
+
+ size_t needs = rubberband_get_samples_required(p->rubber);
+ in_data = (void *)&p->pending->planes;
+ in_samples = MPMIN(p->pending->samples, needs);
+ }
+
+ if (p->needs_reset)
+ break; // previous EOF
+ p->needs_reset = !p->pending; // EOF
+
+ rubberband_process(p->rubber, in_data, in_samples, p->needs_reset);
+ p->rubber_delay += in_samples;
+
+ if (!p->pending)
+ break;
+ mp_audio_skip_samples(p->pending, in_samples);
+ }
+
+ int out_samples = rubberband_available(p->rubber);
+ if (out_samples > 0) {
+ struct mp_audio *out =
+ mp_audio_pool_get(af->out_pool, af->data, out_samples);
+ if (!out)
+ return -1;
+ if (p->pending)
+ mp_audio_copy_config(out, p->pending);
+
+ float **out_data = (void *)&out->planes;
+ out->samples = rubberband_retrieve(p->rubber, out_data, out->samples);
+ p->rubber_delay -= out->samples * p->speed;
+
+ af_add_output_frame(af, out);
+ }
+
+ int delay_samples = p->rubber_delay;
+ if (p->pending)
+ delay_samples += p->pending->samples;
+ af->delay = delay_samples / (af->data->rate * p->speed);
+
+ return 0;
+}
+
+static void uninit(struct af_instance *af)
+{
+ struct priv *p = af->priv;
+
+ if (p->rubber)
+ rubberband_delete(p->rubber);
+ talloc_free(p->pending);
+}
+
+static int af_open(struct af_instance *af)
+{
+ af->control = control;
+ af->filter_frame = filter_frame;
+ af->filter_out = filter_out;
+ af->uninit = uninit;
+ return AF_OK;
+}
+
+#define OPT_BASE_STRUCT struct priv
+const struct af_info af_info_rubberband = {
+ .info = "Pitch conversion with librubberband",
+ .name = "rubberband",
+ .open = af_open,
+ .priv_size = sizeof(struct priv),
+ .priv_defaults = &(const struct priv) {
+ .speed = 1.0,
+ .opt_pitch = RubberBandOptionPitchHighConsistency,
+ .opt_transients = RubberBandOptionTransientsMixed,
+ .opt_formant = RubberBandOptionFormantPreserved,
+ },
+ .options = (const struct m_option[]) {
+ OPT_CHOICE("transients", opt_transients, 0,
+ ({"crisp", RubberBandOptionTransientsCrisp},
+ {"mixed", RubberBandOptionTransientsMixed},
+ {"smooth", RubberBandOptionTransientsSmooth})),
+ OPT_CHOICE("detector", opt_detector, 0,
+ ({"compound", RubberBandOptionDetectorCompound},
+ {"percussive", RubberBandOptionDetectorPercussive},
+ {"soft", RubberBandOptionDetectorSoft})),
+ OPT_CHOICE("phase", opt_phase, 0,
+ ({"laminar", RubberBandOptionPhaseLaminar},
+ {"independent", RubberBandOptionPhaseIndependent})),
+ OPT_CHOICE("window", opt_window, 0,
+ ({"standard", RubberBandOptionWindowStandard},
+ {"short", RubberBandOptionWindowShort},
+ {"long", RubberBandOptionWindowLong})),
+ OPT_CHOICE("smoothing", opt_smoothing, 0,
+ ({"off", RubberBandOptionSmoothingOff},
+ {"on", RubberBandOptionSmoothingOn})),
+ OPT_CHOICE("formant", opt_formant, 0,
+ ({"shifted", RubberBandOptionFormantShifted},
+ {"preserved", RubberBandOptionFormantPreserved})),
+ OPT_CHOICE("pitch", opt_pitch, 0,
+ ({"quality", RubberBandOptionPitchHighQuality},
+ {"speed", RubberBandOptionPitchHighSpeed},
+ {"consistency", RubberBandOptionPitchHighConsistency})),
+ OPT_CHOICE("channels", opt_channels, 0,
+ ({"apart", RubberBandOptionChannelsApart},
+ {"together", RubberBandOptionChannelsTogether})),
+ {0}
+ },
+};
diff --git a/audio/filter/af_scaletempo.c b/audio/filter/af_scaletempo.c
index f48f67f..131dc5d 100644
--- a/audio/filter/af_scaletempo.c
+++ b/audio/filter/af_scaletempo.c
@@ -14,21 +14,20 @@
*
* Copyright (c) 2007 Robert Juliano
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
@@ -76,9 +75,9 @@ typedef struct af_scaletempo_s
float ms_stride;
float percent_overlap;
float ms_search;
+#define SCALE_TEMPO 1
+#define SCALE_PITCH 2
int speed_opt;
- short speed_tempo;
- short speed_pitch;
} af_scaletempo_t;
static int fill_queue(struct af_instance *af, struct mp_audio *data, int offset)
@@ -276,6 +275,22 @@ static int filter(struct af_instance *af, struct mp_audio *data)
return 0;
}
+static void update_speed(struct af_instance *af, float speed)
+{
+ af_scaletempo_t *s = af->priv;
+
+ s->speed = speed;
+
+ double factor = (s->speed_opt & SCALE_PITCH) ? 1.0 / s->speed : s->speed;
+ s->scale = factor * s->scale_nominal;
+
+ s->frames_stride_scaled = s->scale * s->frames_stride;
+ s->frames_stride_error = MPMIN(s->frames_stride_error, s->frames_stride_scaled);
+
+ MP_VERBOSE(af, "%.3f speed * %.3f scale_nominal = %.3f\n",
+ s->speed, s->scale_nominal, s->scale);
+}
+
// Initialization and runtime control
static int control(struct af_instance *af, int cmd, void *arg)
{
@@ -287,19 +302,9 @@ static int control(struct af_instance *af, int cmd, void *arg)
int nch = data->nch;
int use_int = 0;
- MP_VERBOSE(af, "%.3f speed * %.3f scale_nominal = %.3f\n",
- s->speed, s->scale_nominal, s->scale);
-
mp_audio_force_interleaved_format(data);
mp_audio_copy_config(af->data, data);
- if (s->scale == 1.0) {
- if (s->speed_tempo && s->speed_pitch)
- return AF_DETACH;
- af->delay = 0;
- return af_test_output(af, data);
- }
-
if (data->format == AF_FORMAT_S16) {
use_int = 1;
} else {
@@ -309,10 +314,10 @@ static int control(struct af_instance *af, int cmd, void *arg)
s->frames_stride = srate * s->ms_stride;
s->bytes_stride = s->frames_stride * bps * nch;
- s->frames_stride_scaled = s->scale * s->frames_stride;
- s->frames_stride_error = 0;
af->delay = 0;
+ update_speed(af, s->speed);
+
int frames_overlap = s->frames_stride * s->percent_overlap;
if (frames_overlap <= 0) {
s->bytes_standing = s->bytes_stride;
@@ -422,17 +427,14 @@ static int control(struct af_instance *af, int cmd, void *arg)
return af_test_output(af, (struct mp_audio *)arg);
}
case AF_CONTROL_SET_PLAYBACK_SPEED: {
- if (s->speed_tempo) {
- if (s->speed_pitch)
+ double speed = *(double *)arg;
+ if (s->speed_opt & SCALE_TEMPO) {
+ if (s->speed_opt & SCALE_PITCH)
break;
- s->speed = *(double *)arg;
- s->scale = s->speed * s->scale_nominal;
- } else {
- if (s->speed_pitch) {
- s->speed = 1 / *(double *)arg;
- s->scale = s->speed * s->scale_nominal;
- break;
- }
+ update_speed(af, speed);
+ } else if (s->speed_opt & SCALE_PITCH) {
+ update_speed(af, speed);
+ break; // do not signal OK
}
return AF_OK;
}
@@ -456,22 +458,12 @@ static void uninit(struct af_instance *af)
free(s->table_window);
}
-#define SCALE_TEMPO 1
-#define SCALE_PITCH 2
-
// Allocate memory and set function pointers
static int af_open(struct af_instance *af)
{
- af_scaletempo_t *s = af->priv;
-
af->control = control;
af->uninit = uninit;
af->filter_frame = filter;
-
- s->speed_tempo = !!(s->speed_opt & SCALE_TEMPO);
- s->speed_pitch = !!(s->speed_opt & SCALE_PITCH);
-
- s->scale = s->speed * s->scale_nominal;
return AF_OK;
}
diff --git a/audio/filter/af_sinesuppress.c b/audio/filter/af_sinesuppress.c
index 32e074d..2418262 100644
--- a/audio/filter/af_sinesuppress.c
+++ b/audio/filter/af_sinesuppress.c
@@ -3,21 +3,20 @@
* Copyright (C) 2004 Alex Beregszaszi
* based upon af_extrastereo.c by Pierre Lombard
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
diff --git a/audio/filter/af_sub.c b/audio/filter/af_sub.c
index 243c9ce..82aa77f 100644
--- a/audio/filter/af_sub.c
+++ b/audio/filter/af_sub.c
@@ -1,21 +1,20 @@
/*
* Copyright (C) 2002 Anders Johansson ajh@watri.uwa.edu.au
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
/* This filter adds a sub-woofer channels to the audio stream by
diff --git a/audio/filter/af_surround.c b/audio/filter/af_surround.c
index eabf147..fdbd396 100644
--- a/audio/filter/af_surround.c
+++ b/audio/filter/af_surround.c
@@ -3,23 +3,22 @@
* This will provide a (basic) surround-sound effect from
* audio encoded for Dolby Surround, Pro Logic etc.
*
- * original author: Steve Davies <steve@daviesfam.org>
+ * Original author: Steve Davies <steve@daviesfam.org>
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
/* The principle: Make rear channels by extracting anti-phase data
diff --git a/audio/filter/af_sweep.c b/audio/filter/af_sweep.c
index 854ef14..5f33863 100644
--- a/audio/filter/af_sweep.c
+++ b/audio/filter/af_sweep.c
@@ -1,21 +1,20 @@
/*
* Copyright (c) 2004 Michael Niedermayer <michaelni@gmx.at>
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
diff --git a/audio/filter/af_volume.c b/audio/filter/af_volume.c
index 0a3264e..a0b47e7 100644
--- a/audio/filter/af_volume.c
+++ b/audio/filter/af_volume.c
@@ -1,21 +1,20 @@
/*
* Copyright (C)2002 Anders Johansson ajh@atri.curtin.edu.au
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
@@ -59,7 +58,7 @@ static int control(struct af_instance *af, int cmd, void *arg)
} else {
mp_audio_set_format(af->data, AF_FORMAT_FLOAT);
}
- if (af_fmt_is_planar(in->format))
+ if (AF_FORMAT_IS_PLANAR(in->format))
mp_audio_set_format(af->data, af_fmt_to_planar(af->data->format));
s->rgain = 1.0;
if ((s->rgain_track || s->rgain_album) && af->replaygain_data) {
diff --git a/audio/filter/dsp.h b/audio/filter/dsp.h
index 164eccf..85fbb29 100644
--- a/audio/filter/dsp.h
+++ b/audio/filter/dsp.h
@@ -1,21 +1,20 @@
/*
* Copyright (C) 2002 Anders Johansson ajh@atri.curtin.edu.au
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_DSP_H
diff --git a/audio/filter/equalizer.h b/audio/filter/equalizer.h
index 6505379..db68235 100644
--- a/audio/filter/equalizer.h
+++ b/audio/filter/equalizer.h
@@ -1,21 +1,20 @@
/*
* Copyright (C) 2002 Anders Johansson ajh@atri.curtin.edu.au
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_EQUALIZER_H
diff --git a/audio/filter/filter.c b/audio/filter/filter.c
index dbbe743..9a2107c 100644
--- a/audio/filter/filter.c
+++ b/audio/filter/filter.c
@@ -3,21 +3,20 @@
*
* Copyright (C) 2001 Anders Johansson ajh@atri.curtin.edu.au
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
diff --git a/audio/filter/filter.h b/audio/filter/filter.h
index f3dea88..8a1864a 100644
--- a/audio/filter/filter.h
+++ b/audio/filter/filter.h
@@ -1,21 +1,20 @@
/*
* Copyright (C) 2001 Anders Johansson ajh@atri.curtin.edu.au
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined MPLAYER_DSP_H
diff --git a/audio/filter/tools.c b/audio/filter/tools.c
index 2314779..a465e7e 100644
--- a/audio/filter/tools.c
+++ b/audio/filter/tools.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <math.h>
@@ -40,24 +39,6 @@ int af_from_dB(int n, float* in, float* out, float k, float mi, float ma)
return AF_OK;
}
-/* Convert from gain value to dB. Returns AF_OK if of and AF_ERROR if
- * fail. gain=0 will become -200 dB. k is just a multiplier. */
-int af_to_dB(int n, float* in, float* out, float k)
-{
- int i = 0;
- // Sanity check
- if(!in || !out)
- return AF_ERROR;
-
- for(i=0;i<n;i++){
- if(in[i] == 0.0)
- out[i]=-200.0;
- else
- out[i]=k*log10(in[i]);
- }
- return AF_OK;
-}
-
/* Convert from ms to sample time */
int af_from_ms(int n, float* in, int* out, int rate, float mi, float ma)
{
@@ -72,20 +53,6 @@ int af_from_ms(int n, float* in, int* out, int rate, float mi, float ma)
return AF_OK;
}
-/* Convert from sample time to ms */
-int af_to_ms(int n, int* in, float* out, int rate)
-{
- int i = 0;
- // Sanity check
- if(!in || !out || !rate)
- return AF_ERROR;
-
- for(i=0;i<n;i++)
- out[i]=1000.0 * (float)in[i]/((float)rate);
-
- return AF_OK;
-}
-
/*
* test if output format matches
* af: audio filter
diff --git a/audio/filter/window.c b/audio/filter/window.c
index 089c629..3cc8fe4 100644
--- a/audio/filter/window.c
+++ b/audio/filter/window.c
@@ -1,21 +1,20 @@
/*
* Copyright (C) 2001 Anders Johansson ajh@atri.curtin.edu.au
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
/* Calculates a number of window functions. The following window
diff --git a/audio/filter/window.h b/audio/filter/window.h
index 1c179b7..0187423 100644
--- a/audio/filter/window.h
+++ b/audio/filter/window.h
@@ -1,21 +1,20 @@
/*
* Copyright (C) 2001 Anders Johansson ajh@atri.curtin.edu.au
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
/* Calculates a number of window functions. The following window
diff --git a/audio/fmt-conversion.c b/audio/fmt-conversion.c
index 5550e69..74b0552 100644
--- a/audio/fmt-conversion.c
+++ b/audio/fmt-conversion.c
@@ -1,20 +1,19 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
+ */
#include <libavutil/avutil.h>
#include <libavutil/samplefmt.h>
diff --git a/audio/fmt-conversion.h b/audio/fmt-conversion.h
index 7f2739f..9238557 100644
--- a/audio/fmt-conversion.h
+++ b/audio/fmt-conversion.h
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_SAMPLE_FMT_CONVERSION_H
diff --git a/audio/format.c b/audio/format.c
index 08b369c..4012142 100644
--- a/audio/format.c
+++ b/audio/format.c
@@ -1,21 +1,20 @@
/*
* Copyright (C) 2005 Alex Beregszaszi
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
@@ -45,11 +44,6 @@ int af_fmt2bits(int format)
return af_fmt2bps(format) * 8;
}
-bool af_fmt_is_float(int format)
-{
- return !!(format & AF_FORMAT_F);
-}
-
static int bits_to_mask(int bits)
{
switch (bits) {
@@ -105,12 +99,6 @@ int af_fmt_from_planar(int format)
return format;
}
-// false for interleaved and AF_FORMAT_UNKNOWN
-bool af_fmt_is_planar(int format)
-{
- return !!(format & AF_FORMAT_PLANAR);
-}
-
const struct af_fmt_entry af_fmtstr_table[] = {
{"u8", AF_FORMAT_U8},
{"s8", AF_FORMAT_S8},
@@ -161,7 +149,7 @@ const char *af_fmt_to_str(int format)
int af_fmt_seconds_to_bytes(int format, float seconds, int channels, int samplerate)
{
- assert(!af_fmt_is_planar(format));
+ assert(!AF_FORMAT_IS_PLANAR(format));
int bps = af_fmt2bps(format);
int framelen = channels * bps;
int bytes = seconds * bps * samplerate;
@@ -227,3 +215,19 @@ int af_format_conversion_score(int dst_format, int src_format)
score -= 2048; // has to convert float<->int
return score;
}
+
+// Return the number of samples that make up one frame in this format.
+// You get the byte size by multiplying them with sample size and channel count.
+int af_format_sample_alignment(int format)
+{
+ switch (format) {
+ case AF_FORMAT_S_AAC: return 16384 / 4;
+ case AF_FORMAT_S_AC3: return 6144 / 4;
+ case AF_FORMAT_S_DTSHD: return 32768 / 16;
+ case AF_FORMAT_S_DTS: return 2048 / 4;
+ case AF_FORMAT_S_EAC3: return 24576 / 4;
+ case AF_FORMAT_S_MP3: return 4608 / 4;
+ case AF_FORMAT_S_TRUEHD: return 61440 / 16;
+ default: return 1;
+ }
+}
diff --git a/audio/format.h b/audio/format.h
index b3ac000..605875e 100644
--- a/audio/format.h
+++ b/audio/format.h
@@ -3,21 +3,20 @@
* The format definition only refers to the storage format,
* not the resolution.
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_AF_FORMAT_H
@@ -96,6 +95,9 @@ enum af_format {
#define AF_FORMAT_IS_IEC61937(f) (((f) & AF_FORMAT_TYPE_MASK) == AF_FORMAT_S)
#define AF_FORMAT_IS_SPECIAL(f) AF_FORMAT_IS_IEC61937(f)
+#define AF_FORMAT_IS_FLOAT(f) (!!((f) & AF_FORMAT_F))
+// false for interleaved and AF_FORMAT_UNKNOWN
+#define AF_FORMAT_IS_PLANAR(f) (!!((f) & AF_FORMAT_PLANAR))
struct af_fmt_entry {
const char *name;
@@ -109,12 +111,10 @@ const char *af_fmt_to_str(int format);
int af_fmt2bps(int format);
int af_fmt2bits(int format);
-bool af_fmt_is_float(int format);
int af_fmt_change_bits(int format, int bits);
int af_fmt_to_planar(int format);
int af_fmt_from_planar(int format);
-bool af_fmt_is_planar(int format);
// Amount of bytes that contain audio of the given duration, aligned to frames.
int af_fmt_seconds_to_bytes(int format, float seconds, int channels, int samplerate);
@@ -125,4 +125,6 @@ void af_fill_silence(void *dst, size_t bytes, int format);
int af_format_conversion_score(int dst_format, int src_format);
+int af_format_sample_alignment(int format);
+
#endif /* MPLAYER_AF_FORMAT_H */
diff --git a/audio/mixer.c b/audio/mixer.c
index ced2196..e56d5db 100644
--- a/audio/mixer.c
+++ b/audio/mixer.c
@@ -1,24 +1,24 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#include <stdio.h>
#include <math.h>
+#include <assert.h>
#include <libavutil/common.h>
@@ -37,7 +37,7 @@ struct mixer {
struct af_stream *af;
// Static, dependent on ao/softvol settings
bool softvol; // use AO (false) or af_volume (true)
- bool ao_softvol; // AO has private or per-app volume
+ bool persistent_volume; // volume does not need to be restored
bool emulate_mute; // if true, emulate mute with volume=0
// Last known values (possibly out of sync with reality)
float vol_l, vol_r;
@@ -88,6 +88,7 @@ static void checkvolume(struct mixer *mixer)
vol.left = (gain / (mixer->opts->softvol_max / 100.0)) * 100.0;
vol.right = (gain / (mixer->opts->softvol_max / 100.0)) * 100.0;
} else {
+ MP_DBG(mixer, "Reading volume from AO.\n");
// Rely on the values not changing if the query is not supported
ao_control(mixer->ao, AOCONTROL_GET_VOLUME, &vol);
ao_control(mixer->ao, AOCONTROL_GET_MUTE, &mixer->muted);
@@ -123,14 +124,17 @@ static void setvolume_internal(struct mixer *mixer, float l, float r)
{
struct ao_control_vol vol = {.left = l, .right = r};
if (!mixer->softvol) {
+ MP_DBG(mixer, "Setting volume on AO.\n");
if (ao_control(mixer->ao, AOCONTROL_SET_VOLUME, &vol) != CONTROL_OK)
MP_ERR(mixer, "Failed to change audio output volume.\n");
return;
}
float gain = (l + r) / 2.0 / 100.0 * mixer->opts->softvol_max / 100.0;
if (!af_control_any_rev(mixer->af, AF_CONTROL_SET_VOLUME, &gain)) {
+ if (gain == 1.0)
+ return;
MP_VERBOSE(mixer, "Inserting volume filter.\n");
- if (!(af_add(mixer->af, "volume", NULL)
+ if (!(af_add(mixer->af, "volume", "softvol", NULL)
&& af_control_any_rev(mixer->af, AF_CONTROL_SET_VOLUME, &gain)))
MP_ERR(mixer, "No volume control available.\n");
}
@@ -139,8 +143,7 @@ static void setvolume_internal(struct mixer *mixer, float l, float r)
void mixer_setvolume(struct mixer *mixer, float l, float r)
{
checkvolume(mixer); // to check mute status
- if (mixer->vol_l == l && mixer->vol_r == r)
- return; // just prevent af_volume insertion when not needed
+
mixer->vol_l = av_clipf(l, 0, 100);
mixer->vol_r = av_clipf(r, 0, 100);
if (mixer->ao && !(mixer->emulate_mute && mixer->muted))
@@ -218,7 +221,7 @@ void mixer_setbalance(struct mixer *mixer, float val)
if (val == 0)
return;
- if (!(af_pan_balance = af_add(mixer->af, "pan", NULL))) {
+ if (!(af_pan_balance = af_add(mixer->af, "pan", "autopan", NULL))) {
MP_ERR(mixer, "No balance control available.\n");
return;
}
@@ -246,17 +249,23 @@ char *mixer_get_volume_restore_data(struct mixer *mixer)
static void probe_softvol(struct mixer *mixer)
{
- mixer->ao_softvol =
- ao_control(mixer->ao, AOCONTROL_HAS_SOFT_VOLUME, 0) == 1 ||
- ao_control(mixer->ao, AOCONTROL_HAS_PER_APP_VOLUME, 0) == 1;
+ bool ao_perapp = ao_control(mixer->ao, AOCONTROL_HAS_PER_APP_VOLUME, 0) == 1;
+ bool ao_softvol = ao_control(mixer->ao, AOCONTROL_HAS_SOFT_VOLUME, 0) == 1;
+ assert(!(ao_perapp && ao_softvol));
+ mixer->persistent_volume = !ao_softvol;
if (mixer->opts->softvol == SOFTVOL_AUTO) {
// No system-wide volume => fine with AO volume control.
- mixer->softvol = !mixer->ao_softvol;
+ mixer->softvol = !ao_softvol && !ao_perapp;
} else {
mixer->softvol = mixer->opts->softvol == SOFTVOL_YES;
}
+ if (mixer->softvol)
+ mixer->persistent_volume = false;
+
+ MP_DBG(mixer, "Will use af_volume: %s\n", mixer->softvol ? "yes" : "no");
+
// If we can't use real volume control => force softvol.
if (!mixer->softvol) {
ao_control_vol_t vol;
@@ -285,11 +294,9 @@ static void restore_volume(struct mixer *mixer)
const char *prev_driver = mixer->driver;
mixer->driver = mixer->softvol ? "softvol" : ao_get_name(ao);
- bool restore = mixer->softvol || mixer->ao_softvol;
-
// Restore old parameters if volume won't survive reinitialization.
// But not if volume scale is possibly different.
- if (restore && strcmp(mixer->driver, prev_driver) == 0) {
+ if (!mixer->persistent_volume && strcmp(mixer->driver, prev_driver) == 0) {
force_vol_l = mixer->vol_l;
force_vol_r = mixer->vol_r;
}
@@ -306,7 +313,7 @@ static void restore_volume(struct mixer *mixer)
// Set parameters from playback resume.
char *data = mixer->opts->mixer_restore_volume_data;
- if (restore && data && data[0]) {
+ if (!mixer->persistent_volume && data && data[0]) {
char drv[40];
float v_l, v_r, s;
int m;
@@ -316,6 +323,7 @@ static void restore_volume(struct mixer *mixer)
force_vol_l = v_l;
force_vol_r = v_r;
force_mute = !!m;
+ MP_DBG(mixer, "Restoring volume from resume config.\n");
}
}
talloc_free(mixer->opts->mixer_restore_volume_data);
@@ -331,10 +339,14 @@ static void restore_volume(struct mixer *mixer)
opts->mixer_init_mute = -1;
checkvolume(mixer);
- if (force_vol_l >= 0 && force_vol_r >= 0)
+ if (force_vol_l >= 0 && force_vol_r >= 0) {
+ MP_DBG(mixer, "Restoring previous volume.\n");
mixer_setvolume(mixer, force_vol_l, force_vol_r);
- if (force_mute >= 0)
+ }
+ if (force_mute >= 0) {
+ MP_DBG(mixer, "Restoring previous mute toggle.\n");
mixer_setmute(mixer, force_mute);
+ }
}
// Called after the audio filter chain is built or rebuilt.
@@ -346,6 +358,8 @@ void mixer_reinit_audio(struct mixer *mixer, struct ao *ao, struct af_stream *af
mixer->ao = ao;
mixer->af = af;
+ MP_DBG(mixer, "Reinit...\n");
+
probe_softvol(mixer);
restore_volume(mixer);
@@ -362,8 +376,11 @@ void mixer_uninit_audio(struct mixer *mixer)
if (!mixer->ao)
return;
+ MP_DBG(mixer, "Uninit...\n");
+
checkvolume(mixer);
- if (mixer->muted_by_us && !mixer->softvol && !mixer->ao_softvol) {
+ if (mixer->muted_by_us && mixer->persistent_volume) {
+ MP_DBG(mixer, "Draining.\n");
/* Current audio output API combines playing the remaining buffered
* audio and uninitializing the AO into one operation, even though
* ideally unmute would happen between those two steps. We can't do
diff --git a/audio/mixer.h b/audio/mixer.h
index 1716401..5aaf605 100644
--- a/audio/mixer.h
+++ b/audio/mixer.h
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_MIXER_H
diff --git a/audio/out/ao.c b/audio/out/ao.c
index ccb39f7..c23b22d 100644
--- a/audio/out/ao.c
+++ b/audio/out/ao.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
@@ -199,7 +198,7 @@ static struct ao *ao_init(bool probing, struct mpv_global *global,
ao->sstride = af_fmt2bps(ao->format);
ao->num_planes = 1;
- if (af_fmt_is_planar(ao->format)) {
+ if (AF_FORMAT_IS_PLANAR(ao->format)) {
ao->num_planes = ao->channels.num;
} else {
ao->sstride *= ao->channels.num;
@@ -211,6 +210,9 @@ static struct ao *ao_init(bool probing, struct mpv_global *global,
if (ao->device_buffer)
MP_VERBOSE(ao, "device buffer: %d samples.\n", ao->device_buffer);
ao->buffer = MPMAX(ao->device_buffer, ao->def_buffer * ao->samplerate);
+
+ int align = af_format_sample_alignment(ao->format);
+ ao->buffer = (ao->buffer + align - 1) / align * align;
MP_VERBOSE(ao, "using soft-buffer of %d samples.\n", ao->buffer);
if (ao->api->init(ao) < 0)
@@ -386,14 +388,25 @@ int ao_query_and_reset_events(struct ao *ao, int events)
int actual_events = 0;
if (atomic_load(&ao->request_reload)) // don't need to reset it
actual_events |= AO_EVENT_RELOAD;
+ if (atomic_load(&ao->request_hotplug))
+ actual_events |= AO_EVENT_HOTPLUG;
return actual_events & events;
}
-// Request that the player core destroys and recreates the AO.
+// Request that the player core destroys and recreates the AO. Fully thread-safe.
void ao_request_reload(struct ao *ao)
{
atomic_store(&ao->request_reload, true);
- mp_input_wakeup(ao->input_ctx);
+ if (ao->input_ctx)
+ mp_input_wakeup(ao->input_ctx);
+}
+
+// Notify the player that the device list changed. Fully thread-safe.
+void ao_hotplug_event(struct ao *ao)
+{
+ atomic_store(&ao->request_hotplug, true);
+ if (ao->input_ctx)
+ mp_input_wakeup(ao->input_ctx);
}
bool ao_chmap_sel_adjust(struct ao *ao, const struct mp_chmap_sel *s,
@@ -405,7 +418,10 @@ bool ao_chmap_sel_adjust(struct ao *ao, const struct mp_chmap_sel *s,
MP_DBG(ao, "chmap_sel #%d: %s\n", i, mp_chmap_to_str(&c));
}
}
- return mp_chmap_sel_adjust(s, map);
+ bool r = mp_chmap_sel_adjust(s, map);
+ if (r)
+ MP_DBG(ao, "result: %s\n", mp_chmap_to_str(map));
+ return r;
}
bool ao_chmap_sel_get_def(struct ao *ao, const struct mp_chmap_sel *s,
@@ -439,31 +455,95 @@ bool ao_untimed(struct ao *ao)
return ao->untimed;
}
-const char *ao_get_detected_device(struct ao *ao)
+// ---
+
+struct ao_hotplug {
+ struct mpv_global *global;
+ struct input_ctx *input_ctx;
+ // A single AO instance is used to listen to hotplug events. It wouldn't
+ // make much sense to allow multiple AO drivers; all sane platforms have
+ // a single such audio API.
+ // This is _not_ the same AO instance as used for playing audio.
+ struct ao *ao;
+ // cached
+ struct ao_device_list *list;
+ bool needs_update;
+};
+
+struct ao_hotplug *ao_hotplug_create(struct mpv_global *global,
+ struct input_ctx *input_ctx)
+{
+ struct ao_hotplug *hp = talloc_ptrtype(NULL, hp);
+ *hp = (struct ao_hotplug){
+ .global = global,
+ .input_ctx = input_ctx,
+ .needs_update = true,
+ };
+ return hp;
+}
+
+static void get_devices(struct ao *ao, struct ao_device_list *list)
+{
+ int num = list->num_devices;
+ if (ao->driver->list_devs)
+ ao->driver->list_devs(ao, list);
+ // Add at least a default entry
+ if (list->num_devices == num)
+ ao_device_list_add(list, ao, &(struct ao_device_desc){"", "Default"});
+}
+
+bool ao_hotplug_check_update(struct ao_hotplug *hp)
+{
+ if (hp->ao && ao_query_and_reset_events(hp->ao, AO_EVENT_HOTPLUG)) {
+ hp->needs_update = true;
+ atomic_store(&hp->ao->request_hotplug, false);
+ return true;
+ }
+ return false;
+}
+
+const char *ao_hotplug_get_detected_device(struct ao_hotplug *hp)
{
- return ao->detected_device;
+ if (!hp || !hp->ao)
+ return NULL;
+ return hp->ao->detected_device;
}
-struct ao_device_list *ao_get_device_list(struct mpv_global *global)
+// The return value is valid until the next call to this API.
+struct ao_device_list *ao_hotplug_get_device_list(struct ao_hotplug *hp)
{
- struct ao_device_list *list = talloc_zero(NULL, struct ao_device_list);
+ if (hp->list && !hp->needs_update)
+ return hp->list;
+
+ talloc_free(hp->list);
+ struct ao_device_list *list = talloc_zero(hp, struct ao_device_list);
+ hp->list = list;
+
MP_TARRAY_APPEND(list, list->devices, list->num_devices,
(struct ao_device_desc){"auto", "Autoselect device"});
+
for (int n = 0; audio_out_drivers[n]; n++) {
const struct ao_driver *d = audio_out_drivers[n];
if (d == &audio_out_null)
break; // don't add unsafe/special entries
- struct ao *ao = ao_alloc(true, global, NULL, (char *)d->name, NULL);
+
+ struct ao *ao = ao_alloc(true, hp->global, hp->input_ctx,
+ (char *)d->name, NULL);
if (!ao)
continue;
- int num = list->num_devices;
- if (d->list_devs)
- d->list_devs(ao, list);
- // Add at least a default entry
- if (list->num_devices == num)
- ao_device_list_add(list, ao, &(struct ao_device_desc){"", "Default"});
- talloc_free(ao);
+
+ if (ao->driver->hotplug_init) {
+ if (!hp->ao && ao->driver->hotplug_init(ao) >= 0)
+ hp->ao = ao; // keep this one
+ if (hp->ao && hp->ao->driver == d)
+ get_devices(hp->ao, list);
+ } else {
+ get_devices(ao, list);
+ }
+ if (ao != hp->ao)
+ talloc_free(ao);
}
+ hp->needs_update = false;
return list;
}
@@ -478,13 +558,24 @@ void ao_device_list_add(struct ao_device_list *list, struct ao *ao,
MP_TARRAY_APPEND(list, list->devices, list->num_devices, c);
}
+void ao_hotplug_destroy(struct ao_hotplug *hp)
+{
+ if (!hp)
+ return;
+ if (hp->ao && hp->ao->driver->hotplug_uninit)
+ hp->ao->driver->hotplug_uninit(hp->ao);
+ talloc_free(hp->ao);
+ talloc_free(hp);
+}
+
void ao_print_devices(struct mpv_global *global, struct mp_log *log)
{
- struct ao_device_list *list = ao_get_device_list(global);
+ struct ao_hotplug *hp = ao_hotplug_create(global, NULL);
+ struct ao_device_list *list = ao_hotplug_get_device_list(hp);
mp_info(log, "List of detected audio devices:\n");
for (int n = 0; n < list->num_devices; n++) {
struct ao_device_desc *desc = &list->devices[n];
mp_info(log, " '%s' (%s)\n", desc->name, desc->desc);
}
- talloc_free(list);
+ ao_hotplug_destroy(hp);
}
diff --git a/audio/out/ao.h b/audio/out/ao.h
index dbbed24..baf10ac 100644
--- a/audio/out/ao.h
+++ b/audio/out/ao.h
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_AUDIO_OUT_H
@@ -48,6 +47,7 @@ enum aocontrol {
enum {
AO_EVENT_RELOAD = 1,
+ AO_EVENT_HOTPLUG = 2,
};
typedef struct ao_control_vol {
@@ -79,7 +79,6 @@ void ao_uninit(struct ao *ao);
void ao_get_format(struct ao *ao, struct mp_audio *format);
const char *ao_get_name(struct ao *ao);
const char *ao_get_description(struct ao *ao);
-const char *ao_get_detected_device(struct ao *ao);
bool ao_untimed(struct ao *ao);
int ao_play(struct ao *ao, void **data, int samples, int flags);
int ao_control(struct ao *ao, enum aocontrol cmd, void *arg);
@@ -92,8 +91,16 @@ void ao_drain(struct ao *ao);
bool ao_eof_reached(struct ao *ao);
int ao_query_and_reset_events(struct ao *ao, int events);
void ao_request_reload(struct ao *ao);
+void ao_hotplug_event(struct ao *ao);
+
+struct ao_hotplug;
+struct ao_hotplug *ao_hotplug_create(struct mpv_global *global,
+ struct input_ctx *input_ctx);
+void ao_hotplug_destroy(struct ao_hotplug *hp);
+bool ao_hotplug_check_update(struct ao_hotplug *hp);
+const char *ao_hotplug_get_detected_device(struct ao_hotplug *hp);
+struct ao_device_list *ao_hotplug_get_device_list(struct ao_hotplug *hp);
-struct ao_device_list *ao_get_device_list(struct mpv_global *global);
void ao_print_devices(struct mpv_global *global, struct mp_log *log);
#endif /* MPLAYER_AUDIO_OUT_H */
diff --git a/audio/out/ao_alsa.c b/audio/out/ao_alsa.c
index dba6b19..f4565da 100644
--- a/audio/out/ao_alsa.c
+++ b/audio/out/ao_alsa.c
@@ -10,21 +10,20 @@
* 04/13/2004 merged with ao_alsa1.x, fixes provided by Jindrich Makovicka
* 04/25/2004 printfs converted to mp_msg, Zsolt.
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <errno.h>
@@ -64,6 +63,7 @@ struct priv {
int cfg_mixer_index;
int cfg_resample;
int cfg_ni;
+ int cfg_ignore_chmap;
};
#define BUFFER_TIME 250000 // 250ms
@@ -375,6 +375,7 @@ static int try_open_device(struct ao *ao, const char *device)
IEC958_AES1_CON_ORIGINAL | IEC958_AES1_CON_PCM_CODER,
map_iec958_srate(ao->samplerate));
const char *ac3_device = append_params(tmp, device, params);
+ MP_VERBOSE(ao, "opening device '%s' => '%s'\n", device, ac3_device);
int err = snd_pcm_open
(&p->alsa, ac3_device, SND_PCM_STREAM_PLAYBACK, 0);
talloc_free(tmp);
@@ -382,6 +383,7 @@ static int try_open_device(struct ao *ao, const char *device)
return 0;
}
+ MP_VERBOSE(ao, "opening device '%s'\n", device);
return snd_pcm_open(&p->alsa, device, SND_PCM_STREAM_PLAYBACK, 0);
}
@@ -407,23 +409,14 @@ static int init_device(struct ao *ao)
struct priv *p = ao->priv;
int err;
- if (!p->cfg_ni)
- ao->format = af_fmt_from_planar(ao->format);
-
const char *device = "default";
- if (AF_FORMAT_IS_IEC61937(ao->format)) {
+ if (AF_FORMAT_IS_IEC61937(ao->format))
device = "iec958";
- MP_VERBOSE(ao, "playing AC3/iec61937/iec958, %i channels\n",
- ao->channels.num);
- }
if (ao->device)
device = ao->device;
if (p->cfg_device && p->cfg_device[0])
device = p->cfg_device;
- MP_VERBOSE(ao, "using device: %s\n", device);
- MP_VERBOSE(ao, "using ALSA version: %s\n", snd_asoundlib_version());
-
err = try_open_device(ao, device);
CHECK_ALSA_ERROR("Playback open error");
@@ -466,11 +459,11 @@ static int init_device(struct ao *ao)
err = snd_pcm_hw_params_set_format(p->alsa, alsa_hwparams, p->alsa_fmt);
CHECK_ALSA_ERROR("Unable to set format");
- snd_pcm_access_t access = af_fmt_is_planar(ao->format)
+ snd_pcm_access_t access = AF_FORMAT_IS_PLANAR(ao->format)
? SND_PCM_ACCESS_RW_NONINTERLEAVED
: SND_PCM_ACCESS_RW_INTERLEAVED;
err = snd_pcm_hw_params_set_access(p->alsa, alsa_hwparams, access);
- if (err < 0 && af_fmt_is_planar(ao->format)) {
+ if (err < 0 && AF_FORMAT_IS_PLANAR(ao->format)) {
ao->format = af_fmt_from_planar(ao->format);
access = SND_PCM_ACCESS_RW_INTERLEAVED;
err = snd_pcm_hw_params_set_access(p->alsa, alsa_hwparams, access);
@@ -478,7 +471,7 @@ static int init_device(struct ao *ao)
CHECK_ALSA_ERROR("Unable to set access type");
struct mp_chmap dev_chmap = ao->channels;
- if (AF_FORMAT_IS_IEC61937(ao->format)) {
+ if (AF_FORMAT_IS_IEC61937(ao->format) || p->cfg_ignore_chmap) {
dev_chmap.num = 0; // disable chmap API
} else if (query_chmaps(ao, &dev_chmap)) {
ao->channels = dev_chmap;
@@ -499,8 +492,11 @@ static int init_device(struct ao *ao)
}
if (num_channels != ao->channels.num) {
- MP_ERR(ao, "Couldn't get requested number of channels.\n");
mp_chmap_from_channels_alsa(&ao->channels, num_channels);
+ if (!mp_chmap_is_valid(&ao->channels))
+ mp_chmap_from_channels(&ao->channels, 2);
+ MP_ERR(ao, "Couldn't get requested number of channels, fallback to %s.\n",
+ mp_chmap_to_str(&ao->channels));
}
// Some ALSA drivers have broken delay reporting, so disable the ALSA
@@ -550,8 +546,11 @@ static int init_device(struct ao *ao)
err = snd_pcm_set_chmap(p->alsa, alsa_chmap);
if (err == -ENXIO) {
- MP_WARN(ao, "Device does not support requested channel map (%s)\n",
- mp_chmap_to_str(&dev_chmap));
+ // I consider this an ALSA bug: the channel map was reported as
+ // supported, but we still can't set it. It happens virtually
+ // always with dmix, though.
+ MP_VERBOSE(ao, "Device does not support requested channel map (%s)\n",
+ mp_chmap_to_str(&dev_chmap));
} else {
CHECK_ALSA_WARN("Channel map setup failed");
}
@@ -571,7 +570,9 @@ static int init_device(struct ao *ao)
MP_VERBOSE(ao, "which we understand as: %s\n", mp_chmap_to_str(&chmap));
- if (AF_FORMAT_IS_IEC61937(ao->format)) {
+ if (p->cfg_ignore_chmap) {
+ MP_VERBOSE(ao, "user set ignore-chmap; ignoring the channel map.\n");
+ } else if (AF_FORMAT_IS_IEC61937(ao->format)) {
MP_VERBOSE(ao, "using spdif passthrough; ignoring the channel map.\n");
} else if (mp_chmap_is_valid(&chmap)) {
if (mp_chmap_equals(&chmap, &ao->channels)) {
@@ -604,6 +605,7 @@ static int init_device(struct ao *ao)
// the number of channels to 2 either, because the hw params
// are already set! So just fuck it and reopen the device with
// the chmap "cleaned out" of NA entries.
+ MP_VERBOSE(ao, "Working around braindead ALSA behavior.\n");
err = snd_pcm_close(p->alsa);
p->alsa = NULL;
CHECK_ALSA_ERROR("pcm close error");
@@ -675,6 +677,12 @@ alsa_error:
static int init(struct ao *ao)
{
+ struct priv *p = ao->priv;
+ if (!p->cfg_ni)
+ ao->format = af_fmt_from_planar(ao->format);
+
+ MP_VERBOSE(ao, "using ALSA version: %s\n", snd_asoundlib_version());
+
int r = init_device(ao);
if (r == INIT_BRAINDEATH)
r = init_device(ao); // retry with normalized channel layout
@@ -814,7 +822,7 @@ static int play(struct ao *ao, void **data, int samples, int flags)
return 0;
do {
- if (af_fmt_is_planar(ao->format)) {
+ if (AF_FORMAT_IS_PLANAR(ao->format)) {
res = snd_pcm_writen(p->alsa, data, samples);
} else {
res = snd_pcm_writei(p->alsa, data[0], samples);
@@ -934,6 +942,7 @@ const struct ao_driver audio_out_alsa = {
OPT_STRING("mixer-name", cfg_mixer_name, 0),
OPT_INTRANGE("mixer-index", cfg_mixer_index, 0, 0, 99),
OPT_FLAG("non-interleaved", cfg_ni, 0),
+ OPT_FLAG("ignore-chmap", cfg_ignore_chmap, 0),
{0}
},
};
diff --git a/audio/out/ao_coreaudio.c b/audio/out/ao_coreaudio.c
index 0eab220..5e923ac 100644
--- a/audio/out/ao_coreaudio.c
+++ b/audio/out/ao_coreaudio.c
@@ -38,11 +38,6 @@ struct priv {
bool ca_layout_to_mp_chmap(struct ao *ao, AudioChannelLayout *layout,
struct mp_chmap *chmap);
-static int64_t ca_frames_to_us(struct ao *ao, uint32_t frames)
-{
- return frames / (float) ao->samplerate * 1e6;
-}
-
static int64_t ca_get_hardware_latency(struct ao *ao) {
struct priv *p = ao->priv;
@@ -73,17 +68,6 @@ coreaudio_error:
return 0;
}
-static int64_t ca_get_latency(const AudioTimeStamp *ts)
-{
- uint64_t out = AudioConvertHostTimeToNanos(ts->mHostTime);
- uint64_t now = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime());
-
- if (now > out)
- return 0;
-
- return (out - now) * 1e-3;
-}
-
static OSStatus render_cb_lpcm(void *ctx, AudioUnitRenderActionFlags *aflags,
const AudioTimeStamp *ts, UInt32 bus,
UInt32 frames, AudioBufferList *buffer_list)
@@ -134,8 +118,6 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg)
return set_volume(ao, arg);
case AOCONTROL_HAS_SOFT_VOLUME:
return CONTROL_TRUE;
- case AOCONTROL_HAS_PER_APP_VOLUME:
- return CONTROL_TRUE;
}
return CONTROL_UNKNOWN;
}
@@ -143,14 +125,7 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg)
static bool init_chmap(struct ao *ao);
static bool init_audiounit(struct ao *ao, AudioStreamBasicDescription asbd);
-static int init(struct ao *ao)
-{
- if (AF_FORMAT_IS_IEC61937(ao->format)) {
- MP_WARN(ao, "detected IEC61937, redirecting to coreaudio_exclusive\n");
- ao->redirect = "coreaudio_exclusive";
- return CONTROL_ERROR;
- }
-
+static bool reinit_device(struct ao *ao) {
struct priv *p = ao->priv;
OSStatus err = ca_select_device(ao, ao->device, &p->device);
@@ -161,6 +136,23 @@ static int init(struct ao *ao)
CHECK_CA_ERROR("failed to get device UID");
ao->detected_device = talloc_steal(ao, uid);
+ return true;
+
+coreaudio_error:
+ return false;
+}
+
+static int init(struct ao *ao)
+{
+ if (AF_FORMAT_IS_IEC61937(ao->format)) {
+ MP_WARN(ao, "detected IEC61937, redirecting to coreaudio_exclusive\n");
+ ao->redirect = "coreaudio_exclusive";
+ return CONTROL_ERROR;
+ }
+
+ if (!reinit_device(ao))
+ goto coreaudio_error;
+
if (!init_chmap(ao))
goto coreaudio_error;
@@ -361,6 +353,67 @@ static void uninit(struct ao *ao)
AudioComponentInstanceDispose(p->audio_unit);
}
+static OSStatus hotplug_cb(AudioObjectID id, UInt32 naddr,
+ const AudioObjectPropertyAddress addr[],
+ void *ctx) {
+ reinit_device(ctx);
+ ao_hotplug_event(ctx);
+ return noErr;
+}
+
+static uint32_t hotplug_properties[] = {
+ kAudioHardwarePropertyDevices,
+ kAudioHardwarePropertyDefaultOutputDevice
+};
+
+static int hotplug_init(struct ao *ao)
+{
+ if (!reinit_device(ao))
+ goto coreaudio_error;
+
+ OSStatus err = noErr;
+ for (int i = 0; i < MP_ARRAY_SIZE(hotplug_properties); i++) {
+ AudioObjectPropertyAddress addr = {
+ hotplug_properties[i],
+ kAudioObjectPropertyScopeGlobal,
+ kAudioObjectPropertyElementMaster
+ };
+ err = AudioObjectAddPropertyListener(
+ kAudioObjectSystemObject, &addr, hotplug_cb, (void *)ao);
+ if (err != noErr) {
+ char *c1 = fourcc_repr(ao, hotplug_properties[i]);
+ char *c2 = fourcc_repr(ao, err);
+ MP_ERR(ao, "failed to set device listener %s (%s)", c1, c2);
+ goto coreaudio_error;
+ }
+ }
+
+ return 0;
+
+coreaudio_error:
+ return -1;
+}
+
+static void hotplug_uninit(struct ao *ao)
+{
+ OSStatus err = noErr;
+ for (int i = 0; i < MP_ARRAY_SIZE(hotplug_properties); i++) {
+ AudioObjectPropertyAddress addr = {
+ hotplug_properties[i],
+ kAudioObjectPropertyScopeGlobal,
+ kAudioObjectPropertyElementMaster
+ };
+ err = AudioObjectRemovePropertyListener(
+ kAudioObjectSystemObject, &addr, hotplug_cb, (void *)ao);
+ if (err != noErr) {
+ char *c1 = fourcc_repr(ao, hotplug_properties[i]);
+ char *c2 = fourcc_repr(ao, err);
+ MP_ERR(ao, "failed to set device listener %s (%s)", c1, c2);
+ }
+ }
+}
+
+
// Channel Mapping functions
static const int speaker_map[][2] = {
{ kAudioChannelLabel_Left, MP_SPEAKER_ID_FL },
@@ -513,13 +566,15 @@ coreaudio_error:
#define OPT_BASE_STRUCT struct priv
const struct ao_driver audio_out_coreaudio = {
- .description = "CoreAudio AudioUnit",
- .name = "coreaudio",
- .uninit = uninit,
- .init = init,
- .control = control,
- .pause = stop,
- .resume = start,
- .list_devs = ca_get_device_list,
- .priv_size = sizeof(struct priv),
+ .description = "CoreAudio AudioUnit",
+ .name = "coreaudio",
+ .uninit = uninit,
+ .init = init,
+ .control = control,
+ .pause = stop,
+ .resume = start,
+ .hotplug_init = hotplug_init,
+ .hotplug_uninit = hotplug_uninit,
+ .list_devs = ca_get_device_list,
+ .priv_size = sizeof(struct priv),
};
diff --git a/audio/out/ao_coreaudio_exclusive.c b/audio/out/ao_coreaudio_exclusive.c
index af77409..cc8c33e 100644
--- a/audio/out/ao_coreaudio_exclusive.c
+++ b/audio/out/ao_coreaudio_exclusive.c
@@ -11,21 +11,20 @@
* module from VideoLAN:
* Copyright (c) 2006 Derk-Jan Hartman <hartman at videolan dot org>
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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
- * along with MPlayer; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
/*
@@ -35,21 +34,18 @@
* when you are wanting to do good buffering of audio).
*/
+#include <CoreAudio/HostTime.h>
+
#include "config.h"
#include "ao.h"
#include "internal.h"
#include "audio/format.h"
#include "osdep/timer.h"
#include "options/m_option.h"
-#include "misc/ring.h"
#include "common/msg.h"
#include "audio/out/ao_coreaudio_properties.h"
#include "audio/out/ao_coreaudio_utils.h"
-static void audio_pause(struct ao *ao);
-static void audio_resume(struct ao *ao);
-static void reset(struct ao *ao);
-
static bool ca_format_is_digital(AudioStreamBasicDescription asbd)
{
switch (asbd.mFormatID)
@@ -310,8 +306,6 @@ struct priv {
bool paused;
- struct mp_ring *buffer;
-
// digital render callback
AudioDeviceIOProcID render_cb;
@@ -332,14 +326,10 @@ struct priv {
bool changed_mixing;
int stream_asbd_changed;
- bool muted;
-};
+ bool reload_requested;
-static int get_ring_size(struct ao *ao)
-{
- return af_fmt_seconds_to_bytes(
- ao->format, 0.5, ao->channels.num, ao->samplerate);
-}
+ uint32_t hw_latency_us;
+};
static OSStatus render_cb_digital(
AudioDeviceID device, const AudioTimeStamp *ts,
@@ -351,45 +341,31 @@ static OSStatus render_cb_digital(
AudioBuffer buf = out_data->mBuffers[p->stream_idx];
int requested = buf.mDataByteSize;
- if (p->muted)
- mp_ring_drain(p->buffer, requested);
- else
- mp_ring_read(p->buffer, buf.mData, requested);
+ int pseudo_frames = requested / ao->sstride;
- return noErr;
-}
+ // we expect the callback to read full frames, which are aligned accordingly
+ if (pseudo_frames * ao->sstride != requested) {
+ MP_ERR(ao, "Unsupported unaligned read of %d bytes.\n", requested);
+ return kAudioHardwareUnspecifiedError;
+ }
-static int control(struct ao *ao, enum aocontrol cmd, void *arg)
-{
- struct priv *p = ao->priv;
- ao_control_vol_t *control_vol;
- switch (cmd) {
- case AOCONTROL_GET_VOLUME:
- control_vol = (ao_control_vol_t *)arg;
- // Digital output has no volume adjust.
- int digitalvol = p->muted ? 0 : 100;
- *control_vol = (ao_control_vol_t) {
- .left = digitalvol, .right = digitalvol,
- };
- return CONTROL_TRUE;
-
- case AOCONTROL_SET_VOLUME:
- control_vol = (ao_control_vol_t *)arg;
- // Digital output can not set volume. Here we have to return true
- // to make mixer forget it. Else mixer will add a soft filter,
- // that's not we expected and the filter not support ac3 stream
- // will cause mplayer die.
-
- // Although not support set volume, but at least we support mute.
- // MPlayer set mute by set volume to zero, we handle it.
- if (control_vol->left == 0 && control_vol->right == 0)
- p->muted = true;
- else
- p->muted = false;
- return CONTROL_TRUE;
-
- } // end switch
- return CONTROL_UNKNOWN;
+ int64_t end = mp_time_us();
+ end += p->hw_latency_us + ca_get_latency(ts)
+ + ca_frames_to_us(ao, pseudo_frames);
+
+ ao_read_data(ao, &buf.mData, pseudo_frames, end);
+
+ // Check whether we need to reset the digital output stream.
+ if (p->stream_asbd_changed) {
+ p->stream_asbd_changed = 0;
+ if (!p->reload_requested && ca_stream_supports_digital(ao, p->stream)) {
+ p->reload_requested = true;
+ ao_request_reload(ao);
+ MP_INFO(ao, "Stream format changed! Reloading.\n");
+ }
+ }
+
+ return noErr;
}
static int init_digital(struct ao *ao, AudioStreamBasicDescription asbd);
@@ -524,17 +500,22 @@ static int init_digital(struct ao *ao, AudioStreamBasicDescription asbd)
(p->stream_asbd.mBytesPerPacket /
p->stream_asbd.mFramesPerPacket);
- p->buffer = mp_ring_new(p, get_ring_size(ao));
+ uint32_t latency_frames = 0;
+ err = CA_GET_O(p->device, kAudioDevicePropertyLatency, &latency_frames);
+ if (err != noErr) {
+ CHECK_CA_WARN("cannot get device latency");
+ latency_frames = 0;
+ }
+
+ p->hw_latency_us = ca_frames_to_us(ao, latency_frames);
+ MP_VERBOSE(ao, "base latency: %d microseconds\n", (int)p->hw_latency_us);
err = AudioDeviceCreateIOProcID(p->device,
(AudioDeviceIOProc)render_cb_digital,
(void *)ao,
&p->render_cb);
-
CHECK_CA_ERROR("failed to register digital render callback");
- reset(ao);
-
return CONTROL_TRUE;
coreaudio_error:
@@ -543,52 +524,6 @@ coreaudio_error:
return CONTROL_ERROR;
}
-static int play(struct ao *ao, void **data, int samples, int flags)
-{
- struct priv *p = ao->priv;
- void *output_samples = data[0];
- int num_bytes = samples * ao->sstride;
-
- // Check whether we need to reset the digital output stream.
- if (p->stream_asbd_changed) {
- p->stream_asbd_changed = 0;
- if (ca_stream_supports_digital(ao, p->stream)) {
- if (!ca_change_format(ao, p->stream, p->stream_asbd)) {
- MP_WARN(ao , "can't restore digital output\n");
- } else {
- MP_WARN(ao, "restoring digital output succeeded.\n");
- reset(ao);
- }
- }
- }
-
- int wrote = mp_ring_write(p->buffer, output_samples, num_bytes);
- audio_resume(ao);
-
- return wrote / ao->sstride;
-}
-
-static void reset(struct ao *ao)
-{
- struct priv *p = ao->priv;
- audio_pause(ao);
- mp_ring_reset(p->buffer);
-}
-
-static int get_space(struct ao *ao)
-{
- struct priv *p = ao->priv;
- return mp_ring_available(p->buffer) / ao->sstride;
-}
-
-static double get_delay(struct ao *ao)
-{
- // FIXME: should also report the delay of coreaudio itself (hardware +
- // internal buffers)
- struct priv *p = ao->priv;
- return mp_ring_buffered(p->buffer) / (double)ao->bps;
-}
-
static void uninit(struct ao *ao)
{
struct priv *p = ao->priv;
@@ -618,26 +553,16 @@ static void audio_pause(struct ao *ao)
{
struct priv *p = ao->priv;
- if (p->paused)
- return;
-
OSStatus err = AudioDeviceStop(p->device, p->render_cb);
CHECK_CA_WARN("can't stop digital device");
-
- p->paused = true;
}
static void audio_resume(struct ao *ao)
{
struct priv *p = ao->priv;
- if (!p->paused)
- return;
-
OSStatus err = AudioDeviceStart(p->device, p->render_cb);
CHECK_CA_WARN("can't start digital device");
-
- p->paused = false;
}
#define OPT_BASE_STRUCT struct priv
@@ -647,17 +572,11 @@ const struct ao_driver audio_out_coreaudio_exclusive = {
.name = "coreaudio_exclusive",
.uninit = uninit,
.init = init,
- .play = play,
- .control = control,
- .get_space = get_space,
- .get_delay = get_delay,
- .reset = reset,
.pause = audio_pause,
.resume = audio_resume,
.list_devs = ca_get_device_list,
.priv_size = sizeof(struct priv),
.priv_defaults = &(const struct priv){
- .muted = false,
.stream_asbd_changed = 0,
.hog_pid = -1,
.stream = 0,
diff --git a/audio/out/ao_coreaudio_utils.c b/audio/out/ao_coreaudio_utils.c
index 90ebdbc..d606587 100644
--- a/audio/out/ao_coreaudio_utils.c
+++ b/audio/out/ao_coreaudio_utils.c
@@ -22,6 +22,8 @@
* on CoreAudio but not the AUHAL (such as using AudioQueue services).
*/
+#include <CoreAudio/HostTime.h>
+
#include "audio/out/ao_coreaudio_utils.h"
#include "audio/out/ao_coreaudio_properties.h"
#include "osdep/timer.h"
@@ -115,12 +117,12 @@ OSStatus ca_select_device(struct ao *ao, char* name, AudioDeviceID *device)
if (mp_msg_test(ao->log, MSGL_V)) {
char *desc;
err = CA_GET_STR(*device, kAudioObjectPropertyName, &desc);
- CHECK_CA_ERROR("could not get selected audio device name");
-
- MP_VERBOSE(ao, "selected audio output device: %s (%" PRIu32 ")\n",
- desc, *device);
-
- talloc_free(desc);
+ CHECK_CA_WARN("could not get selected audio device name");
+ if (err == noErr) {
+ MP_VERBOSE(ao, "selected audio output device: %s (%" PRIu32 ")\n",
+ desc, *device);
+ talloc_free(desc);
+ }
}
coreaudio_error:
@@ -139,16 +141,17 @@ char *fourcc_repr(void *talloc_ctx, uint32_t code)
};
bool valid_fourcc = true;
- for (int i = 0; i < 4; i++)
- if (fcc[i] >= 32 && fcc[i] < 128)
+ for (int i = 0; i < 4; i++) {
+ if (fcc[i] < 32 || fcc[i] >= 128)
valid_fourcc = false;
+ }
char *repr;
if (valid_fourcc)
repr = talloc_asprintf(talloc_ctx, "'%c%c%c%c'",
fcc[0], fcc[1], fcc[2], fcc[3]);
else
- repr = talloc_asprintf(NULL, "%d", code);
+ repr = talloc_asprintf(NULL, "%u", (unsigned int)code);
return repr;
}
@@ -214,3 +217,18 @@ void ca_print_asbd(struct ao *ao, const char *description,
talloc_free(format);
}
+int64_t ca_frames_to_us(struct ao *ao, uint32_t frames)
+{
+ return frames / (float) ao->samplerate * 1e6;
+}
+
+int64_t ca_get_latency(const AudioTimeStamp *ts)
+{
+ uint64_t out = AudioConvertHostTimeToNanos(ts->mHostTime);
+ uint64_t now = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime());
+
+ if (now > out)
+ return 0;
+
+ return (out - now) * 1e-3;
+}
diff --git a/audio/out/ao_coreaudio_utils.h b/audio/out/ao_coreaudio_utils.h
index 17c60b9..bd233e4 100644
--- a/audio/out/ao_coreaudio_utils.h
+++ b/audio/out/ao_coreaudio_utils.h
@@ -56,4 +56,7 @@ void ca_fill_asbd(struct ao *ao, AudioStreamBasicDescription *asbd);
void ca_print_asbd(struct ao *ao, const char *description,
const AudioStreamBasicDescription *asbd);
+int64_t ca_frames_to_us(struct ao *ao, uint32_t frames);
+int64_t ca_get_latency(const AudioTimeStamp *ts);
+
#endif /* MPV_COREAUDIO_UTILS_H */
diff --git a/audio/out/ao_dsound.c b/audio/out/ao_dsound.c
index 825e309..9d216e6 100644
--- a/audio/out/ao_dsound.c
+++ b/audio/out/ao_dsound.c
@@ -3,21 +3,20 @@
*
* Copyright (c) 2004 Gabor Szecsi <deje@miki.hu>
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
/**
diff --git a/audio/out/ao_jack.c b/audio/out/ao_jack.c
index 727a5f5..04e5c71 100644
--- a/audio/out/ao_jack.c
+++ b/audio/out/ao_jack.c
@@ -6,21 +6,20 @@
*
* Copyleft 2013 by William Light <wrl@illest.net> for the mpv project
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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
- * along with MPlayer; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
diff --git a/audio/out/ao_lavc.c b/audio/out/ao_lavc.c
index 2405b22..b322982 100644
--- a/audio/out/ao_lavc.c
+++ b/audio/out/ao_lavc.c
@@ -5,19 +5,18 @@
*
* This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
@@ -242,7 +241,7 @@ static int encode(struct ao *ao, double apts, void **data)
frame->format = af_to_avformat(ao->format);
frame->nb_samples = ac->aframesize;
- size_t num_planes = af_fmt_is_planar(ao->format) ? ao->channels.num : 1;
+ size_t num_planes = AF_FORMAT_IS_PLANAR(ao->format) ? ao->channels.num : 1;
assert(num_planes <= AV_NUM_DATA_POINTERS);
for (int n = 0; n < num_planes; n++)
frame->extended_data[n] = data[n];
@@ -351,7 +350,7 @@ static int play(struct ao *ao, void **data, int samples, int flags)
double pts = ectx->last_audio_in_pts;
pts += ectx->samples_since_last_pts / (double)ao->samplerate;
- size_t num_planes = af_fmt_is_planar(ao->format) ? ao->channels.num : 1;
+ size_t num_planes = AF_FORMAT_IS_PLANAR(ao->format) ? ao->channels.num : 1;
void *tempdata = NULL;
void *padded[MP_NUM_CHANNELS];
diff --git a/audio/out/ao_null.c b/audio/out/ao_null.c
index 6fcd06b..0b5ff34 100644
--- a/audio/out/ao_null.c
+++ b/audio/out/ao_null.c
@@ -1,21 +1,20 @@
/*
* null audio output driver
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
/*
diff --git a/audio/out/ao_openal.c b/audio/out/ao_openal.c
index b864bb8..9207a84 100644
--- a/audio/out/ao_openal.c
+++ b/audio/out/ao_openal.c
@@ -3,21 +3,20 @@
*
* Copyleft 2006 by Reimar Döffinger (Reimar.Doeffinger@stud.uni-karlsruhe.de)
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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
- * along with MPlayer; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
diff --git a/audio/out/ao_oss.c b/audio/out/ao_oss.c
index 3b8c910..bf320e9 100644
--- a/audio/out/ao_oss.c
+++ b/audio/out/ao_oss.c
@@ -1,25 +1,24 @@
/*
* OSS audio output driver
*
- * This file is part of MPlayer.
- *
* Original author: A'rpi
* Support for >2 output channels added 2001-11-25
* - Steve Davies <steve@daviesfam.org>
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
diff --git a/audio/out/ao_pcm.c b/audio/out/ao_pcm.c
index c077c8e..5b9d61b 100644
--- a/audio/out/ao_pcm.c
+++ b/audio/out/ao_pcm.c
@@ -1,23 +1,22 @@
/*
* PCM audio output driver
*
- * This file is part of MPlayer.
- *
* Original author: Atmosfear
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
diff --git a/audio/out/ao_pulse.c b/audio/out/ao_pulse.c
index d6e08de..f797802 100644
--- a/audio/out/ao_pulse.c
+++ b/audio/out/ao_pulse.c
@@ -3,21 +3,20 @@
* Copyright (C) 2006 Lennart Poettering
* Copyright (C) 2007 Reimar Doeffinger
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
@@ -81,6 +80,27 @@ static void context_state_cb(pa_context *c, void *userdata)
}
}
+static void subscribe_cb(pa_context *c, pa_subscription_event_type_t t,
+ uint32_t idx, void *userdata)
+{
+ struct ao *ao = userdata;
+ int type = t & PA_SUBSCRIPTION_MASK_SINK;
+ int fac = t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK;
+ if ((type == PA_SUBSCRIPTION_EVENT_NEW || type == PA_SUBSCRIPTION_EVENT_REMOVE)
+ && fac == PA_SUBSCRIPTION_EVENT_SINK)
+ {
+ ao_hotplug_event(ao);
+ }
+}
+
+static void context_success_cb(pa_context *c, int success, void *userdata)
+{
+ struct ao *ao = userdata;
+ struct priv *priv = ao->priv;
+ priv->retval = success;
+ pa_threaded_mainloop_signal(priv->mainloop, 0);
+}
+
static void stream_state_cb(pa_stream *s, void *userdata)
{
struct ao *ao = userdata;
@@ -323,6 +343,7 @@ static int pa_init_boilerplate(struct ao *ao)
(long)pa_context_get_server_protocol_version(priv->context));
pa_context_set_state_callback(priv->context, context_state_cb, ao);
+ pa_context_set_subscribe_callback(priv->context, subscribe_cb, ao);
if (pa_context_connect(priv->context, host, 0, NULL) < 0)
goto fail;
@@ -397,7 +418,6 @@ static int init(struct ao *ao)
MP_ERR(ao, "Failed to allocate proplist\n");
goto unlock_and_fail;
}
- (void)pa_proplist_sets(proplist, PA_PROP_MEDIA_ROLE, "video");
(void)pa_proplist_sets(proplist, PA_PROP_MEDIA_ICON_NAME, ao->client_name);
pa_format_info_set_rate(format, ao->samplerate);
@@ -751,22 +771,32 @@ static void sink_info_cb(pa_context *c, const pa_sink_info *i, int eol, void *ud
ao_device_list_add(ctx->list, ctx->ao, &entry);
}
+static int hotplug_init(struct ao *ao)
+{
+ struct priv *priv = ao->priv;
+ if (pa_init_boilerplate(ao) < 0)
+ return -1;
+
+ pa_threaded_mainloop_lock(priv->mainloop);
+ waitop(priv, pa_context_subscribe(priv->context, PA_SUBSCRIPTION_MASK_SINK,
+ context_success_cb, ao));
+
+ return 0;
+}
+
static void list_devs(struct ao *ao, struct ao_device_list *list)
{
struct priv *priv = ao->priv;
- bool need_uninit = !priv->mainloop;
struct sink_cb_ctx ctx = {ao, list};
- if (need_uninit && pa_init_boilerplate(ao) < 0)
- return;
-
pa_threaded_mainloop_lock(priv->mainloop);
waitop(priv, pa_context_get_sink_info_list(priv->context, sink_info_cb, &ctx));
-
- if (need_uninit)
- uninit(ao);
}
+static void hotplug_uninit(struct ao *ao)
+{
+ uninit(ao);
+}
#define OPT_BASE_STRUCT struct priv
@@ -785,6 +815,8 @@ const struct ao_driver audio_out_pulse = {
.drain = drain,
.wait = wait_audio,
.wakeup = wakeup,
+ .hotplug_init = hotplug_init,
+ .hotplug_uninit = hotplug_uninit,
.list_devs = list_devs,
.priv_size = sizeof(struct priv),
.priv_defaults = &(const struct priv) {
diff --git a/audio/out/ao_rsound.c b/audio/out/ao_rsound.c
index 26a0e21..ef1a001 100644
--- a/audio/out/ao_rsound.c
+++ b/audio/out/ao_rsound.c
@@ -3,21 +3,20 @@
*
* Copyright (C) 2011 Hans-Kristian Arntzen
*
- * This file is part of mplayer2.
+ * This file is part of mpv.
*
- * mplayer2 is free software; you can redistribute it and/or modify
+ * 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.
*
- * mplayer2 is distributed in the hope that it will be useful,
+ * 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 mplayer2; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
diff --git a/audio/out/ao_sdl.c b/audio/out/ao_sdl.c
index 91554d7..c1ecff8 100644
--- a/audio/out/ao_sdl.c
+++ b/audio/out/ao_sdl.c
@@ -4,19 +4,18 @@
*
* This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
diff --git a/audio/out/ao_sndio.c b/audio/out/ao_sndio.c
index 2daa63f..80add47 100644
--- a/audio/out/ao_sndio.c
+++ b/audio/out/ao_sndio.c
@@ -125,7 +125,7 @@ static int init(struct ao *ao)
const struct af_to_par *ap;
int i;
- p->hdl = sio_open(p->dev, SIO_PLAY, 1);
+ p->hdl = sio_open(p->dev, SIO_PLAY, 0);
if (p->hdl == NULL) {
MP_ERR(ao, "can't open sndio %s\n", p->dev);
goto error;
@@ -304,25 +304,7 @@ static void audio_pause(struct ao *ao)
*/
static void audio_resume(struct ao *ao)
{
- struct priv *p = ao->priv;
- int n, count, todo;
-
- /*
- * we want to start with buffers full, because mpv uses
- * get_delay() as clock, which would cause video to
- * accelerate while buffers are filled.
- */
- todo = p->par.bufsz * p->par.pchan * p->par.bps;
- while (todo > 0) {
- count = todo;
- if (count > SILENCE_NMAX)
- count = SILENCE_NMAX;
- n = sio_write(p->hdl, p->silence, count);
- if (n == 0)
- break;
- todo -= n;
- p->delay += n;
- }
+ return;
}
#define OPT_BASE_STRUCT struct priv
diff --git a/audio/out/ao_wasapi.c b/audio/out/ao_wasapi.c
index 75318c2..3c0bab3 100644
--- a/audio/out/ao_wasapi.c
+++ b/audio/out/ao_wasapi.c
@@ -63,14 +63,13 @@ static HRESULT get_device_delay(struct wasapi_state *state, double *delay) {
return S_OK;
exit_label:
- MP_ERR(state, "Error getting device delay: %s (0x%"PRIx32")\n",
- wasapi_explain_err(hr), (uint32_t) hr);
+ MP_ERR(state, "Error getting device delay: %s\n", mp_HRESULT_to_str(hr));
return hr;
}
static void thread_feed(struct ao *ao)
{
- struct wasapi_state *state = (struct wasapi_state *)ao->priv;
+ struct wasapi_state *state = ao->priv;
HRESULT hr;
UINT32 frame_count = state->bufferFrameCount;
@@ -106,8 +105,7 @@ static void thread_feed(struct ao *ao)
return;
exit_label:
- MP_ERR(state, "Error feeding audio: %s (0x%"PRIx32")\n",
- wasapi_explain_err(hr), (uint32_t) hr);
+ 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;
@@ -115,93 +113,97 @@ exit_label:
static void thread_resume(struct ao *ao)
{
- struct wasapi_state *state = (struct wasapi_state *)ao->priv;
+ 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 (0x%"PRIx32")\n",
- wasapi_explain_err(hr), (uint32_t) hr);
+ 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)
+ 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 (0x%"PRIx32")\n",
- wasapi_explain_err(hr), (uint32_t) hr);
- }
+ if (hr != S_OK)
+ MP_ERR(state, "IAudioClient_Start returned %s\n", mp_HRESULT_to_str(hr));
return;
}
static void thread_reset(struct ao *ao)
{
- struct wasapi_state *state = (struct wasapi_state *)ao->priv;
+ struct wasapi_state *state = ao->priv;
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) {
- MP_ERR(state, "IAudioClient_Stop returned: %s (0x%"PRIx32")\n",
- wasapi_explain_err(hr), (uint32_t) hr);
- }
+ if (hr != S_OK && hr != S_FALSE)
+ 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) {
- MP_ERR(state, "IAudioClient_Reset returned: %s (0x%"PRIx32")\n",
- wasapi_explain_err(hr), (uint32_t) hr);
- }
+ if (hr != S_OK && hr != S_FALSE)
+ 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 DWORD __stdcall ThreadLoop(void *lpParameter)
+static DWORD __stdcall AudioThread(void *lpParameter)
{
struct ao *ao = lpParameter;
- struct wasapi_state *state = (struct wasapi_state *)ao->priv;
+ struct wasapi_state *state = ao->priv;
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
state->init_ret = wasapi_thread_init(ao);
- SetEvent(state->init_done);
+ SetEvent(state->hInitDone);
if (state->init_ret != S_OK)
goto exit_label;
- DWORD waitstatus;
- HANDLE playcontrol[] =
- {state->hUninit, state->hFeed, state->hReset, state->hResume, NULL};
MP_DBG(ao, "Entering dispatch loop\n");
while (true) { /* watch events */
- waitstatus = MsgWaitForMultipleObjects(4, playcontrol, FALSE, INFINITE,
- QS_POSTMESSAGE | QS_SENDMESSAGE);
- switch (waitstatus) {
- case WAIT_OBJECT_0: /*shutdown*/
- MP_DBG(ao, "Thread shutdown\n");
- goto exit_label;
- case (WAIT_OBJECT_0 + 1): /* feed */
- thread_feed(ao);
- break;
- case (WAIT_OBJECT_0 + 2): /* reset */
- thread_reset(ao);
- break;
- case (WAIT_OBJECT_0 + 3): /* resume */
- thread_resume(ao);
+ 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;
+ }
break;
- case (WAIT_OBJECT_0 + 4): /* messages to dispatch (COM marshalling) */
- MP_DBG(ao, "Dispatch\n");
- wasapi_dispatch();
+ /* messages to dispatch (COM marshalling) */
+ case (WAIT_OBJECT_0 + MP_ARRAY_SIZE(events)):
+ wasapi_dispatch(ao);
break;
default:
- MP_ERR(ao, "Unhandled case in thread loop\n");
+ MP_ERR(ao, "Unhandled thread event\n");
goto exit_label;
}
}
@@ -213,32 +215,35 @@ exit_label:
return 0;
}
-static void closehandles(struct ao *ao)
+static void set_thread_state(struct ao *ao, enum wasapi_thread_state thread_state)
{
- struct wasapi_state *state = (struct wasapi_state *)ao->priv;
- if (state->init_done) CloseHandle(state->init_done);
- if (state->hUninit) CloseHandle(state->hUninit);
- if (state->hFeed) CloseHandle(state->hFeed);
- if (state->hResume) CloseHandle(state->hResume);
- if (state->hReset) CloseHandle(state->hReset);
- if (state->threadLoop) CloseHandle(state->threadLoop);
+ 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 = (struct wasapi_state *)ao->priv;
+ struct wasapi_state *state = ao->priv;
wasapi_release_proxies(state);
- if (state->hUninit)
- SetEvent(state->hUninit);
+ if (state->hWake)
+ set_thread_state(ao, WASAPI_THREAD_SHUTDOWN);
+
/* wait up to 10 seconds */
- if (WaitForSingleObject(state->threadLoop, 10000) == WAIT_TIMEOUT) {
+ if (state->hAudioThread &&
+ WaitForSingleObject(state->hAudioThread, 10000) == WAIT_TIMEOUT)
+ {
MP_ERR(ao, "Audio loop thread refuses to abort\n");
return;
}
if (state->VistaBlob.hAvrt)
FreeLibrary(state->VistaBlob.hAvrt);
- closehandles(ao);
+
+ SAFE_RELEASE(state->hInitDone, CloseHandle(state->hInitDone));
+ SAFE_RELEASE(state->hWake, CloseHandle(state->hWake));
+ SAFE_RELEASE(state->hAudioThread,CloseHandle(state->hAudioThread));
+
CoUninitialize();
MP_DBG(ao, "Uninit wasapi done\n");
}
@@ -248,53 +253,29 @@ static int init(struct ao *ao)
MP_DBG(ao, "Init wasapi\n");
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
- ao->format = af_fmt_from_planar(ao->format);
- struct mp_chmap_sel sel = {0};
- mp_chmap_sel_add_waveext(&sel);
- if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels)) {
- MP_ERR(ao, "Error adjusting channel map to waveext channel order\n");
- return -1;
- }
- struct wasapi_state *state = (struct wasapi_state *)ao->priv;
+ struct wasapi_state *state = ao->priv;
state->log = ao->log;
if(!wasapi_fill_VistaBlob(state))
MP_WARN(ao, "Error loading thread priority functions\n");
- if (state->opt_list) {
- if(!wasapi_enumerate_devices(state->log, NULL, NULL))
- MP_WARN(ao, "Error enumerating devices\n");
- }
-
- if (state->opt_exclusive) {
- state->share_mode = AUDCLNT_SHAREMODE_EXCLUSIVE;
- } else {
- state->share_mode = AUDCLNT_SHAREMODE_SHARED;
- }
-
- state->init_done = CreateEventW(NULL, FALSE, FALSE, NULL);
- state->hUninit = CreateEventW(NULL, FALSE, FALSE, NULL);
- state->hFeed = CreateEventW(NULL, FALSE, FALSE, NULL); /* for wasapi event mode */
- state->hResume = CreateEventW(NULL, FALSE, FALSE, NULL);
- state->hReset = CreateEventW(NULL, FALSE, FALSE, NULL);
- if (!state->init_done || !state->hFeed || !state->hUninit ||
- !state->hResume || !state->hReset)
- {
- MP_ERR(ao, "Error initing events\n");
+ 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");
uninit(ao);
- /* failed to init events */
return -1;
}
state->init_ret = E_FAIL;
- state->threadLoop = (HANDLE) CreateThread(NULL, 0, &ThreadLoop, ao, 0, NULL);
- if (!state->threadLoop) {
- /* failed to init thread */
- MP_ERR(ao, "Failed to create thread\n");
+ state->hAudioThread = CreateThread(NULL, 0, &AudioThread, ao, 0, NULL);
+ if (!state->hAudioThread) {
+ MP_ERR(ao, "Failed to create audio thread\n");
uninit(ao);
return -1;
}
- WaitForSingleObject(state->init_done, 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 (!ao->probing)
MP_ERR(ao, "Received failure from audio thread\n");
@@ -309,8 +290,8 @@ static int init(struct ao *ao)
static int control(struct ao *ao, enum aocontrol cmd, void *arg)
{
- struct wasapi_state *state = (struct wasapi_state *)ao->priv;
- ao_control_vol_t *vol = (ao_control_vol_t *)arg;
+ struct wasapi_state *state = ao->priv;
+ ao_control_vol_t *vol = arg;
BOOL mute;
switch (cmd) {
@@ -388,40 +369,58 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg)
static void audio_reset(struct ao *ao)
{
- struct wasapi_state *state = (struct wasapi_state *)ao->priv;
- SetEvent(state->hReset);
+ set_thread_state(ao, WASAPI_THREAD_RESET);
}
static void audio_resume(struct ao *ao)
{
- struct wasapi_state *state = (struct wasapi_state *)ao->priv;
- SetEvent(state->hResume);
+ set_thread_state(ao, WASAPI_THREAD_RESUME);
}
-static void list_devs(struct ao *ao, struct ao_device_list *list)
+static void hotplug_uninit(struct ao *ao)
{
- CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
- if(!wasapi_enumerate_devices(mp_null_log, ao, list))
- MP_WARN(ao, "Error enumerating devices\n");
+ 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();
}
+static int hotplug_init(struct ao *ao)
+{
+ MP_DBG(ao, "Hotplug init\n");
+ struct wasapi_state *state = ao->priv;
+ 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);
+ EXIT_ON_ERROR(hr);
+
+ return 0;
+ exit_label:
+ MP_ERR(state, "Error setting up audio hotplug: %s\n", mp_HRESULT_to_str(hr));
+ hotplug_uninit(ao);
+ return -1;
+}
+
#define OPT_BASE_STRUCT struct wasapi_state
const struct ao_driver audio_out_wasapi = {
- .description = "Windows WASAPI audio output (event mode)",
- .name = "wasapi",
- .init = init,
- .uninit = uninit,
- .control = control,
- .reset = audio_reset,
- .resume = audio_resume,
- .list_devs = list_devs,
- .priv_size = sizeof(wasapi_state),
- .options = (const struct m_option[]) {
+ .description = "Windows WASAPI audio output (event mode)",
+ .name = "wasapi",
+ .init = init,
+ .uninit = uninit,
+ .control = control,
+ .reset = audio_reset,
+ .resume = audio_resume,
+ .list_devs = wasapi_list_devs,
+ .hotplug_init = hotplug_init,
+ .hotplug_uninit = hotplug_uninit,
+ .priv_size = sizeof(wasapi_state),
+ .options = (const struct m_option[]) {
OPT_FLAG("exclusive", opt_exclusive, 0),
- OPT_FLAG("list", opt_list, 0),
- OPT_STRING_VALIDATE("device", opt_device, 0, wasapi_validate_device),
+ OPT_STRING("device", opt_device, 0),
{NULL},
},
};
diff --git a/audio/out/ao_wasapi.h b/audio/out/ao_wasapi.h
index 2fd266e..24b9b86 100755
--- a/audio/out/ao_wasapi.h
+++ b/audio/out/ao_wasapi.h
@@ -20,6 +20,7 @@
#ifndef MP_AO_WASAPI_H_
#define MP_AO_WASAPI_H_
+#include <stdbool.h>
#include <audioclient.h>
#include <audiopolicy.h>
#include <mmdeviceapi.h>
@@ -30,10 +31,11 @@
typedef struct change_notify {
IMMNotificationClient client; /* this must be first in the structure! */
LPWSTR monitored; /* Monitored device */
+ bool is_hotplug;
struct ao *ao;
} change_notify;
-HRESULT wasapi_change_init(struct ao* ao);
+HRESULT wasapi_change_init(struct ao* ao, bool is_hotplug);
void wasapi_change_uninit(struct ao* ao);
#define EXIT_ON_ERROR(hres) \
@@ -41,28 +43,27 @@ void wasapi_change_uninit(struct ao* ao);
#define SAFE_RELEASE(unk, release) \
do { if ((unk) != NULL) { release; (unk) = NULL; } } while(0)
+enum wasapi_thread_state {
+ WASAPI_THREAD_FEED = 0,
+ WASAPI_THREAD_RESUME,
+ WASAPI_THREAD_RESET,
+ WASAPI_THREAD_SHUTDOWN
+};
+
typedef struct wasapi_state {
struct mp_log *log;
- HANDLE threadLoop;
-
/* Init phase */
HRESULT init_ret;
- HANDLE init_done;
+ HANDLE hInitDone;
int share_mode;
- HANDLE hUninit;
-
/* volume control */
- DWORD vol_hw_support, status;
+ DWORD vol_hw_support;
float audio_volume;
float previous_volume;
float initial_volume;
- /* Buffers */
- 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 */
-
- /* WASAPI handles, owned by other thread */
+ /* WASAPI handles, owned by audio thread */
IMMDevice *pDevice;
IAudioClient *pAudioClient;
IAudioRenderClient *pRenderClient;
@@ -71,15 +72,17 @@ typedef struct wasapi_state {
IAudioSessionControl *pSessionControl;
IMMDeviceEnumerator *pEnumerator;
- HANDLE hFeed; /* wasapi event */
- HANDLE hResume; /* signal audio thread to resume the stream */
- HANDLE hReset; /* signal audio thread to reset the stream */
+ /* 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 */
DWORD taskIndex; /* AV task ID */
- WAVEFORMATEXTENSIBLE format;
/* WASAPI proxy handles, for Single-Threaded Apartment communication.
- One is needed for each object that's accessed by a different thread. */
+ One is needed for each audio thread object that's accessed from the main thread. */
ISimpleAudioVolume *pAudioVolumeProxy;
IAudioEndpointVolume *pEndpointVolumeProxy;
IAudioSessionControl *pSessionControlProxy;
@@ -97,10 +100,16 @@ typedef struct wasapi_state {
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 */
+ /* ao options */
int opt_exclusive;
int opt_list;
char *opt_device;
+ /* 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 */
+
/* Don't use these functions directly in case
they are unimplemented for some reason.
(XP shouldn't be an issue since it doesn't support wasapi, maybe wine?)
diff --git a/audio/out/ao_wasapi_changenotify.c b/audio/out/ao_wasapi_changenotify.c
index ea7e2c6..1521578 100755
--- a/audio/out/ao_wasapi_changenotify.c
+++ b/audio/out/ao_wasapi_changenotify.c
@@ -27,32 +27,13 @@
#include "ao_wasapi.h"
#include "ao_wasapi_utils.h"
-static char* ERole_to_str(ERole role)
-{
- switch (role) {
- case eConsole: return "console";
- case eMultimedia: return "multimedia";
- case eCommunications: return "communications";
- default: return "<Unknown>";
- }
-}
-
-static char* EDataFlow_to_str(EDataFlow flow)
-{
- switch (flow) {
- case eRender: return "render";
- case eCapture: return "capture";
- case eAll: return "all";
- default: return "<Unknown>";
- }
-}
-
static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_QueryInterface(
IMMNotificationClient* This, REFIID riid, void **ppvObject)
{
/* Compatible with IMMNotificationClient and IUnknown */
if (IsEqualGUID(&IID_IMMNotificationClient, riid) ||
- IsEqualGUID(&IID_IUnknown, riid)) {
+ IsEqualGUID(&IID_IUnknown, riid))
+ {
*ppvObject = (void *)This;
return S_OK;
} else {
@@ -83,19 +64,23 @@ static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_OnDeviceStateChanged(
change_notify *change = (change_notify *)This;
struct ao *ao = change->ao;
- if (pwstrDeviceId && !wcscmp(change->monitored, pwstrDeviceId)) {
+ if (change->is_hotplug) {
+ MP_VERBOSE(ao, "OnDeviceStateChanged triggered: sending hotplug event\n");
+ ao_hotplug_event(ao);
+ } else if (pwstrDeviceId && !wcscmp(pwstrDeviceId, change->monitored)) {
switch (dwNewState) {
case DEVICE_STATE_DISABLED:
case DEVICE_STATE_NOTPRESENT:
case DEVICE_STATE_UNPLUGGED:
- MP_VERBOSE(ao,
- "OnDeviceStateChange triggered - requesting ao reload\n");
+ MP_VERBOSE(ao, "OnDeviceStateChanged triggered on device %S: "
+ "requesting ao reload\n", pwstrDeviceId);
ao_request_reload(ao);
+ break;
case DEVICE_STATE_ACTIVE:
- default:
- return S_OK;
+ break;
}
}
+
return S_OK;
}
@@ -106,9 +91,11 @@ static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_OnDeviceAdded(
change_notify *change = (change_notify *)This;
struct ao *ao = change->ao;
- MP_VERBOSE(ao, "OnDeviceAdded triggered\n");
- if(pwstrDeviceId)
- MP_VERBOSE(ao, "New device %S\n",pwstrDeviceId);
+ if (change->is_hotplug) {
+ MP_VERBOSE(ao, "OnDeviceAdded triggered: sending hotplug event\n");
+ ao_hotplug_event(ao);
+ }
+
return S_OK;
}
@@ -120,10 +107,15 @@ static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_OnDeviceRemoved(
change_notify *change = (change_notify *)This;
struct ao *ao = change->ao;
- if (pwstrDeviceId && !wcscmp(change->monitored, pwstrDeviceId)) {
- MP_VERBOSE(ao, "OnDeviceRemoved triggered - requesting ao reload\n");
+ if (change->is_hotplug) {
+ MP_VERBOSE(ao, "OnDeviceRemoved triggered: sending hotplug event\n");
+ ao_hotplug_event(ao);
+ } else if (pwstrDeviceId && !wcscmp(pwstrDeviceId, change->monitored)) {
+ MP_VERBOSE(ao, "OnDeviceRemoved triggered for device %S: "
+ "requesting ao reload\n", pwstrDeviceId);
ao_request_reload(ao);
}
+
return S_OK;
}
@@ -135,31 +127,34 @@ static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_OnDefaultDeviceChanged(
{
change_notify *change = (change_notify *)This;
struct ao *ao = change->ao;
- struct wasapi_state *state = (struct wasapi_state *)ao->priv;
-
- MP_VERBOSE(ao, "OnDefaultDeviceChanged triggered for role:%s flow:%s\n",
- ERole_to_str(role), EDataFlow_to_str(flow));
- if(pwstrDeviceId)
- MP_VERBOSE(ao, "New default device %S\n", pwstrDeviceId);
+ struct wasapi_state *state = ao->priv;
/* don't care about "eCapture" or non-"eMultimedia" roles */
if (flow == eCapture || role != eMultimedia) return S_OK;
- /* stay on the device the user specified */
- if (state->opt_device) {
- MP_VERBOSE(ao, "Staying on specified device \"%s\"", state->opt_device);
- return S_OK;
- }
+ if (change->is_hotplug) {
+ 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) {
+ MP_VERBOSE(ao, "OnDefaultDeviceChanged triggered: "
+ "staying on specified device %s\n", state->opt_device);
+ return S_OK;
+ }
- /* don't reload if already on the new default */
- if (pwstrDeviceId && !wcscmp(change->monitored, pwstrDeviceId)) {
- MP_VERBOSE(ao, "Already using default device, no reload required\n");
- return S_OK;
+ /* 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");
+ ao_request_reload(ao);
}
- /* if we got here, we need to reload */
- ao_request_reload(ao);
- MP_VERBOSE(ao, "Requesting ao reload\n");
return S_OK;
}
@@ -171,17 +166,21 @@ static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_OnPropertyValueChanged(
change_notify *change = (change_notify *)This;
struct ao *ao = change->ao;
- if (pwstrDeviceId && !wcscmp(change->monitored, pwstrDeviceId)) {
- MP_VERBOSE(ao, "OnPropertyValueChanged triggered\n");
- MP_VERBOSE(ao, "Changed property: ");
+ if (!change->is_hotplug && pwstrDeviceId &&
+ !wcscmp(pwstrDeviceId, change->monitored))
+ {
+ MP_VERBOSE(ao, "OnPropertyValueChanged triggered on device %S\n",
+ pwstrDeviceId);
if (IsEqualPropertyKey(PKEY_AudioEngine_DeviceFormat, key)) {
MP_VERBOSE(change->ao,
- "PKEY_AudioEngine_DeviceFormat - requesting ao reload\n");
+ "Changed property: PKEY_AudioEngine_DeviceFormat "
+ "- requesting ao reload\n");
ao_request_reload(change->ao);
} else {
- MP_VERBOSE(ao, "%s\n", mp_PKEY_to_str(&key));
+ MP_VERBOSE(ao, "Changed property: %s\n", mp_PKEY_to_str(&key));
}
}
+
return S_OK;
}
@@ -197,43 +196,51 @@ static CONST_VTBL IMMNotificationClientVtbl sIMMDeviceEnumeratorVtbl_vtbl = {
};
-HRESULT wasapi_change_init(struct ao *ao)
+HRESULT wasapi_change_init(struct ao *ao, bool is_hotplug)
{
- MP_DBG(ao, "Setting up monitoring on playback device\n");
- struct wasapi_state *state = (struct wasapi_state *)ao->priv;
+ 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;
- /* so the callbacks can access the ao */
- change->ao = ao;
-
- /* get the device string to compare with the pwstrDeviceId argument in callbacks */
- HRESULT hr = IMMDevice_GetId(state->pDevice, &change->monitored);
- EXIT_ON_ERROR(hr);
-
/* register the change notification client */
hr = IMMDeviceEnumerator_RegisterEndpointNotificationCallback(
state->pEnumerator, (IMMNotificationClient *)change);
EXIT_ON_ERROR(hr);
- MP_VERBOSE(state, "Monitoring changes in device: %S\n", state->change.monitored);
+ /* so the callbacks can access the ao */
+ change->ao = ao;
+
+ /* 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);
+ MP_VERBOSE(ao, "Monitoring changes in device %S\n", change->monitored);
+ }
+
return hr;
exit_label:
MP_ERR(state, "Error setting up device change monitoring: %s\n",
- wasapi_explain_err(hr));
+ mp_HRESULT_to_str(hr));
wasapi_change_uninit(ao);
return hr;
}
void wasapi_change_uninit(struct ao *ao)
{
- struct wasapi_state *state = (struct wasapi_state *)ao->priv;
+ struct wasapi_state *state = ao->priv;
struct change_notify *change = &state->change;
- if(state->pEnumerator && change->client.lpVtbl)
+ if (state->pEnumerator && change->client.lpVtbl) {
IMMDeviceEnumerator_UnregisterEndpointNotificationCallback(
state->pEnumerator, (IMMNotificationClient *)change);
+ }
if (change->monitored) CoTaskMemFree(change->monitored);
}
diff --git a/audio/out/ao_wasapi_utils.c b/audio/out/ao_wasapi_utils.c
index 13bc0fa..b0b0c10 100755
--- a/audio/out/ao_wasapi_utils.c
+++ b/audio/out/ao_wasapi_utils.c
@@ -19,7 +19,10 @@
#include <math.h>
#include <libavutil/common.h>
-#include <initguid.h>
+#include <windows.h>
+#include <errors.h>
+#include <ksguid.h>
+#include <ksmedia.h>
#include <audioclient.h>
#include <endpointvolume.h>
#include <mmdeviceapi.h>
@@ -39,14 +42,71 @@ DEFINE_PROPERTYKEY(mp_PKEY_Device_FriendlyName,
DEFINE_PROPERTYKEY(mp_PKEY_Device_DeviceDesc,
0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20,
0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 2);
-
-DEFINE_GUID(mp_KSDATAFORMAT_SUBTYPE_PCM,
- 0x00000001, 0x0000, 0x0010, 0x80, 0x00,
+// CEA 861 subformats
+// should work on vista
+DEFINE_GUID(mp_KSDATAFORMAT_SUBTYPE_IEC61937_DTS,
+ 0x00000008, 0x0000, 0x0010, 0x80, 0x00,
+ 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
+DEFINE_GUID(mp_KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL,
+ 0x00000092, 0x0000, 0x0010, 0x80, 0x00,
+ 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
+// might require 7+
+DEFINE_GUID(mp_KSDATAFORMAT_SUBTYPE_IEC61937_AAC,
+ 0x00000006, 0x0cea, 0x0010, 0x80, 0x00,
+ 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
+DEFINE_GUID(mp_KSDATAFORMAT_SUBTYPE_IEC61937_MPEG3,
+ 0x00000004, 0x0cea, 0x0010, 0x80, 0x00,
+ 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
+DEFINE_GUID(mp_KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL_PLUS,
+ 0x0000000a, 0x0cea, 0x0010, 0x80, 0x00,
0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
-DEFINE_GUID(mp_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT,
- 0x00000003, 0x0000, 0x0010, 0x80, 0x00,
+DEFINE_GUID(mp_KSDATAFORMAT_SUBTYPE_IEC61937_DTS_HD,
+ 0x0000000b, 0x0cea, 0x0010, 0x80, 0x00,
+ 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
+DEFINE_GUID(mp_KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_MLP,
+ 0x0000000c, 0x0cea, 0x0010, 0x80, 0x00,
0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
+struct wasapi_fmt_mapping {
+ const GUID *subtype;
+ int format;
+};
+
+const struct wasapi_fmt_mapping wasapi_fmt_table[] = {
+ {&mp_KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL, AF_FORMAT_S_AC3},
+ {&mp_KSDATAFORMAT_SUBTYPE_IEC61937_DTS, AF_FORMAT_S_DTS},
+ {&mp_KSDATAFORMAT_SUBTYPE_IEC61937_AAC, AF_FORMAT_S_AAC},
+ {&mp_KSDATAFORMAT_SUBTYPE_IEC61937_MPEG3, AF_FORMAT_S_MP3},
+ {&mp_KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_MLP, AF_FORMAT_S_TRUEHD},
+ {&mp_KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL_PLUS, AF_FORMAT_S_EAC3},
+ {&mp_KSDATAFORMAT_SUBTYPE_IEC61937_DTS_HD, AF_FORMAT_S_DTSHD},
+ {0}
+};
+
+static const GUID *format_to_subtype(int format)
+{
+ if (AF_FORMAT_IS_SPECIAL(format)) {
+ for (int i = 0; wasapi_fmt_table[i].format; i++) {
+ if (wasapi_fmt_table[i].format == format)
+ return wasapi_fmt_table[i].subtype;
+ }
+ return &KSDATAFORMAT_SPECIFIER_NONE;
+ } else if (AF_FORMAT_IS_FLOAT(format)) {
+ return &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
+ }
+ return &KSDATAFORMAT_SUBTYPE_PCM;
+}
+
+// "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))
+ return wasapi_fmt_table[i].format;
+ }
+ return 0;
+}
+
char *mp_GUID_to_str_buf(char *buf, size_t buf_size, const GUID *guid)
{
snprintf(buf, buf_size,
@@ -67,46 +127,19 @@ char *mp_PKEY_to_str_buf(char *buf, size_t buf_size, const PROPERTYKEY *pkey)
return buf;
}
-bool wasapi_fill_VistaBlob(wasapi_state *state)
-{
- if (!state)
- goto exit_label;
- state->VistaBlob.hAvrt = LoadLibraryW(L"avrt.dll");
- if (!state->VistaBlob.hAvrt)
- goto exit_label;
-
- state->VistaBlob.pAvSetMmThreadCharacteristicsW =
- (HANDLE (WINAPI *)(LPCWSTR, LPDWORD))
- GetProcAddress(state->VistaBlob.hAvrt, "AvSetMmThreadCharacteristicsW");
- if (!state->VistaBlob.pAvSetMmThreadCharacteristicsW)
- goto exit_label;
-
- state->VistaBlob.pAvRevertMmThreadCharacteristics =
- (WINBOOL (WINAPI *)(HANDLE))
- GetProcAddress(state->VistaBlob.hAvrt, "AvRevertMmThreadCharacteristics");
- if (!state->VistaBlob.pAvRevertMmThreadCharacteristics)
- goto exit_label;
-
- return true;
-exit_label:
- if (state->VistaBlob.hAvrt) {
- FreeLibrary(state->VistaBlob.hAvrt);
- state->VistaBlob.hAvrt = NULL;
- }
- return false;
-}
-
-const char *wasapi_explain_err(const HRESULT hr)
+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)
@@ -146,24 +179,92 @@ const char *wasapi_explain_err(const HRESULT hr)
#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;
+}
+
+bool wasapi_fill_VistaBlob(wasapi_state *state)
+{
+ if (!state)
+ goto exit_label;
+ state->VistaBlob.hAvrt = LoadLibraryW(L"avrt.dll");
+ if (!state->VistaBlob.hAvrt)
+ goto exit_label;
+
+ state->VistaBlob.pAvSetMmThreadCharacteristicsW =
+ (HANDLE (WINAPI *)(LPCWSTR, LPDWORD))
+ GetProcAddress(state->VistaBlob.hAvrt, "AvSetMmThreadCharacteristicsW");
+ if (!state->VistaBlob.pAvSetMmThreadCharacteristicsW)
+ goto exit_label;
+
+ state->VistaBlob.pAvRevertMmThreadCharacteristics =
+ (WINBOOL (WINAPI *)(HANDLE))
+ GetProcAddress(state->VistaBlob.hAvrt, "AvRevertMmThreadCharacteristics");
+ if (!state->VistaBlob.pAvRevertMmThreadCharacteristics)
+ goto exit_label;
+
+ return true;
+exit_label:
+ if (state->VistaBlob.hAvrt) {
+ FreeLibrary(state->VistaBlob.hAvrt);
+ state->VistaBlob.hAvrt = NULL;
+ }
+ return false;
+}
+
+static void update_waveformat_datarate(WAVEFORMATEXTENSIBLE *wformat)
+{
+ WAVEFORMATEX *wf = &wformat->Format;
+ wf->nBlockAlign = wf->nChannels * wf->wBitsPerSample / 8;
+ wf->nAvgBytesPerSec = wf->nSamplesPerSec * wf->nBlockAlign;
+}
+
static void set_waveformat(WAVEFORMATEXTENSIBLE *wformat,
- WORD bits, WORD valid_bits, bool is_float,
+ int format, WORD valid_bits,
DWORD samplerate, struct mp_chmap *channels)
{
- int block_align = channels->num * bits / 8;
- wformat->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
- wformat->Format.nChannels = channels->num;
+ wformat->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
+ wformat->Format.nChannels = channels->num;
wformat->Format.nSamplesPerSec = samplerate;
- wformat->Format.nAvgBytesPerSec = samplerate * block_align;
- wformat->Format.nBlockAlign = block_align;
- wformat->Format.wBitsPerSample = bits;
+ wformat->Format.wBitsPerSample = af_fmt2bits(format);
wformat->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
- if (is_float)
- wformat->SubFormat = mp_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
- else
- wformat->SubFormat = mp_KSDATAFORMAT_SUBTYPE_PCM;
- wformat->Samples.wValidBitsPerSample = valid_bits;
- wformat->dwChannelMask = mp_chmap_to_waveext(channels);
+
+ wformat->SubFormat = *format_to_subtype(format);
+ 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.
+static void set_waveformat_with_ao(WAVEFORMATEXTENSIBLE *wformat, struct ao *ao)
+{
+ struct mp_chmap channels = ao->channels;
+ mp_chmap_reorder_to_waveext(&channels);
+
+ set_waveformat(wformat, ao->format, 0, ao->samplerate, &channels);
+}
+
+// other wformat parameters must already be set with set_waveformat
+static void change_waveformat_samplerate(WAVEFORMATEXTENSIBLE *wformat,
+ DWORD samplerate)
+{
+ wformat->Format.nSamplesPerSec = samplerate;
+ update_waveformat_datarate(wformat);
+}
+
+// other wformat parameters must already be set with set_waveformat
+static void change_waveformat_channels(WAVEFORMATEXTENSIBLE *wformat,
+ struct mp_chmap *channels)
+{
+ wformat->Format.nChannels = channels->num;
+ wformat->dwChannelMask = mp_chmap_to_waveext(channels);
+ update_waveformat_datarate(wformat);
}
static WORD waveformat_valid_bits(const WAVEFORMATEX *wf)
@@ -176,296 +277,320 @@ static WORD waveformat_valid_bits(const WAVEFORMATEX *wf)
}
}
-static char *waveformat_to_str_buf(char *buf, size_t buf_size, WAVEFORMATEX *wf)
+static int format_from_waveformat(WAVEFORMATEX *wf)
{
- char* type = "?";
+ int format;
switch (wf->wFormatTag) {
case WAVE_FORMAT_EXTENSIBLE:
{
WAVEFORMATEXTENSIBLE *wformat = (WAVEFORMATEXTENSIBLE *)wf;
- if (IsEqualGUID(&mp_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT,
- &wformat->SubFormat))
- type = "float";
- else if (IsEqualGUID(&mp_KSDATAFORMAT_SUBTYPE_PCM,
- &wformat->SubFormat))
- type = "s";
+ 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)) {
+ format = AF_FORMAT_FLOAT;
+ } else {
+ format = special_subtype_to_format(&wformat->SubFormat);
+ }
break;
}
- case WAVE_FORMAT_IEEE_FLOAT:
- type = "float";
- break;
case WAVE_FORMAT_PCM:
- type = "s";
+ format = wf->wBitsPerSample == 8 ? AF_FORMAT_U8 : AF_FORMAT_S32;
break;
+ case WAVE_FORMAT_IEEE_FLOAT:
+ format = AF_FORMAT_FLOAT;
+ break;
+ default:
+ return 0;
}
- int valid_bits = waveformat_valid_bits(wf);
- if (valid_bits == wf->wBitsPerSample) {
- snprintf(buf, buf_size, "%"PRIu16"ch %s%"PRIu16" @ %"PRIu32"hz",
- wf->nChannels, type, valid_bits, (unsigned) wf->nSamplesPerSec);
- } else {
- snprintf(buf, buf_size, "%"PRIu16"ch %s%"PRIu16" (in %s%"PRIu16") @ %"PRIu32"hz",
- wf->nChannels, type, valid_bits, type, wf->wBitsPerSample,
- (unsigned) wf->nSamplesPerSec);
- }
- return buf;
+ // 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_bits operations should be a no-op for properly
+ // configured "special" formats, otherwise it will return 0.
+ return af_fmt_change_bits(format, wf->wBitsPerSample);
}
-#define waveformat_to_str(wf) waveformat_to_str_buf((char[40]){0}, 40, (wf))
-static bool waveformat_is_float(WAVEFORMATEX *wf)
+static bool chmap_from_waveformat(struct mp_chmap *channels, const WAVEFORMATEX *wf)
{
- switch (wf->wFormatTag) {
- case WAVE_FORMAT_EXTENSIBLE:
- {
+ if (wf->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
WAVEFORMATEXTENSIBLE *wformat = (WAVEFORMATEXTENSIBLE *)wf;
- return IsEqualGUID(&mp_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, &wformat->SubFormat);
+ mp_chmap_from_waveext(channels, wformat->dwChannelMask);
+ } else {
+ mp_chmap_from_channels(channels, wf->nChannels);
}
- case WAVE_FORMAT_IEEE_FLOAT:
- return true;
- default:
+
+ if (channels->num != wf->nChannels) {
+ mp_chmap_from_str(channels, bstr0("empty"));
return false;
}
+
+ return true;
}
-static bool waveformat_is_pcm(WAVEFORMATEX *wf)
+static char *waveformat_to_str_buf(char *buf, size_t buf_size, WAVEFORMATEX *wf)
{
- switch (wf->wFormatTag) {
- case WAVE_FORMAT_EXTENSIBLE:
- {
- WAVEFORMATEXTENSIBLE *wformat = (WAVEFORMATEXTENSIBLE *)wf;
- return IsEqualGUID(&mp_KSDATAFORMAT_SUBTYPE_PCM, &wformat->SubFormat);
- }
- case WAVE_FORMAT_PCM:
- return true;
- default:
- return false;
- }
+ struct mp_chmap channels;
+ chmap_from_waveformat(&channels, wf);
+
+ unsigned valid_bits = waveformat_valid_bits(wf);
+ char validstr[12] = "";
+ if (valid_bits != wf->wBitsPerSample)
+ 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)),
+ validstr, (unsigned) wf->nSamplesPerSec);
+ return buf;
}
+#define waveformat_to_str(wf) waveformat_to_str_buf((char[40]){0}, 40, (wf))
static void waveformat_copy(WAVEFORMATEXTENSIBLE* dst, WAVEFORMATEX* src)
{
- if (src->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
+ if (src->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
*dst = *(WAVEFORMATEXTENSIBLE *)src;
- else
+ } else {
dst->Format = *src;
+ }
}
-static bool set_ao_format(struct ao *ao, WAVEFORMATEX *wf)
+static bool set_ao_format(struct ao *ao, WAVEFORMATEX *wf, AUDCLNT_SHAREMODE share_mode)
{
- struct wasapi_state *state = (struct wasapi_state *)ao->priv;
- // explicitly disallow 8 bits - this will catch if the
- // closestMatch returned by IsFormatSupported in try_format below returns
- // a bit depth of less than 16
- if (wf->wBitsPerSample < 16)
- return false;
-
- int format;
- if (waveformat_is_float(wf)) {
- format = AF_FORMAT_FLOAT;
- } else if (waveformat_is_pcm(wf)) {
- format = AF_FORMAT_S32;
- } else {
- MP_ERR(ao, "Unknown WAVEFORMAT\n");
+ struct wasapi_state *state = ao->priv;
+ int format = format_from_waveformat(wf);
+ if (!format) {
+ MP_ERR(ao, "Unable to construct sample format from WAVEFORMAT %s\n",
+ waveformat_to_str(wf));
return false;
}
- // set the correct number of bits (the 32-bits assumed above may be wrong)
- format = af_fmt_change_bits(format, wf->wBitsPerSample);
- if (!format)
- return false;
-
- ao->samplerate = wf->nSamplesPerSec;
- ao->bps = wf->nAvgBytesPerSec;
- ao->format = format;
- if (wf->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
- WAVEFORMATEXTENSIBLE *wformat = (WAVEFORMATEXTENSIBLE *)wf;
- mp_chmap_from_waveext(&ao->channels, wformat->dwChannelMask);
- } else {
- mp_chmap_from_channels(&ao->channels, wf->nChannels);
- }
-
- if (ao->channels.num != wf->nChannels) {
- MP_ERR(ao, "Channel map doesn't match number of channels\n");
- return false;
+ // Do not touch the ao for passthrough, just assume that we set WAVEFORMATEX correctly.
+ if (!AF_FORMAT_IS_SPECIAL(format)) {
+ struct mp_chmap channels;
+ if (!chmap_from_waveformat(&channels, wf)) {
+ MP_ERR(ao, "Unable to construct channel map from WAVEFORMAT %s\n",
+ waveformat_to_str(wf));
+ return false;
+ }
+ ao->samplerate = wf->nSamplesPerSec;
+ ao->format = format;
+ ao->channels = channels;
}
-
waveformat_copy(&state->format, wf);
+ state->share_mode = share_mode;
return true;
}
-static bool try_format(struct ao *ao,
- int bits, int valid_bits, bool is_float,
- int samplerate, struct mp_chmap *channels)
+static bool try_format_exclusive(struct ao *ao, WAVEFORMATEXTENSIBLE *wformat)
{
- struct wasapi_state *state = (struct wasapi_state *)ao->priv;
- /* If for some reason we get 8-bits, try 16-bit equivalent instead.
- This can happen if 8-bits is initially requested in ao->format
- or in the unlikely event that GetMixFormat return 8-bits.
- If someone with a "vintage" sound card or something complains
- this can be reconsidered */
- bits = FFMAX(16, bits);
-
- WAVEFORMATEXTENSIBLE wformat;
- set_waveformat(&wformat, bits, valid_bits, is_float, samplerate, channels);
- MP_VERBOSE(ao, "Trying %s\n", waveformat_to_str(&wformat.Format));
-
- WAVEFORMATEX *closestMatch;
+ struct wasapi_state *state = ao->priv;
+ MP_VERBOSE(ao, "Trying %s (exclusive)\n", waveformat_to_str(&wformat->Format));
HRESULT hr = IAudioClient_IsFormatSupported(state->pAudioClient,
- state->share_mode,
- &wformat.Format, &closestMatch);
-
- if (closestMatch) {
- waveformat_copy(&wformat, closestMatch);
- CoTaskMemFree(closestMatch);
- }
+ AUDCLNT_SHAREMODE_EXCLUSIVE,
+ &wformat->Format, NULL);
+ if (hr != AUDCLNT_E_UNSUPPORTED_FORMAT)
+ EXIT_ON_ERROR(hr);
- if (hr == S_FALSE) {
- if (set_ao_format(ao, &wformat.Format)) {
- MP_VERBOSE(ao, "Accepted as %dch %s @ %dhz\n",
- ao->channels.num, af_fmt_to_str(ao->format), ao->samplerate);
- return true;
- }
- } else if (hr == S_OK) {
- if (set_ao_format(ao, &wformat.Format)) {
- MP_VERBOSE(ao, "%dch %s @ %dhz accepted\n",
- ao->channels.num, af_fmt_to_str(ao->format), samplerate);
- return true;
- }
- }
+ return hr == S_OK;
+exit_label:
+ MP_ERR(state, "Error testing exclusive format: %s\n", mp_HRESULT_to_str(hr));
return false;
}
-static bool try_mix_format(struct ao *ao)
+static bool search_sample_formats(struct ao *ao, WAVEFORMATEXTENSIBLE *wformat,
+ int samplerate, struct mp_chmap *channels)
{
- struct wasapi_state *state = (struct wasapi_state *)ao->priv;
- WAVEFORMATEX *wf = NULL;
- HRESULT hr = IAudioClient_GetMixFormat(state->pAudioClient, &wf);
- EXIT_ON_ERROR(hr);
-
- bool ret = try_format(ao, wf->wBitsPerSample, waveformat_valid_bits(wf),
- waveformat_is_float(wf), wf->nSamplesPerSec, &ao->channels);
+ // some common bit depths / container sizes (requests welcome)
+ int try[] = {AF_FORMAT_DOUBLE, AF_FORMAT_FLOAT, AF_FORMAT_S32,
+ AF_FORMAT_S24 , AF_FORMAT_S32 , AF_FORMAT_S16,
+ AF_FORMAT_U8 , 0};
+ unsigned valid[] = {0 , 0, 0,
+ 0 , 24, 0,
+ 0 };
+ for (int i = 0; try[i]; i++) {
+ set_waveformat(wformat, try[i], valid[i], samplerate, channels);
+ if (try_format_exclusive(ao, wformat))
+ return true;
+ }
- SAFE_RELEASE(wf, CoTaskMemFree(wf));
- return ret;
-exit_label:
- MP_ERR(state, "Error getting mix format: %s (0x%"PRIx32")\n",
- wasapi_explain_err(hr), (uint32_t) hr);
- SAFE_RELEASE(wf, CoTaskMemFree(wf));
+ wformat->Format.wBitsPerSample = 0;
return false;
}
-static bool try_passthrough(struct ao *ao)
+static bool search_samplerates(struct ao *ao, WAVEFORMATEXTENSIBLE *wformat,
+ struct mp_chmap *channels)
{
- struct wasapi_state *state = (struct wasapi_state *)ao->priv;
- WAVEFORMATEXTENSIBLE wformat = {
- .Format = {
- .wFormatTag = WAVE_FORMAT_EXTENSIBLE,
- .nChannels = ao->channels.num,
- .nSamplesPerSec = ao->samplerate,
- .nAvgBytesPerSec = (ao->samplerate) * (ao->channels.num * 2),
- .nBlockAlign = ao->channels.num * 2,
- .wBitsPerSample = 16,
- .cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX),
- },
- .Samples.wValidBitsPerSample = 16,
- .dwChannelMask = mp_chmap_to_waveext(&ao->channels),
- .SubFormat = mp_KSDATAFORMAT_SUBTYPE_PCM,
- };
- wformat.SubFormat.Data1 = WAVE_FORMAT_DOLBY_AC3_SPDIF; // see INIT_WAVEFORMATEX_GUID macro
-
- MP_VERBOSE(ao, "Trying passthrough for %s...\n", af_fmt_to_str(ao->format));
+ // try list of typical sample rates (requests welcome)
+ int try[] = {8000, 11025, 16000, 22050, 32000, 44100, 48000, 88200, 96000,
+ 176400, 192000, 352800, 384000, 0};
+
+ // get a list of supported rates
+ int n = 0;
+ int supported[MP_ARRAY_SIZE(try)] = {0};
+
+ wformat->Format.wBitsPerSample = 0;
+ for (int i = 0; try[i]; i++) {
+ if (!wformat->Format.wBitsPerSample) {
+ if (search_sample_formats(ao, wformat, try[i], channels))
+ supported[n++] = try[i];
+ } else {
+ change_waveformat_samplerate(wformat, try[i]);
+ if (try_format_exclusive(ao, wformat))
+ supported[n++] = try[i];
+ }
+ }
- HRESULT hr = IAudioClient_IsFormatSupported(state->pAudioClient,
- state->share_mode,
- &wformat.Format, NULL);
- if (!FAILED(hr)) {
- ao->format = ao->format;
- state->format = wformat;
+ for (int i = 0; supported[i]; i++) {
+ // first choose the lowest integer multiple of the sample rate
+ if (!(supported[i] % ao->samplerate)) {
+ change_waveformat_samplerate(wformat, supported[i]);
+ return true;
+ }
+ }
+
+ // then choose the highest supported (if any)
+ if (n) {
+ change_waveformat_samplerate(wformat, supported[n-1]);
return true;
}
+
+ // otherwise, this is probably an unsupported channel map
+ wformat->Format.nSamplesPerSec = 0;
return false;
}
-static bool search_sample_formats(struct ao *ao, int samplerate, struct mp_chmap *channels)
+static bool search_channels(struct ao *ao, WAVEFORMATEXTENSIBLE *wformat)
{
- // try float
- for (int bits = 64; bits; bits -= 32) {
- if (try_format(ao, bits, bits, true, samplerate, channels))
- return true;
+ struct wasapi_state *state = ao->priv;
+ struct mp_chmap_sel chmap_sel = {.tmp = state};
+ struct mp_chmap entry;
+ // put common layouts first so that we find sample rate/format early
+ char *channel_layouts[] =
+ {"mono", "stereo", "2.1", "4.0", "5.0", "5.1", "6.1", "7.1",
+ "3.0", "3.0(back)",
+ "quad", "quad(side)", "3.1",
+ "5.0(side)", "4.1",
+ "5.1(side)", "6.0", "6.0(front)", "hexagonal"
+ "6.1(back)", "6.1(front)", "7.0", "7.0(front)",
+ "7.1(wide)", "7.1(wide-side)", "7.1(rear)", "octagonal", NULL};
+
+ wformat->Format.nSamplesPerSec = 0;
+ for (int j = 0; channel_layouts[j]; j++) {
+ mp_chmap_from_str(&entry, bstr0(channel_layouts[j]));
+ 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));
+ }
+ } else {
+ change_waveformat_channels(wformat, &entry);
+ if (try_format_exclusive(ao, wformat)) {
+ mp_chmap_sel_add_map(&chmap_sel, &entry);
+ MP_VERBOSE(ao, "%s is supported\n", mp_chmap_to_str(&entry));
+ }
+ }
}
- // try int
- // some common bit depths / container sizes (requests welcome)
- int bits[] = {32, 24, 32, 24, 32, 16};
- int valid_bits[] = {32, 24, 24, 20, 20, 16};
- for (int i = 0; i < MP_ARRAY_SIZE(bits); i++) {
- if (try_format(ao, bits[i], valid_bits[i], false, samplerate, channels))
- return true;
+ entry = ao->channels;
+ if (ao_chmap_sel_adjust(ao, &chmap_sel, &entry)){
+ change_waveformat_channels(wformat, &entry);
+ return true;
}
+
+ MP_ERR(ao, "No suitable audio format found\n");
return false;
}
-static bool find_formats(struct ao *ao)
+static bool find_formats_exclusive(struct ao *ao, bool do_search)
{
- struct wasapi_state *state = (struct wasapi_state *)ao->priv;
+ WAVEFORMATEXTENSIBLE wformat;
+ set_waveformat_with_ao(&wformat, ao);
- if (AF_FORMAT_IS_IEC61937(ao->format)) {
- if (try_passthrough(ao))
- return true;
+ // 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(ao, &wformat) &&
+ (!do_search || !search_channels(ao, &wformat)))
+ return false;
- MP_ERR(ao, "Couldn't use passthrough");
- if (!state->opt_exclusive)
- MP_ERR(ao, " (try exclusive mode)");
- MP_ERR(ao, "\n");
+ if (!set_ao_format(ao, &wformat.Format, AUDCLNT_SHAREMODE_EXCLUSIVE))
return false;
- }
- /* See if the format works as-is */
- int bits = af_fmt2bits(ao->format);
- if (try_format(ao, bits, bits, af_fmt_is_float(ao->format),
- ao->samplerate, &ao->channels))
- return true;
+ MP_VERBOSE(ao, "Accepted as %s %s @ %dhz (exclusive)\n",
+ mp_chmap_to_str(&ao->channels),
+ af_fmt_to_str(ao->format), ao->samplerate);
+ return true;
+}
- if (!state->opt_exclusive) {
- /* shared mode, we can use the system default mix format. */
- if (try_mix_format(ao))
- return true;
- MP_WARN(ao, "Couldn't use default mix format\n");
+static bool find_formats_shared(struct ao *ao)
+{
+ struct wasapi_state *state = ao->priv;
+
+ WAVEFORMATEXTENSIBLE wformat;
+ set_waveformat_with_ao(&wformat, ao);
+
+ MP_VERBOSE(ao, "Trying %s (shared)\n", waveformat_to_str(&wformat.Format));
+ WAVEFORMATEX *closestMatch;
+ HRESULT hr = IAudioClient_IsFormatSupported(state->pAudioClient,
+ AUDCLNT_SHAREMODE_SHARED,
+ &wformat.Format, &closestMatch);
+ if (hr != AUDCLNT_E_UNSUPPORTED_FORMAT)
+ EXIT_ON_ERROR(hr);
+
+ switch (hr) {
+ case S_OK:
+ break;
+ case S_FALSE:
+ waveformat_copy(&wformat, closestMatch);
+ CoTaskMemFree(closestMatch);
+ MP_VERBOSE(ao, "Closest match is %s\n",
+ waveformat_to_str(&wformat.Format));
+ break;
+ default:
+ hr = IAudioClient_GetMixFormat(state->pAudioClient, &closestMatch);
+ EXIT_ON_ERROR(hr);
+ waveformat_copy(&wformat, closestMatch);
+ MP_VERBOSE(ao, "Fallback to mix format %s\n",
+ waveformat_to_str(&wformat.Format));
+ CoTaskMemFree(closestMatch);
}
- /* Exclusive mode, we have to guess. */
- while (true) {
- int samplerate = ao->samplerate;
- // try integer multiples of requested sample rate first
- while (samplerate <= 384000) {
- if (search_sample_formats(ao, samplerate, &ao->channels))
- return true;
- samplerate += ao->samplerate;
- }
+ if (!set_ao_format(ao, &wformat.Format, AUDCLNT_SHAREMODE_SHARED))
+ return false;
- // now try list of typical sample rates (requests welcome)
- int samplerates[] = {384000, 352800, 192000, 176400, 96000, 88200,
- 48000, 44100, 32000, 22050, 16000, 11025,
- 8000};
- for (int i = 0; i < MP_ARRAY_SIZE(samplerates); i++) {
- if (search_sample_formats(ao, samplerates[i], &ao->channels))
- return true;
- }
+ MP_VERBOSE(ao, "Accepted as %s %s @ %dhz (shared)\n",
+ mp_chmap_to_str(&ao->channels),
+ 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));
+ return false;
+}
- if (ao->channels.num > 1) {
- mp_chmap_from_channels(&ao->channels, ao->channels.num - 1);
- } else {
- MP_ERR(ao, "Couldn't find acceptable audio format\n");
- return false;
- }
+static bool find_formats(struct ao *ao)
+{
+ struct wasapi_state *state = ao->priv;
+
+ if (state->opt_exclusive) {
+ // If exclusive is requested, try the requested format (which
+ // might be passthrough). If that fails, do a pcm format
+ // search.
+ return find_formats_exclusive(ao, true);
+ } else if (AF_FORMAT_IS_SPECIAL(ao->format)) {
+ // If a passthrough format is requested, but exclusive mode
+ // was not explicitly set, try only the requested passthrough
+ // format in exclusive mode. Fall back on shared mode if that
+ // fails without doing the exclusive pcm format search.
+ if (find_formats_exclusive(ao, false))
+ return true;
}
+ // Default is to use shared mode
+ return find_formats_shared(ao);
}
static HRESULT init_clock(struct wasapi_state *state) {
- HRESULT hr;
-
- hr = IAudioClient_GetService(state->pAudioClient,
- &IID_IAudioClock,
- (void **)&state->pAudioClock);
+ HRESULT hr = IAudioClient_GetService(state->pAudioClient,
+ &IID_IAudioClock,
+ (void **)&state->pAudioClock);
EXIT_ON_ERROR(hr);
hr = IAudioClock_GetFrequency(state->pAudioClock, &state->clock_frequency);
EXIT_ON_ERROR(hr);
@@ -479,18 +604,17 @@ static HRESULT init_clock(struct wasapi_state *state) {
return S_OK;
exit_label:
- MP_ERR(state, "Error obtaining the audio device's timing: %s (0x%"PRIx32")\n",
- wasapi_explain_err(hr), (uint32_t) hr);
+ MP_ERR(state, "Error obtaining the audio device's timing: %s\n",
+ mp_HRESULT_to_str(hr));
return hr;
}
static HRESULT init_session_display(struct wasapi_state *state) {
- HRESULT hr;
wchar_t path[MAX_PATH+12] = {0};
- hr = IAudioClient_GetService(state->pAudioClient,
- &IID_IAudioSessionControl,
- (void **)&state->pSessionControl);
+ HRESULT hr = IAudioClient_GetService(state->pAudioClient,
+ &IID_IAudioSessionControl,
+ (void **)&state->pSessionControl);
EXIT_ON_ERROR(hr);
GetModuleFileNameW(NULL, path, MAX_PATH);
@@ -503,19 +627,18 @@ static HRESULT init_session_display(struct wasapi_state *state) {
return S_OK;
exit_label:
- MP_WARN(state, "Error setting audio session display name: %s (0x%"PRIx32")\n",
- wasapi_explain_err(hr), (uint32_t) hr);
+ MP_WARN(state, "Error setting audio session display name: %s\n",
+ mp_HRESULT_to_str(hr));
return S_OK; // No reason to abort initialization.
}
static HRESULT fix_format(struct ao *ao)
{
- struct wasapi_state *state = (struct wasapi_state *)ao->priv;
- HRESULT hr;
+ struct wasapi_state *state = ao->priv;
REFERENCE_TIME devicePeriod, bufferDuration, bufferPeriod;
MP_DBG(state, "IAudioClient::GetDevicePeriod\n");
- hr = IAudioClient_GetDevicePeriod(state->pAudioClient,&devicePeriod, NULL);
+ 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 */
@@ -539,13 +662,13 @@ reinit:
NULL);
/* something about buffer sizes on Win7 */
if (hr == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED) {
- if (retries > 0)
+ if (retries > 0) {
EXIT_ON_ERROR(hr);
- else
+ } else {
retries ++;
-
- MP_VERBOSE(state, "IAudioClient::Initialize negotiation failed with %s (0x%"PRIx32"), used %lld * 100ns\n",
- wasapi_explain_err(hr), (uint32_t) hr, bufferDuration);
+ }
+ 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 =
@@ -574,7 +697,7 @@ reinit:
EXIT_ON_ERROR(hr);
MP_DBG(state, "IAudioClient::Initialize IAudioClient_SetEventHandle\n");
- hr = IAudioClient_SetEventHandle(state->pAudioClient, state->hFeed);
+ hr = IAudioClient_SetEventHandle(state->pAudioClient, state->hWake);
EXIT_ON_ERROR(hr);
MP_DBG(state, "IAudioClient::Initialize IAudioClient_GetBufferSize\n");
@@ -608,15 +731,13 @@ reinit:
return S_OK;
exit_label:
- MP_ERR(state, "Error initializing device: %s (0x%"PRIx32")\n",
- wasapi_explain_err(hr), (uint32_t) hr);
+ MP_ERR(state, "Error initializing device: %s\n", mp_HRESULT_to_str(hr));
return hr;
}
static char* get_device_id(IMMDevice *pDevice) {
- if (!pDevice) {
+ if (!pDevice)
return NULL;
- }
LPWSTR devid = NULL;
char *idstr = NULL;
@@ -638,9 +759,8 @@ exit_label:
}
static char* get_device_name(IMMDevice *pDevice) {
- if (!pDevice) {
+ if (!pDevice)
return NULL;
- }
IPropertyStore *pProps = NULL;
char *namestr = NULL;
@@ -663,9 +783,8 @@ exit_label:
}
static char* get_device_desc(IMMDevice *pDevice) {
- if (!pDevice) {
+ if (!pDevice)
return NULL;
- }
IPropertyStore *pProps = NULL;
char *desc = NULL;
@@ -707,95 +826,51 @@ end:
return found;
}
-// Warning: ao and list are NULL in the "--ao=wasapi:device=help" path!
-static HRESULT enumerate_with_state(struct mp_log *log, struct ao *ao,
- struct ao_device_list *list,
- char *header, int status, int with_id)
+void wasapi_list_devs(struct ao *ao, struct ao_device_list *list)
{
- HRESULT hr;
- IMMDeviceEnumerator *pEnumerator = NULL;
+ struct wasapi_state *state = ao->priv;
IMMDeviceCollection *pDevices = NULL;
IMMDevice *pDevice = NULL;
- char *defid = NULL;
-
- hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL,
- &IID_IMMDeviceEnumerator, (void **)&pEnumerator);
- EXIT_ON_ERROR(hr);
-
- hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(pEnumerator,
- eRender, eMultimedia,
- &pDevice);
- EXIT_ON_ERROR(hr);
-
- defid = get_device_id(pDevice);
+ char *name = NULL, *id = NULL;
- SAFE_RELEASE(pDevice, IMMDevice_Release(pDevice));
-
- hr = IMMDeviceEnumerator_EnumAudioEndpoints(pEnumerator, eRender,
- status, &pDevices);
+ HRESULT hr = IMMDeviceEnumerator_EnumAudioEndpoints(state->pEnumerator, eRender,
+ DEVICE_STATE_ACTIVE, &pDevices);
EXIT_ON_ERROR(hr);
int count;
- IMMDeviceCollection_GetCount(pDevices, &count);
- if (count > 0) {
- mp_info(log, "%s\n", header);
- }
+ 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);
- char *name = get_device_name(pDevice);
- char *id = get_device_id(pDevice);
-
- char *mark = "";
- if (strcmp(id, defid) == 0)
- mark = " (default)";
-
- if (with_id) {
- mp_info(log, "Device #%d: %s, ID: %s%s\n", i, name, id, mark);
- } else {
- mp_info(log, "%s, ID: %s%s\n", name, id, mark);
+ 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});
- if (ao) {
- char *desc = talloc_asprintf(NULL, "%s, ID: %s%s", name, id, mark);
- struct ao_device_desc e = {id, desc};
- ao_device_list_add(list, ao, &e);
- }
+ MP_VERBOSE(ao, "#%d, GUID: \'%s\', name: \'%s\'\n", i, id, safe_name);
talloc_free(name);
talloc_free(id);
SAFE_RELEASE(pDevice, IMMDevice_Release(pDevice));
}
- talloc_free(defid);
SAFE_RELEASE(pDevices, IMMDeviceCollection_Release(pDevices));
- SAFE_RELEASE(pEnumerator, IMMDeviceEnumerator_Release(pEnumerator));
- return S_OK;
+ return;
exit_label:
- talloc_free(defid);
+ 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));
- SAFE_RELEASE(pEnumerator, IMMDeviceEnumerator_Release(pEnumerator));
- return hr;
-}
-
-bool wasapi_enumerate_devices(struct mp_log *log, struct ao *ao,
- struct ao_device_list *list)
-{
- HRESULT hr;
- hr = enumerate_with_state(log, ao, list, "Active devices:",
- DEVICE_STATE_ACTIVE, 1);
- EXIT_ON_ERROR(hr);
- hr = enumerate_with_state(log, ao, list, "Unplugged devices:",
- DEVICE_STATE_UNPLUGGED, 0);
- EXIT_ON_ERROR(hr);
- return true;
-exit_label:
- mp_err(log, "Error enumerating devices: %s (0x%"PRIx32")\n",
- wasapi_explain_err(hr), (uint32_t) hr);
- return false;
}
static HRESULT load_default_device(struct ao *ao, IMMDeviceEnumerator* pEnumerator,
@@ -812,8 +887,7 @@ static HRESULT load_default_device(struct ao *ao, IMMDeviceEnumerator* pEnumerat
return S_OK;
exit_label:
- MP_ERR(ao , "Error loading default device: %s (0x%"PRIx32")\n",
- wasapi_explain_err(hr), (uint32_t) hr);
+ MP_ERR(ao , "Error loading default device: %s\n", mp_HRESULT_to_str(hr));
return hr;
}
@@ -826,12 +900,11 @@ static HRESULT find_and_load_device(struct ao *ao, IMMDeviceEnumerator* pEnumera
LPWSTR deviceID = NULL;
char *end;
- int devno = (int) strtol(search, &end, 10);
+ int devno = strtol(search, &end, 10);
char *devid = NULL;
- if (end == search || *end) {
+ if (end == search || *end)
devid = search;
- }
int search_err = 0;
@@ -900,9 +973,8 @@ static HRESULT find_and_load_device(struct ao *ao, IMMDeviceEnumerator* pEnumera
SAFE_RELEASE(pTempDevice, IMMDevice_Release(pTempDevice));
}
- if (deviceID == NULL) {
+ if (deviceID == NULL)
MP_ERR(ao, "Could not find device %s\n", devid);
- }
}
SAFE_RELEASE(pTempDevice, IMMDevice_Release(pTempDevice));
@@ -915,9 +987,8 @@ static HRESULT find_and_load_device(struct ao *ao, IMMDeviceEnumerator* pEnumera
hr = IMMDeviceEnumerator_GetDevice(pEnumerator, deviceID, ppDevice);
- if (FAILED(hr)) {
+ if (FAILED(hr))
MP_ERR(ao, "Could not load requested device\n");
- }
}
exit_label:
@@ -928,27 +999,6 @@ exit_label:
return hr;
}
-int wasapi_validate_device(struct mp_log *log, const m_option_t *opt,
- struct bstr name, struct bstr param)
-{
- if (bstr_equals0(param, "help")) {
- wasapi_enumerate_devices(log, NULL, NULL);
- return M_OPT_EXIT;
- }
-
- mp_dbg(log, "Validating device=%s\n", param.start);
-
- char *end;
- int devno = (int) strtol(param.start, &end, 10);
-
- int ret = 1;
- if ((end == (void *)param.start || *end) && devno < 0)
- ret = M_OPT_OUT_OF_RANGE;
-
- mp_dbg(log, "device=%s %svalid\n", param.start, ret == 1 ? "" : "not ");
- return ret;
-}
-
HRESULT wasapi_setup_proxies(struct wasapi_state *state) {
HRESULT hr;
@@ -966,8 +1016,7 @@ HRESULT wasapi_setup_proxies(struct wasapi_state *state) {
return S_OK;
exit_label:
- MP_ERR(state, "Error reading COM proxy: %s (0x%"PRIx32")\n",
- wasapi_explain_err(hr), (uint32_t) hr);
+ MP_ERR(state, "Error reading COM proxy: %s\n", mp_HRESULT_to_str(hr));
return hr;
}
@@ -995,8 +1044,7 @@ static HRESULT create_proxies(struct wasapi_state *state) {
return S_OK;
exit_label:
- MP_ERR(state, "Error creating COM proxy: %s (0x%"PRIx32")\n",
- wasapi_explain_err(hr), (uint32_t) hr);
+ MP_ERR(state, "Error creating COM proxy: %s\n", mp_HRESULT_to_str(hr));
return hr;
}
@@ -1006,8 +1054,9 @@ static void destroy_proxies(struct wasapi_state *state) {
SAFE_RELEASE(state->sSessionControl, IUnknown_Release(state->sSessionControl));
}
-void wasapi_dispatch(void)
+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))
@@ -1016,15 +1065,14 @@ void wasapi_dispatch(void)
HRESULT wasapi_thread_init(struct ao *ao)
{
- struct wasapi_state *state = (struct wasapi_state *)ao->priv;
- HRESULT hr;
+ struct wasapi_state *state = ao->priv;
MP_DBG(ao, "Init wasapi thread\n");
int64_t retry_wait = 1;
retry:
state->initial_volume = -1.0;
- hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL,
- &IID_IMMDeviceEnumerator, (void **)&state->pEnumerator);
+ HRESULT hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL,
+ &IID_IMMDeviceEnumerator, (void **)&state->pEnumerator);
EXIT_ON_ERROR(hr);
char *device = state->opt_device;
@@ -1032,10 +1080,11 @@ retry:
device = ao->device;
- if (!device || !device[0])
+ if (!device || !device[0]) {
hr = load_default_device(ao, state->pEnumerator, &state->pDevice);
- else
+ } else {
hr = find_and_load_device(ao, state->pEnumerator, &state->pDevice, device);
+ }
EXIT_ON_ERROR(hr);
char *name = get_device_name(state->pDevice);
@@ -1056,9 +1105,10 @@ retry:
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 (0x%"PRIx32")\n",
- wasapi_explain_err(hr), (uint32_t) hr);
+ 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)) {
@@ -1068,9 +1118,9 @@ retry:
MP_DBG(ao, "Fixing format\n");
hr = fix_format(ao);
- if ((hr == AUDCLNT_E_DEVICE_IN_USE ||
- hr == AUDCLNT_E_DEVICE_INVALIDATED) &&
- retry_wait <= 8) {
+ if ((hr == AUDCLNT_E_DEVICE_IN_USE || hr == AUDCLNT_E_DEVICE_INVALIDATED) &&
+ retry_wait <= 8)
+ {
wasapi_thread_uninit(ao);
MP_WARN(ao, "Retrying in %"PRId64" us\n", retry_wait);
mp_sleep_us(retry_wait);
@@ -1084,42 +1134,39 @@ retry:
EXIT_ON_ERROR(hr);
MP_DBG(ao, "Read volume levels\n");
- if (state->opt_exclusive)
+ if (state->opt_exclusive) {
IAudioEndpointVolume_GetMasterVolumeLevelScalar(state->pEndpointVolume,
&state->initial_volume);
- else
+ } else {
ISimpleAudioVolume_GetMasterVolume(state->pAudioVolume,
&state->initial_volume);
-
+ }
state->previous_volume = state->initial_volume;
- wasapi_change_init(ao);
+ 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 (0x%"PRIx32")\n",
- wasapi_explain_err(hr), (uint32_t) hr);
+ MP_ERR(state, "Error setting up audio thread: %s\n", mp_HRESULT_to_str(hr));
return hr;
}
void wasapi_thread_uninit(struct ao *ao)
{
- struct wasapi_state *state = (struct wasapi_state *)ao->priv;
-
- wasapi_dispatch();
+ 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 )
+ 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));
diff --git a/audio/out/ao_wasapi_utils.h b/audio/out/ao_wasapi_utils.h
index a44cd4d..5ac2d3e 100755
--- a/audio/out/ao_wasapi_utils.h
+++ b/audio/out/ao_wasapi_utils.h
@@ -29,21 +29,16 @@
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))
bool wasapi_fill_VistaBlob(wasapi_state *state);
-const char *wasapi_explain_err(const HRESULT hr);
+void wasapi_list_devs(struct ao *ao, struct ao_device_list *list);
-bool wasapi_enumerate_devices(struct mp_log *log, struct ao *ao,
- struct ao_device_list *list);
-
-int wasapi_validate_device(struct mp_log *log, const m_option_t *opt,
- struct bstr name, struct bstr param);
-
-
-void wasapi_dispatch(void);
+void wasapi_dispatch(struct ao *ao);
HRESULT wasapi_thread_init(struct ao *ao);
void wasapi_thread_uninit(struct ao *ao);
diff --git a/audio/out/internal.h b/audio/out/internal.h
index 9414208..820ac05 100644
--- a/audio/out/internal.h
+++ b/audio/out/internal.h
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MP_AO_INTERNAL_H_
@@ -59,7 +58,8 @@ struct ao {
// Used during init: if init fails, redirect to this ao
char *redirect;
- atomic_bool request_reload;
+ // Internal events (use ao_request_reload(), ao_hotplug_event())
+ atomic_bool request_reload, request_hotplug;
int buffer;
double def_buffer;
@@ -161,17 +161,19 @@ struct ao_driver {
// Return the list of devices currently available in the system. Use
// ao_device_list_add() to add entries. The selected device will be set as
// ao->device (using ao_device_desc.name).
- // Warning: the ao struct passed doesn't necessarily have ao_driver->init()
- // called on it - in this case, ->uninit() won't be called either
- // after this function. The idea is that list_devs can be called
- // both when no audio or when audio is active. the latter can
- // happen if the audio config change at runtime, and in this case
- // we don't want to force a new connection to the audio server
- // just to update the device list. For runtime updates, ->init()
- // will have been called. In both cases, ao->priv is properly
- // allocated. (Runtime updates are not used/supported yet.)
+ // Warning: the ao struct passed is not initialized with ao_driver->init().
+ // Instead, hotplug_init/hotplug_uninit is called. If these
+ // callbacks are not set, no driver initialization call is done
+ // on the ao struct.
void (*list_devs)(struct ao *ao, struct ao_device_list *list);
+ // If set, these are called before/after ao_driver->list_devs is called.
+ // It is also assumed that the driver can do hotplugging - which means
+ // it is expected to call ao_hotplug_event(ao) whenever the system's
+ // audio device list changes. The player will then call list_devs() again.
+ int (*hotplug_init)(struct ao *ao);
+ void (*hotplug_uninit)(struct ao *ao);
+
// For option parsing (see vo.h)
int priv_size;
const void *priv_defaults;
diff --git a/common/av_log.c b/common/av_log.c
index b11289f..e5c414f 100644
--- a/common/av_log.c
+++ b/common/av_log.c
@@ -3,21 +3,20 @@
* Copyright (C) 2006 Michael Niedermayer
* Copyright (C) 2009 Uoti Urpala
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
diff --git a/common/common.c b/common/common.c
index 4c1f9f3..15be4d1 100644
--- a/common/common.c
+++ b/common/common.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdarg.h>
diff --git a/common/common.h b/common/common.h
index e341142..2151d03 100644
--- a/common/common.h
+++ b/common/common.h
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_MPCOMMON_H
diff --git a/common/encode_lavc.c b/common/encode_lavc.c
index 4cb653d..2a01bee 100644
--- a/common/encode_lavc.c
+++ b/common/encode_lavc.c
@@ -5,19 +5,18 @@
*
* This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <libavutil/avutil.h>
diff --git a/common/encode_lavc.h b/common/encode_lavc.h
index fc1e754..0ab922f 100644
--- a/common/encode_lavc.h
+++ b/common/encode_lavc.h
@@ -4,19 +4,18 @@
*
* This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_ENCODE_LAVC_H
diff --git a/common/msg.c b/common/msg.c
index 2ae52a4..3a441d0 100644
--- a/common/msg.c
+++ b/common/msg.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
@@ -62,8 +61,6 @@ struct mp_log_root {
int num_buffers;
FILE *log_file;
FILE *stats_file;
- // --- semi-atomic access
- bool mute;
// --- must be accessed atomically
/* This is incremented every time the msglevels must be reloaded.
* (This is perhaps better than maintaining a globally accessible and
@@ -134,7 +131,7 @@ static void update_loglevel(struct mp_log *log)
bool mp_msg_test(struct mp_log *log, int lev)
{
struct mp_log_root *root = log->root;
- if (!root || root->mute)
+ if (!root)
return false;
if (atomic_load_explicit(&log->reload_counter, memory_order_relaxed) !=
atomic_load_explicit(&root->reload_counter, memory_order_relaxed))
@@ -487,13 +484,6 @@ void mp_msg_update_msglevels(struct mpv_global *global)
pthread_mutex_unlock(&mp_msg_lock);
}
-void mp_msg_mute(struct mpv_global *global, bool mute)
-{
- struct mp_log_root *root = global->log->root;
-
- root->mute = mute;
-}
-
void mp_msg_force_stderr(struct mpv_global *global, bool force_stderr)
{
struct mp_log_root *root = global->log->root;
diff --git a/common/msg.h b/common/msg.h
index 5ca868d..9789ba9 100644
--- a/common/msg.h
+++ b/common/msg.h
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_MP_MSG_H
diff --git a/common/msg_control.h b/common/msg_control.h
index 2e1a4ec..c26a557 100644
--- a/common/msg_control.h
+++ b/common/msg_control.h
@@ -7,7 +7,6 @@ struct mpv_global;
void mp_msg_init(struct mpv_global *global);
void mp_msg_uninit(struct mpv_global *global);
void mp_msg_update_msglevels(struct mpv_global *global);
-void mp_msg_mute(struct mpv_global *global, bool mute);
void mp_msg_force_stderr(struct mpv_global *global, bool force_stderr);
bool mp_msg_has_status_line(struct mpv_global *global);
diff --git a/common/playlist.c b/common/playlist.c
index 857f5cb..bb849f1 100644
--- a/common/playlist.c
+++ b/common/playlist.c
@@ -1,18 +1,18 @@
/*
- * This file is part of mplayer.
+ * This file is part of mpv.
*
- * mplayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * mplayer is distributed in the hope that it will be useful,
+ * 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 mplayer. If not, see <http://www.gnu.org/licenses/>.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <assert.h>
@@ -270,21 +270,19 @@ struct playlist *playlist_parse_file(const char *file, struct mpv_global *global
struct mp_log *log = mp_log_new(NULL, global->log, "!playlist_parser");
mp_verbose(log, "Parsing playlist file %s...\n", file);
- struct playlist *ret = NULL;
- stream_t *stream = stream_open(file, global);
- if(!stream) {
- mp_err(log, "Error while opening playlist file %s\n", file);
+ struct demuxer_params p = {.force_format = "playlist"};
+ struct demuxer *d = demux_open_url(file, &p, NULL, global);
+ if (!d) {
talloc_free(log);
return NULL;
}
- struct demuxer *pl_demux = demux_open(stream, "playlist", NULL, global);
- if (pl_demux && pl_demux->playlist) {
+ struct playlist *ret = NULL;
+ if (d && d->playlist) {
ret = talloc_zero(NULL, struct playlist);
- playlist_transfer_entries(ret, pl_demux->playlist);
+ playlist_transfer_entries(ret, d->playlist);
}
- free_demuxer(pl_demux);
- free_stream(stream);
+ free_demuxer_and_stream(d);
if (ret) {
mp_verbose(log, "Playlist successfully parsed\n");
diff --git a/common/playlist.h b/common/playlist.h
index 119e591..0e571a0 100644
--- a/common/playlist.h
+++ b/common/playlist.h
@@ -1,18 +1,18 @@
/*
- * This file is part of mplayer.
+ * This file is part of mpv.
*
- * mplayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * mplayer is distributed in the hope that it will be useful,
+ * 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 mplayer. If not, see <http://www.gnu.org/licenses/>.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_PLAYLIST_H
diff --git a/common/version.c b/common/version.c
index 7e508a7..af110fa 100644
--- a/common/version.c
+++ b/common/version.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * 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 fd95f83..fd67933 100644
--- a/demux/codec_tags.c
+++ b/demux/codec_tags.c
@@ -60,8 +60,7 @@ static const struct mp_codec_tag mp_codec_tags[] = {
{0}
};
-#define HAVE_QT_TAGS (LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(55, 25, 100) && \
- LIBAVFORMAT_VERSION_MICRO >= 100)
+#define HAVE_QT_TAGS (LIBAVFORMAT_VERSION_MICRO >= 100)
static const char *lookup_tag(int type, uint32_t tag)
{
diff --git a/demux/demux.c b/demux/demux.c
index 930d19f..bb36705 100644
--- a/demux/demux.c
+++ b/demux/demux.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
@@ -54,6 +53,7 @@ extern const demuxer_desc_t demuxer_desc_libass;
extern const demuxer_desc_t demuxer_desc_subreader;
extern const demuxer_desc_t demuxer_desc_playlist;
extern const demuxer_desc_t demuxer_desc_disc;
+extern const demuxer_desc_t demuxer_desc_rar;
/* 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
@@ -72,6 +72,7 @@ const demuxer_desc_t *const demuxer_list[] = {
&demuxer_desc_libass,
#endif
&demuxer_desc_matroska,
+ &demuxer_desc_rar,
&demuxer_desc_lavf,
&demuxer_desc_mf,
&demuxer_desc_playlist,
@@ -121,6 +122,9 @@ struct demux_internal {
int seek_flags; // flags for next seek (if seeking==true)
double seek_pts;
+ bool refresh_seeks_enabled;
+ bool start_refresh_seek;
+
// Cached state.
bool force_cache_update;
double time_length;
@@ -140,7 +144,10 @@ struct demux_stream {
// all fields are protected by in->lock
bool selected; // user wants packets from this stream
bool active; // try to keep at least 1 packet queued
+ // if false, this stream is disabled, or passively
+ // read (like subtitles)
bool eof; // end of demuxed stream? (true if all buffer empty)
+ bool refreshing;
size_t packs; // number of packets in buffer
size_t bytes; // total bytes of packets in buffer
double base_ts; // timestamp of the last packet returned to decoder
@@ -148,6 +155,7 @@ struct demux_stream {
double last_br_ts; // timestamp of last packet bitrate was calculated
size_t last_br_bytes; // summed packet sizes since last bitrate calculation
double bitrate;
+ int64_t last_pos;
struct demux_packet *head;
struct demux_packet *tail;
};
@@ -179,6 +187,8 @@ static void ds_flush(struct demux_stream *ds)
ds->bitrate = -1;
ds->eof = false;
ds->active = false;
+ ds->refreshing = false;
+ ds->last_pos = -1;
}
struct sh_stream *new_sh_stream(demuxer_t *demuxer, enum stream_type type)
@@ -238,6 +248,15 @@ void free_demuxer(demuxer_t *demuxer)
talloc_free(demuxer);
}
+void free_demuxer_and_stream(struct demuxer *demuxer)
+{
+ if (!demuxer)
+ return;
+ struct stream *s = demuxer->stream;
+ free_demuxer(demuxer);
+ free_stream(s);
+}
+
// Start the demuxer thread, which reads ahead packets on its own.
void demux_start_thread(struct demuxer *demuxer)
{
@@ -297,7 +316,17 @@ int demux_add_packet(struct sh_stream *stream, demux_packet_t *dp)
}
struct demux_internal *in = ds->in;
pthread_mutex_lock(&in->lock);
- if (!ds->selected || in->seeking) {
+
+ bool drop = false;
+ if (ds->refreshing) {
+ // Resume reading once the old position was reached (i.e. we start
+ // returning packets where we left off before the refresh).
+ drop = dp->pos <= ds->last_pos;
+ if (dp->pos >= ds->last_pos)
+ ds->refreshing = false;
+ }
+
+ if (!ds->selected || in->seeking || drop) {
pthread_mutex_unlock(&in->lock);
talloc_free(dp);
return 0;
@@ -306,6 +335,7 @@ int demux_add_packet(struct sh_stream *stream, demux_packet_t *dp)
dp->stream = stream->index;
dp->next = NULL;
+ ds->last_pos = dp->pos;
ds->packs++;
ds->bytes += dp->len;
if (ds->tail) {
@@ -403,7 +433,6 @@ static bool read_packet(struct demux_internal *in)
for (int n = 0; n < in->d_buffer->num_streams; n++) {
struct demux_stream *ds = in->d_buffer->streams[n]->ds;
ds->eof = true;
- ds->active = false;
}
// If we had EOF previously, then don't wakeup (avoids wakeup loop)
if (!in->last_eof) {
@@ -438,6 +467,43 @@ static void ds_get_packets(struct demux_stream *ds)
}
}
+// An obscure mechanism to get stream switching to be executed faster.
+// On a switch, it seeks back, and then grabs all packets that were
+// "missing" from the packet queue of the newly selected stream.
+static void start_refreshing(struct demux_internal *in)
+{
+ struct demuxer *demux = in->d_thread;
+
+ 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;
+ if (ds->type == STREAM_VIDEO || ds->type == STREAM_AUDIO)
+ start_ts = MP_PTS_MIN(start_ts, ds->base_ts);
+ }
+
+ if (start_ts == MP_NOPTS_VALUE || !demux->desc->seek || !demux->seekable ||
+ 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;
+ // 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)
+ ds->refreshing = true;
+ }
+
+ 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);
+
+ pthread_mutex_lock(&in->lock);
+}
+
static void execute_trackswitch(struct demux_internal *in)
{
in->tracks_switched = false;
@@ -448,6 +514,9 @@ static void execute_trackswitch(struct demux_internal *in)
in->d_thread->desc->control(in->d_thread, DEMUXER_CTRL_SWITCHED_TRACKS, 0);
pthread_mutex_lock(&in->lock);
+
+ if (in->start_refresh_seek)
+ start_refreshing(in);
}
static void execute_seek(struct demux_internal *in)
@@ -761,6 +830,8 @@ static void demux_copy(struct demuxer *dst, struct demuxer *src)
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;
}
@@ -882,8 +953,6 @@ static struct demuxer *open_given_type(struct mpv_global *global,
mp_verbose(log, "Trying demuxer: %s (force-level: %s)\n",
desc->name, d_level(check));
- in->d_thread->params = params; // temporary during open()
-
if (stream->seekable) // not for DVD/BD/DVB in particular
stream_seek(stream, 0);
@@ -891,6 +960,7 @@ static struct demuxer *open_given_type(struct mpv_global *global,
// will flush previous peeked data.
stream_peek(stream, STREAM_BUFFER_SIZE);
+ in->d_thread->params = params; // temporary during open()
int ret = demuxer->desc->open(in->d_thread, check);
if (ret >= 0) {
in->d_thread->params = NULL;
@@ -921,14 +991,15 @@ static const int d_normal[] = {DEMUX_CHECK_NORMAL, DEMUX_CHECK_UNSAFE, -1};
static const int d_request[] = {DEMUX_CHECK_REQUEST, -1};
static const int d_force[] = {DEMUX_CHECK_FORCE, -1};
-struct demuxer *demux_open(struct stream *stream, char *force_format,
- struct demuxer_params *params,
+// params can be NULL
+struct demuxer *demux_open(struct stream *stream, struct demuxer_params *params,
struct mpv_global *global)
{
const int *check_levels = d_normal;
const struct demuxer_desc *check_desc = NULL;
struct mp_log *log = mp_log_new(NULL, global->log, "!demux");
struct demuxer *demuxer = NULL;
+ char *force_format = params ? params->force_format : NULL;
if (!force_format)
force_format = stream->demuxer;
@@ -970,6 +1041,26 @@ done:
return demuxer;
}
+// Convenience function: open the stream, enable the cache (according to params
+// and global opts.), open the demuxer.
+// (use free_demuxer_and_stream() to free the underlying stream too)
+struct demuxer *demux_open_url(const char *url,
+ struct demuxer_params *params,
+ struct mp_cancel *cancel,
+ struct mpv_global *global)
+{
+ struct MPOpts *opts = global->opts;
+ struct stream *s = stream_create(url, STREAM_READ, cancel, global);
+ if (!s)
+ return NULL;
+ if (!(params && params->disable_cache))
+ stream_enable_cache(&s, &opts->stream_cache);
+ struct demuxer *d = demux_open(s, params, global);
+ if (!d)
+ free_stream(s);
+ return d;
+}
+
static void flush_locked(demuxer_t *demuxer)
{
for (int n = 0; n < demuxer->num_streams; n++)
@@ -1031,6 +1122,18 @@ int demux_seek(demuxer_t *demuxer, double rel_seek_secs, int flags)
return 1;
}
+// Enable doing a "refresh seek" on the next stream switch.
+// Note that this by design does not disable ongoing refresh seeks, and
+// does not affect previous stream switch commands (even if they were
+// asynchronous).
+void demux_set_enable_refresh_seeks(struct demuxer *demuxer, bool enabled)
+{
+ struct demux_internal *in = demuxer->in;
+ pthread_mutex_lock(&in->lock);
+ in->refresh_seeks_enabled = enabled;
+ pthread_mutex_unlock(&in->lock);
+}
+
struct sh_stream *demuxer_stream_by_demuxer_id(struct demuxer *d,
enum stream_type t, int id)
{
@@ -1057,16 +1160,19 @@ void demuxer_switch_track(struct demuxer *demuxer, enum stream_type type,
void demuxer_select_track(struct demuxer *demuxer, struct sh_stream *stream,
bool selected)
{
- // don't flush buffers if stream is already selected / unselected
- pthread_mutex_lock(&demuxer->in->lock);
+ 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->start_refresh_seek = true;
update = true;
}
- pthread_mutex_unlock(&demuxer->in->lock);
+ pthread_mutex_unlock(&in->lock);
if (update)
demux_control(demuxer, DEMUXER_CTRL_SWITCHED_TRACKS, NULL);
}
@@ -1277,10 +1383,8 @@ static int cached_demux_control(struct demux_internal *in, int cmd, void *arg)
struct demux_stream *ds = in->d_user->streams[n]->ds;
if (ds->active) {
r->underrun |= !ds->head && !ds->eof;
- if (!ds->eof) {
- r->ts_range[0] = MP_PTS_MAX(r->ts_range[0], ds->base_ts);
- r->ts_range[1] = MP_PTS_MIN(r->ts_range[1], ds->last_ts);
- }
+ r->ts_range[0] = MP_PTS_MAX(r->ts_range[0], ds->base_ts);
+ r->ts_range[1] = MP_PTS_MIN(r->ts_range[1], ds->last_ts);
num_packets += ds->packs;
}
}
diff --git a/demux/demux.h b/demux/demux.h
index 0078ca8..f3ed97c 100644
--- a/demux/demux.h
+++ b/demux/demux.h
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_DEMUXER_H
@@ -68,7 +67,6 @@ struct demux_ctrl_stream_ctrl {
#define SEEK_FACTOR (1 << 1) // argument is in range [0,1]
#define SEEK_FORWARD (1 << 2) // prefer later time if not exact
#define SEEK_BACKWARD (1 << 3) // prefer earlier time if not exact
-#define SEEK_SUBPREROLL (1 << 4) // try to get more subtitle packets
#define SEEK_HR (1 << 5) // hr-seek (this is a weak hint only)
// Strictness of the demuxer open format check.
@@ -166,11 +164,13 @@ typedef struct demux_attachment
} demux_attachment_t;
struct demuxer_params {
+ char *force_format;
int matroska_num_wanted_uids;
struct matroska_segment_uid *matroska_wanted_uids;
int matroska_wanted_segment;
bool *matroska_was_valid;
bool expect_subtitle;
+ bool disable_cache; // demux_open_url() only
};
typedef struct demuxer {
@@ -186,6 +186,17 @@ typedef struct demuxer {
// 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
+ // monotonically increasing
+ // - seeking leaves packet positions invariant
+ bool allow_refresh_seeks;
+ // The file data was fully read, and there is no need to keep the stream
+ // open, keep the cache active, or to run the demuxer thread. Generating
+ // packets is not slow either (unlike e.g. libavdevice pseudo-demuxers).
+ // Typical examples: text subtitles, playlists
+ bool fully_read;
// Bitmask of DEMUX_EVENT_*
int events;
@@ -231,6 +242,7 @@ typedef struct {
} demux_program_t;
void free_demuxer(struct demuxer *demuxer);
+void free_demuxer_and_stream(struct demuxer *demuxer);
int demux_add_packet(struct sh_stream *stream, demux_packet_t *dp);
@@ -243,10 +255,15 @@ struct demux_packet *demux_read_any_packet(struct demuxer *demuxer);
struct sh_stream *new_sh_stream(struct demuxer *demuxer, enum stream_type type);
-struct demuxer *demux_open(struct stream *stream, char *force_format,
- struct demuxer_params *params,
+struct demuxer *demux_open(struct stream *stream, struct demuxer_params *params,
struct mpv_global *global);
+struct mp_cancel;
+struct demuxer *demux_open_url(const char *url,
+ struct demuxer_params *params,
+ struct mp_cancel *cancel,
+ struct mpv_global *global);
+
void demux_start_thread(struct demuxer *demuxer);
void demux_stop_thread(struct demuxer *demuxer);
void demux_set_wakeup_cb(struct demuxer *demuxer, void (*cb)(void *ctx), void *ctx);
@@ -255,6 +272,7 @@ bool demux_cancel_test(struct demuxer *demuxer);
void demux_flush(struct demuxer *demuxer);
int demux_seek(struct demuxer *demuxer, double rel_seek_secs, int flags);
+void demux_set_enable_refresh_seeks(struct demuxer *demuxer, bool enabled);
int demux_control(struct demuxer *demuxer, int cmd, void *arg);
diff --git a/demux/demux_cue.c b/demux/demux_cue.c
index 969c3eb..93518c9 100644
--- a/demux/demux_cue.c
+++ b/demux/demux_cue.c
@@ -1,21 +1,20 @@
/*
- * This file is part of MPlayer.
- *
* Original author: Uoti Urpala
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
@@ -192,10 +191,10 @@ static bool try_open(struct timeline *tl, char *filename)
|| bstrcasecmp(bstr0(tl->demuxer->filename), bfilename) == 0)
return false;
- struct stream *s = stream_open(filename, tl->global);
+ struct stream *s = stream_create(filename, STREAM_READ, tl->cancel, tl->global);
if (!s)
return false;
- struct demuxer *d = demux_open(s, NULL, NULL, tl->global);
+ struct demuxer *d = demux_open(s, NULL, tl->global);
// Since .bin files are raw PCM data with no headers, we have to explicitly
// open them. Also, try to avoid to open files that are most likely not .bin
// files, as that would only play noise. Checking the file extension is
@@ -204,7 +203,8 @@ static bool try_open(struct timeline *tl, char *filename)
// CD sector size (2352 bytes)
if (!d && bstr_case_endswith(bfilename, bstr0(".bin"))) {
MP_WARN(tl, "CUE: Opening as BIN file!\n");
- d = demux_open(s, "rawaudio", NULL, tl->global);
+ struct demuxer_params p = {.force_format = "rawaudio"};
+ d = demux_open(s, &p, tl->global);
}
if (d) {
add_source(tl, d);
@@ -433,6 +433,7 @@ static int try_open_file(struct demuxer *demuxer, enum demux_check check)
}
struct priv *p = talloc_zero(demuxer, struct priv);
demuxer->priv = p;
+ demuxer->fully_read = true;
p->data = stream_read_complete(s, demuxer, 1000000);
if (p->data.start == NULL)
return -1;
diff --git a/demux/demux_disc.c b/demux/demux_disc.c
index afa8de2..3cbd01a 100644
--- a/demux/demux_disc.c
+++ b/demux/demux_disc.c
@@ -293,9 +293,10 @@ static int d_open(demuxer_t *demuxer, enum demux_check check)
if (check != DEMUX_CHECK_FORCE)
return -1;
- char *demux = "+lavf";
+ struct demuxer_params params = {.force_format = "+lavf"};
+
if (demuxer->stream->uncached_type == STREAMTYPE_CDDA)
- demux = "+rawaudio";
+ params.force_format = "+rawaudio";
char *t = NULL;
stream_control(demuxer->stream, STREAM_CTRL_GET_DISC_NAME, &t);
@@ -309,7 +310,7 @@ static int d_open(demuxer_t *demuxer, enum demux_check check)
stream_peek(demuxer->stream, 1);
reset_pts(demuxer);
- p->slave = demux_open(demuxer->stream, demux, NULL, demuxer->global);
+ p->slave = demux_open(demuxer->stream, &params, demuxer->global);
if (!p->slave)
return -1;
diff --git a/demux/demux_edl.c b/demux/demux_edl.c
index c9f8f34..3bda4f4 100644
--- a/demux/demux_edl.c
+++ b/demux/demux_edl.c
@@ -1,21 +1,20 @@
/*
- * This file is part of MPlayer.
- *
* Original author: Uoti Urpala
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
@@ -134,22 +133,6 @@ error:
return NULL;
}
-static struct demuxer *open_file(char *filename, struct timeline *tl)
-{
- struct MPOpts *opts = tl->global->opts;
- struct demuxer *d = NULL;
- struct stream *s = stream_open(filename, tl->global);
- if (s) {
- stream_enable_cache(&s, &opts->stream_cache);
- d = demux_open(s, NULL, NULL, tl->global);
- }
- if (!d) {
- MP_ERR(tl, "EDL: Could not open source file '%s'.\n", filename);
- free_stream(s);
- }
- return d;
-}
-
static struct demuxer *open_source(struct timeline *tl, char *filename)
{
for (int n = 0; n < tl->num_sources; n++) {
@@ -157,9 +140,12 @@ static struct demuxer *open_source(struct timeline *tl, char *filename)
if (strcmp(d->stream->url, filename) == 0)
return d;
}
- struct demuxer *d = open_file(filename, tl);
- if (d)
+ struct demuxer *d = demux_open_url(filename, NULL, tl->cancel, tl->global);
+ if (d) {
MP_TARRAY_APPEND(tl, tl->sources, tl->num_sources, d);
+ } else {
+ MP_ERR(tl, "EDL: Could not open source file '%s'.\n", filename);
+ }
return d;
}
@@ -311,6 +297,7 @@ static int try_open_file(struct demuxer *demuxer, enum demux_check check)
{
struct priv *p = talloc_zero(demuxer, struct priv);
demuxer->priv = p;
+ demuxer->fully_read = true;
struct stream *s = demuxer->stream;
if (s->uncached_type == STREAMTYPE_EDL) {
diff --git a/demux/demux_lavf.c b/demux/demux_lavf.c
index 208bd60..c148695 100644
--- a/demux/demux_lavf.c
+++ b/demux/demux_lavf.c
@@ -1,21 +1,20 @@
/*
* Copyright (C) 2004 Michael Niedermayer <michaelni@gmx.at>
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
@@ -32,12 +31,8 @@
#include <libavutil/avutil.h>
#include <libavutil/avstring.h>
#include <libavutil/mathematics.h>
-#if HAVE_AVCODEC_REPLAYGAIN_SIDE_DATA
-# include <libavutil/replaygain.h>
-#endif
-#if HAVE_AV_DISPLAYMATRIX
-# include <libavutil/display.h>
-#endif
+#include <libavutil/replaygain.h>
+#include <libavutil/display.h>
#include <libavutil/opt.h>
#include "options/options.h"
@@ -70,6 +65,7 @@ struct demux_lavf_opts {
char *format;
char *cryptokey;
char **avopts;
+ int hacks;
int genptsmode;
};
@@ -83,6 +79,7 @@ const struct m_sub_options demux_lavf_conf = {
OPT_FLAG("allow-mimetype", allow_mimetype, 0),
OPT_INTRANGE("probescore", probescore, 0, 1, AVPROBE_SCORE_MAX),
OPT_STRING("cryptokey", cryptokey, 0),
+ OPT_FLAG("hacks", hacks, 0),
OPT_CHOICE("genpts-mode", genptsmode, 0,
({"lavf", 1}, {"no", 0})),
OPT_KEYVALUELIST("o", avopts, 0),
@@ -91,6 +88,7 @@ const struct m_sub_options demux_lavf_conf = {
.size = sizeof(struct demux_lavf_opts),
.defaults = &(const struct demux_lavf_opts){
.allow_mimetype = 1,
+ .hacks = 1,
// AVPROBE_SCORE_MAX/4 + 1 is the "recommended" limit. Below that, the
// user is supposed to retry with larger probe sizes until a higher
// value is reached.
@@ -103,16 +101,21 @@ struct format_hack {
const char *mime_type;
int probescore;
float analyzeduration;
+ unsigned int if_flags; // additional AVInputFormat.flags flags
bool max_probe : 1; // use probescore only if max. probe size reached
bool ignore : 1; // blacklisted
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 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).
bool clear_filepos : 1;
};
#define BLACKLIST(fmt) {fmt, .ignore = true}
+#define TEXTSUB(fmt) {fmt, .fully_read = true}
+#define IMAGEFMT(fmt) {fmt, .image_format = true}
static const struct format_hack format_hacks[] = {
// for webradios
@@ -127,18 +130,24 @@ static const struct format_hack format_hacks[] = {
{"mpeg", .use_stream_ids = true},
{"mpegts", .use_stream_ids = true},
+ // In theory, such streams might contain timestamps, but virtually none do.
+ {"h264", .if_flags = AVFMT_NOTIMESTAMPS },
+ {"hevc", .if_flags = AVFMT_NOTIMESTAMPS },
+
+ TEXTSUB("aqtitle"), TEXTSUB("ass"), 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"),
+
// Useless non-sense, sometimes breaks MLP2 subreader.c fallback
BLACKLIST("tty"),
// Let's open files with extremely generic extensions (.bin) with a
// demuxer that doesn't have a probe function! NO.
BLACKLIST("bin"),
- // Image demuxers, disabled in favor of demux_mf (for now):
- BLACKLIST("image"),
+ // Useless, does not work with custom streams.
BLACKLIST("image2"),
- BLACKLIST("image2pipe"),
- BLACKLIST("bmp_pipe"), BLACKLIST("dpx_pipe"), BLACKLIST("exr_pipe"),
- BLACKLIST("j2k_pipe"), BLACKLIST("png_pipe"), BLACKLIST("tiff_pipe"),
- BLACKLIST("jpeg_pipe"),
+ // Image demuxers ("<name>_pipe" is detected explicitly)
+ IMAGEFMT("image2pipe"),
{0}
};
@@ -146,10 +155,10 @@ typedef struct lavf_priv {
char *filename;
struct format_hack format_hack;
AVInputFormat *avif;
+ int avif_flags;
AVFormatContext *avfc;
AVIOContext *pb;
int64_t last_pts;
- bool init_pts;
struct sh_stream **streams; // NULL for unknown streams
int num_streams;
int cur_program;
@@ -345,6 +354,8 @@ static int lavf_check_file(demuxer_t *demuxer, enum demux_check check)
for (int n = 0; format_hacks[n].ff_name; n++) {
const struct format_hack *entry = &format_hacks[n];
+ if (!lavfdopts->hacks)
+ continue;
if (!matches_avinputformat_name(priv, entry->ff_name))
continue;
if (entry->mime_type && strcasecmp(entry->mime_type, mime_type) != 0)
@@ -378,6 +389,13 @@ static int lavf_check_file(demuxer_t *demuxer, enum demux_check check)
return -1;
}
+ if (bstr_endswith0(bstr0(priv->avif->name), "_pipe")) {
+ MP_VERBOSE(demuxer, "Assuming this is an image format.\n");
+ priv->format_hack.image_format = true;
+ }
+
+ priv->avif_flags = priv->avif->flags | priv->format_hack.if_flags;
+
demuxer->filetype = priv->avif->name;
return 0;
@@ -416,7 +434,6 @@ static void select_tracks(struct demuxer *demuxer, int start)
static void export_replaygain(demuxer_t *demuxer, sh_audio_t *sh, AVStream *st)
{
-#if HAVE_AVCODEC_REPLAYGAIN_SIDE_DATA
for (int i = 0; i < st->nb_side_data; i++) {
AVReplayGain *av_rgain;
struct replaygain_data *rgain;
@@ -442,7 +459,6 @@ static void export_replaygain(demuxer_t *demuxer, sh_audio_t *sh, AVStream *st)
sh->replaygain_data = rgain;
}
-#endif
}
// Return a dictionary entry as (decimal) integer.
@@ -520,6 +536,8 @@ static void handle_stream(demuxer_t *demuxer, int i)
av_q2d(st->codec->time_base) *
st->codec->ticks_per_frame);
sh_video->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);
@@ -527,19 +545,9 @@ static void handle_stream(demuxer_t *demuxer, int i)
sh_video->aspect = codec->width * codec->sample_aspect_ratio.num
/ (float)(codec->height * codec->sample_aspect_ratio.den);
- sh_video->bitrate = codec->bit_rate;
- if (sh_video->bitrate == 0)
- sh_video->bitrate = avfc->bit_rate;
-
-#if HAVE_AV_DISPLAYMATRIX
uint8_t *sd = av_stream_get_side_data(st, AV_PKT_DATA_DISPLAYMATRIX, NULL);
if (sd)
sh_video->rotate = -av_display_rotation_get((uint32_t *)sd);
-#else
- int rot = dict_get_decimal(st->metadata, "rotate", -1);
- if (rot >= 0)
- sh_video->rotate = rot;
-#endif
sh_video->rotate = ((sh_video->rotate % 360) + 360) % 360;
// This also applies to vfw-muxed mkv, but we can't detect these easily.
@@ -559,8 +567,6 @@ static void handle_stream(demuxer_t *demuxer, int i)
memcpy(sh_sub->extradata, codec->extradata, codec->extradata_size);
sh_sub->extradata_len = codec->extradata_size;
}
- sh_sub->w = codec->width;
- sh_sub->h = codec->height;
if (matches_avinputformat_name(priv, "microdvd")) {
AVRational r;
@@ -578,16 +584,10 @@ static void handle_stream(demuxer_t *demuxer, int i)
break;
}
case AVMEDIA_TYPE_ATTACHMENT: {
- AVDictionaryEntry *ftag = av_dict_get(st->metadata, "filename",
- NULL, 0);
+ AVDictionaryEntry *ftag = av_dict_get(st->metadata, "filename", NULL, 0);
char *filename = ftag ? ftag->value : NULL;
- char *mimetype = NULL;
- switch (st->codec->codec_id) {
- case AV_CODEC_ID_TTF: mimetype = "application/x-truetype-font"; break;
-#if LIBAVFORMAT_VERSION_MICRO >= 100
- case AV_CODEC_ID_OTF: mimetype = "application/vnd.ms-opentype"; break;
-#endif
- }
+ AVDictionaryEntry *mt = av_dict_get(st->metadata, "mimetype", NULL, 0);
+ char *mimetype = mt ? mt->value : NULL;
if (mimetype) {
demuxer_add_attachment(demuxer, bstr0(filename), bstr0(mimetype),
(struct bstr){codec->extradata,
@@ -619,6 +619,7 @@ static void handle_stream(demuxer_t *demuxer, int i)
sh->hls_bitrate = dict_get_decimal(st->metadata, "variant_bitrate", 0);
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);
}
select_tracks(demuxer, i);
@@ -635,7 +636,6 @@ static void add_new_streams(demuxer_t *demuxer)
static void update_metadata(demuxer_t *demuxer, AVPacket *pkt)
{
-#if HAVE_AVFORMAT_METADATA_UPDATE_FLAG
lavf_priv_t *priv = demuxer->priv;
if (priv->avfc->event_flags & AVFMT_EVENT_FLAG_METADATA_UPDATED) {
mp_tags_copy_from_av_dictionary(demuxer->metadata, priv->avfc->metadata);
@@ -652,24 +652,6 @@ static void update_metadata(demuxer_t *demuxer, AVPacket *pkt)
}
}
}
-#elif HAVE_AVCODEC_METADATA_UPDATE_SIDE_DATA
- lavf_priv_t *priv = demuxer->priv;
- int md_size;
- const uint8_t *md;
- if (!pkt)
- return;
- md = av_packet_get_side_data(pkt, AV_PKT_DATA_METADATA_UPDATE, &md_size);
- if (md && priv->merge_track_metadata) {
- AVDictionary *dict = NULL;
- av_packet_unpack_dictionary(md, md_size, &dict);
- if (dict) {
- mp_tags_clear(demuxer->metadata);
- mp_tags_copy_from_av_dictionary(demuxer->metadata, dict);
- av_dict_free(&dict);
- demux_changed(demuxer, DEMUX_EVENT_METADATA);
- }
- }
-#endif
}
static int interrupt_cb(void *ctx)
@@ -731,7 +713,7 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
AVDictionary *dopts = NULL;
- if ((priv->avif->flags & AVFMT_NOFILE) ||
+ if ((priv->avif_flags & AVFMT_NOFILE) ||
demuxer->stream->type == STREAMTYPE_AVDEVICE ||
priv->format_hack.no_stream)
{
@@ -814,11 +796,15 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
mp_tags_copy_from_av_dictionary(demuxer->metadata, avfc->metadata);
update_metadata(demuxer, NULL);
- demuxer->ts_resets_possible = priv->avif->flags & AVFMT_TS_DISCONT;
+ demuxer->ts_resets_possible =
+ priv->avif_flags & (AVFMT_TS_DISCONT | AVFMT_NOTIMESTAMPS);
demuxer->start_time = priv->avfc->start_time == AV_NOPTS_VALUE ?
0 : (double)priv->avfc->start_time / AV_TIME_BASE;
+ demuxer->allow_refresh_seeks = matches_avinputformat_name(priv, "mp4");
+ demuxer->fully_read = priv->format_hack.fully_read;
+
return 0;
}
@@ -856,12 +842,6 @@ static int demux_lavf_fill_buffer(demuxer_t *demux)
return 1;
}
- if (!priv->init_pts && (priv->avfc->flags & AVFMT_NOTIMESTAMPS)) {
- if (pkt->pts == AV_NOPTS_VALUE && pkt->dts == AV_NOPTS_VALUE)
- pkt->dts = 0;
- priv->init_pts = true;
- }
-
if (pkt->pts != AV_NOPTS_VALUE)
dp->pts = pkt->pts * av_q2d(st->time_base);
if (pkt->dts != AV_NOPTS_VALUE)
@@ -890,8 +870,6 @@ static void demux_seek_lavf(demuxer_t *demuxer, double rel_seek_secs, int flags)
lavf_priv_t *priv = demuxer->priv;
int avsflags = 0;
- priv->init_pts = false;
-
if (flags & SEEK_ABSOLUTE)
priv->last_pts = 0;
else if (rel_seek_secs < 0)
@@ -907,7 +885,7 @@ static void demux_seek_lavf(demuxer_t *demuxer, double rel_seek_secs, int flags)
int64_t end = 0;
stream_control(s, STREAM_CTRL_GET_SIZE, &end);
if (end > 0 && demuxer->ts_resets_possible &&
- !(priv->avif->flags & AVFMT_NO_BYTE_SEEK))
+ !(priv->avif_flags & AVFMT_NO_BYTE_SEEK))
{
avsflags |= AVSEEK_FLAG_BYTE;
priv->last_pts = end * rel_seek_secs;
@@ -920,48 +898,35 @@ static void demux_seek_lavf(demuxer_t *demuxer, double rel_seek_secs, int flags)
priv->last_pts += rel_seek_secs * AV_TIME_BASE;
}
+ int r;
if (!priv->avfc->iformat->read_seek2) {
// Normal seeking.
- int r = av_seek_frame(priv->avfc, -1, priv->last_pts, avsflags);
+ r = av_seek_frame(priv->avfc, -1, priv->last_pts, 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;
- av_seek_frame(priv->avfc, -1, priv->last_pts, avsflags);
+ r = av_seek_frame(priv->avfc, -1, priv->last_pts, avsflags);
}
} else {
// av_seek_frame() won't work. Use "new" seeking API. We don't use this
// API by default, because there are some major issues.
// Set max_ts==ts, so that demuxing starts from an earlier position in
// the worst case.
- // To make this horrible situation even worse, some lavf demuxers have
- // broken timebase handling (everything that uses
- // ff_subtitles_queue_seek()), and always uses the stream timebase. So
- // we use the timebase and stream index of the first enabled stream
- // (i.e. a stream which can participate in seeking).
- int stream_index = -1;
- AVRational time_base = {1, AV_TIME_BASE};
- for (int n = 0; n < priv->num_streams; n++) {
- struct sh_stream *stream = priv->streams[n];
- AVStream *st = priv->avfc->streams[n];
- if (stream && st->discard != AVDISCARD_ALL) {
- stream_index = n;
- time_base = st->time_base;
- break;
- }
- }
- int64_t pts = priv->last_pts;
- if (pts != AV_NOPTS_VALUE)
- pts = pts / (double)AV_TIME_BASE * av_q2d(av_inv_q(time_base));
- int r = avformat_seek_file(priv->avfc, stream_index, INT64_MIN,
- pts, pts, avsflags);
+ r = avformat_seek_file(priv->avfc, -1, INT64_MIN,
+ priv->last_pts, priv->last_pts, avsflags);
// Similar issue as in the normal seeking codepath.
if (r < 0) {
- avformat_seek_file(priv->avfc, stream_index, INT64_MIN,
- pts, INT64_MAX, avsflags);
+ r = avformat_seek_file(priv->avfc, -1, INT64_MIN,
+ priv->last_pts, INT64_MAX, avsflags);
}
}
+ if (r < 0) {
+ char buf[180];
+ av_strerror(r, buf, sizeof(buf));
+ MP_VERBOSE(demuxer, "Seek failed (%s)\n", buf);
+ }
}
static int demux_lavf_control(demuxer_t *demuxer, int cmd, void *arg)
diff --git a/demux/demux_libass.c b/demux/demux_libass.c
index a528f73..b83408b 100644
--- a/demux/demux_libass.c
+++ b/demux/demux_libass.c
@@ -99,6 +99,7 @@ static int d_check_file(struct demuxer *demuxer, enum demux_check check)
sh->sub->extradata_len = cbuf.len;
demuxer->seekable = true;
+ demuxer->fully_read = true;
return 0;
}
diff --git a/demux/demux_mf.c b/demux/demux_mf.c
index 4fb5f2a..ab703c0 100644
--- a/demux/demux_mf.c
+++ b/demux/demux_mf.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
diff --git a/demux/demux_mkv.c b/demux/demux_mkv.c
index 9cc521f..b4ae042 100644
--- a/demux/demux_mkv.c
+++ b/demux/demux_mkv.c
@@ -4,27 +4,27 @@
* Based on the one written by Ronald Bultje for gstreamer
* and on demux_mkv.cpp from Moritz Bunkus.
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <stdio.h>
#include <inttypes.h>
#include <stdbool.h>
+#include <math.h>
#include <assert.h>
#include <libavutil/common.h>
@@ -44,6 +44,7 @@
#include "talloc.h"
#include "common/av_common.h"
#include "options/options.h"
+#include "options/m_option.h"
#include "misc/bstr.h"
#include "stream/stream.h"
#include "video/csputils.h"
@@ -153,7 +154,6 @@ typedef struct mkv_demuxer {
int64_t segment_start, segment_end;
double duration, last_pts;
- uint64_t last_filepos;
mkv_track_t **tracks;
int num_tracks;
@@ -186,6 +186,30 @@ typedef struct mkv_demuxer {
bool eof_warning;
} mkv_demuxer_t;
+#define OPT_BASE_STRUCT struct demux_mkv_opts
+struct demux_mkv_opts {
+ int subtitle_preroll;
+ double subtitle_preroll_secs;
+ int probe_duration;
+ int fix_timestamps;
+};
+
+const struct m_sub_options demux_mkv_conf = {
+ .opts = (const m_option_t[]) {
+ OPT_FLAG("subtitle-preroll", subtitle_preroll, 0),
+ OPT_DOUBLE("subtitle-preroll-secs", subtitle_preroll_secs,
+ M_OPT_MIN, .min = 0),
+ OPT_FLAG("probe-video-duration", probe_duration, 0),
+ OPT_FLAG("fix-timestamps", fix_timestamps, 0),
+ {0}
+ },
+ .size = sizeof(struct demux_mkv_opts),
+ .defaults = &(const struct demux_mkv_opts){
+ .subtitle_preroll_secs = 1.0,
+ .fix_timestamps = 1,
+ },
+};
+
#define REALHEADER_SIZE 16
#define RVPROPERTIES_SIZE 34
#define RAPROPERTIES4_SIZE 56
@@ -1753,6 +1777,7 @@ static int read_mkv_segment_header(demuxer_t *demuxer, int64_t *segment_end)
static int demux_mkv_open(demuxer_t *demuxer, enum demux_check check)
{
stream_t *s = demuxer->stream;
+ struct MPOpts *opts = demuxer->opts;
mkv_demuxer_t *mkv_d;
int64_t start_pos;
int64_t end_pos;
@@ -1836,8 +1861,9 @@ static int demux_mkv_open(demuxer_t *demuxer, enum demux_check check)
process_tags(demuxer);
display_create_tracks(demuxer);
add_coverart(demuxer);
+ demuxer->allow_refresh_seeks = true;
- if (demuxer->opts->mkv_probe_duration)
+ if (opts->demux_mkv->probe_duration)
probe_last_timestamp(demuxer);
return 0;
@@ -1993,8 +2019,8 @@ static bool handle_realaudio(demuxer_t *demuxer, mkv_track_t *track,
uint8_t ov = track->audio_buf[o / 2];
int x = (i & 1) ? iv >> 4 : iv & 0x0F;
int y = (o & 1) ? ov >> 4 : ov & 0x0F;
- track->audio_buf[o / 2] = ov & 0x0F | (o & 1 ? x << 4 : x);
- track->audio_buf[i / 2] = iv & 0x0F | (i & 1 ? y << 4 : y);
+ track->audio_buf[o / 2] = (ov & 0x0F) | (o & 1 ? x << 4 : x);
+ track->audio_buf[i / 2] = (iv & 0x0F) | (i & 1 ? y << 4 : y);
i++;
o++;
}
@@ -2026,7 +2052,7 @@ static bool handle_realaudio(demuxer_t *demuxer, mkv_track_t *track,
* audio packets in file */
dp->pts = (x * apk_usize % w) ? MP_NOPTS_VALUE :
track->audio_timestamp[x * apk_usize / w];
- dp->pos = orig->pos;
+ dp->pos = orig->pos + x;
dp->keyframe = !x; // Mark first packet as keyframe
demux_add_packet(track->stream, dp);
}
@@ -2201,6 +2227,7 @@ static void mkv_parse_and_add_packet(demuxer_t *demuxer, mkv_track_t *track,
break;
dp->buffer += len;
dp->len -= len;
+ dp->pos += len;
if (size) {
struct demux_packet *new = new_demux_packet_from(data, size);
if (!new)
@@ -2304,6 +2331,19 @@ exit:
return res;
}
+static double fix_timestamp(demuxer_t *demuxer, mkv_track_t *track, double ts)
+{
+ mkv_demuxer_t *mkv_d = demuxer->priv;
+ if (demuxer->opts->demux_mkv->fix_timestamps && track->default_duration > 0) {
+ // Assume that timestamps have been rounded to the timecode scale.
+ double quant = MPMIN(mkv_d->tc_scale / 1e9, 0.001);
+ double rts = rint(ts / track->default_duration) * track->default_duration;
+ if (fabs(rts - ts) < quant)
+ ts = rts;
+ }
+ return ts;
+}
+
static int handle_block(demuxer_t *demuxer, struct block_info *block_info)
{
mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv;
@@ -2326,7 +2366,7 @@ static int handle_block(demuxer_t *demuxer, struct block_info *block_info)
return 0;
}
- current_pts = tc / 1e9 - track->codec_delay;
+ current_pts = fix_timestamp(demuxer, track, tc / 1e9) - track->codec_delay;
if (track->type == MATROSKA_TRACK_AUDIO) {
if (mkv_d->a_skip_to_keyframe)
@@ -2356,8 +2396,8 @@ 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;
- mkv_d->last_filepos = block_info->filepos;
for (int i = 0; i < laces; i++) {
bstr block = bstr_splice(data, 0, lace_size[i]);
@@ -2369,7 +2409,7 @@ static int handle_block(demuxer_t *demuxer, struct block_info *block_info)
if (!dp)
break;
dp->keyframe = keyframe;
- dp->pos = mkv_d->last_filepos;
+ dp->pos = filepos;
/* If default_duration is 0, assume no pts value is known
* for packets after the first one (rather than all pts
* values being the same). Also, don't use it for extra
@@ -2390,6 +2430,7 @@ static int handle_block(demuxer_t *demuxer, struct block_info *block_info)
mkv_parse_and_add_packet(demuxer, track, dp);
talloc_free_children(track->parser_tmp);
+ filepos += block.len;
}
if (stream->type == STREAM_VIDEO) {
@@ -2618,35 +2659,30 @@ static int create_index_until(struct demuxer *demuxer, uint64_t timecode)
return 0;
}
+#define FLAG_BACKWARD 1
+#define FLAG_SUBPREROLL 2
static struct mkv_index *seek_with_cues(struct demuxer *demuxer, int seek_id,
int64_t target_timecode, int flags)
{
+ struct MPOpts *opts = demuxer->opts;
struct mkv_demuxer *mkv_d = demuxer->priv;
struct mkv_index *index = NULL;
- /* Find the entry in the index closest to the target timecode in the
- * give direction. If there are no such entries - we're trying to seek
- * backward from a target time before the first entry or forward from a
- * target time after the last entry - then still seek to the first/last
- * entry if that's further in the direction wanted than mkv_d->last_pts.
- */
- int64_t min_diff = target_timecode - (int64_t)(mkv_d->last_pts * 1e9 + 0.5);
- if (flags & SEEK_BACKWARD)
- min_diff = -min_diff;
- min_diff = FFMAX(min_diff, 1);
-
+ int64_t min_diff = INT64_MIN;
for (size_t i = 0; i < mkv_d->num_indexes; i++) {
if (seek_id < 0 || mkv_d->indexes[i].tnum == seek_id) {
int64_t diff =
target_timecode -
(int64_t) (mkv_d->indexes[i].timecode * mkv_d->tc_scale);
- if (flags & SEEK_BACKWARD)
+ if (flags & FLAG_BACKWARD)
diff = -diff;
- if (diff <= 0) {
- if (min_diff <= 0 && diff <= min_diff)
+ if (min_diff != INT64_MIN) {
+ if (diff <= 0) {
+ if (min_diff <= 0 && diff <= min_diff)
+ continue;
+ } else if (diff >= min_diff)
continue;
- } else if (diff >= min_diff)
- continue;
+ }
min_diff = diff;
index = mkv_d->indexes + i;
}
@@ -2654,10 +2690,10 @@ static struct mkv_index *seek_with_cues(struct demuxer *demuxer, int seek_id,
if (index) { /* We've found an entry. */
uint64_t seek_pos = index->filepos;
- if (flags & SEEK_SUBPREROLL) {
+ if (flags & FLAG_SUBPREROLL) {
// Find the cluster with the highest filepos, that has a timestamp
// still lower than min_tc.
- double secs = demuxer->opts->mkv_subtitle_preroll_secs;
+ double secs = opts->demux_mkv->subtitle_preroll_secs;
uint64_t pre = MPMIN(INT64_MAX, secs * 1e9 / mkv_d->tc_scale);
uint64_t min_tc = pre < index->timecode ? index->timecode - pre : 0;
uint64_t prev_target = 0;
@@ -2716,9 +2752,13 @@ static void demux_mkv_seek(demuxer_t *demuxer, double rel_seek_secs, int flags)
a_tnum = track->tnum;
}
}
+
+ int cueflags = (flags & SEEK_BACKWARD) ? FLAG_BACKWARD : 0;
+
mkv_d->subtitle_preroll = NUM_SUB_PREROLL_PACKETS;
- if (!st_active[STREAM_SUB] || !st_active[STREAM_VIDEO])
- flags &= ~SEEK_SUBPREROLL;
+ if (((flags & SEEK_HR) || demuxer->opts->demux_mkv->subtitle_preroll) &&
+ st_active[STREAM_SUB] && st_active[STREAM_VIDEO])
+ cueflags |= FLAG_SUBPREROLL;
// Adjust the target a little bit to catch cases where the target position
// specifies a keyframe with high, but not perfect, precision.
@@ -2734,9 +2774,9 @@ static void demux_mkv_seek(demuxer_t *demuxer, double rel_seek_secs, int flags)
if (create_index_until(demuxer, target_timecode) >= 0) {
int seek_id = st_active[STREAM_VIDEO] ? v_tnum : a_tnum;
- index = seek_with_cues(demuxer, seek_id, target_timecode, flags);
+ index = seek_with_cues(demuxer, seek_id, target_timecode, cueflags);
if (!index)
- index = seek_with_cues(demuxer, -1, target_timecode, flags);
+ index = seek_with_cues(demuxer, -1, target_timecode, cueflags);
}
if (!index)
diff --git a/demux/demux_mkv_timeline.c b/demux/demux_mkv_timeline.c
index 548f001..c7310a6 100644
--- a/demux/demux_mkv_timeline.c
+++ b/demux/demux_mkv_timeline.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
@@ -47,6 +46,7 @@
struct tl_ctx {
struct mp_log *log;
struct mpv_global *global;
+ struct timeline *tl;
struct demuxer *demuxer;
@@ -149,37 +149,6 @@ static char **find_files(const char *original_file)
return results;
}
-static int enable_cache(struct mpv_global *global, struct stream **stream,
- struct demuxer **demuxer, struct demuxer_params *params)
-{
- struct MPOpts *opts = global->opts;
-
- if (!stream_wants_cache(*stream, &opts->stream_cache))
- return 0;
-
- char *filename = talloc_strdup(NULL, (*demuxer)->filename);
- free_demuxer(*demuxer);
- free_stream(*stream);
-
- *stream = stream_open(filename, global);
- if (!*stream) {
- talloc_free(filename);
- return -1;
- }
-
- stream_enable_cache(stream, &opts->stream_cache);
-
- *demuxer = demux_open(*stream, "mkv", params, global);
- if (!*demuxer) {
- talloc_free(filename);
- free_stream(*stream);
- return -1;
- }
-
- talloc_free(filename);
- return 1;
-}
-
static bool has_source_request(struct tl_ctx *ctx,
struct matroska_segment_uid *new_uid)
{
@@ -195,20 +164,20 @@ static bool check_file_seg(struct tl_ctx *ctx, char *filename, int segment)
{
bool was_valid = false;
struct demuxer_params params = {
+ .force_format = "mkv",
.matroska_num_wanted_uids = ctx->num_sources,
.matroska_wanted_uids = ctx->uids,
.matroska_wanted_segment = segment,
.matroska_was_valid = &was_valid,
+ .disable_cache = true,
};
- struct stream *s = stream_open(filename, ctx->global);
- if (!s)
+ struct mp_cancel *cancel = ctx->tl->cancel;
+ if (mp_cancel_test(cancel))
return false;
- struct demuxer *d = demux_open(s, "mkv", &params, ctx->global);
- if (!d) {
- free_stream(s);
- return was_valid;
- }
+ struct demuxer *d = demux_open_url(filename, &params, cancel, ctx->global);
+ if (!d)
+ return false;
struct matroska_data *m = &d->matroska_data;
@@ -240,17 +209,22 @@ static bool check_file_seg(struct tl_ctx *ctx, char *filename, int segment)
MP_TARRAY_APPEND(NULL, ctx->sources, ctx->num_sources, NULL);
}
- params.matroska_wanted_uids = ctx->uids; // potentially reallocated, same data
- if (enable_cache(ctx->global, &s, &d, &params) < 0)
- continue;
+ if (stream_wants_cache(d->stream, &ctx->global->opts->stream_cache))
+ {
+ free_demuxer_and_stream(d);
+ params.disable_cache = false;
+ params.matroska_wanted_uids = ctx->uids; // potentially reallocated, same data
+ d = demux_open_url(filename, &params, cancel, ctx->global);
+ if (!d)
+ return false;
+ }
ctx->sources[i] = d;
return true;
}
}
- free_demuxer(d);
- free_stream(s);
+ free_demuxer_and_stream(d);
return was_valid;
}
@@ -538,6 +512,7 @@ void build_ordered_chapter_timeline(struct timeline *tl)
*ctx = (struct tl_ctx){
.log = tl->log,
.global = tl->global,
+ .tl = tl,
.demuxer = demuxer,
};
diff --git a/demux/demux_playlist.c b/demux/demux_playlist.c
index 9602eaf..6f73bea 100644
--- a/demux/demux_playlist.c
+++ b/demux/demux_playlist.c
@@ -15,7 +15,10 @@
* with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <stdlib.h>
+#include <string.h>
#include <strings.h>
+#include <dirent.h>
#include "common/common.h"
#include "options/options.h"
@@ -23,7 +26,7 @@
#include "common/playlist.h"
#include "options/path.h"
#include "stream/stream.h"
-#include "stream/rar.h"
+#include "osdep/io.h"
#include "demux.h"
#define PROBE_SIZE (8 * 1024)
@@ -48,6 +51,7 @@ struct pl_parser {
bool error;
bool probing;
bool force;
+ bool add_base;
enum demux_check check_level;
struct stream *real_stream;
};
@@ -189,30 +193,6 @@ static int parse_pls(struct pl_parser *p)
return 0;
}
-static int parse_rar(struct pl_parser *p)
-{
- if (RarProbe(p->s))
- return -1;
- if (p->probing)
- return 0;
-
- int count;
- rar_file_t **files;
- if (RarParse(p->s, &count, &files))
- return -1;
-
- p->pl->disable_safety = true; // make it load rar://
- char *prefix = mp_url_escape(p, p->real_stream->url, "~|");
- for (int n = 0; n < count; n++) {
- // stream_rar.c does the real work
- playlist_add_file(p->pl,
- talloc_asprintf(p, "rar://%s|%s", prefix, files[n]->name));
- RarFileDelete(files[n]);
- }
- talloc_free(files);
- return 0;
-}
-
static int parse_txt(struct pl_parser *p)
{
if (!p->force)
@@ -229,6 +209,51 @@ static int parse_txt(struct pl_parser *p)
return 0;
}
+static int cmp_filename(const void *a, const void *b)
+{
+ return strcmp(*(char **)a, *(char **)b);
+}
+
+static int parse_dir(struct pl_parser *p)
+{
+ if (p->real_stream->type != STREAMTYPE_DIR)
+ return -1;
+ if (p->probing)
+ return 0;
+
+ char *path = mp_file_get_path(p, bstr0(p->real_stream->url));
+ if (strlen(path) >= 8192)
+ return -1; // things like mount bind loops
+
+ DIR *dp = opendir(path);
+ if (!dp) {
+ MP_ERR(p, "Could not read directory.\n");
+ return -1;
+ }
+
+ char **files = NULL;
+ int num_files = 0;
+
+ struct dirent *ep;
+ while ((ep = readdir(dp))) {
+ if (strcmp(ep->d_name, ".") == 0 || strcmp(ep->d_name, "..") == 0)
+ continue;
+ MP_TARRAY_APPEND(p, files, num_files, talloc_strdup(p, ep->d_name));
+ }
+
+ if (files)
+ qsort(files, num_files, sizeof(files[0]), cmp_filename);
+
+ for (int n = 0; n < num_files; n++)
+ playlist_add_file(p->pl, mp_path_join(p, bstr0(path), bstr0(files[n])));
+
+ closedir(dp);
+
+ p->add_base = false;
+
+ return num_files > 0 ? 0 : -1;
+}
+
#define MIME_TYPES(...) \
.mime_types = (const char*const[]){__VA_ARGS__, NULL}
@@ -239,13 +264,13 @@ struct pl_format {
};
static const struct pl_format formats[] = {
+ {"directory", parse_dir},
{"m3u", parse_m3u,
MIME_TYPES("audio/mpegurl", "audio/x-mpegurl", "application/x-mpegurl")},
{"ini", parse_ref_init},
{"mov", parse_mov_rtsptext},
{"pls", parse_pls,
MIME_TYPES("audio/x-scpls")},
- {"rar", parse_rar},
{"txt", parse_txt},
};
@@ -274,6 +299,7 @@ static int open_file(struct demuxer *demuxer, enum demux_check check)
p->log = demuxer->log;
p->pl = talloc_zero(p, struct playlist);
p->real_stream = demuxer->stream;
+ p->add_base = true;
bstr probe_buf = stream_peek(demuxer->stream, PROBE_SIZE);
p->s = open_memory_stream(probe_buf.start, probe_buf.len);
@@ -295,10 +321,11 @@ static int open_file(struct demuxer *demuxer, enum demux_check check)
p->s = demuxer->stream;
p->utf16 = stream_skip_bom(p->s);
bool ok = fmt->parse(p) >= 0 && !p->error;
- if (ok)
+ if (p->add_base)
playlist_add_base_path(p->pl, mp_dirname(demuxer->filename));
demuxer->playlist = talloc_steal(demuxer, p->pl);
demuxer->filetype = fmt->name;
+ demuxer->fully_read = true;
talloc_free(p);
return ok ? 0 : -1;
}
diff --git a/demux/demux_rar.c b/demux/demux_rar.c
new file mode 100644
index 0000000..46311bc
--- /dev/null
+++ b/demux/demux_rar.c
@@ -0,0 +1,62 @@
+/*
+ * 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 "common/common.h"
+#include "common/playlist.h"
+#include "stream/stream.h"
+#include "stream/rar.h"
+#include "demux.h"
+
+static int open_file(struct demuxer *demuxer, enum demux_check check)
+{
+ if (RarProbe(demuxer->stream))
+ return -1;
+
+ int count;
+ rar_file_t **files;
+ if (RarParse(demuxer->stream, &count, &files))
+ return -1;
+
+ void *tmp = talloc_new(NULL);
+ talloc_steal(tmp, files);
+
+ struct playlist *pl = talloc_zero(demuxer, struct playlist);
+ demuxer->playlist = pl;
+
+ // make it load rar://
+ pl->disable_safety = true;
+
+ char *prefix = mp_url_escape(tmp, demuxer->stream->url, "~|");
+ for (int n = 0; n < count; n++) {
+ // stream_rar.c does the real work
+ playlist_add_file(pl,
+ talloc_asprintf(tmp, "rar://%s|%s", prefix, files[n]->name));
+ RarFileDelete(files[n]);
+ }
+
+ demuxer->filetype = "rar";
+ demuxer->fully_read = true;
+
+ talloc_free(tmp);
+ return 0;
+}
+
+const struct demuxer_desc demuxer_desc_rar = {
+ .name = "rar",
+ .desc = "Rar archive file",
+ .open = open_file,
+};
diff --git a/demux/demux_raw.c b/demux/demux_raw.c
index bd928cc..65d6b75 100644
--- a/demux/demux_raw.c
+++ b/demux/demux_raw.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
@@ -227,7 +226,6 @@ static int demux_rawvideo_open(demuxer_t *demuxer, enum demux_check check)
sh_video->fps = opts->fps;
sh_video->disp_w = width;
sh_video->disp_h = height;
- sh_video->bitrate = sh_video->fps * imgsize * 8;
struct priv *p = talloc_ptrtype(demuxer, p);
demuxer->priv = p;
diff --git a/demux/demux_subreader.c b/demux/demux_subreader.c
index 145754b..55a8542 100644
--- a/demux/demux_subreader.c
+++ b/demux/demux_subreader.c
@@ -4,21 +4,20 @@
* Copyright (c) 2001 laaz
* Some code cleanup & realloc() by A'rpi/ESP-team
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
@@ -791,6 +790,7 @@ static int d_open_file(struct demuxer *demuxer, enum demux_check check)
subdata_free(sd);
demuxer->seekable = true;
+ demuxer->fully_read = true;
return 0;
}
diff --git a/demux/ebml.c b/demux/ebml.c
index 52e0f75..d75b61e 100644
--- a/demux/ebml.c
+++ b/demux/ebml.c
@@ -4,21 +4,20 @@
* copyright (c) 2004 Aurelien Jacobs <aurel@gnuage.org>
* based on the one written by Ronald Bultje for gstreamer
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
diff --git a/demux/ebml.h b/demux/ebml.h
index 9d7a0cc..6821c1b 100644
--- a/demux/ebml.h
+++ b/demux/ebml.h
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_EBML_H
diff --git a/demux/matroska.h b/demux/matroska.h
index b4f13d9..fce4e6a 100644
--- a/demux/matroska.h
+++ b/demux/matroska.h
@@ -3,21 +3,20 @@
*
* see http://www.matroska.org/technical/specs/codecid/index.html
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_MATROSKA_H
diff --git a/demux/packet.h b/demux/packet.h
index 213ee02..784a1de 100644
--- a/demux/packet.h
+++ b/demux/packet.h
@@ -1,19 +1,18 @@
/*
- * This file is part of mplayer2.
+ * This file is part of mpv.
*
- * mplayer2 is free software; you can redistribute it and/or modify
+ * 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.
*
- * mplayer2 is distributed in the hope that it will be useful,
+ * 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 mplayer2; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_DEMUX_PACKET_H
diff --git a/demux/stheader.h b/demux/stheader.h
index d0f4097..444f2f3 100644
--- a/demux/stheader.h
+++ b/demux/stheader.h
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_STHEADER_H
@@ -58,6 +57,8 @@ struct sh_stream {
bool default_track; // container default track flag
int hls_bitrate;
+ bool missing_timestamps;
+
// stream is a picture (such as album art)
struct demux_packet *attached_picture;
@@ -81,7 +82,6 @@ typedef struct sh_video {
bool avi_dts; // use DTS timing; first frame and DTS is 0
float fps; // frames per second (set only if constant fps)
float aspect; // aspect ratio stored in the file (for prescaling)
- int bitrate; // compressed bits/sec
int bits_per_coded_sample;
unsigned char *extradata;
int extradata_len;
@@ -93,7 +93,6 @@ typedef struct sh_video {
typedef struct sh_sub {
unsigned char *extradata; // extra header data passed from demuxer
int extradata_len;
- int w, h; // mp4 vobsubs
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
diff --git a/demux/timeline.c b/demux/timeline.c
index 6274c25..73f3ab7 100644
--- a/demux/timeline.c
+++ b/demux/timeline.c
@@ -14,6 +14,7 @@ struct timeline *timeline_load(struct mpv_global *global, struct mp_log *log,
*tl = (struct timeline){
.global = global,
.log = log,
+ .cancel = demuxer->stream->cancel,
.demuxer = demuxer,
.track_layout = demuxer,
};
@@ -32,11 +33,8 @@ void timeline_destroy(struct timeline *tl)
return;
for (int n = 0; n < tl->num_sources; n++) {
struct demuxer *d = tl->sources[n];
- if (d != tl->demuxer) {
- struct stream *s = d->stream;
- free_demuxer(d);
- free_stream(s);
- }
+ if (d != tl->demuxer)
+ free_demuxer_and_stream(d);
}
talloc_free(tl);
}
diff --git a/demux/timeline.h b/demux/timeline.h
index e4d6f67..edc6a2f 100644
--- a/demux/timeline.h
+++ b/demux/timeline.h
@@ -10,6 +10,7 @@ struct timeline_part {
struct timeline {
struct mpv_global *global;
struct mp_log *log;
+ struct mp_cancel *cancel;
// main source
struct demuxer *demuxer;
diff --git a/etc/input.conf b/etc/input.conf
index 07e2dde..73be521 100644
--- a/etc/input.conf
+++ b/etc/input.conf
@@ -51,10 +51,10 @@
#DOWN seek -60
# Do smaller, always exact (non-keyframe-limited), seeks with shift.
# Don't show them on the OSD (no-osd).
-#Shift+RIGHT no-osd seek 1 - exact
-#Shift+LEFT no-osd seek -1 - exact
-#Shift+UP no-osd seek 5 - exact
-#Shift+DOWN no-osd seek -5 - exact
+#Shift+RIGHT no-osd seek 1 exact
+#Shift+LEFT no-osd seek -1 exact
+#Shift+UP no-osd seek 5 exact
+#Shift+DOWN no-osd seek -5 exact
# Skip to previous/next subtitle (subject to some restrictions; see manpage)
#Ctrl+LEFT no-osd sub_seek -1
#Ctrl+RIGHT no-osd sub_seek 1
@@ -108,6 +108,9 @@
#v cycle sub-visibility
# stretch SSA/ASS subtitles with anamorphic videos to match historical
#V cycle ass-vsfilter-aspect-compat
+# switch between applying no style overrides to SSA/ASS subtitles, and
+# overriding them almost completely with the normal subtitle style
+#u cycle_values ass-style-override "force" "no"
#j cycle sub # cycle through subtitles
#J cycle sub down # ...backwards
#SHARP cycle audio # switch audio streams
@@ -116,13 +119,12 @@
#f cycle fullscreen # toggle fullscreen
#s screenshot # take a screenshot
#S screenshot video # ...without subtitles
-#Alt+s screenshot - each-frame # automatically screenshot every frame
+#Alt+s screenshot each-frame # automatically screenshot every frame
#w add panscan -0.1 # zoom out with -panscan 0 -fs
#e add panscan +0.1 # in
# cycle video aspect ratios; "-1" is the container aspect
#A cycle_values video-aspect "16:9" "4:3" "2.35:1" "-1"
#POWER quit
-#MENU cycle osd
#PLAY cycle pause
#PAUSE cycle pause
#PLAYPAUSE cycle pause
@@ -156,20 +158,6 @@
#AR_VDOWN add volume -2
#AR_VDOWN_HOLD add chapter -1
-# Joystick section
-# WARNING: joystick support has to be explicitly enabled at
-# compiletime with --enable-joystick
-#
-
-#JOY_AXIS0_PLUS seek 10
-#JOY_AXIS0_MINUS seek -10
-#JOY_AXIS1_MINUS seek 60
-#JOY_AXIS1_PLUS seek -60
-#JOY_BTN0 cycle pause
-#JOY_BTN1 cycle osd
-#JOY_BTN2 add volume 2
-#JOY_BTN3 add volume -2
-
# For dvdnav:// and bdnav://
# navigation controls during playback
diff --git a/etc/mplayer-input.conf b/etc/mplayer-input.conf
index 0837039..de60fdc 100644
--- a/etc/mplayer-input.conf
+++ b/etc/mplayer-input.conf
@@ -75,22 +75,6 @@ n cycle tv-norm
#b tv_step_chanlist
##
-## Joystick section
-## WARNING: joystick support has to be explicitly enabled at
-## compiletime with --enable-joystick
-##
-
-JOY_RIGHT seek 10
-JOY_LEFT seek -10
-JOY_UP seek 60
-JOY_DOWN seek -60
-JOY_BTN0 cycle pause
-JOY_BTN1 osd
-JOY_BTN2 add volume 1
-JOY_BTN3 add volume -1
-#JOY_BTN4 set_menu main
-
-##
## Apple Remote section
##
## To use OSD menu with Apple Remote, set key AR_MENU to any OSD menu command,
diff --git a/etc/mpv.desktop b/etc/mpv.desktop
index 7bdeee3..9fe33a6 100644
--- a/etc/mpv.desktop
+++ b/etc/mpv.desktop
@@ -24,9 +24,8 @@ Comment[zh-CN]=播放电影和歌曲
Comment[zh-TW]=播放電影和歌曲
Icon=mpv
TryExec=mpv
-Exec=mpv --no-terminal --force-window -- %U
+Exec=mpv --profile=pseudo-gui -- %U
Terminal=false
-NoDisplay=true
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;
X-KDE-Protocols=ftp,http,https,mms,rtmp,rtsp,sftp,smb
diff --git a/input/cmd_list.c b/input/cmd_list.c
index 28c8988..acd8ae7 100644
--- a/input/cmd_list.c
+++ b/input/cmd_list.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <limits.h>
@@ -51,6 +50,7 @@
#define OARG_DOUBLE(def) OPT_DOUBLE(ARG(d), 0, OPTDEF_DOUBLE(def))
#define OARG_INT(def) OPT_INT(ARG(i), 0, OPTDEF_INT(def))
#define OARG_CHOICE(def, c) OPT_CHOICE(ARG(i), 0, c, OPTDEF_INT(def))
+#define OARG_FLAGS(def, c) OPT_FLAGS(ARG(i), 0, c, OPTDEF_INT(def))
#define OARG_STRING(def) OPT_STRING(ARG(s), 0, OPTDEF_STR(def))
#define OARG_CYCLEDIR(def) OPT_CYCLEDIR(ARG(d), 0, OPTDEF_DOUBLE(def))
@@ -60,17 +60,20 @@ const struct mp_cmd_def mp_cmds[] = {
{ MP_CMD_SEEK, "seek", {
ARG_TIME,
- OARG_CHOICE(0, ({"relative", 0}, {"0", 0}, {"-", 0},
- {"absolute-percent", 1}, {"1", 1},
- {"absolute", 2}, {"2", 2})),
- OARG_CHOICE(0, ({"default-precise", 0}, {"0", 0},
- {"exact", 1}, {"1", 1},
- {"keyframes", -1}, {"-1", -1})),
+ OARG_FLAGS(4|0, ({"relative", 4|0}, {"-", 4|0},
+ {"absolute-percent", 4|1},
+ {"absolute", 4|2},
+ {"keyframes", 32|8},
+ {"exact", 32|16})),
+ // backwards compatibility only
+ OARG_CHOICE(0, ({"unused", 0}, {"default-precise", 0},
+ {"keyframes", 32|8},
+ {"exact", 32|16})),
},
.allow_auto_repeat = true,
},
{ MP_CMD_REVERT_SEEK, "revert_seek", {
- OARG_CHOICE(0, ({"-", 0}, {"mark", 1})),
+ OARG_FLAGS(0, ({"mark", 1})),
}},
{ MP_CMD_QUIT, "quit", { OARG_INT(0) } },
{ MP_CMD_QUIT_WATCH_LATER, "quit_watch_later", { OARG_INT(0) } },
@@ -79,12 +82,12 @@ const struct mp_cmd_def mp_cmds[] = {
.on_updown = true },
{ MP_CMD_FRAME_BACK_STEP, "frame_back_step", .allow_auto_repeat = true },
{ MP_CMD_PLAYLIST_NEXT, "playlist_next", {
- OARG_CHOICE(0, ({"weak", 0}, {"0", 0},
- {"force", 1}, {"1", 1})),
+ OARG_CHOICE(0, ({"weak", 0},
+ {"force", 1})),
}},
{ MP_CMD_PLAYLIST_PREV, "playlist_prev", {
- OARG_CHOICE(0, ({"weak", 0}, {"0", 0},
- {"force", 1}, {"1", 1})),
+ OARG_CHOICE(0, ({"weak", 0},
+ {"force", 1})),
}},
{ MP_CMD_SUB_STEP, "sub_step", { ARG_INT }, .allow_auto_repeat = true },
{ MP_CMD_SUB_SEEK, "sub_seek", { ARG_INT }, .allow_auto_repeat = true },
@@ -102,11 +105,13 @@ const struct mp_cmd_def mp_cmds[] = {
{ MP_CMD_TV_LAST_CHANNEL, "tv_last_channel", },
{ MP_CMD_SCREENSHOT, "screenshot", {
- OARG_CHOICE(2, ({"video", 0},
- {"window", 1},
- {"subtitles", 2}, {"-", 2})),
- OARG_CHOICE(0, ({"single", 0},
- {"each-frame", 1})),
+ OARG_FLAGS(4|2, ({"video", 4|0}, {"-", 4|0},
+ {"window", 4|1},
+ {"subtitles", 4|2},
+ {"each-frame", 8})),
+ // backwards compatibility
+ OARG_CHOICE(0, ({"unused", 0}, {"single", 0},
+ {"each-frame", 8})),
}},
{ MP_CMD_SCREENSHOT_TO_FILE, "screenshot_to_file", {
ARG_STRING,
@@ -114,17 +119,22 @@ const struct mp_cmd_def mp_cmds[] = {
{"window", 1},
{"subtitles", 2})),
}},
+ { MP_CMD_SCREENSHOT_RAW, "screenshot_raw", {
+ OARG_CHOICE(2, ({"video", 0},
+ {"window", 1},
+ {"subtitles", 2})),
+ }},
{ MP_CMD_LOADFILE, "loadfile", {
ARG_STRING,
- OARG_CHOICE(0, ({"replace", 0}, {"0", 0},
- {"append", 1}, {"1", 1},
+ OARG_CHOICE(0, ({"replace", 0},
+ {"append", 1},
{"append-play", 2})),
OPT_KEYVALUELIST(ARG(str_list), MP_CMD_OPT_ARG),
}},
{ MP_CMD_LOADLIST, "loadlist", {
ARG_STRING,
- OARG_CHOICE(0, ({"replace", 0}, {"0", 0},
- {"append", 1}, {"1", 1})),
+ OARG_CHOICE(0, ({"replace", 0},
+ {"append", 1})),
}},
{ MP_CMD_PLAYLIST_CLEAR, "playlist_clear", },
{ MP_CMD_PLAYLIST_REMOVE, "playlist_remove", {
@@ -199,6 +209,11 @@ const struct mp_cmd_def mp_cmds[] = {
{ MP_CMD_AUDIO_REMOVE, "audio_remove", { OARG_INT(-1) } },
{ MP_CMD_AUDIO_RELOAD, "audio_reload", { OARG_INT(-1) } },
+ { MP_CMD_RESCAN_EXTERNAL_FILES, "rescan_external_files", {
+ OARG_CHOICE(0, ({"keep-selection", 0},
+ {"reselect", 1})),
+ }},
+
{0}
};
diff --git a/input/cmd_list.h b/input/cmd_list.h
index c761be9..e9418d4 100644
--- a/input/cmd_list.h
+++ b/input/cmd_list.h
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MP_COMMAND_LIST_H
@@ -49,6 +48,7 @@ enum mp_command_type {
MP_CMD_OSD,
MP_CMD_SCREENSHOT,
MP_CMD_SCREENSHOT_TO_FILE,
+ MP_CMD_SCREENSHOT_RAW,
MP_CMD_LOADFILE,
MP_CMD_LOADLIST,
MP_CMD_PLAYLIST_CLEAR,
@@ -111,6 +111,8 @@ enum mp_command_type {
MP_CMD_HOOK_ADD,
MP_CMD_HOOK_ACK,
+ MP_CMD_RESCAN_EXTERNAL_FILES,
+
// Internal
MP_CMD_COMMAND_LIST, // list of sub-commands in args[0].v.p
};
diff --git a/input/cmd_parse.c b/input/cmd_parse.c
index 44d4c8a..f588bb7 100644
--- a/input/cmd_parse.c
+++ b/input/cmd_parse.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stddef.h>
diff --git a/input/cmd_parse.h b/input/cmd_parse.h
index 6c4d100..252dde4 100644
--- a/input/cmd_parse.h
+++ b/input/cmd_parse.h
@@ -1,20 +1,20 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
+
#ifndef MP_PARSE_COMMAND_H
#define MP_PARSE_COMMAND_H
diff --git a/input/input.c b/input/input.c
index ecf897a..2a108be 100644
--- a/input/input.c
+++ b/input/input.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
@@ -163,11 +162,6 @@ struct input_opts {
// Autorepeat config (be aware of mp_input_set_repeat_info())
int ar_delay;
int ar_rate;
- char *js_dev;
- int use_joystick;
- int use_lirc;
- char *lirc_configfile;
- int use_lircc;
int use_alt_gr;
int use_appleremote;
int use_media_keys;
@@ -180,25 +174,19 @@ struct input_opts {
const struct m_sub_options input_config = {
.opts = (const m_option_t[]) {
- OPT_STRING("conf", config_file, CONF_GLOBAL),
+ OPT_STRING("conf", config_file, CONF_GLOBAL | M_OPT_FILE),
OPT_INT("ar-delay", ar_delay, CONF_GLOBAL),
OPT_INT("ar-rate", ar_rate, CONF_GLOBAL),
OPT_PRINT("keylist", mp_print_key_list),
OPT_PRINT("cmdlist", mp_print_cmd_list),
- OPT_STRING("js-dev", js_dev, CONF_GLOBAL),
OPT_FLAG("default-bindings", default_bindings, CONF_GLOBAL),
OPT_FLAG("test", test, CONF_GLOBAL),
OPT_INTRANGE("doubleclick-time", doubleclick_time, 0, 0, 1000),
- OPT_FLAG("joystick", use_joystick, CONF_GLOBAL),
- OPT_FLAG("lirc", use_lirc, CONF_GLOBAL),
OPT_FLAG("right-alt-gr", use_alt_gr, CONF_GLOBAL),
OPT_INTRANGE("key-fifo-size", key_fifo_size, CONF_GLOBAL, 2, 65000),
OPT_FLAG("cursor", enable_mouse_movements, CONF_GLOBAL),
OPT_FLAG("vo-keyboard", vo_key_input, CONF_GLOBAL),
OPT_FLAG("x11-keyboard", vo_key_input, CONF_GLOBAL), // old alias
-#if HAVE_LIRC
- OPT_STRING("lirc-conf", lirc_configfile, CONF_GLOBAL),
-#endif
#if HAVE_COCOA
OPT_FLAG("appleremote", use_appleremote, CONF_GLOBAL),
OPT_FLAG("media-keys", use_media_keys, CONF_GLOBAL),
@@ -212,7 +200,6 @@ const struct m_sub_options input_config = {
.doubleclick_time = 300,
.ar_delay = 200,
.ar_rate = 40,
- .use_lirc = 1,
.use_alt_gr = 1,
.enable_mouse_movements = 1,
#if HAVE_COCOA
@@ -463,7 +450,7 @@ static mp_cmd_t *get_cmd_from_keys(struct input_ctx *ictx, char *force_section,
if (code == MP_KEY_CLOSE_WIN)
return mp_input_parse_cmd_strv(ictx->log, (const char*[]){"quit", 0});
int msgl = MSGL_WARN;
- if (code == MP_KEY_MOUSE_MOVE || code == MP_KEY_MOUSE_LEAVE)
+ if (MP_KEY_IS_MOUSE_MOVE(code))
msgl = MSGL_DEBUG;
char *key_buf = mp_input_get_key_combo_name(&code, 1);
MP_MSG(ictx, msgl, "No bind found for key '%s'.\n", key_buf);
@@ -631,7 +618,7 @@ static void mp_input_feed_key(struct input_ctx *ictx, int code, double scale)
}
if (!opts->enable_mouse_movements && MP_KEY_IS_MOUSE(unmod))
return;
- if (unmod == MP_KEY_MOUSE_LEAVE) {
+ if (unmod == MP_KEY_MOUSE_LEAVE || unmod == MP_KEY_MOUSE_ENTER) {
update_mouse_section(ictx);
mp_input_queue_cmd(ictx, get_cmd_from_keys(ictx, NULL, code));
return;
@@ -1252,16 +1239,6 @@ void mp_input_load(struct input_ctx *ictx)
MP_VERBOSE(ictx, "Falling back on default (hardcoded) input config\n");
}
-#if HAVE_JOYSTICK
- if (input_conf->use_joystick)
- mp_input_joystick_add(ictx, input_conf->js_dev);
-#endif
-
-#if HAVE_LIRC
- if (input_conf->use_lirc)
- mp_input_lirc_add(ictx, input_conf->lirc_configfile);
-#endif
-
if (input_conf->use_alt_gr) {
ictx->using_alt_gr = true;
}
diff --git a/input/input.h b/input/input.h
index 33d458a..d51492d 100644
--- a/input/input.h
+++ b/input/input.h
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_INPUT_H
@@ -257,8 +256,6 @@ void mp_input_run_cmd(struct input_ctx *ictx, const char **cmd);
void mp_input_set_repeat_info(struct input_ctx *ictx, int rate, int delay);
void mp_input_pipe_add(struct input_ctx *ictx, const char *filename);
-void mp_input_joystick_add(struct input_ctx *ictx, char *dev);
-void mp_input_lirc_add(struct input_ctx *ictx, char *lirc_configfile);
struct mp_ipc_ctx;
struct mp_client_api;
diff --git a/input/ipc.c b/input/ipc.c
index 2e17fd4..f1caeee 100644
--- a/input/ipc.c
+++ b/input/ipc.c
@@ -482,14 +482,20 @@ static char *text_execute_command(struct client_arg *arg, void *tmp, char *src)
return NULL;
}
-static int ipc_write(int fd, const char *buf, size_t count)
+static int ipc_write_str(struct client_arg *client, const char *buf)
{
+ size_t count = strlen(buf);
while (count > 0) {
- ssize_t rc = write(fd, buf, count);
+ ssize_t rc = write(client->client_fd, buf, count);
if (rc <= 0) {
if (rc == 0)
return -1;
+ if (errno == EBADF) {
+ client->writable = false;
+ return 0;
+ }
+
if (errno == EINTR)
continue;
@@ -567,16 +573,16 @@ static void *client_thread(void *p)
goto done;
}
- rc = ipc_write(arg->client_fd, event_msg, strlen(event_msg));
+ rc = ipc_write_str(arg, event_msg);
talloc_free(event_msg);
if (rc < 0) {
- MP_ERR(arg, "Write error\n");
+ MP_ERR(arg, "Write error (%s)\n", mp_strerror(errno));
goto done;
}
}
}
- if (fds[1].revents & POLLIN) {
+ if (fds[1].revents & (POLLIN | POLLHUP)) {
while (1) {
char buf[128];
bstr append = { buf, 0 };
@@ -619,8 +625,7 @@ static void *client_thread(void *p)
}
if (reply_msg && arg->writable) {
- rc = ipc_write(arg->client_fd, reply_msg,
- strlen(reply_msg));
+ rc = ipc_write_str(arg, reply_msg);
if (rc < 0) {
MP_ERR(arg, "Write error (%s)\n", mp_strerror(errno));
talloc_free(tmp);
@@ -678,10 +683,20 @@ static void ipc_start_client_text(struct mp_ipc_ctx *ctx, const char *path)
int mode = O_RDONLY;
int client_fd = -1;
bool close_client_fd = true;
+ bool writable = false;
if (strcmp(path, "/dev/stdin") == 0) { // for symmetry with Linux
client_fd = STDIN_FILENO;
close_client_fd = false;
+ } else if (strncmp(path, "fd://", 5) == 0) {
+ char *end = NULL;
+ client_fd = strtol(path + 5, &end, 0);
+ if (!end || end == path + 5 || end[0]) {
+ MP_ERR(ctx, "Invalid FD: %s\n", path);
+ return;
+ }
+ close_client_fd = false;
+ writable = true; // maybe
} else {
// Use RDWR for FIFOs to ensure they stay open over multiple accesses.
struct stat st;
@@ -690,7 +705,7 @@ static void ipc_start_client_text(struct mp_ipc_ctx *ctx, const char *path)
client_fd = open(path, mode);
}
if (client_fd < 0) {
- MP_ERR(ctx, "Could not open pipe at '%s'\n", path);
+ MP_ERR(ctx, "Could not open '%s'\n", path);
return;
}
@@ -699,8 +714,7 @@ static void ipc_start_client_text(struct mp_ipc_ctx *ctx, const char *path)
.client_name = "input-file",
.client_fd = client_fd,
.close_client_fd = close_client_fd,
-
- .writable = false,
+ .writable = writable,
};
ipc_start_client(ctx, client);
@@ -725,6 +739,10 @@ static void *ipc_thread(void *p)
goto done;
}
+#if HAVE_FCHMOD
+ fchmod(ipc_fd, 0600);
+#endif
+
size_t path_len = strlen(arg->path);
if (path_len >= sizeof(ipc_un.sun_path) - 1) {
MP_ERR(arg, "Could not create IPC socket\n");
diff --git a/input/joystick.c b/input/joystick.c
deleted file mode 100644
index e3df106..0000000
--- a/input/joystick.c
+++ /dev/null
@@ -1,216 +0,0 @@
-/*
- * 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 "input.h"
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <poll.h>
-
-#include "common/common.h"
-#include "common/msg.h"
-#include "keycodes.h"
-
-#ifndef JOY_AXIS_DELTA
-#define JOY_AXIS_DELTA 500
-#endif
-
-#ifndef JS_DEV
-#define JS_DEV "/dev/input/js0"
-#endif
-
-#include <linux/joystick.h>
-
-struct ctx {
- struct mp_log *log;
- int axis[256];
- int btns;
- int fd;
-};
-
-static int close_js(void *ctx, int fd)
-{
- close(fd);
- talloc_free(ctx);
- return 0;
-}
-
-static struct ctx *joystick_init(struct input_ctx *ictx, struct mp_log *log, char *dev)
-{
- int fd,l=0;
- int initialized = 0;
- struct js_event ev;
-
- mp_verbose(log, "Opening joystick device %s\n",dev ? dev : JS_DEV);
-
- fd = open( dev ? dev : JS_DEV , O_RDONLY | O_NONBLOCK );
- if(fd < 0) {
- mp_err(log, "Can't open joystick device %s: %s\n",dev ? dev : JS_DEV,
- mp_strerror(errno));
- return NULL;
- }
-
- struct ctx *ctx = talloc_ptrtype(NULL, ctx);
- *ctx = (struct ctx) {.log = log};
-
- while(! initialized) {
- l = 0;
- while((unsigned int)l < sizeof(struct js_event)) {
- int r = read(fd,((char*)&ev)+l,sizeof(struct js_event)-l);
- if(r < 0) {
- if(errno == EINTR)
- continue;
- else if(errno == EAGAIN) {
- initialized = 1;
- break;
- }
- MP_ERR(ctx, "Error while reading joystick device: %s\n",
- mp_strerror(errno));
- close(fd);
- talloc_free(ctx);
- return NULL;
- }
- l += r;
- }
- if((unsigned int)l < sizeof(struct js_event)) {
- if(l > 0)
- MP_WARN(ctx, "Joystick: We lose %d bytes of data\n",l);
- break;
- }
- if(ev.type == JS_EVENT_BUTTON)
- ctx->btns |= (ev.value << ev.number);
- if(ev.type == JS_EVENT_AXIS)
- ctx->axis[ev.number] = ev.value;
- }
-
- ctx->fd = fd;
- return ctx;
-}
-
-static int mp_input_joystick_read(void *pctx, int fd) {
- struct ctx *ctx = pctx;
- struct js_event ev;
- int l=0;
-
- while((unsigned int)l < sizeof(struct js_event)) {
- int r = read(fd,((char*)&ev)+l,sizeof(struct js_event)-l);
- if(r <= 0) {
- if(errno == EINTR)
- continue;
- else if(errno == EAGAIN)
- return 0;
- if( r < 0) {
- MP_ERR(ctx, "Error while reading joystick device: %s\n",
- mp_strerror(errno));
- } else {
- MP_ERR(ctx, "Error while reading joystick device: %s\n","EOF");
- }
- return -1;
- }
- l += r;
- }
-
- if((unsigned int)l < sizeof(struct js_event)) {
- if(l > 0)
- MP_WARN(ctx, "Joystick: We lose %d bytes of data\n",l);
- return 0;
- }
-
- if(ev.type & JS_EVENT_INIT) {
- MP_WARN(ctx, "Joystick: warning init event, we have lost sync with driver.\n");
- ev.type &= ~JS_EVENT_INIT;
- if(ev.type == JS_EVENT_BUTTON) {
- int s = (ctx->btns >> ev.number) & 1;
- if(s == ev.value) // State is the same : ignore
- return 0;
- }
- if(ev.type == JS_EVENT_AXIS) {
- if( ( ctx->axis[ev.number] == 1 && ev.value > JOY_AXIS_DELTA) ||
- (ctx->axis[ev.number] == -1 && ev.value < -JOY_AXIS_DELTA) ||
- (ctx->axis[ev.number] == 0 && ev.value >= -JOY_AXIS_DELTA && ev.value <= JOY_AXIS_DELTA)
- ) // State is the same : ignore
- return 0;
- }
- }
-
- if(ev.type & JS_EVENT_BUTTON) {
- ctx->btns &= ~(1 << ev.number);
- ctx->btns |= (ev.value << ev.number);
- if(ev.value == 1)
- return (MP_JOY_BTN0 + ev.number) | MP_KEY_STATE_DOWN;
- else
- return (MP_JOY_BTN0 + ev.number) | MP_KEY_STATE_UP;
- } else if(ev.type & JS_EVENT_AXIS) {
- if(ev.value < -JOY_AXIS_DELTA && ctx->axis[ev.number] != -1) {
- ctx->axis[ev.number] = -1;
- return (MP_JOY_AXIS0_MINUS+(2*ev.number)) | MP_KEY_STATE_DOWN;
- } else if(ev.value > JOY_AXIS_DELTA && ctx->axis[ev.number] != 1) {
- ctx->axis[ev.number] = 1;
- return (MP_JOY_AXIS0_PLUS+(2*ev.number)) | MP_KEY_STATE_DOWN;
- } else if(ev.value <= JOY_AXIS_DELTA && ev.value >= -JOY_AXIS_DELTA && ctx->axis[ev.number] != 0) {
- int r = ctx->axis[ev.number] == 1 ? MP_JOY_AXIS0_PLUS+(2*ev.number) : MP_JOY_AXIS0_MINUS+(2*ev.number);
- ctx->axis[ev.number] = 0;
- return r | MP_KEY_STATE_UP;
- } else
- return 0;
- } else {
- MP_WARN(ctx, "Joystick warning unknown event type %d\n",ev.type);
- return -1;
- }
-
- return 0;
-}
-
-static void read_joystick_thread(struct mp_input_src *src, void *param)
-{
- int wakeup_fd = mp_input_src_get_wakeup_fd(src);
- struct ctx *ctx = joystick_init(src->input_ctx, src->log, param);
-
- if (!ctx)
- return;
-
- mp_input_src_init_done(src);
-
- while (1) {
- struct pollfd fds[2] = {
- { .fd = ctx->fd, .events = POLLIN },
- { .fd = wakeup_fd, .events = POLLIN },
- };
- poll(fds, 2, -1);
- if (!(fds[0].revents & POLLIN))
- break;
- int r = mp_input_joystick_read(ctx, ctx->fd);
- if (r < 0)
- break;
- if (r > 0)
- mp_input_put_key(src->input_ctx, r);
- }
-
- close_js(ctx, ctx->fd);
-}
-
-void mp_input_joystick_add(struct input_ctx *ictx, char *dev)
-{
- mp_input_add_thread_src(ictx, (void *)dev, read_joystick_thread);
-}
diff --git a/input/keycodes.c b/input/keycodes.c
index e448a9b..514bb22 100644
--- a/input/keycodes.c
+++ b/input/keycodes.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stddef.h>
@@ -118,42 +117,6 @@ static const struct key_name key_names[] = {
{ MP_MOUSE_BTN17_DBL, "MOUSE_BTN17_DBL" },
{ MP_MOUSE_BTN18_DBL, "MOUSE_BTN18_DBL" },
{ MP_MOUSE_BTN19_DBL, "MOUSE_BTN19_DBL" },
- { MP_JOY_AXIS1_MINUS, "JOY_UP" },
- { MP_JOY_AXIS1_PLUS, "JOY_DOWN" },
- { MP_JOY_AXIS0_MINUS, "JOY_LEFT" },
- { MP_JOY_AXIS0_PLUS, "JOY_RIGHT" },
-
- { MP_JOY_AXIS0_PLUS, "JOY_AXIS0_PLUS" },
- { MP_JOY_AXIS0_MINUS, "JOY_AXIS0_MINUS" },
- { MP_JOY_AXIS1_PLUS, "JOY_AXIS1_PLUS" },
- { MP_JOY_AXIS1_MINUS, "JOY_AXIS1_MINUS" },
- { MP_JOY_AXIS2_PLUS, "JOY_AXIS2_PLUS" },
- { MP_JOY_AXIS2_MINUS, "JOY_AXIS2_MINUS" },
- { MP_JOY_AXIS3_PLUS, "JOY_AXIS3_PLUS" },
- { MP_JOY_AXIS3_MINUS, "JOY_AXIS3_MINUS" },
- { MP_JOY_AXIS4_PLUS, "JOY_AXIS4_PLUS" },
- { MP_JOY_AXIS4_MINUS, "JOY_AXIS4_MINUS" },
- { MP_JOY_AXIS5_PLUS, "JOY_AXIS5_PLUS" },
- { MP_JOY_AXIS5_MINUS, "JOY_AXIS5_MINUS" },
- { MP_JOY_AXIS6_PLUS, "JOY_AXIS6_PLUS" },
- { MP_JOY_AXIS6_MINUS, "JOY_AXIS6_MINUS" },
- { MP_JOY_AXIS7_PLUS, "JOY_AXIS7_PLUS" },
- { MP_JOY_AXIS7_MINUS, "JOY_AXIS7_MINUS" },
- { MP_JOY_AXIS8_PLUS, "JOY_AXIS8_PLUS" },
- { MP_JOY_AXIS8_MINUS, "JOY_AXIS8_MINUS" },
- { MP_JOY_AXIS9_PLUS, "JOY_AXIS9_PLUS" },
- { MP_JOY_AXIS9_MINUS, "JOY_AXIS9_MINUS" },
-
- { MP_JOY_BTN0, "JOY_BTN0" },
- { MP_JOY_BTN1, "JOY_BTN1" },
- { MP_JOY_BTN2, "JOY_BTN2" },
- { MP_JOY_BTN3, "JOY_BTN3" },
- { MP_JOY_BTN4, "JOY_BTN4" },
- { MP_JOY_BTN5, "JOY_BTN5" },
- { MP_JOY_BTN6, "JOY_BTN6" },
- { MP_JOY_BTN7, "JOY_BTN7" },
- { MP_JOY_BTN8, "JOY_BTN8" },
- { MP_JOY_BTN9, "JOY_BTN9" },
{ MP_AR_PLAY, "AR_PLAY" },
{ MP_AR_PLAY_HOLD, "AR_PLAY_HOLD" },
@@ -205,6 +168,7 @@ static const struct key_name key_names[] = {
{ MP_KEY_CLOSE_WIN, "CLOSE_WIN" },
{ MP_KEY_MOUSE_MOVE, "MOUSE_MOVE" },
{ MP_KEY_MOUSE_LEAVE, "MOUSE_LEAVE" },
+ { MP_KEY_MOUSE_ENTER, "MOUSE_ENTER" },
{ 0, NULL }
};
diff --git a/input/keycodes.h b/input/keycodes.h
index 3922cfc..d95edb1 100644
--- a/input/keycodes.h
+++ b/input/keycodes.h
@@ -1,21 +1,20 @@
/*
* KEY code definitions for keys/events not passed by ASCII value
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_KEYCODES_H
@@ -97,43 +96,6 @@
#define MP_KEY_KPDEL (MP_KEY_KEYPAD+12)
#define MP_KEY_KPENTER (MP_KEY_KEYPAD+13)
-
-// Joystick input module
-#define MP_JOY_BASE (MP_KEY_BASE+0x70)
-#define MP_JOY_AXIS0_PLUS (MP_JOY_BASE+0)
-#define MP_JOY_AXIS0_MINUS (MP_JOY_BASE+1)
-#define MP_JOY_AXIS1_PLUS (MP_JOY_BASE+2)
-#define MP_JOY_AXIS1_MINUS (MP_JOY_BASE+3)
-#define MP_JOY_AXIS2_PLUS (MP_JOY_BASE+4)
-#define MP_JOY_AXIS2_MINUS (MP_JOY_BASE+5)
-#define MP_JOY_AXIS3_PLUS (MP_JOY_BASE+6)
-#define MP_JOY_AXIS3_MINUS (MP_JOY_BASE+7)
-#define MP_JOY_AXIS4_PLUS (MP_JOY_BASE+8)
-#define MP_JOY_AXIS4_MINUS (MP_JOY_BASE+9)
-#define MP_JOY_AXIS5_PLUS (MP_JOY_BASE+10)
-#define MP_JOY_AXIS5_MINUS (MP_JOY_BASE+11)
-#define MP_JOY_AXIS6_PLUS (MP_JOY_BASE+12)
-#define MP_JOY_AXIS6_MINUS (MP_JOY_BASE+13)
-#define MP_JOY_AXIS7_PLUS (MP_JOY_BASE+14)
-#define MP_JOY_AXIS7_MINUS (MP_JOY_BASE+15)
-#define MP_JOY_AXIS8_PLUS (MP_JOY_BASE+16)
-#define MP_JOY_AXIS8_MINUS (MP_JOY_BASE+17)
-#define MP_JOY_AXIS9_PLUS (MP_JOY_BASE+18)
-#define MP_JOY_AXIS9_MINUS (MP_JOY_BASE+19)
-
-#define MP_JOY_BTN_BASE ((MP_KEY_BASE+0x90)|MP_NO_REPEAT_KEY)
-#define MP_JOY_BTN0 (MP_JOY_BTN_BASE+0)
-#define MP_JOY_BTN1 (MP_JOY_BTN_BASE+1)
-#define MP_JOY_BTN2 (MP_JOY_BTN_BASE+2)
-#define MP_JOY_BTN3 (MP_JOY_BTN_BASE+3)
-#define MP_JOY_BTN4 (MP_JOY_BTN_BASE+4)
-#define MP_JOY_BTN5 (MP_JOY_BTN_BASE+5)
-#define MP_JOY_BTN6 (MP_JOY_BTN_BASE+6)
-#define MP_JOY_BTN7 (MP_JOY_BTN_BASE+7)
-#define MP_JOY_BTN8 (MP_JOY_BTN_BASE+8)
-#define MP_JOY_BTN9 (MP_JOY_BTN_BASE+9)
-
-
// Mouse events from VOs
#define MP_MOUSE_BASE ((MP_KEY_BASE+0xA0)|MP_NO_REPEAT_KEY|MP_KEY_EMIT_ON_UP)
#define MP_MOUSE_BTN0 (MP_MOUSE_BASE+0)
@@ -222,14 +184,21 @@
// Generated by input.c (VOs use mp_input_set_mouse_pos())
#define MP_KEY_MOUSE_MOVE ((MP_KEY_INTERN+1)|MP_NO_REPEAT_KEY)
#define MP_KEY_MOUSE_LEAVE ((MP_KEY_INTERN+2)|MP_NO_REPEAT_KEY)
+#define MP_KEY_MOUSE_ENTER ((MP_KEY_INTERN+3)|MP_NO_REPEAT_KEY)
+
+#define MP_KEY_IS_MOUSE_CLICK(code) \
+ (MP_KEY_IS_MOUSE_BTN_SINGLE(code) || MP_KEY_IS_MOUSE_BTN_DBL(code))
+#define MP_KEY_IS_MOUSE_MOVE(code) \
+ ((code) == MP_KEY_MOUSE_MOVE || (code) == MP_KEY_MOUSE_ENTER || \
+ (code) == MP_KEY_MOUSE_LEAVE)
+// Whether to dispatch the key binding by current mouse position.
#define MP_KEY_DEPENDS_ON_MOUSE_POS(code) \
- (MP_KEY_IS_MOUSE_BTN_SINGLE(code) || MP_KEY_IS_MOUSE_BTN_DBL(code) || \
- (code) == MP_KEY_MOUSE_MOVE)
+ (MP_KEY_IS_MOUSE_CLICK(code) || (code) == MP_KEY_MOUSE_MOVE)
#define MP_KEY_IS_MOUSE(code) \
- (MP_KEY_DEPENDS_ON_MOUSE_POS(code) || (code) == MP_KEY_MOUSE_LEAVE)
+ (MP_KEY_IS_MOUSE_CLICK(code) || MP_KEY_IS_MOUSE_MOVE(code))
// 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.
diff --git a/input/lirc.c b/input/lirc.c
deleted file mode 100644
index db8d4f8..0000000
--- a/input/lirc.c
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * 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 <lirc/lirc_client.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <poll.h>
-
-#include "common/common.h"
-#include "common/msg.h"
-#include "input.h"
-
-struct ctx {
- struct mp_log *log;
- struct lirc_config *lirc_config;
- char* cmd_buf;
- int fd;
-};
-
-static struct ctx *mp_input_lirc_init(struct input_ctx *ictx, struct mp_log *log,
- char *lirc_configfile)
-{
- int lirc_sock;
- int mode;
-
- mp_verbose(log,"Setting up LIRC support...\n");
- if((lirc_sock=lirc_init("mpv",0))==-1){
- mp_verbose(log,"Failed to open LIRC support. You will not be able to use your remote control.\n");
- return NULL;
- }
-
- mode = fcntl(lirc_sock, F_GETFL);
- if (mode < 0 || fcntl(lirc_sock, F_SETFL, mode | O_NONBLOCK) < 0) {
- mp_err(log, "setting non-blocking mode failed: %s\n", mp_strerror(errno));
- lirc_deinit();
- return NULL;
- }
-
- struct lirc_config *lirc_config = NULL;
- if(lirc_readconfig( lirc_configfile,&lirc_config,NULL )!=0 ){
- mp_err(log, "Failed to read LIRC config file %s.\n",
- lirc_configfile == NULL ? "~/.lircrc" : lirc_configfile);
- lirc_deinit();
- return NULL;
- }
-
- struct ctx *ctx = talloc_ptrtype(NULL, ctx);
- *ctx = (struct ctx){
- .log = log,
- .lirc_config = lirc_config,
- .fd = lirc_sock,
- };
- return ctx;
-}
-
-static int mp_input_lirc_read(void *pctx,int fd,char* dest, int s) {
- int r,cl = 0;
- char *code = NULL,*c = NULL;
- struct ctx *ctx = pctx;
-
- // We have something in the buffer return it
- if(ctx->cmd_buf != NULL) {
- int l = strlen(ctx->cmd_buf), w = l > s ? s : l;
- memcpy(dest,ctx->cmd_buf,w);
- l -= w;
- if(l > 0)
- memmove(ctx->cmd_buf,&ctx->cmd_buf[w],l+1);
- else {
- free(ctx->cmd_buf);
- ctx->cmd_buf = NULL;
- }
- return w;
- }
-
- // Nothing in the buffer, poll the lirc fd
- if(lirc_nextcode(&code) != 0) {
- MP_ERR(ctx, "Lirc error.\n");
- return -1;
- }
-
- if(!code) return 0;
-
- // We put all cmds in a single buffer separated by \n
- while((r = lirc_code2char(ctx->lirc_config,code,&c))==0 && c!=NULL) {
- int l = strlen(c);
- if(l <= 0)
- continue;
- ctx->cmd_buf = realloc(ctx->cmd_buf,cl+l+2);
- memcpy(&ctx->cmd_buf[cl],c,l);
- cl += l+1;
- ctx->cmd_buf[cl-1] = '\n';
- ctx->cmd_buf[cl] = '\0';
- }
-
- free(code);
-
- if(r < 0)
- return -1;
- else if(ctx->cmd_buf) // return the first command in the buffer
- return mp_input_lirc_read(ctx,fd,dest,s);
- else
- return 0;
-
-}
-
-static int mp_input_lirc_close(void *pctx,int fd)
-{
- struct ctx *ctx = pctx;
- free(ctx->cmd_buf);
- lirc_freeconfig(ctx->lirc_config);
- lirc_deinit();
- close(fd);
- talloc_free(ctx);
- return 0;
-}
-
-static void read_lirc_thread(struct mp_input_src *src, void *param)
-{
- int wakeup_fd = mp_input_src_get_wakeup_fd(src);
- struct ctx *ctx = mp_input_lirc_init(src->input_ctx, src->log, param);
-
- if (!ctx)
- return;
-
- mp_input_src_init_done(src);
-
- while (1) {
- struct pollfd fds[2] = {
- { .fd = ctx->fd, .events = POLLIN },
- { .fd = wakeup_fd, .events = POLLIN },
- };
- poll(fds, 2, -1);
- if (!(fds[0].revents & POLLIN))
- break;
- char buffer[128];
- int r = mp_input_lirc_read(ctx, ctx->fd, buffer, sizeof(buffer));
- if (r < 0)
- break;
- mp_input_src_feed_cmd_text(src, buffer, r);
- }
-
- mp_input_lirc_close(ctx, ctx->fd);
-}
-
-void mp_input_lirc_add(struct input_ctx *ictx, char *lirc_configfile)
-{
- mp_input_add_thread_src(ictx, (void *)lirc_configfile, read_lirc_thread);
-}
diff --git a/libmpv/client.h b/libmpv/client.h
index 0607f9a..a800043 100644
--- a/libmpv/client.h
+++ b/libmpv/client.h
@@ -24,6 +24,7 @@
#ifndef MPV_CLIENT_API_H_
#define MPV_CLIENT_API_H_
+#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
@@ -195,7 +196,7 @@ extern "C" {
* relational operators (<, >, <=, >=).
*/
#define MPV_MAKE_VERSION(major, minor) (((major) << 16) | (minor) | 0UL)
-#define MPV_CLIENT_API_VERSION MPV_MAKE_VERSION(1, 14)
+#define MPV_CLIENT_API_VERSION MPV_MAKE_VERSION(1, 16)
/**
* Return the MPV_CLIENT_API_VERSION the mpv source has been compiled with.
@@ -360,8 +361,10 @@ const char *mpv_client_name(mpv_handle *ctx);
* equivalent to setting the --no-terminal option.
* (Technically, this also suppresses C signal handling.)
* - No config files will be loaded. This is roughly equivalent to using
- * --no-config (but actually the code path for loading config files is
- * disabled).
+ * --no-config. Since libmpv 1.15, you can actually re-enable this option,
+ * which will make libmpv load config files during mpv_initialize(). If you
+ * do this, you are strongly encouraged to set the "config-dir" option too.
+ * (Otherwise it will load the mpv command line player's config.)
* - Idle mode is enabled, which means the playback core will enter idle mode
* if there are no more files to play on the internal playlist, instead of
* exiting. This is equivalent to the --idle option.
@@ -630,7 +633,13 @@ typedef enum mpv_format {
/**
* See MPV_FORMAT_NODE_ARRAY.
*/
- MPV_FORMAT_NODE_MAP = 8
+ MPV_FORMAT_NODE_MAP = 8,
+ /**
+ * A raw, untyped byte array. Only used only with mpv_node, and only in
+ * some very special situations. (Currently, only for the screenshot_raw
+ * command.)
+ */
+ MPV_FORMAT_BYTE_ARRAY = 9
} mpv_format;
/**
@@ -652,6 +661,10 @@ typedef struct mpv_node {
* or if format==MPV_FORMAT_NODE_MAP
*/
struct mpv_node_list *list;
+ /**
+ * valid if format==MPV_FORMAT_BYTE_ARRAY
+ */
+ struct mpv_byte_array *ba;
} u;
/**
* Type of the data stored in this struct. This value rules what members in
@@ -664,6 +677,7 @@ typedef struct mpv_node {
* MPV_FORMAT_DOUBLE (u.double_)
* MPV_FORMAT_NODE_ARRAY (u.list)
* MPV_FORMAT_NODE_MAP (u.list)
+ * MPV_FORMAT_BYTE_ARRAY (u.ba)
* MPV_FORMAT_NONE (no member)
*
* If you encounter a value you don't know, you must not make any
@@ -705,6 +719,21 @@ typedef struct mpv_node_list {
} mpv_node_list;
/**
+ * (see mpv_node)
+ */
+typedef struct mpv_byte_array {
+ /**
+ * Pointer to the data. In what format the data is stored is up to whatever
+ * uses MPV_FORMAT_BYTE_ARRAY.
+ */
+ void *data;
+ /**
+ * Size of the data pointed to by ptr.
+ */
+ size_t size;
+} mpv_byte_array;
+
+/**
* Frees any data referenced by the node. It doesn't free the node itself.
* Call this only if the mpv client API set the node. If you constructed the
* node yourself (manually), you have to free it yourself.
diff --git a/libmpv/mpv.def b/libmpv/mpv.def
index d4d4587..840ba1e 100644
--- a/libmpv/mpv.def
+++ b/libmpv/mpv.def
@@ -22,7 +22,9 @@ mpv_get_wakeup_pipe
mpv_initialize
mpv_load_config_file
mpv_observe_property
+mpv_opengl_cb_draw
mpv_opengl_cb_init_gl
+mpv_opengl_cb_report_flip
mpv_opengl_cb_render
mpv_opengl_cb_set_update_callback
mpv_opengl_cb_uninit_gl
diff --git a/libmpv/opengl_cb.h b/libmpv/opengl_cb.h
index 0e679e5..fd1409a 100644
--- a/libmpv/opengl_cb.h
+++ b/libmpv/opengl_cb.h
@@ -44,7 +44,7 @@ extern "C" {
* the OpenGL API, because it's simpler and more flexible on the mpv side.
*
* The renderer needs to be explicitly initialized with mpv_opengl_cb_init_gl(),
- * and then video can be drawn with mpv_opengl_cb_render(). The user thread can
+ * and then video can be drawn with mpv_opengl_cb_draw(). The user thread can
* be notified by new frames with mpv_opengl_cb_set_update_callback().
*
* OpenGL interop
@@ -53,7 +53,7 @@ extern "C" {
* This assumes the OpenGL context lives on a certain thread controlled by the
* API user. The following functions require access to the OpenGL context:
* mpv_opengl_cb_init_gl
- * mpv_opengl_cb_render
+ * mpv_opengl_cb_draw
* mpv_opengl_cb_uninit_gl
*
* The OpenGL context is indirectly accessed through the OpenGL function
@@ -172,33 +172,60 @@ int mpv_opengl_cb_init_gl(mpv_opengl_cb_context *ctx, const char *exts,
/**
* Render video. Requires that the OpenGL state is initialized.
*
- * The video will use the provided viewport rectangle as window size. Options
- * like "panscan" are applied to determine which part of the video should be
- * visible and how the video should be scaled. You can change these options
- * at runtime by using the mpv property API.
+ * The video will use the full provided framebuffer. Options like "panscan" are
+ * applied to determine which part of the video should be visible and how the
+ * video should be scaled. You can change these options at runtime by using the
+ * mpv property API.
*
* The renderer will reconfigure itself every time the output rectangle/size
* is changed. (If you want to do animations, it might be better to do the
* animation on a FBO instead.)
*
+ * This function implicitly pulls a video frame from the internal queue and
+ * renders it. If no new frame is available, the previous frame is redrawn.
+ * The update callback set with mpv_opengl_cb_set_update_callback() notifies
+ * you when a new frame was added.
+ *
* @param fbo The framebuffer object to render on. Because the renderer might
* manage multiple FBOs internally for the purpose of video
* postprocessing, it will always bind and unbind FBOs itself. If
* you want mpv to render on the main framebuffer, pass 0.
- * @param vp Viewport to render on. The renderer will essentially call:
- * glViewport(0, 0, vp[2], vp[3]);
- * before rendering. The height (vp[3]) can be negative to flip the
- * image - the renderer will flip it before setting the viewport
- * (typically you want to flip the image if you are rendering
- * directly to the main framebuffer).
- * vp[0] and vp[1] should be set to 0.
- * The viewport width and height imply the target FBO or framebuffer's
- * size. It will always render to the full size of it.
+ * @param w Width of the framebuffer. This is either the video size if the fbo
+ * parameter is 0, or the allocated size of the texture backing the
+ * fbo. The renderer will always use the full size of the fbo.
+ * @param h Height of the framebuffer. Same as with the w parameter, except
+ * that this parameter can be negative. In this case, the video
+ * frame will be rendered flipped.
* @return the number of left frames in the internal queue to be rendered
*/
+int mpv_opengl_cb_draw(mpv_opengl_cb_context *ctx, int fbo, int w, int h);
+
+/**
+ * Deprecated. Use mpv_opengl_cb_draw(). This function is equivalent to:
+ *
+ * int mpv_opengl_cb_render(mpv_opengl_cb_context *ctx, int fbo, int vp[4])
+ * { return mpv_opengl_cb_draw(ctx, fbo, vp[2], vp[3]); }
+ *
+ * vp[0] and vp[1] used to have a meaning, but are ignored in newer versions.
+ *
+ * This function will be removed in the future without version bump (this API
+ * was never marked as stable).
+ */
int mpv_opengl_cb_render(mpv_opengl_cb_context *ctx, int fbo, int vp[4]);
/**
+ * Tell the renderer that a frame was flipped at the given time. This is
+ * optional, but can help the player to achieve better timing.
+ *
+ * If this is called while no video or no OpenGL is initialized, it is ignored.
+ *
+ * @param time The mpv time (using mpv_get_time_us()) at which the flip call
+ * returned. If 0 is passed, mpv_get_time_us() is used instead.
+ * @return error code
+ */
+int mpv_opengl_cb_report_flip(mpv_opengl_cb_context *ctx, int64_t time);
+
+/**
* Destroy the mpv OpenGL state.
*
* If video is still active (e.g. a file playing), video will be disabled
diff --git a/misc/bstr.c b/misc/bstr.c
index bb8446d..b168acc 100644
--- a/misc/bstr.c
+++ b/misc/bstr.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
diff --git a/misc/bstr.h b/misc/bstr.h
index 6871da1..ef5591a 100644
--- a/misc/bstr.h
+++ b/misc/bstr.h
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_BSTR_H
diff --git a/old-configure b/old-configure
index 8a37e42..b41e7b5 100755
--- a/old-configure
+++ b/old-configure
@@ -164,8 +164,6 @@ options_state_machine() {
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 _lirc "LIRC (remote control) support"
- opt_yes_no _joystick "joystick support" no
opt_yes_no _vm "X video mode extensions"
opt_yes_no _dvb "DVB input"
opt_yes_no _tv "TV interface (TV/DVB grabbers)" yes
@@ -183,10 +181,10 @@ options_state_machine() {
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 _ladspa "LADSPA plugin support"
opt_yes_no _libbs2b "libbs2b audio filter support"
- opt_yes_no _mpg123 " libmpg123 MP3 decoding support"
opt_yes_no _libavresample "libavresample (preferred over libswresample)"
opt_yes_no _libswresample "libswresample"
opt_yes_no _caca "CACA video output"
@@ -326,7 +324,7 @@ test -z "$_bindir" && _bindir="$_prefix/bin"
test -z "$_mandir" && _mandir="$_prefix/share/man"
test -z "$_confdir" && _confdir="$_prefix/etc/mpv"
-mplayer_tmpdir=$(mktemp -d --tmpdir mpv-configure-XXXX)
+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
@@ -378,7 +376,7 @@ addcflags() { cflag_check "$@" && OURCFLAGS="$OURCFLAGS $@" ; }
OURCFLAGS="-std=c99 -Wall $_opt"
addcflags -g -g3 -ggdb
-addcflags -Wundef -Wmissing-prototypes -Wshadow -Wno-switch -Wno-parentheses -Wpointer-arith -Wredundant-decls -Wno-pointer-sign -Werror=implicit-function-declaration -Wno-error=deprecated-declarations -Wno-error=unused-function
+addcflags -Wundef -Wmissing-prototypes -Wshadow -Wno-switch -Wparentheses -Wpointer-arith -Wredundant-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
@@ -663,6 +661,8 @@ EOF
_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
@@ -717,6 +717,8 @@ 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)
@@ -734,8 +736,6 @@ check_pkg_config "ENCA" $_enca ENCA 'enca'
check_pkg_config "zlib" auto ZLIB 'zlib'
test $(defretval) = no && die "Unable to find development files for zlib."
-check_pkg_config "mpg123 support" $_mpg123 MPG123 'libmpg123 >= 1.14.0'
-
test "$_dl" = no && _ladspa=no
check_statement_libs "LADSPA plugin support" $_ladspa LADSPA ladspa.h 'LADSPA_Descriptor ld = {0}'
@@ -744,15 +744,15 @@ check_pkg_config "libbs2b audio filter support" $_libbs2b LIBBS2B 'libbs2b'
check_pkg_config "LCMS2 support" $_lcms2 LCMS2 'lcms2 >= 2.6'
check_pkg_config "FFmpeg/Libav" $_ffmpeg FFMPEG \
- "libavutil >= 52.48.101 libavcodec >= 55.34.1 libavformat >= 55.12.0 libswscale >= 2.1.2"
+ "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 >= 0.17.104'
+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 >= 1.1.0'
+check_pkg_config "Libavresample" $_libavresample LIBAVRESAMPLE 'libavresample >= 2.1.0'
_libavresample=$(defretval)
if test "$_libswresample" = no && test "$_libavresample" = no ; then
@@ -775,36 +775,6 @@ api_statement_check \
'int x, y; avcodec_enum_to_chroma_pos(&x, &y, AVCHROMA_LOC_UNSPECIFIED)'
api_statement_check \
- "libavcodec avcol_spc_bt2020 available" \
- HAVE_AVCOL_SPC_BT2020 \
- libavcodec/avcodec.h \
- 'int x = AVCOL_SPC_BT2020_NCL'
-
-api_statement_check \
- "libavcodec metadata update side data" \
- HAVE_AVCODEC_METADATA_UPDATE_SIDE_DATA \
- libavcodec/avcodec.h \
- 'enum AVPacketSideDataType type = AV_PKT_DATA_METADATA_UPDATE'
-
-api_statement_check \
- "libavformat metadata update flags" \
- HAVE_AVFORMAT_METADATA_UPDATE_FLAG \
- libavformat/avformat.h \
- 'int x = AVFMT_EVENT_FLAG_METADATA_UPDATED'
-
-api_statement_check \
- "libavcodec replaygain side data" \
- HAVE_AVCODEC_REPLAYGAIN_SIDE_DATA \
- libavcodec/avcodec.h \
- 'enum AVPacketSideDataType type = AV_PKT_DATA_REPLAYGAIN'
-
-api_statement_check \
- "libavutil/libavcodec display matrix side data" \
- HAVE_AV_DISPLAYMATRIX \
- libavutil/frame.h \
- 'enum AVFrameSideDataType type = AV_FRAME_DATA_DISPLAYMATRIX'
-
-api_statement_check \
"libavutil AVFrame metadata" \
HAVE_AVFRAME_METADATA \
libavutil/frame.h \
@@ -816,15 +786,9 @@ api_statement_check \
libavutil/frame.h \
'enum AVFrameSideDataType type = AV_FRAME_DATA_SKIP_SAMPLES'
-api_statement_check \
- "libavcodec av_vdpau_alloc_context()" \
- HAVE_AVCODEC_VDPAU_ALLOC_CONTEXT \
- libavcodec/vdpau.h \
- 'AVVDPAUContext *x = av_vdpau_alloc_context()'
-
-check_pkg_config "libavfilter" $_libavfilter LIBAVFILTER 'libavfilter >= 3.90.100'
+check_pkg_config "libavfilter" $_libavfilter LIBAVFILTER 'libavfilter >= 5.0.0'
-check_pkg_config "libavdevice" $_libavdevice LIBAVDEVICE 'libavdevice >= 54.0.0'
+check_pkg_config "libavdevice" $_libavdevice LIBAVDEVICE 'libavdevice >= 55.0.0'
check_trivial "TV interface" $_tv TV
@@ -851,13 +815,15 @@ test_lua() {
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 "lua >= 5.2.0"
test_lua "lua5.2 >= 5.2.0" # debian
+test_lua "lua52 >= 5.2.0" # OpenBSD
test "$_lua" != yes && check_yes_no no LUA
-if ! ( $_pkg_config 'vapoursynth >= 23' ) ; then
+if ! ( $_pkg_config 'vapoursynth >= 24' ) ; then
_vapoursynth=no
_vapoursynth_lazy=no
fi
@@ -875,10 +841,6 @@ if test "$_vapoursynth" = no && test "$_vapoursynth_lazy" = no ; then
fi
check_trivial "VapourSynth core" $_vapoursynth_core VAPOURSYNTH_CORE
-check_trivial "joystick" $_joystick JOYSTICK
-
-check_statement_libs "lirc" $_lirc LIRC lirc/lirc_client.h "" -llirc_client
-
check_trivial "encoding" $_encoding ENCODING
# needs dlopen on unix
@@ -969,9 +931,19 @@ cat > $TMPC << EOF
#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
+
+#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"
diff --git a/old-makefile b/old-makefile
index fd7d575..7adc45d 100644
--- a/old-makefile
+++ b/old-makefile
@@ -36,6 +36,7 @@ SOURCES-$(DVDNAV) += stream/stream_dvdnav.c \
stream/stream_dvd_common.c
SOURCES-$(LADSPA) += audio/filter/af_ladspa.c
+SOURCES-$(RUBBERBAND) += audio/filter/af_rubberband.c
SOURCES-$(LIBASS) += sub/ass_mp.c sub/sd_ass.c \
demux/demux_libass.c
@@ -44,8 +45,6 @@ SOURCES-$(LIBBS2B) += audio/filter/af_bs2b.c
SOURCES-$(LIBSMBCLIENT) += stream/stream_smb.c
-SOURCES-$(MPG123) += audio/decode/ad_mpg123.c
-
SOURCES-$(PVR) += stream/stream_pvr.c
SOURCES-$(TV) += stream/stream_tv.c stream/tv.c \
@@ -74,8 +73,6 @@ SOURCES-$(GL_WAYLAND) += video/out/wayland_common.c \
video/out/gl_wayland.c
SOURCES-$(JACK) += audio/out/ao_jack.c
-SOURCES-$(JOYSTICK) += input/joystick.c
-SOURCES-$(LIRC) += input/lirc.c
SOURCES-$(OPENAL) += audio/out/ao_openal.c
SOURCES-$(OSS_AUDIO) += audio/out/ao_oss.c
SOURCES-$(PULSE) += audio/out/ao_pulse.c
@@ -133,7 +130,6 @@ SOURCES = audio/audio.c \
audio/filter/af_equalizer.c \
audio/filter/af_export.c \
audio/filter/af_extrastereo.c \
- audio/filter/af_forcespeed.c \
audio/filter/af_format.c \
audio/filter/af_hrtf.c \
audio/filter/af_karaoke.c \
@@ -173,6 +169,7 @@ SOURCES = audio/audio.c \
demux/demux_mkv.c \
demux/demux_mkv_timeline.c \
demux/demux_playlist.c \
+ demux/demux_rar.c \
demux/demux_raw.c \
demux/demux_subreader.c \
demux/ebml.c \
@@ -199,6 +196,7 @@ SOURCES = audio/audio.c \
options/path.c \
osdep/io.c \
osdep/semaphore_osx.c \
+ osdep/subprocess.c \
osdep/subprocess-posix.c \
osdep/terminal-unix.c \
osdep/timer.c \
@@ -265,7 +263,6 @@ SOURCES = audio/audio.c \
video/filter/vf_mirror.c \
video/filter/vf_noformat.c \
video/filter/vf_scale.c \
- video/filter/vf_screenshot.c \
video/filter/vf_stereo3d.c \
video/filter/vf_sub.c \
video/out/bitmap_packer.c \
@@ -337,7 +334,7 @@ all: $(ALL_TARGETS)
%.o: %.c
$(CC) $(DEPFLAGS) $(CFLAGS) -c -o $@ $<
-mpv: $(OBJECTS) player/main_fn.o
+mpv: $(OBJECTS) player/main-fn-unix.o
$(CC) -o $@ $^ $(EXTRALIBS)
input/input.c: input/input_conf.h
@@ -358,10 +355,6 @@ demux/ebml.c: demux/ebml_defs.c
demux/ebml_defs.c: TOOLS/matroska.pl $(MKVLIB_DEPS)
./$< --generate-definitions > $@
-video/out/gl_video.c: video/out/gl_video_shaders.h
-video/out/gl_video_shaders.h: TOOLS/file2string.pl video/out/gl_video_shaders.glsl
- ./$^ >$@
-
video/out/x11_common.c: video/out/x11_icon.inc
video/out/x11_icon.inc: TOOLS/file2string.pl video/out/x11_icon.bin
./$^ >$@
@@ -475,7 +468,6 @@ clean:
-$(RM) input/input_conf.h
-$(RM) video/out/vdpau_template.c
-$(RM) demux/ebml_types.h demux/ebml_defs.c
- -$(RM) video/out/gl_video_shaders.h
-$(RM) video/out/x11_icon.inc
-$(RM) sub/osd_font.h
-$(RM) player/lua/defaults.inc
diff --git a/options/m_config.c b/options/m_config.c
index 43fd508..4809191 100644
--- a/options/m_config.c
+++ b/options/m_config.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
/// \file
@@ -42,6 +41,8 @@ static const union m_option_value default_value;
// Profiles allow to predefine some sets of options that can then
// be applied later on with the internal -profile option.
#define MAX_PROFILE_DEPTH 20
+// Maximal include depth.
+#define MAX_RECURSION_DEPTH 8
struct m_profile {
struct m_profile *next;
@@ -66,8 +67,14 @@ static int parse_include(struct m_config *config, struct bstr param, bool set,
return M_OPT_MISSING_PARAM;
if (!set)
return 1;
+ if (config->recursion_depth >= MAX_RECURSION_DEPTH) {
+ MP_ERR(config, "Maximum 'include' nesting depth exceeded.\n");
+ return M_OPT_INVALID;
+ }
char *filename = bstrdup0(NULL, param);
+ config->recursion_depth += 1;
config->includefunc(config->includefunc_ctx, filename, flags);
+ config->recursion_depth -= 1;
talloc_free(filename);
return 1;
}
@@ -453,7 +460,9 @@ static void m_config_add_option(struct m_config *config,
struct m_config_option *m_config_get_co(const struct m_config *config,
struct bstr name)
{
- const char *prefix = config->is_toplevel ? "--" : "";
+ if (!name.len)
+ return NULL;
+
for (int n = 0; n < config->num_opts; n++) {
struct m_config_option *co = &config->opts[n];
struct bstr coname = bstr0(co->name);
@@ -466,6 +475,7 @@ struct m_config_option *m_config_get_co(const struct m_config *config,
} else if (bstrcmp(coname, name) == 0)
matches = true;
if (matches) {
+ const char *prefix = config->is_toplevel ? "--" : "";
if (co->opt->type == &m_option_type_alias) {
const char *alias = (const char *)co->opt->priv;
if (!co->warning_was_printed) {
@@ -590,7 +600,6 @@ static int m_config_parse_option(struct m_config *config, struct bstr name,
struct bstr param, int flags)
{
assert(config != NULL);
- assert(name.len != 0);
struct m_config_option *co = m_config_get_co(config, name);
if (!co)
diff --git a/options/m_config.h b/options/m_config.h
index 9993909..ba14c05 100644
--- a/options/m_config.h
+++ b/options/m_config.h
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_M_CONFIG_H
diff --git a/options/m_option.c b/options/m_option.c
index ca90c52..bd8e335 100644
--- a/options/m_option.c
+++ b/options/m_option.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
/// \file
@@ -61,7 +60,7 @@ char *m_option_strerror(int code)
case M_OPT_MISSING_PARAM:
return "option requires parameter";
case M_OPT_INVALID:
- return "option parameter could not be parsed";
+ return "option could not be parsed";
case M_OPT_OUT_OF_RANGE:
return "parameter is outside values allowed for option";
case M_OPT_DISALLOW_PARAM:
@@ -74,9 +73,15 @@ char *m_option_strerror(int code)
int m_option_required_params(const m_option_t *opt)
{
- if (((opt->flags & M_OPT_OPTIONAL_PARAM) ||
- (opt->type->flags & M_OPT_TYPE_OPTIONAL_PARAM)))
+ if (opt->type->flags & M_OPT_TYPE_OPTIONAL_PARAM)
return 0;
+ if (opt->type == &m_option_type_choice) {
+ struct m_opt_choice_alternatives *alt;
+ for (alt = opt->priv; alt->name; alt++) {
+ if (strcmp(alt->name, "yes") == 0)
+ return 0;
+ }
+ }
return 1;
}
@@ -124,29 +129,23 @@ static int clamp_flag(const m_option_t *opt, void *val)
static int parse_flag(struct mp_log *log, const m_option_t *opt,
struct bstr name, struct bstr param, void *dst)
{
- if (param.len) {
- if (!bstrcmp0(param, "yes")) {
- if (dst)
- VAL(dst) = 1;
- return 1;
- }
- if (!bstrcmp0(param, "no")) {
- if (dst)
- VAL(dst) = 0;
- return 1;
- }
- mp_fatal(log, "Invalid parameter for %.*s flag: %.*s\n",
- BSTR_P(name), BSTR_P(param));
- mp_info(log, "Valid values are:\n");
- mp_info(log, " yes\n");
- mp_info(log, " no\n");
- mp_info(log, " (passing nothing)\n");
- return M_OPT_INVALID;
- } else {
+ if (bstr_equals0(param, "yes") || !param.len) {
if (dst)
VAL(dst) = 1;
- return 0;
+ return 1;
}
+ if (bstr_equals0(param, "no")) {
+ if (dst)
+ VAL(dst) = 0;
+ return 1;
+ }
+ mp_fatal(log, "Invalid parameter for %.*s flag: %.*s\n",
+ BSTR_P(name), BSTR_P(param));
+ mp_info(log, "Valid values are:\n");
+ mp_info(log, " yes\n");
+ mp_info(log, " no\n");
+ mp_info(log, " (passing nothing)\n");
+ return M_OPT_INVALID;
}
static char *print_flag(const m_option_t *opt, const void *val)
@@ -522,6 +521,16 @@ const struct m_option_type m_option_type_intpair = {
.copy = copy_opt,
};
+const char *m_opt_choice_str(const struct m_opt_choice_alternatives *choices,
+ int value)
+{
+ for (const struct m_opt_choice_alternatives *c = choices; c->name; c++) {
+ if (c->value == value)
+ return c->name;
+ }
+ return NULL;
+}
+
static int clamp_choice(const m_option_t *opt, void *val)
{
int v = *(int *)val;
@@ -541,9 +550,17 @@ static int parse_choice(struct mp_log *log, const struct m_option *opt,
struct bstr name, struct bstr param, void *dst)
{
struct m_opt_choice_alternatives *alt = opt->priv;
- for ( ; alt->name; alt++)
+ for ( ; alt->name; alt++) {
if (!bstrcmp0(param, alt->name))
break;
+ }
+ if (!alt->name && param.len == 0) {
+ // allow flag-style options, e.g. "--mute" implies "--mute=yes"
+ for (alt = opt->priv; alt->name; alt++) {
+ if (!strcmp("yes", alt->name))
+ break;
+ }
+ }
if (!alt->name) {
if (param.len == 0)
return M_OPT_MISSING_PARAM;
@@ -710,6 +727,123 @@ const struct m_option_type m_option_type_choice = {
.get = choice_get,
};
+static int apply_flag(const struct m_option *opt, int *val, bstr flag)
+{
+ struct m_opt_choice_alternatives *alt;
+ for (alt = opt->priv; alt->name; alt++) {
+ if (bstr_equals0(flag, alt->name)) {
+ if (*val & alt->value)
+ return M_OPT_INVALID;
+ *val |= alt->value;
+ return 0;
+ }
+ }
+ return M_OPT_UNKNOWN;
+}
+
+static const char *find_next_flag(const struct m_option *opt, int *val)
+{
+ struct m_opt_choice_alternatives *alt;
+ for (alt = opt->priv; alt->name; alt++) {
+ if (alt->value && (alt->value & (*val)) == alt->value) {
+ *val = *val & ~(unsigned)alt->value;
+ return alt->name;
+ }
+ }
+ *val = 0; // if there are still flags left, there's not much we can do
+ return NULL;
+}
+
+static int parse_flags(struct mp_log *log, const struct m_option *opt,
+ struct bstr name, struct bstr param, void *dst)
+{
+ int value = 0;
+ while (param.len) {
+ bstr flag;
+ bstr_split_tok(param, "+", &flag, &param);
+ int r = apply_flag(opt, &value, flag);
+ if (r == M_OPT_UNKNOWN) {
+ mp_fatal(log, "Invalid flag for option %.*s: %.*s\n",
+ BSTR_P(name), BSTR_P(flag));
+ mp_info(log, "Valid flags are:\n");
+ struct m_opt_choice_alternatives *alt;
+ for (alt = opt->priv; alt->name; alt++)
+ mp_info(log, " %s\n", alt->name);
+ mp_info(log, "Flags can usually be combined with '+'.\n");
+ return M_OPT_INVALID;
+ } else if (r < 0) {
+ mp_fatal(log, "Option %.*s: flag '%.*s' conflicts with a previous "
+ "flag value.\n", BSTR_P(name), BSTR_P(flag));
+ return M_OPT_INVALID;
+ }
+ }
+ if (dst)
+ *(int *)dst = value;
+ return 1;
+}
+
+static int flags_set(const m_option_t *opt, void *dst, struct mpv_node *src)
+{
+ int value = 0;
+ if (src->format != MPV_FORMAT_NODE_ARRAY)
+ return M_OPT_UNKNOWN;
+ struct mpv_node_list *srclist = src->u.list;
+ for (int n = 0; n < srclist->num; n++) {
+ if (srclist->values[n].format != MPV_FORMAT_STRING)
+ return M_OPT_INVALID;
+ if (apply_flag(opt, &value, bstr0(srclist->values[n].u.string)) < 0)
+ return M_OPT_INVALID;
+ }
+ *(int *)dst = value;
+ return 0;
+}
+
+static int flags_get(const m_option_t *opt, void *ta_parent,
+ struct mpv_node *dst, void *src)
+{
+ int value = *(int *)src;
+
+ dst->format = MPV_FORMAT_NODE_ARRAY;
+ dst->u.list = talloc_zero(ta_parent, struct mpv_node_list);
+ struct mpv_node_list *list = dst->u.list;
+ while (1) {
+ const char *flag = find_next_flag(opt, &value);
+ if (!flag)
+ break;
+
+ struct mpv_node node;
+ node.format = MPV_FORMAT_STRING;
+ node.u.string = (char *)flag;
+ MP_TARRAY_APPEND(list, list->values, list->num, node);
+ }
+
+ return 1;
+}
+
+static char *print_flags(const m_option_t *opt, const void *val)
+{
+ int value = *(int *)val;
+ char *res = talloc_strdup(NULL, "");
+ while (1) {
+ const char *flag = find_next_flag(opt, &value);
+ if (!flag)
+ break;
+
+ res = talloc_asprintf_append_buffer(res, "%s%s", res[0] ? "+" : "", flag);
+ }
+ return res;
+}
+
+const struct m_option_type m_option_type_flags = {
+ .name = "Flags",
+ .size = sizeof(int),
+ .parse = parse_flags,
+ .print = print_flags,
+ .copy = copy_opt,
+ .set = flags_set,
+ .get = flags_get,
+};
+
// Float
#undef VAL
@@ -1602,11 +1736,22 @@ static int read_subparam(struct mp_log *log, bstr optname,
}
p = bstr_cut(p, 1);
} else if (bstr_eatstart0(&p, "[")) {
- if (!bstr_split_tok(p, "]", &subparam, &p)) {
+ bstr s = p;
+ int balance = 1;
+ while (p.len && balance > 0) {
+ if (p.start[0] == '[') {
+ balance++;
+ } else if (p.start[0] == ']') {
+ balance--;
+ }
+ p = bstr_cut(p, 1);
+ }
+ if (balance != 0) {
mp_err(log, "Terminating ']' missing for '%.*s'\n",
BSTR_P(optname));
return M_OPT_INVALID;
}
+ subparam = bstr_splice(s, 0, s.len - p.len - 1);
} else if (bstr_eatstart0(&p, "%")) {
int optlen = bstrtoll(p, &p, 0);
if (!bstr_startswith0(p, "%") || (optlen > p.len - 1)) {
@@ -1865,6 +2010,31 @@ error:
#undef READ_NUM
#undef READ_SIGN
+#define APPEND_PER(F, F_PER) \
+ res = talloc_asprintf_append(res, "%d%s", gm->F, gm->F_PER ? "%" : "")
+
+static char *print_geometry(const m_option_t *opt, const void *val)
+{
+ const struct m_geometry *gm = val;
+ char *res = talloc_strdup(NULL, "");
+ if (gm->wh_valid || gm->xy_valid) {
+ if (gm->wh_valid) {
+ APPEND_PER(w, w_per);
+ res = talloc_asprintf_append(res, "x");
+ APPEND_PER(h, h_per);
+ }
+ if (gm->xy_valid) {
+ res = talloc_asprintf_append(res, gm->x_sign ? "-" : "+");
+ APPEND_PER(x, x_per);
+ res = talloc_asprintf_append(res, gm->y_sign ? "-" : "+");
+ APPEND_PER(y, y_per);
+ }
+ }
+ return res;
+}
+
+#undef APPEND_PER
+
// xpos,ypos: position of the left upper corner
// widw,widh: width and height of the window
// scrw,scrh: width and height of the current screen
@@ -1928,6 +2098,7 @@ const m_option_type_t m_option_type_geometry = {
.name = "Window geometry",
.size = sizeof(struct m_geometry),
.parse = parse_geometry,
+ .print = print_geometry,
.copy = copy_opt,
};
@@ -1957,6 +2128,7 @@ const m_option_type_t m_option_type_size_box = {
.name = "Window size",
.size = sizeof(struct m_geometry),
.parse = parse_size_box,
+ .print = print_geometry,
.copy = copy_opt,
};
@@ -2000,59 +2172,6 @@ const m_option_type_t m_option_type_imgfmt = {
.copy = copy_opt,
};
-#include "video/csputils.h"
-
-static int parse_stereo_mode(struct mp_log *log, const m_option_t *opt,
- struct bstr name, struct bstr param, void *dst)
-{
- if (param.len == 0)
- return M_OPT_MISSING_PARAM;
-
- if (!bstrcmp0(param, "help")) {
- mp_info(log, "Available modes:");
- for (int n = 0; n < MP_STEREO3D_COUNT; n++) {
- if (mp_stereo3d_names[n])
- mp_info(log, " %s\n", mp_stereo3d_names[n]);
- }
- mp_info(log, " none\n");
- return M_OPT_EXIT - 1;
- }
-
- int mode = -1;
-
- for (int n = 0; n < MP_STEREO3D_COUNT; n++) {
- if (bstr_equals(param, bstr0(mp_stereo3d_names[n]))) {
- mode = n;
- break;
- }
- }
-
- if (mode < 0 && !bstr_equals0(param, "none")) {
- mp_err(log, "Option %.*s: unknown parameter: '%.*s'\n",
- BSTR_P(name), BSTR_P(param));
- return M_OPT_INVALID;
- }
-
- if (dst)
- *((int *)dst) = mode;
-
- return 1;
-}
-
-static char *print_stereo_mode(const m_option_t *opt, const void *val)
-{
- int mode = *(int *)val;
- const char *name = mode >= 0 ? MP_STEREO3D_NAME(mode) : "none";
- return talloc_strdup(NULL, name);
-}
-
-const m_option_type_t m_option_vid_stereo_mode = {
- .name = "Stereo 3D mode",
- .size = sizeof(int),
- .parse = parse_stereo_mode,
- .print = print_stereo_mode,
-};
-
static int parse_fourcc(struct mp_log *log, const m_option_t *opt,
struct bstr name, struct bstr param, void *dst)
{
@@ -2826,6 +2945,11 @@ static int parse_obj_settings_list(struct mp_log *log, const m_option_t *opt,
}
if (op == OP_CLR) {
+ if (param.len) {
+ mp_err(log, "Option %.*s: -clr does not take an argument.\n",
+ BSTR_P(name));
+ return M_OPT_INVALID;
+ }
if (dst)
free_obj_settings_list(dst);
return 0;
diff --git a/options/m_option.h b/options/m_option.h
index c05e2d7..1775688 100644
--- a/options/m_option.h
+++ b/options/m_option.h
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_M_OPTION_H
@@ -52,11 +51,11 @@ extern const m_option_type_t m_option_type_keyvalue_list;
extern const m_option_type_t m_option_type_time;
extern const m_option_type_t m_option_type_rel_time;
extern const m_option_type_t m_option_type_choice;
+extern const m_option_type_t m_option_type_flags;
extern const m_option_type_t m_option_type_msglevels;
extern const m_option_type_t m_option_type_print_fn;
extern const m_option_type_t m_option_type_subconfig;
extern const m_option_type_t m_option_type_imgfmt;
-extern const m_option_type_t m_option_vid_stereo_mode;
extern const m_option_type_t m_option_type_fourcc;
extern const m_option_type_t m_option_type_afmt;
extern const m_option_type_t m_option_type_color;
@@ -164,6 +163,9 @@ struct m_opt_choice_alternatives {
int value;
};
+const char *m_opt_choice_str(const struct m_opt_choice_alternatives *choices,
+ int value);
+
// For OPT_STRING_VALIDATE(). Behaves like m_option_type.parse().
typedef int (*m_opt_string_validate_fn)(struct mp_log *log, const m_option_t *opt,
struct bstr name, struct bstr param);
@@ -358,9 +360,6 @@ struct m_option {
// The option should be set during command line pre-parsing
#define M_OPT_PRE_PARSE (1 << 6)
-// See M_OPT_TYPE_OPTIONAL_PARAM.
-#define M_OPT_OPTIONAL_PARAM (1 << 10)
-
// The option expects a file name (or a list of file names)
#define M_OPT_FILE (1 << 11)
@@ -628,6 +627,14 @@ extern const char m_option_path_separator;
OPT_CHOICE_(__VA_ARGS__, .type = &m_option_type_choice)
#define OPT_CHOICE_(optname, varname, flags, choices, ...) \
OPT_GENERAL(int, optname, varname, flags, M_CHOICES(choices), __VA_ARGS__)
+// Variant which takes a pointer to struct m_opt_choice_alternatives directly
+#define OPT_CHOICE_C(optname, varname, flags, choices) \
+ OPT_GENERAL(int, optname, varname, flags, .priv = (void *) \
+ MP_EXPECT_TYPE(const struct m_opt_choice_alternatives*, choices), \
+ .type = &m_option_type_choice)
+
+#define OPT_FLAGS(...) \
+ OPT_CHOICE_(__VA_ARGS__, .type = &m_option_type_flags)
// Union of choices and an int range. The choice values can be included in the
// int range, or be completely separate - both works.
@@ -656,9 +663,6 @@ extern const char m_option_path_separator;
#define OPT_TRACKCHOICE(name, var) \
OPT_CHOICE_OR_INT(name, var, 0, 0, 8190, ({"no", -2}, {"auto", -1}))
-#define OPT_VID_STEREO_MODE(...) \
- OPT_GENERAL(int, __VA_ARGS__, .type = &m_option_vid_stereo_mode)
-
#define OPT_STRING_VALIDATE_(optname, varname, flags, validate_fn, ...) \
OPT_GENERAL(char*, optname, varname, flags, __VA_ARGS__, \
.priv = MP_EXPECT_TYPE(m_opt_string_validate_fn, validate_fn))
diff --git a/options/m_property.c b/options/m_property.c
index d16767b..7b1505c 100644
--- a/options/m_property.c
+++ b/options/m_property.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
/// \file
@@ -273,7 +272,8 @@ static int m_property_do_bstr(const struct m_property *prop_list, bstr name,
static void append_str(char **s, int *len, bstr append)
{
MP_TARRAY_GROW(NULL, *s, *len + append.len);
- memcpy(*s + *len, append.start, append.len);
+ if (append.len)
+ memcpy(*s + *len, append.start, append.len);
*len = *len + append.len;
}
diff --git a/options/m_property.h b/options/m_property.h
index 0336677..7c5f924 100644
--- a/options/m_property.h
+++ b/options/m_property.h
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_M_PROPERTY_H
diff --git a/options/options.c b/options/options.c
index a0674b6..08ee420 100644
--- a/options/options.c
+++ b/options/options.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_CFG_MPLAYER_H
@@ -68,6 +67,7 @@ extern const struct m_sub_options sws_conf;
extern const struct m_sub_options demux_rawaudio_conf;
extern const struct m_sub_options demux_rawvideo_conf;
extern const struct m_sub_options demux_lavf_conf;
+extern const struct m_sub_options demux_mkv_conf;
extern const struct m_sub_options vd_lavc_conf;
extern const struct m_sub_options ad_lavc_conf;
extern const struct m_sub_options input_config;
@@ -99,7 +99,7 @@ const m_option_t mp_opts[] = {
{ "leak-report", CONF_TYPE_STORE, CONF_GLOBAL | CONF_NOCFG | M_OPT_FIXED,
.offset = -1 },
- OPT_FLAG("shuffle", shuffle, CONF_GLOBAL | CONF_NOCFG),
+ OPT_FLAG("shuffle", shuffle, 0),
// ------------------------- common options --------------------
OPT_FLAG("quiet", quiet, CONF_GLOBAL),
@@ -109,7 +109,7 @@ const m_option_t mp_opts[] = {
.type = &m_option_type_msglevels),
OPT_STRING("dump-stats", dump_stats, CONF_GLOBAL | CONF_PRE_PARSE),
OPT_FLAG("msg-color", msg_color, CONF_GLOBAL | CONF_PRE_PARSE),
- OPT_STRING("log-file", log_file, CONF_GLOBAL | CONF_PRE_PARSE),
+ OPT_STRING("log-file", log_file, CONF_GLOBAL | CONF_PRE_PARSE | M_OPT_FILE),
OPT_FLAG("msg-module", msg_module, CONF_GLOBAL),
OPT_FLAG("msg-time", msg_time, CONF_GLOBAL),
#ifdef _WIN32
@@ -133,6 +133,7 @@ const m_option_t mp_opts[] = {
OPT_FLAG("osc", lua_load_osc, CONF_GLOBAL),
OPT_FLAG("ytdl", lua_load_ytdl, CONF_GLOBAL),
OPT_STRING("ytdl-format", lua_ytdl_format, CONF_GLOBAL),
+ OPT_KEYVALUELIST("ytdl-raw-options", lua_ytdl_raw_options, CONF_GLOBAL),
OPT_FLAG("load-scripts", auto_load_scripts, CONF_GLOBAL),
#endif
@@ -140,7 +141,8 @@ const m_option_t mp_opts[] = {
OPT_CHOICE_OR_INT("cache", stream_cache.size, 0, 32, 0x7fffffff,
({"no", 0},
- {"auto", -1})),
+ {"auto", -1},
+ {"yes", -2})),
OPT_CHOICE_OR_INT("cache-default", stream_cache.def_size, 0, 32, 0x7fffffff,
({"no", 0})),
OPT_INTRANGE("cache-initial", stream_cache.initial, 0, 0, 0x7fffffff),
@@ -188,9 +190,9 @@ const m_option_t mp_opts[] = {
OPT_TIME("ab-loop-b", ab_loop[1], 0, .min = MP_NOPTS_VALUE),
OPT_FLAG("pause", pause, M_OPT_FIXED),
- OPT_CHOICE("keep-open", keep_open, M_OPT_OPTIONAL_PARAM,
+ OPT_CHOICE("keep-open", keep_open, 0,
({"no", 0},
- {"yes", 1}, {"", 1},
+ {"yes", 1},
{"always", 2})),
OPT_CHOICE("index", index_mode, 0, ({"default", 1}, {"recreate", 0})),
@@ -273,10 +275,10 @@ const m_option_t mp_opts[] = {
OPT_SETTINGSLIST("vf-defaults", vf_defs, 0, &vf_obj_list),
OPT_SETTINGSLIST("vf*", vf_settings, 0, &vf_obj_list),
- OPT_CHOICE("deinterlace", deinterlace, M_OPT_OPTIONAL_PARAM,
+ OPT_CHOICE("deinterlace", deinterlace, 0,
({"auto", -1},
{"no", 0},
- {"yes", 1}, {"", 1})),
+ {"yes", 1})),
OPT_STRING("ad", audio_decoders, 0),
OPT_STRING("vd", video_decoders, 0),
@@ -290,7 +292,8 @@ const m_option_t mp_opts[] = {
{"vda", 2},
{"vaapi", 4},
{"vaapi-copy", 5},
- {"dxva2-copy", 6})),
+ {"dxva2-copy", 6},
+ {"rpi", 7})),
OPT_STRING("hwdec-codecs", hwdec_codecs, 0),
OPT_SUBSTRUCT("sws", vo.sws_opts, sws_conf, 0),
@@ -309,11 +312,7 @@ const m_option_t mp_opts[] = {
OPT_SUBSTRUCT("demuxer-lavf", demux_lavf, demux_lavf_conf, 0),
OPT_SUBSTRUCT("demuxer-rawaudio", demux_rawaudio, demux_rawaudio_conf, 0),
OPT_SUBSTRUCT("demuxer-rawvideo", demux_rawvideo, demux_rawvideo_conf, 0),
-
- OPT_FLAG("demuxer-mkv-subtitle-preroll", mkv_subtitle_preroll, 0),
- OPT_DOUBLE("demuxer-mkv-subtitle-preroll-secs", mkv_subtitle_preroll_secs,
- M_OPT_MIN, .min = 0),
- OPT_FLAG("demuxer-mkv-probe-video-duration", mkv_probe_duration, 0),
+ OPT_SUBSTRUCT("demuxer-mkv", demux_mkv, demux_mkv_conf, 0),
// ------------------------- subtitles options --------------------
@@ -337,7 +336,8 @@ const m_option_t mp_opts[] = {
OPT_FLAG("sub-ass", ass_enabled, 0),
OPT_FLOATRANGE("sub-scale", sub_scale, 0, 0, 100),
OPT_FLOATRANGE("ass-line-spacing", ass_line_spacing, 0, -1000, 1000),
- OPT_FLAG("ass-use-margins", ass_use_margins, 0),
+ OPT_FLAG("sub-use-margins", sub_use_margins, 0),
+ OPT_FLAG("ass-force-margins", ass_use_margins, 0),
OPT_FLAG("ass-vsfilter-aspect-compat", ass_vsfilter_aspect_compat, 0),
OPT_CHOICE("ass-vsfilter-color-compat", ass_vsfilter_color_compat, 0,
({"no", 0}, {"basic", 1}, {"full", 2}, {"force-601", 3})),
@@ -353,6 +353,7 @@ const m_option_t mp_opts[] = {
({"no", 0}, {"yes", 1}, {"force", 3}, {"signfs", 4})),
OPT_FLAG("sub-scale-by-window", sub_scale_by_window, 0),
OPT_FLAG("sub-scale-with-window", sub_scale_with_window, 0),
+ OPT_FLAG("ass-scale-with-window", ass_scale_with_window, 0),
OPT_FLAG("osd-bar", osd_bar_visible, 0),
OPT_FLOATRANGE("osd-bar-align-x", osd_bar_align_x, 0, -1.0, +1.0),
OPT_FLOATRANGE("osd-bar-align-y", osd_bar_align_y, 0, -1.0, +1.0),
@@ -360,7 +361,7 @@ const m_option_t mp_opts[] = {
OPT_FLOATRANGE("osd-bar-h", osd_bar_h, 0, 0.1, 50),
OPT_SUBSTRUCT("osd", osd_style, osd_style_conf, 0),
OPT_FLAG("use-text-osd", use_text_osd, CONF_GLOBAL),
- OPT_SUBSTRUCT("sub-text", sub_text_style, osd_style_conf, 0),
+ OPT_SUBSTRUCT("sub-text", sub_text_style, sub_style_conf, 0),
OPT_FLAG("sub-clear-on-seek", sub_clear_on_seek, 0),
//---------------------- libao/libvo options ------------------------
@@ -370,7 +371,7 @@ const m_option_t mp_opts[] = {
OPT_SETTINGSLIST("ao-defaults", ao_defs, 0, &ao_obj_list),
OPT_STRING("audio-device", audio_device, 0),
OPT_STRING("audio-client-name", audio_client_name, 0),
- OPT_FLAG("force-window", force_vo, CONF_GLOBAL),
+ OPT_FLAG("force-window", force_vo, 0),
OPT_FLAG("ontop", vo.ontop, M_OPT_FIXED),
OPT_FLAG("border", vo.border, M_OPT_FIXED),
OPT_FLAG("on-all-workspaces", vo.all_workspaces, M_OPT_FIXED),
@@ -383,14 +384,14 @@ const m_option_t mp_opts[] = {
{"auto", SOFTVOL_AUTO})),
OPT_FLOATRANGE("softvol-max", softvol_max, 0, 10, 10000),
OPT_FLOATRANGE("volume", mixer_init_volume, 0, -1, 100),
- OPT_CHOICE("mute", mixer_init_mute, M_OPT_OPTIONAL_PARAM,
+ OPT_CHOICE("mute", mixer_init_mute, 0,
({"auto", -1},
{"no", 0},
- {"yes", 1}, {"", 1})),
+ {"yes", 1})),
OPT_STRING("volume-restore-data", mixer_restore_volume_data, 0),
- OPT_CHOICE("gapless-audio", gapless_audio, M_OPT_OPTIONAL_PARAM,
+ OPT_CHOICE("gapless-audio", gapless_audio, 0,
({"no", 0},
- {"yes", 1}, {"", 1},
+ {"yes", 1},
{"weak", -1})),
OPT_DOUBLE("audio-buffer", audio_buffer, M_OPT_MIN | M_OPT_MAX,
.min = 0, .max = 10),
@@ -399,6 +400,7 @@ const m_option_t mp_opts[] = {
OPT_SIZE_BOX("autofit", vo.autofit, 0),
OPT_SIZE_BOX("autofit-larger", vo.autofit_larger, 0),
OPT_SIZE_BOX("autofit-smaller", vo.autofit_smaller, 0),
+ OPT_FLOATRANGE("window-scale", vo.window_scale, 0, 0.001, 100),
OPT_FLAG("force-window-position", vo.force_window_position, 0),
// vo name (X classname) and window title strings
OPT_STRING("x11-name", vo.winname, 0),
@@ -419,31 +421,9 @@ 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("colormatrix", requested_colorspace, 0,
- ({"auto", MP_CSP_AUTO},
- {"BT.601", MP_CSP_BT_601},
- {"BT.709", MP_CSP_BT_709},
- {"SMPTE-240M", MP_CSP_SMPTE_240M},
- {"BT.2020-NCL", MP_CSP_BT_2020_NC},
- {"BT.2020-CL", MP_CSP_BT_2020_C},
- {"YCgCo", MP_CSP_YCGCO})),
- OPT_CHOICE("colormatrix-input-range", requested_input_range, 0,
- ({"auto", MP_CSP_LEVELS_AUTO},
- {"limited", MP_CSP_LEVELS_TV},
- {"full", MP_CSP_LEVELS_PC})),
- OPT_CHOICE("colormatrix-output-range", requested_output_range, 0,
- ({"auto", MP_CSP_LEVELS_AUTO},
- {"limited", MP_CSP_LEVELS_TV},
- {"full", MP_CSP_LEVELS_PC})),
- OPT_CHOICE("colormatrix-primaries", requested_primaries, 0,
- ({"auto", MP_CSP_PRIM_AUTO},
- {"BT.601-525", MP_CSP_PRIM_BT_601_525},
- {"BT.601-625", MP_CSP_PRIM_BT_601_625},
- {"BT.709", MP_CSP_PRIM_BT_709},
- {"BT.2020", MP_CSP_PRIM_BT_2020})),
- OPT_CHOICE_OR_INT("video-rotate", video_rotate, 0, 0, 359,
+ OPT_CHOICE_OR_INT("video-rotate", video_rotate, 0, 0, 360,
({"no", -1})),
- OPT_VID_STEREO_MODE("video-stereo-mode", video_stereo_mode, 0),
+ OPT_CHOICE_C("video-stereo-mode", video_stereo_mode, 0, mp_stereo3d_names),
OPT_CHOICE_OR_INT("cursor-autohide", cursor_autohide_delay, 0,
0, 30000, ({"no", -1}, {"always", -2})),
@@ -473,9 +453,7 @@ const m_option_t mp_opts[] = {
OPT_FLAG("keepaspect", vo.keepaspect, 0),
OPT_FLAG("keepaspect-window", vo.keepaspect_window, 0),
-//---------------------- mplayer-only options ------------------------
-
- OPT_FLAG("use-filedir-conf", use_filedir_conf, M_OPT_GLOBAL),
+ OPT_FLAG("use-filedir-conf", use_filedir_conf, 0),
OPT_CHOICE("osd-level", osd_level, 0,
({"0", 0}, {"1", 1}, {"2", 2}, {"3", 3})),
OPT_INTRANGE("osd-duration", osd_duration, 0, 0, 3600000),
@@ -500,12 +478,13 @@ const m_option_t mp_opts[] = {
OPT_FLAG("stop-playback-on-init-failure", stop_playback_on_init_failure, 0),
- OPT_CHOICE_OR_INT("loop", loop_times, M_OPT_GLOBAL, 1, 10000,
+ OPT_CHOICE_OR_INT("loop", loop_times, 0, 1, 10000,
({"no", 1},
{"inf", -1},
{"force", -2})),
- OPT_CHOICE_OR_INT("loop-file", loop_file, M_OPT_OPTIONAL_PARAM, 0, 10000,
- ({"yes", -1}, {"", -1}, {"no", 0}, // compat
+ OPT_CHOICE_OR_INT("loop-file", loop_file, 0, 0, 10000,
+ ({"no", 0},
+ {"yes", -1},
{"inf", -1})),
OPT_FLAG("resume-playback", position_resume, 0),
@@ -530,8 +509,8 @@ const m_option_t mp_opts[] = {
({"auto", 0}, {"decoder", 1}, {"sort", 2})),
OPT_FLAG("initial-audio-sync", initial_audio_sync, 0),
OPT_CHOICE("hr-seek", hr_seek, 0,
- ({"no", -1}, {"absolute", 0}, {"always", 1}, {"yes", 1})),
- OPT_FLOATRANGE("hr-seek-demuxer-offset", hr_seek_demuxer_offset, 0, -9, 99),
+ ({"no", -1}, {"absolute", 0}, {"yes", 1}, {"always", 1})),
+ OPT_FLOAT("hr-seek-demuxer-offset", hr_seek_demuxer_offset, 0),
OPT_FLAG("hr-seek-framedrop", hr_seek_framedrop, 0),
OPT_CHOICE_OR_INT("autosync", autosync, 0, 0, 10000,
({"no", -1})),
@@ -552,11 +531,10 @@ const m_option_t mp_opts[] = {
OPT_STRING("osd-msg2", osd_msg[1], 0),
OPT_STRING("osd-msg3", osd_msg[2], 0),
- OPT_CHOICE("idle", player_idle_mode, M_OPT_OPTIONAL_PARAM,
+ OPT_CHOICE("idle", player_idle_mode, 0,
({"no", 0},
{"once", 1},
- {"yes", 2},
- {"", 2})),
+ {"yes", 2})),
OPT_FLAG("input-terminal", consolecontrols, CONF_GLOBAL),
@@ -602,7 +580,6 @@ const m_option_t mp_opts[] = {
OPT_REMOVED("identify", "use TOOLS/mpv_identify.sh"),
OPT_REMOVED("lavdopts", "use --vd-lavc-..."),
OPT_REMOVED("lavfdopts", "use --demuxer-lavf-..."),
- OPT_REPLACED("lircconf", "input-lirc-conf"),
OPT_REPLACED("lua", "script"),
OPT_REPLACED("lua-opts", "script-opts"),
OPT_REMOVED("mixer-channel", "use AO suboptions (alsa, oss)"),
@@ -615,7 +592,6 @@ const m_option_t mp_opts[] = {
OPT_REPLACED("noar", "no-input-appleremote"),
OPT_REPLACED("noautosub", "no-sub-auto"),
OPT_REPLACED("noconsolecontrols", "no-input-terminal"),
- OPT_REPLACED("nojoystick", "no-input-joystick"),
OPT_REPLACED("nosound", "no-audio"),
OPT_REPLACED("osdlevel", "osd-level"),
OPT_REMOVED("panscanrange", "use --video-zoom, --video-pan-x/y"),
@@ -645,7 +621,6 @@ const m_option_t mp_opts[] = {
OPT_REMOVED("xy", "use --autofit"),
OPT_REMOVED("zoom", "Inverse available as ``--video-unscaled"),
OPT_REPLACED("media-keys", "input-media-keys"),
- OPT_REPLACED("lirc", "input-lirc"),
OPT_REPLACED("right-alt-gr", "input-right-alt-gr"),
OPT_REPLACED("autosub", "sub-auto"),
OPT_REPLACED("autosub-match", "sub-auto"),
@@ -658,6 +633,7 @@ const m_option_t mp_opts[] = {
OPT_REMOVED("fixed-vo", "--fixed-vo=yes is now the default"),
OPT_REPLACED("mkv-subtitle-preroll", "demuxer-mkv-subtitle-preroll"),
OPT_REPLACED("dtshd", "ad-spdif-dtshd"),
+ OPT_REPLACED("ass-use-margins", "sub-use-margins"),
{0}
};
@@ -666,7 +642,7 @@ const struct MPOpts mp_default_opts = {
.use_terminal = 1,
.msg_color = 1,
.audio_driver_list = NULL,
- .audio_decoders = "-spdif:*", // never select spdif by default
+ .audio_decoders = "lavc:libdcadec,-spdif:*", // never select spdif by default
.video_decoders = NULL,
.deinterlace = -1,
.softvol = SOFTVOL_AUTO,
@@ -687,6 +663,7 @@ const struct MPOpts mp_default_opts = {
.keepaspect_window = 1,
.border = 1,
.WinID = -1,
+ .window_scale = 1.0,
},
.allow_win_drag = 1,
.wintitle = "mpv - ${?media-title:${media-title}}${!media-title:No file.}",
@@ -706,11 +683,16 @@ const struct MPOpts mp_default_opts = {
.osd_scale = 1,
.osd_scale_by_window = 1,
.sub_scale_by_window = 1,
+ .ass_use_margins = 0,
+ .sub_use_margins = 1,
+ .ass_scale_with_window = 0,
+ .sub_scale_with_window = 1,
.use_text_osd = 1,
#if HAVE_LUA
.lua_load_osc = 1,
.lua_load_ytdl = 1,
.lua_ytdl_format = NULL,
+ .lua_ytdl_raw_options = NULL,
#endif
.auto_load_scripts = 1,
.loop_times = 1,
@@ -722,7 +704,7 @@ const struct MPOpts mp_default_opts = {
.position_resume = 1,
.stream_cache = {
.size = -1,
- .def_size = 25000,
+ .def_size = 150000,
.initial = 0,
.seek_min = 500,
.file_max = 1024 * 1024,
@@ -730,11 +712,11 @@ const struct MPOpts mp_default_opts = {
.demuxer_thread = 1,
.demuxer_min_packs = 0,
.demuxer_min_bytes = 0,
- .demuxer_min_secs = 0.2,
+ .demuxer_min_secs = 1.0,
.network_rtsp_transport = 2,
.network_timeout = 0.0,
.hls_bitrate = 2,
- .demuxer_min_secs_cache = 2,
+ .demuxer_min_secs_cache = 10.0,
.cache_pausing = 1,
.chapterrange = {-1, -1},
.ab_loop = {MP_NOPTS_VALUE, MP_NOPTS_VALUE},
@@ -781,7 +763,6 @@ const struct MPOpts mp_default_opts = {
.use_embedded_fonts = 1,
.sub_fix_timing = 1,
.sub_cp = "auto",
- .mkv_subtitle_preroll_secs = 1.0,
.screenshot_template = "shot%n",
.hwdec_codecs = "h264,vc1,wmv3",
@@ -792,6 +773,10 @@ 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",
diff --git a/options/options.h b/options/options.h
index c558176..ced5184 100644
--- a/options/options.h
+++ b/options/options.h
@@ -30,6 +30,7 @@ typedef struct mp_vo_opts {
struct m_geometry autofit;
struct m_geometry autofit_larger;
struct m_geometry autofit_smaller;
+ float window_scale;
int keepaspect;
int keepaspect_window;
@@ -68,6 +69,7 @@ typedef struct MPOpts {
int lua_load_osc;
int lua_load_ytdl;
char *lua_ytdl_format;
+ char **lua_ytdl_raw_options;
int auto_load_scripts;
@@ -101,11 +103,6 @@ typedef struct MPOpts {
int cursor_autohide_delay;
int cursor_autohide_fs;
- int requested_colorspace;
- int requested_input_range;
- int requested_output_range;
- int requested_primaries;
-
int video_rotate;
int video_stereo_mode;
@@ -203,9 +200,6 @@ typedef struct MPOpts {
double demuxer_min_secs;
char *audio_demuxer_name;
char *sub_demuxer_name;
- int mkv_subtitle_preroll;
- double mkv_subtitle_preroll_secs;
- int mkv_probe_duration;
double demuxer_min_secs_cache;
int cache_pausing;
@@ -241,6 +235,7 @@ typedef struct MPOpts {
int osd_scale_by_window;
int sub_scale_by_window;
int sub_scale_with_window;
+ int ass_scale_with_window;
struct osd_style_opts *osd_style;
struct osd_style_opts *sub_text_style;
float sub_scale;
@@ -249,6 +244,7 @@ typedef struct MPOpts {
int ass_enabled;
float ass_line_spacing;
int ass_use_margins;
+ int sub_use_margins;
int ass_vsfilter_aspect_compat;
int ass_vsfilter_color_compat;
int ass_vsfilter_blur_compat;
@@ -296,6 +292,7 @@ typedef struct MPOpts {
struct demux_rawaudio_opts *demux_rawaudio;
struct demux_rawvideo_opts *demux_rawvideo;
struct demux_lavf_opts *demux_lavf;
+ struct demux_mkv_opts *demux_mkv;
struct vd_lavc_params *vd_lavc_params;
struct ad_lavc_params *ad_lavc_params;
diff --git a/options/parse_commandline.c b/options/parse_commandline.c
index 424b8fc..ab1f28f 100644
--- a/options/parse_commandline.c
+++ b/options/parse_commandline.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
@@ -41,7 +40,6 @@
struct parse_state {
struct m_config *config;
- int argc;
char **argv;
bool no_more_opts;
@@ -57,14 +55,13 @@ static int split_opt_silent(struct parse_state *p)
{
assert(!p->error);
- if (p->argc < 1)
+ if (!p->argv || !p->argv[0])
return 1;
p->is_opt = false;
p->arg = bstr0(p->argv[0]);
p->param = bstr0(NULL);
- p->argc--;
p->argv++;
if (p->no_more_opts || !bstr_startswith0(p->arg, "-") || p->arg.len == 1)
@@ -82,13 +79,15 @@ static int split_opt_silent(struct parse_state *p)
bool ambiguous = !bstr_split_tok(p->arg, "=", &p->arg, &p->param);
+ if (!p->arg.len)
+ return M_OPT_INVALID;
+
bool need_param = m_config_option_requires_param(p->config, p->arg) > 0;
if (ambiguous && need_param) {
- if (p->argc < 1)
+ if (!p->argv[0])
return M_OPT_MISSING_PARAM;
p->param = bstr0(p->argv[0]);
- p->argc--;
p->argv++;
}
@@ -103,7 +102,7 @@ static bool split_opt(struct parse_state *p)
return r == 0;
p->error = true;
- MP_FATAL(p->config, "Error parsing commandline option %.*s: %s\n",
+ MP_FATAL(p->config, "Error parsing command line option '%.*s': %s\n",
BSTR_P(p->arg), m_option_strerror(r));
return false;
}
@@ -132,8 +131,7 @@ static void process_non_option(struct playlist *files, const char *arg)
// returns M_OPT_... error code
int m_config_parse_mp_command_line(m_config_t *config, struct playlist *files,
- struct mpv_global *global,
- int argc, char **argv)
+ struct mpv_global *global, char **argv)
{
int ret = M_OPT_UNKNOWN;
int mode = 0;
@@ -146,7 +144,7 @@ int m_config_parse_mp_command_line(m_config_t *config, struct playlist *files,
mode = GLOBAL;
- struct parse_state p = {config, argc - 1, argv + 1};
+ struct parse_state p = {config, argv};
while (split_opt(&p)) {
if (p.is_opt) {
int flags = M_SETOPT_FROM_CMDLINE;
@@ -158,7 +156,7 @@ int m_config_parse_mp_command_line(m_config_t *config, struct playlist *files,
goto err_out;
}
if (r < 0) {
- MP_FATAL(config, "Setting commandline option --%.*s=%.*s failed.\n",
+ MP_FATAL(config, "Setting command line option '--%.*s=%.*s' failed.\n",
BSTR_P(p.arg), BSTR_P(p.param));
goto err_out;
}
@@ -209,7 +207,8 @@ int m_config_parse_mp_command_line(m_config_t *config, struct playlist *files,
struct playlist *pl = playlist_parse_file(param0, global);
talloc_free(param0);
if (!pl) {
- MP_FATAL(config, "Error reading playlist '%.*s'", BSTR_P(p.param));
+ MP_FATAL(config, "Error reading playlist '%.*s'\n",
+ BSTR_P(p.param));
goto err_out;
}
playlist_transfer_entries(files, pl);
@@ -282,14 +281,11 @@ err_out:
* during normal options parsing.
*/
void m_config_preparse_command_line(m_config_t *config, struct mpv_global *global,
- int argc, char **argv)
+ char **argv)
{
struct MPOpts *opts = global->opts;
- // Hack to shut up parser error messages
- mp_msg_mute(global, true);
-
- struct parse_state p = {config, argc - 1, argv + 1};
+ struct parse_state p = {config, argv};
while (split_opt_silent(&p) == 0) {
if (p.is_opt) {
// Ignore non-pre-parse options. They will be set later.
@@ -301,8 +297,6 @@ void m_config_preparse_command_line(m_config_t *config, struct mpv_global *globa
}
}
- mp_msg_mute(global, false);
-
for (int n = 0; n < config->num_opts; n++)
config->opts[n].warning_was_printed = false;
}
diff --git a/options/parse_commandline.h b/options/parse_commandline.h
index 39512ca..6624b58 100644
--- a/options/parse_commandline.h
+++ b/options/parse_commandline.h
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_PARSER_MPCMD_H
@@ -26,9 +25,8 @@ struct m_config;
struct mpv_global;
int m_config_parse_mp_command_line(m_config_t *config, struct playlist *files,
- struct mpv_global *global,
- int argc, char **argv);
+ struct mpv_global *global, char **argv);
void m_config_preparse_command_line(m_config_t *config, struct mpv_global *global,
- int argc, char **argv);
+ char **argv);
#endif /* MPLAYER_PARSER_MPCMD_H */
diff --git a/options/parse_configfile.c b/options/parse_configfile.c
index 1f08349..8ccf657 100644
--- a/options/parse_configfile.c
+++ b/options/parse_configfile.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
@@ -33,242 +32,177 @@
#include "m_option.h"
#include "m_config.h"
-/// Maximal include depth.
-#define MAX_RECURSION_DEPTH 8
+// Skip whitespace and comments (assuming there are no line breaks)
+static bool skip_ws(bstr *s)
+{
+ *s = bstr_lstrip(*s);
+ if (bstr_startswith0(*s, "#"))
+ s->len = 0;
+ return s->len;
+}
-// Load options and profiles from from a config file.
-// conffile: path to the config file
-// initial_section: default section where to add normal options
-// flags: M_SETOPT_* bits
-// returns: 1 on sucess, -1 on error, 0 if file not accessible.
-int m_config_parse_config_file(m_config_t *config, const char *conffile,
- char *initial_section, int flags)
+int m_config_parse(m_config_t *config, const char *location, bstr data,
+ char *initial_section, int flags)
{
-#define PRINT_LINENUM MP_ERR(config, "%s:%d: ", conffile, line_num)
-#define MAX_LINE_LEN 10000
-#define MAX_OPT_LEN 1000
-#define MAX_PARAM_LEN 1500
- FILE *fp = NULL;
- char *line = NULL;
- char opt[MAX_OPT_LEN + 1];
- char param[MAX_PARAM_LEN + 1];
- char c; /* for the "" and '' check */
- int tmp;
- int line_num = 0;
- int line_pos; /* line pos */
- int opt_pos; /* opt pos */
- int param_pos; /* param pos */
- int ret = 1;
- int errors = 0;
m_profile_t *profile = m_config_add_profile(config, initial_section);
+ void *tmp = talloc_new(NULL);
+ int line_no = 0;
+ int errors = 0;
- flags = flags | M_SETOPT_FROM_CONFIG_FILE;
-
- MP_VERBOSE(config, "Reading config file %s\n", conffile);
-
- if (config->recursion_depth > MAX_RECURSION_DEPTH) {
- MP_ERR(config, "Maximum 'include' nesting depth exceeded.\n");
- ret = -1;
- goto out;
- }
-
- if ((line = malloc(MAX_LINE_LEN + 1)) == NULL) {
- ret = -1;
- goto out;
- } else
-
- MP_VERBOSE(config, "\n");
-
- if ((fp = fopen(conffile, "r")) == NULL) {
- MP_VERBOSE(config, "Can't open config file: %s\n", mp_strerror(errno));
- ret = 0;
- goto out;
- }
-
- while (fgets(line, MAX_LINE_LEN, fp)) {
- if (errors >= 16) {
- MP_FATAL(config, "too many errors\n");
- goto out;
- }
-
- line_num++;
- line_pos = 0;
+ bstr_eatstart0(&data, "\xEF\xBB\xBF"); // skip BOM
- /* skip BOM */
- if (strncmp(line, "\xEF\xBB\xBF", 3) == 0)
- line_pos += 3;
+ while (data.len) {
+ talloc_free_children(tmp);
+ bool ok = false;
- /* skip whitespaces */
- while (mp_isspace(line[line_pos]))
- ++line_pos;
+ line_no++;
+ char loc[512];
+ snprintf(loc, sizeof(loc), "%s:%d:", location, line_no);
- /* EOL / comment */
- if (line[line_pos] == '\0' || line[line_pos] == '#')
+ bstr line = bstr_strip_linebreaks(bstr_getline(data, &data));
+ if (!skip_ws(&line))
continue;
- /* read option. */
- for (opt_pos = 0; mp_isprint(line[line_pos]) &&
- line[line_pos] != ' ' &&
- line[line_pos] != '#' &&
- line[line_pos] != '='; /* NOTHING */) {
- opt[opt_pos++] = line[line_pos++];
- if (opt_pos >= MAX_OPT_LEN) {
- PRINT_LINENUM;
- MP_ERR(config, "option name too long\n");
- errors++;
- ret = -1;
- goto nextline;
+ // Profile declaration
+ if (bstr_eatstart0(&line, "[")) {
+ bstr profilename;
+ if (!bstr_split_tok(line, "]", &profilename, &line)) {
+ MP_ERR(config, "%s missing closing ]\n", loc);
+ goto error;
}
- }
- if (opt_pos == 0) {
- PRINT_LINENUM;
- MP_ERR(config, "parse error\n");
- ret = -1;
- errors++;
- continue;
- }
- opt[opt_pos] = '\0';
-
- /* Profile declaration */
- if (opt_pos > 2 && opt[0] == '[' && opt[opt_pos - 1] == ']') {
- opt[opt_pos - 1] = '\0';
- profile = m_config_add_profile(config, opt + 1);
+ if (skip_ws(&line)) {
+ MP_ERR(config, "%s unparseable extra characters: '%.*s'\n",
+ loc, BSTR_P(line));
+ goto error;
+ }
+ profile = m_config_add_profile(config, bstrto0(tmp, profilename));
continue;
}
- /* skip whitespaces */
- while (mp_isspace(line[line_pos]))
- ++line_pos;
-
- param_pos = 0;
- bool param_set = false;
-
- /* check '=' */
- if (line[line_pos] == '=') {
- line_pos++;
- param_set = true;
-
- /* whitespaces... */
- while (mp_isspace(line[line_pos]))
- ++line_pos;
-
- /* read the parameter */
- if (line[line_pos] == '"' || line[line_pos] == '\'') {
- c = line[line_pos];
- ++line_pos;
- for (param_pos = 0; line[line_pos] != c; /* NOTHING */) {
- if (!line[line_pos]) {
- PRINT_LINENUM;
- MP_ERR(config, "unterminated quotes\n");
- ret = -1;
- errors++;
- goto nextline;
- }
- param[param_pos++] = line[line_pos++];
- if (param_pos >= MAX_PARAM_LEN) {
- PRINT_LINENUM;
- MP_ERR(config, "option %s has a too long parameter\n", opt);
- ret = -1;
- errors++;
- goto nextline;
- }
+ bstr_eatstart0(&line, "--");
+
+ bstr option = line;
+ while (line.len && (mp_isalnum(line.start[0]) || line.start[0] == '_' ||
+ line.start[0] == '-'))
+ line = bstr_cut(line, 1);
+ option.len = option.len - line.len;
+ skip_ws(&line);
+
+ bstr value = {0};
+ if (bstr_eatstart0(&line, "=")) {
+ skip_ws(&line);
+ if (line.len && (line.start[0] == '"' || line.start[0] == '\'')) {
+ // Simple quoting, like "value"
+ char term[2] = {line.start[0], 0};
+ line = bstr_cut(line, 1);
+ if (!bstr_split_tok(line, term, &value, &line)) {
+ MP_ERR(config, "%s unterminated quote\n", loc);
+ goto error;
}
- line_pos++; /* skip the closing " or ' */
- goto param_done;
- }
-
- if (line[line_pos] == '%') {
- char *start = &line[line_pos + 1];
- char *end = start;
- unsigned long len = strtoul(start, &end, 10);
- if (start != end && end[0] == '%') {
- if (len >= MAX_PARAM_LEN - 1 ||
- strlen(end + 1) < len)
- {
- PRINT_LINENUM;
- MP_ERR(config, "bogus %% length\n");
- ret = -1;
- errors++;
- goto nextline;
- }
- param_pos = snprintf(param, sizeof(param), "%.*s",
- (int)len, end + 1);
- line_pos += 1 + (end - start) + 1 + len;
- goto param_done;
+ } else if (bstr_eatstart0(&line, "%")) {
+ // Quoting with length, like %5%value
+ bstr rest;
+ long long len = bstrtoll(line, &rest, 10);
+ if (rest.len == line.len || !bstr_eatstart0(&rest, "%") ||
+ len > rest.len)
+ {
+ MP_ERR(config, "%s fixed-length quoting expected - put "
+ "\"quotes\" around the option value if you did not "
+ "intend to use this, but your option value starts "
+ "with '%%'\n", loc);
+ goto error;
}
+ value = bstr_splice(rest, 0, len);
+ line = bstr_cut(rest, len);
+ } else {
+ // No quoting; take everything until the comment or end of line
+ int end = bstrchr(line, '#');
+ value = bstr_strip(end < 0 ? line : bstr_splice(line, 0, end));
+ line.len = 0;
}
+ }
+ if (skip_ws(&line)) {
+ MP_ERR(config, "%s unparseable extra characters: '%.*s'\n",
+ loc, BSTR_P(line));
+ goto error;
+ }
- for (param_pos = 0; mp_isprint(line[line_pos])
- && !mp_isspace(line[line_pos])
- && line[line_pos] != '#'; /* NOTHING */) {
- param[param_pos++] = line[line_pos++];
- if (param_pos >= MAX_PARAM_LEN) {
- PRINT_LINENUM;
- MP_ERR(config, "too long parameter\n");
- ret = -1;
- errors++;
- goto nextline;
- }
+ int res;
+ if (profile) {
+ if (bstr_equals0(option, "profile-desc")) {
+ m_profile_set_desc(profile, value);
+ res = 0;
+ } else {
+ res = m_config_set_profile_option(config, profile, option, value);
}
-
- param_done:
-
- while (mp_isspace(line[line_pos]))
- ++line_pos;
+ } else {
+ res = m_config_set_option_ext(config, option, value, flags);
}
- param[param_pos] = '\0';
-
- /* EOL / comment */
- if (line[line_pos] != '\0' && line[line_pos] != '#') {
- PRINT_LINENUM;
- MP_ERR(config, "extra characters: %s\n", line + line_pos);
- ret = -1;
+ if (res < 0) {
+ MP_ERR(config, "%s setting option %.*s='%.*s' failed.\n",
+ loc, BSTR_P(option), BSTR_P(value));
+ goto error;
}
- bstr bopt = bstr0(opt);
- bstr bparam = bstr0(param);
-
- if (bopt.len >= 3)
- bstr_eatstart0(&bopt, "--");
-
- if (profile && bstr_equals0(bopt, "profile-desc")) {
- m_profile_set_desc(profile, bparam);
- goto nextline;
+ ok = true;
+ error:
+ if (!ok)
+ errors++;
+ if (errors > 16) {
+ MP_ERR(config, "%s: too many errors, stopping.\n", location);
+ break;
}
+ }
- bool need_param = m_config_option_requires_param(config, bopt) > 0;
- if (need_param && !param_set) {
- PRINT_LINENUM;
- MP_ERR(config, "error parsing option %.*s=%.*s: %s\n",
- BSTR_P(bopt), BSTR_P(bparam),
- m_option_strerror(M_OPT_MISSING_PARAM));
- continue;
- }
+ talloc_free(tmp);
+ return 1;
+}
- if (profile) {
- tmp = m_config_set_profile_option(config, profile, bopt, bparam);
- } else {
- tmp = m_config_set_option_ext(config, bopt, bparam, flags);
- }
- if (tmp < 0) {
- PRINT_LINENUM;
- MP_ERR(config, "setting option %.*s='%.*s' failed.\n",
- BSTR_P(bopt), BSTR_P(bparam));
+static bstr read_file(struct mp_log *log, const char *filename)
+{
+ FILE *f = fopen(filename, "rb");
+ if (!f) {
+ mp_verbose(log, "Can't open config file: %s\n", mp_strerror(errno));
+ return (bstr){0};
+ }
+ char *data = talloc_array(NULL, char, 0);
+ size_t size = 0;
+ while (1) {
+ size_t left = talloc_get_size(data) - size;
+ if (!left) {
+ MP_TARRAY_GROW(NULL, data, size + 1);
continue;
- /* break */
}
-nextline:
- ;
+ size_t s = fread(data + size, 1, left, f);
+ if (!s) {
+ if (ferror(f))
+ mp_err(log, "Error reading config file.\n");
+ fclose(f);
+ MP_TARRAY_APPEND(NULL, data, size, 0);
+ return (bstr){data, size - 1};
+ }
+ size += s;
}
+ assert(0);
+}
-out:
- free(line);
- if (fp)
- fclose(fp);
- config->recursion_depth -= 1;
- if (ret < 0) {
- MP_FATAL(config, "Error loading config file %s.\n",
- conffile);
- }
- return ret;
+// Load options and profiles from from a config file.
+// conffile: path to the config file
+// initial_section: default section where to add normal options
+// flags: M_SETOPT_* bits
+// returns: 1 on success, -1 on error, 0 if file not accessible.
+int m_config_parse_config_file(m_config_t *config, const char *conffile,
+ char *initial_section, int flags)
+{
+ flags = flags | M_SETOPT_FROM_CONFIG_FILE;
+
+ MP_VERBOSE(config, "Reading config file %s\n", conffile);
+
+ bstr data = read_file(config->log, conffile);
+ if (!data.start)
+ return 0;
+
+ int r = m_config_parse(config, conffile, data, initial_section, flags);
+ talloc_free(data.start);
+ return r;
}
diff --git a/options/parse_configfile.h b/options/parse_configfile.h
index 4a42cee..9bbaf62 100644
--- a/options/parse_configfile.h
+++ b/options/parse_configfile.h
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_PARSER_CFG_H
@@ -24,4 +23,7 @@
int m_config_parse_config_file(m_config_t* config, const char *conffile,
char *initial_section, int flags);
+int m_config_parse(m_config_t *config, const char *location, bstr data,
+ char *initial_section, int flags);
+
#endif /* MPLAYER_PARSER_CFG_H */
diff --git a/options/path.c b/options/path.c
index 2b2bb3d..433f511 100644
--- a/options/path.c
+++ b/options/path.c
@@ -6,21 +6,20 @@
* zero terminated path string. This buffer has to be FREED
* by the caller.
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <assert.h>
diff --git a/options/path.h b/options/path.h
index 44c3489..7f9c0cb 100644
--- a/options/path.h
+++ b/options/path.h
@@ -1,21 +1,20 @@
/*
* Get path to config dir/file.
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_PATH_H
diff --git a/osdep/io.c b/osdep/io.c
index e63509a..fdb625d 100644
--- a/osdep/io.c
+++ b/osdep/io.c
@@ -1,22 +1,24 @@
/*
* unicode/utf-8 I/O helpers and wrappers for Windows
*
- * This file is part of mplayer2.
* Contains parts based on libav code (http://libav.org).
*
- * mplayer2 is free software; you can redistribute it and/or modify
+ * 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.
*
- * mplayer2 is distributed in the hope that it will be useful,
+ * 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 mplayer2. If not, see <http://www.gnu.org/licenses/>.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
+
#include <unistd.h>
#include <errno.h>
#include <assert.h>
@@ -121,32 +123,6 @@ char *mp_to_utf8(void *talloc_ctx, const wchar_t *s)
#include <fcntl.h>
#include <pthread.h>
-//http://git.libav.org/?p=libav.git;a=blob;f=cmdutils.c;h=ade3f10ce2fc030e32e375a85fbd06c26d43a433#l161
-
-static char** win32_argv_utf8;
-static int win32_argc;
-
-void mp_get_converted_argv(int *argc, char ***argv)
-{
- if (!win32_argv_utf8) {
- win32_argc = 0;
- wchar_t **argv_w = CommandLineToArgvW(GetCommandLineW(), &win32_argc);
- if (win32_argc <= 0 || !argv_w)
- return;
-
- win32_argv_utf8 = talloc_zero_array(NULL, char*, win32_argc + 1);
-
- for (int i = 0; i < win32_argc; i++) {
- win32_argv_utf8[i] = mp_to_utf8(NULL, argv_w[i]);
- }
-
- LocalFree(argv_w);
- }
-
- *argc = win32_argc;
- *argv = win32_argv_utf8;
-}
-
static void copy_stat(struct mp_stat *dst, struct _stat64 *src)
{
dst->st_dev = src->st_dev;
diff --git a/osdep/io.h b/osdep/io.h
index 0348d3d..8d3e24f 100644
--- a/osdep/io.h
+++ b/osdep/io.h
@@ -1,20 +1,20 @@
/*
* unicode/utf-8 I/O helpers and wrappers for Windows
*
- * This file is part of mplayer2.
+ * This file is part of mpv.
*
- * mplayer2 is free software; you can redistribute it and/or modify
+ * 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.
*
- * mplayer2 is distributed in the hope that it will be useful,
+ * 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 mplayer2. If not, see <http://www.gnu.org/licenses/>.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_OSDEP_IO
@@ -65,8 +65,6 @@ char *mp_to_utf8(void *talloc_ctx, const wchar_t *s);
#include <sys/stat.h>
#include <fcntl.h>
-void mp_get_converted_argv(int *argc, char ***argv);
-
int mp_printf(const char *format, ...);
int mp_fprintf(FILE *stream, const char *format, ...);
int mp_open(const char *filename, int oflag, ...);
diff --git a/osdep/macosx_application.h b/osdep/macosx_application.h
index d6a9425..8202093 100644
--- a/osdep/macosx_application.h
+++ b/osdep/macosx_application.h
@@ -12,8 +12,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
- * with mpv; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPV_MACOSX_APPLICATION
diff --git a/osdep/macosx_application.m b/osdep/macosx_application.m
index 818d8df..3691e92 100644
--- a/osdep/macosx_application.m
+++ b/osdep/macosx_application.m
@@ -12,8 +12,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
- * with mpv; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
@@ -366,7 +365,10 @@ int cocoa_main(mpv_main_fn mpv_main, int argc, char *argv[])
ctx.argv = &argv;
if (bundle_started_from_finder(argc, argv)) {
- argc = 1; // clears out -psn argument is present
+ if (argc > 1) {
+ argc = 1; // clears out -psn argument if present
+ argv[1] = NULL;
+ }
macosx_redirect_output_to_logfile("mpv");
init_cocoa_application(true);
} else {
diff --git a/osdep/macosx_application_objc.h b/osdep/macosx_application_objc.h
index 6b0ca15..0f18952 100644
--- a/osdep/macosx_application_objc.h
+++ b/osdep/macosx_application_objc.h
@@ -12,8 +12,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
- * with mpv; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#import <Cocoa/Cocoa.h>
diff --git a/osdep/macosx_events.h b/osdep/macosx_events.h
index f9008b4..a6bfbfe 100644
--- a/osdep/macosx_events.h
+++ b/osdep/macosx_events.h
@@ -3,12 +3,12 @@
*
* This file is part of mpv.
*
- * mplayer2 is free software; you can redistribute it and/or modify
+ * 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.
*
- * mplayer2 is distributed in the hope that it will be useful,
+ * 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.
diff --git a/osdep/mpv.rc b/osdep/mpv.rc
index 4a9d611..f2d9042 100644
--- a/osdep/mpv.rc
+++ b/osdep/mpv.rc
@@ -1,21 +1,20 @@
/*
- * Windows resource file for MPlayer
+ * Windows resource file for mpv
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <winver.h>
diff --git a/osdep/path-macosx.m b/osdep/path-macosx.m
index 33086c3..ee34f96 100644
--- a/osdep/path-macosx.m
+++ b/osdep/path-macosx.m
@@ -12,8 +12,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
- * with mpv; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#import <Foundation/Foundation.h>
diff --git a/osdep/subprocess-posix.c b/osdep/subprocess-posix.c
index c2120b2..9d565f4 100644
--- a/osdep/subprocess-posix.c
+++ b/osdep/subprocess-posix.c
@@ -20,6 +20,7 @@
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
+#include <fcntl.h>
#include <errno.h>
#include <signal.h>
@@ -63,6 +64,7 @@ int mp_subprocess(char **args, struct mp_cancel *cancel, void *ctx,
int status = -1;
int p_stdout[2] = {-1, -1};
int p_stderr[2] = {-1, -1};
+ int devnull = -1;
pid_t pid = -1;
if (on_stdout && mp_make_cloexec_pipe(p_stdout) < 0)
@@ -70,10 +72,16 @@ int mp_subprocess(char **args, struct mp_cancel *cancel, void *ctx,
if (on_stderr && mp_make_cloexec_pipe(p_stderr) < 0)
goto done;
+ devnull = open("/dev/null", O_RDONLY | O_CLOEXEC);
+ if (devnull < 0)
+ goto done;
+
if (posix_spawn_file_actions_init(&fa))
goto done;
fa_destroy = true;
- // redirect stdout and stderr
+ // redirect stdin/stdout/stderr
+ if (posix_spawn_file_actions_adddup2(&fa, devnull, 0))
+ goto done;
if (p_stdout[1] >= 0 && posix_spawn_file_actions_adddup2(&fa, p_stdout[1], 1))
goto done;
if (p_stderr[1] >= 0 && posix_spawn_file_actions_adddup2(&fa, p_stderr[1], 2))
@@ -88,6 +96,8 @@ int mp_subprocess(char **args, struct mp_cancel *cancel, void *ctx,
p_stdout[1] = -1;
close(p_stderr[1]);
p_stderr[1] = -1;
+ close(devnull);
+ devnull = -1;
int *read_fds[2] = {&p_stdout[0], &p_stderr[0]};
subprocess_read_cb read_cbs[2] = {on_stdout, on_stderr};
@@ -133,6 +143,7 @@ done:
close(p_stdout[1]);
close(p_stderr[0]);
close(p_stderr[1]);
+ close(devnull);
if (WIFEXITED(status) && WEXITSTATUS(status) != 127) {
*error = NULL;
diff --git a/osdep/subprocess-win.c b/osdep/subprocess-win.c
index 211f407..6c9ccf2 100644
--- a/osdep/subprocess-win.c
+++ b/osdep/subprocess-win.c
@@ -30,6 +30,12 @@
static void write_arg(bstr *cmdline, char *arg)
{
+ // Empty args must be represented as an empty quoted string
+ if (!arg[0]) {
+ bstr_xappend(NULL, cmdline, bstr0("\"\""));
+ return;
+ }
+
// If the string doesn't have characters that need to be escaped, it's best
// to leave it alone for the sake of Windows programs that don't process
// quoted args correctly.
@@ -48,22 +54,22 @@ static void write_arg(bstr *cmdline, char *arg)
for (int pos = 0; arg[pos]; pos++) {
switch (arg[pos]) {
case '\\':
- // Count backslashes that appear in a row
+ // Count consecutive backslashes
num_slashes++;
break;
case '"':
+ // Write the argument up to the point before the quote
bstr_xappend(NULL, cmdline, (struct bstr){arg, pos});
+ arg += pos;
+ pos = 0;
- // Double preceding slashes
+ // Double backslashes preceding the quote
for (int i = 0; i < num_slashes; i++)
bstr_xappend(NULL, cmdline, bstr0("\\"));
+ num_slashes = 0;
- // Escape the following quote
+ // Escape the quote itself
bstr_xappend(NULL, cmdline, bstr0("\\"));
-
- arg += pos;
- pos = 0;
- num_slashes = 0;
break;
default:
num_slashes = 0;
@@ -73,7 +79,7 @@ static void write_arg(bstr *cmdline, char *arg)
// Write the rest of the argument
bstr_xappend(NULL, cmdline, bstr0(arg));
- // Double slashes that appear at the end of the string
+ // Double backslashes at the end of the argument
for (int i = 0; i < num_slashes; i++)
bstr_xappend(NULL, cmdline, bstr0("\\"));
diff --git a/osdep/subprocess.c b/osdep/subprocess.c
new file mode 100644
index 0000000..84a1b52
--- /dev/null
+++ b/osdep/subprocess.c
@@ -0,0 +1,57 @@
+/*
+ * This file is part of mpv.
+ *
+ * mpv is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * mpv is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <pthread.h>
+
+#include "common/common.h"
+#include "common/msg.h"
+#include "common/msg_control.h"
+
+#include "subprocess.h"
+
+struct subprocess_args {
+ struct mp_log *log;
+ char **args;
+};
+
+static void *run_subprocess(void *ptr)
+{
+ struct subprocess_args *p = ptr;
+ pthread_detach(pthread_self());
+
+ mp_msg_flush_status_line(p->log);
+
+ char *err = NULL;
+ if (mp_subprocess(p->args, NULL, NULL, NULL, NULL, &err) < 0)
+ mp_err(p->log, "Running subprocess failed: %s\n", err);
+
+ talloc_free(p);
+ return NULL;
+}
+
+void mp_subprocess_detached(struct mp_log *log, char **args)
+{
+ struct subprocess_args *p = talloc_zero(NULL, struct subprocess_args);
+ p->log = mp_log_new(p, log, NULL);
+ int num_args = 0;
+ for (int n = 0; args[n]; n++)
+ MP_TARRAY_APPEND(p, p->args, num_args, talloc_strdup(p, args[n]));
+ MP_TARRAY_APPEND(p, p->args, num_args, NULL);
+ pthread_t thread;
+ if (pthread_create(&thread, NULL, run_subprocess, p))
+ talloc_free(p);
+}
diff --git a/osdep/subprocess.h b/osdep/subprocess.h
index 09c07da..1bd5afe 100644
--- a/osdep/subprocess.h
+++ b/osdep/subprocess.h
@@ -29,4 +29,7 @@ int mp_subprocess(char **args, struct mp_cancel *cancel, void *ctx,
subprocess_read_cb on_stdout, subprocess_read_cb on_stderr,
char **error);
+struct mp_log;
+void mp_subprocess_detached(struct mp_log *log, char **args);
+
#endif
diff --git a/osdep/terminal-unix.c b/osdep/terminal-unix.c
index d0f40f3..f43376b 100644
--- a/osdep/terminal-unix.c
+++ b/osdep/terminal-unix.c
@@ -275,8 +275,7 @@ static void enable_kx(bool enable)
// shouldn't be relied on here either.
if (isatty(STDOUT_FILENO)) {
char *cmd = enable ? "\033=" : "\033>";
- printf("%s", cmd);
- fflush(stdout);
+ write(STDOUT_FILENO, cmd, strlen(cmd));
}
}
diff --git a/osdep/terminal-win.c b/osdep/terminal-win.c
index 678cedb..e6b17fd 100644
--- a/osdep/terminal-win.c
+++ b/osdep/terminal-win.c
@@ -2,21 +2,20 @@
*
* copyright (C) 2003 Sascha Sommer
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
// See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/WinUI/WindowsUserInterface/UserInput/VirtualKeyCodes.asp
@@ -250,24 +249,48 @@ void mp_write_console_ansi(HANDLE wstream, char *buf)
}
}
-int terminal_init(void)
+static bool is_a_console(HANDLE h)
+{
+ return GetConsoleMode(h, &(DWORD){0});
+}
+
+static void reopen_console_handle(DWORD std, FILE *stream)
+{
+ HANDLE wstream = GetStdHandle(std);
+ if (is_a_console(wstream)) {
+ int fd = _open_osfhandle((intptr_t)wstream, _O_TEXT);
+ dup2(fd, fileno(stream));
+ setvbuf(stream, NULL, _IONBF, 0);
+ }
+}
+
+bool terminal_try_attach(void)
{
- if (AttachConsole(ATTACH_PARENT_PROCESS)) {
- // We have been started by something with a console window.
- // Redirect output streams to that console's low-level handles,
- // so we can actually use WriteConsole later on.
+ // mpv.exe is a flagged as a GUI application, but it acts as a console
+ // application when started from the console wrapper (see
+ // osdep/win32-console-wrapper.c). The console wrapper sets
+ // _started_from_console=yes, so check that variable before trying to
+ // attach to the console.
+ wchar_t console_env[4] = { 0 };
+ if (!GetEnvironmentVariableW(L"_started_from_console", console_env, 4))
+ return false;
+ if (wcsncmp(console_env, L"yes", 4))
+ return false;
+ SetEnvironmentVariableW(L"_started_from_console", NULL);
- int hConHandle;
+ if (!AttachConsole(ATTACH_PARENT_PROCESS))
+ return false;
- hConHandle = _open_osfhandle((intptr_t)hSTDOUT, _O_TEXT);
- *stdout = *_fdopen(hConHandle, "w");
- setvbuf(stdout, NULL, _IONBF, 0);
+ // We have a console window. Redirect output streams to that console's
+ // low-level handles, so we can actually use WriteConsole later on.
+ reopen_console_handle(STD_OUTPUT_HANDLE, stdout);
+ reopen_console_handle(STD_ERROR_HANDLE, stderr);
- hConHandle = _open_osfhandle((intptr_t)hSTDERR, _O_TEXT);
- *stderr = *_fdopen(hConHandle, "w");
- setvbuf(stderr, NULL, _IONBF, 0);
- }
+ return true;
+}
+int terminal_init(void)
+{
CONSOLE_SCREEN_BUFFER_INFO cinfo;
DWORD cmode = 0;
GetConsoleMode(hSTDOUT, &cmode);
diff --git a/osdep/terminal.h b/osdep/terminal.h
index fc98f0c..ebe4cc7 100644
--- a/osdep/terminal.h
+++ b/osdep/terminal.h
@@ -4,21 +4,20 @@
*
* copyright (C) 1999 A'rpi/ESP-team
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_GETCH2_H
@@ -47,4 +46,7 @@ void terminal_get_size(int *w, int *h);
// Windows only.
void mp_write_console_ansi(void *wstream, char *buf);
+/* Windows-only function to attach to the parent process's console */
+bool terminal_try_attach(void);
+
#endif /* MPLAYER_GETCH2_H */
diff --git a/osdep/timer-linux.c b/osdep/timer-linux.c
index 2424cc6..a343381 100644
--- a/osdep/timer-linux.c
+++ b/osdep/timer-linux.c
@@ -2,21 +2,20 @@
* precise timer routines for Linux/UNIX
* copyright (C) LGB & A'rpi/ASTRAL
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <unistd.h>
diff --git a/osdep/timer-win2.c b/osdep/timer-win2.c
index fbbdc92..b87456f 100644
--- a/osdep/timer-win2.c
+++ b/osdep/timer-win2.c
@@ -1,21 +1,20 @@
/*
* precise timer routines for Windows
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <windows.h>
diff --git a/osdep/timer.h b/osdep/timer.h
index 2bef413..5473239 100644
--- a/osdep/timer.h
+++ b/osdep/timer.h
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_TIMER_H
diff --git a/osdep/win32-console-wrapper.c b/osdep/win32-console-wrapper.c
index c8c297b..8cebcf8 100644
--- a/osdep/win32-console-wrapper.c
+++ b/osdep/win32-console-wrapper.c
@@ -72,5 +72,9 @@ int wmain(int argc, wchar_t **argv, wchar_t **envp)
GetModuleFileNameW(NULL, exe, MAX_PATH);
wcscpy(wcsrchr(exe, '.') + 1, L"exe");
+ // Set an environment variable so the child process can tell whether it
+ // was started from this wrapper and attach to the console accordingly
+ SetEnvironmentVariableW(L"_started_from_console", L"yes");
+
return cr_runproc(exe, cmd);
}
diff --git a/player/audio.c b/player/audio.c
index 46a552a..ea729ce 100644
--- a/player/audio.c
+++ b/player/audio.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stddef.h>
@@ -43,63 +42,57 @@
#include "core.h"
#include "command.h"
-static int try_filter(struct MPContext *mpctx,
- char *name, char *label, char **args)
+static int update_playback_speed_filters(struct MPContext *mpctx)
{
- struct dec_audio *d_audio = mpctx->d_audio;
+ struct MPOpts *opts = mpctx->opts;
+ double speed = opts->playback_speed;
+ struct af_stream *afs = mpctx->d_audio->afilter;
- if (af_find_by_label(d_audio->afilter, label))
+ // Make sure only exactly one filter changes speed; resetting them all
+ // and setting 1 filter is the easiest way to achieve this.
+ af_control_all(afs, AF_CONTROL_SET_PLAYBACK_SPEED, &(double){1});
+ af_control_all(afs, AF_CONTROL_SET_PLAYBACK_SPEED_RESAMPLE, &(double){1});
+
+ if (speed == 1.0)
+ return af_remove_by_label(afs, "playback-speed");
+
+ // Compatibility: if the user uses --af=scaletempo, always use this
+ // filter to change speed. Don't insert a second filter (any) either.
+ if (!af_find_by_label(afs, "playback-speed") &&
+ af_control_any_rev(afs, AF_CONTROL_SET_PLAYBACK_SPEED, &speed))
return 0;
- struct af_instance *af = af_add(d_audio->afilter, name, args);
- if (!af)
- return -1;
+ int method = AF_CONTROL_SET_PLAYBACK_SPEED_RESAMPLE;
+ if (opts->pitch_correction)
+ method = AF_CONTROL_SET_PLAYBACK_SPEED;
- af->label = talloc_strdup(af, label);
+ if (!af_control_any_rev(afs, method, &speed)) {
+ if (af_remove_by_label(afs, "playback-speed") < 0)
+ return -1;
- return 1;
+ char *filter = method == AF_CONTROL_SET_PLAYBACK_SPEED
+ ? "scaletempo" : "lavrresample";
+ if (af_add(afs, filter, "playback-speed", NULL) < 0)
+ return -1;
+ // Try again.
+ if (!af_control_any_rev(afs, method, &speed))
+ return -1;
+ }
+
+ return 0;
}
static int recreate_audio_filters(struct MPContext *mpctx)
{
assert(mpctx->d_audio);
- struct MPOpts *opts = mpctx->opts;
- struct af_stream *afs = mpctx->d_audio->afilter;
-
- double speed = opts->playback_speed;
-
- if (speed != 1.0) {
- int method = AF_CONTROL_SET_PLAYBACK_SPEED_RESAMPLE;
- if (speed > 1.0 && opts->pitch_correction)
- method = AF_CONTROL_SET_PLAYBACK_SPEED;
- if (!af_control_any_rev(afs, method, &speed)) {
- if (af_remove_by_label(afs, "playback-speed") < 0)
- return -1;
-
- // Compatibility: if the user uses --af=scaletempo, always use
- // this filter to change speed. Don't insert a second "scaletempo"
- // filter either.
- if (!af_control_any_rev(afs, AF_CONTROL_SET_PLAYBACK_SPEED, &speed))
- {
- char *filter = method == AF_CONTROL_SET_PLAYBACK_SPEED
- ? "scaletempo" : "forcespeed";
- if (try_filter(mpctx, filter, "playback-speed", NULL) < 0)
- return -1;
- // Try again.
- if (!af_control_any_rev(afs, method, &speed))
- return -1;
- }
- }
- } else {
- if (af_remove_by_label(afs, "playback-speed") < 0)
- return -1;
- // The filters could be inserted by the user (we don't remove them).
- af_control_any_rev(afs, AF_CONTROL_SET_PLAYBACK_SPEED, &speed);
- af_control_any_rev(afs, AF_CONTROL_SET_PLAYBACK_SPEED_RESAMPLE, &speed);
+ if (update_playback_speed_filters(mpctx) < 0) {
+ mpctx->opts->playback_speed = 1.0;
+ mp_notify(mpctx, MP_EVENT_CHANGE_ALL, NULL);
}
- if (af_init(afs) < 0) {
+ struct af_stream *afs = mpctx->d_audio->afilter;
+ if (afs->initialized < 1 && af_init(afs) < 0) {
MP_ERR(mpctx, "Couldn't find matching filter/ao format!\n");
return -1;
}
@@ -249,8 +242,6 @@ void reinit_audio_chain(struct MPContext *mpctx)
}
if (!mpctx->ao) {
- afs->initialized = 0; // do it again
-
mp_chmap_remove_useless_channels(&afs->output.channels,
&opts->audio_output_channels);
mp_audio_set_channels(&afs->output, &afs->output.channels);
@@ -270,6 +261,8 @@ void reinit_audio_chain(struct MPContext *mpctx)
mp_audio_buffer_reinit(mpctx->ao_buffer, &fmt);
afs->output = fmt;
+ if (!mp_audio_config_equals(&afs->output, &afs->filter_output))
+ afs->initialized = 0;
mpctx->ao_decoder_fmt = talloc(NULL, struct mp_audio);
*mpctx->ao_decoder_fmt = in_format;
@@ -392,8 +385,7 @@ static bool get_sync_samples(struct MPContext *mpctx, int *skip)
ao_get_format(mpctx->ao, &out_format);
double play_samplerate = out_format.rate / opts->playback_speed;
- bool is_pcm = !AF_FORMAT_IS_SPECIAL(out_format.format); // no spdif
- if (!opts->initial_audio_sync || !is_pcm) {
+ if (!opts->initial_audio_sync) {
mpctx->audio_status = STATUS_FILLING;
return true;
}
@@ -421,13 +413,14 @@ 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) > 300) {
+ if (written_pts == MP_NOPTS_VALUE || fabs(ptsdiff) > 3600) {
MP_WARN(mpctx, "Failed audio resync.\n");
mpctx->audio_status = STATUS_FILLING;
return true;
}
- *skip = -ptsdiff * play_samplerate;
+ int align = af_format_sample_alignment(out_format.format);
+ *skip = (-ptsdiff * play_samplerate) / align * align;
return true;
}
diff --git a/player/client.c b/player/client.c
index 1c24d55..43c7c07 100644
--- a/player/client.c
+++ b/player/client.c
@@ -516,11 +516,9 @@ static void *playback_thread(void *p)
int mpv_initialize(mpv_handle *ctx)
{
- if (mp_initialize(ctx->mpctx) < 0)
+ if (mp_initialize(ctx->mpctx, NULL) < 0)
return MPV_ERROR_INVALID_PARAMETER;
- mp_print_version(ctx->mpctx->log, false);
-
pthread_t thread;
if (pthread_create(&thread, NULL, playback_thread, ctx->mpctx) != 0)
return MPV_ERROR_NOMEM;
@@ -902,7 +900,6 @@ int mpv_set_option(mpv_handle *ctx, const char *name, mpv_format format,
if (format != MPV_FORMAT_NODE) {
tmp.format = format;
memcpy(&tmp.u, data, type->type->size);
- format = MPV_FORMAT_NODE;
data = &tmp;
}
lock_core(ctx);
@@ -957,6 +954,7 @@ static int run_async(mpv_handle *ctx, void (*fn)(void *fn_data), void *fn_data)
struct cmd_request {
struct MPContext *mpctx;
struct mp_cmd *cmd;
+ struct mpv_node *res;
int status;
struct mpv_handle *reply_ctx;
uint64_t userdata;
@@ -965,7 +963,7 @@ struct cmd_request {
static void cmd_fn(void *data)
{
struct cmd_request *req = data;
- int r = run_command(req->mpctx, req->cmd);
+ int r = run_command(req->mpctx, req->cmd, req->res);
req->status = r >= 0 ? 0 : MPV_ERROR_COMMAND;
talloc_free(req->cmd);
if (req->reply_ctx) {
@@ -974,7 +972,7 @@ static void cmd_fn(void *data)
}
}
-static int run_client_command(mpv_handle *ctx, struct mp_cmd *cmd)
+static int run_client_command(mpv_handle *ctx, struct mp_cmd *cmd, mpv_node *res)
{
if (!ctx->mpctx->initialized)
return MPV_ERROR_UNINITIALIZED;
@@ -989,6 +987,7 @@ static int run_client_command(mpv_handle *ctx, struct mp_cmd *cmd)
struct cmd_request req = {
.mpctx = ctx->mpctx,
.cmd = cmd,
+ .res = res,
};
run_locked(ctx, cmd_fn, &req);
return req.status;
@@ -996,21 +995,22 @@ static int run_client_command(mpv_handle *ctx, struct mp_cmd *cmd)
int mpv_command(mpv_handle *ctx, const char **args)
{
- return run_client_command(ctx, mp_input_parse_cmd_strv(ctx->log, args));
+ return run_client_command(ctx, mp_input_parse_cmd_strv(ctx->log, args), NULL);
}
int mpv_command_node(mpv_handle *ctx, mpv_node *args, mpv_node *result)
{
- int r = run_client_command(ctx, mp_input_parse_cmd_node(ctx->log, args));
+ struct mpv_node rn = {.format = MPV_FORMAT_NONE};
+ int r = run_client_command(ctx, mp_input_parse_cmd_node(ctx->log, args), &rn);
if (result && r >= 0)
- *result = (mpv_node){.format = MPV_FORMAT_NONE};
+ *result = rn;
return r;
}
int mpv_command_string(mpv_handle *ctx, const char *args)
{
return run_client_command(ctx,
- mp_input_parse_cmd(ctx->mpctx->input, bstr0((char*)args), ctx->name));
+ mp_input_parse_cmd(ctx->mpctx->input, bstr0((char*)args), ctx->name), NULL);
}
static int run_cmd_async(mpv_handle *ctx, uint64_t ud, struct mp_cmd *cmd)
@@ -1687,7 +1687,7 @@ static mpv_opengl_cb_context *opengl_cb_get_context(mpv_handle *ctx)
{
mpv_opengl_cb_context *cb = ctx->mpctx->gl_cb_ctx;
if (!cb) {
- cb = mp_opengl_create(ctx->mpctx->global, ctx->mpctx->osd, ctx->clients);
+ cb = mp_opengl_create(ctx->mpctx->global, ctx->clients);
ctx->mpctx->gl_cb_ctx = cb;
}
return cb;
@@ -1708,7 +1708,11 @@ int mpv_opengl_cb_init_gl(mpv_opengl_cb_context *ctx, const char *exts,
{
return MPV_ERROR_NOT_IMPLEMENTED;
}
-int mpv_opengl_cb_render(mpv_opengl_cb_context *ctx, int fbo, int vp[4])
+int mpv_opengl_cb_draw(mpv_opengl_cb_context *ctx, int fbo, int w, int h)
+{
+ return MPV_ERROR_NOT_IMPLEMENTED;
+}
+int mpv_opengl_cb_report_flip(mpv_opengl_cb_context *ctx, int64_t time)
{
return MPV_ERROR_NOT_IMPLEMENTED;
}
@@ -1718,6 +1722,11 @@ int mpv_opengl_cb_uninit_gl(mpv_opengl_cb_context *ctx)
}
#endif
+int mpv_opengl_cb_render(mpv_opengl_cb_context *ctx, int fbo, int vp[4])
+{
+ return mpv_opengl_cb_draw(ctx, fbo, vp[2], vp[3]);
+}
+
void *mpv_get_sub_api(mpv_handle *ctx, mpv_sub_api sub_api)
{
if (!ctx->mpctx->initialized)
diff --git a/player/client.h b/player/client.h
index 656e360..5bc3c77 100644
--- a/player/client.h
+++ b/player/client.h
@@ -42,7 +42,6 @@ struct mpv_opengl_cb_context;
struct mpv_global;
struct osd_state;
struct mpv_opengl_cb_context *mp_opengl_create(struct mpv_global *g,
- struct osd_state *osd,
struct mp_client_api *client_api);
void kill_video(struct mp_client_api *client_api);
diff --git a/player/command.c b/player/command.c
index c775e0b..3556986 100644
--- a/player/command.c
+++ b/player/command.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
@@ -90,7 +89,7 @@ struct command_ctx {
int num_hooks;
int64_t hook_seq; // for hook_handler.seq
- struct ao_device_list *cached_ao_devices;
+ struct ao_hotplug *hotplug;
};
struct overlay {
@@ -217,14 +216,6 @@ static void mark_seek(struct MPContext *mpctx)
cmd->last_seek_time = now;
}
-static char *format_bitrate(int rate)
-{
- if (rate < 1024 * 1024)
- return talloc_asprintf(NULL, "%.3f kbps", rate / 1000.0);
-
- return talloc_asprintf(NULL, "%.3f mbps", rate / 1000000.0);
-}
-
static char *format_file_size(int64_t size)
{
double s = size;
@@ -565,7 +556,7 @@ static int mp_property_percent_pos(void *ctx, struct m_property *prop,
switch (action) {
case M_PROPERTY_SET: {
double pos = *(double *)arg;
- queue_seek(mpctx, MPSEEK_FACTOR, pos / 100.0, 0, true);
+ queue_seek(mpctx, MPSEEK_FACTOR, pos / 100.0, MPSEEK_DEFAULT, true);
return M_PROPERTY_OK;
}
case M_PROPERTY_GET: {
@@ -613,7 +604,7 @@ static int mp_property_time_pos(void *ctx, struct m_property *prop,
return M_PROPERTY_UNAVAILABLE;
if (action == M_PROPERTY_SET) {
- queue_seek(mpctx, MPSEEK_ABSOLUTE, *(double *)arg, 0, true);
+ queue_seek(mpctx, MPSEEK_ABSOLUTE, *(double *)arg, MPSEEK_DEFAULT, true);
return M_PROPERTY_OK;
}
return property_time(action, arg, get_current_time(mpctx));
@@ -703,6 +694,14 @@ static int mp_property_disc_menu(void *ctx, struct m_property *prop,
return m_property_flag_ro(action, arg, !!state);
}
+static int mp_property_mouse_on_button(void *ctx, struct m_property *prop,
+ int action, void *arg)
+{
+ MPContext *mpctx = ctx;
+ bool on = mp_nav_mouse_on_button(mpctx);
+ return m_property_flag_ro(action, arg, on);
+}
+
/// Current chapter (RW)
static int mp_property_chapter(void *ctx, struct m_property *prop,
int action, void *arg)
@@ -1384,6 +1383,24 @@ static int mp_property_demuxer_cache_duration(void *ctx, struct m_property *prop
return m_property_double_ro(action, arg, s.ts_duration);
}
+static int mp_property_demuxer_cache_time(void *ctx, struct m_property *prop,
+ int action, void *arg)
+{
+ MPContext *mpctx = ctx;
+ if (!mpctx->demuxer)
+ return M_PROPERTY_UNAVAILABLE;
+
+ struct demux_ctrl_reader_state s;
+ if (demux_control(mpctx->demuxer, DEMUXER_CTRL_GET_READER_STATE, &s) < 1)
+ return M_PROPERTY_UNAVAILABLE;
+
+ double ts = s.ts_range[1];
+ if (ts == MP_NOPTS_VALUE)
+ return M_PROPERTY_UNAVAILABLE;
+
+ return m_property_double_ro(action, arg, ts);
+}
+
static int mp_property_demuxer_cache_idle(void *ctx, struct m_property *prop,
int action, void *arg)
{
@@ -1549,6 +1566,19 @@ static int mp_property_audio_device(void *ctx, struct m_property *prop,
int action, void *arg)
{
struct MPContext *mpctx = ctx;
+ struct command_ctx *cmd = mpctx->command_ctx;
+ if (action == M_PROPERTY_PRINT) {
+ if (!cmd->hotplug)
+ cmd->hotplug = ao_hotplug_create(mpctx->global, mpctx->input);
+ struct ao_device_list *list = ao_hotplug_get_device_list(cmd->hotplug);
+ for (int n = 0; n < list->num_devices; n++) {
+ struct ao_device_desc *dev = &list->devices[n];
+ if (dev->name && strcmp(dev->name, mpctx->opts->audio_device)) {
+ *(char **)arg = talloc_strdup(NULL, dev->desc ? dev->desc : "?");
+ return M_PROPERTY_OK;
+ }
+ }
+ }
int r = mp_property_generic_option(mpctx, prop, action, arg);
if (action == M_PROPERTY_SET)
reload_audio_output(mpctx);
@@ -1560,14 +1590,12 @@ static int mp_property_audio_devices(void *ctx, struct m_property *prop,
{
struct MPContext *mpctx = ctx;
struct command_ctx *cmd = mpctx->command_ctx;
- if (!cmd->cached_ao_devices)
- cmd->cached_ao_devices = ao_get_device_list(mpctx->global);
- if (!cmd->cached_ao_devices)
- return M_PROPERTY_ERROR;
- talloc_steal(cmd, cmd->cached_ao_devices);
+ if (!cmd->hotplug)
+ cmd->hotplug = ao_hotplug_create(mpctx->global, mpctx->input);
- return m_property_read_list(action, arg, cmd->cached_ao_devices->num_devices,
- get_device_entry, cmd->cached_ao_devices);
+ struct ao_device_list *list = ao_hotplug_get_device_list(cmd->hotplug);
+ return m_property_read_list(action, arg, list->num_devices,
+ get_device_entry, list);
}
static int mp_property_ao(void *ctx, struct m_property *p, int action, void *arg)
@@ -1580,10 +1608,12 @@ static int mp_property_ao(void *ctx, struct m_property *p, int action, void *arg
static int mp_property_ao_detected_device(void *ctx,struct m_property *prop,
int action, void *arg)
{
- MPContext *mpctx = ctx;
+ struct MPContext *mpctx = ctx;
+ struct command_ctx *cmd = mpctx->command_ctx;
if (!mpctx->ao)
return M_PROPERTY_UNAVAILABLE;
- return m_property_strdup_ro(action, arg, ao_get_detected_device(mpctx->ao));
+ const char *d = ao_hotplug_get_detected_device(cmd->hotplug);
+ return m_property_strdup_ro(action, arg, d);
}
/// Audio delay (RW)
@@ -1624,20 +1654,6 @@ static int mp_property_audio_codec(void *ctx, struct m_property *prop,
return m_property_strdup_ro(action, arg, c);
}
-/// Audio bitrate (RO)
-static int mp_property_audio_bitrate(void *ctx, struct m_property *prop,
- int action, void *arg)
-{
- MPContext *mpctx = ctx;
- if (!mpctx->d_audio)
- return M_PROPERTY_UNAVAILABLE;
- if (action == M_PROPERTY_PRINT) {
- *(char **)arg = format_bitrate(mpctx->d_audio->bitrate);
- return M_PROPERTY_OK;
- }
- return m_property_int_ro(action, arg, mpctx->d_audio->bitrate);
-}
-
/// Samplerate (RO)
static int mp_property_samplerate(void *ctx, struct m_property *prop,
int action, void *arg)
@@ -2016,7 +2032,7 @@ static int mp_property_hwdec(void *ctx, struct m_property *prop,
opts->hwdec_api = new;
reinit_video_chain(mpctx);
if (last_pts != MP_NOPTS_VALUE)
- queue_seek(mpctx, MPSEEK_ABSOLUTE, last_pts, 1, true);
+ queue_seek(mpctx, MPSEEK_ABSOLUTE, last_pts, MPSEEK_EXACT, true);
return M_PROPERTY_OK;
}
}
@@ -2145,20 +2161,6 @@ static int mp_property_deinterlace(void *ctx, struct m_property *prop,
return M_PROPERTY_NOT_IMPLEMENTED;
}
-// Generic option + requires hard refresh to make changes take effect.
-static int video_refresh_property_helper(struct m_property *prop, int action,
- void *arg, MPContext *mpctx)
-{
- int r = mp_property_generic_option(mpctx, prop, action, arg);
- if (action == M_PROPERTY_SET) {
- if (mpctx->d_video) {
- reinit_video_filters(mpctx);
- mp_force_video_refresh(mpctx);
- }
- }
- return r;
-}
-
static int video_simple_refresh_property(void *ctx, struct m_property *prop,
int action, void *arg)
{
@@ -2169,113 +2171,6 @@ static int video_simple_refresh_property(void *ctx, struct m_property *prop,
return r;
}
-static void append_csp(char **ptr, const char *name, const char *const *names,
- int value)
-{
- const char *cspname = names[value];
- if (name[0] == '*') {
- name++;
- } else if (value == 0) {
- cspname = "unknown";
- }
- *ptr = talloc_asprintf_append(*ptr, "%s: %s\n", name, cspname);
-}
-
-static int mp_property_colormatrix(void *ctx, struct m_property *prop,
- int action, void *arg)
-{
- MPContext *mpctx = ctx;
- if (action != M_PROPERTY_PRINT)
- return video_refresh_property_helper(prop, action, arg, mpctx);
-
- struct MPOpts *opts = mpctx->opts;
-
- struct mp_image_params vo_csp = {0};
- if (mpctx->video_out)
- vo_control(mpctx->video_out, VOCTRL_GET_COLORSPACE, &vo_csp);
-
- struct mp_image_params vd_csp = {0};
- if (mpctx->d_video)
- vd_csp = mpctx->d_video->decoder_output;
-
- char *res = talloc_strdup(NULL, "");
- append_csp(&res, "*Requested", mp_csp_names, opts->requested_colorspace);
- append_csp(&res, "Video decoder", mp_csp_names, vd_csp.colorspace);
- *(char **)arg = res;
- return M_PROPERTY_OK;
-}
-
-static int mp_property_colormatrix_input_range(void *ctx, struct m_property *prop,
- int action, void *arg)
-{
- MPContext *mpctx = ctx;
- if (action != M_PROPERTY_PRINT)
- return video_refresh_property_helper(prop, action, arg, mpctx);
-
- struct MPOpts *opts = mpctx->opts;
-
- struct mp_image_params vo_csp = {0};
- if (mpctx->video_out)
- vo_control(mpctx->video_out, VOCTRL_GET_COLORSPACE, &vo_csp);
-
- struct mp_image_params vd_csp = {0};
- if (mpctx->d_video)
- vd_csp = mpctx->d_video->decoder_output;
-
- char *res = talloc_strdup(NULL, "");
- append_csp(&res, "*Requested", mp_csp_levels_names,
- opts->requested_input_range);
- append_csp(&res, "Video decoder", mp_csp_levels_names, vd_csp.colorlevels);
- *(char **)arg = res;
- return M_PROPERTY_OK;
-}
-
-static int mp_property_colormatrix_output_range(void *ctx, struct m_property *prop,
- int action, void *arg)
-{
- MPContext *mpctx = ctx;
- if (action != M_PROPERTY_PRINT)
- return video_refresh_property_helper(prop, action, arg, mpctx);
-
- struct MPOpts *opts = mpctx->opts;
-
- struct mp_image_params actual = {0};
- if (mpctx->video_out)
- vo_control(mpctx->video_out, VOCTRL_GET_COLORSPACE, &actual);
-
- char *res = talloc_strdup(NULL, "");
- append_csp(&res, "*Requested", mp_csp_levels_names,
- opts->requested_output_range);
- append_csp(&res, "Video output", mp_csp_levels_names, actual.outputlevels);
- *(char **)arg = res;
- return M_PROPERTY_OK;
-}
-
-static int mp_property_primaries(void *ctx, struct m_property *prop,
- int action, void *arg)
-{
- MPContext *mpctx = ctx;
- if (action != M_PROPERTY_PRINT)
- return video_refresh_property_helper(prop, action, arg, mpctx);
-
- struct MPOpts *opts = mpctx->opts;
-
- struct mp_image_params vo_csp = {0};
- if (mpctx->video_out)
- vo_control(mpctx->video_out, VOCTRL_GET_COLORSPACE, &vo_csp);
-
- struct mp_image_params vd_csp = {0};
- if (mpctx->d_video)
- vd_csp = mpctx->d_video->decoder_output;
-
- char *res = talloc_strdup(NULL, "");
- append_csp(&res, "*Requested", mp_csp_prim_names, opts->requested_primaries);
- append_csp(&res, "Video decoder", mp_csp_prim_names, vd_csp.primaries);
- append_csp(&res, "Video output", mp_csp_prim_names, vo_csp.primaries);
- *(char **)arg = res;
- return M_PROPERTY_OK;
-}
-
// Update options which are managed through VOCTRL_GET/SET_PANSCAN.
static int panscan_property_helper(void *ctx, struct m_property *prop,
int action, void *arg)
@@ -2438,21 +2333,6 @@ static int mp_property_video_codec(void *ctx, struct m_property *prop,
return m_property_strdup_ro(action, arg, c);
}
-
-/// Video bitrate (RO)
-static int mp_property_video_bitrate(void *ctx, struct m_property *prop,
- int action, void *arg)
-{
- MPContext *mpctx = ctx;
- if (!mpctx->d_video)
- return M_PROPERTY_UNAVAILABLE;
- if (action == M_PROPERTY_PRINT) {
- *(char **)arg = format_bitrate(mpctx->d_video->bitrate);
- return M_PROPERTY_OK;
- }
- return m_property_int_ro(action, arg, mpctx->d_video->bitrate);
-}
-
static int property_imgparams(struct mp_image_params p, int action, void *arg)
{
if (!p.imgfmt)
@@ -2478,10 +2358,18 @@ static int property_imgparams(struct mp_image_params p, int action, void *arg)
{"dh", SUB_PROP_INT(p.d_h)},
{"aspect", SUB_PROP_FLOAT(dar)},
{"par", SUB_PROP_FLOAT(dar / sar)},
- {"colormatrix", SUB_PROP_STR(mp_csp_names[p.colorspace])},
- {"colorlevels", SUB_PROP_STR(mp_csp_levels_names[p.colorlevels])},
- {"primaries", SUB_PROP_STR(mp_csp_prim_names[p.primaries])},
- {"chroma-location", SUB_PROP_STR(mp_chroma_names[p.chroma_location])},
+ {"colormatrix",
+ SUB_PROP_STR(m_opt_choice_str(mp_csp_names, p.colorspace))},
+ {"colorlevels",
+ SUB_PROP_STR(m_opt_choice_str(mp_csp_levels_names, p.colorlevels))},
+ {"outputlevels",
+ SUB_PROP_STR(m_opt_choice_str(mp_csp_levels_names, p.outputlevels))},
+ {"primaries",
+ SUB_PROP_STR(m_opt_choice_str(mp_csp_prim_names, p.primaries))},
+ {"gamma",
+ SUB_PROP_STR(m_opt_choice_str(mp_csp_trc_names, p.gamma))},
+ {"chroma-location",
+ SUB_PROP_STR(m_opt_choice_str(mp_chroma_names, p.chroma_location))},
{"rotate", SUB_PROP_INT(p.rotate)},
{0}
};
@@ -2587,6 +2475,21 @@ static int mp_property_win_minimized(void *ctx, struct m_property *prop,
return m_property_flag_ro(action, arg, state & VO_WIN_STATE_MINIMIZED);
}
+static int mp_property_display_fps(void *ctx, struct m_property *prop,
+ int action, void *arg)
+{
+ MPContext *mpctx = ctx;
+ struct vo *vo = mpctx->video_out;
+ if (!vo)
+ return M_PROPERTY_UNAVAILABLE;
+
+ double fps = vo_get_display_fps(vo);
+ if (fps < 1)
+ return M_PROPERTY_UNAVAILABLE;
+
+ return m_property_double_ro(action, arg, fps);
+}
+
static int mp_property_display_names(void *ctx, struct m_property *prop,
int action, void *arg)
{
@@ -3095,7 +2998,8 @@ static int mp_property_ab_loop(void *ctx, struct m_property *prop,
double now = mpctx->playback_pts;
if (now != MP_NOPTS_VALUE && opts->ab_loop[0] != MP_NOPTS_VALUE &&
opts->ab_loop[1] != MP_NOPTS_VALUE && now >= opts->ab_loop[1])
- queue_seek(mpctx, MPSEEK_ABSOLUTE, opts->ab_loop[0], 1, false);
+ queue_seek(mpctx, MPSEEK_ABSOLUTE, opts->ab_loop[0],
+ MPSEEK_EXACT, false);
}
// Update if visible
set_osd_bar_chapters(mpctx, OSD_BAR_SEEK);
@@ -3107,7 +3011,8 @@ static int mp_property_packet_bitrate(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
- int type = (intptr_t)prop->priv;
+ int type = (uintptr_t)prop->priv & ~0x100;
+ bool old = (uintptr_t)prop->priv & 0x100;
if (!mpctx->demuxer)
return M_PROPERTY_UNAVAILABLE;
@@ -3116,8 +3021,41 @@ static int mp_property_packet_bitrate(void *ctx, struct m_property *prop,
if (demux_control(mpctx->demuxer, DEMUXER_CTRL_GET_BITRATE_STATS, &r) < 1)
return M_PROPERTY_UNAVAILABLE;
- // r[type] is in bytes/second -> kilobits
- return m_property_int64_ro(action, arg, r[type] * 8 / 1000.0 + 0.5);
+ // r[type] is in bytes/second -> bits
+ double rate = r[type] * 8;
+
+ // Same story, but used kilobits for some reason.
+ if (old)
+ return m_property_int64_ro(action, arg, rate / 1000.0 + 0.5);
+
+ if (action == M_PROPERTY_PRINT) {
+ rate /= 1000;
+ if (rate < 1000) {
+ *(char **)arg = talloc_asprintf(NULL, "%d kbps", (int)rate);
+ } else {
+ *(char **)arg = talloc_asprintf(NULL, "%.3f mbps", rate / 1000.0);
+ }
+ return M_PROPERTY_OK;
+ }
+ return m_property_int64_ro(action, arg, rate);
+}
+
+static int mp_property_cwd(void *ctx, struct m_property *prop,
+ int action, void *arg)
+{
+ switch (action) {
+ case M_PROPERTY_GET: {
+ char *cwd = mp_getcwd(NULL);
+ if (!cwd)
+ return M_PROPERTY_ERROR;
+ *(char **)arg = cwd;
+ return M_PROPERTY_OK;
+ }
+ 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_version(void *ctx, struct m_property *prop,
@@ -3329,6 +3267,7 @@ static const struct m_property mp_properties[] = {
{"playback-time", mp_property_playback_time},
{"disc-title", mp_property_disc_title},
{"disc-menu-active", mp_property_disc_menu},
+ {"disc-mouse-on-button", mp_property_mouse_on_button},
{"chapter", mp_property_chapter},
{"edition", mp_property_edition},
{"disc-titles", mp_property_disc_titles},
@@ -3350,6 +3289,7 @@ static const struct m_property mp_properties[] = {
{"cache-size", mp_property_cache_size},
{"cache-idle", mp_property_cache_idle},
{"demuxer-cache-duration", mp_property_demuxer_cache_duration},
+ {"demuxer-cache-time", mp_property_demuxer_cache_time},
{"demuxer-cache-idle", mp_property_demuxer_cache_idle},
{"cache-buffering-state", mp_property_cache_buffering},
{"paused-for-cache", mp_property_paused_for_cache},
@@ -3375,7 +3315,6 @@ static const struct m_property mp_properties[] = {
{"audio-delay", mp_property_audio_delay},
{"audio-format", mp_property_audio_format},
{"audio-codec", mp_property_audio_codec},
- {"audio-bitrate", mp_property_audio_bitrate},
{"audio-samplerate", mp_property_samplerate},
{"audio-channels", mp_property_channels},
{"aid", mp_property_audio},
@@ -3390,10 +3329,6 @@ static const struct m_property mp_properties[] = {
{"fullscreen", mp_property_fullscreen},
{"deinterlace", mp_property_deinterlace},
{"field-dominance", mp_property_generic_option},
- {"colormatrix", mp_property_colormatrix},
- {"colormatrix-input-range", mp_property_colormatrix_input_range},
- {"colormatrix-output-range", mp_property_colormatrix_output_range},
- {"colormatrix-primaries", mp_property_primaries},
{"ontop", mp_property_ontop},
{"border", mp_property_border},
{"on-all-workspaces", mp_property_all_workspaces},
@@ -3414,7 +3349,6 @@ static const struct m_property mp_properties[] = {
{"video-params", mp_property_vd_imgparams},
{"video-format", mp_property_video_format},
{"video-codec", mp_property_video_codec},
- {"video-bitrate", mp_property_video_bitrate},
M_PROPERTY_ALIAS("dwidth", "video-out-params/dw"),
M_PROPERTY_ALIAS("dheight", "video-out-params/dh"),
M_PROPERTY_ALIAS("width", "video-params/w"),
@@ -3448,7 +3382,8 @@ static const struct m_property mp_properties[] = {
{"sub-visibility", property_osd_helper},
{"sub-forced-only", property_osd_helper},
{"sub-scale", property_osd_helper},
- {"ass-use-margins", property_osd_helper},
+ {"sub-use-margins", property_osd_helper},
+ {"ass-force-margins", property_osd_helper},
{"ass-vsfilter-aspect-compat", property_osd_helper},
{"ass-style-override", property_osd_helper},
@@ -3460,11 +3395,15 @@ static const struct m_property mp_properties[] = {
{"ab-loop-a", mp_property_ab_loop},
{"ab-loop-b", mp_property_ab_loop},
-#define PROPERTY_BITRATE(name, type) \
- {name, mp_property_packet_bitrate, (void *)(intptr_t)type}
- PROPERTY_BITRATE("packet-video-bitrate", STREAM_VIDEO),
- PROPERTY_BITRATE("packet-audio-bitrate", STREAM_AUDIO),
- PROPERTY_BITRATE("packet-sub-bitrate", STREAM_SUB),
+#define PROPERTY_BITRATE(name, old, type) \
+ {name, mp_property_packet_bitrate, (void *)(uintptr_t)((type)|(old?0x100:0))}
+ PROPERTY_BITRATE("packet-video-bitrate", true, STREAM_VIDEO),
+ PROPERTY_BITRATE("packet-audio-bitrate", true, STREAM_AUDIO),
+ PROPERTY_BITRATE("packet-sub-bitrate", true, STREAM_SUB),
+
+ PROPERTY_BITRATE("video-bitrate", false, STREAM_VIDEO),
+ PROPERTY_BITRATE("audio-bitrate", false, STREAM_AUDIO),
+ PROPERTY_BITRATE("sub-bitrate", false, STREAM_SUB),
#define PROPERTY_TV_COLOR(name, type) \
{name, mp_property_tv_color, (void *)(intptr_t)type}
@@ -3486,12 +3425,11 @@ static const struct m_property mp_properties[] = {
TRACK_FF("ff-aid", STREAM_AUDIO),
TRACK_FF("ff-sid", STREAM_SUB),
- M_PROPERTY_ALIAS("video", "vid"),
- M_PROPERTY_ALIAS("audio", "aid"),
- M_PROPERTY_ALIAS("sub", "sid"),
-
{"window-minimized", mp_property_win_minimized},
{"display-names", mp_property_display_names},
+ {"display-fps", mp_property_display_fps},
+
+ {"working-directory", mp_property_cwd},
{"mpv-version", mp_property_version},
{"mpv-configuration", mp_property_configuration},
@@ -3501,6 +3439,16 @@ static const struct m_property mp_properties[] = {
{"option-info", mp_property_option_info},
{"property-list", mp_property_list},
+ // compatibility
+ M_PROPERTY_ALIAS("video", "vid"),
+ M_PROPERTY_ALIAS("audio", "aid"),
+ M_PROPERTY_ALIAS("sub", "sid"),
+ M_PROPERTY_ALIAS("colormatrix", "video-params/colormatrix"),
+ M_PROPERTY_ALIAS("colormatrix-input-range", "video-params/colorlevels"),
+ M_PROPERTY_ALIAS("colormatrix-output-range", "video-params/outputlevels"),
+ M_PROPERTY_ALIAS("colormatrix-primaries", "video-params/primaries"),
+ M_PROPERTY_ALIAS("colormatrix-gamma", "video-params/gamma"),
+
{0},
};
@@ -3519,22 +3467,27 @@ static const char *const *const mp_event_property_change[] = {
E(MPV_EVENT_UNPAUSE, "pause", "paused-on-cache", "core-idle", "eof-reached"),
E(MPV_EVENT_TICK, "time-pos", "stream-pos", "stream-time-pos", "avsync",
"percent-pos", "time-remaining", "playtime-remaining", "playback-time",
- "estimated-vf-fps"),
+ "estimated-vf-fps", "drop-frame-count", "vo-drop-frame-count",
+ "total-avsync-change"),
E(MPV_EVENT_VIDEO_RECONFIG, "video-out-params", "video-params",
"video-format", "video-codec", "video-bitrate", "dwidth", "dheight",
"width", "height", "fps", "aspect", "vo-configured", "current-vo",
- "detected-hwdec"),
+ "detected-hwdec", "colormatrix", "colormatrix-input-range",
+ "colormatrix-output-range", "colormatrix-primaries"),
E(MPV_EVENT_AUDIO_RECONFIG, "audio-format", "audio-codec", "audio-bitrate",
"samplerate", "channels", "audio", "volume", "mute", "balance",
- "volume-restore-data", "current-ao", "audio-out-detected-device"),
+ "volume-restore-data", "current-ao"),
E(MPV_EVENT_SEEK, "seeking", "core-idle"),
E(MPV_EVENT_PLAYBACK_RESTART, "seeking", "core-idle"),
- E(MPV_EVENT_METADATA_UPDATE, "metadata", "filtered-metadata"),
+ E(MPV_EVENT_METADATA_UPDATE, "metadata", "filtered-metadata", "media-title"),
E(MPV_EVENT_CHAPTER_CHANGE, "chapter", "chapter-metadata"),
E(MP_EVENT_CACHE_UPDATE, "cache", "cache-free", "cache-used", "cache-idle",
- "demuxer-cache-duration", "demuxer-cache-idle", "paused-for-cache"),
+ "demuxer-cache-duration", "demuxer-cache-idle", "paused-for-cache",
+ "demuxer-cache-time"),
E(MP_EVENT_WIN_RESIZE, "window-scale"),
- E(MP_EVENT_WIN_STATE, "window-minimized", "display-names"),
+ E(MP_EVENT_WIN_STATE, "window-minimized", "display-names", "display-fps"),
+ E(MP_EVENT_AUDIO_DEVICES, "audio-device-list"),
+ E(MP_EVENT_DETECTED_AUDIO_DEVICE, "audio-out-detected-device"),
};
#undef E
@@ -3686,6 +3639,8 @@ static const struct property_osd_display {
.msg = "RGB output range:\n${colormatrix-output-range}" },
{ "colormatrix-primaries",
.msg = "Colorspace primaries:\n${colormatrix-primaries}", },
+ { "colormatrix-gamma",
+ .msg = "Colorspace gamma:\n${colormatrix-gamma}", },
{ "gamma", "Gamma", .osd_progbar = OSD_BRIGHTNESS },
{ "brightness", "Brightness", .osd_progbar = OSD_BRIGHTNESS },
{ "contrast", "Contrast", .osd_progbar = OSD_CONTRAST },
@@ -3711,6 +3666,7 @@ static const struct property_osd_display {
{ "tv-contrast", "Contrast", .osd_progbar = OSD_CONTRAST },
{ "ab-loop-a", "A-B loop point A"},
{ "ab-loop-b", "A-B loop point B"},
+ { "audio-device", "Audio device"},
// By default, don't display the following properties on OSD
{ "pause", NULL },
{ "fullscreen", NULL },
@@ -3896,7 +3852,7 @@ static void recreate_overlays(struct MPContext *mpctx)
if (new == cmd->overlay_osd_current)
new += 1; // pick the unused one
new->format = SUBBITMAP_RGBA;
- new->bitmap_id = new->bitmap_pos_id = 1;
+ new->change_id = 1;
// overlay array can have unused entries, but parts list must be "packed"
new->num_parts = 0;
for (int n = 0; n < cmd->num_overlays; n++) {
@@ -4014,39 +3970,6 @@ static void overlay_uninit(struct MPContext *mpctx)
osd_set_external2(mpctx->osd, NULL);
}
-struct subprocess_args {
- struct mp_log *log;
- char **args;
-};
-
-static void *run_subprocess(void *ptr)
-{
- struct subprocess_args *p = ptr;
- pthread_detach(pthread_self());
-
- mp_msg_flush_status_line(p->log);
-
- char *err = NULL;
- if (mp_subprocess(p->args, NULL, NULL, NULL, NULL, &err) < 0)
- mp_err(p->log, "Running subprocess failed: %s\n", err);
-
- talloc_free(p);
- return NULL;
-}
-
-static void subprocess_detached(struct mp_log *log, char **args)
-{
- struct subprocess_args *p = talloc_zero(NULL, struct subprocess_args);
- p->log = mp_log_new(p, log, NULL);
- int num_args = 0;
- for (int n = 0; args[n]; n++)
- MP_TARRAY_APPEND(p, p->args, num_args, talloc_strdup(p, args[n]));
- MP_TARRAY_APPEND(p, p->args, num_args, NULL);
- pthread_t thread;
- if (pthread_create(&thread, NULL, run_subprocess, p))
- talloc_free(p);
-}
-
struct cycle_counter {
char **args;
int counter;
@@ -4137,7 +4060,23 @@ static bool check_property_autorepeat(char *property, struct MPContext *mpctx)
return true;
}
-int run_command(MPContext *mpctx, mp_cmd_t *cmd)
+static struct mpv_node *add_map_entry(struct mpv_node *dst, const char *key)
+{
+ struct mpv_node_list *list = dst->u.list;
+ assert(dst->format == MPV_FORMAT_NODE_MAP && dst->u.list);
+ MP_TARRAY_GROW(list, list->values, list->num);
+ MP_TARRAY_GROW(list, list->keys, list->num);
+ list->keys[list->num] = talloc_strdup(list, key);
+ return &list->values[list->num++];
+}
+
+#define ADD_MAP_INT(dst, name, i) (*add_map_entry(dst, name) = \
+ (struct mpv_node){ .format = MPV_FORMAT_INT64, .u.int64 = (i) });
+
+#define ADD_MAP_CSTR(dst, name, s) (*add_map_entry(dst, name) = \
+ (struct mpv_node){ .format = MPV_FORMAT_STRING, .u.string = (s) });
+
+int run_command(struct MPContext *mpctx, struct mp_cmd *cmd, struct mpv_node *res)
{
struct command_ctx *cmdctx = mpctx->command_ctx;
struct MPOpts *opts = mpctx->opts;
@@ -4166,20 +4105,24 @@ int run_command(MPContext *mpctx, mp_cmd_t *cmd)
switch (cmd->id) {
case MP_CMD_SEEK: {
double v = cmd->args[0].v.d * cmd->scale;
- int abs = cmd->args[1].v.i;
- int exact = cmd->args[2].v.i;
+ int abs = cmd->args[1].v.i & 3;
+ enum seek_precision precision = MPSEEK_DEFAULT;
+ switch (((cmd->args[2].v.i | cmd->args[1].v.i) >> 3) & 3) {
+ case 1: precision = MPSEEK_KEYFRAME; break;
+ case 2: precision = MPSEEK_EXACT; break;
+ }
if (!mpctx->num_sources)
return -1;
mark_seek(mpctx);
if (abs == 2) { // Absolute seek to a timestamp in seconds
- queue_seek(mpctx, MPSEEK_ABSOLUTE, v, exact, false);
+ queue_seek(mpctx, MPSEEK_ABSOLUTE, v, precision, false);
set_osd_function(mpctx,
v > get_current_time(mpctx) ? OSD_FFW : OSD_REW);
} else if (abs) { /* Absolute seek by percentage */
- queue_seek(mpctx, MPSEEK_FACTOR, v / 100.0, exact, false);
+ queue_seek(mpctx, MPSEEK_FACTOR, v / 100.0, precision, false);
set_osd_function(mpctx, OSD_FFW); // Direction isn't set correctly
} else {
- queue_seek(mpctx, MPSEEK_RELATIVE, v, exact, false);
+ queue_seek(mpctx, MPSEEK_RELATIVE, v, precision, false);
set_osd_function(mpctx, (v > 0) ? OSD_FFW : OSD_REW);
}
if (bar_osd)
@@ -4200,7 +4143,7 @@ int run_command(MPContext *mpctx, mp_cmd_t *cmd)
} else if (oldpts != MP_NOPTS_VALUE) {
cmdctx->last_seek_pts = get_current_time(mpctx);
cmdctx->marked_pts = MP_NOPTS_VALUE;
- queue_seek(mpctx, MPSEEK_ABSOLUTE, oldpts, 1, false);
+ queue_seek(mpctx, MPSEEK_ABSOLUTE, oldpts, MPSEEK_EXACT, false);
set_osd_function(mpctx, OSD_REW);
if (bar_osd)
mpctx->add_osd_seek_info |= OSD_SEEK_INFO_BAR;
@@ -4378,9 +4321,7 @@ int run_command(MPContext *mpctx, mp_cmd_t *cmd)
struct playlist_entry *e = mp_next_file(mpctx, dir, force);
if (!e && !force)
return -1;
- mpctx->playlist->current = e;
- mpctx->playlist->current_was_replaced = false;
- mpctx->stop_play = PT_CURRENT_ENTRY;
+ mp_set_playlist_entry(mpctx, e);
if (on_osd & MP_ON_OSD_MSG)
mpctx->add_osd_seek_info |= OSD_SEEK_INFO_CURRENT_FILE;
break;
@@ -4409,7 +4350,7 @@ int run_command(MPContext *mpctx, mp_cmd_t *cmd)
// rounding for the mess of it.
a[0] += 0.01 * (a[1] > 0 ? 1 : -1);
mark_seek(mpctx);
- queue_seek(mpctx, MPSEEK_RELATIVE, a[0], 1, false);
+ queue_seek(mpctx, MPSEEK_RELATIVE, a[0], MPSEEK_EXACT, false);
set_osd_function(mpctx, (a[0] > 0) ? OSD_FFW : OSD_REW);
if (bar_osd)
mpctx->add_osd_seek_info |= OSD_SEEK_INFO_BAR;
@@ -4618,19 +4559,63 @@ int run_command(MPContext *mpctx, mp_cmd_t *cmd)
return -1;
}
- case MP_CMD_SCREENSHOT:
- screenshot_request(mpctx, cmd->args[0].v.i, cmd->args[1].v.i, msg_osd);
+ case MP_CMD_RESCAN_EXTERNAL_FILES: {
+ if (!mpctx->playing)
+ return -1;
+ autoload_external_files(mpctx);
+ if (cmd->args[0].v.i) {
+ // somewhat fuzzy and not ideal
+ struct track *a = select_track(mpctx, STREAM_AUDIO, opts->audio_id,
+ opts->audio_id_ff, opts->audio_lang);
+ if (a && a->is_external)
+ mp_switch_track(mpctx, STREAM_AUDIO, a);
+ struct track *s = select_track(mpctx, STREAM_SUB, opts->sub_id,
+ opts->sub_id_ff, opts->sub_lang);
+ if (s && s->is_external)
+ mp_switch_track(mpctx, STREAM_SUB, s);
+ }
break;
+ }
+
+ case MP_CMD_SCREENSHOT: {
+ int mode = cmd->args[0].v.i & 3;
+ int freq = (cmd->args[0].v.i | cmd->args[1].v.i) >> 3;
+ screenshot_request(mpctx, mode, freq, msg_osd);
+ break;
+ }
case MP_CMD_SCREENSHOT_TO_FILE:
screenshot_to_file(mpctx, cmd->args[0].v.s, cmd->args[1].v.i, msg_osd);
break;
+ case MP_CMD_SCREENSHOT_RAW: {
+ if (!res)
+ return -1;
+ struct mp_image *img = screenshot_get_rgb(mpctx, cmd->args[0].v.i);
+ if (!img)
+ return -1;
+ struct mpv_node_list *info = talloc_zero(NULL, struct mpv_node_list);
+ talloc_steal(info, img);
+ *res = (mpv_node){ .format = MPV_FORMAT_NODE_MAP, .u.list = info };
+ ADD_MAP_INT(res, "w", img->w);
+ ADD_MAP_INT(res, "h", img->h);
+ ADD_MAP_INT(res, "stride", img->stride[0]);
+ ADD_MAP_CSTR(res, "format", "bgr0");
+ struct mpv_byte_array *ba = talloc_ptrtype(info, ba);
+ *ba = (struct mpv_byte_array){
+ .data = img->planes[0],
+ .size = img->stride[0] * img->h,
+ };
+ *add_map_entry(res, "data") =
+ (struct mpv_node){.format = MPV_FORMAT_BYTE_ARRAY, .u.ba = ba,};
+ break;
+ }
+
case MP_CMD_RUN: {
char *args[MP_CMD_MAX_ARGS + 1] = {0};
for (int n = 0; n < cmd->nargs; n++)
args[n] = cmd->args[n].v.s;
- subprocess_detached(mpctx->log, args);
+ mp_subprocess_detached(mpctx->log, args);
break;
}
@@ -4766,7 +4751,7 @@ int run_command(MPContext *mpctx, mp_cmd_t *cmd)
case MP_CMD_COMMAND_LIST: {
for (struct mp_cmd *sub = cmd->args[0].v.p; sub; sub = sub->queue_next)
- run_command(mpctx, sub);
+ run_command(mpctx, sub, NULL);
break;
}
@@ -4822,6 +4807,7 @@ int run_command(MPContext *mpctx, mp_cmd_t *cmd)
void command_uninit(struct MPContext *mpctx)
{
overlay_uninit(mpctx);
+ ao_hotplug_destroy(mpctx->command_ctx->hotplug);
talloc_free(mpctx->command_ctx);
mpctx->command_ctx = NULL;
}
@@ -4856,7 +4842,8 @@ static void command_event(struct MPContext *mpctx, int event, void *arg)
now >= opts->ab_loop[1])
{
mark_seek(mpctx);
- queue_seek(mpctx, MPSEEK_ABSOLUTE, opts->ab_loop[0], 1, false);
+ queue_seek(mpctx, MPSEEK_ABSOLUTE, opts->ab_loop[0],
+ MPSEEK_EXACT, false);
}
}
ctx->prev_pts = now;
@@ -4873,6 +4860,18 @@ static void command_event(struct MPContext *mpctx, int event, void *arg)
}
}
+void handle_command_updates(struct MPContext *mpctx)
+{
+ struct command_ctx *ctx = mpctx->command_ctx;
+
+ // This is a bit messy: ao_hotplug wakes up the player, and then we have
+ // to recheck the state. Then the client(s) will read the property.
+ if (ctx->hotplug && ao_hotplug_check_update(ctx->hotplug)) {
+ mp_notify_property(mpctx, "audio-device-list");
+ mp_notify_property(mpctx, "audio-out-detected-device");
+ }
+}
+
void mp_notify(struct MPContext *mpctx, int event, void *arg)
{
command_event(mpctx, event, arg);
diff --git a/player/command.h b/player/command.h
index 3038794..447e01c 100644
--- a/player/command.h
+++ b/player/command.h
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_COMMAND_H
@@ -22,11 +21,12 @@
struct MPContext;
struct mp_cmd;
struct mp_log;
+struct mpv_node;
void command_init(struct MPContext *mpctx);
void command_uninit(struct MPContext *mpctx);
-int run_command(struct MPContext *mpctx, struct mp_cmd *cmd);
+int run_command(struct MPContext *mpctx, struct mp_cmd *cmd, struct mpv_node *res);
char *mp_property_expand_string(struct MPContext *mpctx, const char *str);
char *mp_property_expand_escaped_string(struct MPContext *mpctx, const char *str);
void property_print_help(struct mp_log *log);
@@ -36,6 +36,8 @@ int mp_property_do(const char* name, int action, void* val,
void mp_notify(struct MPContext *mpctx, int event, void *arg);
void mp_notify_property(struct MPContext *mpctx, const char *property);
+void handle_command_updates(struct MPContext *mpctx);
+
int mp_get_property_id(const char *name);
uint64_t mp_get_property_event_mask(const char *name);
@@ -47,6 +49,8 @@ enum {
MP_EVENT_CACHE_UPDATE,
MP_EVENT_WIN_RESIZE,
MP_EVENT_WIN_STATE,
+ MP_EVENT_AUDIO_DEVICES,
+ MP_EVENT_DETECTED_AUDIO_DEVICE,
};
bool mp_hook_test_completion(struct MPContext *mpctx, char *type);
diff --git a/player/configfiles.c b/player/configfiles.c
index bc89439..b7d3795 100644
--- a/player/configfiles.c
+++ b/player/configfiles.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stddef.h>
@@ -73,16 +72,14 @@ void mp_parse_cfgfiles(struct MPContext *mpctx)
// So we "divert" normal options into a separate section, and the diverted
// section is never used - unless maybe it's explicitly referenced from an
// encoding profile.
- if (encoding)
+ if (encoding) {
section = "playback-default";
- // The #if is a stupid hack to avoid errors if libavfilter is not available.
-#if HAVE_LIBAVFILTER && HAVE_ENCODING
- char *cf = mp_find_config_file(NULL, mpctx->global, "encoding-profiles.conf");
- if (cf)
- m_config_parse_config_file(mpctx->mconfig, cf, SECT_ENCODE, 0);
- talloc_free(cf);
-#endif
+ char *cf = mp_find_config_file(NULL, mpctx->global, "encoding-profiles.conf");
+ if (cf)
+ m_config_parse_config_file(mpctx->mconfig, cf, SECT_ENCODE, 0);
+ talloc_free(cf);
+ }
load_all_cfgfiles(mpctx, section, "mpv.conf|config");
@@ -243,7 +240,8 @@ static const char *const backup_properties[] = {
"options/sub-pos",
"options/sub-visibility",
"options/sub-scale",
- "options/ass-use-margins",
+ "options/sub-use-margins",
+ "options/ass-force-margins",
"options/ass-vsfilter-aspect-compat",
"options/ass-style-override",
"options/ab-loop-a",
@@ -288,6 +286,12 @@ void mp_write_watch_later_conf(struct MPContext *mpctx)
if (!filename)
goto exit;
+ struct demuxer *demux = mpctx->demuxer;
+ if (demux && (!demux->seekable || demux->partially_seekable)) {
+ MP_INFO(mpctx, "Not seekable - not saving state.\n");
+ goto exit;
+ }
+
double pos = get_current_time(mpctx);
if (pos == MP_NOPTS_VALUE)
goto exit;
diff --git a/player/core.h b/player/core.h
index 74136f2..714fd5d 100644
--- a/player/core.h
+++ b/player/core.h
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_MP_CORE_H
@@ -67,6 +66,14 @@ enum seek_type {
MPSEEK_FACTOR,
};
+enum seek_precision {
+ MPSEEK_DEFAULT = 0,
+ // The following values are numerically sorted by increasing precision
+ MPSEEK_KEYFRAME,
+ MPSEEK_EXACT,
+ MPSEEK_VERY_EXACT,
+};
+
struct track {
enum stream_type type;
@@ -293,8 +300,8 @@ typedef struct MPContext {
// Used to communicate the parameters of a seek between parts
struct seek_params {
enum seek_type type;
+ enum seek_precision exact;
double amount;
- int exact; // -1 = disable, 0 = default, 1 = enable
bool immediate; // disable seek delay logic
} seek;
@@ -366,6 +373,7 @@ void mp_nav_destroy(struct MPContext *mpctx);
void mp_nav_user_input(struct MPContext *mpctx, char *command);
void mp_handle_nav(struct MPContext *mpctx);
int mp_nav_in_menu(struct MPContext *mpctx);
+bool mp_nav_mouse_on_button(struct MPContext *mpctx);
// loadfile.c
void uninit_player(struct MPContext *mpctx, unsigned int mask);
@@ -390,10 +398,13 @@ void mp_play_files(struct MPContext *mpctx);
void update_demuxer_properties(struct MPContext *mpctx);
void reselect_demux_streams(struct MPContext *mpctx);
void prepare_playlist(struct MPContext *mpctx, struct playlist *pl);
+void autoload_external_files(struct MPContext *mpctx);
+struct track *select_track(struct MPContext *mpctx, enum stream_type type,
+ int tid, int ffid, char **langs);
// main.c
int mpv_main(int argc, char *argv[]);
-int mp_initialize(struct MPContext *mpctx);
+int mp_initialize(struct MPContext *mpctx, char **argv);
struct MPContext *mp_create(void);
void mp_destroy(struct MPContext *mpctx);
void mp_print_version(struct mp_log *log, int always);
@@ -434,7 +445,7 @@ void pause_player(struct MPContext *mpctx);
void unpause_player(struct MPContext *mpctx);
void add_step_frame(struct MPContext *mpctx, int dir);
void queue_seek(struct MPContext *mpctx, enum seek_type type, double amount,
- int exact, bool immediate);
+ enum seek_precision exact, bool immediate);
bool mp_seek_chapter(struct MPContext *mpctx, int chapter);
double get_time_length(struct MPContext *mpctx);
double get_current_time(struct MPContext *mpctx);
@@ -480,7 +491,6 @@ int reinit_video_chain(struct MPContext *mpctx);
int reinit_video_filters(struct MPContext *mpctx);
void write_video(struct MPContext *mpctx, double endpts);
void mp_force_video_refresh(struct MPContext *mpctx);
-void update_fps(struct MPContext *mpctx);
void uninit_video_out(struct MPContext *mpctx);
void uninit_video_chain(struct MPContext *mpctx);
diff --git a/player/discnav.c b/player/discnav.c
index d66b569..719e88a 100644
--- a/player/discnav.c
+++ b/player/discnav.c
@@ -42,6 +42,7 @@ struct mp_nav_state {
bool nav_eof;
bool nav_menu;
bool nav_draining;
+ bool nav_mouse_on_button;
// Accessed by OSD (possibly separate thread)
// Protected by the given lock
@@ -91,6 +92,25 @@ int mp_nav_in_menu(struct MPContext *mpctx)
return mpctx->nav_state ? mpctx->nav_state->nav_menu : -1;
}
+static void update_mouse_on_button(struct MPContext *mpctx)
+{
+ mp_notify_property(mpctx, "disc-mouse-on-button");
+}
+
+static void set_mouse_on_button(struct MPContext *mpctx, bool in)
+{
+ struct mp_nav_state *nav = mpctx->nav_state;
+ if (nav->nav_mouse_on_button != in) {
+ nav->nav_mouse_on_button = in;
+ update_mouse_on_button(mpctx);
+ }
+}
+
+bool mp_nav_mouse_on_button(struct MPContext *mpctx)
+{
+ return mpctx->nav_state ? mpctx->nav_state->nav_mouse_on_button : false;
+}
+
// If a demuxer is accessing the stream, we have to use demux_stream_control()
// to avoid synchronization issues; otherwise access it directly.
static int run_stream_control(struct MPContext *mpctx, int cmd, void *arg)
@@ -129,6 +149,7 @@ void mp_nav_init(struct MPContext *mpctx)
MP_INPUT_ALLOW_VO_DRAGGING | MP_INPUT_ALLOW_HIDE_CURSOR);
update_state(mpctx);
+ update_mouse_on_button(mpctx);
}
void mp_nav_reset(struct MPContext *mpctx)
@@ -159,6 +180,7 @@ void mp_nav_destroy(struct MPContext *mpctx)
talloc_free(mpctx->nav_state);
mpctx->nav_state = NULL;
update_state(mpctx);
+ update_mouse_on_button(mpctx);
}
void mp_nav_user_input(struct MPContext *mpctx, char *command)
@@ -182,6 +204,7 @@ void mp_nav_user_input(struct MPContext *mpctx, char *command)
inp.u.mouse_pos.x = x;
inp.u.mouse_pos.y = y;
run_stream_control(mpctx, STREAM_CTRL_NAV_CMD, &inp);
+ set_mouse_on_button(mpctx, inp.mouse_on_button);
} else {
struct mp_nav_cmd inp = {MP_NAV_CMD_MENU};
inp.u.menu.action = command;
diff --git a/player/loadfile.c b/player/loadfile.c
index 76a28c2..33f27d6 100644
--- a/player/loadfile.c
+++ b/player/loadfile.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stddef.h>
@@ -268,8 +267,8 @@ static void enable_demux_thread(struct MPContext *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->stream->type != STREAM_SUB)
+ 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);
@@ -449,9 +448,8 @@ static bool compare_track(struct track *t1, struct track *t2, char **langs,
}
return t1->user_tid <= t2->user_tid;
}
-static struct track *select_track(struct MPContext *mpctx,
- enum stream_type type, int tid, int ffid,
- char **langs)
+struct track *select_track(struct MPContext *mpctx, enum stream_type type,
+ int tid, int ffid, char **langs)
{
if (ffid != -1)
tid = -1; // prefer selecting ffid
@@ -556,6 +554,9 @@ void mp_switch_track_n(struct MPContext *mpctx, int order, enum stream_type type
if (current)
current->selected = false;
+ if (track && track->demuxer == mpctx->demuxer)
+ demux_set_enable_refresh_seeks(mpctx->demuxer, true);
+
reselect_demux_streams(mpctx);
mpctx->current_track[order][type] = track;
@@ -565,6 +566,8 @@ void mp_switch_track_n(struct MPContext *mpctx, int order, enum stream_type type
reselect_demux_streams(mpctx);
+ demux_set_enable_refresh_seeks(mpctx->demuxer, false);
+
if (type == STREAM_VIDEO && order == 0) {
reinit_video_chain(mpctx);
} else if (type == STREAM_AUDIO && order == 0) {
@@ -650,11 +653,8 @@ bool mp_remove_track(struct MPContext *mpctx, struct track *track)
for (int n = mpctx->num_tracks - 1; n >= 0 && !in_use; n--)
in_use |= mpctx->tracks[n]->demuxer == d;
- if (!in_use) {
- struct stream *s = d->stream;
- free_demuxer(d);
- free_stream(s);
- }
+ if (!in_use)
+ free_demuxer_and_stream(d);
mp_notify(mpctx, MPV_EVENT_TRACKS_CHANGED, NULL);
@@ -672,30 +672,23 @@ struct track *mp_add_external_file(struct MPContext *mpctx, char *filename,
if (strncmp(disp_filename, "memory://", 9) == 0)
disp_filename = "memory://"; // avoid noise
- struct stream *stream = stream_open(filename, mpctx->global);
- if (!stream)
- goto err_out;
+ struct demuxer_params params = {
+ .expect_subtitle = filter == STREAM_SUB,
+ };
- char *demuxer_name = NULL;
switch (filter) {
case STREAM_SUB:
- demuxer_name = opts->sub_demuxer_name;
+ params.force_format = opts->sub_demuxer_name;
break;
case STREAM_AUDIO:
- demuxer_name = opts->audio_demuxer_name;
- stream_enable_cache(&stream, &opts->stream_cache);
+ params.force_format = opts->audio_demuxer_name;
break;
}
- struct demuxer_params params = {
- .expect_subtitle = filter == STREAM_SUB,
- };
struct demuxer *demuxer =
- demux_open(stream, demuxer_name, &params, mpctx->global);
- if (!demuxer) {
- free_stream(stream);
+ demux_open_url(filename, &params, mpctx->playback_abort, mpctx->global);
+ if (!demuxer)
goto err_out;
- }
struct track *first = NULL;
for (int n = 0; n < demuxer->num_streams; n++) {
@@ -709,19 +702,18 @@ struct track *mp_add_external_file(struct MPContext *mpctx, char *filename,
}
}
if (!first) {
- free_demuxer(demuxer);
- free_stream(stream);
- MP_WARN(mpctx, "No streams added from file %s.\n",
- disp_filename);
+ free_demuxer_and_stream(demuxer);
+ MP_WARN(mpctx, "No streams added from file %s.\n", disp_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:
- MP_ERR(mpctx, "Can not open external file %s.\n",
- disp_filename);
+ MP_ERR(mpctx, "Can not open external file %s.\n", disp_filename);
return false;
}
@@ -739,7 +731,7 @@ static void open_subtitles_from_options(struct MPContext *mpctx)
mp_add_external_file(mpctx, opts->sub_name[i], STREAM_SUB);
}
-static void autoload_external_files(struct MPContext *mpctx)
+void autoload_external_files(struct MPContext *mpctx)
{
if (mpctx->opts->sub_auto < 0 && mpctx->opts->audiofile_auto < 0)
return;
@@ -877,7 +869,7 @@ static void load_chapters(struct MPContext *mpctx)
struct stream *stream = stream_create(chapter_file, STREAM_READ,
mpctx->playback_abort, mpctx->global);
if (stream) {
- struct demuxer *demux = demux_open(stream, NULL, NULL, mpctx->global);
+ struct demuxer *demux = demux_open(stream, NULL, mpctx->global);
if (demux) {
src = demux;
free_src = true;
@@ -955,7 +947,8 @@ static void open_demux_thread(void *pctx)
struct demux_open_args *args = pctx;
struct stream *s = args->stream;
struct mpv_global *global = args->global;
- args->demux = demux_open(s, global->opts->demuxer_name, NULL, global);
+ struct demuxer_params p = {.force_format = global->opts->demuxer_name};
+ args->demux = demux_open(s, &p, global);
if (args->demux)
args->tl = timeline_load(global, args->log, args->demux);
}
@@ -1425,7 +1418,7 @@ void mp_play_files(struct MPContext *mpctx)
// e must be on the mpctx->playlist.
void mp_set_playlist_entry(struct MPContext *mpctx, struct playlist_entry *e)
{
- assert(playlist_entry_to_index(mpctx->playlist, e) >= 0);
+ assert(!e || playlist_entry_to_index(mpctx->playlist, e) >= 0);
mpctx->playlist->current = e;
mpctx->playlist->current_was_replaced = false;
mpctx->stop_play = PT_CURRENT_ENTRY;
diff --git a/player/lua.c b/player/lua.c
index f329c47..4ef772f 100644
--- a/player/lua.c
+++ b/player/lua.c
@@ -377,6 +377,11 @@ static int load_lua(struct mpv_handle *client, const char *fname)
.filename = fname,
};
+ if (LUA_VERSION_NUM != 501 && LUA_VERSION_NUM != 502) {
+ MP_FATAL(ctx, "Only Lua 5.1 and 5.2 are supported.\n");
+ goto error_out;
+ }
+
lua_State *L = ctx->state = luaL_newstate();
if (!L)
goto error_out;
@@ -855,6 +860,9 @@ static void pushnode(lua_State *L, mpv_node *node)
lua_rawset(L, -3);
}
break;
+ case MPV_FORMAT_BYTE_ARRAY:
+ lua_pushlstring(L, node->u.ba->data, node->u.ba->size);
+ break;
default:
// unknown value - what do we do?
// for now, set a unique dummy value
@@ -1088,19 +1096,6 @@ static int script_get_wakeup_pipe(lua_State *L)
return 1;
}
-static int script_getcwd(lua_State *L)
-{
- char *cwd = mp_getcwd(NULL);
- if (!cwd) {
- lua_pushnil(L);
- lua_pushstring(L, "error");
- return 2;
- }
- lua_pushstring(L, cwd);
- talloc_free(cwd);
- return 1;
-}
-
static int script_readdir(lua_State *L)
{
// 0 1 2 3
@@ -1159,7 +1154,6 @@ static int script_join_path(lua_State *L)
return 1;
}
-#if HAVE_POSIX_SPAWN || defined(__MINGW32__)
struct subprocess_cb_ctx {
struct mp_log *log;
void* talloc_ctx;
@@ -1236,7 +1230,6 @@ static int script_subprocess(lua_State *L)
lua_setfield(L, -2, "stdout"); // res
return 1;
}
-#endif
static int script_parse_json(lua_State *L)
{
@@ -1262,6 +1255,23 @@ static int script_parse_json(lua_State *L)
return 3;
}
+static int script_format_json(lua_State *L)
+{
+ void *tmp = mp_lua_PITA(L);
+ struct mpv_node node;
+ makenode(tmp, &node, L, 1);
+ char *dst = talloc_strdup(tmp, "");
+ if (json_write(&dst, &node) >= 0) {
+ lua_pushstring(L, dst);
+ lua_pushnil(L);
+ } else {
+ lua_pushnil(L);
+ lua_pushstring(L, "error");
+ }
+ talloc_free_children(tmp);
+ return 2;
+}
+
#define FN_ENTRY(name) {#name, script_ ## name}
struct fn_entry {
const char *name;
@@ -1305,14 +1315,12 @@ static const struct fn_entry main_fns[] = {
};
static const struct fn_entry utils_fns[] = {
- FN_ENTRY(getcwd),
FN_ENTRY(readdir),
FN_ENTRY(split_path),
FN_ENTRY(join_path),
-#if HAVE_POSIX_SPAWN || defined(__MINGW32__)
FN_ENTRY(subprocess),
-#endif
FN_ENTRY(parse_json),
+ FN_ENTRY(format_json),
{0}
};
diff --git a/player/lua/defaults.lua b/player/lua/defaults.lua
index 3a55e3a..265463a 100644
--- a/player/lua/defaults.lua
+++ b/player/lua/defaults.lua
@@ -542,4 +542,8 @@ function mp_utils.to_string(v, set)
end
end
+function mp_utils.getcwd()
+ return mp.get_property("working-directory")
+end
+
return {}
diff --git a/player/lua/osc.lua b/player/lua/osc.lua
index 26e2f89..7f602b8 100644
--- a/player/lua/osc.lua
+++ b/player/lua/osc.lua
@@ -266,11 +266,13 @@ end
-- get the currently selected track of <type>, OSC-style counted
function get_track(type)
local track = mp.get_property(type)
- if (track == "no" or track == nil) then
- return 0
- else
- return tracks_mpv[type][tonumber(track)].osc_id
+ if track ~= "no" and track ~= nil then
+ local tr = tracks_mpv[type][tonumber(track)]
+ if tr then
+ return tr.osc_id
+ end
end
+ return 0
end
@@ -446,7 +448,7 @@ function render_elements(master_ass)
style_ass:append(string.format("{\\1a&H%X&\\2a&H%X&\\3a&H%X&\\4a&H%X&}",
ar[1], ar[2], ar[3], ar[4]))
- if (state.active_element == n) then
+ if element.eventresponder and (state.active_element == n) then
-- run render event functions
if not (element.eventresponder.render == nil) then
diff --git a/player/lua/ytdl_hook.lua b/player/lua/ytdl_hook.lua
index f757e5a..508c43f 100644
--- a/player/lua/ytdl_hook.lua
+++ b/player/lua/ytdl_hook.lua
@@ -37,6 +37,20 @@ local function set_http_headers(http_headers)
end
end
+local function append_rtmp_prop(props, name, value)
+ if not name or not value then
+ return props
+ end
+
+ if props and props ~= "" then
+ props = props..","
+ else
+ props = ""
+ end
+
+ return props..name.."=\""..value.."\""
+end
+
mp.add_hook("on_load", 10, function ()
local url = mp.get_property("stream-open-filename")
@@ -80,6 +94,7 @@ mp.add_hook("on_load", 10, function ()
end
local format = mp.get_property("options/ytdl-format")
+ local raw_options = mp.get_property_native("options/ytdl-raw-options")
-- subformat workaround
local subformat = "ass/srt/best"
@@ -92,6 +107,19 @@ mp.add_hook("on_load", 10, function ()
table.insert(command, "--format")
table.insert(command, format)
end
+
+ -- Checks if no-video option is set and disables video in ytdl if set
+ if (mp.get_property("options/vid") == "no") then
+ table.insert(command, "-x")
+ msg.verbose("Video disabled. Only using audio")
+ end
+
+ for param, arg in pairs(raw_options) do
+ table.insert(command, "--" .. param)
+ if (arg ~= "") then
+ table.insert(command, arg)
+ end
+ end
table.insert(command, "--")
table.insert(command, url)
local es, json = exec(command)
@@ -227,9 +255,13 @@ mp.add_hook("on_load", 10, function ()
-- for rtmp
if not (json.play_path == nil) then
- mp.set_property("file-local-options/stream-lavf-o",
- "rtmp_tcurl=\""..streamurl..
- "\",rtmp_playpath=\""..json.play_path.."\"")
+ local rtmp_prop = append_rtmp_prop(nil, "rtmp_tcurl", streamurl)
+ rtmp_prop = append_rtmp_prop(rtmp_prop, "rtmp_pageurl", json.page_url)
+ rtmp_prop = append_rtmp_prop(rtmp_prop, "rtmp_playpath", json.play_path)
+ rtmp_prop = append_rtmp_prop(rtmp_prop, "rtmp_swfverify", json.player_url)
+ rtmp_prop = append_rtmp_prop(rtmp_prop, "rtmp_app", json.app)
+
+ mp.set_property("file-local-options/stream-lavf-o", rtmp_prop)
end
end
end
diff --git a/player/main_fn.c b/player/main-fn-unix.c
index 23a047b..23a047b 100644
--- a/player/main_fn.c
+++ b/player/main-fn-unix.c
diff --git a/player/main-fn-win.c b/player/main-fn-win.c
new file mode 100644
index 0000000..28fc5b3
--- /dev/null
+++ b/player/main-fn-win.c
@@ -0,0 +1,55 @@
+#include <windows.h>
+
+#include "config.h"
+
+#include "osdep/io.h"
+#include "osdep/terminal.h"
+
+#include "core.h"
+
+int wmain(int argc, wchar_t *argv[]);
+
+// mpv does its own wildcard expansion in the option parser
+int _dowildcard = 0;
+
+static bool is_valid_handle(HANDLE h)
+{
+ return h != INVALID_HANDLE_VALUE && h != NULL &&
+ GetFileType(h) != FILE_TYPE_UNKNOWN;
+}
+
+static bool has_redirected_stdio(void)
+{
+ return is_valid_handle(GetStdHandle(STD_INPUT_HANDLE)) ||
+ is_valid_handle(GetStdHandle(STD_OUTPUT_HANDLE)) ||
+ is_valid_handle(GetStdHandle(STD_ERROR_HANDLE));
+}
+
+int wmain(int argc, wchar_t *argv[])
+{
+ // If started from the console wrapper (see osdep/win32-console-wrapper.c),
+ // attach to the console and set up the standard IO handles
+ bool has_console = terminal_try_attach();
+
+ // If mpv is started from Explorer, the Run dialog or the Start Menu, it
+ // will have no console and no standard IO handles. In this case, the user
+ // is expecting mpv to show some UI, so enable the pseudo-GUI profile.
+ bool gui = !has_console && !has_redirected_stdio();
+
+ int argv_len = 0;
+ char **argv_u8 = NULL;
+
+ // Build mpv's UTF-8 argv, and add the pseudo-GUI profile if necessary
+ if (argv[0])
+ MP_TARRAY_APPEND(NULL, argv_u8, argv_len, mp_to_utf8(argv_u8, argv[0]));
+ if (gui)
+ MP_TARRAY_APPEND(NULL, argv_u8, argv_len, "--profile=pseudo-gui");
+ for (int i = 1; i < argc; i++)
+ MP_TARRAY_APPEND(NULL, argv_u8, argv_len, mp_to_utf8(argv_u8, argv[i]));
+ MP_TARRAY_APPEND(NULL, argv_u8, argv_len, NULL);
+
+ int ret = mpv_main(argv_len - 1, argv_u8);
+
+ talloc_free(argv_u8);
+ return ret;
+}
diff --git a/player/main.c b/player/main.c
index dbd969e..1d56f0c 100644
--- a/player/main.c
+++ b/player/main.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
@@ -99,6 +98,12 @@ const char mp_help_text[] =
" --list-options list all mpv options\n"
"\n";
+static const char def_config[] =
+ "[pseudo-gui]\n"
+ "terminal=no\n"
+ "force-window=yes\n"
+ "idle=once\n";
+
static pthread_mutex_t terminal_owner_lock = PTHREAD_MUTEX_INITIALIZER;
static struct MPContext *terminal_owner;
@@ -112,6 +117,13 @@ static bool cas_terminal_owner(struct MPContext *old, struct MPContext *new)
return r;
}
+static void update_logging(struct MPContext *mpctx)
+{
+ mp_msg_update_msglevels(mpctx->global);
+ if (mpctx->opts->use_terminal && cas_terminal_owner(NULL, mpctx))
+ terminal_init();
+}
+
void mp_print_version(struct mp_log *log, int always)
{
int v = always ? MSGL_INFO : MSGL_V;
@@ -123,7 +135,7 @@ void mp_print_version(struct mp_log *log, int always)
// Only in verbose mode.
if (!always) {
mp_msg(log, MSGL_V, "Configuration: " CONFIGURATION "\n");
- mp_msg(log, MSGL_V, "config.h:\n%s\n", FULLCONFIG);
+ mp_msg(log, MSGL_V, "List of enabled features: %s\n", FULLCONFIG);
}
}
@@ -262,19 +274,15 @@ static bool handle_help_options(struct MPContext *mpctx)
return opt_exit;
}
-static void osdep_preinit(int *p_argc, char ***p_argv)
+static void osdep_preinit(int argc, char **argv)
{
char *enable_talloc = getenv("MPV_LEAK_REPORT");
- if (*p_argc > 1 && (strcmp((*p_argv)[1], "-leak-report") == 0 ||
- strcmp((*p_argv)[1], "--leak-report") == 0))
+ if (argc > 1 && (strcmp(argv[1], "-leak-report") == 0 ||
+ strcmp(argv[1], "--leak-report") == 0))
enable_talloc = "1";
if (enable_talloc && strcmp(enable_talloc, "1") == 0)
talloc_enable_leak_report();
-#ifdef __MINGW32__
- mp_get_converted_argv(p_argc, p_argv);
-#endif
-
#ifdef _WIN32
// stop Windows from showing all kinds of annoying error dialogs
SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
@@ -339,6 +347,7 @@ struct MPContext *mp_create(void)
mpctx->mconfig->includefunc_ctx = mpctx;
mpctx->mconfig->use_profiles = true;
mpctx->mconfig->is_toplevel = true;
+ m_config_parse(mpctx->mconfig, "", bstr0(def_config), NULL, 0);
mpctx->global->opts = mpctx->opts;
@@ -361,31 +370,58 @@ void wakeup_playloop(void *ctx)
// Finish mpctx initialization. This must be done after setting up all options.
// Some of the initializations depend on the options, and can't be changed or
// undone later.
-// cplayer: true if called by the command line player, false for client API
+// If options is not NULL, apply them as command line player arguments.
// Returns: <0 on error, 0 on success.
-int mp_initialize(struct MPContext *mpctx)
+int mp_initialize(struct MPContext *mpctx, char **options)
{
struct MPOpts *opts = mpctx->opts;
assert(!mpctx->initialized);
+ if (options) {
+ // Preparse the command line, so we can init the terminal early.
+ m_config_preparse_command_line(mpctx->mconfig, mpctx->global, options);
+
+ update_logging(mpctx);
+
+ MP_VERBOSE(mpctx, "Command line options:");
+ for (int i = 0; options[i]; i++)
+ MP_VERBOSE(mpctx, " '%s'", options[i]);
+ MP_VERBOSE(mpctx, "\n");
+ }
+
+ update_logging(mpctx);
+ mp_print_version(mpctx->log, false);
+
+ mp_parse_cfgfiles(mpctx);
+ update_logging(mpctx);
+
+ if (options) {
+ int r = m_config_parse_mp_command_line(mpctx->mconfig, mpctx->playlist,
+ mpctx->global, options);
+ if (r < 0)
+ return r <= M_OPT_EXIT ? -2 : -1;
+ update_logging(mpctx);
+ }
+
+ if (handle_help_options(mpctx))
+ return -2;
+
if (opts->dump_stats && opts->dump_stats[0]) {
if (mp_msg_open_stats_file(mpctx->global, opts->dump_stats) < 0)
MP_ERR(mpctx, "Failed to open stats file '%s'\n", opts->dump_stats);
}
MP_STATS(mpctx, "start init");
- if (mpctx->opts->use_terminal && cas_terminal_owner(NULL, mpctx))
- terminal_init();
-
- mp_msg_update_msglevels(mpctx->global);
-
if (opts->slave_mode) {
MP_WARN(mpctx, "--slave-broken is deprecated (see manpage).\n");
opts->consolecontrols = 0;
m_config_set_option0(mpctx->mconfig, "input-file", "/dev/stdin");
}
+ if (!mpctx->playlist->first && !opts->player_idle_mode)
+ return -3;
+
mp_input_load(mpctx->input);
mp_input_set_cancel(mpctx->input, mpctx->playback_abort);
@@ -409,8 +445,10 @@ int mp_initialize(struct MPContext *mpctx)
m_config_set_option0(mpctx->mconfig, "osc", "no");
m_config_set_option0(mpctx->mconfig, "framedrop", "no");
// never use auto
- if (!opts->audio_output_channels.num)
- m_config_set_option0(mpctx->mconfig, "audio-channels", "stereo");
+ if (!opts->audio_output_channels.num) {
+ m_config_set_option_ext(mpctx->mconfig, bstr0("audio-channels"),
+ bstr0("stereo"), M_SETOPT_PRESERVE_CMDLINE);
+ }
mp_input_enable_section(mpctx->input, "encode", MP_INPUT_EXCLUSIVE);
}
#endif
@@ -457,6 +495,11 @@ int mp_initialize(struct MPContext *mpctx)
mpctx->ipc_ctx = mp_init_ipc(mpctx->clients, mpctx->global);
#endif
+#ifdef _WIN32
+ if (opts->w32_priority > 0)
+ SetPriorityClass(GetCurrentProcess(), opts->w32_priority);
+#endif
+
prepare_playlist(mpctx, mpctx->playlist);
MP_STATS(mpctx, "end init");
@@ -466,7 +509,7 @@ int mp_initialize(struct MPContext *mpctx)
int mpv_main(int argc, char *argv[])
{
- osdep_preinit(&argc, &argv);
+ osdep_preinit(argc, argv);
struct MPContext *mpctx = mp_create();
struct MPOpts *opts = mpctx->opts;
@@ -475,50 +518,16 @@ int mpv_main(int argc, char *argv[])
if (verbose_env)
opts->verbose = atoi(verbose_env);
- // Preparse the command line
- m_config_preparse_command_line(mpctx->mconfig, mpctx->global, argc, argv);
-
- if (mpctx->opts->use_terminal && cas_terminal_owner(NULL, mpctx))
- terminal_init();
-
- mp_msg_update_msglevels(mpctx->global);
-
- MP_VERBOSE(mpctx, "Command line:");
- for (int i = 0; i < argc; i++)
- MP_VERBOSE(mpctx, " '%s'", argv[i]);
- MP_VERBOSE(mpctx, "\n");
-
- mp_print_version(mpctx->log, false);
-
- mp_parse_cfgfiles(mpctx);
-
- int r = m_config_parse_mp_command_line(mpctx->mconfig, mpctx->playlist,
- mpctx->global, argc, argv);
- if (r < 0) {
- if (r <= M_OPT_EXIT) {
- return prepare_exit_cplayer(mpctx, EXIT_NONE);
- } else {
- return prepare_exit_cplayer(mpctx, EXIT_ERROR);
- }
- }
-
- mp_msg_update_msglevels(mpctx->global);
-
- if (handle_help_options(mpctx))
+ char **options = argv && argv[0] ? argv + 1 : NULL; // skips program name
+ int r = mp_initialize(mpctx, options);
+ if (r == -2) // help
return prepare_exit_cplayer(mpctx, EXIT_NONE);
-
- if (!mpctx->playlist->first && !opts->player_idle_mode) {
+ if (r == -3) { // nothing to play
mp_print_version(mpctx->log, true);
MP_INFO(mpctx, "%s", mp_help_text);
return prepare_exit_cplayer(mpctx, EXIT_NONE);
}
-
-#ifdef _WIN32
- if (opts->w32_priority > 0)
- SetPriorityClass(GetCurrentProcess(), opts->w32_priority);
-#endif
-
- if (mp_initialize(mpctx) < 0)
+ if (r < 0) // another error
return prepare_exit_cplayer(mpctx, EXIT_ERROR);
mp_play_files(mpctx);
diff --git a/player/misc.c b/player/misc.c
index f6696fa..f3d9214 100644
--- a/player/misc.c
+++ b/player/misc.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stddef.h>
diff --git a/player/osd.c b/player/osd.c
index 456822d..1f482e2 100644
--- a/player/osd.c
+++ b/player/osd.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stddef.h>
diff --git a/player/playloop.c b/player/playloop.c
index defc6ba..c34ae7e 100644
--- a/player/playloop.c
+++ b/player/playloop.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stddef.h>
@@ -68,7 +67,7 @@ void mp_process_input(struct MPContext *mpctx)
mp_cmd_t *cmd = mp_input_read_cmd(mpctx->input);
if (!cmd)
break;
- run_command(mpctx, cmd);
+ run_command(mpctx, cmd, NULL);
mp_cmd_free(cmd);
mp_dispatch_queue_process(mpctx->dispatch, 0);
}
@@ -183,7 +182,7 @@ static int mp_seek(MPContext *mpctx, struct seek_params seek,
mpctx->stop_play = KEEP_PLAYING;
double hr_seek_offset = opts->hr_seek_demuxer_offset;
- bool hr_seek_very_exact = seek.exact > 1;
+ 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.
@@ -191,9 +190,9 @@ static int mp_seek(MPContext *mpctx, struct seek_params seek,
if (hr_seek_very_exact)
hr_seek_offset = MPMAX(hr_seek_offset, 0.5); // arbitrary
- bool hr_seek = opts->correct_pts && seek.exact >= 0;
+ 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 > 0;
+ opts->hr_seek > 0 || seek.exact >= MPSEEK_EXACT;
if (seek.type == MPSEEK_FACTOR || seek.amount < 0 ||
(seek.type == MPSEEK_ABSOLUTE && seek.amount < mpctx->last_chapter_pts))
mpctx->last_chapter_seek = -2;
@@ -241,8 +240,6 @@ static int mp_seek(MPContext *mpctx, struct seek_params seek,
}
if (hr_seek)
demuxer_style |= SEEK_HR;
- if (hr_seek || opts->mkv_subtitle_preroll)
- demuxer_style |= SEEK_SUBPREROLL;
if (hr_seek)
demuxer_amount -= hr_seek_offset;
@@ -303,7 +300,7 @@ static int mp_seek(MPContext *mpctx, struct seek_params seek,
// This combines consecutive seek requests.
void queue_seek(struct MPContext *mpctx, enum seek_type type, double amount,
- int exact, bool immediate)
+ enum seek_precision exact, bool immediate)
{
struct seek_params *seek = &mpctx->seek;
switch (type) {
@@ -339,7 +336,7 @@ void execute_queued_seek(struct MPContext *mpctx)
{
if (mpctx->seek.type) {
// Let explicitly imprecise seeks cancel precise seeks:
- if (mpctx->hrseek_active && mpctx->seek.exact < 0)
+ if (mpctx->hrseek_active && mpctx->seek.exact == MPSEEK_KEYFRAME)
mpctx->start_timestamp = -1e9;
/* If the user seeks continuously (keeps arrow key down)
* try to finish showing a frame from one location before doing
@@ -505,7 +502,7 @@ bool mp_seek_chapter(struct MPContext *mpctx, int chapter)
if (pts == MP_NOPTS_VALUE)
return false;
- queue_seek(mpctx, MPSEEK_ABSOLUTE, pts, 0, true);
+ queue_seek(mpctx, MPSEEK_ABSOLUTE, pts, MPSEEK_DEFAULT, true);
mpctx->last_chapter_seek = chapter;
mpctx->last_chapter_pts = pts;
return true;
@@ -717,14 +714,15 @@ static void handle_backstep(struct MPContext *mpctx)
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, 2, true);
+ 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, 2, true);
+ 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.
@@ -761,7 +759,7 @@ static void handle_sstep(struct MPContext *mpctx)
if (opts->step_sec > 0 && !mpctx->paused) {
set_osd_function(mpctx, OSD_FFW);
- queue_seek(mpctx, MPSEEK_RELATIVE, opts->step_sec, 0, true);
+ queue_seek(mpctx, MPSEEK_RELATIVE, opts->step_sec, MPSEEK_DEFAULT, true);
}
if (mpctx->video_status >= STATUS_EOF) {
@@ -778,7 +776,8 @@ static void handle_loop_file(struct MPContext *mpctx)
if (opts->loop_file && mpctx->stop_play == AT_END_OF_FILE) {
mpctx->stop_play = KEEP_PLAYING;
set_osd_function(mpctx, OSD_FFW);
- queue_seek(mpctx, MPSEEK_ABSOLUTE, get_start_time(mpctx), 0, true);
+ queue_seek(mpctx, MPSEEK_ABSOLUTE, get_start_time(mpctx),
+ MPSEEK_DEFAULT, true);
if (opts->loop_file > 0)
opts->loop_file--;
}
@@ -799,7 +798,7 @@ void seek_to_last_frame(struct MPContext *mpctx)
mp_seek(mpctx, (struct seek_params){
.type = MPSEEK_ABSOLUTE,
.amount = end,
- .exact = 2, // "very exact", no framedrop
+ .exact = MPSEEK_VERY_EXACT,
}, false);
// Make it exact: stop seek only if last frame was reached.
if (mpctx->hrseek_active) {
@@ -915,13 +914,13 @@ static void handle_playback_restart(struct MPContext *mpctx, double endpts)
mpctx->restart_complete = true;
mp_notify(mpctx, MPV_EVENT_PLAYBACK_RESTART, NULL);
if (!mpctx->playing_msg_shown) {
- if (opts->playing_msg) {
+ if (opts->playing_msg && opts->playing_msg[0]) {
char *msg =
mp_property_expand_escaped_string(mpctx, opts->playing_msg);
MP_INFO(mpctx, "%s\n", msg);
talloc_free(msg);
}
- if (opts->osd_playing_msg) {
+ if (opts->osd_playing_msg && opts->osd_playing_msg[0]) {
char *msg =
mp_property_expand_escaped_string(mpctx, opts->osd_playing_msg);
set_osd_msg(mpctx, 1, opts->osd_duration, "%s", msg);
@@ -986,6 +985,7 @@ void run_playloop(struct MPContext *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);
diff --git a/player/screenshot.c b/player/screenshot.c
index 27ef468..a47de29 100644
--- a/player/screenshot.c
+++ b/player/screenshot.c
@@ -1,19 +1,18 @@
/*
- * This file is part of mplayer2.
+ * This file is part of mpv.
*
- * mplayer2 is free software; you can redistribute it and/or modify
+ * 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.
*
- * mplayer2 is distributed in the hope that it will be useful,
+ * 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 mplayer2; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
@@ -33,7 +32,6 @@
#include "options/path.h"
#include "video/mp_image.h"
#include "video/decode/dec_video.h"
-#include "video/filter/vf.h"
#include "video/out/vo.h"
#include "video/image_writer.h"
#include "sub/osd.h"
@@ -331,11 +329,7 @@ static struct mp_image *screenshot_get(struct MPContext *mpctx, int mode)
if (mode == MODE_SUBTITLES && osd_get_render_subs_in_filter(mpctx->osd))
mode = 0;
- // vf_screenshot
- if (mpctx->d_video && mpctx->d_video->vfilter)
- vf_control_any(mpctx->d_video->vfilter, VFCTRL_SCREENSHOT, &image);
-
- if (!image && mpctx->video_out && mpctx->video_out->config_ok) {
+ if (mpctx->video_out && mpctx->video_out->config_ok) {
vo_wait_frame(mpctx->video_out); // important for each-frame mode
if (mode != MODE_FULL_WINDOW)
@@ -349,7 +343,7 @@ 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;
struct mp_image *nimage = NULL;
- if (ctx && ctx->download_image)
+ if (ctx && ctx->download_image && (image->fmt.flags & MP_IMGFLAG_HWACCEL))
nimage = ctx->download_image(ctx, image, NULL);
if (nimage) {
talloc_free(image);
@@ -363,6 +357,16 @@ static struct mp_image *screenshot_get(struct MPContext *mpctx, int mode)
return image;
}
+struct mp_image *screenshot_get_rgb(struct MPContext *mpctx, int mode)
+{
+ struct mp_image *mpi = screenshot_get(mpctx, mode);
+ if (!mpi)
+ return NULL;
+ struct mp_image *res = convert_image(mpi, IMGFMT_BGR0, mpctx->log);
+ talloc_free(mpi);
+ return res;
+}
+
void screenshot_to_file(struct MPContext *mpctx, const char *filename, int mode,
bool osd)
{
@@ -371,11 +375,6 @@ void screenshot_to_file(struct MPContext *mpctx, const char *filename, int mode,
bool old_osd = ctx->osd;
ctx->osd = osd;
- if (mp_path_exists(filename)) {
- screenshot_msg(ctx, SMSG_ERR, "Screenshot: file '%s' already exists.",
- filename);
- goto end;
- }
char *ext = mp_splitext(filename, NULL);
if (ext)
opts.format = ext;
diff --git a/player/screenshot.h b/player/screenshot.h
index 1b12ac9..9ebe9ef 100644
--- a/player/screenshot.h
+++ b/player/screenshot.h
@@ -1,19 +1,18 @@
/*
- * This file is part of mplayer2.
+ * This file is part of mpv.
*
- * mplayer2 is free software; you can redistribute it and/or modify
+ * 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.
*
- * mplayer2 is distributed in the hope that it will be useful,
+ * 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 mplayer2; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_SCREENSHOT_H
@@ -40,6 +39,9 @@ void screenshot_request(struct MPContext *mpctx, int mode, bool each_frame,
void screenshot_to_file(struct MPContext *mpctx, const char *filename, int mode,
bool osd);
+// mode is the same as in screenshot_request()
+struct mp_image *screenshot_get_rgb(struct MPContext *mpctx, int mode);
+
// Called by the playback core code when a new frame is displayed.
void screenshot_flip(struct MPContext *mpctx);
diff --git a/player/sub.c b/player/sub.c
index a37ea4b..89da9f7 100644
--- a/player/sub.c
+++ b/player/sub.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stddef.h>
@@ -321,6 +320,8 @@ static void reinit_subdec(struct MPContext *mpctx, struct track *track,
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);
}
}
diff --git a/player/video.c b/player/video.c
index e905583..d35a9de 100644
--- a/player/video.c
+++ b/player/video.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stddef.h>
@@ -77,15 +76,6 @@ static const char av_desync_help_text[] =
" with --no-video, --no-audio, or --no-sub.\n"
"If none of this helps you, file a bug report.\n\n";
-void update_fps(struct MPContext *mpctx)
-{
-#if HAVE_ENCODING
- struct dec_video *d_video = mpctx->d_video;
- if (mpctx->encode_lavc_ctx && d_video)
- encode_lavc_set_video_fps(mpctx->encode_lavc_ctx, d_video->fps);
-#endif
-}
-
static void set_allowed_vo_formats(struct vf_chain *c, struct vo *vo)
{
vo_query_formats(vo, c->allowed_output_formats);
@@ -159,7 +149,7 @@ static void filter_reconfig(struct MPContext *mpctx,
if (params.stereo_in != params.stereo_out &&
params.stereo_in > 0 && params.stereo_out >= 0)
{
- char *to = MP_STEREO3D_NAME(params.stereo_out);
+ 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)
@@ -238,6 +228,7 @@ void uninit_video_out(struct MPContext *mpctx)
if (mpctx->video_out)
vo_destroy(mpctx->video_out);
mpctx->video_out = NULL;
+ mp_notify(mpctx, MPV_EVENT_VIDEO_RECONFIG, NULL);
}
void uninit_video_chain(struct MPContext *mpctx)
@@ -296,12 +287,18 @@ int reinit_video_chain(struct MPContext *mpctx)
d_video->fps = sh->video->fps;
d_video->vo = mpctx->video_out;
+ MP_VERBOSE(d_video, "Container reported FPS: %f\n", sh->video->fps);
+
if (opts->force_fps) {
d_video->fps = opts->force_fps;
MP_INFO(mpctx, "FPS forced to %5.3f.\n", d_video->fps);
MP_INFO(mpctx, "Use --no-correct-pts to force FPS based timing.\n");
}
- update_fps(mpctx);
+
+#if HAVE_ENCODING
+ if (mpctx->encode_lavc_ctx && d_video)
+ encode_lavc_set_video_fps(mpctx->encode_lavc_ctx, d_video->fps);
+#endif
vo_control(mpctx->video_out, VOCTRL_GET_HWDEC_INFO, &d_video->hwdec_info);
@@ -348,8 +345,10 @@ void mp_force_video_refresh(struct MPContext *mpctx)
return;
// If not paused, the next frame should come soon enough.
- if (opts->pause && mpctx->last_vo_pts != MP_NOPTS_VALUE)
- queue_seek(mpctx, MPSEEK_ABSOLUTE, mpctx->last_vo_pts, 2, true);
+ if (opts->pause && mpctx->last_vo_pts != MP_NOPTS_VALUE) {
+ queue_seek(mpctx, MPSEEK_ABSOLUTE, mpctx->last_vo_pts,
+ MPSEEK_VERY_EXACT, true);
+ }
}
static int check_framedrop(struct MPContext *mpctx)
@@ -378,7 +377,7 @@ static int decode_image(struct MPContext *mpctx)
if (d_video->header->attached_picture) {
d_video->waiting_decoded_mpi =
video_decode(d_video, d_video->header->attached_picture, 0);
- return VD_EOF;
+ return d_video->waiting_decoded_mpi ? VD_EOF : VD_PROGRESS;
}
struct demux_packet *pkt;
@@ -403,7 +402,8 @@ static int decode_image(struct MPContext *mpctx)
talloc_free(pkt);
if (had_packet && !d_video->waiting_decoded_mpi &&
- mpctx->video_status == STATUS_PLAYING)
+ mpctx->video_status == STATUS_PLAYING &&
+ (mpctx->opts->frame_dropping & 2))
{
mpctx->dropped_frames_total++;
mpctx->dropped_frames++;
@@ -544,11 +544,60 @@ static void adjust_sync(struct MPContext *mpctx, double v_pts, double frame_time
mpctx->total_avsync_change += change;
}
+// Move the frame in next_frame[1] to next_frame[0]. This makes the frame
+// "known" to the playback logic. A frame in next_frame[0] is either "known" or
+// NULL, so the moving must always be done by this function.
+static void shift_new_frame(struct MPContext *mpctx)
+{
+ if (mpctx->next_frame[0] || !mpctx->next_frame[1])
+ return;
+
+ mpctx->next_frame[0] = mpctx->next_frame[1];
+ mpctx->next_frame[1] = NULL;
+
+ double frame_time = 0;
+ double pts = mpctx->next_frame[0]->pts;
+ if (mpctx->video_pts != MP_NOPTS_VALUE) {
+ frame_time = pts - mpctx->video_pts;
+ if (frame_time <= 0 || frame_time >= 60) {
+ // Assume a PTS difference >= 60 seconds is a discontinuity.
+ MP_WARN(mpctx, "Invalid video timestamp: %f -> %f\n",
+ mpctx->video_pts, pts);
+ frame_time = 0;
+ }
+ }
+ mpctx->video_next_pts = pts;
+ mpctx->delay -= frame_time;
+ if (mpctx->video_status >= STATUS_PLAYING) {
+ mpctx->time_frame += frame_time / mpctx->opts->playback_speed;
+ adjust_sync(mpctx, pts, frame_time);
+ }
+ mpctx->dropped_frames = 0;
+ MP_TRACE(mpctx, "frametime=%5.3f\n", frame_time);
+}
+
+// Whether it's fine to call add_new_frame() now.
+static bool needs_new_frame(struct MPContext *mpctx)
+{
+ return !mpctx->next_frame[1];
+}
+
+// Queue a frame to mpctx->next_frame[]. 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(frame);
+ mpctx->next_frame[1] = frame;
+ shift_new_frame(mpctx);
+}
+
// Enough video filtered already to push one frame to the VO?
-static bool have_new_frame(struct MPContext *mpctx)
+// Set eof to true if no new frames are to be expected.
+static bool have_new_frame(struct MPContext *mpctx, bool eof)
{
bool need_2nd = !!(mpctx->opts->frame_dropping & 1) // we need the duration
- && mpctx->video_pts != MP_NOPTS_VALUE; // ...except for the 1st frame
+ && mpctx->video_pts != MP_NOPTS_VALUE // ...except for the 1st frame
+ && !eof; // on EOF, drain the remaining frames
return mpctx->next_frame[0] && (!need_2nd || mpctx->next_frame[1]);
}
@@ -572,39 +621,12 @@ static int video_output_image(struct MPContext *mpctx, double endpts)
return r <= 0 ? VD_EOF : VD_PROGRESS;
}
- if (have_new_frame(mpctx))
- return VD_NEW_FRAME;
-
- if (!mpctx->next_frame[0] && mpctx->next_frame[1]) {
- mpctx->next_frame[0] = mpctx->next_frame[1];
- mpctx->next_frame[1] = NULL;
-
- double pts = mpctx->next_frame[0]->pts;
- double last_pts = mpctx->video_pts;
- if (last_pts == MP_NOPTS_VALUE)
- last_pts = pts;
- double frame_time = pts - last_pts;
- if (frame_time < 0 || frame_time >= 60) {
- // Assume a PTS difference >= 60 seconds is a discontinuity.
- MP_WARN(mpctx, "Jump in video pts: %f -> %f\n", last_pts, pts);
- frame_time = 0;
- }
- mpctx->video_next_pts = pts;
- mpctx->delay -= frame_time;
- if (mpctx->video_status >= STATUS_PLAYING) {
- mpctx->time_frame += frame_time / mpctx->opts->playback_speed;
- adjust_sync(mpctx, pts, frame_time);
- }
- mpctx->dropped_frames = 0;
- MP_TRACE(mpctx, "frametime=%5.3f\n", frame_time);
- }
-
- if (have_new_frame(mpctx))
+ if (have_new_frame(mpctx, false))
return VD_NEW_FRAME;
// Get a new frame if we need one.
int r = VD_PROGRESS;
- if (!mpctx->next_frame[1]) {
+ if (needs_new_frame(mpctx)) {
// Filter a new frame.
r = video_decode_and_filter(mpctx);
if (r < 0)
@@ -614,39 +636,30 @@ static int video_output_image(struct MPContext *mpctx, double endpts)
// Always add these; they make backstepping after seeking faster.
add_frame_pts(mpctx, img->pts);
- bool drop = false;
- if ((endpts != MP_NOPTS_VALUE && img->pts >= endpts) ||
- mpctx->max_frames == 0)
- {
- drop = true;
+ if (endpts != MP_NOPTS_VALUE && img->pts >= endpts) {
r = VD_EOF;
- }
- if (!drop && hrseek && mpctx->hrseek_lastframe) {
+ } else if (mpctx->max_frames == 0) {
+ r = VD_EOF;
+ } else if (hrseek && mpctx->hrseek_lastframe) {
mp_image_setrefp(&mpctx->saved_frame, img);
- drop = true;
- }
- if (hrseek && img->pts < mpctx->hrseek_pts - .005)
- drop = true;
- if (drop) {
- talloc_free(img);
+ } else if (hrseek && img->pts < mpctx->hrseek_pts - .005) {
+ /* just skip */
} else {
- mpctx->next_frame[1] = img;
+ add_new_frame(mpctx, img);
+ img = NULL;
}
+ talloc_free(img);
}
}
- // On EOF, always allow the playloop to use the remaining frame.
- if (have_new_frame(mpctx) || (r <= 0 && mpctx->next_frame[0]))
- return VD_NEW_FRAME;
-
// Last-frame seek
if (r <= 0 && hrseek && mpctx->hrseek_lastframe && mpctx->saved_frame) {
- mpctx->next_frame[1] = mpctx->saved_frame;
+ add_new_frame(mpctx, mpctx->saved_frame);
mpctx->saved_frame = NULL;
- return VD_PROGRESS;
+ r = VD_PROGRESS;
}
- return r;
+ return have_new_frame(mpctx, r <= 0) ? VD_NEW_FRAME : r;
}
/* Update avsync before a new video frame is displayed. Actually, this can be
@@ -711,9 +724,9 @@ static void update_avsync_after_frame(struct MPContext *mpctx)
mpctx->last_av_difference = a_pos - mpctx->video_pts + opts->audio_delay;
if (mpctx->time_frame > 0)
mpctx->last_av_difference += mpctx->time_frame * opts->playback_speed;
- if (a_pos == MP_NOPTS_VALUE || mpctx->video_pts == MP_NOPTS_VALUE)
+ if (a_pos == MP_NOPTS_VALUE || mpctx->video_pts == MP_NOPTS_VALUE) {
mpctx->last_av_difference = MP_NOPTS_VALUE;
- if (fabs(mpctx->last_av_difference) > 0.5 && !mpctx->drop_message_shown) {
+ } else if (fabs(mpctx->last_av_difference) > 0.5 && !mpctx->drop_message_shown) {
MP_WARN(mpctx, "%s", av_desync_help_text);
mpctx->drop_message_shown = true;
}
@@ -753,8 +766,6 @@ void write_video(struct MPContext *mpctx, double endpts)
if (mpctx->paused && mpctx->video_status >= STATUS_READY)
return;
- update_fps(mpctx);
-
int r = video_output_image(mpctx, endpts);
MP_TRACE(mpctx, "video_output_image: %d\n", r);
@@ -769,7 +780,7 @@ void write_video(struct MPContext *mpctx, double endpts)
vo_still_displaying(vo) ? STATUS_DRAINING : STATUS_EOF;
mpctx->delay = 0;
mpctx->last_av_difference = 0;
- MP_VERBOSE(mpctx, "video EOF (status=%d)\n", mpctx->video_status);
+ MP_DBG(mpctx, "video EOF (status=%d)\n", mpctx->video_status);
return;
}
@@ -848,6 +859,8 @@ void write_video(struct MPContext *mpctx, double endpts)
vo_queue_frame(vo, mpctx->next_frame[0], pts, duration);
mpctx->next_frame[0] = NULL;
+ shift_new_frame(mpctx);
+
mpctx->shown_vframes++;
if (mpctx->video_status < STATUS_PLAYING) {
mpctx->video_status = STATUS_READY;
diff --git a/stream/ai_alsa1x.c b/stream/ai_alsa1x.c
index c279505..c5dc84c 100644
--- a/stream/ai_alsa1x.c
+++ b/stream/ai_alsa1x.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
diff --git a/stream/ai_oss.c b/stream/ai_oss.c
index 98f65a7..0362f20 100644
--- a/stream/ai_oss.c
+++ b/stream/ai_oss.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
diff --git a/stream/audio_in.c b/stream/audio_in.c
index 3584ee4..90b036f 100644
--- a/stream/audio_in.c
+++ b/stream/audio_in.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
diff --git a/stream/audio_in.h b/stream/audio_in.h
index 0afd926..28d7d33 100644
--- a/stream/audio_in.h
+++ b/stream/audio_in.h
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_AUDIO_IN_H
diff --git a/stream/cache.c b/stream/cache.c
index edfe3ef..0ffa392 100644
--- a/stream/cache.c
+++ b/stream/cache.c
@@ -1,25 +1,24 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
// Time in seconds the main thread waits for the cache thread. On wakeups, the
// code checks for user requested aborts and also prints warnings that the
// cache is being slow.
-#define CACHE_WAIT_TIME 0.5
+#define CACHE_WAIT_TIME 1.0
// The time the cache sleeps in idle mode. This controls how often the cache
// retries reading from the stream after EOF has reached (in case the stream is
@@ -33,9 +32,6 @@
// the cache is active.
#define CACHE_UPDATE_CONTROLS_TIME 2.0
-// Time in seconds the cache prints a new message at all.
-#define CACHE_NO_SPAM 5.0
-
#include <stdio.h>
#include <stdlib.h>
@@ -82,7 +78,6 @@ struct priv {
// Owned by the main thread
stream_t *cache; // wrapper stream, used by demuxer etc.
- double last_warn_time;
// Owned by the cache thread
stream_t *stream; // "real" stream, used to read from the source media
@@ -119,8 +114,6 @@ struct priv {
};
enum {
- CACHE_INTERRUPTED = -1,
-
CACHE_CTRL_NONE = 0,
CACHE_CTRL_QUIT = -1,
CACHE_CTRL_PING = -2,
@@ -132,31 +125,19 @@ enum {
// Used by the main thread to wakeup the cache thread, and to wait for the
// cache thread. The cache mutex has to be locked when calling this function.
// *retry_time should be set to 0 on the first call.
-// Returns CACHE_INTERRUPTED if the caller is supposed to abort.
-static int cache_wakeup_and_wait(struct priv *s, double *retry_time)
+static void cache_wakeup_and_wait(struct priv *s, double *retry_time)
{
- if (mp_cancel_test(s->cache->cancel))
- return CACHE_INTERRUPTED;
-
double start = mp_time_sec();
-
- if (!s->last_warn_time || start - s->last_warn_time >= CACHE_NO_SPAM) {
- // Print a "more severe" warning after waiting 1 second and no new data
- if ((*retry_time) >= 1.0) {
- MP_ERR(s, "Cache keeps not responding.\n");
- s->last_warn_time = start;
- } else if (*retry_time > 0.1) {
- MP_WARN(s, "Cache is not responding - slow/stuck network connection?\n");
- s->last_warn_time = start;
- }
+ if (*retry_time >= CACHE_WAIT_TIME) {
+ MP_WARN(s, "Cache is not responding - slow/stuck network connection?\n");
+ *retry_time = -1; // do not warn again for this call
}
pthread_cond_signal(&s->wakeup);
mpthread_cond_timedwait_rel(&s->wakeup, &s->mutex, CACHE_WAIT_TIME);
- *retry_time += mp_time_sec() - start;
-
- return 0;
+ if (*retry_time >= 0)
+ *retry_time += mp_time_sec() - start;
}
// Runs in the cache thread
@@ -227,6 +208,9 @@ static bool cache_fill(struct priv *s)
goto done;
}
+ if (mp_cancel_test(s->cache->cancel))
+ goto done;
+
// number of buffer bytes which should be preserved in backwards direction
int64_t back = MPCLAMP(read - s->min_filepos, 0, s->back_size);
@@ -512,8 +496,9 @@ static int cache_fill_buffer(struct stream *cache, char *buffer, int max_len)
if (s->eof && s->read_filepos >= s->max_filepos && s->reads >= retry)
break;
s->idle = false;
- if (cache_wakeup_and_wait(s, &retry_time) == CACHE_INTERRUPTED)
+ if (mp_cancel_test(s->cache->cancel))
break;
+ cache_wakeup_and_wait(s, &retry_time);
}
}
@@ -571,11 +556,12 @@ static int cache_control(stream_t *cache, int cmd, void *arg)
s->control_arg = arg;
double retry = 0;
while (s->control != CACHE_CTRL_NONE) {
- if (cache_wakeup_and_wait(s, &retry) == CACHE_INTERRUPTED) {
+ if (mp_cancel_test(s->cache->cancel)) {
s->eof = 1;
r = STREAM_UNSUPPORTED;
goto done;
}
+ cache_wakeup_and_wait(s, &retry);
}
r = s->control_res;
if (s->control_flush) {
diff --git a/stream/cookies.c b/stream/cookies.c
index f8bc852..ce2d4ed 100644
--- a/stream/cookies.c
+++ b/stream/cookies.c
@@ -4,21 +4,20 @@
*
* Copyright (c) 2003 Dave Lambley <mplayer@davel.me.uk>
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
diff --git a/stream/cookies.h b/stream/cookies.h
index b2a31d0..7c4d641 100644
--- a/stream/cookies.h
+++ b/stream/cookies.h
@@ -4,21 +4,20 @@
*
* Copyright (c) 2003 Dave Lambley <mplayer@davel.me.uk>
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_COOKIES_H
diff --git a/stream/discnav.h b/stream/discnav.h
index 894418c..b40998d 100644
--- a/stream/discnav.h
+++ b/stream/discnav.h
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_STREAM_DVDNAV_H
@@ -81,6 +80,7 @@ struct mp_nav_cmd {
int x, y;
} mouse_pos;
} u;
+ bool mouse_on_button;
};
#endif /* MPLAYER_STREAM_DVDNAV_H */
diff --git a/stream/frequencies.c b/stream/frequencies.c
index 9f5be33..2e4027c 100644
--- a/stream/frequencies.c
+++ b/stream/frequencies.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
diff --git a/stream/frequencies.h b/stream/frequencies.h
index 3439412..be4a33a 100644
--- a/stream/frequencies.h
+++ b/stream/frequencies.h
@@ -5,21 +5,20 @@
*
* Frequencies are given in kHz
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_FREQUENCIES_H
diff --git a/stream/stream.c b/stream/stream.c
index 825d7a1..9456ad1 100644
--- a/stream/stream.c
+++ b/stream/stream.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
@@ -752,6 +751,8 @@ static struct mp_cache_opts check_cache_opts(stream_t *stream,
struct mp_cache_opts use_opts = *opts;
if (use_opts.size == -1)
use_opts.size = stream->streaming ? use_opts.def_size : 0;
+ if (use_opts.size == -2)
+ use_opts.size = use_opts.def_size;
if (stream->mode != STREAM_READ || !stream->allow_caching || use_opts.size < 1)
use_opts.size = 0;
diff --git a/stream/stream.h b/stream/stream.h
index b2b92cd..7b47514 100644
--- a/stream/stream.h
+++ b/stream/stream.h
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_STREAM_H
@@ -32,6 +31,7 @@
enum streamtype {
STREAMTYPE_GENERIC = 0,
STREAMTYPE_FILE,
+ STREAMTYPE_DIR,
STREAMTYPE_DVB,
STREAMTYPE_DVD,
STREAMTYPE_BLURAY,
diff --git a/stream/stream_avdevice.c b/stream/stream_avdevice.c
index cd6fd1a..d1fd74f 100644
--- a/stream/stream_avdevice.c
+++ b/stream/stream_avdevice.c
@@ -12,8 +12,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
- * with mpv; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * 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 04efed6..adc2e58 100644
--- a/stream/stream_bluray.c
+++ b/stream/stream_bluray.c
@@ -1,21 +1,20 @@
/*
* Copyright (C) 2010 Benjamin Zores <ben@geexbox.org>
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
/*
@@ -456,7 +455,9 @@ static void handle_nav_command(stream_t *s, struct mp_nav_cmd *ev)
bd_vk_key_e key = translate_nav_menu_action(action);
if (key != BD_VK_NONE) {
if (key == BD_VK_MOUSE_ACTIVATE)
- bd_mouse_select(priv->bd, pts, priv->mousex, priv->mousey);
+ ev->mouse_on_button = bd_mouse_select(priv->bd, pts,
+ priv->mousex,
+ priv->mousey);
bd_user_input(priv->bd, pts, key);
} else if (strcmp(action, "menu") == 0) {
if (priv->popup_enabled)
@@ -468,7 +469,9 @@ static void handle_nav_command(stream_t *s, struct mp_nav_cmd *ev)
} case MP_NAV_CMD_MOUSE_POS:
priv->mousex = ev->u.mouse_pos.x;
priv->mousey = ev->u.mouse_pos.y;
- bd_mouse_select(priv->bd, mp_time_us(), priv->mousex, priv->mousey);
+ ev->mouse_on_button = bd_mouse_select(priv->bd, mp_time_us(),
+ priv->mousex,
+ priv->mousey);
break;
case MP_NAV_CMD_SKIP_STILL:
bd_read_skip_still(priv->bd);
diff --git a/stream/stream_cdda.c b/stream/stream_cdda.c
index 8bb9400..6781f4a 100644
--- a/stream/stream_cdda.c
+++ b/stream/stream_cdda.c
@@ -1,21 +1,20 @@
/*
- * This file is part of MPlayer.
- *
* Original author: Albeu
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
@@ -64,6 +63,7 @@ typedef struct cdda_params {
int skip;
char *device;
int span[2];
+ int cdtext;
} cdda_priv;
#define OPT_BASE_STRUCT struct cdda_params
@@ -86,6 +86,7 @@ const struct m_sub_options stream_cdda_conf = {
OPT_FLAG("skip", skip, 0),
OPT_STRING("device", device, 0),
OPT_INTPAIR("span", span, 0),
+ OPT_FLAG("cdtext", cdtext, 0),
{0}
},
.size = sizeof(struct cdda_params),
@@ -117,9 +118,11 @@ static const char *const cdtext_name[] = {
#endif
};
-static bool print_cdtext(stream_t *s, int track)
+static void print_cdtext(stream_t *s, int track)
{
cdda_priv* p = (cdda_priv*)s->priv;
+ if (!p->cdtext)
+ return;
#ifdef OLD_API
cdtext_t *text = cdio_get_cdtext(p->cd->p_cdio, track);
#else
@@ -141,9 +144,7 @@ static bool print_cdtext(stream_t *s, int track)
MP_INFO(s, " %s: '%s'\n", name, value);
}
}
- return true;
}
- return false;
}
static void print_track_info(stream_t *s, int track)
diff --git a/stream/stream_dvb.c b/stream/stream_dvb.c
index 3144f95..0510143 100644
--- a/stream/stream_dvb.c
+++ b/stream/stream_dvb.c
@@ -73,7 +73,7 @@ const struct m_sub_options stream_dvb_conf = {
OPT_STRING("prog", cfg_prog, 0),
OPT_INTRANGE("card", cfg_card, 0, 1, 4),
OPT_INTRANGE("timeout", cfg_timeout, 0, 1, 30),
- OPT_STRING("file", cfg_file, 0),
+ OPT_STRING("file", cfg_file, M_OPT_FILE),
OPT_FLAG("full-transponder", cfg_full_transponder, 0),
{0}
},
diff --git a/stream/stream_dvd.c b/stream/stream_dvd.c
index 0906796..641305e 100644
--- a/stream/stream_dvd.c
+++ b/stream/stream_dvd.c
@@ -1,21 +1,20 @@
/*
- * This file is part of MPlayer.
- *
* Original author: Benjamin Zores
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
diff --git a/stream/stream_dvd_common.c b/stream/stream_dvd_common.c
index 99e9067..82da42e 100644
--- a/stream/stream_dvd_common.c
+++ b/stream/stream_dvd_common.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <fcntl.h>
diff --git a/stream/stream_dvd_common.h b/stream/stream_dvd_common.h
index 96dfbb3..8caa281 100644
--- a/stream/stream_dvd_common.h
+++ b/stream/stream_dvd_common.h
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_STREAM_DVD_COMMON_H
diff --git a/stream/stream_dvdnav.c b/stream/stream_dvdnav.c
index 6f381c6..095ba98 100644
--- a/stream/stream_dvdnav.c
+++ b/stream/stream_dvdnav.c
@@ -1,20 +1,20 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
+
#include <libavutil/common.h>
#include "config.h"
@@ -202,18 +202,19 @@ static void handle_menu_input(stream_t *stream, const char *cmd)
}
}
-static void handle_mouse_pos(stream_t *stream, int x, int y)
+static dvdnav_status_t handle_mouse_pos(stream_t *stream, int x, int y)
{
struct priv *priv = stream->priv;
dvdnav_t *nav = priv->dvdnav;
pci_t *pci = dvdnav_get_current_nav_pci(nav);
if (!pci)
- return;
+ return DVDNAV_STATUS_ERR;
- dvdnav_mouse_select(nav, pci, x, y);
+ dvdnav_status_t status = dvdnav_mouse_select(nav, pci, x, y);
priv->mousex = x;
priv->mousey = y;
+ return status;
}
/**
@@ -305,7 +306,7 @@ static void handle_cmd(stream_t *s, struct mp_nav_cmd *ev)
handle_menu_input(s, ev->u.menu.action);
break;
case MP_NAV_CMD_MOUSE_POS:
- handle_mouse_pos(s, ev->u.mouse_pos.x, ev->u.mouse_pos.y);
+ ev->mouse_on_button = handle_mouse_pos(s, ev->u.mouse_pos.x, ev->u.mouse_pos.y);
break;
}
diff --git a/stream/stream_file.c b/stream/stream_file.c
index 5ce346b..d0da862 100644
--- a/stream/stream_file.c
+++ b/stream/stream_file.c
@@ -1,21 +1,20 @@
/*
- * This file is part of MPlayer.
- *
* Original authors: Albeu, probably Arpi
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
@@ -156,12 +155,11 @@ char *mp_file_get_path(void *talloc_ctx, bstr url)
}
#if HAVE_BSD_FSTATFS
-static bool check_stream_network(stream_t *stream)
+static bool check_stream_network(int fd)
{
struct statfs fs;
const char *stypes[] = { "afpfs", "nfs", "smbfs", "webdav", NULL };
- struct priv *priv = stream->priv;
- if (fstatfs(priv->fd, &fs) == 0)
+ if (fstatfs(fd, &fs) == 0)
for (int i=0; stypes[i]; i++)
if (strcmp(stypes[i], fs.f_fstypename) == 0)
return true;
@@ -169,7 +167,7 @@ static bool check_stream_network(stream_t *stream)
}
#elif HAVE_LINUX_FSTATFS
-static bool check_stream_network(stream_t *stream)
+static bool check_stream_network(int fd)
{
struct statfs fs;
const uint32_t stypes[] = {
@@ -182,8 +180,7 @@ static bool check_stream_network(stream_t *stream)
0xBEEFDEAD /*SNFS*/, 0xBACBACBC /*VMHGFS*/, 0x7461636f /*OCFS2*/,
0
};
- struct priv *priv = stream->priv;
- if (fstatfs(priv->fd, &fs) == 0) {
+ if (fstatfs(fd, &fs) == 0) {
for (int i=0; stypes[i]; i++) {
if (stypes[i] == fs.f_type)
return true;
@@ -193,7 +190,7 @@ static bool check_stream_network(stream_t *stream)
}
#elif defined(_WIN32)
-static bool check_stream_network(stream_t *stream)
+static bool check_stream_network(int fd)
{
NTSTATUS (NTAPI *pNtQueryVolumeInformationFile)(HANDLE,
PIO_STATUS_BLOCK, PVOID, ULONG, FS_INFORMATION_CLASS) = NULL;
@@ -209,8 +206,7 @@ static bool check_stream_network(stream_t *stream)
if (!pNtQueryVolumeInformationFile)
return false;
- struct priv *priv = stream->priv;
- HANDLE h = (HANDLE)_get_osfhandle(priv->fd);
+ HANDLE h = (HANDLE)_get_osfhandle(fd);
if (h == INVALID_HANDLE_VALUE)
return false;
@@ -225,7 +221,7 @@ static bool check_stream_network(stream_t *stream)
(info.Characteristics & FILE_REMOTE_DEVICE);
}
#else
-static bool check_stream_network(stream_t *stream)
+static bool check_stream_network(int fd)
{
return false;
}
@@ -239,6 +235,7 @@ static int open_f(stream_t *stream)
.fd = -1
};
stream->priv = priv;
+ stream->type = STREAMTYPE_FILE;
bool write = stream->mode == STREAM_WRITE;
int m = O_CLOEXEC | (write ? O_RDWR | O_CREAT | O_TRUNC : O_RDONLY);
@@ -279,9 +276,9 @@ static int open_f(stream_t *stream)
struct stat st;
if (fstat(fd, &st) == 0) {
if (S_ISDIR(st.st_mode)) {
- MP_ERR(stream, "File is a directory: '%s'\n", filename);
- close(fd);
- return STREAM_ERROR;
+ stream->type = STREAMTYPE_DIR;
+ stream->allow_caching = false;
+ MP_INFO(stream, "This is a directory - adding to playlist.\n");
}
#ifndef __MINGW32__
if (S_ISREG(st.st_mode)) {
@@ -303,7 +300,6 @@ static int open_f(stream_t *stream)
stream->seekable = true;
}
- stream->type = STREAMTYPE_FILE;
stream->fast_skip = true;
stream->fill_buffer = fill_buffer;
stream->write_buffer = write_buffer;
@@ -311,7 +307,7 @@ static int open_f(stream_t *stream)
stream->read_chunk = 64 * 1024;
stream->close = s_close;
- if (check_stream_network(stream))
+ if (check_stream_network(fd))
stream->streaming = true;
return STREAM_OK;
diff --git a/stream/stream_lavf.c b/stream/stream_lavf.c
index 1b9f736..bae6866 100644
--- a/stream/stream_lavf.c
+++ b/stream/stream_lavf.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <libavformat/avformat.h>
@@ -262,6 +261,13 @@ static int open_f(stream_t *stream)
filename = normalize_url(stream, filename);
+ if (strncmp(filename, "rtmp", 4) == 0) {
+ stream->demuxer = "lavf";
+ stream->lavf_type = "flv";
+ // Setting timeout enables listen mode - force it to disabled.
+ av_dict_set(&dict, "timeout", "0", 0);
+ }
+
int err = avio_open2(&avio, filename, flags, &cb, &dict);
if (err < 0) {
if (err == AVERROR_PROTOCOL_NOT_FOUND)
@@ -280,10 +286,6 @@ static int open_f(stream_t *stream)
}
}
- if (strncmp(filename, "rtmp", 4) == 0) {
- stream->demuxer = "lavf";
- stream->lavf_type = "flv";
- }
stream->priv = avio;
stream->seekable = avio->seekable;
stream->seek = stream->seekable ? seek : NULL;
diff --git a/stream/stream_mf.c b/stream/stream_mf.c
index ac2751b..a027e8b 100644
--- a/stream/stream_mf.c
+++ b/stream/stream_mf.c
@@ -4,21 +4,20 @@
* Copyright (C) 2006 Benjamin Zores
* Original author: Albeu
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
diff --git a/stream/stream_null.c b/stream/stream_null.c
index 8bd0fb7..a95a7ca 100644
--- a/stream/stream_null.c
+++ b/stream/stream_null.c
@@ -1,21 +1,20 @@
/*
- * This file is part of MPlayer.
- *
* Original author: Albeu
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
diff --git a/stream/stream_pvr.c b/stream/stream_pvr.c
index 9a18b29..ca35f1f 100644
--- a/stream/stream_pvr.c
+++ b/stream/stream_pvr.c
@@ -7,21 +7,20 @@
* Copyright (C) 2006 Benjamin Zores
* Copyright (C) 2007 Sven Gothel (channel navigation)
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
diff --git a/stream/stream_rar.c b/stream/stream_rar.c
index 510babf..3ba689b 100644
--- a/stream/stream_rar.c
+++ b/stream/stream_rar.c
@@ -43,16 +43,14 @@ This works as follows:
- stream_open() with file01.rar
- is opened as normal file (stream_file.c or others) first
- - the rar code in demux_playlist.c detects it
+ - demux_rar.c detects it
- if multi-part, opens file02.rar, file03.rar, etc. as actual streams
- (recursive opening is prevented with the STREAM_NO_FILTERS flag)
- - it returns a playlist like this to the player:
+ - it returns a playlist with entries like this to the player:
rar://bla01.rar|subfile.mkv
(one such entry for each file contained in the rar)
- stream_open() with the playlist entry, e.g. rar://bla01.rar|subfile.mkv
- leads to rar_entry_open()
- opens bla01.rar etc. again as actual streams
- (again, STREAM_NO_FILTERS to open the actual files)
- read accesses go into subfile.mkv contained in the rar file(s)
*/
diff --git a/stream/stream_smb.c b/stream/stream_smb.c
index ebea370..fd66310 100644
--- a/stream/stream_smb.c
+++ b/stream/stream_smb.c
@@ -1,21 +1,20 @@
/*
- * This file is part of MPlayer.
- *
* Original author: M. Tourne
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
diff --git a/stream/stream_tv.c b/stream/stream_tv.c
index b4181ac..9c8861d 100644
--- a/stream/stream_tv.c
+++ b/stream/stream_tv.c
@@ -4,21 +4,20 @@
* Copyright (C) 2006 Benjamin Zores
* Original author: Albeu
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
diff --git a/stream/tv.c b/stream/tv.c
index bb39cf2..b2bf150 100644
--- a/stream/tv.c
+++ b/stream/tv.c
@@ -8,21 +8,20 @@
* Feb 19, 2002: Significant rewrites by Charles R. Henrich (henrich@msu.edu)
* to add support for audio, and bktr *BSD support.
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
diff --git a/stream/tv.h b/stream/tv.h
index 0f9fa1d..d22b5b7 100644
--- a/stream/tv.h
+++ b/stream/tv.h
@@ -5,21 +5,20 @@
* Copyright (C) 2007 Attila Ötvös
* Copyright (C) 2007 Vladimir Voroshilov <voroshil@gmail.com>
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_TV_H
diff --git a/stream/tvi_def.h b/stream/tvi_def.h
index 987141b..8f2e72d 100644
--- a/stream/tvi_def.h
+++ b/stream/tvi_def.h
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_TVI_DEF_H
diff --git a/stream/tvi_dummy.c b/stream/tvi_dummy.c
index e0bfdb9..571dace 100644
--- a/stream/tvi_dummy.c
+++ b/stream/tvi_dummy.c
@@ -1,21 +1,20 @@
/*
* Only a sample!
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
diff --git a/stream/tvi_v4l2.c b/stream/tvi_v4l2.c
index 8eca53f..f882818 100644
--- a/stream/tvi_v4l2.c
+++ b/stream/tvi_v4l2.c
@@ -8,29 +8,26 @@
* Alex Beregszaszi <alex@fsn.hu>
* Gerd Knorr <kraxel@bytesex.org>
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
/*
-
known issues:
- norm setting isn't consistent with tvi_v4l
- the same for volume/bass/treble/balance
-
*/
#include "config.h"
diff --git a/sub/ass_mp.c b/sub/ass_mp.c
index 991a94a..b413a28 100644
--- a/sub/ass_mp.c
+++ b/sub/ass_mp.c
@@ -1,21 +1,20 @@
/*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 libass; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <inttypes.h>
@@ -56,7 +55,6 @@ void mp_ass_set_style(ASS_Style *style, double res_y,
style->FontSize = opts->font_size * scale;
style->PrimaryColour = MP_ASS_COLOR(opts->color);
style->SecondaryColour = style->PrimaryColour;
-#if LIBASS_VERSION >= 0x01102001
style->OutlineColour = MP_ASS_COLOR(opts->border_color);
if (opts->back_color.a) {
style->BackColour = MP_ASS_COLOR(opts->back_color);
@@ -65,16 +63,6 @@ void mp_ass_set_style(ASS_Style *style, double res_y,
style->BackColour = MP_ASS_COLOR(opts->shadow_color);
style->BorderStyle = 1; // outline
}
-#else
- if (opts->back_color.a) {
- style->OutlineColour = MP_ASS_COLOR(opts->back_color);
- style->BorderStyle = 3; // opaque box
- } else {
- style->OutlineColour = MP_ASS_COLOR(opts->border_color);
- style->BorderStyle = 1; // outline
- }
- style->BackColour = MP_ASS_COLOR(opts->shadow_color);
-#endif
style->Outline = opts->border_size * scale;
style->Shadow = opts->shadow_offset * scale;
style->Spacing = opts->spacing * scale;
@@ -83,9 +71,9 @@ void mp_ass_set_style(ASS_Style *style, double res_y,
style->MarginV = opts->margin_y * scale;
style->ScaleX = 1.;
style->ScaleY = 1.;
-#if LIBASS_VERSION >= 0x01020000
+ style->Alignment = 1 + (opts->align_x + 1) + (opts->align_y + 2) % 3 * 4;
style->Blur = opts->blur;
-#endif
+ style->Bold = opts->bold;
}
// Add default styles, if the track does not have any styles yet.
@@ -105,7 +93,6 @@ void mp_ass_add_default_styles(ASS_Track *track, struct MPOpts *opts)
track->default_style = sid;
ASS_Style *style = track->styles + sid;
style->Name = strdup("Default");
- style->Alignment = 2;
mp_ass_set_style(style, track->PlayResY, opts->sub_text_style);
}
@@ -125,58 +112,6 @@ ASS_Track *mp_ass_default_track(ASS_Library *library, struct MPOpts *opts)
return track;
}
-void mp_ass_configure(ASS_Renderer *priv, struct MPOpts *opts,
- struct mp_osd_res *dim)
-{
- ass_set_frame_size(priv, dim->w, dim->h);
- ass_set_margins(priv, dim->mt, dim->mb, dim->ml, dim->mr);
-
- int set_use_margins = 0;
- int set_sub_pos = 0;
- float set_line_spacing = 0;
- float set_font_scale = 1;
- int set_hinting = 0;
- if (opts->ass_style_override) {
- set_use_margins = opts->ass_use_margins;
- set_sub_pos = 100 - opts->sub_pos;
- set_line_spacing = opts->ass_line_spacing;
- set_hinting = opts->ass_hinting;
- set_font_scale = opts->sub_scale;
- if (opts->sub_scale_with_window) {
- int vidh = dim->h - (dim->mt + dim->mb);
- set_font_scale *= dim->h / (float)MPMAX(vidh, 1);
- }
- if (!opts->sub_scale_by_window) {
- double factor = dim->h / 720.0;
- if (factor != 0.0)
- set_font_scale /= factor;
- }
- }
-
- ass_set_use_margins(priv, set_use_margins);
-#if LIBASS_VERSION >= 0x01010000
- ass_set_line_position(priv, set_sub_pos);
-#endif
-#if LIBASS_VERSION >= 0x01000000
- ass_set_shaper(priv, opts->ass_shaper);
-#endif
-#if LIBASS_VERSION >= 0x01103001
- int set_force_flags = 0;
- if (opts->ass_style_override == 3)
- set_force_flags |= ASS_OVERRIDE_BIT_STYLE | ASS_OVERRIDE_BIT_FONT_SIZE;
- if (opts->ass_style_override == 4)
- set_force_flags |= ASS_OVERRIDE_BIT_FONT_SIZE;
- ass_set_selective_style_override_enabled(priv, set_force_flags);
- ASS_Style style = {0};
- mp_ass_set_style(&style, 288, opts->sub_text_style);
- ass_set_selective_style_override(priv, &style);
- free(style.FontName);
-#endif
- ass_set_font_scale(priv, set_font_scale);
- ass_set_hinting(priv, set_hinting);
- ass_set_line_spacing(priv, set_line_spacing);
-}
-
void mp_ass_configure_fonts(ASS_Renderer *priv, struct osd_style_opts *opts,
struct mpv_global *global, struct mp_log *log)
{
@@ -199,10 +134,8 @@ void mp_ass_render_frame(ASS_Renderer *renderer, ASS_Track *track, double time,
{
int changed;
ASS_Image *imgs = ass_render_frame(renderer, track, time, &changed);
- if (changed == 2)
- res->bitmap_id = ++res->bitmap_pos_id;
- else if (changed)
- res->bitmap_pos_id++;
+ if (changed)
+ res->change_id++;
res->format = SUBBITMAP_LIBASS;
res->parts = *parts;
diff --git a/sub/ass_mp.h b/sub/ass_mp.h
index 86c5c7f..176ad50 100644
--- a/sub/ass_mp.h
+++ b/sub/ass_mp.h
@@ -1,21 +1,20 @@
/*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 libass; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_ASS_MP_H
@@ -52,9 +51,6 @@ void mp_ass_add_default_styles(ASS_Track *track, struct MPOpts *opts);
ASS_Track *mp_ass_default_track(ASS_Library *library, struct MPOpts *opts);
-struct MPOpts;
-void mp_ass_configure(ASS_Renderer *priv, struct MPOpts *opts,
- struct mp_osd_res *dim);
void mp_ass_configure_fonts(ASS_Renderer *priv, struct osd_style_opts *opts,
struct mpv_global *global, struct mp_log *log);
ASS_Library *mp_ass_init(struct mpv_global *global, struct mp_log *log);
diff --git a/sub/dec_sub.c b/sub/dec_sub.c
index 7a0f5eb..bfb2c04 100644
--- a/sub/dec_sub.c
+++ b/sub/dec_sub.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
@@ -49,8 +48,8 @@ static const struct sd_functions *const sd_list[] = {
&sd_movtext,
&sd_srt,
&sd_lavf_srt,
- &sd_lavc_conv,
&sd_microdvd,
+ &sd_lavc_conv,
NULL
};
@@ -207,8 +206,6 @@ void sub_init_from_sh(struct dec_sub *sub, struct sh_stream *sh)
sub_set_extradata(sub, sh->sub->extradata, sh->sub->extradata_len);
struct sd init_sd = sub->init_sd;
init_sd.codec = sh->codec;
- init_sd.sub_stream_w = sh->sub->w;
- init_sd.sub_stream_h = sh->sub->h;
while (sub->num_sd < MAX_NUM_SD) {
struct sd *sd = talloc(NULL, struct sd);
@@ -346,7 +343,7 @@ static void multiply_timings(struct packet_list *subs, double factor)
}
}
-#define MS_TS(f_ts) ((int)((f_ts) * 1000 + 0.5))
+#define MS_TS(f_ts) ((long long)((f_ts) * 1000 + 0.5))
// Remove overlaps and fill gaps between adjacent subtitle packets. This is done
// by adjusting the duration of the earlier packet. If the gaps or overlap are
diff --git a/sub/draw_bmp.c b/sub/draw_bmp.c
index c5c0ddd..2093e5f 100644
--- a/sub/draw_bmp.c
+++ b/sub/draw_bmp.c
@@ -12,8 +12,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
- * with mpv; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stddef.h>
@@ -43,7 +42,7 @@ struct sub_cache {
};
struct part {
- int bitmap_pos_id;
+ int change_id;
int imgfmt;
enum mp_csp colorspace;
enum mp_csp_levels levels;
@@ -295,7 +294,7 @@ static void draw_ass(struct mp_draw_sub_cache *cache, struct mp_rect bb,
cspar.int_bits_out = 8;
struct mp_cmat yuv2rgb, rgb2yuv;
- bool need_conv = temp->flags & MP_IMGFLAG_YUV;
+ bool need_conv = temp->fmt.flags & MP_IMGFLAG_YUV;
if (need_conv) {
mp_get_yuv2rgb_coeffs(&cspar, &yuv2rgb);
mp_invert_yuv2rgb(&rgb2yuv, &yuv2rgb);
@@ -334,13 +333,13 @@ static void draw_ass(struct mp_draw_sub_cache *cache, struct mp_rect bb,
static void get_swscale_alignment(const struct mp_image *img, int *out_xstep,
int *out_ystep)
{
- int sx = (1 << img->chroma_x_shift);
- int sy = (1 << img->chroma_y_shift);
+ int sx = (1 << img->fmt.chroma_xs);
+ int sy = (1 << img->fmt.chroma_ys);
for (int p = 0; p < img->num_planes; ++p) {
int bits = img->fmt.bpp[p];
// the * 2 fixes problems with writing past the destination width
- while (((sx >> img->chroma_x_shift) * bits) % (SWS_MIN_BYTE_ALIGN * 8 * 2))
+ while (((sx >> img->fmt.chroma_xs) * bits) % (SWS_MIN_BYTE_ALIGN * 8 * 2))
sx *= 2;
}
@@ -399,7 +398,7 @@ static struct part *get_cache(struct mp_draw_sub_cache *cache,
if (use_cache) {
part = cache->parts[sbs->render_index];
if (part) {
- if (part->bitmap_pos_id != sbs->bitmap_pos_id
+ if (part->change_id != sbs->change_id
|| part->imgfmt != format->imgfmt
|| part->colorspace != format->params.colorspace
|| part->levels != format->params.colorlevels)
@@ -411,7 +410,7 @@ static struct part *get_cache(struct mp_draw_sub_cache *cache,
if (!part) {
part = talloc(cache, struct part);
*part = (struct part) {
- .bitmap_pos_id = sbs->bitmap_pos_id,
+ .change_id = sbs->change_id,
.num_imgs = sbs->num_parts,
.imgfmt = format->imgfmt,
.levels = format->params.colorlevels,
@@ -470,7 +469,7 @@ static struct mp_image *chroma_up(struct mp_draw_sub_cache *cache, int imgfmt,
// The temp image is always YUV, but src not necessarily.
// Reduce amount of conversions in YUV case (upsampling/shifting only)
- if (src->flags & MP_IMGFLAG_YUV) {
+ if (src->fmt.flags & MP_IMGFLAG_YUV) {
temp->params.colorspace = src->params.colorspace;
temp->params.colorlevels = src->params.colorlevels;
}
@@ -481,9 +480,9 @@ static struct mp_image *chroma_up(struct mp_draw_sub_cache *cache, int imgfmt,
// The whole point is not having swscale copy the Y plane
struct mp_image t_dst = *temp;
mp_image_setfmt(&t_dst, IMGFMT_Y8);
- mp_image_set_size(&t_dst, temp->chroma_width, temp->chroma_height);
+ mp_image_set_size(&t_dst, temp->w, temp->h);
struct mp_image t_src = t_dst;
- mp_image_set_size(&t_src, src->chroma_width, src->chroma_height);
+ mp_image_set_size(&t_src, src->w >> 1, src->h >> 1);
for (int c = 0; c < 2; c++) {
t_dst.planes[0] = temp->planes[1 + c];
t_dst.stride[0] = temp->stride[1 + c];
@@ -511,10 +510,9 @@ static void chroma_down(struct mp_image *old_src, struct mp_image *temp)
assert(temp->planes[0] == old_src->planes[0]);
struct mp_image t_dst = *temp;
mp_image_setfmt(&t_dst, IMGFMT_Y8);
- mp_image_set_size(&t_dst, old_src->chroma_width,
- old_src->chroma_height);
+ mp_image_set_size(&t_dst, old_src->w >> 1, old_src->h >> 1);
struct mp_image t_src = t_dst;
- mp_image_set_size(&t_src, temp->chroma_width, temp->chroma_height);
+ mp_image_set_size(&t_src, temp->w, temp->h);
for (int c = 0; c < 2; c++) {
t_dst.planes[0] = old_src->planes[1 + c];
t_dst.stride[0] = old_src->stride[1 + c];
diff --git a/sub/find_subfiles.c b/sub/find_subfiles.c
index bbab9a4..10dd479 100644
--- a/sub/find_subfiles.c
+++ b/sub/find_subfiles.c
@@ -19,7 +19,7 @@ static const char *const sub_exts[] = {"utf", "utf8", "utf-8", "idx", "sub", "sr
"js", "ass", "mks", "vtt", NULL};
static const char *const audio_exts[] = {"mp3", "aac", "mka", "dts", "flac",
- "ogg", "m4a", NULL};
+ "ogg", "m4a", "ac3", NULL};
static bool test_ext_list(bstr ext, const char *const *list)
{
diff --git a/sub/find_subfiles.h b/sub/find_subfiles.h
index a531b3e..223bf06 100644
--- a/sub/find_subfiles.h
+++ b/sub/find_subfiles.h
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_FIND_SUBFILES_H
diff --git a/sub/img_convert.c b/sub/img_convert.c
index 2094e98..1db73e1 100644
--- a/sub/img_convert.c
+++ b/sub/img_convert.c
@@ -1,18 +1,18 @@
/*
- * This file is part of mplayer.
+ * This file is part of mpv.
*
- * mplayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * mplayer is distributed in the hope that it will be useful,
+ * 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 mplayer. If not, see <http://www.gnu.org/licenses/>.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
@@ -29,7 +29,6 @@
#include "video/img_format.h"
#include "video/mp_image.h"
#include "video/sws_utils.h"
-#include "video/memcpy_pic.h"
struct osd_conv_cache {
struct sub_bitmap part[MP_SUB_BB_LIST_MAX];
diff --git a/sub/osd.c b/sub/osd.c
index 1a9e7f5..992806e 100644
--- a/sub/osd.c
+++ b/sub/osd.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
@@ -41,37 +40,62 @@
#include "video/mp_image.h"
#include "video/mp_image_pool.h"
-static const struct osd_style_opts osd_style_opts_def = {
- .font = "sans-serif",
- .font_size = 55,
- .color = {255, 255, 255, 255},
- .border_color = {0, 0, 0, 255},
- .shadow_color = {240, 240, 240, 128},
- .border_size = 3,
- .shadow_offset = 0,
- .margin_x = 25,
- .margin_y = 22,
+#define OPT_BASE_STRUCT struct osd_style_opts
+static const m_option_t style_opts[] = {
+ OPT_STRING("font", font, 0),
+ OPT_FLOATRANGE("font-size", font_size, 0, 1, 9000),
+ OPT_COLOR("color", color, 0),
+ OPT_COLOR("border-color", border_color, 0),
+ OPT_COLOR("shadow-color", shadow_color, 0),
+ OPT_COLOR("back-color", back_color, 0),
+ OPT_FLOATRANGE("border-size", border_size, 0, 0, 10),
+ OPT_FLOATRANGE("shadow-offset", shadow_offset, 0, 0, 10),
+ OPT_FLOATRANGE("spacing", spacing, 0, -10, 10),
+ OPT_INTRANGE("margin-x", margin_x, 0, 0, 300),
+ OPT_INTRANGE("margin-y", margin_y, 0, 0, 600),
+ OPT_CHOICE("align-x", align_x, 0,
+ ({"left", -1}, {"center", 0}, {"right", +1})),
+ OPT_CHOICE("align-y", align_y, 0,
+ ({"top", -1}, {"center", 0}, {"bottom", +1})),
+ OPT_FLOATRANGE("blur", blur, 0, 0, 20),
+ OPT_FLAG("bold", bold, 0),
+ {0}
};
-#define OPT_BASE_STRUCT struct osd_style_opts
const struct m_sub_options osd_style_conf = {
- .opts = (const m_option_t[]) {
- OPT_STRING("font", font, 0),
- OPT_FLOATRANGE("font-size", font_size, 0, 1, 9000),
- OPT_COLOR("color", color, 0),
- OPT_COLOR("border-color", border_color, 0),
- OPT_COLOR("shadow-color", shadow_color, 0),
- OPT_COLOR("back-color", back_color, 0),
- OPT_FLOATRANGE("border-size", border_size, 0, 0, 10),
- OPT_FLOATRANGE("shadow-offset", shadow_offset, 0, 0, 10),
- OPT_FLOATRANGE("spacing", spacing, 0, -10, 10),
- OPT_INTRANGE("margin-x", margin_x, 0, 0, 300),
- OPT_INTRANGE("margin-y", margin_y, 0, 0, 600),
- OPT_FLOATRANGE("blur", blur, 0, 0, 20),
- {0}
+ .opts = style_opts,
+ .size = sizeof(struct osd_style_opts),
+ .defaults = &(const struct osd_style_opts){
+ .font = "sans-serif",
+ .font_size = 55,
+ .color = {255, 255, 255, 255},
+ .border_color = {0, 0, 0, 255},
+ .shadow_color = {240, 240, 240, 128},
+ .border_size = 3,
+ .shadow_offset = 0,
+ .margin_x = 25,
+ .margin_y = 22,
+ .align_x = -1,
+ .align_y = -1,
},
+};
+
+const struct m_sub_options sub_style_conf = {
+ .opts = style_opts,
.size = sizeof(struct osd_style_opts),
- .defaults = &osd_style_opts_def,
+ .defaults = &(const struct osd_style_opts){
+ .font = "sans-serif",
+ .font_size = 55,
+ .color = {255, 255, 255, 255},
+ .border_color = {0, 0, 0, 255},
+ .shadow_color = {240, 240, 240, 128},
+ .border_size = 3,
+ .shadow_offset = 0,
+ .margin_x = 25,
+ .margin_y = 22,
+ .align_x = 0,
+ .align_y = 1,
+ },
};
static bool osd_res_equals(struct mp_osd_res a, struct mp_osd_res b)
@@ -239,7 +263,7 @@ static void render_object(struct osd_state *osd, struct osd_object *obj,
} else if (obj->type == OSDTYPE_EXTERNAL2) {
if (obj->external2 && obj->external2->format) {
*out_imgs = *obj->external2;
- obj->external2->bitmap_id = obj->external2->bitmap_pos_id = 0;
+ obj->external2->change_id = 0;
}
} else if (obj->type == OSDTYPE_NAV_HIGHLIGHT) {
if (obj->highlight_priv)
@@ -248,29 +272,23 @@ static void render_object(struct osd_state *osd, struct osd_object *obj,
osd_object_get_bitmaps(osd, obj, out_imgs);
}
- if (obj->force_redraw) {
- out_imgs->bitmap_id++;
- out_imgs->bitmap_pos_id++;
- }
+ if (obj->force_redraw)
+ out_imgs->change_id++;
obj->force_redraw = false;
- obj->vo_bitmap_id += out_imgs->bitmap_id;
- obj->vo_bitmap_pos_id += out_imgs->bitmap_pos_id;
+ obj->vo_change_id += out_imgs->change_id;
if (out_imgs->num_parts == 0)
return;
- if (obj->cached.bitmap_id == obj->vo_bitmap_id
- && obj->cached.bitmap_pos_id == obj->vo_bitmap_pos_id
- && formats[obj->cached.format])
+ if (obj->cached.change_id == obj->vo_change_id && formats[obj->cached.format])
{
*out_imgs = obj->cached;
return;
}
out_imgs->render_index = obj->type;
- out_imgs->bitmap_id = obj->vo_bitmap_id;
- out_imgs->bitmap_pos_id = obj->vo_bitmap_pos_id;
+ out_imgs->change_id = obj->vo_change_id;
if (formats[out_imgs->format])
return;
@@ -314,6 +332,8 @@ void osd_draw(struct osd_state *osd, struct mp_osd_res res,
continue;
if ((draw_flags & OSD_DRAW_SUB_ONLY) && !obj->is_sub)
continue;
+ if ((draw_flags & OSD_DRAW_OSD_ONLY) && obj->is_sub)
+ continue;
if (obj->sub_state.dec_sub)
sub_lock(obj->sub_state.dec_sub);
diff --git a/sub/osd.h b/sub/osd.h
index 015bae2..3f23e69 100644
--- a/sub/osd.h
+++ b/sub/osd.h
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_SUB_H
@@ -70,8 +69,7 @@ struct sub_bitmaps {
struct sub_bitmap *parts;
int num_parts;
- // Incremented on each change
- int bitmap_id, bitmap_pos_id;
+ int change_id; // Incremented on each change
};
struct mp_osd_res {
@@ -133,10 +131,14 @@ struct osd_style_opts {
float spacing;
int margin_x;
int margin_y;
+ int align_x;
+ int align_y;
float blur;
+ int bold;
};
extern const struct m_sub_options osd_style_conf;
+extern const struct m_sub_options sub_style_conf;
struct osd_state;
struct osd_object;
@@ -178,6 +180,7 @@ void osd_set_nav_highlight(struct osd_state *osd, void *priv);
enum mp_osd_draw_flags {
OSD_DRAW_SUB_FILTER = (1 << 0),
OSD_DRAW_SUB_ONLY = (1 << 1),
+ OSD_DRAW_OSD_ONLY = (1 << 2),
};
void osd_draw(struct osd_state *osd, struct mp_osd_res res,
diff --git a/sub/osd_libass.c b/sub/osd_libass.c
index 6e61355..727faa3 100644
--- a/sub/osd_libass.c
+++ b/sub/osd_libass.c
@@ -1,18 +1,18 @@
/*
- * This file is part of mplayer2.
+ * This file is part of mpv.
*
- * mplayer2 is free software; you can redistribute it and/or modify
+ * 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.
*
- * mplayer2 is distributed in the hope that it will be useful,
+ * 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 mplayer2. If not, see <http://www.gnu.org/licenses/>.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
@@ -111,7 +111,6 @@ static void create_ass_track(struct osd_state *osd, struct osd_object *obj,
int sid = ass_alloc_style(track);
track->default_style = sid;
ASS_Style *style = track->styles + sid;
- style->Alignment = 5; // top-title, left
style->Name = strdup("OSD");
// Set to neutral base direction, as opposed to VSFilter LTR default
style->Encoding = -1;
@@ -317,6 +316,8 @@ static void get_osd_bar_box(struct osd_state *osd, struct osd_object *obj,
// Rendering with shadow is broken (because there's more than one shape)
style->Shadow = 0;
+ style->Alignment = 5;
+
*o_border = style->Outline;
*o_x = get_align(opts->osd_bar_align_x, track->PlayResX, *o_w, *o_border);
@@ -439,9 +440,7 @@ static void update_sub(struct osd_state *osd, struct osd_object *obj)
if (obj->type == OSDTYPE_SUB2)
style->Alignment = 6;
-#if LIBASS_VERSION >= 0x01010000
ass_set_line_position(obj->osd_render, 100 - opts->sub_pos);
-#endif
add_osd_ass_event_escaped(obj->osd_track, obj->text);
}
diff --git a/sub/osd_state.h b/sub/osd_state.h
index 01546d7..73bfcdd 100644
--- a/sub/osd_state.h
+++ b/sub/osd_state.h
@@ -36,8 +36,7 @@ struct osd_object {
struct sub_bitmaps cached;
// VO cache state
- int vo_bitmap_id;
- int vo_bitmap_pos_id;
+ int vo_change_id;
struct mp_osd_res vo_res;
// Internally used by osd_libass.c
diff --git a/sub/sd.h b/sub/sd.h
index 3f348fc..7b23990 100644
--- a/sub/sd.h
+++ b/sub/sd.h
@@ -25,9 +25,6 @@ struct sd {
// the resolution of the VO, nor does it have to be the OSD resolution.
int sub_video_w, sub_video_h;
- // Resolution hints stored in mp4 files.
- int sub_stream_w, sub_stream_h;
-
// Shared renderer for ASS - done to avoid reloading embedded fonts.
struct ass_library *ass_library;
struct ass_renderer *ass_renderer;
diff --git a/sub/sd_ass.c b/sub/sd_ass.c
index 8bfefb3..6f3e24a 100644
--- a/sub/sd_ass.c
+++ b/sub/sd_ass.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
@@ -124,6 +123,71 @@ static void decode(struct sd *sd, struct demux_packet *packet)
event->Text = strdup(text);
}
+static void configure_ass(struct sd *sd, struct mp_osd_res *dim)
+{
+ struct sd_ass_priv *ctx = sd->priv;
+ struct MPOpts *opts = sd->opts;
+ ASS_Renderer *priv = sd->ass_renderer;
+ ASS_Track *track = ctx->ass_track;
+
+ ass_set_frame_size(priv, dim->w, dim->h);
+ ass_set_margins(priv, dim->mt, dim->mb, dim->ml, dim->mr);
+
+ bool set_use_margins = false;
+ int set_sub_pos = 0;
+ float set_line_spacing = 0;
+ float set_font_scale = 1;
+ int set_hinting = 0;
+ bool set_scale_with_window = false;
+ bool set_scale_by_window = true;
+ bool total_override = false;
+ // With forced overrides, apply the --sub-* specific options
+ if (ctx->is_converted || opts->ass_style_override == 3) {
+ set_scale_with_window = opts->sub_scale_with_window;
+ set_use_margins = opts->sub_use_margins;
+ set_scale_by_window = opts->sub_scale_by_window;
+ total_override = true;
+ } else {
+ set_scale_with_window = opts->ass_scale_with_window;
+ set_use_margins = opts->ass_use_margins;
+ }
+ if (ctx->is_converted || opts->ass_style_override) {
+ set_sub_pos = 100 - opts->sub_pos;
+ set_line_spacing = opts->ass_line_spacing;
+ set_hinting = opts->ass_hinting;
+ set_font_scale = opts->sub_scale;
+ }
+ if (set_scale_with_window) {
+ int vidh = dim->h - (dim->mt + dim->mb);
+ set_font_scale *= dim->h / (float)MPMAX(vidh, 1);
+ }
+ if (!set_scale_by_window) {
+ double factor = dim->h / 720.0;
+ if (factor != 0.0)
+ set_font_scale /= factor;
+ }
+ ass_set_use_margins(priv, set_use_margins);
+ ass_set_line_position(priv, set_sub_pos);
+ ass_set_shaper(priv, opts->ass_shaper);
+ int set_force_flags = 0;
+ if (total_override)
+ set_force_flags |= ASS_OVERRIDE_BIT_STYLE | ASS_OVERRIDE_BIT_FONT_SIZE;
+ if (opts->ass_style_override == 4)
+ set_force_flags |= ASS_OVERRIDE_BIT_FONT_SIZE;
+ ass_set_selective_style_override_enabled(priv, set_force_flags);
+ ASS_Style style = {0};
+ mp_ass_set_style(&style, 288, opts->sub_text_style);
+ ass_set_selective_style_override(priv, &style);
+ free(style.FontName);
+ if (ctx->is_converted && track->default_style < track->n_styles) {
+ mp_ass_set_style(track->styles + track->default_style,
+ track->PlayResY, opts->sub_text_style);
+ }
+ ass_set_font_scale(priv, set_font_scale);
+ ass_set_hinting(priv, set_hinting);
+ ass_set_line_spacing(priv, set_line_spacing);
+}
+
static void get_bitmaps(struct sd *sd, struct mp_osd_res dim, double pts,
struct sub_bitmaps *res)
{
@@ -145,9 +209,8 @@ static void get_bitmaps(struct sd *sd, struct mp_osd_res dim, double pts,
if (isnormal(par))
scale = par;
}
- mp_ass_configure(renderer, opts, &dim);
- ass_set_aspect_ratio(renderer, scale, 1);
-#if LIBASS_VERSION >= 0x01020000
+ configure_ass(sd, &dim);
+ ass_set_pixel_aspect(renderer, scale);
if (!ctx->is_converted && (!opts->ass_style_override ||
opts->ass_vsfilter_blur_compat))
{
@@ -155,7 +218,6 @@ static void get_bitmaps(struct sd *sd, struct mp_osd_res dim, double pts,
} else {
ass_set_storage_size(renderer, 0, 0);
}
-#endif
mp_ass_render_frame(renderer, ctx->ass_track, pts * 1000 + .5,
&ctx->parts, res);
talloc_steal(ctx, ctx->parts);
@@ -181,6 +243,7 @@ static void append(struct buf *b, char c)
static void ass_to_plaintext(struct buf *b, const char *in)
{
bool in_tag = false;
+ const char *open_tag_pos = NULL;
bool in_drawing = false;
while (*in) {
if (in_tag) {
@@ -189,11 +252,13 @@ static void ass_to_plaintext(struct buf *b, const char *in)
in_tag = false;
} else if (in[0] == '\\' && in[1] == 'p') {
in += 2;
- // skip text between \pN and \p0 tags
- if (in[0] == '0') {
- in_drawing = false;
- } else if (in[0] >= '1' && in[0] <= '9') {
- in_drawing = true;
+ // Skip text between \pN and \p0 tags. A \p without a number
+ // is the same as \p0, and leading 0s are also allowed.
+ in_drawing = false;
+ while (in[0] >= '0' && in[0] <= '9') {
+ if (in[0] != '0')
+ in_drawing = true;
+ in += 1;
}
} else {
in += 1;
@@ -206,6 +271,7 @@ static void ass_to_plaintext(struct buf *b, const char *in)
in += 2;
append(b, ' ');
} else if (in[0] == '{') {
+ open_tag_pos = in;
in += 1;
in_tag = true;
} else {
@@ -215,6 +281,11 @@ static void ass_to_plaintext(struct buf *b, const char *in)
}
}
}
+ // A '{' without a closing '}' is always visible.
+ if (in_tag) {
+ while (*open_tag_pos)
+ append(b, *open_tag_pos++);
+ }
}
// Empty string counts as whitespace. Reads s[len-1] even if there are \0s.
@@ -327,7 +398,6 @@ static void mangle_colors(struct sd *sd, struct sub_bitmaps *parts)
if (opts->ass_vsfilter_color_compat == 0) // "no"
return;
bool force_601 = opts->ass_vsfilter_color_compat == 3;
-#if LIBASS_VERSION >= 0x01020000
ASS_Track *track = ctx->ass_track;
static const int ass_csp[] = {
[YCBCR_BT601_TV] = MP_CSP_BT_601,
@@ -362,7 +432,6 @@ static void mangle_colors(struct sd *sd, struct sub_bitmaps *parts)
// Unknown colorspace (either YCBCR_UNKNOWN, or a valid value unknown to us)
if (!csp || !levels)
return;
-#endif
struct mp_image_params params = ctx->video_params;
@@ -389,9 +458,11 @@ static void mangle_colors(struct sd *sd, struct sub_bitmaps *parts)
int msgl = basic_conv ? MSGL_V : MSGL_WARN;
ctx->last_params = params;
MP_MSG(sd, msgl, "mangling colors like vsfilter: "
- "RGB -> %s %s -> %s %s -> RGB\n", mp_csp_names[csp],
- mp_csp_levels_names[levels], mp_csp_names[params.colorspace],
- mp_csp_levels_names[params.colorlevels]);
+ "RGB -> %s %s -> %s %s -> RGB\n",
+ m_opt_choice_str(mp_csp_names, csp),
+ m_opt_choice_str(mp_csp_levels_names, levels),
+ m_opt_choice_str(mp_csp_names, params.colorspace),
+ m_opt_choice_str(mp_csp_names, params.colorlevels));
}
// Conversion that VSFilter would use
diff --git a/sub/sd_lavc.c b/sub/sd_lavc.c
index 50af016..80a8409 100644
--- a/sub/sd_lavc.c
+++ b/sub/sd_lavc.c
@@ -1,18 +1,18 @@
/*
- * This file is part of mplayer2.
+ * This file is part of mpv.
*
- * mplayer2 is free software; you can redistribute it and/or modify
+ * 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.
*
- * mplayer2 is distributed in the hope that it will be useful,
+ * 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 mplayer2. If not, see <http://www.gnu.org/licenses/>.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
@@ -28,7 +28,6 @@
#include "common/av_common.h"
#include "options/options.h"
#include "video/mp_image.h"
-#include "video/csputils.h"
#include "sd.h"
#include "dec_sub.h"
@@ -98,35 +97,6 @@ static void get_resolution(struct sd *sd, int wh[2])
}
}
-static void set_mp4_vobsub_idx(AVCodecContext *avctx, char *src, int w, int h)
-{
- char pal_s[128];
- int pal_s_pos = 0;
- for (int i = 0; i < 16; i++) {
- unsigned int e = AV_RB32(src + i * 4);
-
- // lavc doesn't accept YUV palette - "does god hate me?"
- struct mp_csp_params csp = MP_CSP_PARAMS_DEFAULTS;
- csp.int_bits_in = 8;
- csp.int_bits_out = 8;
- struct mp_cmat cmatrix;
- mp_get_yuv2rgb_coeffs(&csp, &cmatrix);
- int c[3] = {(e >> 16) & 0xff, (e >> 8) & 0xff, e & 0xff};
- mp_map_int_color(&cmatrix, 8, c);
- e = (c[2] << 16) | (c[1] << 8) | c[0];
-
- snprintf(pal_s + pal_s_pos, sizeof(pal_s) - pal_s_pos, "%06x%s", e,
- i != 15 ? ", " : "");
- pal_s_pos = strlen(pal_s);
- if (pal_s_pos >= sizeof(pal_s))
- break;
- }
-
- char buf[256] = "";
- snprintf(buf, sizeof(buf), "size: %dx%d\npalette: %s\n", w, h, pal_s);
- mp_lavc_set_extradata(avctx, buf, strlen(buf));
-}
-
static int init(struct sd *sd)
{
struct sd_lavc_priv *priv = talloc_zero(NULL, struct sd_lavc_priv);
@@ -139,11 +109,6 @@ static int init(struct sd *sd)
if (!ctx)
goto error;
mp_lavc_set_extradata(ctx, sd->extradata, sd->extradata_len);
- if (sd->extradata_len == 64 && sd->sub_stream_w && sd->sub_stream_h &&
- cid == AV_CODEC_ID_DVD_SUBTITLE)
- {
- set_mp4_vobsub_idx(ctx, sd->extradata, sd->sub_stream_w, sd->sub_stream_h);
- }
if (avcodec_open2(ctx, sub_codec, NULL) < 0)
goto error;
priv->avctx = ctx;
@@ -291,7 +256,7 @@ static void get_bitmaps(struct sd *sd, struct mp_osd_res d, double pts,
res->parts = priv->outbitmaps;
res->num_parts = current->count;
if (priv->displayed_id != current->id)
- res->bitmap_id = ++res->bitmap_pos_id;
+ res->change_id++;
priv->displayed_id = current->id;
res->format = SUBBITMAP_INDEXED;
diff --git a/sub/sd_microdvd.c b/sub/sd_microdvd.c
index cd4d523..b6e3a93 100644
--- a/sub/sd_microdvd.c
+++ b/sub/sd_microdvd.c
@@ -1,21 +1,20 @@
/*
* Subtitles converter to SSA/ASS in order to allow special formatting
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
diff --git a/sub/sd_srt.c b/sub/sd_srt.c
index d2628c9..5c5cff5 100644
--- a/sub/sd_srt.c
+++ b/sub/sd_srt.c
@@ -1,21 +1,20 @@
/*
* Subtitles converter to SSA/ASS in order to allow special formatting
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
diff --git a/ta/ta_utils.c b/ta/ta_utils.c
index 80a42bb..860a964 100644
--- a/ta/ta_utils.c
+++ b/ta/ta_utils.c
@@ -106,7 +106,9 @@ static bool strndup_append_at(char **str, size_t at, const char *append,
*str = t;
}
- memcpy(*str + at, append, append_len);
+ if (append_len)
+ memcpy(*str + at, append, append_len);
+
(*str)[at + append_len] = '\0';
ta_dbg_mark_as_string(*str);
diff --git a/test/gl_video.c b/test/gl_video.c
new file mode 100644
index 0000000..543ff2e
--- /dev/null
+++ b/test/gl_video.c
@@ -0,0 +1,42 @@
+#include "test_helpers.h"
+#include "video/out/gl_video.h"
+
+static void test_scale_ambient_lux_limits(void **state) {
+ float x;
+ x = gl_video_scale_ambient_lux(16.0, 64.0, 2.40, 1.961, 16.0);
+ assert_double_equal(x, 2.40f);
+
+ x = gl_video_scale_ambient_lux(16.0, 64.0, 2.40, 1.961, 64.0);
+ assert_double_equal(x, 1.961f);
+}
+
+static void test_scale_ambient_lux_sign(void **state) {
+ float x;
+ x = gl_video_scale_ambient_lux(16.0, 64.0, 1.961, 2.40, 64.0);
+ assert_double_equal(x, 2.40f);
+}
+
+static void test_scale_ambient_lux_clamping(void **state) {
+ float x;
+ x = gl_video_scale_ambient_lux(16.0, 64.0, 2.40, 1.961, 0.0);
+ assert_double_equal(x, 2.40f);
+}
+
+static void test_scale_ambient_lux_log10_midpoint(void **state) {
+ float x;
+ // 32 corresponds to the the midpoint after converting lux to the log10 scale
+ x = gl_video_scale_ambient_lux(16.0, 64.0, 2.40, 1.961, 32.0);
+ float mid_gamma = (2.40 - 1.961) / 2 + 1.961;
+ assert_double_equal(x, mid_gamma);
+}
+
+int main(void) {
+ const UnitTest tests[] = {
+ unit_test(test_scale_ambient_lux_limits),
+ unit_test(test_scale_ambient_lux_sign),
+ unit_test(test_scale_ambient_lux_clamping),
+ unit_test(test_scale_ambient_lux_log10_midpoint),
+ };
+ return run_tests(tests);
+}
+
diff --git a/test/test_helpers.h b/test/test_helpers.h
index 3dfe08f..7a61da8 100644
--- a/test/test_helpers.h
+++ b/test/test_helpers.h
@@ -7,5 +7,9 @@
#include <cmocka.h>
#include <stdio.h>
+#include <math.h>
+#include <float.h>
+
+#define assert_double_equal(a, b) assert_true(fabs(a - b) <= DBL_EPSILON)
#endif
diff --git a/travis-deps b/travis-deps
index 8df9147..194f183 100755
--- a/travis-deps
+++ b/travis-deps
@@ -89,7 +89,7 @@ class Libav < TravisDepsBuilder
{
"libav-stable" => {
:action => :stable,
- :url => 'http://libav.org/releases/libav-10.tar.gz'
+ :url => 'http://libav.org/releases/libav-11.tar.gz'
},
"libav-git" => {
:action => :git,
@@ -97,7 +97,7 @@ class Libav < TravisDepsBuilder
},
"ffmpeg-stable" => {
:action => :stable,
- :url => 'http://www.ffmpeg.org/releases/ffmpeg-2.1.4.tar.gz'
+ :url => 'http://www.ffmpeg.org/releases/ffmpeg-2.4.tar.gz'
},
"ffmpeg-git" => {
:action => :git,
@@ -124,7 +124,7 @@ class Libass < TravisDepsBuilder
{
"libass-stable" => {
:action => :stable,
- :url => 'http://libass.googlecode.com/files/libass-0.10.1.tar.gz'
+ :url => 'https://github.com/libass/libass/releases/download/0.12.1/libass-0.12.1.tar.gz'
}
}
end
diff --git a/video/csputils.c b/video/csputils.c
index 835f06b..a4d896f 100644
--- a/video/csputils.c
+++ b/video/csputils.c
@@ -5,21 +5,20 @@
*
* mp_invert_yuv2rgb based on DarkPlaces engine, original code (GPL2 or later)
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * 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
@@ -37,31 +36,52 @@
#include "mp_image.h"
#include "csputils.h"
+#include "options/m_option.h"
+
+const struct m_opt_choice_alternatives mp_csp_names[] = {
+ {"auto", MP_CSP_AUTO},
+ {"bt.601", MP_CSP_BT_601},
+ {"bt.709", MP_CSP_BT_709},
+ {"smpte-240m", MP_CSP_SMPTE_240M},
+ {"bt.2020-ncl", MP_CSP_BT_2020_NC},
+ {"bt.2020-cl", MP_CSP_BT_2020_C},
+ {"rgb", MP_CSP_RGB},
+ {"xyz", MP_CSP_XYZ},
+ {"ycgco", MP_CSP_YCGCO},
+ {0}
+};
-const char *const mp_csp_names[MP_CSP_COUNT] = {
- "Autoselect",
- "BT.601 (SD)",
- "BT.709 (HD)",
- "SMPTE-240M",
- "BT.2020-NCL (UHD)",
- "BT.2020-CL (UHD)",
- "RGB",
- "XYZ",
- "YCgCo",
+const struct m_opt_choice_alternatives mp_csp_levels_names[] = {
+ {"auto", MP_CSP_LEVELS_AUTO},
+ {"limited", MP_CSP_LEVELS_TV},
+ {"full", MP_CSP_LEVELS_PC},
+ {0}
};
-const char *const mp_csp_levels_names[MP_CSP_LEVELS_COUNT] = {
- "Autoselect",
- "TV",
- "PC",
+const struct m_opt_choice_alternatives mp_csp_prim_names[] = {
+ {"auto", MP_CSP_PRIM_AUTO},
+ {"bt.601-525", MP_CSP_PRIM_BT_601_525},
+ {"bt.601-625", MP_CSP_PRIM_BT_601_625},
+ {"bt.709", MP_CSP_PRIM_BT_709},
+ {"bt.2020", MP_CSP_PRIM_BT_2020},
+ {"bt.470m", MP_CSP_PRIM_BT_470M},
+ {"apple", MP_CSP_PRIM_APPLE},
+ {"adobe", MP_CSP_PRIM_ADOBE},
+ {"prophoto", MP_CSP_PRIM_PRO_PHOTO},
+ {"cie1931", MP_CSP_PRIM_CIE_1931},
+ {0}
};
-const char *const mp_csp_prim_names[MP_CSP_PRIM_COUNT] = {
- "Autoselect",
- "BT.601 (525-line SD)",
- "BT.601 (625-line SD)",
- "BT.709 (HD)",
- "BT.2020 (UHD)",
+const struct m_opt_choice_alternatives mp_csp_trc_names[] = {
+ {"auto", MP_CSP_TRC_AUTO},
+ {"bt.1886", MP_CSP_TRC_BT_1886},
+ {"srgb", MP_CSP_TRC_SRGB},
+ {"linear", MP_CSP_TRC_LINEAR},
+ {"gamma1.8", MP_CSP_TRC_GAMMA18},
+ {"gamma2.2", MP_CSP_TRC_GAMMA22},
+ {"gamma2.8", MP_CSP_TRC_GAMMA28},
+ {"prophoto", MP_CSP_TRC_PRO_PHOTO},
+ {0}
};
const char *const mp_csp_equalizer_names[MP_CSP_EQ_COUNT] = {
@@ -72,32 +92,32 @@ const char *const mp_csp_equalizer_names[MP_CSP_EQ_COUNT] = {
"gamma",
};
-const char *const mp_chroma_names[MP_CHROMA_COUNT] = {
- "unknown",
- "mpeg2/4/h264",
- "mpeg1/jpeg",
+const struct m_opt_choice_alternatives mp_chroma_names[] = {
+ {"unknown", MP_CHROMA_AUTO},
+ {"mpeg2/4/h264",MP_CHROMA_LEFT},
+ {"mpeg1/jpeg", MP_CHROMA_CENTER},
+ {0}
};
// The short name _must_ match with what vf_stereo3d accepts (if supported).
-// The long name is closer to the Matroska spec (StereoMode element).
+// The long name in comments is closer to the Matroska spec (StereoMode element).
// The numeric index matches the Matroska StereoMode value. If you add entries
// that don't match Matroska, make sure demux_mkv.c rejects them properly.
-// The long name is unused.
-#define E(index, short, long) [index] = short
-const char *const mp_stereo3d_names[MP_STEREO3D_COUNT] = {
- E(0, "mono", "mono"), // unsupported by vf_stereo3d
- E(1, "sbs2l", "side_by_side_left"),
- E(2, "ab2r", "top_bottom_right"),
- E(3, "ab2l", "top_bottom_left"),
- E(4, "checkr", "checkboard_right"), // unsupported by vf_stereo3d
- E(5, "checkl", "checkboard_left"), // unsupported by vf_stereo3d
- E(6, "irr", "row_interleaved_right"),
- E(7, "irl", "row_interleaved_left"),
- E(8, "icr", "column_interleaved_right"),// unsupported by vf_stereo3d
- E(9, "icl", "column_interleaved_left"), // unsupported by vf_stereo3d
- E(10, "arcc", "anaglyph_cyan_red"), // Matroska: unclear which mode
- E(11, "sbs2r", "side_by_side_right"),
- E(12, "agmc", "anaglyph_green_magenta"), // Matroska: unclear which mode
+const struct m_opt_choice_alternatives mp_stereo3d_names[] = {
+ {"mono", 0},
+ {"sbs2l", 1}, // "side_by_side_left"
+ {"ab2r", 2}, // "top_bottom_right"
+ {"ab2l", 3}, // "top_bottom_left"
+ {"checkr", 4}, // "checkboard_right" (unsupported by vf_stereo3d)
+ {"checkl", 5}, // "checkboard_left" (unsupported by vf_stereo3d)
+ {"irr", 6}, // "row_interleaved_right"
+ {"irl", 7}, // "row_interleaved_left"
+ {"icr", 8}, // "column_interleaved_right" (unsupported by vf_stereo3d)
+ {"icl", 9}, // "column_interleaved_left" (unsupported by vf_stereo3d)
+ {"arcc", 10}, // "anaglyph_cyan_red" (Matroska: unclear which mode)
+ {"sbs2r", 11}, // "side_by_side_right"
+ {"agmc", 12}, // "anaglyph_green_magenta" (Matroska: unclear which mode)
+ {0}
};
enum mp_csp avcol_spc_to_mp_csp(int avcolorspace)
@@ -105,10 +125,8 @@ enum mp_csp avcol_spc_to_mp_csp(int avcolorspace)
switch (avcolorspace) {
case AVCOL_SPC_BT709: return MP_CSP_BT_709;
case AVCOL_SPC_BT470BG: return MP_CSP_BT_601;
-#if HAVE_AVCOL_SPC_BT2020
case AVCOL_SPC_BT2020_NCL: return MP_CSP_BT_2020_NC;
case AVCOL_SPC_BT2020_CL: return MP_CSP_BT_2020_C;
-#endif
case AVCOL_SPC_SMPTE170M: return MP_CSP_BT_601;
case AVCOL_SPC_SMPTE240M: return MP_CSP_SMPTE_240M;
case AVCOL_SPC_RGB: return MP_CSP_RGB;
@@ -133,22 +151,36 @@ enum mp_csp_prim avcol_pri_to_mp_csp_prim(int avpri)
case AVCOL_PRI_SMPTE170M: return MP_CSP_PRIM_BT_601_525;
case AVCOL_PRI_BT470BG: return MP_CSP_PRIM_BT_601_625;
case AVCOL_PRI_BT709: return MP_CSP_PRIM_BT_709;
-#if HAVE_AVCOL_SPC_BT2020
case AVCOL_PRI_BT2020: return MP_CSP_PRIM_BT_2020;
-#endif
+ case AVCOL_PRI_BT470M: return MP_CSP_PRIM_BT_470M;
default: return MP_CSP_PRIM_AUTO;
}
}
+enum mp_csp_trc avcol_trc_to_mp_csp_trc(int avtrc)
+{
+ switch (avtrc) {
+ case AVCOL_TRC_BT709:
+ case AVCOL_TRC_SMPTE170M:
+ case AVCOL_TRC_SMPTE240M:
+ case AVCOL_TRC_BT1361_ECG:
+ case AVCOL_TRC_BT2020_10:
+ case AVCOL_TRC_BT2020_12: return MP_CSP_TRC_BT_1886;
+ case AVCOL_TRC_IEC61966_2_1: return MP_CSP_TRC_SRGB;
+ case AVCOL_TRC_LINEAR: return MP_CSP_TRC_LINEAR;
+ case AVCOL_TRC_GAMMA22: return MP_CSP_TRC_GAMMA22;
+ case AVCOL_TRC_GAMMA28: return MP_CSP_TRC_GAMMA28;
+ default: return MP_CSP_TRC_AUTO;
+ }
+}
+
int mp_csp_to_avcol_spc(enum mp_csp colorspace)
{
switch (colorspace) {
case MP_CSP_BT_709: return AVCOL_SPC_BT709;
case MP_CSP_BT_601: return AVCOL_SPC_BT470BG;
-#if HAVE_AVCOL_SPC_BT2020
case MP_CSP_BT_2020_NC: return AVCOL_SPC_BT2020_NCL;
case MP_CSP_BT_2020_C: return AVCOL_SPC_BT2020_CL;
-#endif
case MP_CSP_SMPTE_240M: return AVCOL_SPC_SMPTE240M;
case MP_CSP_RGB: return AVCOL_SPC_RGB;
case MP_CSP_YCGCO: return AVCOL_SPC_YCOCG;
@@ -171,13 +203,25 @@ int mp_csp_prim_to_avcol_pri(enum mp_csp_prim prim)
case MP_CSP_PRIM_BT_601_525: return AVCOL_PRI_SMPTE170M;
case MP_CSP_PRIM_BT_601_625: return AVCOL_PRI_BT470BG;
case MP_CSP_PRIM_BT_709: return AVCOL_PRI_BT709;
-#if HAVE_AVCOL_SPC_BT2020
case MP_CSP_PRIM_BT_2020: return AVCOL_PRI_BT2020;
-#endif
+ case MP_CSP_PRIM_BT_470M: return AVCOL_PRI_BT470M;
default: return AVCOL_PRI_UNSPECIFIED;
}
}
+int mp_csp_trc_to_avcol_trc(enum mp_csp_trc trc)
+{
+ switch (trc) {
+ // We just call it BT.1886 since we're decoding, but it's still BT.709
+ case MP_CSP_TRC_BT_1886: return AVCOL_TRC_BT709;
+ case MP_CSP_TRC_SRGB: return AVCOL_TRC_IEC61966_2_1;
+ case MP_CSP_TRC_LINEAR: return AVCOL_TRC_LINEAR;
+ case MP_CSP_TRC_GAMMA22: return AVCOL_TRC_GAMMA22;
+ case MP_CSP_TRC_GAMMA28: return AVCOL_TRC_GAMMA28;
+ default: return AVCOL_TRC_UNSPECIFIED;
+ }
+}
+
enum mp_csp mp_csp_guess_colorspace(int width, int height)
{
return width >= 1280 || height > 576 ? MP_CSP_BT_709 : MP_CSP_BT_601;
@@ -276,16 +320,31 @@ static void mp_mul_matrix3x3(float a[3][3], float b[3][3])
struct mp_csp_primaries mp_get_csp_primaries(enum mp_csp_prim spc)
{
/*
- Values from: ITU-R Recommendations BT.601-7, BT.709-5, BT.2020-0
+ Values from: ITU-R Recommendations BT.470-6, BT.601-7, BT.709-5, BT.2020-0
+ https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.470-6-199811-S!!PDF-E.pdf
https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.601-7-201103-I!!PDF-E.pdf
https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.709-5-200204-I!!PDF-E.pdf
https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.2020-0-201208-I!!PDF-E.pdf
+
+ Other colorspaces from https://en.wikipedia.org/wiki/RGB_color_space#Specifications
*/
- static const struct mp_csp_col_xy d65 = {0.3127, 0.3290};
+ // CIE standard illuminant series
+ static const struct mp_csp_col_xy
+ d50 = {0.34577, 0.35850},
+ d65 = {0.31271, 0.32902},
+ c = {0.31006, 0.31616},
+ e = {1.0/3.0, 1.0/3.0};
switch (spc) {
+ case MP_CSP_PRIM_BT_470M:
+ return (struct mp_csp_primaries) {
+ .red = {0.670, 0.330},
+ .green = {0.210, 0.710},
+ .blue = {0.140, 0.080},
+ .white = c
+ };
case MP_CSP_PRIM_BT_601_525:
return (struct mp_csp_primaries) {
.red = {0.630, 0.340},
@@ -317,6 +376,34 @@ struct mp_csp_primaries mp_get_csp_primaries(enum mp_csp_prim spc)
.blue = {0.131, 0.046},
.white = d65
};
+ case MP_CSP_PRIM_APPLE:
+ return (struct mp_csp_primaries) {
+ .red = {0.625, 0.340},
+ .green = {0.280, 0.595},
+ .blue = {0.115, 0.070},
+ .white = d65
+ };
+ case MP_CSP_PRIM_ADOBE:
+ return (struct mp_csp_primaries) {
+ .red = {0.640, 0.330},
+ .green = {0.210, 0.710},
+ .blue = {0.150, 0.060},
+ .white = d65
+ };
+ case MP_CSP_PRIM_PRO_PHOTO:
+ return (struct mp_csp_primaries) {
+ .red = {0.7347, 0.2653},
+ .green = {0.1596, 0.8404},
+ .blue = {0.0366, 0.0001},
+ .white = d50
+ };
+ case MP_CSP_PRIM_CIE_1931:
+ return (struct mp_csp_primaries) {
+ .red = {0.7347, 0.2653},
+ .green = {0.2738, 0.7174},
+ .blue = {0.1666, 0.0089},
+ .white = e
+ };
default:
return (struct mp_csp_primaries) {{0}};
}
diff --git a/video/csputils.h b/video/csputils.h
index 029b321..85a97c2 100644
--- a/video/csputils.h
+++ b/video/csputils.h
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * 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
@@ -27,6 +26,8 @@
#include <stdbool.h>
#include <stdint.h>
+#include "options/m_option.h"
+
/* NOTE: the csp and levels AUTO values are converted to specific ones
* above vf/vo level. At least vf_scale relies on all valid settings being
* nonzero at vf/vo level.
@@ -45,8 +46,7 @@ enum mp_csp {
MP_CSP_COUNT
};
-// Any enum mp_csp value is a valid index (except MP_CSP_COUNT)
-extern const char *const mp_csp_names[MP_CSP_COUNT];
+extern const struct m_opt_choice_alternatives mp_csp_names[];
enum mp_csp_levels {
MP_CSP_LEVELS_AUTO,
@@ -55,8 +55,7 @@ enum mp_csp_levels {
MP_CSP_LEVELS_COUNT,
};
-// Any enum mp_csp_levels value is a valid index (except MP_CSP_LEVELS_COUNT)
-extern const char *const mp_csp_levels_names[MP_CSP_LEVELS_COUNT];
+extern const struct m_opt_choice_alternatives mp_csp_levels_names[];
enum mp_csp_prim {
MP_CSP_PRIM_AUTO,
@@ -64,18 +63,29 @@ enum mp_csp_prim {
MP_CSP_PRIM_BT_601_625,
MP_CSP_PRIM_BT_709,
MP_CSP_PRIM_BT_2020,
+ MP_CSP_PRIM_BT_470M,
+ MP_CSP_PRIM_APPLE,
+ MP_CSP_PRIM_ADOBE,
+ MP_CSP_PRIM_PRO_PHOTO,
+ MP_CSP_PRIM_CIE_1931,
MP_CSP_PRIM_COUNT
};
+extern const struct m_opt_choice_alternatives mp_csp_prim_names[];
+
enum mp_csp_trc {
- MP_CSP_TRC_NONE,
+ MP_CSP_TRC_AUTO,
MP_CSP_TRC_BT_1886,
MP_CSP_TRC_SRGB,
- MP_CSP_TRC_LINEAR
+ MP_CSP_TRC_LINEAR,
+ MP_CSP_TRC_GAMMA18,
+ MP_CSP_TRC_GAMMA22,
+ MP_CSP_TRC_GAMMA28,
+ MP_CSP_TRC_PRO_PHOTO,
+ MP_CSP_TRC_COUNT
};
-// Any enum mp_csp_prim value is a valid index (except MP_CSP_PRIM_COUNT)
-extern const char *const mp_csp_prim_names[MP_CSP_PRIM_COUNT];
+extern const struct m_opt_choice_alternatives mp_csp_trc_names[];
// These constants are based on the ICC specification (Table 23) and match
// up with the API of LittleCMS, which treats them as integers.
@@ -99,10 +109,10 @@ enum mp_stereo3d_mode {
MP_STEREO3D_COUNT = 13, // 12 is last valid mode
};
-extern const char *const mp_stereo3d_names[MP_STEREO3D_COUNT];
+extern const struct m_opt_choice_alternatives mp_stereo3d_names[];
+
+#define MP_STEREO3D_NAME(x) m_opt_choice_str(mp_stereo3d_names, x)
-#define MP_STEREO3D_NAME(x) \
- ((x) >= 0 && (x) < MP_STEREO3D_COUNT ? (char *)mp_stereo3d_names[(x)] : NULL)
#define MP_STEREO3D_NAME_DEF(x, def) \
(MP_STEREO3D_NAME(x) ? MP_STEREO3D_NAME(x) : (def))
@@ -143,7 +153,7 @@ enum mp_chroma_location {
MP_CHROMA_COUNT,
};
-extern const char *const mp_chroma_names[MP_CHROMA_COUNT];
+extern const struct m_opt_choice_alternatives mp_chroma_names[];
enum mp_csp_equalizer_param {
MP_CSP_EQ_BRIGHTNESS,
@@ -198,12 +208,16 @@ enum mp_csp_levels avcol_range_to_mp_csp_levels(int avrange);
enum mp_csp_prim avcol_pri_to_mp_csp_prim(int avpri);
+enum mp_csp_trc avcol_trc_to_mp_csp_trc(int avtrc);
+
int mp_csp_to_avcol_spc(enum mp_csp colorspace);
int mp_csp_levels_to_avcol_range(enum mp_csp_levels range);
int mp_csp_prim_to_avcol_pri(enum mp_csp_prim prim);
+int mp_csp_trc_to_avcol_trc(enum mp_csp_trc trc);
+
enum mp_csp mp_csp_guess_colorspace(int width, int height);
enum mp_csp_prim mp_csp_guess_primaries(int width, int height);
diff --git a/video/decode/dec_video.c b/video/decode/dec_video.c
index 5687abc..0c733b0 100644
--- a/video/decode/dec_video.c
+++ b/video/decode/dec_video.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
@@ -200,6 +199,12 @@ bool video_init_best_codec(struct dec_video *d_video, char* video_decoders)
d_video->header->codec ? d_video->header->codec : "<unknown>");
}
+ if (d_video->header->missing_timestamps) {
+ MP_WARN(d_video, "This stream has no timestamps!\n");
+ MP_WARN(d_video, "Making up playback time using %f FPS.\n", d_video->fps);
+ MP_WARN(d_video, "Seeking will probably fail badly.\n");
+ }
+
talloc_free(list);
return !!d_video->vd_driver;
}
@@ -351,7 +356,7 @@ struct mp_image *video_decode(struct dec_video *d_video,
pts = retrieve_sorted_pts(d_video, pts);
if (!opts->correct_pts || pts == MP_NOPTS_VALUE) {
- if (opts->correct_pts)
+ if (opts->correct_pts && !d_video->header->missing_timestamps)
MP_WARN(d_video, "No video PTS! Making something up.\n");
double frame_time = 1.0f / (d_video->fps > 0 ? d_video->fps : 25);
@@ -363,11 +368,6 @@ struct mp_image *video_decode(struct dec_video *d_video,
pts += frame_time;
}
- if (d_video->decoded_pts != MP_NOPTS_VALUE && pts <= d_video->decoded_pts) {
- MP_WARN(d_video, "Non-monotonic video pts: %f <= %f\n",
- pts, d_video->decoded_pts);
- }
-
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)
@@ -385,13 +385,6 @@ int video_reconfig_filters(struct dec_video *d_video,
struct mp_image_params p = *params;
struct sh_video *sh = d_video->header->video;
- MP_VERBOSE(d_video, "VIDEO: %dx%d %5.3f fps %5.1f kbps (%4.1f kB/s)\n",
- p.w, p.h, sh->fps, sh->bitrate / 1000.0,
- sh->bitrate / 8000.0);
-
- MP_VERBOSE(d_video, "VDec: vo config request - %d x %d (%s)\n",
- p.w, p.h, vo_format_name(p.imgfmt));
-
float decoder_aspect = p.d_w / (float)p.d_h;
if (d_video->initial_decoder_aspect == 0)
d_video->initial_decoder_aspect = decoder_aspect;
@@ -411,23 +404,9 @@ int video_reconfig_filters(struct dec_video *d_video,
if (force_aspect >= 0.0)
vf_set_dar(&p.d_w, &p.d_h, p.w, p.h, force_aspect);
- // Apply user overrides
- if (opts->requested_colorspace != MP_CSP_AUTO)
- p.colorspace = opts->requested_colorspace;
- if (opts->requested_input_range != MP_CSP_LEVELS_AUTO)
- p.colorlevels = opts->requested_input_range;
- p.outputlevels = opts->requested_output_range;
- if (opts->requested_primaries != MP_CSP_PRIM_AUTO)
- p.primaries = opts->requested_primaries;
-
// Detect colorspace from resolution.
- // Make sure the user-overrides are consistent (no RGB csp for YUV, etc.).
mp_image_params_guess_csp(&p);
- // Time to config libvo!
- MP_VERBOSE(d_video, "VO Config (%dx%d->%dx%d,0x%X)\n",
- p.w, p.h, p.d_w, p.d_h, p.imgfmt);
-
if (vf_reconfig(d_video->vfilter, params, &p) < 0) {
MP_FATAL(d_video, "Cannot initialize video filters.\n");
return -1;
diff --git a/video/decode/dec_video.h b/video/decode/dec_video.h
index 5298a56..5ab7213 100644
--- a/video/decode/dec_video.h
+++ b/video/decode/dec_video.h
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_DEC_VIDEO_H
@@ -72,7 +71,6 @@ struct dec_video {
// Final PTS of previously decoded image
double decoded_pts;
- int bitrate; // compressed bits/sec
float fps; // FPS from demuxer or from user override
float initial_decoder_aspect;
diff --git a/video/decode/dxva2.c b/video/decode/dxva2.c
index cbd08a7..ef77561 100644
--- a/video/decode/dxva2.c
+++ b/video/decode/dxva2.c
@@ -256,6 +256,9 @@ static void copy_nv12_fallback(struct mp_image *dest, uint8_t *src_bits,
mp_image_copy(dest, &buf);
}
+#pragma GCC push_options
+#pragma GCC target("sse4.1")
+
static void copy_nv12_gpu_sse4(struct mp_image *dest, uint8_t *src_bits,
unsigned src_pitch, unsigned surf_height)
{
@@ -291,6 +294,8 @@ static void copy_nv12_gpu_sse4(struct mp_image *dest, uint8_t *src_bits,
}
}
+#pragma GCC pop_options
+
static struct mp_image *dxva2_retrieve_image(struct lavc_ctx *s,
struct mp_image *img)
{
diff --git a/video/decode/lavc.h b/video/decode/lavc.h
index 85cf49a..eaddd4a 100644
--- a/video/decode/lavc.h
+++ b/video/decode/lavc.h
@@ -52,6 +52,8 @@ struct vd_lavc_hwdec {
// For horrible Intel shit-drivers only
void (*lock)(struct lavc_ctx *ctx);
void (*unlock)(struct lavc_ctx *ctx);
+ // Optional; if a special hardware decoder is needed (instead of "hwaccel").
+ const char *(*get_codec)(struct lavc_ctx *ctx);
};
enum {
diff --git a/video/decode/rpi.c b/video/decode/rpi.c
new file mode 100644
index 0000000..44a550f
--- /dev/null
+++ b/video/decode/rpi.c
@@ -0,0 +1,56 @@
+/*
+ * 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 "lavc.h"
+#include "common/common.h"
+
+static int init_decoder(struct lavc_ctx *ctx, int fmt, int w, int h)
+{
+ return 0;
+}
+
+static void uninit(struct lavc_ctx *ctx)
+{
+}
+
+static int init(struct lavc_ctx *ctx)
+{
+ return 0;
+}
+
+static int probe(struct vd_lavc_hwdec *hwdec, struct mp_hwdec_info *info,
+ const char *decoder)
+{
+ if (strcmp(decoder, "h264") != 0)
+ return HWDEC_ERR_NO_CODEC;
+ return 0;
+}
+
+static const char *get_codec(struct lavc_ctx *ctx)
+{
+ return "h264_mmal";
+}
+
+const struct vd_lavc_hwdec mp_vd_lavc_rpi = {
+ .type = HWDEC_RPI,
+ .image_format = IMGFMT_MMAL,
+ .probe = probe,
+ .init = init,
+ .uninit = uninit,
+ .init_decoder = init_decoder,
+ .get_codec = get_codec,
+};
diff --git a/video/decode/vd.h b/video/decode/vd.h
index 28ace3c..6f5016a 100644
--- a/video/decode/vd.h
+++ b/video/decode/vd.h
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_VD_H
diff --git a/video/decode/vd_lavc.c b/video/decode/vd_lavc.c
index 8c01538..ca9ab9e 100644
--- a/video/decode/vd_lavc.c
+++ b/video/decode/vd_lavc.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
@@ -121,8 +120,12 @@ const struct vd_lavc_hwdec mp_vd_lavc_vda;
const struct vd_lavc_hwdec mp_vd_lavc_vaapi;
const struct vd_lavc_hwdec mp_vd_lavc_vaapi_copy;
const struct vd_lavc_hwdec mp_vd_lavc_dxva2_copy;
+const struct vd_lavc_hwdec mp_vd_lavc_rpi;
static const struct vd_lavc_hwdec *const hwdec_list[] = {
+#if HAVE_RPI
+ &mp_vd_lavc_rpi,
+#endif
#if HAVE_VDPAU_HWACCEL
&mp_vd_lavc_vdpau,
#endif
@@ -296,13 +299,16 @@ static int init(struct dec_video *vd, const char *decoder)
hwdec = probe_hwdec(vd, false, vd->opts->hwdec_api, decoder);
}
} else {
- MP_VERBOSE(vd, "Not trying to use hardware decoding: "
- "codec %s is blacklisted by user.\n", decoder);
+ MP_VERBOSE(vd, "Not trying to use hardware decoding: codec %s is not "
+ "on whitelist, or does not support hardware acceleration.\n",
+ decoder);
}
if (hwdec) {
ctx->software_fallback_decoder = talloc_strdup(ctx, decoder);
- MP_INFO(vd, "Trying to use hardware decoding.\n");
+ if (hwdec->get_codec)
+ decoder = hwdec->get_codec(ctx);
+ MP_INFO(vd, "Using hardware decoding.\n");
} else if (vd->opts->hwdec_api != HWDEC_NONE) {
MP_INFO(vd, "Using software decoding.\n");
}
@@ -322,9 +328,6 @@ static int init(struct dec_video *vd, const char *decoder)
}
}
- if (ctx->avctx->bit_rate != 0)
- vd->bitrate = ctx->avctx->bit_rate;
-
return 1;
}
@@ -356,7 +359,6 @@ static void init_avctx(struct dec_video *vd, const char *decoder,
AVCodecContext *avctx = ctx->avctx;
if (!ctx->avctx)
goto error;
- avctx->bit_rate = 0;
avctx->opaque = vd;
avctx->codec_type = AVMEDIA_TYPE_VIDEO;
avctx->codec_id = lavc_codec->id;
@@ -369,7 +371,8 @@ static void init_avctx(struct dec_video *vd, const char *decoder,
if (ctx->hwdec) {
avctx->thread_count = 1;
avctx->get_format = get_format_hwdec;
- avctx->get_buffer2 = get_buffer2_hwdec;
+ if (ctx->hwdec->allocate_image)
+ avctx->get_buffer2 = get_buffer2_hwdec;
if (ctx->hwdec->init(ctx) < 0)
goto error;
} else {
@@ -477,6 +480,7 @@ static void update_image_params(struct dec_video *vd, AVFrame *frame,
.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,
diff --git a/video/decode/vdpau.c b/video/decode/vdpau.c
index 92ea401..83dfa0b 100644
--- a/video/decode/vdpau.c
+++ b/video/decode/vdpau.c
@@ -29,6 +29,7 @@
#include "video/fmt-conversion.h"
#include "video/vdpau.h"
#include "video/hwdec.h"
+#include "video/decode/dec_video.h"
struct priv {
struct mp_log *log;
@@ -148,6 +149,24 @@ static void uninit(struct lavc_ctx *ctx)
ctx->hwdec_priv = NULL;
}
+#if LIBAVCODEC_VERSION_MICRO >= 100
+static int render2(struct AVCodecContext *avctx, struct AVFrame *frame,
+ const VdpPictureInfo *pic_info, uint32_t buffers_used,
+ const VdpBitstreamBuffer *buffers)
+{
+ struct dec_video *vd = avctx->opaque;
+ struct lavc_ctx *ctx = vd->priv;
+ struct priv *p = ctx->hwdec_priv;
+ VdpVideoSurface surf = (uintptr_t)frame->data[3];
+ VdpStatus status;
+
+ status = p->vdp->decoder_render(p->context->decoder, surf, pic_info,
+ buffers_used, buffers);
+
+ return status;
+}
+#endif
+
static int init(struct lavc_ctx *ctx)
{
struct priv *p = talloc_ptrtype(NULL, p);
@@ -157,16 +176,16 @@ static int init(struct lavc_ctx *ctx)
};
ctx->hwdec_priv = p;
-#if HAVE_AVCODEC_VDPAU_ALLOC_CONTEXT
p->context = av_vdpau_alloc_context();
-#else
- p->context = av_mallocz(sizeof(AVVDPAUContext));
-#endif
if (!p->context)
goto error;
p->vdp = &p->mpvdp->vdp;
+#if LIBAVCODEC_VERSION_MICRO >= 100
+ p->context->render2 = render2;
+#else
p->context->render = p->vdp->decoder_render;
+#endif
p->context->decoder = VDP_INVALID_HANDLE;
if (mp_vdpau_handle_preemption(p->mpvdp, &p->preemption_counter) < 1)
diff --git a/video/filter/vf.c b/video/filter/vf.c
index b308a32..4f9f43f 100644
--- a/video/filter/vf.c
+++ b/video/filter/vf.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
@@ -39,8 +38,6 @@
#include "video/mp_image_pool.h"
#include "vf.h"
-#include "video/memcpy_pic.h"
-
extern const vf_info_t vf_info_crop;
extern const vf_info_t vf_info_expand;
extern const vf_info_t vf_info_scale;
@@ -57,7 +54,6 @@ extern const vf_info_t vf_info_hqdn3d;
extern const vf_info_t vf_info_dsize;
extern const vf_info_t vf_info_pullup;
extern const vf_info_t vf_info_delogo;
-extern const vf_info_t vf_info_screenshot;
extern const vf_info_t vf_info_sub;
extern const vf_info_t vf_info_yadif;
extern const vf_info_t vf_info_stereo3d;
@@ -77,9 +73,9 @@ static const vf_info_t *const filter_list[] = {
&vf_info_format,
&vf_info_noformat,
&vf_info_flip,
- &vf_info_mirror,
#if HAVE_LIBAVFILTER
+ &vf_info_mirror,
&vf_info_lavfi,
&vf_info_rotate,
&vf_info_noise,
@@ -91,8 +87,6 @@ static const vf_info_t *const filter_list[] = {
&vf_info_yadif,
#endif
- &vf_info_screenshot,
-
&vf_info_eq,
&vf_info_dsize,
&vf_info_sub,
diff --git a/video/filter/vf.h b/video/filter/vf.h
index 23791e6..7683552 100644
--- a/video/filter/vf.h
+++ b/video/filter/vf.h
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_VF_H
@@ -145,7 +144,6 @@ enum vf_ctrl {
VFCTRL_SEEK_RESET = 1, // reset on picture and PTS discontinuities
VFCTRL_SET_EQUALIZER, // set color options (brightness,contrast etc)
VFCTRL_GET_EQUALIZER, // get color options (brightness,contrast etc)
- VFCTRL_SCREENSHOT, // Take screenshot, arg is mp_image**
VFCTRL_INIT_OSD, // Filter OSD renderer present?
VFCTRL_SET_DEINTERLACE, // Set deinterlacing status
VFCTRL_GET_DEINTERLACE, // Get deinterlacing status
diff --git a/video/filter/vf_crop.c b/video/filter/vf_crop.c
index 23b44e0..02787b8 100644
--- a/video/filter/vf_crop.c
+++ b/video/filter/vf_crop.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
@@ -59,7 +58,7 @@ static int config(struct vf_instance *vf,
// check:
if(vf->priv->crop_w+vf->priv->crop_x>width ||
vf->priv->crop_h+vf->priv->crop_y>height){
- MP_WARN(vf, "[CROP] Bad position/width/height - cropped area outside of the original!\n");
+ MP_WARN(vf, "Bad position/width/height - cropped area outside of the original!\n");
return 0;
}
vf_rescale_dsize(&d_width, &d_height, width, height,
@@ -86,11 +85,6 @@ static int vf_open(vf_instance_t *vf){
vf->config=config;
vf->filter=filter;
vf->query_format=query_format;
- MP_INFO(vf, "Crop: %d x %d, %d ; %d\n",
- vf->priv->crop_w,
- vf->priv->crop_h,
- vf->priv->crop_x,
- vf->priv->crop_y);
return 1;
}
diff --git a/video/filter/vf_delogo.c b/video/filter/vf_delogo.c
index 8fdbd77..899a972 100644
--- a/video/filter/vf_delogo.c
+++ b/video/filter/vf_delogo.c
@@ -1,21 +1,20 @@
/*
* Copyright (C) 2002 Jindrich Makovicka <makovick@gmail.com>
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
diff --git a/video/filter/vf_dlopen.c b/video/filter/vf_dlopen.c
index 00e6245..3ba4a0d 100644
--- a/video/filter/vf_dlopen.c
+++ b/video/filter/vf_dlopen.c
@@ -1,18 +1,18 @@
/*
- * This file is part of mplayer.
+ * This file is part of mpv.
*
- * mplayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * mplayer is distributed in the hope that it will be useful,
+ * 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 mplayer. If not, see <http://www.gnu.org/licenses/>.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
@@ -82,11 +82,11 @@ static void set_imgprop(struct vf_dlopen_picdata *out, const mp_image_t *mpi)
out->plane[i] = mpi->planes[i];
out->planestride[i] = mpi->stride[i];
out->planewidth[i] =
- i ? (/*mpi->chroma_width*/ mpi->w >> mpi->chroma_x_shift) : mpi->w;
+ i ? (/*mpi->chroma_width*/ mpi->w >> mpi->fmt.chroma_xs) : mpi->w;
out->planeheight[i] =
- i ? (/*mpi->chroma_height*/ mpi->h >> mpi->chroma_y_shift) : mpi->h;
- out->planexshift[i] = i ? mpi->chroma_x_shift : 0;
- out->planeyshift[i] = i ? mpi->chroma_y_shift : 0;
+ i ? (/*mpi->chroma_height*/ mpi->h >> mpi->fmt.chroma_ys) : mpi->h;
+ out->planexshift[i] = i ? mpi->fmt.chroma_xs : 0;
+ out->planeyshift[i] = i ? mpi->fmt.chroma_ys : 0;
}
}
diff --git a/video/filter/vf_dsize.c b/video/filter/vf_dsize.c
index 158b182..476ec27 100644
--- a/video/filter/vf_dsize.c
+++ b/video/filter/vf_dsize.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
diff --git a/video/filter/vf_eq.c b/video/filter/vf_eq.c
index 3381f24..33b340e 100644
--- a/video/filter/vf_eq.c
+++ b/video/filter/vf_eq.c
@@ -6,21 +6,20 @@
* Richard Felker (original MMX contrast/brightness code (vf_eq.c))
* Michael Niedermayer <michalni@gmx.at> (LUT16)
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
@@ -191,8 +190,8 @@ static struct mp_image *filter(struct vf_instance *vf, struct mp_image *src)
if ((eq2->buf_w[0] != src->w) || (eq2->buf_h[0] != src->h)) {
eq2->buf_w[0] = src->w;
eq2->buf_h[0] = src->h;
- eq2->buf_w[1] = eq2->buf_w[2] = src->w >> src->chroma_x_shift;
- eq2->buf_h[1] = eq2->buf_h[2] = src->h >> src->chroma_y_shift;
+ eq2->buf_w[1] = eq2->buf_w[2] = src->w >> src->fmt.chroma_xs;
+ eq2->buf_h[1] = eq2->buf_h[2] = src->h >> src->fmt.chroma_ys;
img_n = eq2->buf_w[0]*eq2->buf_h[0];
if(src->num_planes>1){
img_c = eq2->buf_w[1]*eq2->buf_h[1];
diff --git a/video/filter/vf_expand.c b/video/filter/vf_expand.c
index 4b8e89c..6d5b694 100644
--- a/video/filter/vf_expand.c
+++ b/video/filter/vf_expand.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
@@ -31,8 +30,6 @@
#include "video/mp_image.h"
#include "vf.h"
-#include "video/memcpy_pic.h"
-
#include "options/m_option.h"
static struct vf_priv_s {
@@ -66,18 +63,12 @@ static int config(struct vf_instance *vf,
vf->priv->exp_y = vf->priv->cfg_exp_y;
vf->priv->exp_w = vf->priv->cfg_exp_w;
vf->priv->exp_h = vf->priv->cfg_exp_h;
- // calculate the missing parameters:
-#if 0
- if(vf->priv->exp_w<width) vf->priv->exp_w=width;
- if(vf->priv->exp_h<height) vf->priv->exp_h=height;
-#else
if ( vf->priv->exp_w == -1 ) vf->priv->exp_w=width;
else if (vf->priv->exp_w < -1 ) vf->priv->exp_w=width - vf->priv->exp_w;
else if ( vf->priv->exp_w<width ) vf->priv->exp_w=width;
if ( vf->priv->exp_h == -1 ) vf->priv->exp_h=height;
else if ( vf->priv->exp_h < -1 ) vf->priv->exp_h=height - vf->priv->exp_h;
else if( vf->priv->exp_h<height ) vf->priv->exp_h=height;
-#endif
if (vf->priv->aspect) {
float adjusted_aspect = vf->priv->aspect;
adjusted_aspect *= ((double)width/height) / ((double)d_width/d_height);
@@ -115,8 +106,10 @@ static struct mp_image *filter(struct vf_instance *vf, struct mp_image *mpi)
return mpi;
struct mp_image *dmpi = vf_alloc_out_image(vf);
- if (!dmpi)
+ if (!dmpi) {
+ talloc_free(mpi);
return NULL;
+ }
mp_image_copy_attributes(dmpi, mpi);
struct mp_image cropped = *dmpi;
@@ -150,13 +143,6 @@ static int vf_open(vf_instance_t *vf){
vf->config=config;
vf->query_format=query_format;
vf->filter=filter;
- MP_INFO(vf, "Expand: %d x %d, %d ; %d, aspect: %f, round: %d\n",
- vf->priv->cfg_exp_w,
- vf->priv->cfg_exp_h,
- vf->priv->cfg_exp_x,
- vf->priv->cfg_exp_y,
- vf->priv->aspect,
- vf->priv->round);
return 1;
}
diff --git a/video/filter/vf_flip.c b/video/filter/vf_flip.c
index 2dde098..bec7e9b 100644
--- a/video/filter/vf_flip.c
+++ b/video/filter/vf_flip.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
diff --git a/video/filter/vf_format.c b/video/filter/vf_format.c
index 4342aa5..881f102 100644
--- a/video/filter/vf_format.c
+++ b/video/filter/vf_format.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
@@ -21,8 +20,8 @@
#include <string.h>
#include <inttypes.h>
-#include "config.h"
#include "common/msg.h"
+#include "common/common.h"
#include "video/img_format.h"
#include "video/mp_image.h"
@@ -30,45 +29,105 @@
#include "options/m_option.h"
-static struct vf_priv_s {
+struct vf_priv_s {
int fmt;
int outfmt;
-} const vf_priv_dflt = {
- IMGFMT_YUYV,
- 0
+ int colormatrix;
+ int colorlevels;
+ int outputlevels;
+ int primaries;
+ int gamma;
+ int chroma_location;
+ int stereo_in;
+ int stereo_out;
+ int rotate;
+ int dw, dh;
+ double dar;
};
-//===========================================================================//
+static bool is_compatible(int fmt1, int fmt2)
+{
+ struct mp_imgfmt_desc d1 = mp_imgfmt_get_desc(fmt1);
+ struct mp_imgfmt_desc d2 = mp_imgfmt_get_desc(fmt2);
+ if (d1.num_planes < d2.num_planes)
+ return false;
+ if (!(d1.flags & MP_IMGFLAG_BYTE_ALIGNED) ||
+ !(d2.flags & MP_IMGFLAG_BYTE_ALIGNED))
+ return false;
+ for (int n = 0; n < MPMIN(d1.num_planes, d2.num_planes); n++) {
+ if (d1.bytes[n] != d2.bytes[n])
+ return false;
+ if (d1.xs[n] != d2.xs[n] || d1.ys[n] != d2.ys[n])
+ return false;
+ }
+ return true;
+}
-static int query_format(struct vf_instance *vf, unsigned int fmt){
- if(fmt==vf->priv->fmt) {
- if (vf->priv->outfmt)
+static int query_format(struct vf_instance *vf, unsigned int fmt)
+{
+ if (fmt == vf->priv->fmt || !vf->priv->fmt) {
+ if (vf->priv->outfmt) {
+ if (!is_compatible(fmt, vf->priv->outfmt))
+ return 0;
fmt = vf->priv->outfmt;
- return vf_next_query_format(vf,fmt);
+ }
+ return vf_next_query_format(vf, fmt);
}
return 0;
}
-static int config(struct vf_instance *vf, int width, int height,
- int d_width, int d_height,
- unsigned flags, unsigned outfmt){
- return vf_next_config(vf, width, height, d_width, d_height, flags, vf->priv->outfmt);
+static int reconfig(struct vf_instance *vf, struct mp_image_params *in,
+ struct mp_image_params *out)
+{
+ struct vf_priv_s *p = vf->priv;
+
+ *out = *in;
+
+ if (p->outfmt)
+ out->imgfmt = p->outfmt;
+ if (p->colormatrix)
+ out->colorspace = p->colormatrix;
+ if (p->colorlevels)
+ out->colorlevels = p->colorlevels;
+ if (p->outputlevels)
+ out->outputlevels = p->outputlevels;
+ if (p->primaries)
+ out->primaries = p->primaries;
+ if (p->gamma)
+ out->gamma = p->gamma;
+ if (p->chroma_location)
+ out->chroma_location = p->chroma_location;
+ if (p->stereo_in)
+ out->stereo_in = p->stereo_in;
+ if (p->stereo_out)
+ out->stereo_out = p->stereo_out;
+ if (p->rotate >= 0)
+ out->rotate = p->rotate;
+ if (p->dw > 0)
+ out->d_w = p->dw;
+ if (p->dh > 0)
+ out->d_h = p->dh;
+ if (p->dar > 0)
+ vf_set_dar(&out->d_w, &out->d_h, out->w, out->h, p->dar);
+
+ // Make sure the user-overrides are consistent (no RGB csp for YUV, etc.).
+ mp_image_params_guess_csp(out);
+
+ return 0;
}
static struct mp_image *filter(struct vf_instance *vf, struct mp_image *mpi)
{
- // As documented in the manpage, the user can easily provoke crashes
if (vf->priv->outfmt)
mp_image_setfmt(mpi, vf->priv->outfmt);
return mpi;
}
-static int vf_open(vf_instance_t *vf){
- vf->query_format=query_format;
- if (vf->priv->outfmt) {
- vf->config=config;
- vf->filter=filter;
- }
+static int vf_open(vf_instance_t *vf)
+{
+ vf->query_format = query_format;
+ vf->reconfig = reconfig;
+ vf->filter = filter;
return 1;
}
@@ -76,6 +135,18 @@ static int vf_open(vf_instance_t *vf){
static const m_option_t vf_opts_fields[] = {
OPT_IMAGEFORMAT("fmt", fmt, 0),
OPT_IMAGEFORMAT("outfmt", outfmt, 0),
+ OPT_CHOICE_C("colormatrix", colormatrix, 0, mp_csp_names),
+ OPT_CHOICE_C("colorlevels", colorlevels, 0, mp_csp_levels_names),
+ OPT_CHOICE_C("outputlevels", outputlevels, 0, mp_csp_levels_names),
+ OPT_CHOICE_C("primaries", primaries, 0, mp_csp_prim_names),
+ OPT_CHOICE_C("gamma", gamma, 0, mp_csp_trc_names),
+ OPT_CHOICE_C("chroma-location", chroma_location, 0, mp_chroma_names),
+ OPT_CHOICE_C("stereo-in", stereo_in, 0, mp_stereo3d_names),
+ OPT_CHOICE_C("stereo-out", stereo_out, 0, mp_stereo3d_names),
+ OPT_INTRANGE("rotate", rotate, 0, -1, 359),
+ OPT_INT("dw", dw, 0),
+ OPT_INT("dh", dh, 0),
+ OPT_DOUBLE("dar", dar, 0),
{0}
};
@@ -84,8 +155,8 @@ const vf_info_t vf_info_format = {
.name = "format",
.open = vf_open,
.priv_size = sizeof(struct vf_priv_s),
- .priv_defaults = &vf_priv_dflt,
.options = vf_opts_fields,
+ .priv_defaults = &(const struct vf_priv_s){
+ .rotate = -1,
+ },
};
-
-//===========================================================================//
diff --git a/video/filter/vf_gradfun.c b/video/filter/vf_gradfun.c
index 4831bcc..78e1f4e 100644
--- a/video/filter/vf_gradfun.c
+++ b/video/filter/vf_gradfun.c
@@ -1,21 +1,20 @@
/*
* Copyright (C) 2009 Loren Merritt <lorenm@u.washignton.edu>
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
diff --git a/video/filter/vf_hqdn3d.c b/video/filter/vf_hqdn3d.c
index 063bf44..e29e98b 100644
--- a/video/filter/vf_hqdn3d.c
+++ b/video/filter/vf_hqdn3d.c
@@ -1,21 +1,20 @@
/*
* Copyright (C) 2003 Daniel Moreno <comac@comac.darktech.org>
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
diff --git a/video/filter/vf_lavfi.c b/video/filter/vf_lavfi.c
index cb4c348..850280f 100644
--- a/video/filter/vf_lavfi.c
+++ b/video/filter/vf_lavfi.c
@@ -452,7 +452,6 @@ const vf_info_t vf_info_lavfi = {
// The following code is for the old filters wrapper code.
struct vf_lw_opts {
- int enable;
int64_t sws_flags;
char **avopts;
};
@@ -461,13 +460,11 @@ struct vf_lw_opts {
#define OPT_BASE_STRUCT struct vf_lw_opts
const struct m_sub_options vf_lw_conf = {
.opts = (const m_option_t[]) {
- OPT_FLAG("lavfi", enable, 0),
OPT_INT64("lavfi-sws-flags", sws_flags, 0),
OPT_KEYVALUELIST("lavfi-o", avopts, 0),
{0}
},
.defaults = &(const struct vf_lw_opts){
- .enable = 1,
.sws_flags = SWS_BICUBIC,
},
.size = sizeof(struct vf_lw_opts),
@@ -493,7 +490,7 @@ int vf_lw_set_graph(struct vf_instance *vf, struct vf_lw_opts *lavfi_opts,
{
if (!lavfi_opts)
lavfi_opts = (struct vf_lw_opts *)vf_lw_conf.defaults;
- if (!lavfi_opts->enable || (filter && !have_filter(filter)))
+ if (filter && !have_filter(filter))
return -1;
MP_VERBOSE(vf, "Using libavfilter for '%s'\n", vf->info->name);
void *old_priv = vf->priv;
diff --git a/video/filter/vf_mirror.c b/video/filter/vf_mirror.c
index 90316b2..342c6ab 100644
--- a/video/filter/vf_mirror.c
+++ b/video/filter/vf_mirror.c
@@ -1,107 +1,28 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <inttypes.h>
+#include <stddef.h>
-#include "config.h"
-#include "common/msg.h"
-
-#include "video/img_format.h"
-#include "video/mp_image.h"
#include "vf.h"
+#include "vf_lavfi.h"
-static int config(struct vf_instance *vf, int width, int height,
- int d_width, int d_height,
- unsigned int flags, unsigned int fmt)
-{
- struct mp_imgfmt_desc desc = mp_imgfmt_get_desc(fmt);
- int a_w = MP_ALIGN_DOWN(width, desc.align_x);
- vf_rescale_dsize(&d_width, &d_height, width, height, a_w, height);
- return vf_next_config(vf, a_w, height, d_width, d_height, flags, fmt);
-}
-
-static inline void mirror_4_m(uint8_t *dst, uint8_t *src, int p,
- int c0, int c1, int c2, int c3)
-{
- for (int x = 0; x < p; x++) {
- dst[x * 4 + 0] = src[(p - x - 1) * 4 + c0];
- dst[x * 4 + 1] = src[(p - x - 1) * 4 + c1];
- dst[x * 4 + 2] = src[(p - x - 1) * 4 + c2];
- dst[x * 4 + 3] = src[(p - x - 1) * 4 + c3];
- }
-}
-
-static inline void mirror(uint8_t *dst, uint8_t *src, int bp, int w)
-{
- for (int x = 0; x < w; x++)
- memcpy(dst + x * bp, src + (w - x - 1) * bp, bp);
-}
-
-static struct mp_image *filter(struct vf_instance *vf, struct mp_image *mpi)
+static int vf_open(vf_instance_t *vf)
{
- mp_image_t *dmpi = vf_alloc_out_image(vf);
- if (!dmpi)
- return NULL;
- mp_image_copy_attributes(dmpi, mpi);
-
- for (int p = 0; p < mpi->num_planes; p++) {
- for (int y = 0; y < mpi->plane_h[p]; y++) {
- void *p_src = mpi->planes[p] + mpi->stride[p] * y;
- void *p_dst = dmpi->planes[p] + dmpi->stride[p] * y;
- int w = dmpi->plane_w[p];
- if (mpi->imgfmt == IMGFMT_YUYV) {
- mirror_4_m(p_dst, p_src, w / 2, 2, 1, 0, 3);
- } else if (mpi->imgfmt == IMGFMT_UYVY) {
- mirror_4_m(p_dst, p_src, w / 2, 0, 3, 2, 1);
- } else {
- // make the compiler unroll the memcpy in mirror()
- switch (mpi->fmt.bytes[p]) {
- case 1: mirror(p_dst, p_src, 1, w); break;
- case 2: mirror(p_dst, p_src, 2, w); break;
- case 3: mirror(p_dst, p_src, 3, w); break;
- case 4: mirror(p_dst, p_src, 4, w); break;
- default:
- mirror(p_dst, p_src, mpi->fmt.bytes[p], w);
- }
- }
- }
- }
-
- talloc_free(mpi);
- return dmpi;
-}
-
-static int query_format(struct vf_instance *vf, unsigned int fmt)
-{
- struct mp_imgfmt_desc desc = mp_imgfmt_get_desc(fmt);
- if (!(desc.flags & MP_IMGFLAG_BYTE_ALIGNED))
- return 0;
- return vf_next_query_format(vf, fmt);
-}
-
-static int vf_open(vf_instance_t *vf){
- vf->config=config;
- vf->filter=filter;
- vf->query_format=query_format;
- return 1;
+ return vf_lw_set_graph(vf, NULL, NULL, "hflip") >= 0;
}
const vf_info_t vf_info_mirror = {
@@ -109,5 +30,3 @@ const vf_info_t vf_info_mirror = {
.name = "mirror",
.open = vf_open,
};
-
-//===========================================================================//
diff --git a/video/filter/vf_noformat.c b/video/filter/vf_noformat.c
index 504b6a0..7fcc3de 100644
--- a/video/filter/vf_noformat.c
+++ b/video/filter/vf_noformat.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
diff --git a/video/filter/vf_noise.c b/video/filter/vf_noise.c
index c350f46..7945878 100644
--- a/video/filter/vf_noise.c
+++ b/video/filter/vf_noise.c
@@ -1,21 +1,20 @@
/*
* Copyright (C) 2002 Michael Niedermayer <michaelni@gmx.at>
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
diff --git a/video/filter/vf_pullup.c b/video/filter/vf_pullup.c
index 86aaf54..39907da 100644
--- a/video/filter/vf_pullup.c
+++ b/video/filter/vf_pullup.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
diff --git a/video/filter/vf_rotate.c b/video/filter/vf_rotate.c
index 2df8f94..60e52a0 100644
--- a/video/filter/vf_rotate.c
+++ b/video/filter/vf_rotate.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
diff --git a/video/filter/vf_scale.c b/video/filter/vf_scale.c
index 8b513d6..29d7376 100644
--- a/video/filter/vf_scale.c
+++ b/video/filter/vf_scale.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
diff --git a/video/filter/vf_screenshot.c b/video/filter/vf_screenshot.c
deleted file mode 100644
index 562a1d0..0000000
--- a/video/filter/vf_screenshot.c
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * 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 "config.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <inttypes.h>
-
-#include "talloc.h"
-
-#include "video/img_format.h"
-#include "video/mp_image.h"
-#include "video/sws_utils.h"
-#include "video/out/vo.h"
-
-#include "vf.h"
-
-struct vf_priv_s {
- struct mp_image *current;
-};
-
-static struct mp_image *filter(struct vf_instance *vf, struct mp_image *mpi)
-{
- mp_image_unrefp(&vf->priv->current);
- vf->priv->current = talloc_steal(vf, mp_image_new_ref(mpi));
- return mpi;
-}
-
-static int control (vf_instance_t *vf, int request, void *data)
-{
- if (request == VFCTRL_SCREENSHOT && vf->priv->current) {
- *(struct mp_image **)data = mp_image_new_ref(vf->priv->current);
- return CONTROL_TRUE;
- }
- return CONTROL_UNKNOWN;
-}
-
-static int query_format(struct vf_instance *vf, unsigned int fmt)
-{
- if (mp_sws_supported_format(fmt))
- return vf_next_query_format(vf, fmt);
- return 0;
-}
-
-static int vf_open(vf_instance_t *vf)
-{
- vf->control = control;
- vf->filter = filter;
- vf->query_format = query_format;
- vf->priv = talloc_zero(vf, struct vf_priv_s);
- return 1;
-}
-
-const vf_info_t vf_info_screenshot = {
- .description = "screenshot to file",
- .name = "screenshot",
- .open = vf_open,
-};
diff --git a/video/filter/vf_stereo3d.c b/video/filter/vf_stereo3d.c
index 886483c..acc6629 100644
--- a/video/filter/vf_stereo3d.c
+++ b/video/filter/vf_stereo3d.c
@@ -1,21 +1,20 @@
/*
* Copyright (C) 2010 Gordon Schmidt <gordon.schmidt <at> s2000.tu-chemnitz.de>
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
//==includes==//
@@ -35,8 +34,6 @@
#include "vf.h"
#include "options/m_option.h"
-#include "video/memcpy_pic.h"
-
#include "vf_lavfi.h"
//==types==//
@@ -322,20 +319,18 @@ static struct mp_image *filter(struct vf_instance *vf, struct mp_image *mpi)
case ABOVE_BELOW_2_RL:
case INTERLEAVE_ROWS_LR:
case INTERLEAVE_ROWS_RL:
- memcpy_pic2(dmpi->planes[0] + out_off_left,
+ memcpy_pic(dmpi->planes[0] + out_off_left,
mpi->planes[0] + in_off_left,
3 * vf->priv->width,
vf->priv->height,
dmpi->stride[0] * vf->priv->row_step,
- mpi->stride[0] * vf->priv->row_step,
- vf->priv->row_step != 1);
- memcpy_pic2(dmpi->planes[0] + out_off_right,
+ mpi->stride[0] * vf->priv->row_step);
+ memcpy_pic(dmpi->planes[0] + out_off_right,
mpi->planes[0] + in_off_right,
3 * vf->priv->width,
vf->priv->height,
dmpi->stride[0] * vf->priv->row_step,
- mpi->stride[0] * vf->priv->row_step,
- vf->priv->row_step != 1);
+ mpi->stride[0] * vf->priv->row_step);
break;
case MONO_L:
case MONO_R:
@@ -472,7 +467,7 @@ static const char *rev_map_name(int val)
}
// Extremely stupid; can be dropped when the internal filter is dropped,
-// and OPT_VID_STEREO_MODE() can be used instead.
+// and OPT_CHOICE_C() can be used instead.
static int opt_to_stereo3dmode(int val)
{
// Find x for rev_map_name(val) == MP_STEREO3D_NAME(x)
diff --git a/video/filter/vf_sub.c b/video/filter/vf_sub.c
index b30448c..775d944 100644
--- a/video/filter/vf_sub.c
+++ b/video/filter/vf_sub.c
@@ -1,21 +1,20 @@
/*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
@@ -38,7 +37,6 @@
#include "sub/dec_sub.h"
#include "video/sws_utils.h"
-#include "video/memcpy_pic.h"
#include "options/m_option.h"
diff --git a/video/filter/vf_unsharp.c b/video/filter/vf_unsharp.c
index 81ffbb7..dfee945 100644
--- a/video/filter/vf_unsharp.c
+++ b/video/filter/vf_unsharp.c
@@ -1,21 +1,20 @@
/*
* Copyright (C) 2002 Remi Guyomarch <rguyom@pobox.com>
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
diff --git a/video/filter/vf_vapoursynth.c b/video/filter/vf_vapoursynth.c
index 8ae8add..01cacbf 100644
--- a/video/filter/vf_vapoursynth.c
+++ b/video/filter/vf_vapoursynth.c
@@ -20,6 +20,7 @@
#include <string.h>
#include <inttypes.h>
#include <pthread.h>
+#include <limits.h>
#include <assert.h>
#include <VapourSynth.h>
@@ -72,6 +73,8 @@ struct vf_priv_s {
int max_requests; // upper bound for requested[] array
bool failed; // frame callback returned with an error
bool shutdown; // ask node to return
+ bool eof; // drain remaining data
+ int64_t frames_sent;
bool initializing; // filters are being built
bool in_node_active; // node might still be called
@@ -165,10 +168,10 @@ static void copy_mp_to_vs_frame_props_map(struct vf_priv_s *p, VSMap *map,
}
if (pict_type)
p->vsapi->propSetData(map, "_PictType", &pict_type, 1, 0);
- p->vsapi->propSetInt(map, "_FieldBased",
- !!(img->fields & MP_IMGFIELD_INTERLACED), 0);
- p->vsapi->propSetInt(map, "_Field",
- !!(img->fields & MP_IMGFIELD_TOP_FIRST), 0);
+ int field = 0;
+ if (img->fields & MP_IMGFIELD_INTERLACED)
+ field = img->fields & MP_IMGFIELD_TOP_FIRST ? 2 : 1;
+ p->vsapi->propSetInt(map, "_FieldBased", field, 0);
}
static int set_vs_frame_props(struct vf_priv_s *p, VSFrameRef *frame,
@@ -315,6 +318,7 @@ static int filter_ext(struct vf_instance *vf, struct mp_image *mpi)
{
struct vf_priv_s *p = vf->priv;
int ret = 0;
+ bool eof = !mpi;
if (!p->out_node) {
talloc_free(mpi);
@@ -323,13 +327,12 @@ static int filter_ext(struct vf_instance *vf, struct mp_image *mpi)
MPSWAP(struct mp_image *, p->next_image, mpi);
- if (!mpi)
- return 0;
-
- // Turn PTS into frame duration (the pts field is abused for storing it)
- if (p->out_pts == MP_NOPTS_VALUE)
- p->out_pts = mpi->pts;
- mpi->pts = p->next_image ? p->next_image->pts - mpi->pts : 0;
+ if (mpi) {
+ // Turn PTS into frame duration (the pts field is abused for storing it)
+ if (p->out_pts == MP_NOPTS_VALUE)
+ p->out_pts = mpi->pts;
+ mpi->pts = p->next_image ? p->next_image->pts - mpi->pts : 0;
+ }
// Try to get new frames until we get rid of the input mpi.
pthread_mutex_lock(&p->lock);
@@ -344,6 +347,7 @@ static int filter_ext(struct vf_instance *vf, struct mp_image *mpi)
// Make the input frame available to infiltGetFrame().
if (mpi && locked_need_input(vf)) {
+ p->frames_sent++;
p->buffered[p->num_buffered++] = talloc_steal(p->buffered, mpi);
mpi = NULL;
pthread_cond_broadcast(&p->wakeup);
@@ -351,8 +355,14 @@ static int filter_ext(struct vf_instance *vf, struct mp_image *mpi)
locked_read_output(vf);
- if (!mpi)
+ if (!mpi) {
+ if (eof && p->frames_sent && !p->eof) {
+ MP_VERBOSE(vf, "input EOF\n");
+ p->eof = true;
+ pthread_cond_broadcast(&p->wakeup);
+ }
break;
+ }
pthread_cond_wait(&p->wakeup, &p->lock);
}
pthread_mutex_unlock(&p->lock);
@@ -374,7 +384,7 @@ static int filter_out(struct vf_instance *vf)
break;
// If the VS filter wants new input, there's no guarantee that we can
// actually finish any time soon without feeding new input.
- if (locked_need_input(vf))
+ if (!p->eof && locked_need_input(vf))
break;
pthread_cond_wait(&p->wakeup, &p->lock);
}
@@ -398,6 +408,10 @@ static void VS_CC infiltInit(VSMap *in, VSMap *out, void **instanceData,
{
struct vf_instance *vf = *instanceData;
struct vf_priv_s *p = vf->priv;
+ // The number of frames of our input node is obviously unknown. The user
+ // could for example seek any time, randomly "ending" the clip.
+ // This specific value was suggested by the VapourSynth developer.
+ int enough_for_everyone = INT_MAX / 16;
// Note: this is called from createFilter, so no need for locking.
@@ -405,6 +419,7 @@ static void VS_CC infiltInit(VSMap *in, VSMap *out, void **instanceData,
.format = p->vsapi->getFormatPreset(mp_to_vs(p->fmt_in.imgfmt), p->vscore),
.width = p->fmt_in.w,
.height = p->fmt_in.h,
+ .numFrames = enough_for_everyone,
};
if (!fmt.format) {
p->vsapi->setError(out, "Unsupported input format.\n");
@@ -468,6 +483,14 @@ static const VSFrameRef *VS_CC infiltGetFrame(int frameno, int activationReason,
continue;
}
}
+ if (frameno >= p->in_frameno + p->num_buffered) {
+ // If we think EOF was reached, don't wait for new input, and assume
+ // the VS filter has reached EOF.
+ if (p->eof) {
+ p->shutdown = true;
+ continue;
+ }
+ }
if (frameno < p->in_frameno + p->num_buffered) {
struct mp_image *img = p->buffered[frameno - p->in_frameno];
ret = alloc_vs_frame(p, &img->params);
@@ -539,6 +562,8 @@ static void destroy_vs(struct vf_instance *vf)
assert(num_requested(p) == 0); // async callback didn't return?
p->shutdown = false;
+ p->eof = false;
+ p->frames_sent = 0;
// Kill filtered images that weren't returned yet
for (int n = 0; n < p->max_requests; n++)
mp_image_unrefp(&p->requested[n]);
@@ -745,7 +770,7 @@ static int drv_vss_load_core(struct vf_instance *vf)
// First load an empty script to get a VSScript, so that we get the vsapi
// and vscore.
- if (vsscript_evaluateScript(&p->se, "", NULL, 0))
+ if (vsscript_createScript(&p->se))
return -1;
p->vsapi = vsscript_getVSApi();
p->vscore = vsscript_getCore(p->se);
diff --git a/video/filter/vf_vdpaupp.c b/video/filter/vf_vdpaupp.c
index 4db6ab9..26c1eef 100644
--- a/video/filter/vf_vdpaupp.c
+++ b/video/filter/vf_vdpaupp.c
@@ -92,9 +92,7 @@ static bool output_field(struct vf_instance *vf, int pos)
frame->field = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_FRAME;
if (p->opts.deint) {
- int top_field_first = 1;
- if (mpi->fields & MP_IMGFIELD_ORDERED)
- top_field_first = !!(mpi->fields & MP_IMGFIELD_TOP_FIRST);
+ int top_field_first = !!(mpi->fields & MP_IMGFIELD_TOP_FIRST);
frame->field = top_field_first ^ (pos & 1) ?
VDP_VIDEO_MIXER_PICTURE_STRUCTURE_BOTTOM_FIELD:
VDP_VIDEO_MIXER_PICTURE_STRUCTURE_TOP_FIELD;
diff --git a/video/filter/vf_yadif.c b/video/filter/vf_yadif.c
index 428188c..6526283 100644
--- a/video/filter/vf_yadif.c
+++ b/video/filter/vf_yadif.c
@@ -1,21 +1,20 @@
/*
* Copyright (C) 2006 Michael Niedermayer <michaelni@gmx.at>
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
diff --git a/video/fmt-conversion.c b/video/fmt-conversion.c
index 9ed9777..b280e36 100644
--- a/video/fmt-conversion.c
+++ b/video/fmt-conversion.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <libavutil/pixdesc.h>
@@ -124,6 +123,9 @@ static const struct {
#endif
{IMGFMT_VAAPI, AV_PIX_FMT_VAAPI_VLD},
{IMGFMT_DXVA2, AV_PIX_FMT_DXVA2_VLD},
+#if HAVE_AV_PIX_FMT_MMAL
+ {IMGFMT_MMAL, AV_PIX_FMT_MMAL},
+#endif
{0, AV_PIX_FMT_NONE}
};
diff --git a/video/fmt-conversion.h b/video/fmt-conversion.h
index c8f0b88..03515ec 100644
--- a/video/fmt-conversion.h
+++ b/video/fmt-conversion.h
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_FMT_CONVERSION_H
diff --git a/video/hwdec.h b/video/hwdec.h
index 0574e5c..b04b7c5 100644
--- a/video/hwdec.h
+++ b/video/hwdec.h
@@ -12,6 +12,7 @@ enum hwdec_type {
HWDEC_VAAPI = 4,
HWDEC_VAAPI_COPY = 5,
HWDEC_DXVA2_COPY = 6,
+ HWDEC_RPI = 7,
};
struct mp_hwdec_ctx {
diff --git a/video/image_writer.c b/video/image_writer.c
index ff321cf..bcb71f4 100644
--- a/video/image_writer.c
+++ b/video/image_writer.c
@@ -1,18 +1,18 @@
/*
- * This file is part of mplayer.
+ * This file is part of mpv.
*
- * mplayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * mplayer is distributed in the hope that it will be useful,
+ * 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 mplayer. If not, see <http://www.gnu.org/licenses/>.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
@@ -47,9 +47,8 @@ const struct image_writer_opts image_writer_opts_defaults = {
.jpeg_quality = 90,
.jpeg_optimize = 100,
.jpeg_smooth = 0,
- .jpeg_dpi = 72,
- .jpeg_progressive = 0,
.jpeg_baseline = 1,
+ .tag_csp = 1,
};
#define OPT_BASE_STRUCT struct image_writer_opts
@@ -59,12 +58,11 @@ const struct m_sub_options image_writer_conf = {
OPT_INTRANGE("jpeg-quality", jpeg_quality, 0, 0, 100),
OPT_INTRANGE("jpeg-optimize", jpeg_optimize, 0, 0, 100),
OPT_INTRANGE("jpeg-smooth", jpeg_smooth, 0, 0, 100),
- OPT_INTRANGE("jpeg-dpi", jpeg_dpi, M_OPT_MIN, 1, 99999),
- OPT_FLAG("jpeg-progressive", jpeg_progressive, 0),
OPT_FLAG("jpeg-baseline", jpeg_baseline, 0),
OPT_INTRANGE("png-compression", png_compression, 0, 0, 9),
OPT_INTRANGE("png-filter", png_filter, 0, 0, 5),
OPT_STRING("format", format, 0),
+ OPT_FLAG("tag-colorspace", tag_csp, 0),
{0},
},
.size = sizeof(struct image_writer_opts),
@@ -75,18 +73,19 @@ struct image_writer_ctx {
struct mp_log *log;
const struct image_writer_opts *opts;
const struct img_writer *writer;
+ struct mp_imgfmt_desc original_format;
};
struct img_writer {
const char *file_ext;
- int (*write)(struct image_writer_ctx *ctx, mp_image_t *image, FILE *fp);
+ bool (*write)(struct image_writer_ctx *ctx, mp_image_t *image, FILE *fp);
const int *pixfmts;
int lavc_codec;
};
-static int write_lavc(struct image_writer_ctx *ctx, mp_image_t *image, FILE *fp)
+static bool write_lavc(struct image_writer_ctx *ctx, mp_image_t *image, FILE *fp)
{
- int success = 0;
+ bool success = 0;
AVFrame *pic = NULL;
AVPacket pkt = {0};
int got_output = 0;
@@ -131,6 +130,10 @@ static int write_lavc(struct image_writer_ctx *ctx, mp_image_t *image, FILE *fp)
pic->format = avctx->pix_fmt;
pic->width = avctx->width;
pic->height = avctx->height;
+ if (ctx->opts->tag_csp) {
+ pic->color_primaries = mp_csp_prim_to_avcol_pri(image->params.primaries);
+ pic->color_trc = mp_csp_trc_to_avcol_trc(image->params.gamma);
+ }
int ret = avcodec_encode_video2(avctx, &pkt, pic, &got_output);
if (ret < 0)
goto error_exit;
@@ -158,7 +161,7 @@ static void write_jpeg_error_exit(j_common_ptr cinfo)
longjmp(*(jmp_buf*)cinfo->client_data, 1);
}
-static int write_jpeg(struct image_writer_ctx *ctx, mp_image_t *image, FILE *fp)
+static bool write_jpeg(struct image_writer_ctx *ctx, mp_image_t *image, FILE *fp)
{
struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr;
@@ -170,7 +173,7 @@ static int write_jpeg(struct image_writer_ctx *ctx, mp_image_t *image, FILE *fp)
cinfo.client_data = &error_return_jmpbuf;
if (setjmp(cinfo.client_data)) {
jpeg_destroy_compress(&cinfo);
- return 0;
+ return false;
}
jpeg_create_compress(&cinfo);
@@ -184,18 +187,14 @@ static int write_jpeg(struct image_writer_ctx *ctx, mp_image_t *image, FILE *fp)
cinfo.write_JFIF_header = TRUE;
cinfo.JFIF_major_version = 1;
cinfo.JFIF_minor_version = 2;
- cinfo.density_unit = 1; /* 0=unknown, 1=dpi, 2=dpcm */
- cinfo.X_density = ctx->opts->jpeg_dpi;
- cinfo.Y_density = ctx->opts->jpeg_dpi;
- cinfo.write_Adobe_marker = TRUE;
jpeg_set_defaults(&cinfo);
jpeg_set_quality(&cinfo, ctx->opts->jpeg_quality, ctx->opts->jpeg_baseline);
cinfo.optimize_coding = ctx->opts->jpeg_optimize;
cinfo.smoothing_factor = ctx->opts->jpeg_smooth;
- if (ctx->opts->jpeg_progressive)
- jpeg_simple_progression(&cinfo);
+ cinfo.comp_info[0].h_samp_factor = 1 << ctx->original_format.chroma_xs;
+ cinfo.comp_info[0].v_samp_factor = 1 << ctx->original_format.chroma_ys;
jpeg_start_compress(&cinfo, TRUE);
@@ -210,27 +209,47 @@ static int write_jpeg(struct image_writer_ctx *ctx, mp_image_t *image, FILE *fp)
jpeg_destroy_compress(&cinfo);
- return 1;
+ return true;
}
#endif
+static int get_target_format(struct image_writer_ctx *ctx, int srcfmt)
+{
+ if (!ctx->writer->lavc_codec)
+ goto unknown;
+
+ struct AVCodec *codec = avcodec_find_encoder(ctx->writer->lavc_codec);
+ if (!codec)
+ goto unknown;
+
+ const enum AVPixelFormat *pix_fmts = codec->pix_fmts;
+ if (!pix_fmts)
+ goto unknown;
+
+ int current = 0;
+ for (int n = 0; pix_fmts[n] != AV_PIX_FMT_NONE; n++) {
+ int fmt = pixfmt2imgfmt(pix_fmts[n]);
+ if (!fmt)
+ continue;
+ current = current ? mp_imgfmt_select_best(current, fmt, srcfmt) : fmt;
+ }
+
+ if (!current)
+ goto unknown;
+
+ return current;
+
+unknown:
+ return IMGFMT_RGB24;
+}
+
static const struct img_writer img_writers[] = {
{ "png", write_lavc, .lavc_codec = AV_CODEC_ID_PNG },
{ "ppm", write_lavc, .lavc_codec = AV_CODEC_ID_PPM },
- { "pgm", write_lavc,
- .lavc_codec = AV_CODEC_ID_PGM,
- .pixfmts = (const int[]) { IMGFMT_Y8, 0 },
- },
- { "pgmyuv", write_lavc,
- .lavc_codec = AV_CODEC_ID_PGMYUV,
- .pixfmts = (const int[]) { IMGFMT_420P, 0 },
- },
- { "tga", write_lavc,
- .lavc_codec = AV_CODEC_ID_TARGA,
- .pixfmts = (const int[]) { IMGFMT_BGR24, IMGFMT_BGRA, IMGFMT_RGB555,
- IMGFMT_Y8, 0},
- },
+ { "pgm", write_lavc, .lavc_codec = AV_CODEC_ID_PGM },
+ { "pgmyuv", write_lavc, .lavc_codec = AV_CODEC_ID_PGMYUV },
+ { "tga", write_lavc, .lavc_codec = AV_CODEC_ID_TARGA },
#if HAVE_JPEG
{ "jpg", write_jpeg },
{ "jpeg", write_jpeg },
@@ -260,65 +279,61 @@ const char *image_writer_file_ext(const struct image_writer_opts *opts)
return get_writer(opts)->file_ext;
}
-int write_image(struct mp_image *image, const struct image_writer_opts *opts,
- const char *filename, struct mp_log *log)
+struct mp_image *convert_image(struct mp_image *image, int destfmt,
+ struct mp_log *log)
{
- struct mp_image *allocated_image = NULL;
- struct image_writer_opts defs = image_writer_opts_defaults;
int d_w = image->params.d_w;
int d_h = image->params.d_h;
bool is_anamorphic = image->w != d_w || image->h != d_h;
+ // Caveat: no colorspace/levels conversion done if pixel formats equal
+ // it's unclear what colorspace/levels the target wants
+ if (image->imgfmt == destfmt && !is_anamorphic)
+ return mp_image_new_ref(image);
+
+ struct mp_image *dst = mp_image_alloc(destfmt, d_w, d_h);
+ if (!dst) {
+ mp_err(log, "Out of memory.\n");
+ return NULL;
+ }
+ mp_image_copy_attributes(dst, image);
+
+ if (mp_image_swscale(dst, image, mp_sws_hq_flags) < 0) {
+ mp_err(log, "Error when converting image.\n");
+ talloc_free(dst);
+ return NULL;
+ }
+
+ return dst;
+}
+
+bool write_image(struct mp_image *image, const struct image_writer_opts *opts,
+ const char *filename, struct mp_log *log)
+{
+ struct image_writer_opts defs = image_writer_opts_defaults;
if (!opts)
opts = &defs;
const struct img_writer *writer = get_writer(opts);
- struct image_writer_ctx ctx = { log, opts, writer };
- int destfmt = IMGFMT_RGB24;
-
- if (writer->pixfmts) {
- destfmt = writer->pixfmts[0]; // default to first pixel format
- for (const int *fmt = writer->pixfmts; *fmt; fmt++) {
- if (*fmt == image->imgfmt) {
- destfmt = *fmt;
- break;
- }
- }
- }
+ struct image_writer_ctx ctx = { log, opts, writer, image->fmt };
+ int destfmt = get_target_format(&ctx, image->imgfmt);
- // Caveat: no colorspace/levels conversion done if pixel formats equal
- // it's unclear what colorspace/levels the target wants
- if (image->imgfmt != destfmt || is_anamorphic) {
- struct mp_image *dst = mp_image_alloc(destfmt, d_w, d_h);
- if (!dst) {
- mp_err(log, "Out of memory.\n");
- return 0;
- }
- mp_image_copy_attributes(dst, image);
-
- if (mp_image_swscale(dst, image, mp_sws_hq_flags) < 0) {
- mp_err(log, "Error when converting image.\n");
- talloc_free(dst);
- return 0;
- }
-
- allocated_image = dst;
- image = dst;
- }
+ struct mp_image *dst = convert_image(image, destfmt, log);
+ if (!dst)
+ return false;
FILE *fp = fopen(filename, "wb");
- int success = 0;
+ bool success = false;
if (fp == NULL) {
mp_err(log, "Error opening '%s' for writing!\n", filename);
} else {
- success = writer->write(&ctx, image, fp);
+ success = writer->write(&ctx, dst, fp);
success = !fclose(fp) && success;
if (!success)
mp_err(log, "Error writing file '%s'!\n", filename);
}
- talloc_free(allocated_image);
-
+ talloc_free(dst);
return success;
}
diff --git a/video/image_writer.h b/video/image_writer.h
index 2fce630..b27db39 100644
--- a/video/image_writer.h
+++ b/video/image_writer.h
@@ -1,18 +1,18 @@
/*
- * This file is part of mplayer.
+ * This file is part of mpv.
*
- * mplayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * mplayer is distributed in the hope that it will be useful,
+ * 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 mplayer. If not, see <http://www.gnu.org/licenses/>.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
struct mp_image;
@@ -28,6 +28,7 @@ struct image_writer_opts {
int jpeg_dpi;
int jpeg_progressive;
int jpeg_baseline;
+ int tag_csp;
};
extern const struct image_writer_opts image_writer_opts_defaults;
@@ -47,8 +48,14 @@ const char *image_writer_file_ext(const struct image_writer_opts *opts);
* accordingly. Setting w and width or h and height to different values
* can be used to store snapshots of anamorphic video.
*/
-int write_image(struct mp_image *image, const struct image_writer_opts *opts,
- const char *filename, struct mp_log *log);
+bool write_image(struct mp_image *image, const struct image_writer_opts *opts,
+ const char *filename, struct mp_log *log);
+
+/* Return the image converted to the given format. If the pixel aspect ratio is
+ * not 1:1, the image is scaled as well. Returns NULL on failure.
+ */
+struct mp_image *convert_image(struct mp_image *image, int destfmt,
+ struct mp_log *log);
// Debugging helper.
void dump_png(struct mp_image *image, const char *filename, struct mp_log *log);
diff --git a/video/img_format.c b/video/img_format.c
index f717ff9..6cf585d 100644
--- a/video/img_format.c
+++ b/video/img_format.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <assert.h>
@@ -249,6 +248,9 @@ struct mp_imgfmt_desc mp_imgfmt_get_desc(int mpfmt)
desc.plane_bits = 8; // usually restricted to 8 bit; may change
}
+ if (desc.chroma_xs || desc.chroma_ys)
+ desc.flags |= MP_IMGFLAG_SUBSAMPLED;
+
return desc;
}
diff --git a/video/img_format.h b/video/img_format.h
index 7d0762c..8c79d9f 100644
--- a/video/img_format.h
+++ b/video/img_format.h
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_IMG_FORMAT_H
@@ -66,6 +65,8 @@
#define MP_IMGFLAG_PAL 0x8000
// planes don't contain real data; planes[3] contains an API-specific pointer
#define MP_IMGFLAG_HWACCEL 0x10000
+// Set if the chroma resolution is lower than luma resolution. Unset for non-YUV.
+#define MP_IMGFLAG_SUBSAMPLED 0x20000
// Exactly one of these bits is set in mp_imgfmt_desc.flags
#define MP_IMGFLAG_COLOR_CLASS_MASK \
@@ -204,6 +205,7 @@ enum mp_imgfmt {
IMGFMT_VDA,
IMGFMT_VAAPI,
IMGFMT_DXVA2, // IDirect3DSurface9 (NV12)
+ IMGFMT_MMAL, // MMAL_BUFFER_HEADER_T
// Generic pass-through of AV_PIX_FMT_*. Used for formats which don't have
// a corresponding IMGFMT_ value.
diff --git a/video/memcpy_pic.h b/video/memcpy_pic.h
deleted file mode 100644
index 01c874e..0000000
--- a/video/memcpy_pic.h
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * This file is part of MPlayer.
- *
- * MPlayer 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.
- *
- * 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser 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
- */
-
-#ifndef MPLAYER_FASTMEMCPY_H
-#define MPLAYER_FASTMEMCPY_H
-
-#include <inttypes.h>
-#include <string.h>
-#include <stddef.h>
-
-#define my_memcpy_pic memcpy_pic
-#define memcpy_pic2(d, s, b, h, ds, ss, unused) memcpy_pic(d, s, b, h, ds, ss)
-
-static inline void memcpy_pic(void *dst, const void *src,
- int bytesPerLine, int height,
- int dstStride, int srcStride)
-{
- if (bytesPerLine == dstStride && dstStride == srcStride) {
- if (srcStride < 0) {
- src = (uint8_t*)src + (height - 1) * srcStride;
- dst = (uint8_t*)dst + (height - 1) * dstStride;
- srcStride = -srcStride;
- }
-
- memcpy(dst, src, srcStride * height);
- } else {
- for (int i = 0; i < height; i++) {
- memcpy(dst, src, bytesPerLine);
- src = (uint8_t*)src + srcStride;
- dst = (uint8_t*)dst + dstStride;
- }
- }
-}
-
-static inline void memset_pic(void *dst, int fill, int bytesPerLine, int height,
- int stride)
-{
- if (bytesPerLine == stride) {
- memset(dst, fill, stride * height);
- } else {
- for (int i = 0; i < height; i++) {
- memset(dst, fill, bytesPerLine);
- dst = (uint8_t *)dst + stride;
- }
- }
-}
-
-static inline void memset16_pic(void *dst, int fill, int unitsPerLine,
- int height, int stride)
-{
- if (fill == 0) {
- memset_pic(dst, 0, unitsPerLine * 2, height, stride);
- } else {
- for (int i = 0; i < height; i++) {
- uint16_t *line = dst;
- uint16_t *end = line + unitsPerLine;
- while (line < end)
- *line++ = fill;
- dst = (uint8_t *)dst + stride;
- }
- }
-}
-
-#endif /* MPLAYER_FASTMEMCPY_H */
diff --git a/video/mp_image.c b/video/mp_image.c
index 44f5ab9..dc305a4 100644
--- a/video/mp_image.c
+++ b/video/mp_image.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
@@ -35,7 +34,6 @@
#include "img_format.h"
#include "mp_image.h"
#include "sws_utils.h"
-#include "memcpy_pic.h"
#include "fmt-conversion.h"
#include "video/filter/vf.h"
@@ -48,12 +46,6 @@ struct m_refcount {
void *arg;
// free() is called if refcount reaches 0.
void (*free)(void *arg);
- // External refcounted object (such as libavcodec DR buffers). This assumes
- // that the actual data is managed by the external object, not by
- // m_refcount. The .ext_* calls use that external object's refcount
- // primitives.
- void (*ext_ref)(void *arg);
- void (*ext_unref)(void *arg);
bool (*ext_is_unique)(void *arg);
// Native refcount (there may be additional references if .ext_* are set)
int refcount;
@@ -80,16 +72,10 @@ static void m_refcount_ref(struct m_refcount *ref)
refcount_lock();
ref->refcount++;
refcount_unlock();
-
- if (ref->ext_ref)
- ref->ext_ref(ref->arg);
}
static void m_refcount_unref(struct m_refcount *ref)
{
- if (ref->ext_unref)
- ref->ext_unref(ref->arg);
-
bool dead;
refcount_lock();
assert(ref->refcount > 0);
@@ -132,7 +118,7 @@ static bool mp_image_alloc_planes(struct mp_image *mpi)
size_t plane_size[MP_MAX_PLANES];
for (int n = 0; n < MP_MAX_PLANES; n++) {
int alloc_h = MP_ALIGN_UP(mpi->h, 32) >> mpi->fmt.ys[n];
- int line_bytes = (mpi->plane_w[n] * mpi->fmt.bpp[n] + 7) / 8;
+ int line_bytes = (mp_image_plane_w(mpi, n) * mpi->fmt.bpp[n] + 7) / 8;
mpi->stride[n] = FFALIGN(line_bytes, SWS_MIN_BYTE_ALIGN);
plane_size[n] = mpi->stride[n] * alloc_h;
}
@@ -159,10 +145,7 @@ void mp_image_setfmt(struct mp_image *mpi, int out_fmt)
struct mp_imgfmt_desc fmt = mp_imgfmt_get_desc(out_fmt);
mpi->params.imgfmt = fmt.id;
mpi->fmt = fmt;
- mpi->flags = fmt.flags;
mpi->imgfmt = fmt.id;
- mpi->chroma_x_shift = fmt.chroma_xs;
- mpi->chroma_y_shift = fmt.chroma_ys;
mpi->num_planes = fmt.num_planes;
mp_image_set_size(mpi, mpi->w, mpi->h);
}
@@ -178,18 +161,24 @@ int mp_chroma_div_up(int size, int shift)
return (size + (1 << shift) - 1) >> shift;
}
+// Return the storage width in pixels of the given plane.
+int mp_image_plane_w(struct mp_image *mpi, int plane)
+{
+ return mp_chroma_div_up(mpi->w, mpi->fmt.xs[plane]);
+}
+
+// Return the storage height in pixels of the given plane.
+int mp_image_plane_h(struct mp_image *mpi, int plane)
+{
+ return mp_chroma_div_up(mpi->h, mpi->fmt.ys[plane]);
+}
+
// Caller has to make sure this doesn't exceed the allocated plane data/strides.
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;
- for (int n = 0; n < mpi->num_planes; n++) {
- mpi->plane_w[n] = mp_chroma_div_up(mpi->w, mpi->fmt.xs[n]);
- mpi->plane_h[n] = mp_chroma_div_up(mpi->h, mpi->fmt.ys[n]);
- }
- mpi->chroma_width = mpi->plane_w[1];
- mpi->chroma_height = mpi->plane_h[1];
}
void mp_image_set_params(struct mp_image *image,
@@ -266,40 +255,36 @@ struct mp_image *mp_image_new_ref(struct mp_image *img)
return new;
}
-// Return a reference counted reference to img. If the reference count reaches
-// 0, call free(free_arg). The data passed by img must not be free'd before
-// that. The new reference will be writeable.
-// On allocation failure, unref the frame and return NULL.
-struct mp_image *mp_image_new_custom_ref(struct mp_image *img, void *free_arg,
- void (*free)(void *arg))
-{
- return mp_image_new_external_ref(img, free_arg, NULL, NULL, NULL, free);
-}
-
-// Return a reference counted reference to img. ref/unref/is_unique are used to
-// connect to an external refcounting API. It is assumed that the new object
+// Return a reference counted reference to img. is_unique us used to connect to
+// an external refcounting API. It is assumed that the new object
// has an initial reference to that external API. If free is given, that is
// called after the last unref. All function pointers are optional.
// On allocation failure, unref the frame and return NULL.
-struct mp_image *mp_image_new_external_ref(struct mp_image *img, void *arg,
- void (*ref)(void *arg),
- void (*unref)(void *arg),
- bool (*is_unique)(void *arg),
- void (*free)(void *arg))
+static struct mp_image *mp_image_new_external_ref(struct mp_image *img, void *arg,
+ bool (*is_unique)(void *arg),
+ void (*free)(void *arg))
{
struct mp_image *new = talloc_ptrtype(NULL, new);
talloc_set_destructor(new, mp_image_destructor);
*new = *img;
new->refcount = m_refcount_new();
- new->refcount->ext_ref = ref;
- new->refcount->ext_unref = unref;
new->refcount->ext_is_unique = is_unique;
new->refcount->free = free;
new->refcount->arg = arg;
return new;
}
+// Return a reference counted reference to img. If the reference count reaches
+// 0, call free(free_arg). The data passed by img must not be free'd before
+// that. The new reference will be writeable.
+// On allocation failure, unref the frame and return NULL.
+struct mp_image *mp_image_new_custom_ref(struct mp_image *img, void *free_arg,
+ void (*free)(void *arg))
+{
+ return mp_image_new_external_ref(img, free_arg, NULL, free);
+}
+
bool mp_image_is_writeable(struct mp_image *img)
{
if (!img->refcount)
@@ -347,8 +332,9 @@ void mp_image_copy(struct mp_image *dst, struct mp_image *src)
assert(dst->w == src->w && dst->h == src->h);
assert(mp_image_is_writeable(dst));
for (int n = 0; n < dst->num_planes; n++) {
- int line_bytes = (dst->plane_w[n] * dst->fmt.bpp[n] + 7) / 8;
- memcpy_pic(dst->planes[n], src->planes[n], line_bytes, dst->plane_h[n],
+ int line_bytes = (mp_image_plane_w(dst, n) * dst->fmt.bpp[n] + 7) / 8;
+ int plane_h = mp_image_plane_h(dst, n);
+ memcpy_pic(dst->planes[n], src->planes[n], line_bytes, plane_h,
dst->stride[n], src->stride[n]);
}
// Watch out for AV_PIX_FMT_FLAG_PSEUDOPAL retardation
@@ -368,10 +354,11 @@ void mp_image_copy_attributes(struct mp_image *dst, struct mp_image *src)
dst->params.d_w = src->params.d_w;
dst->params.d_h = src->params.d_h;
}
- if ((dst->flags & MP_IMGFLAG_YUV) == (src->flags & MP_IMGFLAG_YUV)) {
+ dst->params.primaries = src->params.primaries;
+ dst->params.gamma = src->params.gamma;
+ if ((dst->fmt.flags & MP_IMGFLAG_YUV) == (src->fmt.flags & MP_IMGFLAG_YUV)) {
dst->params.colorspace = src->params.colorspace;
dst->params.colorlevels = src->params.colorlevels;
- dst->params.primaries = src->params.primaries;
dst->params.chroma_location = src->params.chroma_location;
dst->params.outputlevels = src->params.outputlevels;
}
@@ -425,9 +412,9 @@ void mp_image_clear(struct mp_image *img, int x0, int y0, int x1, int y1)
plane_clear[0] = av_le2ne16(0x0080);
} else if (area.imgfmt == IMGFMT_NV12 || area.imgfmt == IMGFMT_NV21) {
plane_clear[1] = 0x8080;
- } else if (area.flags & MP_IMGFLAG_YUV_P) {
+ } else if (area.fmt.flags & MP_IMGFLAG_YUV_P) {
uint16_t chroma_clear = (1 << area.fmt.plane_bits) / 2;
- if (!(area.flags & MP_IMGFLAG_NE))
+ if (!(area.fmt.flags & MP_IMGFLAG_NE))
chroma_clear = av_bswap16(chroma_clear);
if (area.num_planes > 2)
plane_clear[1] = plane_clear[2] = chroma_clear;
@@ -435,13 +422,13 @@ void mp_image_clear(struct mp_image *img, int x0, int y0, int x1, int y1)
for (int p = 0; p < area.num_planes; p++) {
int bpp = area.fmt.bpp[p];
- int bytes = (area.plane_w[p] * bpp + 7) / 8;
+ int bytes = (mp_image_plane_w(&area, p) * bpp + 7) / 8;
if (bpp <= 8) {
memset_pic(area.planes[p], plane_clear[p], bytes,
- area.plane_h[p], area.stride[p]);
+ mp_image_plane_h(&area, p), area.stride[p]);
} else {
memset16_pic(area.planes[p], plane_clear[p], (bytes + 1) / 2,
- area.plane_h[p], area.stride[p]);
+ mp_image_plane_h(&area, p), area.stride[p]);
}
}
}
@@ -449,7 +436,8 @@ void mp_image_clear(struct mp_image *img, int x0, int y0, int x1, int y1)
void mp_image_vflip(struct mp_image *img)
{
for (int p = 0; p < img->num_planes; p++) {
- img->planes[p] = img->planes[p] + img->stride[p] * (img->plane_h[p] - 1);
+ int plane_h = mp_image_plane_h(img, p);
+ img->planes[p] = img->planes[p] + img->stride[p] * (plane_h - 1);
img->stride[p] = -img->stride[p];
}
}
@@ -462,11 +450,15 @@ char *mp_image_params_to_str_buf(char *b, size_t bs,
if (p->w != p->d_w || p->h != p->d_h)
mp_snprintf_cat(b, bs, "->%dx%d", p->d_w, p->d_h);
mp_snprintf_cat(b, bs, " %s", mp_imgfmt_to_name(p->imgfmt));
- mp_snprintf_cat(b, bs, " %s/%s", mp_csp_names[p->colorspace],
- mp_csp_levels_names[p->colorlevels]);
- mp_snprintf_cat(b, bs, " CL=%s", mp_chroma_names[p->chroma_location]);
- if (p->outputlevels)
- mp_snprintf_cat(b, bs, " out=%s", mp_csp_levels_names[p->outputlevels]);
+ mp_snprintf_cat(b, bs, " %s/%s",
+ m_opt_choice_str(mp_csp_names, p->colorspace),
+ m_opt_choice_str(mp_csp_levels_names, p->colorlevels));
+ mp_snprintf_cat(b, bs, " CL=%s",
+ m_opt_choice_str(mp_chroma_names, p->chroma_location));
+ if (p->outputlevels) {
+ mp_snprintf_cat(b, bs, " out=%s",
+ m_opt_choice_str(mp_csp_levels_names, p->outputlevels));
+ }
if (p->rotate)
mp_snprintf_cat(b, bs, " rot=%d", p->rotate);
if (p->stereo_in > 0 || p->stereo_out > 0) {
@@ -489,8 +481,7 @@ bool mp_image_params_valid(const struct mp_image_params *p)
// ints. We also should (for now) do the same as FFmpeg, to be sure large
// images don't crash with libswscale or when wrapping with AVFrame and
// passing the result to filters.
- // Unlike FFmpeg, consider 0x0 valid (might be needed for OSD/screenshots).
- if (p->w < 0 || p->h < 0 || (p->w + 128LL) * (p->h + 128LL) >= INT_MAX / 8)
+ 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)
@@ -516,6 +507,7 @@ bool mp_image_params_equal(const struct mp_image_params *p1,
p1->colorlevels == p2->colorlevels &&
p1->outputlevels == p2->outputlevels &&
p1->primaries == p2->primaries &&
+ p1->gamma == p2->gamma &&
p1->chroma_location == p2->chroma_location &&
p1->rotate == p2->rotate &&
p1->stereo_in == p2->stereo_in &&
@@ -578,6 +570,8 @@ void mp_image_params_guess_csp(struct mp_image_params *params)
params->primaries = mp_csp_guess_primaries(params->w, params->h);
}
}
+ if (params->gamma == MP_CSP_TRC_AUTO)
+ params->gamma = MP_CSP_TRC_BT_1886;
} else if (fmt.flags & MP_IMGFLAG_RGB) {
params->colorspace = MP_CSP_RGB;
params->colorlevels = MP_CSP_LEVELS_PC;
@@ -589,6 +583,8 @@ void mp_image_params_guess_csp(struct mp_image_params *params)
// Note: sRGB primaries = BT.709 primaries
if (params->primaries == MP_CSP_PRIM_AUTO)
params->primaries = MP_CSP_PRIM_BT_709;
+ if (params->gamma == MP_CSP_TRC_AUTO)
+ params->gamma = MP_CSP_TRC_SRGB;
} else if (fmt.flags & MP_IMGFLAG_XYZ) {
params->colorspace = MP_CSP_XYZ;
params->colorlevels = MP_CSP_LEVELS_PC;
@@ -603,11 +599,14 @@ void mp_image_params_guess_csp(struct mp_image_params *params)
// tagged with.
if (params->primaries == MP_CSP_PRIM_AUTO)
params->primaries = MP_CSP_PRIM_BT_709;
+ if (params->gamma == MP_CSP_TRC_AUTO)
+ params->gamma = MP_CSP_TRC_LINEAR;
} else {
// We have no clue.
params->colorspace = MP_CSP_AUTO;
params->colorlevels = MP_CSP_LEVELS_AUTO;
params->primaries = MP_CSP_PRIM_AUTO;
+ params->gamma = MP_CSP_TRC_AUTO;
}
}
@@ -626,19 +625,15 @@ void mp_image_copy_fields_from_av_frame(struct mp_image *dst,
dst->pict_type = src->pict_type;
- dst->fields = MP_IMGFIELD_ORDERED;
+ dst->fields = 0;
if (src->interlaced_frame)
dst->fields |= MP_IMGFIELD_INTERLACED;
if (src->top_field_first)
dst->fields |= MP_IMGFIELD_TOP_FIRST;
if (src->repeat_pict == 1)
dst->fields |= MP_IMGFIELD_REPEAT_FIRST;
-
}
-// Not strictly related, but was added in a similar timeframe.
-#define HAVE_AVFRAME_COLORSPACE HAVE_AVCODEC_CHROMA_POS_API
-
// Copy properties and data of the mp_image into the AVFrame, without taking
// care of memory management issues.
void mp_image_copy_fields_to_av_frame(struct AVFrame *dst,
@@ -662,10 +657,8 @@ void mp_image_copy_fields_to_av_frame(struct AVFrame *dst,
if (src->fields & MP_IMGFIELD_REPEAT_FIRST)
dst->repeat_pict = 1;
-#if HAVE_AVFRAME_COLORSPACE
dst->colorspace = mp_csp_to_avcol_spc(src->params.colorspace);
dst->color_range = mp_csp_levels_to_avcol_range(src->params.colorlevels);
-#endif
}
static void frame_free(void *p)
@@ -688,8 +681,7 @@ struct mp_image *mp_image_from_av_frame(struct AVFrame *av_frame)
return NULL;
struct mp_image t = {0};
mp_image_copy_fields_from_av_frame(&t, new_ref);
- return mp_image_new_external_ref(&t, new_ref, NULL, NULL, frame_is_unique,
- frame_free);
+ return mp_image_new_external_ref(&t, new_ref, frame_is_unique, frame_free);
}
static void free_img(void *opaque, uint8_t *data)
@@ -736,3 +728,50 @@ struct AVFrame *mp_image_to_av_frame_and_unref(struct mp_image *img)
talloc_free(new_ref);
return frame;
}
+
+void memcpy_pic(void *dst, const void *src, int bytesPerLine, int height,
+ int dstStride, int srcStride)
+{
+ if (bytesPerLine == dstStride && dstStride == srcStride && height) {
+ if (srcStride < 0) {
+ src = (uint8_t*)src + (height - 1) * srcStride;
+ dst = (uint8_t*)dst + (height - 1) * dstStride;
+ srcStride = -srcStride;
+ }
+
+ memcpy(dst, src, srcStride * (height - 1) + bytesPerLine);
+ } else {
+ for (int i = 0; i < height; i++) {
+ memcpy(dst, src, bytesPerLine);
+ src = (uint8_t*)src + srcStride;
+ dst = (uint8_t*)dst + dstStride;
+ }
+ }
+}
+
+void memset_pic(void *dst, int fill, int bytesPerLine, int height, int stride)
+{
+ if (bytesPerLine == stride && height) {
+ memset(dst, fill, stride * (height - 1) + bytesPerLine);
+ } else {
+ for (int i = 0; i < height; i++) {
+ memset(dst, fill, bytesPerLine);
+ dst = (uint8_t *)dst + stride;
+ }
+ }
+}
+
+void memset16_pic(void *dst, int fill, int unitsPerLine, int height, int stride)
+{
+ if (fill == 0) {
+ memset_pic(dst, 0, unitsPerLine * 2, height, stride);
+ } else {
+ for (int i = 0; i < height; i++) {
+ uint16_t *line = dst;
+ uint16_t *end = line + unitsPerLine;
+ while (line < end)
+ *line++ = fill;
+ dst = (uint8_t *)dst + stride;
+ }
+ }
+}
diff --git a/video/mp_image.h b/video/mp_image.h
index 5263249..b0110c1 100644
--- a/video/mp_image.h
+++ b/video/mp_image.h
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_MP_IMAGE_H
@@ -31,11 +30,8 @@
#define MP_PALETTE_SIZE (256 * 4)
-#define MP_IMGFIELD_ORDERED 0x01
#define MP_IMGFIELD_TOP_FIRST 0x02
#define MP_IMGFIELD_REPEAT_FIRST 0x04
-#define MP_IMGFIELD_TOP 0x08
-#define MP_IMGFIELD_BOTTOM 0x10
#define MP_IMGFIELD_INTERLACED 0x20
// Describes image parameters that usually stay constant.
@@ -48,6 +44,7 @@ struct mp_image_params {
enum mp_csp colorspace;
enum mp_csp_levels colorlevels;
enum mp_csp_prim primaries;
+ enum mp_csp_trc gamma;
enum mp_chroma_location chroma_location;
// The image should be converted to these levels. Unlike colorlevels, it
// does not describe the current state of the image. (Somewhat similar to
@@ -76,7 +73,7 @@ struct mp_image_params {
* image data. mp_image_make_writeable() will do that copy if required.
*/
typedef struct mp_image {
- unsigned int flags; // same as fmt.flags
+ int w, h; // visible dimensions (redundant with params.w/h)
struct mp_image_params params;
@@ -84,22 +81,13 @@ typedef struct mp_image {
struct mp_imgfmt_desc fmt;
enum mp_imgfmt imgfmt;
int num_planes;
- int chroma_x_shift; // horizontal
- int chroma_y_shift; // vertical
- int w,h; // visible dimensions
uint8_t *planes[MP_MAX_PLANES];
int stride[MP_MAX_PLANES];
int pict_type; // 0->unknown, 1->I, 2->P, 3->B
int fields;
- /* redundant */
- int chroma_width;
- int chroma_height;
- int plane_w[MP_MAX_PLANES];
- int plane_h[MP_MAX_PLANES];
-
/* only inside filter chain */
double pts;
/* memory management */
@@ -126,6 +114,8 @@ void mp_image_crop_rc(struct mp_image *img, struct mp_rect rc);
void mp_image_vflip(struct mp_image *img);
void mp_image_set_size(struct mp_image *mpi, int w, int h);
+int mp_image_plane_w(struct mp_image *mpi, int plane);
+int mp_image_plane_h(struct mp_image *mpi, int plane);
void mp_image_setfmt(mp_image_t* mpi, int out_fmt);
void mp_image_steal_data(struct mp_image *dst, struct mp_image *src);
@@ -133,12 +123,6 @@ void mp_image_steal_data(struct mp_image *dst, struct mp_image *src);
struct mp_image *mp_image_new_custom_ref(struct mp_image *img, void *arg,
void (*free)(void *arg));
-struct mp_image *mp_image_new_external_ref(struct mp_image *img, void *arg,
- void (*ref)(void *arg),
- void (*unref)(void *arg),
- bool (*is_unique)(void *arg),
- void (*free)(void *arg));
-
void mp_image_params_guess_csp(struct mp_image_params *params);
char *mp_image_params_to_str_buf(char *b, size_t bs,
@@ -163,4 +147,9 @@ void mp_image_copy_fields_to_av_frame(struct AVFrame *dst,
struct mp_image *mp_image_from_av_frame(struct AVFrame *av_frame);
struct AVFrame *mp_image_to_av_frame_and_unref(struct mp_image *img);
+void memcpy_pic(void *dst, const void *src, int bytesPerLine, int height,
+ int dstStride, int srcStride);
+void memset_pic(void *dst, int fill, int bytesPerLine, int height, int stride);
+void memset16_pic(void *dst, int fill, int unitsPerLine, int height, int stride);
+
#endif /* MPLAYER_MP_IMAGE_H */
diff --git a/video/mp_image_pool.c b/video/mp_image_pool.c
index 17c2526..173c018 100644
--- a/video/mp_image_pool.c
+++ b/video/mp_image_pool.c
@@ -12,8 +12,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
- * with mpv; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
diff --git a/video/out/aspect.c b/video/out/aspect.c
index e92c5e1..851cca9 100644
--- a/video/out/aspect.c
+++ b/video/out/aspect.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
/* Stuff for correct aspect scaling. */
diff --git a/video/out/aspect.h b/video/out/aspect.h
index 04d3149..614b537 100644
--- a/video/out/aspect.h
+++ b/video/out/aspect.h
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_ASPECT_H
diff --git a/video/out/bitmap_packer.c b/video/out/bitmap_packer.c
index 78af8e6..49a66ca 100644
--- a/video/out/bitmap_packer.c
+++ b/video/out/bitmap_packer.c
@@ -3,20 +3,20 @@
*
* Copyright 2009, 2012 Uoti Urpala
*
- * This file is part of mplayer2.
+ * This file is part of mpv.
*
- * mplayer2 is free software; you can redistribute it and/or modify
+ * 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.
*
- * mplayer2 is distributed in the hope that it will be useful,
+ * 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 mplayer2. If not, see <http://www.gnu.org/licenses/>.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
@@ -29,7 +29,7 @@
#include "bitmap_packer.h"
#include "common/common.h"
#include "sub/dec_sub.h"
-#include "video/memcpy_pic.h"
+#include "video/mp_image.h"
#define IS_POWER_OF_2(x) (((x) > 0) && !(((x) - 1) & (x)))
@@ -57,7 +57,7 @@ static int size_index(int s)
{
int n = av_log2_16bit(s);
return (n << HEIGHT_SORT_BITS)
- + (- 1 - (s << HEIGHT_SORT_BITS >> n) & (1 << HEIGHT_SORT_BITS) - 1);
+ + ((- 1 - (s << HEIGHT_SORT_BITS >> n)) & ((1 << HEIGHT_SORT_BITS) - 1));
}
/* Pack the given rectangles into an area of size w * h.
diff --git a/video/out/cocoa/events_view.m b/video/out/cocoa/events_view.m
index 42f48bf..6fec712 100644
--- a/video/out/cocoa/events_view.m
+++ b/video/out/cocoa/events_view.m
@@ -173,6 +173,9 @@
- (void)mouseEntered:(NSEvent *)event
{
[super mouseEntered:event];
+ if ([self.adapter mouseEnabled]) {
+ [self.adapter putKey:MP_KEY_MOUSE_ENTER withModifiers:0];
+ }
}
- (void)mouseExited:(NSEvent *)event
diff --git a/video/out/cocoa/window.m b/video/out/cocoa/window.m
index 14a3ba9..646281d 100644
--- a/video/out/cocoa/window.m
+++ b/video/out/cocoa/window.m
@@ -71,6 +71,16 @@
[self.adapter windowDidBecomeKey:notification];
}
+- (void)windowDidMiniaturize:(NSNotification *)notification
+{
+ [self.adapter windowDidMiniaturize:notification];
+}
+
+- (void)windowDidDeminiaturize:(NSNotification *)notification
+{
+ [self.adapter windowDidDeminiaturize:notification];
+}
+
- (BOOL)canBecomeMainWindow { return YES; }
- (BOOL)canBecomeKeyWindow { return YES; }
- (BOOL)windowShouldClose:(id)sender
diff --git a/video/out/cocoa_common.m b/video/out/cocoa_common.m
index b0f61d1..e768e06 100644
--- a/video/out/cocoa_common.m
+++ b/video/out/cocoa_common.m
@@ -20,6 +20,8 @@
#import <Cocoa/Cocoa.h>
#import <CoreServices/CoreServices.h> // for CGDisplayHideCursor
#import <IOKit/pwr_mgt/IOPMLib.h>
+#import <IOKit/IOKitLib.h>
+#include <mach/mach.h>
#import "cocoa_common.h"
#import "video/out/cocoa/window.h"
@@ -74,6 +76,10 @@ struct vo_cocoa_state {
bool embedded; // wether we are embedding in another GUI
IOPMAssertionID power_mgmt_assertion;
+ io_connect_t light_sensor;
+ uint64_t last_lmuvalue;
+ int last_lux;
+ IONotificationPortRef light_sensor_io_port;
pthread_mutex_t mutex;
struct mp_log *log;
@@ -145,6 +151,88 @@ static void set_application_icon(NSApplication *app)
[pool release];
}
+static int lmuvalue_to_lux(uint64_t v)
+{
+ // the polinomial approximation for apple lmu value -> lux was empirically
+ // derived by firefox developers (Apple provides no documentation).
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=793728
+ double power_c4 = 1/pow((double)10,27);
+ double power_c3 = 1/pow((double)10,19);
+ double power_c2 = 1/pow((double)10,12);
+ double power_c1 = 1/pow((double)10,5);
+
+ double term4 = -3.0 * power_c4 * pow(v,4);
+ double term3 = 2.6 * power_c3 * pow(v,3);
+ double term2 = -3.4 * power_c2 * pow(v,2);
+ double term1 = 3.9 * power_c1 * v;
+
+ int lux = ceil(term4 + term3 + term2 + term1 - 0.19);
+ return lux > 0 ? lux : 0;
+}
+
+static void light_sensor_cb(void *ctx, io_service_t srv, natural_t mtype, void *msg)
+{
+ struct vo *vo = ctx;
+ struct vo_cocoa_state *s = vo->cocoa;
+ uint32_t outputs = 2;
+ uint64_t values[outputs];
+
+ kern_return_t kr = IOConnectCallMethod(
+ s->light_sensor, 0, NULL, 0, NULL, 0, values, &outputs, nil, 0);
+
+ if (kr == KERN_SUCCESS) {
+ uint64_t mean = (values[0] + values[1]) / 2;
+ if (s->last_lmuvalue != mean) {
+ s->last_lmuvalue = mean;
+ s->last_lux = lmuvalue_to_lux(s->last_lmuvalue);
+ s->pending_events |= VO_EVENT_AMBIENT_LIGHTING_CHANGED;
+ vo_wakeup(vo);
+ return;
+ }
+ }
+}
+
+static void cocoa_init_light_sensor(struct vo *vo)
+{
+ with_cocoa_lock_on_main_thread(vo, ^{
+ struct vo_cocoa_state *s = vo->cocoa;
+ io_service_t srv = IOServiceGetMatchingService(
+ kIOMasterPortDefault, IOServiceMatching("AppleLMUController"));
+ if (srv == IO_OBJECT_NULL) {
+ MP_VERBOSE(vo, "can't find an ambient light sensor\n");
+ return;
+ }
+
+ // subscribe to notifications from the light sensor driver
+ s->light_sensor_io_port = IONotificationPortCreate(kIOMasterPortDefault);
+ IONotificationPortSetDispatchQueue(
+ s->light_sensor_io_port, dispatch_get_main_queue());
+
+ io_object_t n;
+ IOServiceAddInterestNotification(
+ s->light_sensor_io_port, srv, kIOGeneralInterest, light_sensor_cb,
+ vo, &n);
+
+ kern_return_t kr = IOServiceOpen(srv, mach_task_self(), 0,
+ &s->light_sensor);
+ IOObjectRelease(srv);
+ if (kr != KERN_SUCCESS) {
+ MP_WARN(vo, "can't start ambient light sensor connection\n");
+ return;
+ }
+
+ light_sensor_cb(vo, 0, 0, NULL);
+ });
+}
+
+static void cocoa_uninit_light_sensor(struct vo_cocoa_state *s)
+{
+ if (s->light_sensor_io_port) {
+ IONotificationPortDestroy(s->light_sensor_io_port);
+ IOObjectRelease(s->light_sensor);
+ }
+}
+
int vo_cocoa_init(struct vo *vo)
{
struct vo_cocoa_state *s = talloc_zero(NULL, struct vo_cocoa_state);
@@ -156,6 +244,7 @@ int vo_cocoa_init(struct vo *vo)
};
mpthread_mutex_init_recursive(&s->mutex);
vo->cocoa = s;
+ cocoa_init_light_sensor(vo);
return 1;
}
@@ -192,6 +281,7 @@ void vo_cocoa_uninit(struct vo *vo)
with_cocoa_lock_on_main_thread(vo, ^{
enable_power_management(s);
+ cocoa_uninit_light_sensor(s);
cocoa_rm_fs_screen_profile_observer(s);
[s->gl_ctx release];
@@ -656,6 +746,13 @@ int vo_cocoa_control(struct vo *vo, int *events, int request, void *arg)
});
return VO_TRUE;
}
+ case VOCTRL_GET_WIN_STATE: {
+ with_cocoa_lock(vo->cocoa, ^{
+ const bool minimized = [[vo->cocoa->view window] isMiniaturized];
+ *(int *)arg = minimized ? VO_WIN_STATE_MINIMIZED : 0;
+ });
+ return VO_TRUE;
+ }
case VOCTRL_SET_CURSOR_VISIBILITY:
return vo_cocoa_set_cursor_visibility(vo, arg);
case VOCTRL_UPDATE_WINDOW_TITLE:
@@ -674,6 +771,11 @@ int vo_cocoa_control(struct vo *vo, int *events, int request, void *arg)
*(double *)arg = vo->cocoa->screen_fps;
return VO_TRUE;
}
+ case VOCTRL_GET_AMBIENT_LUX:
+ if (vo->cocoa->light_sensor != IO_OBJECT_NULL) {
+ *(int *)arg = vo->cocoa->last_lux;
+ return VO_TRUE;
+ }
}
return VO_NOTIMPL;
}
@@ -780,4 +882,16 @@ int vo_cocoa_control(struct vo *vo, int *events, int request, void *arg)
[self didChangeMousePosition];
}
+- (void)windowDidMiniaturize:(NSNotification *)notification
+{
+ struct vo_cocoa_state *s = self.vout->cocoa;
+ s->pending_events |= VO_EVENT_WIN_STATE;
+}
+
+- (void)windowDidDeminiaturize:(NSNotification *)notification
+{
+ struct vo_cocoa_state *s = self.vout->cocoa;
+ s->pending_events |= VO_EVENT_WIN_STATE;
+}
+
@end
diff --git a/video/out/d3d_shader_yuv.h b/video/out/d3d_shader_420p.h
index 49ef753..8f19825 100644
--- a/video/out/d3d_shader_yuv.h
+++ b/video/out/d3d_shader_420p.h
@@ -1,142 +1,142 @@
-#if 0
-//
-// Generated by Microsoft (R) HLSL Shader Compiler 9.27.952.3022
-//
-// fxc /Tps_2_0 /Fhz:\tmp\mplayer\libvo\d3d_shader_yuv.h
-// z:\tmp\mplayer\libvo\d3d_shader_yuv.hlsl /Vnd3d_shader_yuv
-//
-//
-// Parameters:
-//
-// float4x4 colormatrix;
-// sampler2D tex0;
-// sampler2D tex1;
-// sampler2D tex2;
-//
-//
-// Registers:
-//
-// Name Reg Size
-// ------------ ----- ----
-// colormatrix c0 4
-// tex0 s0 1
-// tex1 s1 1
-// tex2 s2 1
-//
-
- ps_2_0
- def c4, 1, 0, 0, 0
- dcl t0.xy
- dcl t1.xy
- dcl t2.xy
- dcl_2d s0
- dcl_2d s1
- dcl_2d s2
- texld r0, t0, s0
- texld r1, t1, s1
- texld r2, t2, s2
- mov r0.y, r1.x
- mov r0.z, r2.x
- mov r0.w, c4.x
- dp4 r1.x, r0, c0
- dp4 r1.y, r0, c1
- dp4 r1.z, r0, c2
- dp4 r1.w, r0, c3
- mov oC0, r1
-
-// approximately 11 instruction slots used (3 texture, 8 arithmetic)
-#endif
-
-const BYTE d3d_shader_yuv[] =
-{
- 0, 2, 255, 255, 254, 255,
- 67, 0, 67, 84, 65, 66,
- 28, 0, 0, 0, 215, 0,
- 0, 0, 0, 2, 255, 255,
- 4, 0, 0, 0, 28, 0,
- 0, 0, 0, 1, 0, 0,
- 208, 0, 0, 0, 108, 0,
- 0, 0, 2, 0, 0, 0,
- 4, 0, 2, 0, 120, 0,
- 0, 0, 0, 0, 0, 0,
- 136, 0, 0, 0, 3, 0,
- 0, 0, 1, 0, 2, 0,
- 144, 0, 0, 0, 0, 0,
- 0, 0, 160, 0, 0, 0,
- 3, 0, 1, 0, 1, 0,
- 6, 0, 168, 0, 0, 0,
- 0, 0, 0, 0, 184, 0,
- 0, 0, 3, 0, 2, 0,
- 1, 0, 10, 0, 192, 0,
- 0, 0, 0, 0, 0, 0,
- 99, 111, 108, 111, 114, 109,
- 97, 116, 114, 105, 120, 0,
- 3, 0, 3, 0, 4, 0,
- 4, 0, 1, 0, 0, 0,
- 0, 0, 0, 0, 116, 101,
- 120, 48, 0, 171, 171, 171,
- 4, 0, 12, 0, 1, 0,
- 1, 0, 1, 0, 0, 0,
- 0, 0, 0, 0, 116, 101,
- 120, 49, 0, 171, 171, 171,
- 4, 0, 12, 0, 1, 0,
- 1, 0, 1, 0, 0, 0,
- 0, 0, 0, 0, 116, 101,
- 120, 50, 0, 171, 171, 171,
- 4, 0, 12, 0, 1, 0,
- 1, 0, 1, 0, 0, 0,
- 0, 0, 0, 0, 112, 115,
- 95, 50, 95, 48, 0, 77,
- 105, 99, 114, 111, 115, 111,
- 102, 116, 32, 40, 82, 41,
- 32, 72, 76, 83, 76, 32,
- 83, 104, 97, 100, 101, 114,
- 32, 67, 111, 109, 112, 105,
- 108, 101, 114, 32, 57, 46,
- 50, 55, 46, 57, 53, 50,
- 46, 51, 48, 50, 50, 0,
- 81, 0, 0, 5, 4, 0,
- 15, 160, 0, 0, 128, 63,
- 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0,
- 31, 0, 0, 2, 0, 0,
- 0, 128, 0, 0, 3, 176,
- 31, 0, 0, 2, 0, 0,
- 0, 128, 1, 0, 3, 176,
- 31, 0, 0, 2, 0, 0,
- 0, 128, 2, 0, 3, 176,
- 31, 0, 0, 2, 0, 0,
- 0, 144, 0, 8, 15, 160,
- 31, 0, 0, 2, 0, 0,
- 0, 144, 1, 8, 15, 160,
- 31, 0, 0, 2, 0, 0,
- 0, 144, 2, 8, 15, 160,
- 66, 0, 0, 3, 0, 0,
- 15, 128, 0, 0, 228, 176,
- 0, 8, 228, 160, 66, 0,
- 0, 3, 1, 0, 15, 128,
- 1, 0, 228, 176, 1, 8,
- 228, 160, 66, 0, 0, 3,
- 2, 0, 15, 128, 2, 0,
- 228, 176, 2, 8, 228, 160,
- 1, 0, 0, 2, 0, 0,
- 2, 128, 1, 0, 0, 128,
- 1, 0, 0, 2, 0, 0,
- 4, 128, 2, 0, 0, 128,
- 1, 0, 0, 2, 0, 0,
- 8, 128, 4, 0, 0, 160,
- 9, 0, 0, 3, 1, 0,
- 1, 128, 0, 0, 228, 128,
- 0, 0, 228, 160, 9, 0,
- 0, 3, 1, 0, 2, 128,
- 0, 0, 228, 128, 1, 0,
- 228, 160, 9, 0, 0, 3,
- 1, 0, 4, 128, 0, 0,
- 228, 128, 2, 0, 228, 160,
- 9, 0, 0, 3, 1, 0,
- 8, 128, 0, 0, 228, 128,
- 3, 0, 228, 160, 1, 0,
- 0, 2, 0, 8, 15, 128,
- 1, 0, 228, 128, 255, 255,
- 0, 0
-};
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+// fxc /Tps_2_0 -DUSE_420P=1 /Fhd3d_shader_420p.h d3d_shader_yuv.hlsl
+// /Vnd3d_shader_420p
+//
+//
+// Parameters:
+//
+// float4x4 colormatrix;
+// sampler2D tex0;
+// sampler2D tex1;
+// sampler2D tex2;
+//
+//
+// Registers:
+//
+// Name Reg Size
+// ------------ ----- ----
+// colormatrix c0 4
+// tex0 s0 1
+// tex1 s1 1
+// tex2 s2 1
+//
+
+ ps_2_0
+ def c4, 1, 0, 0, 0
+ dcl t0.xy
+ dcl t1.xy
+ dcl t2.xy
+ dcl_2d s0
+ dcl_2d s1
+ dcl_2d s2
+ texld r0, t0, s0
+ texld r1, t1, s1
+ texld r2, t2, s2
+ mov r0.y, r1.x
+ mov r0.z, r2.x
+ mov r0.w, c4.x
+ dp4 r1.x, r0, c0
+ dp4 r1.y, r0, c1
+ dp4 r1.z, r0, c2
+ dp4 r1.w, r0, c3
+ mov oC0, r1
+
+// approximately 11 instruction slots used (3 texture, 8 arithmetic)
+#endif
+
+const BYTE d3d_shader_420p[] =
+{
+ 0, 2, 255, 255, 254, 255,
+ 67, 0, 67, 84, 65, 66,
+ 28, 0, 0, 0, 215, 0,
+ 0, 0, 0, 2, 255, 255,
+ 4, 0, 0, 0, 28, 0,
+ 0, 0, 0, 1, 0, 0,
+ 208, 0, 0, 0, 108, 0,
+ 0, 0, 2, 0, 0, 0,
+ 4, 0, 2, 0, 120, 0,
+ 0, 0, 0, 0, 0, 0,
+ 136, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 2, 0,
+ 144, 0, 0, 0, 0, 0,
+ 0, 0, 160, 0, 0, 0,
+ 3, 0, 1, 0, 1, 0,
+ 6, 0, 168, 0, 0, 0,
+ 0, 0, 0, 0, 184, 0,
+ 0, 0, 3, 0, 2, 0,
+ 1, 0, 10, 0, 192, 0,
+ 0, 0, 0, 0, 0, 0,
+ 99, 111, 108, 111, 114, 109,
+ 97, 116, 114, 105, 120, 0,
+ 3, 0, 3, 0, 4, 0,
+ 4, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 116, 101,
+ 120, 48, 0, 171, 171, 171,
+ 4, 0, 12, 0, 1, 0,
+ 1, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 116, 101,
+ 120, 49, 0, 171, 171, 171,
+ 4, 0, 12, 0, 1, 0,
+ 1, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 116, 101,
+ 120, 50, 0, 171, 171, 171,
+ 4, 0, 12, 0, 1, 0,
+ 1, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 112, 115,
+ 95, 50, 95, 48, 0, 77,
+ 105, 99, 114, 111, 115, 111,
+ 102, 116, 32, 40, 82, 41,
+ 32, 72, 76, 83, 76, 32,
+ 83, 104, 97, 100, 101, 114,
+ 32, 67, 111, 109, 112, 105,
+ 108, 101, 114, 32, 57, 46,
+ 50, 57, 46, 57, 53, 50,
+ 46, 51, 49, 49, 49, 0,
+ 81, 0, 0, 5, 4, 0,
+ 15, 160, 0, 0, 128, 63,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 31, 0, 0, 2, 0, 0,
+ 0, 128, 0, 0, 3, 176,
+ 31, 0, 0, 2, 0, 0,
+ 0, 128, 1, 0, 3, 176,
+ 31, 0, 0, 2, 0, 0,
+ 0, 128, 2, 0, 3, 176,
+ 31, 0, 0, 2, 0, 0,
+ 0, 144, 0, 8, 15, 160,
+ 31, 0, 0, 2, 0, 0,
+ 0, 144, 1, 8, 15, 160,
+ 31, 0, 0, 2, 0, 0,
+ 0, 144, 2, 8, 15, 160,
+ 66, 0, 0, 3, 0, 0,
+ 15, 128, 0, 0, 228, 176,
+ 0, 8, 228, 160, 66, 0,
+ 0, 3, 1, 0, 15, 128,
+ 1, 0, 228, 176, 1, 8,
+ 228, 160, 66, 0, 0, 3,
+ 2, 0, 15, 128, 2, 0,
+ 228, 176, 2, 8, 228, 160,
+ 1, 0, 0, 2, 0, 0,
+ 2, 128, 1, 0, 0, 128,
+ 1, 0, 0, 2, 0, 0,
+ 4, 128, 2, 0, 0, 128,
+ 1, 0, 0, 2, 0, 0,
+ 8, 128, 4, 0, 0, 160,
+ 9, 0, 0, 3, 1, 0,
+ 1, 128, 0, 0, 228, 128,
+ 0, 0, 228, 160, 9, 0,
+ 0, 3, 1, 0, 2, 128,
+ 0, 0, 228, 128, 1, 0,
+ 228, 160, 9, 0, 0, 3,
+ 1, 0, 4, 128, 0, 0,
+ 228, 128, 2, 0, 228, 160,
+ 9, 0, 0, 3, 1, 0,
+ 8, 128, 0, 0, 228, 128,
+ 3, 0, 228, 160, 1, 0,
+ 0, 2, 0, 8, 15, 128,
+ 1, 0, 228, 128, 255, 255,
+ 0, 0
+};
diff --git a/video/out/d3d_shader_nv12.h b/video/out/d3d_shader_nv12.h
new file mode 100644
index 0000000..74b592f
--- /dev/null
+++ b/video/out/d3d_shader_nv12.h
@@ -0,0 +1,123 @@
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+// fxc /Tps_2_0 -DUSE_NV12=1 /Fhd3d_shader_nv12.h d3d_shader_yuv.hlsl
+// /Vnd3d_shader_nv12
+//
+//
+// Parameters:
+//
+// float4x4 colormatrix;
+// sampler2D tex0;
+// sampler2D tex1;
+//
+//
+// Registers:
+//
+// Name Reg Size
+// ------------ ----- ----
+// colormatrix c0 4
+// tex0 s0 1
+// tex1 s1 1
+//
+
+ ps_2_0
+ def c4, 1, 0, 0, 0
+ dcl t0.xy
+ dcl t1.xy
+ dcl_2d s0
+ dcl_2d s1
+ texld r0, t0, s0
+ texld r1, t1, s1
+ mov r0.y, r1.x
+ mov r0.z, r1.z
+ mov r0.w, c4.x
+ dp4 r1.x, r0, c0
+ dp4 r1.y, r0, c1
+ dp4 r1.z, r0, c2
+ dp4 r1.w, r0, c3
+ mov oC0, r1
+
+// approximately 10 instruction slots used (2 texture, 8 arithmetic)
+#endif
+
+const BYTE d3d_shader_nv12[] =
+{
+ 0, 2, 255, 255, 254, 255,
+ 56, 0, 67, 84, 65, 66,
+ 28, 0, 0, 0, 171, 0,
+ 0, 0, 0, 2, 255, 255,
+ 3, 0, 0, 0, 28, 0,
+ 0, 0, 0, 1, 0, 0,
+ 164, 0, 0, 0, 88, 0,
+ 0, 0, 2, 0, 0, 0,
+ 4, 0, 2, 0, 100, 0,
+ 0, 0, 0, 0, 0, 0,
+ 116, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 2, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 140, 0, 0, 0,
+ 3, 0, 1, 0, 1, 0,
+ 6, 0, 148, 0, 0, 0,
+ 0, 0, 0, 0, 99, 111,
+ 108, 111, 114, 109, 97, 116,
+ 114, 105, 120, 0, 3, 0,
+ 3, 0, 4, 0, 4, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 116, 101, 120, 48,
+ 0, 171, 171, 171, 4, 0,
+ 12, 0, 1, 0, 1, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 116, 101, 120, 49,
+ 0, 171, 171, 171, 4, 0,
+ 12, 0, 1, 0, 1, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 112, 115, 95, 50,
+ 95, 48, 0, 77, 105, 99,
+ 114, 111, 115, 111, 102, 116,
+ 32, 40, 82, 41, 32, 72,
+ 76, 83, 76, 32, 83, 104,
+ 97, 100, 101, 114, 32, 67,
+ 111, 109, 112, 105, 108, 101,
+ 114, 32, 57, 46, 50, 57,
+ 46, 57, 53, 50, 46, 51,
+ 49, 49, 49, 0, 81, 0,
+ 0, 5, 4, 0, 15, 160,
+ 0, 0, 128, 63, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 31, 0,
+ 0, 2, 0, 0, 0, 128,
+ 0, 0, 3, 176, 31, 0,
+ 0, 2, 0, 0, 0, 128,
+ 1, 0, 3, 176, 31, 0,
+ 0, 2, 0, 0, 0, 144,
+ 0, 8, 15, 160, 31, 0,
+ 0, 2, 0, 0, 0, 144,
+ 1, 8, 15, 160, 66, 0,
+ 0, 3, 0, 0, 15, 128,
+ 0, 0, 228, 176, 0, 8,
+ 228, 160, 66, 0, 0, 3,
+ 1, 0, 15, 128, 1, 0,
+ 228, 176, 1, 8, 228, 160,
+ 1, 0, 0, 2, 0, 0,
+ 2, 128, 1, 0, 0, 128,
+ 1, 0, 0, 2, 0, 0,
+ 4, 128, 1, 0, 170, 128,
+ 1, 0, 0, 2, 0, 0,
+ 8, 128, 4, 0, 0, 160,
+ 9, 0, 0, 3, 1, 0,
+ 1, 128, 0, 0, 228, 128,
+ 0, 0, 228, 160, 9, 0,
+ 0, 3, 1, 0, 2, 128,
+ 0, 0, 228, 128, 1, 0,
+ 228, 160, 9, 0, 0, 3,
+ 1, 0, 4, 128, 0, 0,
+ 228, 128, 2, 0, 228, 160,
+ 9, 0, 0, 3, 1, 0,
+ 8, 128, 0, 0, 228, 128,
+ 3, 0, 228, 160, 1, 0,
+ 0, 2, 0, 8, 15, 128,
+ 1, 0, 228, 128, 255, 255,
+ 0, 0
+};
diff --git a/video/out/d3d_shader_yuv.hlsl b/video/out/d3d_shader_yuv.hlsl
index 0e78554..b74f42e 100644
--- a/video/out/d3d_shader_yuv.hlsl
+++ b/video/out/d3d_shader_yuv.hlsl
@@ -1,5 +1,6 @@
// Compile with:
-// fxc.exe /Tps_2_0 /Fhd3d_shader_yuv.h d3d_shader_yuv.hlsl /Vnd3d_shader_yuv
+// fxc.exe /Tps_2_0 -DUSE_420P=1 /Fhd3d_shader_420p.h d3d_shader_yuv.hlsl /Vnd3d_shader_420p
+// fxc.exe /Tps_2_0 -DUSE_NV12=1 /Fhd3d_shader_nv12.h d3d_shader_yuv.hlsl /Vnd3d_shader_nv12
// Be careful with this shader. You can't use constant slots, since we don't
// load the shader with D3DX. All uniform variables are mapped to hardcoded
@@ -11,19 +12,21 @@ sampler2D tex2 : register(s2);
uniform float4x4 colormatrix : register(c0);
-float1 sample(sampler2D tex, float2 t)
-{
- return tex2D(tex, t).x;
-}
-
float4 main(float2 t0 : TEXCOORD0,
float2 t1 : TEXCOORD1,
float2 t2 : TEXCOORD2)
: COLOR
{
- float4 c = float4(sample(tex0, t0),
- sample(tex1, t1),
- sample(tex2, t2),
+#ifdef USE_420P
+ float4 c = float4(tex2D(tex0, t0).x,
+ tex2D(tex1, t1).x,
+ tex2D(tex2, t2).x,
+ 1);
+#endif
+#ifdef USE_NV12
+ float4 c = float4(tex2D(tex0, t0).x,
+ tex2D(tex1, t1).xz,
1);
+#endif
return mul(c, colormatrix);
}
diff --git a/video/out/dither.c b/video/out/dither.c
index 1fd458b..a164ee8 100644
--- a/video/out/dither.c
+++ b/video/out/dither.c
@@ -1,28 +1,28 @@
-/******************************************************************************
-
- dither.c - generate a dithering matrix for downsampling images
- Copyright © 2013 Wessel Dankers <wsl@fruit.je>
- 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/>.
-
- 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.
-
-******************************************************************************/
+/*
+ * Generate a dithering matrix for downsampling images.
+ *
+ * Copyright © 2013 Wessel Dankers <wsl@fruit.je>
+ *
+ * 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/>.
+ *
+ * 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.
+ */
#include <stdio.h>
#include <stdint.h>
diff --git a/video/out/drm_common.c b/video/out/drm_common.c
new file mode 100644
index 0000000..c61ad69
--- /dev/null
+++ b/video/out/drm_common.c
@@ -0,0 +1,149 @@
+/*
+ * This file is part of mpv.
+ *
+ * by rr- <rr-@sakuya.pl>
+ *
+ * mpv is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * mpv is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <errno.h>
+#include <signal.h>
+#include <sys/ioctl.h>
+#include <sys/poll.h>
+#include <sys/vt.h>
+#include <unistd.h>
+
+#include "drm_common.h"
+
+#include "common/common.h"
+#include "common/msg.h"
+#include "osdep/io.h"
+
+#define EVT_RELEASE 1
+#define EVT_ACQUIRE 2
+#define EVT_INTERRUPT 255
+#define HANDLER_ACQUIRE 0
+#define HANDLER_RELEASE 1
+
+static int vt_switcher_pipe[2];
+
+static void vt_switcher_sighandler(int sig)
+{
+ unsigned char event = sig == SIGUSR1 ? EVT_RELEASE : EVT_ACQUIRE;
+ write(vt_switcher_pipe[1], &event, sizeof(event));
+}
+
+int vt_switcher_init(struct vt_switcher *s, struct mp_log *log)
+{
+ s->log = log;
+ s->tty_fd = -1;
+ vt_switcher_pipe[0] = -1;
+ vt_switcher_pipe[1] = -1;
+
+ if (mp_make_cloexec_pipe(vt_switcher_pipe)) {
+ MP_ERR(s, "Creating pipe failed: %s", mp_strerror(errno));
+ return -1;
+ }
+
+ s->tty_fd = open("/dev/tty", O_RDWR | O_CLOEXEC);
+ if (s->tty_fd < 0) {
+ MP_ERR(s, "Can't open TTY for VT control: %s", mp_strerror(errno));
+ return -1;
+ }
+
+ struct sigaction act;
+ act.sa_handler = vt_switcher_sighandler;
+ act.sa_flags = SA_RESTART;
+ sigemptyset(&act.sa_mask);
+ sigaction(SIGUSR1, &act, 0);
+ sigaction(SIGUSR2, &act, 0);
+
+ struct vt_mode vt_mode;
+ if (ioctl(s->tty_fd, VT_GETMODE, &vt_mode) < 0) {
+ MP_ERR(s, "VT_GETMODE failed: %s", mp_strerror(errno));
+ return -1;
+ }
+
+ vt_mode.mode = VT_PROCESS;
+ vt_mode.relsig = SIGUSR1;
+ vt_mode.acqsig = SIGUSR2;
+ if (ioctl(s->tty_fd, VT_SETMODE, &vt_mode) < 0) {
+ MP_ERR(s, "VT_SETMODE failed: %s", mp_strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+void vt_switcher_acquire(struct vt_switcher *s,
+ void (*handler)(void*), void *user_data)
+{
+ s->handlers[HANDLER_ACQUIRE] = handler;
+ s->handler_data[HANDLER_ACQUIRE] = user_data;
+}
+
+void vt_switcher_release(struct vt_switcher *s,
+ void (*handler)(void*), void *user_data)
+{
+ s->handlers[HANDLER_RELEASE] = handler;
+ s->handler_data[HANDLER_RELEASE] = user_data;
+}
+
+void vt_switcher_interrupt_poll(struct vt_switcher *s)
+{
+ unsigned char event = EVT_INTERRUPT;
+ write(vt_switcher_pipe[1], &event, sizeof(event));
+}
+
+void vt_switcher_destroy(struct vt_switcher *s)
+{
+ close(s->tty_fd);
+ close(vt_switcher_pipe[0]);
+ close(vt_switcher_pipe[1]);
+}
+
+void vt_switcher_poll(struct vt_switcher *s, int timeout_ms)
+{
+ struct pollfd fds[1] = {
+ { .events = POLLIN, .fd = vt_switcher_pipe[0] },
+ };
+ poll(fds, 1, timeout_ms);
+ if (!fds[0].revents)
+ return;
+
+ unsigned char event;
+ if (read(fds[0].fd, &event, sizeof(event)) != sizeof(event))
+ return;
+
+ switch (event) {
+ case EVT_RELEASE:
+ s->handlers[HANDLER_RELEASE](s->handler_data[HANDLER_RELEASE]);
+
+ if (ioctl(s->tty_fd, VT_RELDISP, 1) < 0) {
+ MP_ERR(s, "Failed to release virtual terminal\n");
+ }
+ break;
+
+ case EVT_ACQUIRE:
+ s->handlers[HANDLER_ACQUIRE](s->handler_data[HANDLER_ACQUIRE]);
+
+ if (ioctl(s->tty_fd, VT_RELDISP, VT_ACKACQ) < 0) {
+ MP_ERR(s, "Failed to acquire virtual terminal\n");
+ }
+ break;
+
+ case EVT_INTERRUPT:
+ break;
+ }
+}
diff --git a/video/out/drm_common.h b/video/out/drm_common.h
new file mode 100644
index 0000000..5e6c191
--- /dev/null
+++ b/video/out/drm_common.h
@@ -0,0 +1,36 @@
+/*
+ * This file is part of mpv.
+ *
+ * mpv is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * mpv is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef MP_VT_SWITCHER_H
+#define MP_VT_SWITCHER_H
+
+struct vt_switcher {
+ int tty_fd;
+ struct mp_log *log;
+ void (*handlers[2])(void*);
+ void *handler_data[2];
+};
+
+int vt_switcher_init(struct vt_switcher *s, struct mp_log *log);
+void vt_switcher_destroy(struct vt_switcher *s);
+void vt_switcher_poll(struct vt_switcher *s, int timeout_ms);
+void vt_switcher_interrupt_poll(struct vt_switcher *s);
+
+void vt_switcher_acquire(struct vt_switcher *s, void (*handler)(void*), void *user_data);
+void vt_switcher_release(struct vt_switcher *s, void (*handler)(void*), void *user_data);
+
+#endif
diff --git a/video/out/filter_kernels.c b/video/out/filter_kernels.c
index ebf6891..c759cbb 100644
--- a/video/out/filter_kernels.c
+++ b/video/out/filter_kernels.c
@@ -1,35 +1,34 @@
/*
- * This file is part of mplayer2.
- *
* 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
*
- * Also see glumpy (BSD licensed), contains the same code in Python:
- * http://code.google.com/p/glumpy/source/browse/glumpy/image/filter.py
+ * Copyright (C) 2002-2006 Maxim Shemanarev
*
- * Also see 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
+ * http://vector-agg.cvs.sourceforge.net/viewvc/vector-agg/agg-2.5/include/agg_image_filters.h?view=markup
*
- * Also see: Paul Heckbert's "zoom"
+ * Also see:
+ * - glumpy (BSD licensed), contains the same code in Python:
+ * http://code.google.com/p/glumpy/source/browse/glumpy/image/filter.py
+ * - 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
+ * - Paul Heckbert's "zoom"
+ * - XBMC: ConvolutionKernels.cpp etc.
*
- * Also see XBMC: ConvolutionKernels.cpp etc.
+ * This file is part of mpv.
*
- * mplayer2 is free software; you can redistribute it and/or modify
+ * 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.
*
- * mplayer2 is distributed in the hope that it will be useful,
+ * 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 mplayer2; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stddef.h>
@@ -39,13 +38,25 @@
#include "filter_kernels.h"
-// NOTE: all filters are separable, symmetric, and are intended for use with
-// a lookup table/texture.
+// NOTE: all filters are designed for discrete convolution
+
+const struct filter_window *mp_find_filter_window(const char *name)
+{
+ if (!name)
+ return NULL;
+ for (const struct filter_window *w = mp_filter_windows; w->name; w++) {
+ if (strcmp(w->name, name) == 0)
+ return w;
+ }
+ return NULL;
+}
const struct filter_kernel *mp_find_filter_kernel(const char *name)
{
- for (const struct filter_kernel *k = mp_filter_kernels; k->name; k++) {
- if (strcmp(k->name, name) == 0)
+ if (!name)
+ return NULL;
+ for (const struct filter_kernel *k = mp_filter_kernels; k->f.name; k++) {
+ if (strcmp(k->f.name, name) == 0)
return k;
}
return NULL;
@@ -56,16 +67,22 @@ const struct filter_kernel *mp_find_filter_kernel(const char *name)
bool mp_init_filter(struct filter_kernel *filter, const int *sizes,
double inv_scale)
{
- assert(filter->radius > 0);
- // polar filters are dependent only on the radius
+ assert(filter->f.radius > 0);
+ // Only downscaling requires widening the filter
+ filter->inv_scale = inv_scale >= 1.0 ? inv_scale : 1.0;
+ filter->f.radius *= filter->inv_scale;
+ // Polar filters are dependent solely on the radius
if (filter->polar) {
+ filter->f.radius = fmin(filter->f.radius, 16.0);
filter->size = 1;
+ // Safety precaution to avoid generating a gigantic shader
+ if (filter->f.radius > 16.0) {
+ filter->f.radius = 16.0;
+ return false;
+ }
return true;
}
- // only downscaling requires widening the filter
- filter->inv_scale = inv_scale >= 1.0 ? inv_scale : 1.0;
- double support = filter->radius * filter->inv_scale;
- int size = ceil(2.0 * support);
+ int size = ceil(2.0 * filter->f.radius);
// round up to smallest available size that's still large enough
if (size < sizes[0])
size = sizes[0];
@@ -80,27 +97,42 @@ bool mp_init_filter(struct filter_kernel *filter, const int *sizes,
// largest filter available. This is incorrect, but better than refusing
// to do anything.
filter->size = cursize[-1];
- filter->inv_scale = filter->size / 2.0 / filter->radius;
+ filter->inv_scale *= (filter->size/2.0) / filter->f.radius;
return false;
}
}
+// Sample from the blurred, windowed kernel. Note: The window is always
+// stretched to the true radius, regardless of the filter blur/scale.
+static double sample_filter(struct filter_kernel *filter,
+ struct filter_window *window, double x)
+{
+ double bk = filter->f.blur > 0.0 ? filter->f.blur : 1.0;
+ double bw = window->blur > 0.0 ? window->blur : 1.0;
+ double c = fabs(x) / (filter->inv_scale * bk);
+ double w = window->weight ? window->weight(window, x/bw * window->radius
+ / filter->f.radius)
+ : 1.0;
+ return c < filter->f.radius ? w * filter->f.weight(&filter->f, c) : 0.0;
+}
+
// Calculate the 1D filtering kernel for N sample points.
// N = number of samples, which is filter->size
// The weights will be stored in out_w[0] to out_w[N - 1]
// f = x0 - abs(x0), subpixel position in the range [0,1) or [0,1].
-void mp_compute_weights(struct filter_kernel *filter, double f, float *out_w)
+static void mp_compute_weights(struct filter_kernel *filter,
+ struct filter_window *window,
+ double f, float *out_w)
{
assert(filter->size > 0);
double sum = 0;
for (int n = 0; n < filter->size; n++) {
double x = f - (n - filter->size / 2 + 1);
- double c = fabs(x) / filter->inv_scale;
- double w = c <= filter->radius ? filter->weight(filter, c) : 0;
+ double w = sample_filter(filter, window, x);
out_w[n] = w;
sum += w;
}
- //normalize
+ // Normalize to preserve energy
for (int n = 0; n < filter->size; n++)
out_w[n] /= sum;
}
@@ -109,50 +141,47 @@ void mp_compute_weights(struct filter_kernel *filter, double f, float *out_w)
// interpreted as rectangular array of count * filter->size items.
void mp_compute_lut(struct filter_kernel *filter, int count, float *out_array)
{
+ struct filter_window *window = &filter->w;
if (filter->polar) {
// Compute a 1D array indexed by radius
- assert(filter->radius > 0);
for (int x = 0; x < count; x++) {
- double r = x * filter->radius / (count - 1);
- out_array[x] = r <= filter->radius ? filter->weight(filter, r) : 0;
+ double r = x * filter->f.radius / (count - 1);
+ out_array[x] = sample_filter(filter, window, r);
}
} else {
// Compute a 2D array indexed by subpixel position
for (int n = 0; n < count; n++) {
- mp_compute_weights(filter, n / (double)(count - 1),
+ mp_compute_weights(filter, window, n / (double)(count - 1),
out_array + filter->size * n);
}
}
}
-typedef struct filter_kernel kernel;
+typedef struct filter_window params;
-static double nearest(kernel *k, double x)
+static double box(params *p, double x)
{
- return x > 0.5 ? 0.0 : 1.0;
+ // This is mathematically 1.0 everywhere, the clipping is done implicitly
+ // based on the radius.
+ return 1.0;
}
-static double bilinear(kernel *k, double x)
+static double triangle(params *p, double x)
{
- return 1.0 - x;
+ return fmax(0.0, 1.0 - fabs(x / p->radius));
}
-static double hanning(kernel *k, double x)
+static double hanning(params *p, double x)
{
return 0.5 + 0.5 * cos(M_PI * x);
}
-static double hamming(kernel *k, double x)
+static double hamming(params *p, double x)
{
return 0.54 + 0.46 * cos(M_PI * x);
}
-static double hermite(kernel *k, double x)
-{
- return (2.0 * x - 3.0) * x * x + 1.0;
-}
-
-static double quadric(kernel *k, double x)
+static double quadric(params *p, double x)
{
// NOTE: glumpy uses 0.75, AGG uses 0.5
if (x < 0.5)
@@ -167,7 +196,7 @@ static double bc_pow3(double x)
return (x <= 0) ? 0 : x * x * x;
}
-static double bicubic(kernel *k, double x)
+static double bicubic(params *p, double x)
{
return (1.0/6.0) * ( bc_pow3(x + 2)
- 4 * bc_pow3(x + 1)
@@ -187,29 +216,32 @@ static double bessel_i0(double epsilon, double x)
return sum;
}
-static double kaiser(kernel *k, double x)
+static double kaiser(params *p, double x)
{
- double a = k->params[0];
- double b = k->params[1];
+ double a = p->params[0];
double epsilon = 1e-12;
- double i0a = 1 / bessel_i0(epsilon, b);
+ double i0a = 1 / bessel_i0(epsilon, a);
return bessel_i0(epsilon, a * sqrt(1 - x * x)) * i0a;
}
-static double catmull_rom(kernel *k, double x)
+static double blackman(params *p, double x)
{
- if (x < 1.0)
- return 0.5 * (2.0 + x * x * (-5.0 + x * 3.0));
- if (x < 2.0)
- return 0.5 * (4.0 + x * (-8.0 + x * (5.0 - x)));
- return 0;
+ double a = p->params[0];
+ double a0 = (1-a)/2.0, a1 = 1/2.0, a2 = a/2.0;
+ double pix = M_PI * x;
+ return a0 + a1*cos(pix) + a2*cos(2 * pix);
+}
+
+static double welch(params *p, double x)
+{
+ return 1.0 - x*x;
}
-// Mitchell-Netravali
-static double mitchell(kernel *k, double x)
+// Family of cubic B/C splines
+static double cubic_bc(params *p, double x)
{
- double b = k->params[0];
- double c = k->params[1];
+ 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,
@@ -225,14 +257,14 @@ static double mitchell(kernel *k, double x)
return 0;
}
-static double spline16(kernel *k, double x)
+static double spline16(params *p, double x)
{
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);
}
-static double spline36(kernel *k, double x)
+static double spline36(params *p, double x)
{
if(x < 1.0)
return ((13.0/11.0 * x - 453.0/209.0) * x - 3.0/209.0) * x + 1.0;
@@ -243,7 +275,7 @@ static double spline36(kernel *k, double x)
* (x - 2);
}
-static double spline64(kernel *k, double x)
+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;
@@ -257,86 +289,89 @@ static double spline64(kernel *k, double x)
* (x - 3);
}
-static double gaussian(kernel *k, double x)
+static double gaussian(params *p, double x)
{
- double p = k->params[0];
- if (p > 100.0)
- p = 100.0;
- if (p < 1.0)
- p = 1.0;
- return pow(2.0, -(p / 10.0) * x * x);
+ return pow(2.0, -(M_E / p->params[0]) * x * x);
}
-static double sinc(kernel *k, double x)
+static double sinc(params *p, double x)
{
- if (x == 0.0)
+ if (fabs(x) < 1e-8)
return 1.0;
double pix = M_PI * x;
return sin(pix) / pix;
}
-static double jinc(kernel *k, double x)
+static double jinc(params *p, double x)
{
- if (x == 0.0)
+ if (fabs(x) < 1e-8)
return 1.0;
double pix = M_PI * x;
return 2.0 * j1(pix) / pix;
}
-static double lanczos(kernel *k, double x)
-{
- double radius = k->size / 2;
- if (x < -radius || x > radius)
- return 0;
- if (x == 0)
- return 1;
- double pix = M_PI * x;
- return radius * sin(pix) * sin(pix / radius) / (pix * pix);
-}
-
-static double ewa_lanczos(kernel *k, double x)
+static double sphinx(params *p, double x)
{
- double radius = k->radius;
if (fabs(x) < 1e-8)
return 1.0;
- if (fabs(x) >= radius)
- return 0.0;
- // First zero of the jinc function. We simply scale it to fit into the
- // given radius.
- double jinc_zero = 1.2196698912665045;
- return jinc(k, x) * jinc(k, x * jinc_zero / radius);
-}
-
-static double blackman(kernel *k, double x)
-{
- double radius = k->size / 2;
- if (x == 0.0)
- return 1.0;
- if (x > radius)
- return 0.0;
- x *= M_PI;
- double xr = x / radius;
- return (sin(x) / x) * (0.42 + 0.5 * cos(xr) + 0.08 * cos(2 * xr));
+ double pix = M_PI * x;
+ return 3.0 * (sin(pix) - pix * cos(pix)) / (pix * pix * pix);
}
-const struct filter_kernel mp_filter_kernels[] = {
- {"nearest", 0.5, nearest},
- {"bilinear_slow", 1, bilinear},
+const struct filter_window mp_filter_windows[] = {
+ {"box", 1, box},
+ {"triangle", 1, triangle},
+ {"bartlett", 1, triangle},
{"hanning", 1, hanning},
{"hamming", 1, hamming},
- {"hermite", 1, hermite},
{"quadric", 1.5, quadric},
- {"bicubic", 2, bicubic},
- {"kaiser", 1, kaiser, .params = {6.33, 6.33} },
- {"catmull_rom", 2, catmull_rom},
- {"mitchell", 2, mitchell, .params = {1.0/3.0, 1.0/3.0} },
- {"spline16", 2, spline16},
- {"spline36", 3, spline36},
- {"spline64", 4, spline64},
- {"gaussian", -1, gaussian, .params = {28.85390081777927, NAN} },
- {"sinc", -1, sinc},
- {"ewa_lanczos", -1, ewa_lanczos, .polar = true},
- {"lanczos", -1, lanczos},
- {"blackman", -1, blackman},
+ {"welch", 1, welch},
+ {"kaiser", 1, kaiser, .params = {6.33, NAN} },
+ {"blackman", 1, blackman, .params = {0.16, NAN} },
+ {"gaussian", 2, gaussian, .params = {1.0, NAN} },
+ {"sinc", 1, sinc},
+ {"jinc", 1.2196698912665045, jinc},
+ {"sphinx", 1.4302966531242027, sphinx},
{0}
};
+
+const struct filter_kernel mp_filter_kernels[] = {
+ // Spline filters
+ {{"spline16", 2, spline16}},
+ {{"spline36", 3, spline36}},
+ {{"spline64", 4, spline64}},
+ // Sinc filters
+ {{"sinc", 2, sinc, .resizable = true}},
+ {{"lanczos", 3, sinc, .resizable = true}, .window = "sinc"},
+ {{"ginseng", 3, sinc, .resizable = true}, .window = "jinc"},
+ // Jinc filters
+ {{"jinc", 3, jinc, .resizable = true}, .polar = true},
+ {{"ewa_lanczos", 3, jinc, .resizable = true}, .polar = true, .window = "jinc"},
+ {{"ewa_hanning", 3, jinc, .resizable = true}, .polar = true, .window = "hanning" },
+ {{"ewa_ginseng", 3, jinc, .resizable = true}, .polar = true, .window = "sinc"},
+ // Radius is based on the true jinc radius, slightly sharpened as per
+ // calculations by Nicolas Robidoux. Source: Imagemagick's magick/resize.c
+ {{"ewa_lanczossharp", 3.2383154841662362, jinc, .blur = 0.9812505644269356,
+ .resizable = true}, .polar = true, .window = "jinc"},
+ // Similar to the above, but softened instead. This one makes hash patterns
+ // disappear completely. Blur determined by trial and error.
+ {{"ewa_lanczossoft", 3.2383154841662362, jinc, .blur = 1.015,
+ .resizable = true}, .polar = true, .window = "jinc"},
+ // Very soft (blurred) hanning-windowed jinc; removes almost all aliasing.
+ // Blur paramater picked to match orthogonal and diagonal contributions
+ {{"haasnsoft", 3.2383154841662362, jinc, .blur = 1.11, .resizable = true},
+ .polar = true, .window = "hanning"},
+ // Cubic filters
+ {{"bicubic", 2, bicubic}},
+ {{"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}}, .polar = true},
+ {{"robidouxsharp", 2, cubic_bc, .params = {0.2620, 0.3690}}, .polar = true},
+ // Miscalleaneous filters
+ {{"box", 1, box, .resizable = true}},
+ {{"nearest", 0.5, box}},
+ {{"triangle", 1, triangle, .resizable = true}},
+ {{"gaussian", 2, gaussian, .params = {1.0, NAN}, .resizable = true}},
+ {{0}}
+};
diff --git a/video/out/filter_kernels.h b/video/out/filter_kernels.h
index b2e0786..9e61762 100644
--- a/video/out/filter_kernels.h
+++ b/video/out/filter_kernels.h
@@ -1,19 +1,18 @@
/*
- * This file is part of mplayer2.
+ * This file is part of mpv.
*
- * mplayer2 is free software; you can redistribute it and/or modify
+ * 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.
*
- * mplayer2 is distributed in the hope that it will be useful,
+ * 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 mplayer2; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_FILTER_KERNELS_H
@@ -21,26 +20,35 @@
#include <stdbool.h>
-struct filter_kernel {
+struct filter_window {
const char *name;
- double radius; // A negative value will use user specified radius instead.
- double (*weight)(struct filter_kernel *kernel, double x);
+ double radius; // Preferred radius, should only be changed if resizable
+ double (*weight)(struct filter_window *k, double x);
+ bool resizable; // Filter supports any given radius
+ double params[2]; // User-defined custom filter parameters. Not used by
+ // all filters
+ double blur; // Blur coefficient (sharpens or widens the filter)
+};
- // The filter params can be changed at runtime. Only used by some filters.
- float params[2];
- // Whether or not the filter uses polar coordinates
- bool polar;
+struct filter_kernel {
+ struct filter_window f; // the kernel itself
+ struct filter_window w; // window storage
+ // Constant values
+ const char *window; // default window
+ bool polar; // whether or not the filter uses polar coordinates
// The following values are set by mp_init_filter() at runtime.
int size; // number of coefficients (may depend on radius)
double inv_scale; // scale factor (<1.0 is upscale, >1.0 downscale)
};
+extern const struct filter_window mp_filter_windows[];
extern const struct filter_kernel mp_filter_kernels[];
+const struct filter_window *mp_find_filter_window(const char *name);
const struct filter_kernel *mp_find_filter_kernel(const char *name);
+
bool mp_init_filter(struct filter_kernel *filter, const int *sizes,
double scale);
-void mp_compute_weights(struct filter_kernel *filter, double f, float *out_w);
void mp_compute_lut(struct filter_kernel *filter, int count, float *out_array);
#endif /* MPLAYER_FILTER_KERNELS_H */
diff --git a/video/out/gl_cocoa.c b/video/out/gl_cocoa.c
index d8b0c29..fd12ef4 100644
--- a/video/out/gl_cocoa.c
+++ b/video/out/gl_cocoa.c
@@ -1,17 +1,18 @@
/*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * 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
diff --git a/video/out/gl_common.c b/video/out/gl_common.c
index a059c4d..fc3e9f0 100644
--- a/video/out/gl_common.c
+++ b/video/out/gl_common.c
@@ -5,21 +5,20 @@
* 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 MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * 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
@@ -55,7 +54,6 @@ static const struct feature features[] = {
{MPGL_CAP_VAO, "VAOs"},
{MPGL_CAP_FLOAT_TEX, "Float textures"},
{MPGL_CAP_TEX_RG, "RG textures"},
- {MPGL_CAP_1ST_CLASS_ARRAYS, "1st class shader arrays"},
{MPGL_CAP_1D_TEX, "1D textures"},
{MPGL_CAP_3D_TEX, "3D textures"},
{MPGL_CAP_DEBUG, "debugging extensions"},
@@ -80,14 +78,22 @@ static void GLAPIENTRY dummy_glBindFramebuffer(GLenum target, GLuint framebuffer
assert(framebuffer == 0);
}
+static bool check_ext(GL *gl, const char *name)
+{
+ const char *exts = gl->extensions;
+ char *s = strstr(exts, name);
+ char *e = s ? s + strlen(name) : NULL;
+ return s && (s == exts || s[-1] == ' ') && (e[0] == ' ' || !e[0]);
+}
+
#define FN_OFFS(name) offsetof(GL, name)
-#define DEF_FN(name) {FN_OFFS(name), {"gl" # name}}
-#define DEF_FN_NAMES(name, ...) {FN_OFFS(name), {__VA_ARGS__}}
+#define DEF_FN(name) {FN_OFFS(name), "gl" # name}
+#define DEF_FN_NAME(name, str) {FN_OFFS(name), str}
struct gl_function {
ptrdiff_t offset;
- char *funcnames[7];
+ char *name;
};
struct gl_functions {
@@ -168,8 +174,7 @@ static const struct gl_functions gl_functions[] = {
// GL 2.1+ desktop only (and GLSL 120 shaders)
{
.ver_core = 210,
- .provides = MPGL_CAP_ROW_LENGTH | MPGL_CAP_1D_TEX | MPGL_CAP_3D_TEX |
- MPGL_CAP_1ST_CLASS_ARRAYS,
+ .provides = MPGL_CAP_ROW_LENGTH | MPGL_CAP_1D_TEX | MPGL_CAP_3D_TEX,
.functions = (const struct gl_function[]) {
DEF_FN(DrawBuffer),
DEF_FN(GetTexLevelParameteriv),
@@ -185,7 +190,7 @@ static const struct gl_functions gl_functions[] = {
{
.ver_core = 300,
.ver_es_core = 300,
- .provides = MPGL_CAP_3D_TEX | MPGL_CAP_1ST_CLASS_ARRAYS,
+ .provides = MPGL_CAP_3D_TEX,
.functions = (const struct gl_function[]) {
DEF_FN(GetStringi),
// for ES 3.0
@@ -217,21 +222,6 @@ static const struct gl_functions gl_functions[] = {
{0}
},
},
- // Framebuffers, alternative extension name.
- {
- .ver_removed = 300, // don't touch these fn names in 3.x
- .ver_es_removed = 300,
- .extension = "GL_EXT_framebuffer_object",
- .provides = MPGL_CAP_FB,
- .functions = (const struct gl_function[]) {
- DEF_FN_NAMES(BindFramebuffer, "glBindFramebufferEXT"),
- DEF_FN_NAMES(GenFramebuffers, "glGenFramebuffersEXT"),
- DEF_FN_NAMES(DeleteFramebuffers, "glDeleteFramebuffersEXT"),
- DEF_FN_NAMES(CheckFramebufferStatus, "glCheckFramebufferStatusEXT"),
- DEF_FN_NAMES(FramebufferTexture2D, "glFramebufferTexture2DEXT"),
- {0}
- },
- },
// VAOs, extension in GL 2.x, core in GL 3.x core.
{
.ver_core = 300,
@@ -260,20 +250,26 @@ static const struct gl_functions gl_functions[] = {
.provides = MPGL_CAP_TEX_RG,
},
// Swap control, always an OS specific extension
+ // The OSX code loads this manually.
{
- .extension = "_swap_control",
+ .extension = "GLX_SGI_swap_control",
.functions = (const struct gl_function[]) {
- DEF_FN_NAMES(SwapInterval, "glXSwapIntervalSGI", "glXSwapInterval",
- "wglSwapIntervalSGI", "wglSwapInterval",
- "wglSwapIntervalEXT"),
- {0}
+ DEF_FN_NAME(SwapInterval, "glXSwapIntervalSGI"),
+ {0},
+ },
+ },
+ {
+ .extension = "WGL_EXT_swap_control",
+ .functions = (const struct gl_function[]) {
+ DEF_FN_NAME(SwapInterval, "wglSwapIntervalEXT"),
+ {0},
},
},
{
.extension = "GLX_SGI_video_sync",
.functions = (const struct gl_function[]) {
- DEF_FN_NAMES(GetVideoSync, "glXGetVideoSyncSGI"),
- DEF_FN_NAMES(WaitVideoSync, "glXWaitVideoSyncSGI"),
+ DEF_FN_NAME(GetVideoSync, "glXGetVideoSyncSGI"),
+ DEF_FN_NAME(WaitVideoSync, "glXWaitVideoSyncSGI"),
{0},
},
},
@@ -316,7 +312,7 @@ static const struct gl_functions gl_functions[] = {
#undef FN_OFFS
#undef DEF_FN_HARD
#undef DEF_FN
-#undef DEF_FN_NAMES
+#undef DEF_FN_NAME
// Fill the GL struct with function pointers and extensions from the current
@@ -429,7 +425,7 @@ void mpgl_load_functions2(GL *gl, void *(*get_fn)(void *ctx, const char *n),
if (ver_core)
must_exist = version >= ver_core;
- if (section->extension && strstr(gl->extensions, section->extension))
+ if (section->extension && check_ext(gl, section->extension))
exists = true;
exists |= must_exist;
@@ -440,18 +436,13 @@ void mpgl_load_functions2(GL *gl, void *(*get_fn)(void *ctx, const char *n),
bool all_loaded = true;
const struct gl_function *fnlist = section->functions;
- for (int i = 0; fnlist && fnlist[i].funcnames[0]; i++) {
+ for (int i = 0; fnlist && fnlist[i].name; i++) {
const struct gl_function *fn = &fnlist[i];
- void *ptr = NULL;
- for (int x = 0; fn->funcnames[x]; x++) {
- ptr = get_fn(fn_ctx, fn->funcnames[x]);
- if (ptr)
- break;
- }
+ void *ptr = get_fn(fn_ctx, fn->name);
if (!ptr) {
all_loaded = false;
mp_warn(log, "Required function '%s' not "
- "found for %s OpenGL %d.%d.\n", fn->funcnames[0],
+ "found for %s OpenGL %d.%d.\n", fn->name,
section->extension ? section->extension : "builtin",
MPGL_VER_GET_MAJOR(ver_core),
MPGL_VER_GET_MINOR(ver_core));
@@ -467,7 +458,7 @@ void mpgl_load_functions2(GL *gl, void *(*get_fn)(void *ctx, const char *n),
if (all_loaded) {
gl->mpgl_caps |= section->provides;
- for (int i = 0; fnlist && fnlist[i].funcnames[0]; i++) {
+ for (int i = 0; fnlist && fnlist[i].name; i++) {
const struct gl_function *fn = &fnlist[i];
void **funcptr = (void**)(((char*)gl) + fn->offset);
if (loaded[i])
@@ -537,6 +528,9 @@ struct backend {
};
static const struct backend backends[] = {
+#if HAVE_RPI_GLES
+ {"rpi", mpgl_set_backend_rpi},
+#endif
#if HAVE_GL_COCOA
{"cocoa", mpgl_set_backend_cocoa},
#endif
diff --git a/video/out/gl_common.h b/video/out/gl_common.h
index 98b9d9c..8a13378 100644
--- a/video/out/gl_common.h
+++ b/video/out/gl_common.h
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * 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
@@ -61,7 +60,6 @@ enum {
MPGL_CAP_TEX_RG = (1 << 10), // GL_ARB_texture_rg / GL 3.x
MPGL_CAP_VDPAU = (1 << 11), // GL_NV_vdpau_interop
MPGL_CAP_APPLE_RGB_422 = (1 << 12), // GL_APPLE_rgb_422
- MPGL_CAP_1ST_CLASS_ARRAYS = (1 << 13),
MPGL_CAP_1D_TEX = (1 << 14),
MPGL_CAP_3D_TEX = (1 << 15),
MPGL_CAP_DEBUG = (1 << 16),
@@ -94,6 +92,12 @@ typedef struct MPGLContext {
void (*releaseGlContext)(struct MPGLContext *);
void (*set_current)(struct MPGLContext *, bool current);
+ // Used on windows only, tries to vsync with the DWM, and modifies SwapInterval
+ // when it does so. Returns the possibly modified swapinterval value.
+ int (*DwmFlush)(struct MPGLContext *, int opt_dwmflush,
+ int opt_swapinterval, int current_swapinterval);
+
+
// Resize the window, or create a new window if there isn't one yet.
// On the first call, it creates a GL context according to what's specified
// in MPGLContext.requested_gl_version. This is just a hint, and if the
@@ -110,6 +114,10 @@ typedef struct MPGLContext {
void (*register_resize_callback)(struct vo *vo,
void (*cb)(struct vo *vo, int w, int h));
+ // Optional activity state of context.
+ // If false, OpenGL renderers should not draw anything.
+ bool (*is_active)(struct MPGLContext *);
+
// For free use by the backend.
void *priv;
} MPGLContext;
@@ -134,6 +142,7 @@ void mpgl_set_backend_x11es(MPGLContext *ctx);
void mpgl_set_backend_x11egl(MPGLContext *ctx);
void mpgl_set_backend_x11egles(MPGLContext *ctx);
void mpgl_set_backend_wayland(MPGLContext *ctx);
+void mpgl_set_backend_rpi(MPGLContext *ctx);
void mpgl_load_functions(GL *gl, void *(*getProcAddress)(const GLubyte *),
const char *ext2, struct mp_log *log);
diff --git a/video/out/gl_header_fixes.h b/video/out/gl_header_fixes.h
index 0e29531..6a5ce6a 100644
--- a/video/out/gl_header_fixes.h
+++ b/video/out/gl_header_fixes.h
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * 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
diff --git a/video/out/gl_hwdec.c b/video/out/gl_hwdec.c
index 7b8adb4..9f6928b 100644
--- a/video/out/gl_hwdec.c
+++ b/video/out/gl_hwdec.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * 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
diff --git a/video/out/gl_hwdec.h b/video/out/gl_hwdec.h
index cd2ab26..e60218f 100644
--- a/video/out/gl_hwdec.h
+++ b/video/out/gl_hwdec.h
@@ -37,14 +37,9 @@ struct gl_hwdec_driver {
// Called on initialization, and every time the video size changes.
// *params must be set to the format the hw textures return.
int (*reinit)(struct gl_hwdec *hw, struct mp_image_params *params);
- // Return textures that contain the given hw_image.
- // Note that the caller keeps a reference to hw_image until unmap_image
- // is called, so the hwdec driver doesn't need to do that.
+ // Return textures that contain a copy or reference of the given hw_image.
int (*map_image)(struct gl_hwdec *hw, struct mp_image *hw_image,
GLuint *out_textures);
- // Undo map_image(). The user of map_image() calls this when the textures
- // are not needed anymore.
- void (*unmap_image)(struct gl_hwdec *hw);
void (*destroy)(struct gl_hwdec *hw);
};
diff --git a/video/out/gl_hwdec_vaglx.c b/video/out/gl_hwdec_vaglx.c
index 99d29dc..86ec3b5 100644
--- a/video/out/gl_hwdec_vaglx.c
+++ b/video/out/gl_hwdec_vaglx.c
@@ -18,10 +18,10 @@
*/
#include <stddef.h>
+#include <string.h>
#include <assert.h>
#include <GL/glx.h>
-#include <va/va_glx.h>
#include "x11_common.h"
#include "gl_hwdec.h"
@@ -31,23 +31,29 @@ struct priv {
struct mp_log *log;
struct mp_vaapi_ctx *ctx;
VADisplay *display;
+ Display *xdisplay;
GLuint gl_texture;
- void *vaglx_surface;
+ GLXFBConfig fbc;
+ Pixmap pixmap;
+ GLXPixmap glxpixmap;
+ void (*glXBindTexImage)(Display *dpy, GLXDrawable draw, int buffer, int *a);
+ void (*glXReleaseTexImage)(Display *dpy, GLXDrawable draw, int buffer);
};
static void destroy_texture(struct gl_hwdec *hw)
{
struct priv *p = hw->priv;
GL *gl = hw->gl;
- VAStatus status;
- if (p->vaglx_surface) {
- va_lock(p->ctx);
- status = vaDestroySurfaceGLX(p->display, p->vaglx_surface);
- va_unlock(p->ctx);
- CHECK_VA_STATUS(p, "vaDestroySurfaceGLX()");
- p->vaglx_surface = NULL;
+ if (p->glxpixmap) {
+ p->glXReleaseTexImage(p->xdisplay, p->glxpixmap, GLX_FRONT_EXT);
+ glXDestroyPixmap(p->xdisplay, p->glxpixmap);
}
+ p->glxpixmap = 0;
+
+ if (p->pixmap)
+ XFreePixmap(p->xdisplay, p->pixmap);
+ p->pixmap = 0;
gl->DeleteTextures(1, &p->gl_texture);
p->gl_texture = 0;
@@ -67,10 +73,21 @@ static int create(struct gl_hwdec *hw)
Display *x11disp = glXGetCurrentDisplay();
if (!x11disp)
return -1;
+ int x11scr = DefaultScreen(x11disp);
struct priv *p = talloc_zero(hw, struct priv);
hw->priv = p;
p->log = hw->log;
- p->display = vaGetDisplayGLX(x11disp);
+ p->xdisplay = x11disp;
+ const char *glxext = glXQueryExtensionsString(x11disp, x11scr);
+ if (!glxext || !strstr(glxext, "GLX_EXT_texture_from_pixmap"))
+ return -1;
+ p->glXBindTexImage =
+ (void*)glXGetProcAddressARB((void*)"glXBindTexImageEXT");
+ p->glXReleaseTexImage =
+ (void*)glXGetProcAddressARB((void*)"glXReleaseTexImageEXT");
+ if (!p->glXBindTexImage || !p->glXReleaseTexImage)
+ return -1;
+ p->display = vaGetDisplay(x11disp);
if (!p->display)
return -1;
p->ctx = va_initialize(p->display, p->log);
@@ -82,6 +99,32 @@ static int create(struct gl_hwdec *hw)
destroy(hw);
return -1;
}
+
+ int attribs[] = {
+ GLX_BIND_TO_TEXTURE_RGBA_EXT, True,
+ GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT,
+ GLX_BIND_TO_TEXTURE_TARGETS_EXT, GLX_TEXTURE_2D_BIT_EXT,
+ GLX_Y_INVERTED_EXT, True,
+ GLX_DOUBLEBUFFER, False,
+ GLX_RED_SIZE, 8,
+ GLX_GREEN_SIZE, 8,
+ GLX_BLUE_SIZE, 8,
+ GLX_ALPHA_SIZE, 0,
+ None
+ };
+
+ int fbcount;
+ GLXFBConfig *fbc = glXChooseFBConfig(x11disp, x11scr, attribs, &fbcount);
+ if (fbcount)
+ p->fbc = fbc[0];
+ if (fbc)
+ XFree(fbc);
+ if (!fbcount) {
+ MP_VERBOSE(p, "No texture-from-pixmap support.\n");
+ destroy(hw);
+ return -1;
+ }
+
hw->hwctx = &p->ctx->hwctx;
hw->converted_imgfmt = IMGFMT_RGB0;
return 0;
@@ -91,7 +134,6 @@ static int reinit(struct gl_hwdec *hw, struct mp_image_params *params)
{
struct priv *p = hw->priv;
GL *gl = hw->gl;
- VAStatus status;
destroy_texture(hw);
@@ -103,15 +145,29 @@ static int reinit(struct gl_hwdec *hw, struct mp_image_params *params)
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->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, params->w, params->h, 0,
- GL_RGBA, GL_UNSIGNED_BYTE, NULL);
gl->BindTexture(GL_TEXTURE_2D, 0);
- va_lock(p->ctx);
- status = vaCreateSurfaceGLX(p->display, GL_TEXTURE_2D,
- p->gl_texture, &p->vaglx_surface);
- va_unlock(p->ctx);
- return CHECK_VA_STATUS(p, "vaCreateSurfaceGLX()") ? 0 : -1;
+ p->pixmap = XCreatePixmap(p->xdisplay,
+ RootWindow(p->xdisplay, DefaultScreen(p->xdisplay)),
+ params->w, params->h, 24);
+ if (!p->pixmap) {
+ MP_FATAL(hw, "could not create pixmap\n");
+ return -1;
+ }
+
+ int attribs[] = {
+ GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT,
+ GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGB_EXT,
+ GLX_MIPMAP_TEXTURE_EXT, False,
+ None,
+ };
+ p->glxpixmap = glXCreatePixmap(p->xdisplay, p->fbc, p->pixmap, attribs);
+
+ gl->BindTexture(GL_TEXTURE_2D, p->gl_texture);
+ p->glXBindTexImage(p->xdisplay, p->glxpixmap, GLX_FRONT_EXT, NULL);
+ gl->BindTexture(GL_TEXTURE_2D, 0);
+
+ return 0;
}
static int map_image(struct gl_hwdec *hw, struct mp_image *hw_image,
@@ -120,31 +176,27 @@ static int map_image(struct gl_hwdec *hw, struct mp_image *hw_image,
struct priv *p = hw->priv;
VAStatus status;
- if (!p->vaglx_surface)
+ if (!p->pixmap)
return -1;
va_lock(p->ctx);
- status = vaCopySurfaceGLX(p->display, p->vaglx_surface,
- va_surface_id(hw_image),
- va_get_colorspace_flag(hw_image->params.colorspace));
+ status = vaPutSurface(p->display, va_surface_id(hw_image), p->pixmap,
+ 0, 0, hw_image->w, hw_image->h,
+ 0, 0, hw_image->w, hw_image->h,
+ NULL, 0,
+ va_get_colorspace_flag(hw_image->params.colorspace));
+ CHECK_VA_STATUS(p, "vaPutSurface()");
va_unlock(p->ctx);
- if (!CHECK_VA_STATUS(p, "vaCopySurfaceGLX()"))
- return -1;
out_textures[0] = p->gl_texture;
return 0;
}
-static void unmap_image(struct gl_hwdec *hw)
-{
-}
-
const struct gl_hwdec_driver gl_hwdec_vaglx = {
.api_name = "vaapi",
.imgfmt = IMGFMT_VAAPI,
.create = create,
.reinit = reinit,
.map_image = map_image,
- .unmap_image = unmap_image,
.destroy = destroy,
};
diff --git a/video/out/gl_hwdec_vda.c b/video/out/gl_hwdec_vda.c
index 181674f..dd28884 100644
--- a/video/out/gl_hwdec_vda.c
+++ b/video/out/gl_hwdec_vda.c
@@ -135,8 +135,6 @@ static int map_image(struct gl_hwdec *hw, struct mp_image *hw_image,
return 0;
}
-static void unmap_image(struct gl_hwdec *hw) { }
-
static void destroy(struct gl_hwdec *hw)
{
struct priv *p = hw->priv;
@@ -153,6 +151,5 @@ const struct gl_hwdec_driver gl_hwdec_vda = {
.create = create,
.reinit = reinit,
.map_image = map_image,
- .unmap_image = unmap_image,
.destroy = destroy,
};
diff --git a/video/out/gl_hwdec_vdpau.c b/video/out/gl_hwdec_vdpau.c
index f0086b3..bb502ad 100644
--- a/video/out/gl_hwdec_vdpau.c
+++ b/video/out/gl_hwdec_vdpau.c
@@ -40,6 +40,7 @@ struct priv {
GLvdpauSurfaceNV vdpgl_surface;
VdpOutputSurface vdp_surface;
struct mp_vdpau_mixer *mixer;
+ bool mapped;
};
static void mark_vdpau_objects_uninitialized(struct gl_hwdec *hw)
@@ -48,6 +49,7 @@ static void mark_vdpau_objects_uninitialized(struct gl_hwdec *hw)
p->vdp_surface = VDP_INVALID_HANDLE;
p->mixer->video_mixer = VDP_INVALID_HANDLE;
+ p->mapped = false;
}
static void destroy_objects(struct gl_hwdec *hw)
@@ -57,6 +59,10 @@ static void destroy_objects(struct gl_hwdec *hw)
struct vdp_functions *vdp = &p->ctx->vdp;
VdpStatus vdp_st;
+ if (p->mapped)
+ gl->VDPAUUnmapSurfacesNV(1, &p->vdpgl_surface);
+ p->mapped = false;
+
if (p->vdpgl_surface)
gl->VDPAUUnregisterSurfaceNV(p->vdpgl_surface);
p->vdpgl_surface = 0;
@@ -182,27 +188,22 @@ static int map_image(struct gl_hwdec *hw, struct mp_image *hw_image,
if (!p->vdpgl_surface)
return -1;
+ if (p->mapped)
+ gl->VDPAUUnmapSurfacesNV(1, &p->vdpgl_surface);
+
mp_vdpau_mixer_render(p->mixer, NULL, p->vdp_surface, NULL, hw_image, NULL);
gl->VDPAUMapSurfacesNV(1, &p->vdpgl_surface);
+ p->mapped = true;
out_textures[0] = p->gl_texture;
return 0;
}
-static void unmap_image(struct gl_hwdec *hw)
-{
- struct priv *p = hw->priv;
- GL *gl = hw->gl;
-
- gl->VDPAUUnmapSurfacesNV(1, &p->vdpgl_surface);
-}
-
const struct gl_hwdec_driver gl_hwdec_vdpau = {
.api_name = "vdpau",
.imgfmt = IMGFMT_VDPAU,
.create = create,
.reinit = reinit,
.map_image = map_image,
- .unmap_image = unmap_image,
.destroy = destroy,
};
diff --git a/video/out/gl_osd.c b/video/out/gl_osd.c
index 117d7fe..a42c208 100644
--- a/video/out/gl_osd.c
+++ b/video/out/gl_osd.c
@@ -1,18 +1,18 @@
/*
- * This file is part of mplayer.
+ * This file is part of mpv.
*
- * mplayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * mplayer is distributed in the hope that it will be useful,
+ * 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 mplayer. If not, see <http://www.gnu.org/licenses/>.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
@@ -55,20 +55,46 @@ static const struct osd_fmt_entry osd_to_gl2_formats[SUBBITMAP_COUNT] = {
struct vertex {
float position[2];
- uint8_t color[4];
float texcoord[2];
+ uint8_t ass_color[4];
};
static const struct gl_vao_entry vertex_vao[] = {
- {"vertex_position", 2, GL_FLOAT, false, offsetof(struct vertex, position)},
- {"vertex_color", 4, GL_UNSIGNED_BYTE, true, offsetof(struct vertex, color)},
- {"vertex_texcoord", 2, GL_FLOAT, false, offsetof(struct vertex, texcoord)},
+ {"position", 2, GL_FLOAT, false, offsetof(struct vertex, position)},
+ {"texcoord" , 2, GL_FLOAT, false, offsetof(struct vertex, texcoord)},
+ {"ass_color", 4, GL_UNSIGNED_BYTE, true, offsetof(struct vertex, ass_color)},
{0}
};
-// programs: SUBBITMAP_COUNT elements
-struct mpgl_osd *mpgl_osd_init(GL *gl, struct mp_log *log, struct osd_state *osd,
- GLuint *programs)
+struct mpgl_osd_part {
+ enum sub_bitmap_format format;
+ int change_id;
+ GLuint texture;
+ int w, h;
+ GLuint buffer;
+ int num_subparts;
+ struct sub_bitmap *subparts;
+ struct vertex *vertices;
+ struct bitmap_packer *packer;
+};
+
+struct mpgl_osd {
+ struct mp_log *log;
+ struct osd_state *osd;
+ GL *gl;
+ bool use_pbo;
+ bool scaled;
+ struct mpgl_osd_part *parts[MAX_OSD_PARTS];
+ const struct osd_fmt_entry *fmt_table;
+ bool formats[SUBBITMAP_COUNT];
+ struct gl_vao vao;
+ // temporary
+ int stereo_mode;
+ int display_size[2];
+ void *scratch;
+};
+
+struct mpgl_osd *mpgl_osd_init(GL *gl, struct mp_log *log, struct osd_state *osd)
{
GLint max_texture_size;
gl->GetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size);
@@ -79,7 +105,6 @@ struct mpgl_osd *mpgl_osd_init(GL *gl, struct mp_log *log, struct osd_state *osd
.osd = osd,
.gl = gl,
.fmt_table = osd_to_gl3_formats,
- .programs = programs,
.scratch = talloc_zero_size(ctx, 1),
};
@@ -126,6 +151,11 @@ void mpgl_osd_destroy(struct mpgl_osd *ctx)
talloc_free(ctx);
}
+void mpgl_osd_set_options(struct mpgl_osd *ctx, bool pbo)
+{
+ ctx->use_pbo = pbo;
+}
+
static bool upload_pbo(struct mpgl_osd *ctx, struct mpgl_osd_part *osd,
struct sub_bitmaps *imgs)
{
@@ -154,8 +184,7 @@ static bool upload_pbo(struct mpgl_osd *ctx, struct mpgl_osd_part *osd,
if (!gl->UnmapBuffer(GL_PIXEL_UNPACK_BUFFER))
success = false;
glUploadTex(gl, GL_TEXTURE_2D, fmt.format, fmt.type, NULL, stride,
- bb[0].x, bb[0].y, bb[1].x - bb[0].x, bb[1].y - bb[0].y,
- 0);
+ bb[0].x, bb[0].y, bb[1].x - bb[0].x, bb[1].y - bb[0].y, 0);
}
gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
@@ -240,99 +269,90 @@ static bool upload_osd(struct mpgl_osd *ctx, struct mpgl_osd_part *osd,
return true;
}
-static struct mpgl_osd_part *mpgl_osd_generate(struct mpgl_osd *ctx,
- struct sub_bitmaps *imgs)
+static void gen_osd_cb(void *pctx, struct sub_bitmaps *imgs)
{
+ struct mpgl_osd *ctx = pctx;
+
if (imgs->num_parts == 0 || !ctx->formats[imgs->format])
- return NULL;
+ return;
struct mpgl_osd_part *osd = ctx->parts[imgs->render_index];
- if (imgs->bitmap_pos_id != osd->bitmap_pos_id) {
- if (imgs->bitmap_id != osd->bitmap_id) {
- if (!upload_osd(ctx, osd, imgs))
- osd->packer->count = 0;
- }
+ if (imgs->change_id != osd->change_id) {
+ if (!upload_osd(ctx, osd, imgs))
+ osd->packer->count = 0;
- osd->bitmap_id = imgs->bitmap_id;
- osd->bitmap_pos_id = imgs->bitmap_pos_id;
- osd->num_vertices = 0;
+ osd->change_id = imgs->change_id;
}
+ osd->num_subparts = osd->packer->count;
- return osd->packer->count ? osd : NULL;
+ MP_TARRAY_GROW(osd, osd->subparts, osd->num_subparts);
+ memcpy(osd->subparts, imgs->parts,
+ osd->num_subparts * sizeof(osd->subparts[0]));
}
-static void write_quad(struct vertex *va,
+static void write_quad(struct vertex *va, struct gl_transform t,
float x0, float y0, float x1, float y1,
float tx0, float ty0, float tx1, float ty1,
float tex_w, float tex_h, const uint8_t color[4])
{
+ gl_transform_vec(t, &x0, &y0);
+ gl_transform_vec(t, &x1, &y1);
+
#define COLOR_INIT {color[0], color[1], color[2], color[3]}
- va[0] = (struct vertex){ {x0, y0}, COLOR_INIT, {tx0 / tex_w, ty0 / tex_h} };
- va[1] = (struct vertex){ {x0, y1}, COLOR_INIT, {tx0 / tex_w, ty1 / tex_h} };
- va[2] = (struct vertex){ {x1, y0}, COLOR_INIT, {tx1 / tex_w, ty0 / tex_h} };
- va[3] = (struct vertex){ {x1, y1}, COLOR_INIT, {tx1 / tex_w, ty1 / tex_h} };
+ va[0] = (struct vertex){ {x0, y0}, {tx0 / tex_w, ty0 / tex_h}, COLOR_INIT };
+ va[1] = (struct vertex){ {x0, y1}, {tx0 / tex_w, ty1 / tex_h}, COLOR_INIT };
+ va[2] = (struct vertex){ {x1, y0}, {tx1 / tex_w, ty0 / tex_h}, COLOR_INIT };
+ va[3] = (struct vertex){ {x1, y1}, {tx1 / tex_w, ty1 / tex_h}, COLOR_INIT };
va[4] = va[2];
va[5] = va[1];
#undef COLOR_INIT
}
-static void draw_osd_cb(void *pctx, struct sub_bitmaps *imgs)
+static int generate_verts(struct mpgl_osd_part *part, struct gl_transform t)
{
- struct mpgl_osd *ctx = pctx;
- GL *gl = ctx->gl;
+ int num_vertices = part->num_subparts * 6;
+ MP_TARRAY_GROW(part, part->vertices, num_vertices);
- struct mpgl_osd_part *part = mpgl_osd_generate(ctx, imgs);
- if (!part)
- return;
+ for (int n = 0; n < part->num_subparts; n++) {
+ struct sub_bitmap *b = &part->subparts[n];
+ struct pos pos = part->packer->result[n];
+ struct vertex *va = part->vertices;
- assert(part->format != SUBBITMAP_EMPTY);
+ // NOTE: the blend color is used with SUBBITMAP_LIBASS only, so it
+ // doesn't matter that we upload garbage for the other formats
+ uint32_t c = b->libass.color;
+ uint8_t color[4] = { c >> 24, (c >> 16) & 0xff,
+ (c >> 8) & 0xff, 255 - (c & 0xff) };
- if (!part->num_vertices) {
- part->vertices = talloc_realloc(part, part->vertices, struct vertex,
- part->packer->count * 6);
+ write_quad(&va[n * 6], t,
+ b->x, b->y, b->x + b->dw, b->y + b->dh,
+ pos.x, pos.y, pos.x + b->w, pos.y + b->h,
+ part->w, part->h, color);
+ }
- struct vertex *va = part->vertices;
+ return num_vertices;
+}
- for (int n = 0; n < part->packer->count; n++) {
- struct sub_bitmap *b = &imgs->parts[n];
- struct pos pos = part->packer->result[n];
-
- // NOTE: the blend color is used with SUBBITMAP_LIBASS only, so it
- // doesn't matter that we upload garbage for the other formats
- uint32_t c = b->libass.color;
- uint8_t color[4] = { c >> 24, (c >> 16) & 0xff,
- (c >> 8) & 0xff, 255 - (c & 0xff) };
-
- write_quad(&va[part->num_vertices],
- b->x, b->y, b->x + b->dw, b->y + b->dh,
- pos.x, pos.y, pos.x + b->w, pos.y + b->h,
- part->w, part->h, color);
- part->num_vertices += 6;
- }
- }
+static void draw_part(struct mpgl_osd *ctx, int index, struct gl_transform t)
+{
+ GL *gl = ctx->gl;
+ struct mpgl_osd_part *part = ctx->parts[index];
+
+ int num_vertices = generate_verts(part, t);
+ if (!num_vertices)
+ return;
+ gl->Enable(GL_BLEND);
gl->BindTexture(GL_TEXTURE_2D, part->texture);
const int *factors = &blend_factors[part->format][0];
gl->BlendFuncSeparate(factors[0], factors[1], factors[2], factors[3]);
- int program = ctx->programs[part->format];
- gl->UseProgram(program);
+ gl_vao_draw_data(&ctx->vao, GL_TRIANGLES, part->vertices, num_vertices);
- bool set_offset = ctx->offset[0] != 0.0f || ctx->offset[1] != 0.0f;
- if (set_offset) {
- gl->Uniform3f(gl->GetUniformLocation(program, "translation"),
- ctx->offset[0], ctx->offset[1], 0);
- }
-
- gl_vao_draw_data(&ctx->vao, GL_TRIANGLES, part->vertices, part->num_vertices);
-
- if (set_offset)
- gl->Uniform3f(gl->GetUniformLocation(program, "translation"), 0, 0, 0);
-
- gl->UseProgram(0);
gl->BindTexture(GL_TEXTURE_2D, 0);
+ gl->Disable(GL_BLEND);
}
// number of screen divisions per axis (x=0, y=1) for the current 3D mode
@@ -347,26 +367,52 @@ static void get_3d_side_by_side(int stereo_mode, int div[2])
}
}
-void mpgl_osd_draw(struct mpgl_osd *ctx, struct mp_osd_res res, double pts,
- int stereo_mode)
+void mpgl_osd_draw_part(struct mpgl_osd *ctx, int vp_w, int vp_h, int index)
{
- GL *gl = ctx->gl;
-
- gl->Enable(GL_BLEND);
-
int div[2];
- get_3d_side_by_side(stereo_mode, div);
+ get_3d_side_by_side(ctx->stereo_mode, div);
+
+ ctx->gl->Viewport(0, 0, vp_w, abs(vp_h));
for (int x = 0; x < div[0]; x++) {
for (int y = 0; y < div[1]; y++) {
- struct mp_osd_res s_res = res;
- s_res.w /= div[0];
- s_res.h /= div[1];
- ctx->offset[0] = s_res.w * x;
- ctx->offset[1] = s_res.h * y;
- osd_draw(ctx->osd, s_res, pts, 0, ctx->formats, draw_osd_cb, ctx);
+ struct gl_transform t;
+ gl_transform_ortho(&t, 0, vp_w, 0, vp_h);
+
+ float a_x = ctx->display_size[0] * x;
+ float a_y = ctx->display_size[1] * y;
+ t.t[0] += a_x * t.m[0][0] + a_y * t.m[1][0];
+ t.t[1] += a_x * t.m[0][1] + a_y * t.m[1][1];
+
+ draw_part(ctx, index, t);
}
}
+}
- gl->Disable(GL_BLEND);
+enum sub_bitmap_format mpgl_osd_get_part_format(struct mpgl_osd *ctx, int index)
+{
+ assert(index >= 0 && index < MAX_OSD_PARTS);
+ return ctx->parts[index]->format;
+}
+
+struct gl_vao *mpgl_osd_get_vao(struct mpgl_osd *ctx)
+{
+ return &ctx->vao;
+}
+
+void mpgl_osd_generate(struct mpgl_osd *ctx, struct mp_osd_res res, double pts,
+ int stereo_mode, int draw_flags)
+{
+ for (int n = 0; n < MAX_OSD_PARTS; n++)
+ ctx->parts[n]->num_subparts = 0;
+
+ int div[2];
+ get_3d_side_by_side(stereo_mode, div);
+
+ struct mp_osd_res s_res = res;
+ ctx->display_size[0] = s_res.w = s_res.w / div[0];
+ ctx->display_size[1] = s_res.h = s_res.h / div[1];
+
+ osd_draw(ctx->osd, s_res, pts, draw_flags, ctx->formats, gen_osd_cb, ctx);
+ ctx->stereo_mode = stereo_mode;
}
diff --git a/video/out/gl_osd.h b/video/out/gl_osd.h
index 5c34911..130008a 100644
--- a/video/out/gl_osd.h
+++ b/video/out/gl_osd.h
@@ -7,38 +7,15 @@
#include "gl_utils.h"
#include "sub/osd.h"
-struct mpgl_osd_part {
- enum sub_bitmap_format format;
- int bitmap_id, bitmap_pos_id;
- GLuint texture;
- int w, h;
- GLuint buffer;
- int num_vertices;
- void *vertices;
- struct bitmap_packer *packer;
-};
-
-struct mpgl_osd {
- struct mp_log *log;
- struct osd_state *osd;
- GL *gl;
- bool use_pbo;
- bool scaled;
- struct mpgl_osd_part *parts[MAX_OSD_PARTS];
- const struct osd_fmt_entry *fmt_table;
- bool formats[SUBBITMAP_COUNT];
- struct gl_vao vao;
- GLuint *programs; // SUBBITMAP_COUNT elements
- // temporary
- float offset[2];
- void *scratch;
-};
-
-struct mpgl_osd *mpgl_osd_init(GL *gl, struct mp_log *log, struct osd_state *osd,
- GLuint *programs);
+struct mpgl_osd *mpgl_osd_init(GL *gl, struct mp_log *log, struct osd_state *osd);
void mpgl_osd_destroy(struct mpgl_osd *ctx);
-void mpgl_osd_draw(struct mpgl_osd *ctx, struct mp_osd_res res, double pts,
- int stereo_mode);
+void mpgl_osd_set_options(struct mpgl_osd *ctx, bool pbo);
+
+void mpgl_osd_generate(struct mpgl_osd *ctx, struct mp_osd_res res, double pts,
+ int stereo_mode, int draw_flags);
+enum sub_bitmap_format mpgl_osd_get_part_format(struct mpgl_osd *ctx, int index);
+struct gl_vao *mpgl_osd_get_vao(struct mpgl_osd *ctx);
+void mpgl_osd_draw_part(struct mpgl_osd *ctx, int vp_w, int vp_h, int index);
#endif
diff --git a/video/out/gl_rpi.c b/video/out/gl_rpi.c
new file mode 100644
index 0000000..f82046e
--- /dev/null
+++ b/video/out/gl_rpi.c
@@ -0,0 +1,219 @@
+/*
+ * 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/>.
+ *
+ * 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.
+ */
+
+#include <stddef.h>
+#include <assert.h>
+
+#include <bcm_host.h>
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+#include "common/common.h"
+#include "x11_common.h"
+#include "gl_common.h"
+
+struct priv {
+ EGLDisplay egl_display;
+ EGLContext egl_context;
+ EGLSurface egl_surface;
+ DISPMANX_DISPLAY_HANDLE_T display;
+ DISPMANX_ELEMENT_HANDLE_T window;
+ DISPMANX_UPDATE_HANDLE_T update;
+ // yep, the API keeps a pointer to it
+ EGL_DISPMANX_WINDOW_T egl_window;
+ int w, h;
+};
+
+static void *get_proc_address(const GLubyte *name)
+{
+ void *p = eglGetProcAddress(name);
+ // It looks like eglGetProcAddress() should work even for builtin
+ // functions, but it doesn't work at least with RPI/Broadcom crap.
+ // (EGL 1.4, which current RPI firmware pretends to support, definitely
+ // is required to return non-extension functions.)
+ if (!p) {
+ void *h = dlopen("/opt/vc/lib/libGLESv2.so", RTLD_LAZY);
+ if (h) {
+ p = dlsym(h, name);
+ dlclose(h);
+ }
+ }
+ return p;
+}
+
+static EGLConfig select_fb_config_egl(struct MPGLContext *ctx)
+{
+ struct priv *p = ctx->priv;
+
+ EGLint attributes[] = {
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+ EGL_RED_SIZE, 8,
+ EGL_GREEN_SIZE, 8,
+ EGL_BLUE_SIZE, 8,
+ EGL_DEPTH_SIZE, 0,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL_NONE
+ };
+
+ EGLint config_count;
+ EGLConfig config;
+
+ eglChooseConfig(p->egl_display, attributes, &config, 1, &config_count);
+
+ if (!config_count) {
+ MP_FATAL(ctx->vo, "Could find EGL configuration!\n");
+ return NULL;
+ }
+
+ return config;
+}
+
+static bool sc_config_window(struct MPGLContext *ctx, int flags)
+{
+ struct priv *p = ctx->priv;
+ struct vo *vo = ctx->vo;
+
+ if (p->egl_context) {
+ vo->dwidth = p->w;
+ vo->dheight = p->h;
+ return true;
+ }
+
+ bcm_host_init();
+
+ p->display = vc_dispmanx_display_open(0);
+ p->update = vc_dispmanx_update_start(0);
+ if (!p->display || !p->update) {
+ MP_FATAL(ctx->vo, "Could not get DISPMANX objects.\n");
+ return false;
+ }
+
+ uint32_t w, h;
+ if (graphics_get_display_size(0, &w, &h) < 0) {
+ MP_FATAL(ctx->vo, "Could not get display size.\n");
+ return false;
+ }
+
+ // dispmanx is like a neanderthal version of Wayland - you can add an
+ // overlay any place on the screen. Just use the whole screen.
+ VC_RECT_T dst = {.width = w, .height = h};
+ VC_RECT_T src = {.width = w << 16, .height = h << 16};
+ VC_DISPMANX_ALPHA_T alpha = {
+ .flags = DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS,
+ .opacity = 0xFF,
+ };
+ p->window = vc_dispmanx_element_add(p->update, p->display, 1, &dst, 0,
+ &src, DISPMANX_PROTECTION_NONE, &alpha, 0, 0);
+ if (!p->window) {
+ MP_FATAL(ctx->vo, "Could not add DISPMANX element.\n");
+ return false;
+ }
+
+ vc_dispmanx_update_submit_sync(p->update);
+
+ p->egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ if (!eglInitialize(p->egl_display, NULL, NULL)) {
+ MP_FATAL(ctx->vo, "EGL failed to initialize.\n");
+ return false;
+ }
+
+ eglBindAPI(EGL_OPENGL_ES_API);
+
+ EGLConfig config = select_fb_config_egl(ctx);
+ if (!config)
+ return false;
+
+ p->egl_window = (EGL_DISPMANX_WINDOW_T){.element = p->window, .width = w, .height = h};
+ p->egl_surface = eglCreateWindowSurface(p->egl_display, config, &p->egl_window, NULL);
+
+ if (p->egl_surface == EGL_NO_SURFACE) {
+ MP_FATAL(ctx->vo, "Could not create EGL surface!\n");
+ return false;
+ }
+
+ EGLint context_attributes[] = {
+ EGL_CONTEXT_CLIENT_VERSION, 2,
+ EGL_NONE
+ };
+ p->egl_context = eglCreateContext(p->egl_display, config,
+ EGL_NO_CONTEXT, context_attributes);
+
+ if (p->egl_context == EGL_NO_CONTEXT) {
+ MP_FATAL(ctx->vo, "Could not create EGL context!\n");
+ return false;
+ }
+
+ eglMakeCurrent(p->egl_display, p->egl_surface, p->egl_surface,
+ p->egl_context);
+
+ const char *exts = eglQueryString(p->egl_display, EGL_EXTENSIONS);
+ mpgl_load_functions(ctx->gl, get_proc_address, exts, vo->log);
+
+ vo->dwidth = p->w = w;
+ vo->dheight = p->h = h;
+
+ return true;
+}
+
+static void sc_releaseGlContext(MPGLContext *ctx)
+{
+ struct priv *p = ctx->priv;
+ if (p->egl_context) {
+ eglMakeCurrent(p->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
+ EGL_NO_CONTEXT);
+ eglDestroyContext(p->egl_display, p->egl_context);
+ }
+ p->egl_context = EGL_NO_CONTEXT;
+ eglTerminate(p->egl_display);
+ vc_dispmanx_display_close(p->display);
+}
+
+static void sc_swapGlBuffers(MPGLContext *ctx)
+{
+ struct priv *p = ctx->priv;
+ eglSwapBuffers(p->egl_display, p->egl_surface);
+}
+
+static int sc_vo_init(struct vo *vo)
+{
+ return 1;
+}
+
+static void sc_vo_uninit(struct vo *vo)
+{
+}
+
+static int sc_vo_control(struct vo *vo, int *events, int request, void *arg)
+{
+ return VO_NOTIMPL;
+}
+
+void mpgl_set_backend_rpi(MPGLContext *ctx)
+{
+ ctx->priv = talloc_zero(ctx, struct priv);
+ ctx->config_window = sc_config_window;
+ ctx->releaseGlContext = sc_releaseGlContext;
+ ctx->swapGlBuffers = sc_swapGlBuffers;
+ ctx->vo_init = sc_vo_init;
+ ctx->vo_uninit = sc_vo_uninit;
+ ctx->vo_control = sc_vo_control;
+}
diff --git a/video/out/gl_utils.c b/video/out/gl_utils.c
index 80ec840..f8b46d9 100644
--- a/video/out/gl_utils.c
+++ b/video/out/gl_utils.c
@@ -25,6 +25,7 @@
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
+#include <stdarg.h>
#include <assert.h>
#include "common/common.h"
@@ -290,14 +291,6 @@ void gl_vao_unbind(struct gl_vao *vao)
}
}
-void gl_vao_bind_attribs(struct gl_vao *vao, GLuint program)
-{
- GL *gl = vao->gl;
-
- for (int n = 0; vao->entries[n].name; n++)
- gl->BindAttribLocation(program, n, vao->entries[n].name);
-}
-
// Draw the vertex data (as described by the gl_vao_entry entries) in ptr
// to the screen. num is the number of vertexes. prim is usually GL_TRIANGLES.
// If ptr is NULL, then skip the upload, and use the data uploaded with the
@@ -320,24 +313,47 @@ void gl_vao_draw_data(struct gl_vao *vao, GLenum prim, void *ptr, size_t num)
}
// Create a texture and a FBO using the texture as color attachments.
-// gl_target: GL_TEXTURE_2D
-// gl_filter: GL_LINEAR
// iformat: texture internal format
// Returns success.
bool fbotex_init(struct fbotex *fbo, GL *gl, struct mp_log *log, int w, int h,
- GLenum gl_target, GLenum gl_filter, GLenum iformat)
+ GLenum iformat)
{
- bool res = true;
-
assert(!fbo->fbo);
assert(!fbo->texture);
+ return fbotex_change(fbo, gl, log, w, h, iformat, 0);
+}
+
+// Like fbotex_init(), except it can be called on an already initialized FBO;
+// and if the parameters are the same as the previous call, do not touch it.
+// flags can be 0, or a combination of FBOTEX_FUZZY_W and FBOTEX_FUZZY_H.
+// Enabling FUZZY for W or H means the w or h does not need to be exact.
+bool fbotex_change(struct fbotex *fbo, GL *gl, struct mp_log *log, int w, int h,
+ GLenum iformat, int flags)
+{
+ bool res = true;
+
+ int cw = w, ch = h;
+
+ if ((flags & FBOTEX_FUZZY_W) && cw < fbo->tex_w)
+ cw = fbo->tex_w;
+ if ((flags & FBOTEX_FUZZY_H) && ch < fbo->tex_h)
+ ch = fbo->tex_h;
+
+ if (fbo->tex_w == cw && fbo->tex_h == ch && fbo->iformat == iformat)
+ return true;
+
+ if (flags & FBOTEX_FUZZY_W)
+ w = MP_ALIGN_UP(w, 256);
+ if (flags & FBOTEX_FUZZY_H)
+ h = MP_ALIGN_UP(h, 256);
+
+ GLenum filter = fbo->tex_filter;
*fbo = (struct fbotex) {
.gl = gl,
- .vp_w = w,
- .vp_h = h,
.tex_w = w,
.tex_h = h,
+ .iformat = iformat,
};
mp_verbose(log, "Create FBO: %dx%d\n", fbo->tex_w, fbo->tex_h);
@@ -347,19 +363,20 @@ bool fbotex_init(struct fbotex *fbo, GL *gl, struct mp_log *log, int w, int h,
gl->GenFramebuffers(1, &fbo->fbo);
gl->GenTextures(1, &fbo->texture);
- gl->BindTexture(gl_target, fbo->texture);
- gl->TexImage2D(gl_target, 0, iformat, fbo->tex_w, fbo->tex_h, 0,
+ gl->BindTexture(GL_TEXTURE_2D, fbo->texture);
+ gl->TexImage2D(GL_TEXTURE_2D, 0, iformat, fbo->tex_w, fbo->tex_h, 0,
GL_RGBA, GL_UNSIGNED_BYTE, NULL);
- gl->TexParameteri(gl_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- gl->TexParameteri(gl_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- gl->TexParameteri(gl_target, GL_TEXTURE_MIN_FILTER, gl_filter);
- gl->TexParameteri(gl_target, GL_TEXTURE_MAG_FILTER, gl_filter);
+ 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);
+
+ fbotex_set_filter(fbo, filter ? filter : GL_LINEAR);
glCheckError(gl, log, "after creating framebuffer texture");
gl->BindFramebuffer(GL_FRAMEBUFFER, fbo->fbo);
gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
- gl_target, fbo->texture, 0);
+ GL_TEXTURE_2D, fbo->texture, 0);
GLenum err = gl->CheckFramebufferStatus(GL_FRAMEBUFFER);
if (err != GL_FRAMEBUFFER_COMPLETE) {
@@ -375,6 +392,19 @@ bool fbotex_init(struct fbotex *fbo, GL *gl, struct mp_log *log, int w, int h,
return res;
}
+void fbotex_set_filter(struct fbotex *fbo, GLenum tex_filter)
+{
+ GL *gl = fbo->gl;
+
+ if (fbo->tex_filter != tex_filter && fbo->texture) {
+ gl->BindTexture(GL_TEXTURE_2D, fbo->texture);
+ gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, tex_filter);
+ gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, tex_filter);
+ gl->BindTexture(GL_TEXTURE_2D, 0);
+ }
+ fbo->tex_filter = tex_filter;
+}
+
void fbotex_uninit(struct fbotex *fbo)
{
GL *gl = fbo->gl;
@@ -386,14 +416,23 @@ void fbotex_uninit(struct fbotex *fbo)
}
}
-void gl_matrix_ortho2d(float m[3][3], float x0, float x1, float y0, float y1)
+// Standard parallel 2D projection, except y1 < y0 means that the coordinate
+// system is flipped, not the projection.
+void gl_transform_ortho(struct gl_transform *t, float x0, float x1,
+ float y0, float y1)
{
- memset(m, 0, 9 * sizeof(float));
- m[0][0] = 2.0f / (x1 - x0);
- m[1][1] = 2.0f / (y1 - y0);
- m[2][0] = -(x1 + x0) / (x1 - x0);
- m[2][1] = -(y1 + y0) / (y1 - y0);
- m[2][2] = 1.0f;
+ if (y1 < y0) {
+ float tmp = y0;
+ y0 = tmp - y1;
+ y1 = tmp;
+ }
+
+ t->m[0][0] = 2.0f / (x1 - x0);
+ t->m[0][1] = 0.0f;
+ t->m[1][0] = 0.0f;
+ t->m[1][1] = 2.0f / (y1 - y0);
+ t->t[0] = -(x1 + x0) / (x1 - x0);
+ t->t[1] = -(y1 + y0) / (y1 - y0);
}
static void GLAPIENTRY gl_debug_cb(GLenum source, GLenum type, GLuint id,
@@ -422,3 +461,419 @@ void gl_set_debug_logger(GL *gl, struct mp_log *log)
}
}
}
+
+#define SC_ENTRIES 16
+#define SC_UNIFORM_ENTRIES 20
+
+enum uniform_type {
+ UT_invalid,
+ UT_i,
+ UT_f,
+ UT_m,
+};
+
+struct sc_uniform {
+ char *name;
+ enum uniform_type type;
+ const char *glsl_type;
+ int size;
+ GLint loc;
+ union {
+ GLfloat f[9];
+ GLint i[4];
+ } v;
+};
+
+struct sc_entry {
+ GLuint gl_shader;
+ // the following fields define the shader's contents
+ char *key; // vertex+frag shader (mangled)
+ struct gl_vao *vao;
+};
+
+struct gl_shader_cache {
+ GL *gl;
+ struct mp_log *log;
+
+ // this is modified during use (gl_sc_add() etc.)
+ char *text;
+ struct gl_vao *vao;
+
+ struct sc_entry entries[SC_ENTRIES];
+ int num_entries;
+
+ struct sc_uniform uniforms[SC_UNIFORM_ENTRIES];
+ int num_uniforms;
+};
+
+struct gl_shader_cache *gl_sc_create(GL *gl, struct mp_log *log)
+{
+ struct gl_shader_cache *sc = talloc_ptrtype(NULL, sc);
+ *sc = (struct gl_shader_cache){
+ .gl = gl,
+ .log = log,
+ .text = talloc_strdup(sc, ""),
+ };
+ return sc;
+}
+
+void gl_sc_reset(struct gl_shader_cache *sc)
+{
+ sc->text[0] = '\0';
+ for (int n = 0; n < sc->num_uniforms; n++)
+ talloc_free(sc->uniforms[n].name);
+ sc->num_uniforms = 0;
+}
+
+static void sc_flush_cache(struct gl_shader_cache *sc)
+{
+ for (int n = 0; n < sc->num_entries; n++) {
+ struct sc_entry *e = &sc->entries[n];
+ sc->gl->DeleteProgram(e->gl_shader);
+ talloc_free(e->key);
+ }
+ sc->num_entries = 0;
+}
+
+void gl_sc_destroy(struct gl_shader_cache *sc)
+{
+ gl_sc_reset(sc);
+ sc_flush_cache(sc);
+ talloc_free(sc);
+}
+
+void gl_sc_add(struct gl_shader_cache *sc, const char *text)
+{
+ sc->text = talloc_strdup_append(sc->text, text);
+}
+
+void gl_sc_addf(struct gl_shader_cache *sc, const char *textf, ...)
+{
+ va_list ap;
+ va_start(ap, textf);
+ ta_xvasprintf_append(&sc->text, textf, ap);
+ va_end(ap);
+}
+
+static struct sc_uniform *find_uniform(struct gl_shader_cache *sc,
+ const char *name)
+{
+ for (int n = 0; n < sc->num_uniforms; n++) {
+ if (strcmp(sc->uniforms[n].name, name) == 0)
+ return &sc->uniforms[n];
+ }
+ // not found -> add it
+ assert(sc->num_uniforms < SC_UNIFORM_ENTRIES); // just don't have too many
+ struct sc_uniform *new = &sc->uniforms[sc->num_uniforms++];
+ *new = (struct sc_uniform) { .loc = -1, .name = talloc_strdup(NULL, name) };
+ return new;
+}
+
+void gl_sc_uniform_sampler(struct gl_shader_cache *sc, char *name, GLenum target,
+ int unit)
+{
+ struct sc_uniform *u = find_uniform(sc, name);
+ u->type = UT_i;
+ u->size = 1;
+ switch (target) {
+ case GL_TEXTURE_1D: u->glsl_type = "sampler1D"; break;
+ case GL_TEXTURE_2D: u->glsl_type = "sampler2D"; break;
+ case GL_TEXTURE_RECTANGLE: u->glsl_type = "sampler2DRect"; break;
+ case GL_TEXTURE_3D: u->glsl_type = "sampler3D"; break;
+ default: abort();
+ }
+ 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);
+ u->type = UT_f;
+ u->size = 1;
+ u->glsl_type = "float";
+ u->v.f[0] = f;
+}
+
+void gl_sc_uniform_vec2(struct gl_shader_cache *sc, char *name, GLfloat f[2])
+{
+ struct sc_uniform *u = find_uniform(sc, name);
+ u->type = UT_f;
+ u->size = 2;
+ u->glsl_type = "vec2";
+ u->v.f[0] = f[0];
+ u->v.f[1] = f[1];
+}
+
+void gl_sc_uniform_vec3(struct gl_shader_cache *sc, char *name, GLfloat f[3])
+{
+ struct sc_uniform *u = find_uniform(sc, name);
+ u->type = UT_f;
+ u->size = 3;
+ u->glsl_type = "vec3";
+ u->v.f[0] = f[0];
+ u->v.f[1] = f[1];
+ u->v.f[2] = f[2];
+}
+
+static void transpose2x2(float r[2 * 2])
+{
+ MPSWAP(float, r[0+2*1], r[1+2*0]);
+}
+
+void gl_sc_uniform_mat2(struct gl_shader_cache *sc, char *name,
+ bool transpose, GLfloat *v)
+{
+ struct sc_uniform *u = find_uniform(sc, name);
+ u->type = UT_m;
+ u->size = 2;
+ u->glsl_type = "mat2";
+ for (int n = 0; n < 4; n++)
+ u->v.f[n] = v[n];
+ if (transpose)
+ transpose2x2(&u->v.f[0]);
+}
+
+static void transpose3x3(float r[3 * 3])
+{
+ MPSWAP(float, r[0+3*1], r[1+3*0]);
+ MPSWAP(float, r[0+3*2], r[2+3*0]);
+ MPSWAP(float, r[1+3*2], r[2+3*1]);
+}
+
+void gl_sc_uniform_mat3(struct gl_shader_cache *sc, char *name,
+ bool transpose, GLfloat *v)
+{
+ struct sc_uniform *u = find_uniform(sc, name);
+ u->type = UT_m;
+ u->size = 3;
+ u->glsl_type = "mat3";
+ for (int n = 0; n < 9; n++)
+ u->v.f[n] = v[n];
+ if (transpose)
+ transpose3x3(&u->v.f[0]);
+}
+
+// This will call glBindAttribLocation() on the shader before it's linked
+// (OpenGL requires this to happen before linking). Basically, it associates
+// the input variable names with the fields in the vao.
+// The vertex shader is setup such that the elements are available as fragment
+// shader variables using the names in the vao entries, which "position" being
+// set to gl_Position.
+void gl_sc_set_vao(struct gl_shader_cache *sc, struct gl_vao *vao)
+{
+ sc->vao = vao;
+}
+
+static const char *vao_glsl_type(const struct gl_vao_entry *e)
+{
+ // pretty dumb... too dumb, but works for us
+ switch (e->num_elems) {
+ case 1: return "float";
+ case 2: return "vec2";
+ case 3: return "vec3";
+ case 4: return "vec4";
+ default: abort();
+ }
+}
+
+// Assumes program is current (gl->UseProgram(program)).
+static void update_uniform(GL *gl, GLuint program, struct sc_uniform *u)
+{
+ GLint loc = gl->GetUniformLocation(program, u->name);
+ if (loc < 0)
+ return;
+ switch (u->type) {
+ case UT_i:
+ assert(u->size == 1);
+ gl->Uniform1i(loc, u->v.i[0]);
+ break;
+ case UT_f:
+ switch (u->size) {
+ case 1: gl->Uniform1f(loc, u->v.f[0]); break;
+ case 2: gl->Uniform2f(loc, u->v.f[0], u->v.f[1]); break;
+ case 3: gl->Uniform3f(loc, u->v.f[0], u->v.f[1], u->v.f[2]); break;
+ case 4: gl->Uniform4f(loc, u->v.f[0], u->v.f[1], u->v.f[2], u->v.f[3]); break;
+ default: abort();
+ }
+ break;
+ case UT_m:
+ switch (u->size) {
+ case 2: gl->UniformMatrix2fv(loc, 1, GL_FALSE, &u->v.f[0]); break;
+ case 3: gl->UniformMatrix3fv(loc, 1, GL_FALSE, &u->v.f[0]); break;
+ default: abort();
+ }
+ break;
+ default:
+ abort();
+ }
+}
+
+static void compile_attach_shader(struct gl_shader_cache *sc, GLuint program,
+ GLenum type, const char *source)
+{
+ GL *gl = sc->gl;
+
+ GLuint shader = gl->CreateShader(type);
+ gl->ShaderSource(shader, 1, &source, NULL);
+ gl->CompileShader(shader);
+ GLint status;
+ gl->GetShaderiv(shader, GL_COMPILE_STATUS, &status);
+ GLint log_length;
+ gl->GetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length);
+
+ int pri = status ? (log_length > 1 ? MSGL_V : MSGL_DEBUG) : MSGL_ERR;
+ const char *typestr = type == GL_VERTEX_SHADER ? "vertex" : "fragment";
+ if (mp_msg_test(sc->log, pri)) {
+ MP_MSG(sc, pri, "%s shader source:\n", typestr);
+ mp_log_source(sc->log, pri, source);
+ }
+ if (log_length > 1) {
+ GLchar *logstr = talloc_zero_size(NULL, log_length + 1);
+ gl->GetShaderInfoLog(shader, log_length, NULL, logstr);
+ MP_MSG(sc, pri, "%s shader compile log (status=%d):\n%s\n",
+ typestr, status, logstr);
+ talloc_free(logstr);
+ }
+
+ gl->AttachShader(program, shader);
+ gl->DeleteShader(shader);
+}
+
+static void link_shader(struct gl_shader_cache *sc, GLuint program)
+{
+ GL *gl = sc->gl;
+ gl->LinkProgram(program);
+ GLint status;
+ gl->GetProgramiv(program, GL_LINK_STATUS, &status);
+ GLint log_length;
+ gl->GetProgramiv(program, GL_INFO_LOG_LENGTH, &log_length);
+
+ int pri = status ? (log_length > 1 ? MSGL_V : MSGL_DEBUG) : MSGL_ERR;
+ if (mp_msg_test(sc->log, pri)) {
+ GLchar *logstr = talloc_zero_size(NULL, log_length + 1);
+ gl->GetProgramInfoLog(program, log_length, NULL, logstr);
+ MP_MSG(sc, pri, "shader link log (status=%d): %s\n", status, logstr);
+ talloc_free(logstr);
+ }
+}
+
+static GLuint create_program(struct gl_shader_cache *sc, const char *vertex,
+ const char *frag)
+{
+ GL *gl = sc->gl;
+ MP_VERBOSE(sc, "recompiling a shader program:\n");
+ mp_log_source(sc->log, MSGL_V, sc->text);
+ GLuint prog = gl->CreateProgram();
+ compile_attach_shader(sc, prog, GL_VERTEX_SHADER, vertex);
+ compile_attach_shader(sc, prog, GL_FRAGMENT_SHADER, frag);
+ for (int n = 0; sc->vao->entries[n].name; n++) {
+ char vname[80];
+ snprintf(vname, sizeof(vname), "vertex_%s", sc->vao->entries[n].name);
+ gl->BindAttribLocation(prog, n, vname);
+ }
+ link_shader(sc, prog);
+ return prog;
+}
+
+#define ADD(x, ...) (x) = talloc_asprintf_append(x, __VA_ARGS__)
+
+// 1. Generate vertex and fragment shaders from the fragment shader text added
+// with gl_sc_add(). The generated shader program is cached (based on the
+// text), so actual compilation happens only the first time.
+// 2. Update the uniforms set with gl_sc_uniform_*.
+// 3. Make the new shader program current (glUseProgram()).
+// 4. Reset the sc state and prepare for a new shader program. (All uniforms
+// and fragment operations needed for the next program have to be re-added.)
+void gl_sc_gen_shader_and_reset(struct gl_shader_cache *sc)
+{
+ GL *gl = sc->gl;
+ void *tmp = talloc_new(NULL);
+
+ assert(sc->vao);
+
+ // set up shader text (header + uniforms + body)
+ char *header = talloc_asprintf(tmp, "#version %d%s\n", gl->glsl_version,
+ gl->es >= 300 ? " es" : "");
+ if (gl->es)
+ ADD(header, "precision mediump float;\n");
+ char *vert_in = gl->glsl_version >= 130 ? "in" : "attribute";
+ char *vert_out = gl->glsl_version >= 130 ? "out" : "varying";
+ char *frag_in = gl->glsl_version >= 130 ? "in" : "varying";
+
+ // vertex shader: we don't use the vertex shader, so just setup a dummy,
+ // which passes through the vertex array attributes.
+ char *vert_head = talloc_strdup(tmp, header);
+ char *vert_body = talloc_strdup(tmp, "void main() {\n");
+ char *frag_vaos = talloc_strdup(tmp, "");
+ for (int n = 0; sc->vao->entries[n].name; n++) {
+ const struct gl_vao_entry *e = &sc->vao->entries[n];
+ const char *glsl_type = vao_glsl_type(e);
+ if (strcmp(e->name, "position") == 0) {
+ // setting raster pos. requires setting gl_Position magic variable
+ assert(e->num_elems == 2 && e->type == GL_FLOAT);
+ ADD(vert_head, "%s vec2 position;\n", vert_in);
+ ADD(vert_body, "gl_Position = vec4(position, 1.0, 1.0);\n");
+ } else {
+ ADD(vert_head, "%s %s vertex_%s;\n", vert_in, glsl_type, e->name);
+ ADD(vert_head, "%s %s %s;\n", vert_out, glsl_type, e->name);
+ ADD(vert_body, "%s = vertex_%s;\n", e->name, e->name);
+ ADD(frag_vaos, "%s %s %s;\n", frag_in, glsl_type, e->name);
+ }
+ }
+ ADD(vert_body, "}\n");
+ char *vert = talloc_asprintf(tmp, "%s%s", vert_head, vert_body);
+
+ // fragment shader; still requires adding used uniforms and VAO elements
+ char *frag = talloc_strdup(tmp, header);
+ ADD(frag, "#define RG %s\n", gl->mpgl_caps & MPGL_CAP_TEX_RG ? "rg" : "ra");
+ if (gl->glsl_version >= 130) {
+ ADD(frag, "#define texture1D texture\n");
+ ADD(frag, "#define texture3D texture\n");
+ ADD(frag, "out vec4 out_color;\n");
+ } else {
+ ADD(frag, "#define texture texture2D\n");
+ }
+ ADD(frag, "%s", frag_vaos);
+ for (int n = 0; n < sc->num_uniforms; n++) {
+ struct sc_uniform *u = &sc->uniforms[n];
+ ADD(frag, "uniform %s %s;\n", u->glsl_type, u->name);
+ }
+ ADD(frag, "void main() {\n");
+ ADD(frag, "%s", sc->text);
+ // we require _all_ frag shaders to write to a "vec4 color"
+ if (gl->glsl_version >= 130) {
+ ADD(frag, "out_color = color;\n");
+ } else {
+ ADD(frag, "gl_FragColor = color;\n");
+ }
+ ADD(frag, "}\n");
+
+ char *key = talloc_asprintf(tmp, "%s%s", vert, frag);
+ struct sc_entry *entry = NULL;
+ for (int n = 0; n < sc->num_entries; n++) {
+ if (strcmp(key, sc->entries[n].key) == 0) {
+ entry = &sc->entries[n];
+ break;
+ }
+ }
+ if (!entry) {
+ if (sc->num_entries == SC_ENTRIES)
+ sc_flush_cache(sc);
+ entry = &sc->entries[sc->num_entries++];
+ *entry = (struct sc_entry){.key = talloc_strdup(NULL, key)};
+ }
+ // build vertex shader from vao
+ if (!entry->gl_shader)
+ entry->gl_shader = create_program(sc, vert, frag);
+
+ gl->UseProgram(entry->gl_shader);
+
+ // For now we set the uniforms every time. This is probably bad, and we
+ // should switch to caching them.
+ for (int n = 0; n < sc->num_uniforms; n++)
+ update_uniform(gl, entry->gl_shader, &sc->uniforms[n]);
+
+ talloc_free(tmp);
+
+ gl_sc_reset(sc);
+}
diff --git a/video/out/gl_utils.h b/video/out/gl_utils.h
index 1934396..2c55e72 100644
--- a/video/out/gl_utils.h
+++ b/video/out/gl_utils.h
@@ -66,23 +66,74 @@ void gl_vao_init(struct gl_vao *vao, GL *gl, int stride,
void gl_vao_uninit(struct gl_vao *vao);
void gl_vao_bind(struct gl_vao *vao);
void gl_vao_unbind(struct gl_vao *vao);
-void gl_vao_bind_attribs(struct gl_vao *vao, GLuint program);
void gl_vao_draw_data(struct gl_vao *vao, GLenum prim, void *ptr, size_t num);
struct fbotex {
GL *gl;
GLuint fbo;
GLuint texture;
- int tex_w, tex_h; // size of .texture
- int vp_x, vp_y, vp_w, vp_h; // viewport of fbo / used part of the texture
+ GLenum iformat;
+ GLenum tex_filter;
+ int tex_w, tex_h; // size of .texture
};
bool fbotex_init(struct fbotex *fbo, GL *gl, struct mp_log *log, int w, int h,
- GLenum gl_target, GLenum gl_filter, GLenum iformat);
+ GLenum iformat);
void fbotex_uninit(struct fbotex *fbo);
+bool fbotex_change(struct fbotex *fbo, GL *gl, struct mp_log *log, int w, int h,
+ GLenum iformat, int flags);
+#define FBOTEX_FUZZY_W 1
+#define FBOTEX_FUZZY_H 2
+#define FBOTEX_FUZZY (FBOTEX_FUZZY_W | FBOTEX_FUZZY_H)
+void fbotex_set_filter(struct fbotex *fbo, GLenum gl_filter);
-void gl_matrix_ortho2d(float m[3][3], float x0, float x1, float y0, float y1);
+// A 3x2 matrix, with the translation part separate.
+struct gl_transform {
+ float m[2][2];
+ float t[2];
+};
+
+void gl_transform_ortho(struct gl_transform *t, float x0, float x1,
+ float y0, float y1);
+
+// This treats m as an affine transformation, in other words m[2][n] gets
+// added to the output.
+static inline void gl_transform_vec(struct gl_transform t, float *x, float *y)
+{
+ float vx = *x, vy = *y;
+ *x = vx * t.m[0][0] + vy * t.m[1][0] + t.t[0];
+ *y = vx * t.m[0][1] + vy * t.m[1][1] + t.t[1];
+}
+
+struct mp_rect_f {
+ float x0, y0, x1, y1;
+};
+
+static inline void gl_transform_rect(struct gl_transform t, struct mp_rect_f *r)
+{
+ gl_transform_vec(t, &r->x0, &r->y0);
+ gl_transform_vec(t, &r->x1, &r->y1);
+}
void gl_set_debug_logger(GL *gl, struct mp_log *log);
+struct gl_shader_cache;
+
+struct gl_shader_cache *gl_sc_create(GL *gl, struct mp_log *log);
+void gl_sc_destroy(struct gl_shader_cache *sc);
+void gl_sc_add(struct gl_shader_cache *sc, const char *text);
+void gl_sc_addf(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_f(struct gl_shader_cache *sc, char *name, GLfloat f);
+void gl_sc_uniform_vec2(struct gl_shader_cache *sc, char *name, GLfloat f[2]);
+void gl_sc_uniform_vec3(struct gl_shader_cache *sc, char *name, GLfloat f[3]);
+void gl_sc_uniform_mat2(struct gl_shader_cache *sc, char *name,
+ bool transpose, GLfloat *v);
+void gl_sc_uniform_mat3(struct gl_shader_cache *sc, char *name,
+ bool transpose, GLfloat *v);
+void gl_sc_set_vao(struct gl_shader_cache *sc, struct gl_vao *vao);
+void gl_sc_gen_shader_and_reset(struct gl_shader_cache *sc);
+void gl_sc_reset(struct gl_shader_cache *sc);
+
#endif
diff --git a/video/out/gl_video.c b/video/out/gl_video.c
index 4b29a26..9b4b46a 100644
--- a/video/out/gl_video.c
+++ b/video/out/gl_video.c
@@ -37,23 +37,19 @@
#include "gl_osd.h"
#include "filter_kernels.h"
#include "aspect.h"
-#include "video/memcpy_pic.h"
#include "bitmap_packer.h"
#include "dither.h"
-static const char vo_opengl_shaders[] =
-// Generated from gl_video_shaders.glsl
-#include "video/out/gl_video_shaders.h"
-;
-
// Pixel width of 1D lookup textures.
#define LOOKUP_TEXTURE_SIZE 256
-// Texture units 0-3 are used by the video, with unit 0 for free use.
-// Units 4-5 are used for scaler LUTs.
-#define TEXUNIT_SCALERS 4
-#define TEXUNIT_3DLUT 6
-#define TEXUNIT_DITHER 7
+// Texture units 0-5 are used by the video, and for free use by the passes
+#define TEXUNIT_VIDEO_NUM 6
+
+// Other texture units are reserved for specific purposes
+#define TEXUNIT_SCALERS TEXUNIT_VIDEO_NUM
+#define TEXUNIT_3DLUT (TEXUNIT_SCALERS+4)
+#define TEXUNIT_DITHER (TEXUNIT_3DLUT+1)
// scale/cscale arguments that map directly to shader filter routines.
// Note that the convolution filters are not included in this list.
@@ -62,22 +58,36 @@ static const char *const fixed_scale_filters[] = {
"bicubic_fast",
"sharpen3",
"sharpen5",
+ "oversample",
+ NULL
+};
+static const char *const fixed_tscale_filters[] = {
+ "oversample",
NULL
};
// must be sorted, and terminated with 0
-// 2 & 6 are special-cased, the rest can be generated with WEIGHTS_N().
int filter_sizes[] =
{2, 4, 6, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60, 64, 0};
+int tscale_sizes[] = {2, 4, 6, 0}; // limited by TEXUNIT_VIDEO_NUM
+
+struct vertex_pt {
+ float x, y;
+};
struct vertex {
- float position[2];
- float texcoord[2];
+ struct vertex_pt position;
+ struct vertex_pt texcoord[TEXUNIT_VIDEO_NUM];
};
static const struct gl_vao_entry vertex_vao[] = {
- {"vertex_position", 2, GL_FLOAT, false, offsetof(struct vertex, position)},
- {"vertex_texcoord", 2, GL_FLOAT, false, offsetof(struct vertex, texcoord)},
+ {"position", 2, GL_FLOAT, false, offsetof(struct vertex, position)},
+ {"texcoord0", 2, GL_FLOAT, false, offsetof(struct vertex, texcoord[0])},
+ {"texcoord1", 2, GL_FLOAT, false, offsetof(struct vertex, texcoord[1])},
+ {"texcoord2", 2, GL_FLOAT, false, offsetof(struct vertex, texcoord[2])},
+ {"texcoord3", 2, GL_FLOAT, false, offsetof(struct vertex, texcoord[3])},
+ {"texcoord4", 2, GL_FLOAT, false, offsetof(struct vertex, texcoord[4])},
+ {"texcoord5", 2, GL_FLOAT, false, offsetof(struct vertex, texcoord[5])},
{0}
};
@@ -85,6 +95,7 @@ struct texplane {
int w, h;
int tex_w, tex_h;
GLint gl_internal_format;
+ GLenum gl_target;
GLenum gl_format;
GLenum gl_type;
GLuint gl_texture;
@@ -101,12 +112,13 @@ struct video_image {
struct scaler {
int index;
- const char *name;
- float params[2];
- float antiring;
+ struct scaler_config conf;
+ double scale_factor;
+ bool initialized;
struct filter_kernel *kernel;
GLuint gl_lut;
- const char *lut_name;
+ GLenum gl_target;
+ struct fbotex sep_fbo;
bool insufficient;
// kernel points here
@@ -116,10 +128,17 @@ struct scaler {
struct fbosurface {
struct fbotex fbotex;
int64_t pts;
- bool valid;
+ double vpts; // used for synchronizing subtitles only
};
-#define FBOSURFACES_MAX 2
+#define FBOSURFACES_MAX 10
+
+struct src_tex {
+ GLuint gl_tex;
+ GLenum gl_target;
+ int tex_w, tex_h;
+ struct mp_rect_f src;
+};
struct gl_video {
GL *gl;
@@ -131,13 +150,12 @@ struct gl_video {
int depth_g;
int texture_16bit_depth; // actual bits available in 16 bit textures
+ struct gl_shader_cache *sc;
+
GLenum gl_target; // texture target (GL_TEXTURE_2D, ...) for video and FBOs
struct gl_vao vao;
- GLuint osd_programs[SUBBITMAP_COUNT];
- GLuint indirect_program, scale_sep_program, final_program, inter_program;
-
struct osd_state *osd_state;
struct mpgl_osd *osd;
double osd_pts;
@@ -146,8 +164,6 @@ struct gl_video {
bool use_lut_3d;
GLuint dither_texture;
- float dither_quantization;
- float dither_center;
int dither_size;
struct mp_image_params real_image_params; // configured format
@@ -159,41 +175,33 @@ struct gl_video {
bool is_yuv, is_rgb, is_packed_yuv;
bool has_alpha;
char color_swizzle[5];
- float chroma_fix[2];
-
- float input_gamma, conv_gamma;
- float user_gamma;
- bool user_gamma_enabled; // shader handles user_gamma
- bool sigmoid_enabled;
struct video_image image;
struct fbotex indirect_fbo; // RGB target
- struct fbotex scale_sep_fbo; // first pass when doing 2 pass scaling
+ struct fbotex chroma_merge_fbo;
+ struct fbotex blend_subs_fbo;
struct fbosurface surfaces[FBOSURFACES_MAX];
- size_t surface_idx;
-
- // state for luma (0) and chroma (1) scalers
- struct scaler scalers[2];
-
- // true if scaler is currently upscaling
- bool upscaling;
-
- // reinit_rendering must be called
- bool need_reinit_rendering;
+ int surface_idx;
+ int surface_now;
bool is_interpolated;
- struct mp_csp_equalizer video_eq;
+ // state for luma (0), luma-down(1), chroma (2) and temporal (3) scalers
+ struct scaler scaler[4];
- // Source and destination color spaces for the CMS matrix
- struct mp_csp_primaries csp_src, csp_dest;
+ struct mp_csp_equalizer video_eq;
struct mp_rect src_rect; // displayed part of the source video
struct mp_rect dst_rect; // video rectangle on output window
struct mp_osd_res osd_rect; // OSD size/margins
- int vp_x, vp_y, vp_w, vp_h; // GL viewport
- bool vp_vflipped;
+ int vp_w, vp_h;
+
+ // temporary during rendering
+ struct src_tex pass_tex[TEXUNIT_VIDEO_NUM];
+ bool use_indirect;
+ bool use_linear;
+ float user_gamma;
int frames_rendered;
@@ -203,8 +211,6 @@ struct gl_video {
struct gl_hwdec *hwdec;
bool hwdec_active;
-
- void *scratch;
};
struct fmt_entry {
@@ -310,11 +316,6 @@ static const struct packed_fmt_entry mp_packed_formats[] = {
{0},
};
-static const char *const osd_shaders[SUBBITMAP_COUNT] = {
- [SUBBITMAP_LIBASS] = "frag_osd_libass",
- [SUBBITMAP_RGBA] = "frag_osd_rgba",
-};
-
const struct gl_video_opts gl_video_opts_def = {
.npot = 1,
.dither_depth = -1,
@@ -322,9 +323,12 @@ const struct gl_video_opts gl_video_opts_def = {
.fbo_format = GL_RGBA,
.sigmoid_center = 0.75,
.sigmoid_slope = 6.5,
- .scalers = { "bilinear", "bilinear" },
- .scaler_params = {{NAN, NAN}, {NAN, NAN}},
- .scaler_radius = {3, 3},
+ .scaler = {
+ {{"bilinear", .params={NAN, NAN}}, {.params = {NAN, NAN}}}, // scale
+ {{NULL, .params={NAN, NAN}}, {.params = {NAN, NAN}}}, // dscale
+ {{"bilinear", .params={NAN, NAN}}, {.params = {NAN, NAN}}}, // cscale
+ {{"oversample", .params={NAN, NAN}}, {.params = {NAN, NAN}}}, // tscale
+ },
.alpha_mode = 2,
.background = {0, 0, 0, 255},
.gamma = 1.0f,
@@ -339,36 +343,65 @@ const struct gl_video_opts gl_video_opts_hq_def = {
.sigmoid_center = 0.75,
.sigmoid_slope = 6.5,
.sigmoid_upscaling = 1,
- .scalers = { "spline36", "bilinear" },
- .dscaler = "mitchell",
- .scaler_params = {{NAN, NAN}, {NAN, NAN}},
- .scaler_radius = {3, 3},
+ .scaler = {
+ {{"spline36", .params={NAN, NAN}}, {.params = {NAN, NAN}}}, // scale
+ {{"mitchell", .params={NAN, NAN}}, {.params = {NAN, NAN}}}, // dscale
+ {{"spline36", .params={NAN, NAN}}, {.params = {NAN, NAN}}}, // cscale
+ {{"oversample", .params={NAN, NAN}}, {.params = {NAN, NAN}}}, // tscale
+ },
.alpha_mode = 2,
.background = {0, 0, 0, 255},
.gamma = 1.0f,
+ .blend_subs = 0,
};
static int validate_scaler_opt(struct mp_log *log, const m_option_t *opt,
struct bstr name, struct bstr param);
+static int validate_window_opt(struct mp_log *log, const m_option_t *opt,
+ struct bstr name, struct bstr param);
+
#define OPT_BASE_STRUCT struct gl_video_opts
const struct m_sub_options gl_video_conf = {
.opts = (const m_option_t[]) {
OPT_FLOATRANGE("gamma", gamma, 0, 0.1, 2.0),
- OPT_FLAG("srgb", srgb, 0),
+ OPT_FLAG("gamma-auto", gamma_auto, 0),
+ OPT_CHOICE_C("target-prim", target_prim, 0, mp_csp_prim_names),
+ OPT_CHOICE_C("target-trc", target_trc, 0, mp_csp_trc_names),
OPT_FLAG("npot", npot, 0),
OPT_FLAG("pbo", pbo, 0),
- OPT_STRING_VALIDATE("scale", scalers[0], 0, validate_scaler_opt),
- OPT_STRING_VALIDATE("cscale", scalers[1], 0, validate_scaler_opt),
- OPT_STRING_VALIDATE("scale-down", dscaler, 0, validate_scaler_opt),
- OPT_FLOAT("scale-param1", scaler_params[0][0], 0),
- OPT_FLOAT("scale-param2", scaler_params[0][1], 0),
- OPT_FLOAT("cscale-param1", scaler_params[1][0], 0),
- OPT_FLOAT("cscale-param2", scaler_params[1][1], 0),
- OPT_FLOATRANGE("scale-radius", scaler_radius[0], 0, 1.0, 16.0),
- OPT_FLOATRANGE("cscale-radius", scaler_radius[1], 0, 1.0, 16.0),
- OPT_FLOATRANGE("scale-antiring", scaler_antiring[0], 0, 0.0, 1.0),
- OPT_FLOATRANGE("cscale-antiring", scaler_antiring[1], 0, 0.0, 1.0),
+ OPT_STRING_VALIDATE("scale", scaler[0].kernel.name, 0, validate_scaler_opt),
+ OPT_STRING_VALIDATE("dscale", scaler[1].kernel.name, 0, validate_scaler_opt),
+ OPT_STRING_VALIDATE("cscale", scaler[2].kernel.name, 0, validate_scaler_opt),
+ OPT_STRING_VALIDATE("tscale", scaler[3].kernel.name, 0, validate_scaler_opt),
+ OPT_FLOAT("scale-param1", scaler[0].kernel.params[0], 0),
+ OPT_FLOAT("scale-param2", scaler[0].kernel.params[1], 0),
+ OPT_FLOAT("dscale-param1", scaler[1].kernel.params[0], 0),
+ OPT_FLOAT("dscale-param2", scaler[1].kernel.params[1], 0),
+ OPT_FLOAT("cscale-param1", scaler[2].kernel.params[0], 0),
+ OPT_FLOAT("cscale-param2", scaler[2].kernel.params[1], 0),
+ OPT_FLOAT("tscale-param1", scaler[3].kernel.params[0], 0),
+ OPT_FLOAT("tscale-param2", scaler[3].kernel.params[1], 0),
+ OPT_FLOAT("scale-blur", scaler[0].kernel.blur, 0),
+ OPT_FLOAT("dscale-blur", scaler[1].kernel.blur, 0),
+ OPT_FLOAT("cscale-blur", scaler[2].kernel.blur, 0),
+ OPT_FLOAT("tscale-blur", scaler[3].kernel.blur, 0),
+ OPT_STRING_VALIDATE("scale-window", scaler[0].window.name, 0, validate_window_opt),
+ OPT_STRING_VALIDATE("dscale-window", scaler[1].window.name, 0, validate_window_opt),
+ OPT_STRING_VALIDATE("cscale-window", scaler[2].window.name, 0, validate_window_opt),
+ OPT_STRING_VALIDATE("tscale-window", scaler[3].window.name, 0, validate_window_opt),
+ OPT_FLOAT("scale-wparam", scaler[0].window.params[0], 0),
+ OPT_FLOAT("dscale-wparam", scaler[1].window.params[0], 0),
+ OPT_FLOAT("cscale-wparam", scaler[2].window.params[0], 0),
+ OPT_FLOAT("tscale-wparam", scaler[3].window.params[0], 0),
+ OPT_FLOATRANGE("scale-radius", scaler[0].radius, 0, 0.5, 16.0),
+ OPT_FLOATRANGE("dscale-radius", scaler[1].radius, 0, 0.5, 16.0),
+ OPT_FLOATRANGE("cscale-radius", scaler[2].radius, 0, 0.5, 16.0),
+ OPT_FLOATRANGE("tscale-radius", scaler[3].radius, 0, 0.5, 3.0),
+ OPT_FLOATRANGE("scale-antiring", scaler[0].antiring, 0, 0.0, 1.0),
+ OPT_FLOATRANGE("dscale-antiring", scaler[1].antiring, 0, 0.0, 1.0),
+ OPT_FLOATRANGE("cscale-antiring", scaler[2].antiring, 0, 0.0, 1.0),
+ OPT_FLOATRANGE("tscale-antiring", scaler[3].antiring, 0, 0.0, 1.0),
OPT_FLAG("scaler-resizes-only", scaler_resizes_only, 0),
OPT_FLAG("linear-scaling", linear_scaling, 0),
OPT_FLAG("fancy-downscaling", fancy_downscaling, 0),
@@ -394,23 +427,23 @@ const struct m_sub_options gl_video_conf = {
({"fruit", 0}, {"ordered", 1}, {"no", -1})),
OPT_INTRANGE("dither-size-fruit", dither_size, 0, 2, 8),
OPT_FLAG("temporal-dither", temporal_dither, 0),
- OPT_CHOICE("chroma-location", chroma_location, 0,
- ({"auto", MP_CHROMA_AUTO},
- {"center", MP_CHROMA_CENTER},
- {"left", MP_CHROMA_LEFT})),
- OPT_CHOICE("alpha", alpha_mode, M_OPT_OPTIONAL_PARAM,
+ OPT_CHOICE("alpha", alpha_mode, 0,
({"no", 0},
- {"yes", 1}, {"", 1},
+ {"yes", 1},
{"blend", 2})),
OPT_FLAG("rectangle-textures", use_rectangle, 0),
OPT_COLOR("background", background, 0),
- OPT_FLAG("smoothmotion", smoothmotion, 0),
- OPT_FLOAT("smoothmotion-threshold", smoothmotion_threshold,
- CONF_RANGE, .min = 0, .max = 0.5),
+ OPT_FLAG("interpolation", interpolation, 0),
+ OPT_CHOICE("blend-subtitles", blend_subs, 0,
+ ({"no", 0},
+ {"yes", 1},
+ {"video", 2})),
+
OPT_REMOVED("approx-gamma", "this is always enabled now"),
OPT_REMOVED("cscale-down", "chroma is never downscaled"),
OPT_REMOVED("scale-sep", "this is set automatically whenever sane"),
OPT_REMOVED("indirect", "this is set automatically whenever sane"),
+ OPT_REMOVED("srgb", "use target-prim=bt709:target-trc=srgb instead"),
OPT_REPLACED("lscale", "scale"),
OPT_REPLACED("lscale-down", "scale-down"),
@@ -422,6 +455,9 @@ const struct m_sub_options gl_video_conf = {
OPT_REPLACED("cparam2", "cscale-param2"),
OPT_REPLACED("cradius", "cscale-radius"),
OPT_REPLACED("cantiring", "cscale-antiring"),
+ OPT_REPLACED("smoothmotion", "interpolation"),
+ OPT_REPLACED("smoothmotion-threshold", "tscale-param1"),
+ OPT_REPLACED("scale-down", "dscale"),
{0}
},
@@ -430,10 +466,12 @@ const struct m_sub_options gl_video_conf = {
};
static void uninit_rendering(struct gl_video *p);
-static void delete_shaders(struct gl_video *p);
+static void uninit_scaler(struct gl_video *p, struct scaler *scaler);
static void check_gl_features(struct gl_video *p);
static bool init_format(int fmt, struct gl_video *init);
-static double get_scale_factor(struct gl_video *p);
+
+#define GLSL(x) gl_sc_add(p->sc, #x "\n");
+#define GLSLF(...) gl_sc_addf(p->sc, __VA_ARGS__)
static const struct fmt_entry *find_tex_format(GL *gl, int bytes_per_comp,
int n_channels)
@@ -466,762 +504,467 @@ void gl_video_set_debug(struct gl_video *p, bool enable)
gl_set_debug_logger(gl, enable ? p->log : NULL);
}
-// Draw a textured quad.
-// x0, y0, x1, y1 = destination coordinates of the quad in pixels
-// tx0, ty0, tx1, ty1 = source texture coordinates in pixels
-// tex_w, tex_h = size of the texture in pixels
-// flags = bits 0-1: rotate, bits 2: flip vertically
-static void draw_quad(struct gl_video *p,
- float x0, float y0, float x1, float y1,
- float tx0, float ty0, float tx1, float ty1,
- float tex_w, float tex_h, int flags)
+static void gl_video_reset_surfaces(struct gl_video *p)
{
- if (p->gl_target != GL_TEXTURE_2D)
- tex_w = tex_h = 1.0f;
-
- if (flags & 4) {
- float tmp = ty0;
- ty0 = ty1;
- ty1 = tmp;
+ for (int i = 0; i < FBOSURFACES_MAX; i++) {
+ p->surfaces[i].pts = 0;
+ p->surfaces[i].vpts = MP_NOPTS_VALUE;
}
+ p->surface_idx = 0;
+ p->surface_now = 0;
+}
- struct vertex va[4] = {
- { {x0, y0}, {tx0 / tex_w, ty0 / tex_h} },
- { {x0, y1}, {tx0 / tex_w, ty1 / tex_h} },
- { {x1, y0}, {tx1 / tex_w, ty0 / tex_h} },
- { {x1, y1}, {tx1 / tex_w, ty1 / tex_h} },
- };
+static inline int fbosurface_wrap(int id)
+{
+ id = id % FBOSURFACES_MAX;
+ return id < 0 ? id + FBOSURFACES_MAX : id;
+}
- int rot = flags & 3;
- while (rot--) {
- static const int perm[4] = {1, 3, 0, 2};
- struct vertex vb[4];
- memcpy(vb, va, sizeof(vb));
- for (int n = 0; n < 4; n++)
- memcpy(va[n].texcoord, vb[perm[n]].texcoord, sizeof(float[2]));
+static void recreate_osd(struct gl_video *p)
+{
+ mpgl_osd_destroy(p->osd);
+ p->osd = NULL;
+ if (p->osd_state) {
+ p->osd = mpgl_osd_init(p->gl, p->log, p->osd_state);
+ mpgl_osd_set_options(p->osd, p->opts.pbo);
}
-
- gl_vao_draw_data(&p->vao, GL_TRIANGLE_STRIP, va, 4);
-
- debug_check_gl(p, "after rendering");
}
-static void transpose3x3(float r[3][3])
+static void reinit_rendering(struct gl_video *p)
{
- MPSWAP(float, r[0][1], r[1][0]);
- MPSWAP(float, r[0][2], r[2][0]);
- MPSWAP(float, r[1][2], r[2][1]);
+ MP_VERBOSE(p, "Reinit rendering.\n");
+
+ debug_check_gl(p, "before scaler initialization");
+
+ uninit_rendering(p);
+
+ recreate_osd(p);
}
-static void update_uniforms(struct gl_video *p, GLuint program)
+static void uninit_rendering(struct gl_video *p)
{
GL *gl = p->gl;
- GLint loc;
- if (program == 0)
- return;
+ for (int n = 0; n < 4; n++)
+ uninit_scaler(p, &p->scaler[n]);
- gl->UseProgram(program);
+ gl->DeleteTextures(1, &p->dither_texture);
+ p->dither_texture = 0;
- struct mp_csp_params cparams = MP_CSP_PARAMS_DEFAULTS;
- cparams.gray = p->is_yuv && !p->is_packed_yuv && p->plane_count == 1;
- cparams.input_bits = p->image_desc.component_bits;
- cparams.texture_bits = (cparams.input_bits + 7) & ~7;
- mp_csp_set_image_params(&cparams, &p->image_params);
- mp_csp_copy_equalizer_values(&cparams, &p->video_eq);
- if (p->image_desc.flags & MP_IMGFLAG_XYZ) {
- cparams.colorspace = MP_CSP_XYZ;
- cparams.input_bits = 8;
- cparams.texture_bits = 8;
- }
+ fbotex_uninit(&p->indirect_fbo);
+ fbotex_uninit(&p->chroma_merge_fbo);
+ fbotex_uninit(&p->blend_subs_fbo);
- loc = gl->GetUniformLocation(program, "transform");
- if (loc >= 0 && p->vp_w > 0 && p->vp_h > 0) {
- float matrix[3][3];
- int vvp[2] = {p->vp_h, 0};
- if (p->vp_vflipped)
- MPSWAP(int, vvp[0], vvp[1]);
- gl_matrix_ortho2d(matrix, 0, p->vp_w, vvp[0], vvp[1]);
- gl->UniformMatrix3fv(loc, 1, GL_FALSE, &matrix[0][0]);
- }
+ for (int n = 0; n < FBOSURFACES_MAX; n++)
+ fbotex_uninit(&p->surfaces[n].fbotex);
- loc = gl->GetUniformLocation(program, "colormatrix");
- if (loc >= 0) {
- struct mp_cmat m = {{{0}}};
- if (p->image_desc.flags & MP_IMGFLAG_XYZ) {
- // Hard-coded as relative colorimetric for now, since this transforms
- // from the source file's D55 material to whatever color space our
- // projector/display lives in, which should be D55 for a proper
- // home cinema setup either way.
- mp_get_xyz2rgb_coeffs(&cparams, p->csp_src,
- MP_INTENT_RELATIVE_COLORIMETRIC, &m);
- } else {
- mp_get_yuv2rgb_coeffs(&cparams, &m);
+ gl_video_reset_surfaces(p);
+}
+
+void gl_video_set_lut3d(struct gl_video *p, struct lut3d *lut3d)
+{
+ GL *gl = p->gl;
+
+ if (!lut3d) {
+ if (p->use_lut_3d) {
+ p->use_lut_3d = false;
+ reinit_rendering(p);
}
- transpose3x3(m.m); // GLES2 can not transpose in glUniformMatrix3fv
- gl->UniformMatrix3fv(loc, 1, GL_FALSE, &m.m[0][0]);
- loc = gl->GetUniformLocation(program, "colormatrix_c");
- gl->Uniform3f(loc, m.c[0], m.c[1], m.c[2]);
+ return;
}
- gl->Uniform1f(gl->GetUniformLocation(program, "input_gamma"),
- p->input_gamma);
+ if (!(gl->mpgl_caps & MPGL_CAP_3D_TEX))
+ return;
- gl->Uniform1f(gl->GetUniformLocation(program, "conv_gamma"),
- p->conv_gamma);
+ if (!p->lut_3d_texture)
+ gl->GenTextures(1, &p->lut_3d_texture);
- // Coefficients for the sigmoidal transform are taken from the
- // formula here: http://www.imagemagick.org/Usage/color_mods/#sigmoidal
- float sig_center = p->opts.sigmoid_center;
- float sig_slope = p->opts.sigmoid_slope;
+ gl->ActiveTexture(GL_TEXTURE0 + TEXUNIT_3DLUT);
+ gl->BindTexture(GL_TEXTURE_3D, p->lut_3d_texture);
+ gl->TexImage3D(GL_TEXTURE_3D, 0, GL_RGB16, lut3d->size[0], lut3d->size[1],
+ lut3d->size[2], 0, GL_RGB, GL_UNSIGNED_SHORT, lut3d->data);
+ gl->TexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ gl->TexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ gl->TexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ gl->TexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ gl->TexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
+ gl->ActiveTexture(GL_TEXTURE0);
- // This function needs to go through (0,0) and (1,1) so we compute the
- // values at 1 and 0, and then scale/shift them, respectively.
- float sig_offset = 1.0/(1+expf(sig_slope * sig_center));
- float sig_scale = 1.0/(1+expf(sig_slope * (sig_center-1))) - sig_offset;
+ p->use_lut_3d = true;
+ check_gl_features(p);
- gl->Uniform1f(gl->GetUniformLocation(program, "sig_center"), sig_center);
- gl->Uniform1f(gl->GetUniformLocation(program, "sig_slope"), sig_slope);
- gl->Uniform1f(gl->GetUniformLocation(program, "sig_scale"), sig_scale);
- gl->Uniform1f(gl->GetUniformLocation(program, "sig_offset"), sig_offset);
+ debug_check_gl(p, "after 3d lut creation");
- gl->Uniform1f(gl->GetUniformLocation(program, "inv_gamma"),
- 1.0f / p->user_gamma);
+ reinit_rendering(p);
+}
- for (int n = 0; n < p->plane_count; n++) {
- char textures_n[32];
- char textures_size_n[32];
- snprintf(textures_n, sizeof(textures_n), "texture%d", n);
- snprintf(textures_size_n, sizeof(textures_size_n), "textures_size[%d]", n);
-
- gl->Uniform1i(gl->GetUniformLocation(program, textures_n), n);
- if (p->gl_target == GL_TEXTURE_2D) {
- gl->Uniform2f(gl->GetUniformLocation(program, textures_size_n),
- p->image.planes[n].tex_w, p->image.planes[n].tex_h);
- } else {
- // Makes the pixel size calculation code think they are 1x1
- gl->Uniform2f(gl->GetUniformLocation(program, textures_size_n), 1, 1);
- }
- }
+static void pass_load_fbotex(struct gl_video *p, struct fbotex *src_fbo, int id,
+ int w, int h)
+{
+ p->pass_tex[id] = (struct src_tex){
+ .gl_tex = src_fbo->texture,
+ .gl_target = GL_TEXTURE_2D,
+ .tex_w = src_fbo->tex_w,
+ .tex_h = src_fbo->tex_h,
+ .src = {0, 0, w, h},
+ };
+}
- loc = gl->GetUniformLocation(program, "chroma_div");
- if (loc >= 0) {
- int xs = p->image_desc.chroma_xs;
- int ys = p->image_desc.chroma_ys;
- gl->Uniform2f(loc, 1.0 / (1 << xs), 1.0 / (1 << ys));
- }
+static void pass_set_image_textures(struct gl_video *p, struct video_image *vimg,
+ struct gl_transform *chroma)
+{
+ GLuint imgtex[4] = {0};
+ *chroma = (struct gl_transform){{{0}}};
+
+ assert(vimg->mpi);
- gl->Uniform2f(gl->GetUniformLocation(program, "chroma_fix"),
- p->chroma_fix[0], p->chroma_fix[1]);
+ float ls_w = 1.0 / (1 << p->image_desc.chroma_xs);
+ float ls_h = 1.0 / (1 << p->image_desc.chroma_ys);
- loc = gl->GetUniformLocation(program, "chroma_center_offset");
- if (loc >= 0) {
- int chr = p->opts.chroma_location;
- if (!chr)
- chr = p->image_params.chroma_location;
+ if (p->image_params.chroma_location != MP_CHROMA_CENTER) {
int cx, cy;
- mp_get_chroma_location(chr, &cx, &cy);
+ mp_get_chroma_location(p->image_params.chroma_location, &cx, &cy);
// By default texture coordinates are such that chroma is centered with
// any chroma subsampling. If a specific direction is given, make it
// so that the luma and chroma sample line up exactly.
// For 4:4:4, setting chroma location should have no effect at all.
// luma sample size (in chroma coord. space)
- float ls_w = 1.0 / (1 << p->image_desc.chroma_xs);
- float ls_h = 1.0 / (1 << p->image_desc.chroma_ys);
- // move chroma center to luma center (in chroma coord. space)
- float o_x = ls_w < 1 ? ls_w * -cx / 2 : 0;
- float o_y = ls_h < 1 ? ls_h * -cy / 2 : 0;
- int c = p->gl_target == GL_TEXTURE_2D ? 1 : 0;
- gl->Uniform2f(loc, o_x / FFMAX(p->image.planes[1].w * c, 1),
- o_y / FFMAX(p->image.planes[1].h * c, 1));
+ chroma->t[0] = ls_w < 1 ? ls_w * -cx / 2 : 0;
+ chroma->t[1] = ls_h < 1 ? ls_h * -cy / 2 : 0;
}
- gl->Uniform2f(gl->GetUniformLocation(program, "dither_size"),
- p->dither_size, p->dither_size);
-
- gl->Uniform1i(gl->GetUniformLocation(program, "lut_3d"), TEXUNIT_3DLUT);
-
- loc = gl->GetUniformLocation(program, "cms_matrix");
- if (loc >= 0) {
- float cms_matrix[3][3] = {{0}};
- // Hard-coded to relative colorimetric - for a BT.2020 3DLUT we expect
- // the input to be actual BT.2020 and not something red- or blueshifted,
- // and for sRGB monitors we most likely want relative scaling either way.
- mp_get_cms_matrix(p->csp_src, p->csp_dest, MP_INTENT_RELATIVE_COLORIMETRIC, cms_matrix);
- gl->UniformMatrix3fv(loc, 1, GL_TRUE, &cms_matrix[0][0]);
- }
+ // Make sure luma/chroma sizes are aligned.
+ // Example: For 4:2:0 with size 3x3, the subsampled chroma plane is 2x2
+ // so luma (3,3) has to align with chroma (2,2).
+ chroma->m[0][0] = ls_w * (float)vimg->planes[0].tex_w
+ / vimg->planes[1].tex_w;
+ chroma->m[1][1] = ls_h * (float)vimg->planes[0].tex_h
+ / vimg->planes[1].tex_h;
- for (int n = 0; n < 2; n++) {
- const char *lut = p->scalers[n].lut_name;
- if (lut)
- gl->Uniform1i(gl->GetUniformLocation(program, lut),
- TEXUNIT_SCALERS + n);
+ if (p->hwdec_active) {
+ p->hwdec->driver->map_image(p->hwdec, vimg->mpi, imgtex);
+ } else {
+ for (int n = 0; n < p->plane_count; n++)
+ imgtex[n] = vimg->planes[n].gl_texture;
}
- gl->Uniform1i(gl->GetUniformLocation(program, "dither"), TEXUNIT_DITHER);
- gl->Uniform1f(gl->GetUniformLocation(program, "dither_quantization"),
- p->dither_quantization);
- gl->Uniform1f(gl->GetUniformLocation(program, "dither_center"),
- p->dither_center);
-
- float sparam1_l = p->opts.scaler_params[0][0];
- float sparam1_c = p->opts.scaler_params[1][0];
- gl->Uniform1f(gl->GetUniformLocation(program, "filter_param1_l"),
- isnan(sparam1_l) ? 0.5f : sparam1_l);
- gl->Uniform1f(gl->GetUniformLocation(program, "filter_param1_c"),
- isnan(sparam1_c) ? 0.5f : sparam1_c);
-
- gl->Uniform3f(gl->GetUniformLocation(program, "translation"), 0, 0, 0);
-
- gl->UseProgram(0);
-
- debug_check_gl(p, "update_uniforms()");
-}
-
-static void update_all_uniforms(struct gl_video *p)
-{
- for (int n = 0; n < SUBBITMAP_COUNT; n++)
- update_uniforms(p, p->osd->programs[n]);
- update_uniforms(p, p->indirect_program);
- update_uniforms(p, p->scale_sep_program);
- update_uniforms(p, p->final_program);
- update_uniforms(p, p->inter_program);
-}
-
-#define SECTION_HEADER "#!section "
-
-static char *get_section(void *talloc_ctx, struct bstr source,
- const char *section)
-{
- char *res = talloc_strdup(talloc_ctx, "");
- bool copy = false;
- while (source.len) {
- struct bstr line = bstr_strip_linebreaks(bstr_getline(source, &source));
- if (bstr_eatstart(&line, bstr0(SECTION_HEADER))) {
- copy = bstrcmp0(line, section) == 0;
- } else if (copy) {
- res = talloc_asprintf_append_buffer(res, "%.*s\n", BSTR_P(line));
- }
+ for (int n = 0; n < p->plane_count; n++) {
+ struct texplane *t = &vimg->planes[n];
+ p->pass_tex[n] = (struct src_tex){
+ .gl_tex = imgtex[n],
+ .gl_target = t->gl_target,
+ .tex_w = t->tex_w,
+ .tex_h = t->tex_h,
+ .src = {0, 0, t->w, t->h},
+ };
}
- return res;
}
-static char *t_concat(void *talloc_ctx, const char *s1, const char *s2)
+static int align_pow2(int s)
{
- return talloc_asprintf(talloc_ctx, "%s%s", s1, s2);
+ int r = 1;
+ while (r < s)
+ r *= 2;
+ return r;
}
-static GLuint create_shader(struct gl_video *p, GLenum type, const char *header,
- const char *source)
+static void init_video(struct gl_video *p)
{
GL *gl = p->gl;
- void *tmp = talloc_new(NULL);
- const char *full_source = t_concat(tmp, header, source);
+ check_gl_features(p);
- GLuint shader = gl->CreateShader(type);
- gl->ShaderSource(shader, 1, &full_source, NULL);
- gl->CompileShader(shader);
- GLint status;
- gl->GetShaderiv(shader, GL_COMPILE_STATUS, &status);
- GLint log_length;
- gl->GetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length);
+ init_format(p->image_params.imgfmt, p);
+ p->gl_target = p->opts.use_rectangle ? GL_TEXTURE_RECTANGLE : GL_TEXTURE_2D;
- int pri = status ? (log_length > 1 ? MSGL_V : MSGL_DEBUG) : MSGL_ERR;
- const char *typestr = type == GL_VERTEX_SHADER ? "vertex" : "fragment";
- if (mp_msg_test(p->log, pri)) {
- MP_MSG(p, pri, "%s shader source:\n", typestr);
- mp_log_source(p->log, pri, full_source);
- }
- if (log_length > 1) {
- GLchar *logstr = talloc_zero_size(tmp, log_length + 1);
- gl->GetShaderInfoLog(shader, log_length, NULL, logstr);
- MP_MSG(p, pri, "%s shader compile log (status=%d):\n%s\n",
- typestr, status, logstr);
+ 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");
+ init_format(p->image_params.imgfmt, p);
+ p->gl_target = p->hwdec->gl_texture_target;
}
- talloc_free(tmp);
-
- return shader;
-}
+ mp_image_params_guess_csp(&p->image_params);
-static void prog_create_shader(struct gl_video *p, GLuint program, GLenum type,
- const char *header, const char *source)
-{
- GL *gl = p->gl;
- GLuint shader = create_shader(p, type, header, source);
- gl->AttachShader(program, shader);
- gl->DeleteShader(shader);
-}
+ p->image_w = p->image_params.w;
+ p->image_h = p->image_params.h;
-static void link_shader(struct gl_video *p, GLuint program)
-{
- GL *gl = p->gl;
- gl->LinkProgram(program);
- GLint status;
- gl->GetProgramiv(program, GL_LINK_STATUS, &status);
- GLint log_length;
- gl->GetProgramiv(program, GL_INFO_LOG_LENGTH, &log_length);
+ int eq_caps = MP_CSP_EQ_CAPS_GAMMA;
+ if (p->is_yuv && 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;
+ p->video_eq.capabilities = eq_caps;
- int pri = status ? (log_length > 1 ? MSGL_V : MSGL_DEBUG) : MSGL_ERR;
- if (mp_msg_test(p->log, pri)) {
- GLchar *logstr = talloc_zero_size(NULL, log_length + 1);
- gl->GetProgramInfoLog(program, log_length, NULL, logstr);
- MP_MSG(p, pri, "shader link log (status=%d): %s\n", status, logstr);
- talloc_free(logstr);
- }
-}
+ debug_check_gl(p, "before video texture creation");
-#define PRELUDE_END "// -- prelude end\n"
+ struct video_image *vimg = &p->image;
-static GLuint create_program(struct gl_video *p, const char *name,
- const char *header, const char *vertex,
- const char *frag, struct gl_vao *vao)
-{
- GL *gl = p->gl;
- MP_VERBOSE(p, "compiling shader program '%s', header:\n", name);
- const char *real_header = strstr(header, PRELUDE_END);
- real_header = real_header ? real_header + strlen(PRELUDE_END) : header;
- mp_log_source(p->log, MSGL_V, real_header);
- GLuint prog = gl->CreateProgram();
- prog_create_shader(p, prog, GL_VERTEX_SHADER, header, vertex);
- prog_create_shader(p, prog, GL_FRAGMENT_SHADER, header, frag);
- gl_vao_bind_attribs(vao, prog);
- link_shader(p, prog);
- return prog;
-}
+ for (int n = 0; n < p->plane_count; n++) {
+ struct texplane *plane = &vimg->planes[n];
-static void shader_def(char **shader, const char *name,
- const char *value)
-{
- *shader = talloc_asprintf_append(*shader, "#define %s %s\n", name, value);
-}
+ plane->gl_target = p->gl_target;
-static void shader_def_opt(char **shader, const char *name, bool b)
-{
- if (b)
- shader_def(shader, name, "1");
-}
+ plane->w = mp_chroma_div_up(p->image_w, p->image_desc.xs[n]);
+ plane->h = mp_chroma_div_up(p->image_h, p->image_desc.ys[n]);
-#define APPENDF(s_ptr, ...) \
- *(s_ptr) = talloc_asprintf_append(*(s_ptr), __VA_ARGS__)
+ plane->tex_w = plane->w;
+ plane->tex_h = plane->h;
-static void shader_setup_scaler(char **shader, struct scaler *scaler, int pass)
-{
- int unit = scaler->index;
- const char *target = unit == 0 ? "SAMPLE" : "SAMPLE_C";
- if (!scaler->kernel) {
- APPENDF(shader, "#define %s(p0, p1, p2) "
- "sample_%s(p0, p1, p2, filter_param1_%c)\n",
- target, scaler->name, "lc"[unit]);
- } else {
- int size = scaler->kernel->size;
- const char *lut_tex = scaler->lut_name;
- char name[40];
- snprintf(name, sizeof(name), "sample_scaler%d", unit);
- APPENDF(shader, "#define DEF_SCALER%d \\\n ", unit);
- char lut_fn[40];
- if (scaler->kernel->polar) {
- int radius = (int)scaler->kernel->radius;
- // SAMPLE_CONVOLUTION_POLAR_R(NAME, R, LUT, WEIGHTS_FN, ANTIRING)
- APPENDF(shader, "SAMPLE_CONVOLUTION_POLAR_R(%s, %d, %s, WEIGHTS%d, %f)\n",
- name, radius, lut_tex, unit, scaler->antiring);
-
- // Pre-compute unrolled weights matrix
- APPENDF(shader, "#define WEIGHTS%d(LUT) \\\n ", unit);
- for (int y = 1-radius; y <= radius; y++) {
- for (int x = 1-radius; x <= radius; x++) {
- // Since we can't know the subpixel position in advance,
- // assume a worst case scenario.
- int yy = y > 0 ? y-1 : y;
- int xx = x > 0 ? x-1 : x;
- double d = sqrt(xx*xx + yy*yy);
-
- // Samples outside the radius are unnecessary
- if (d < radius) {
- APPENDF(shader, "SAMPLE_POLAR_%s(LUT, %f, %d, %d) \\\n ",
- // The center 4 coefficients are the primary
- // contributors, used to clamp the result for
- // anti-ringing
- (x >= 0 && y >= 0 && x <= 1 && y <= 1)
- ? "PRIMARY" : "HELPER",
- (double)radius, x, y);
- }
- }
- }
- APPENDF(shader, "\n");
- } else {
- if (size == 2 || size == 6) {
- snprintf(lut_fn, sizeof(lut_fn), "weights%d", size);
- } else {
- snprintf(lut_fn, sizeof(lut_fn), "weights_scaler%d", unit);
- APPENDF(shader, "WEIGHTS_N(%s, %d) \\\n ", lut_fn, size);
- }
- if (pass != -1) {
- // The direction/pass assignment is rather arbitrary, but fixed in
- // other parts of the code (like FBO setup).
- const char *direction = pass == 0 ? "0, 1" : "1, 0";
- // SAMPLE_CONVOLUTION_SEP_N(NAME, DIR, N, LUT, WEIGHTS_FUNC)
- APPENDF(shader, "SAMPLE_CONVOLUTION_SEP_N(%s, vec2(%s), %d, %s, %s)\n",
- name, direction, size, lut_tex, lut_fn);
- } else {
- // SAMPLE_CONVOLUTION_N(NAME, N, LUT, WEIGHTS_FUNC)
- APPENDF(shader, "SAMPLE_CONVOLUTION_N(%s, %d, %s, %s)\n",
- name, size, lut_tex, lut_fn);
+ if (!p->hwdec_active) {
+ if (!p->opts.npot) {
+ plane->tex_w = align_pow2(plane->tex_w);
+ plane->tex_h = align_pow2(plane->tex_h);
}
- }
- APPENDF(shader, "#define %s %s\n", target, name);
- }
-}
-
-// return false if RGB or 4:4:4 YUV
-static bool input_is_subsampled(struct gl_video *p)
-{
- for (int i = 0; i < p->plane_count; i++)
- if (p->image_desc.xs[i] || p->image_desc.ys[i])
- return true;
- return false;
-}
-static void compile_shaders(struct gl_video *p)
-{
- GL *gl = p->gl;
+ gl->ActiveTexture(GL_TEXTURE0 + n);
+ gl->GenTextures(1, &plane->gl_texture);
+ gl->BindTexture(p->gl_target, plane->gl_texture);
- debug_check_gl(p, "before shaders");
-
- delete_shaders(p);
-
- void *tmp = talloc_new(NULL);
-
- struct bstr src = bstr0(vo_opengl_shaders);
- char *vertex_shader = get_section(tmp, src, "vertex_all");
- char *shader_prelude = get_section(tmp, src, "prelude");
- char *s_video = get_section(tmp, src, "frag_video");
-
- bool rg = gl->mpgl_caps & MPGL_CAP_TEX_RG;
- bool tex1d = gl->mpgl_caps & MPGL_CAP_1D_TEX;
- bool tex3d = gl->mpgl_caps & MPGL_CAP_3D_TEX;
- bool arrays = gl->mpgl_caps & MPGL_CAP_1ST_CLASS_ARRAYS;
- char *header =
- talloc_asprintf(tmp, "#version %d%s\n"
- "#define HAVE_RG %d\n"
- "#define HAVE_1DTEX %d\n"
- "#define HAVE_3DTEX %d\n"
- "#define HAVE_ARRAYS %d\n"
- "%s%s",
- gl->glsl_version, gl->es >= 300 ? " es" : "",
- rg, tex1d, tex3d, arrays, shader_prelude, PRELUDE_END);
-
- bool use_cms = p->opts.srgb || p->use_lut_3d;
- // 3DLUT overrides sRGB
- bool use_srgb = p->opts.srgb && !p->use_lut_3d;
-
- float input_gamma = 1.0;
- float conv_gamma = 1.0;
-
- bool is_xyz = p->image_desc.flags & MP_IMGFLAG_XYZ;
- if (is_xyz) {
- input_gamma *= 2.6;
- // Note that this results in linear light, so we make sure to enable
- // use_linear_light for XYZ inputs as well.
- }
-
- p->input_gamma = input_gamma;
- p->conv_gamma = conv_gamma;
-
- bool use_input_gamma = p->input_gamma != 1.0;
- bool use_conv_gamma = p->conv_gamma != 1.0;
- bool use_const_luma = p->image_params.colorspace == MP_CSP_BT_2020_C;
-
- enum mp_csp_trc gamma_fun = MP_CSP_TRC_NONE;
-
- // If either color correction option (3dlut or srgb) is enabled, or if
- // sigmoidal upscaling is requested, or if the source is linear XYZ, we
- // always scale in linear light
- bool use_linear_light = p->opts.linear_scaling || p->opts.sigmoid_upscaling
- || use_cms || is_xyz;
-
- if (use_linear_light) {
- // We use the color level range to distinguish between PC
- // content like images, which are most likely sRGB, and TV content
- // like movies, which are most likely BT.1886. XYZ input is always
- // treated as linear.
- if (is_xyz) {
- gamma_fun = MP_CSP_TRC_LINEAR;
- } else if (p->image_params.colorlevels == MP_CSP_LEVELS_PC) {
- gamma_fun = MP_CSP_TRC_SRGB;
- } else {
- gamma_fun = MP_CSP_TRC_BT_1886;
- }
- }
+ gl->TexImage2D(p->gl_target, 0, plane->gl_internal_format,
+ plane->tex_w, plane->tex_h, 0,
+ plane->gl_format, plane->gl_type, NULL);
- // The inverse of the above transformation is normally handled by
- // the CMS cases, but if CMS is disabled we need to go back manually
- bool use_inv_bt1886 = false;
- if (use_linear_light && !use_cms) {
- if (gamma_fun == MP_CSP_TRC_SRGB) {
- use_srgb = true;
- } else {
- use_inv_bt1886 = true;
+ gl->TexParameteri(p->gl_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ gl->TexParameteri(p->gl_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ 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);
}
- }
-
- // Optionally transform to sigmoidal color space if requested.
- p->sigmoid_enabled = p->opts.sigmoid_upscaling;
- bool use_sigmoid = p->sigmoid_enabled && p->upscaling;
- // Figure out the right color spaces we need to convert, if any
- enum mp_csp_prim prim_src = p->image_params.primaries, prim_dest;
- if (use_cms) {
- // sRGB mode wants sRGB aka BT.709 primaries, but the 3DLUT is
- // always built against BT.2020.
- prim_dest = p->opts.srgb ? MP_CSP_PRIM_BT_709 : MP_CSP_PRIM_BT_2020;
- } else {
- // If no CMS is being done we just want to output stuff as-is,
- // in the native colorspace of the source.
- prim_dest = prim_src;
+ MP_VERBOSE(p, "Texture for plane %d: %dx%d\n",
+ n, plane->tex_w, plane->tex_h);
}
+ gl->ActiveTexture(GL_TEXTURE0);
- // XYZ input has no defined input color space, so we can directly convert
- // it to whatever output space we actually need.
- if (p->image_desc.flags & MP_IMGFLAG_XYZ)
- prim_src = prim_dest;
+ debug_check_gl(p, "after video texture creation");
- // Set the colorspace primaries and figure out whether we need to perform
- // an extra conversion.
- p->csp_src = mp_get_csp_primaries(prim_src);
- p->csp_dest = mp_get_csp_primaries(prim_dest);
+ reinit_rendering(p);
+}
- bool use_cms_matrix = prim_src != prim_dest;
+static void uninit_video(struct gl_video *p)
+{
+ GL *gl = p->gl;
- if (p->gl_target == GL_TEXTURE_RECTANGLE) {
- shader_def(&header, "VIDEO_SAMPLER", "sampler2DRect");
- shader_def_opt(&header, "USE_RECTANGLE", true);
- } else {
- shader_def(&header, "VIDEO_SAMPLER", "sampler2D");
- }
-
- // Need to pass alpha through the whole chain. (Not needed for OSD shaders.)
- if (p->opts.alpha_mode == 1)
- shader_def_opt(&header, "USE_ALPHA", p->has_alpha);
-
- char *header_osd = talloc_strdup(tmp, header);
- shader_def_opt(&header_osd, "USE_OSD_LINEAR_CONV_BT1886",
- use_cms && gamma_fun == MP_CSP_TRC_BT_1886);
- shader_def_opt(&header_osd, "USE_OSD_LINEAR_CONV_SRGB",
- use_cms && gamma_fun == MP_CSP_TRC_SRGB);
- shader_def_opt(&header_osd, "USE_OSD_CMS_MATRIX", use_cms_matrix);
- shader_def_opt(&header_osd, "USE_OSD_3DLUT", p->use_lut_3d);
- shader_def_opt(&header_osd, "USE_OSD_SRGB", use_cms && use_srgb);
-
- for (int n = 0; n < SUBBITMAP_COUNT; n++) {
- const char *name = osd_shaders[n];
- if (name) {
- char *s_osd = get_section(tmp, src, name);
- p->osd_programs[n] = create_program(p, name, header_osd,
- vertex_shader, s_osd,
- &p->osd->vao);
- }
- }
+ uninit_rendering(p);
- struct gl_vao *v = &p->vao; // VAO to use to draw primitives
+ struct video_image *vimg = &p->image;
- char *header_conv = talloc_strdup(tmp, "");
- char *header_final = talloc_strdup(tmp, "");
- char *header_inter = talloc_strdup(tmp, "");
- char *header_sep = NULL;
+ for (int n = 0; n < p->plane_count; n++) {
+ struct texplane *plane = &vimg->planes[n];
- if (p->image_desc.id == IMGFMT_NV12 || p->image_desc.id == IMGFMT_NV21) {
- shader_def(&header_conv, "USE_CONV", "CONV_NV12");
- } else if (p->plane_count > 1) {
- shader_def(&header_conv, "USE_CONV", "CONV_PLANAR");
+ gl->DeleteTextures(1, &plane->gl_texture);
+ plane->gl_texture = 0;
+ gl->DeleteBuffers(1, &plane->gl_buffer);
+ plane->gl_buffer = 0;
+ plane->buffer_ptr = NULL;
+ plane->buffer_size = 0;
}
+ mp_image_unrefp(&vimg->mpi);
- if (p->color_swizzle[0])
- shader_def(&header_conv, "USE_COLOR_SWIZZLE", p->color_swizzle);
- shader_def_opt(&header_conv, "USE_INPUT_GAMMA", use_input_gamma);
- shader_def_opt(&header_conv, "USE_COLORMATRIX", !p->is_rgb);
- shader_def_opt(&header_conv, "USE_CONV_GAMMA", use_conv_gamma);
- shader_def_opt(&header_conv, "USE_CONST_LUMA", use_const_luma);
- shader_def_opt(&header_conv, "USE_LINEAR_LIGHT_BT1886",
- gamma_fun == MP_CSP_TRC_BT_1886);
- shader_def_opt(&header_conv, "USE_LINEAR_LIGHT_SRGB",
- gamma_fun == MP_CSP_TRC_SRGB);
- shader_def_opt(&header_conv, "USE_SIGMOID", use_sigmoid);
- if (p->opts.alpha_mode > 0 && p->has_alpha && p->plane_count > 3)
- shader_def(&header_conv, "USE_ALPHA_PLANE", "3");
- if (p->opts.alpha_mode == 2 && p->has_alpha)
- shader_def(&header_conv, "USE_ALPHA_BLEND", "1");
- shader_def_opt(&header_conv, "USE_CHROMA_FIX",
- p->chroma_fix[0] != 1.0f || p->chroma_fix[1] != 1.0f);
-
- shader_def_opt(&header_final, "USE_SIGMOID_INV", use_sigmoid);
- shader_def_opt(&header_final, "USE_INV_GAMMA", p->user_gamma_enabled);
- shader_def_opt(&header_final, "USE_CMS_MATRIX", use_cms_matrix);
- shader_def_opt(&header_final, "USE_3DLUT", p->use_lut_3d);
- shader_def_opt(&header_final, "USE_SRGB", use_srgb);
- shader_def_opt(&header_final, "USE_INV_BT1886", use_inv_bt1886);
- shader_def_opt(&header_final, "USE_DITHER", p->dither_texture != 0);
- shader_def_opt(&header_final, "USE_TEMPORAL_DITHER", p->opts.temporal_dither);
-
- if (p->scalers[0].kernel && !p->scalers[0].kernel->polar) {
- header_sep = talloc_strdup(tmp, "");
- shader_def_opt(&header_sep, "FIXED_SCALE", true);
- shader_setup_scaler(&header_sep, &p->scalers[0], 0);
- shader_setup_scaler(&header_inter, &p->scalers[0], 1);
- } else {
- shader_setup_scaler(&header_inter, &p->scalers[0], -1);
- }
+ // Invalidate image_params to ensure that gl_video_config() will call
+ // init_video() on uninitialized gl_video.
+ p->real_image_params = (struct mp_image_params){0};
+ p->image_params = p->real_image_params;
+}
- bool use_interpolation = p->opts.smoothmotion;
+static void pass_prepare_src_tex(struct gl_video *p)
+{
+ GL *gl = p->gl;
+ struct gl_shader_cache *sc = p->sc;
+
+ for (int n = 0; n < TEXUNIT_VIDEO_NUM; n++) {
+ struct src_tex *s = &p->pass_tex[n];
+ if (!s->gl_tex)
+ continue;
+
+ char texture_name[32];
+ char texture_size[32];
+ snprintf(texture_name, sizeof(texture_name), "texture%d", n);
+ snprintf(texture_size, sizeof(texture_size), "texture_size%d", n);
+
+ 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->tex_w;
+ f[1] = s->tex_h;
+ }
+ gl_sc_uniform_vec2(sc, texture_size, f);
- if (use_interpolation) {
- shader_def_opt(&header_inter, "FIXED_SCALE", true);
- shader_def_opt(&header_final, "USE_LINEAR_INTERPOLATION", 1);
+ gl->ActiveTexture(GL_TEXTURE0 + n);
+ gl->BindTexture(s->gl_target, s->gl_tex);
}
+ gl->ActiveTexture(GL_TEXTURE0);
+}
- // The indirect pass is used to preprocess the image before scaling.
- bool use_indirect = false;
-
- // Don't sample from input video textures before converting the input to
- // its proper gamma.
- if (use_input_gamma || use_conv_gamma || use_linear_light || use_const_luma)
- use_indirect = true;
+// flags = bits 0-1: rotate, bit 2: flip vertically
+static void render_pass_quad(struct gl_video *p, int vp_w, int vp_h,
+ const struct mp_rect *dst, int flags)
+{
+ struct vertex va[4];
- // Trivial scalers are implemented directly and efficiently by the GPU.
- // This only includes bilinear and nearest neighbour in OpenGL, but we
- // don't support nearest neighbour upsampling.
- bool trivial_scaling = strcmp(p->scalers[0].name, "bilinear") == 0 &&
- strcmp(p->scalers[1].name, "bilinear") == 0;
+ struct gl_transform t;
+ gl_transform_ortho(&t, 0, vp_w, 0, vp_h);
- // If the video is subsampled, chroma information needs to be pulled up to
- // the input size before scaling can be done. Even for 4:4:4 or planar RGB
- // this is also faster because it means the scalers can operate on all
- // channels simultaneously. This is unnecessary for trivial scaling.
- if (p->plane_count > 1 && !trivial_scaling)
- use_indirect = true;
+ float x[2] = {dst->x0, dst->x1};
+ float y[2] = {dst->y0, dst->y1};
+ gl_transform_vec(t, &x[0], &y[0]);
+ gl_transform_vec(t, &x[1], &y[1]);
- if (input_is_subsampled(p)) {
- shader_setup_scaler(&header_conv, &p->scalers[1], -1);
- } else {
- // Force using the normal scaler on chroma. If the "indirect" stage is
- // used, the actual scaling will happen in the next stage.
- shader_def(&header_conv, "SAMPLE_C",
- use_indirect ? "SAMPLE_TRIVIAL" : "SAMPLE");
- }
-
- if (use_indirect) {
- // We don't use filtering for the Y-plane (luma), because it's never
- // scaled in this scenario.
- shader_def(&header_conv, "SAMPLE", "SAMPLE_TRIVIAL");
- shader_def_opt(&header_conv, "FIXED_SCALE", true);
- header_conv = t_concat(tmp, header, header_conv);
- p->indirect_program =
- create_program(p, "indirect", header_conv, vertex_shader, s_video, v);
- } else if (header_sep) {
- header_sep = t_concat(tmp, header_sep, header_conv);
- } else {
- header_inter = t_concat(tmp, header_inter, header_conv);
- }
-
- if (header_sep) {
- header_sep = t_concat(tmp, header, header_sep);
- p->scale_sep_program =
- create_program(p, "scale_sep", header_sep, vertex_shader, s_video, v);
+ for (int n = 0; n < 4; n++) {
+ struct vertex *v = &va[n];
+ v->position.x = x[n / 2];
+ v->position.y = y[n % 2];
+ for (int i = 0; i < TEXUNIT_VIDEO_NUM; i++) {
+ struct src_tex *s = &p->pass_tex[i];
+ if (s->gl_tex) {
+ float tx[2] = {s->src.x0, s->src.x1};
+ float ty[2] = {s->src.y0, s->src.y1};
+ if (flags & 4)
+ MPSWAP(float, ty[0], ty[1]);
+ bool rect = s->gl_target == GL_TEXTURE_RECTANGLE;
+ v->texcoord[i].x = tx[n / 2] / (rect ? 1 : s->tex_w);
+ v->texcoord[i].y = ty[n % 2] / (rect ? 1 : s->tex_h);
+ }
+ }
}
- if (use_interpolation) {
- header_inter = t_concat(tmp, header, header_inter);
- p->inter_program =
- create_program(p, "inter", header_inter, vertex_shader, s_video, v);
- } else {
- header_final = t_concat(tmp, header_final, header_inter);
+ int rot = flags & 3;
+ while (rot--) {
+ static const int perm[4] = {1, 3, 0, 2};
+ struct vertex vb[4];
+ memcpy(vb, va, sizeof(vb));
+ for (int n = 0; n < 4; n++)
+ memcpy(va[n].texcoord, vb[perm[n]].texcoord,
+ sizeof(struct vertex_pt[TEXUNIT_VIDEO_NUM]));
}
- header_final = t_concat(tmp, header, header_final);
- p->final_program =
- create_program(p, "final", header_final, vertex_shader, s_video, v);
+ p->gl->Viewport(0, 0, vp_w, abs(vp_h));
+ gl_vao_draw_data(&p->vao, GL_TRIANGLE_STRIP, va, 4);
- debug_check_gl(p, "shader compilation");
+ debug_check_gl(p, "after rendering");
+}
- talloc_free(tmp);
+// flags: see render_pass_quad
+static void finish_pass_direct(struct gl_video *p, GLint fbo, int vp_w, int vp_h,
+ const struct mp_rect *dst, int flags)
+{
+ GL *gl = p->gl;
+ pass_prepare_src_tex(p);
+ gl->BindFramebuffer(GL_FRAMEBUFFER, fbo);
+ gl_sc_gen_shader_and_reset(p->sc);
+ render_pass_quad(p, vp_w, vp_h, dst, flags);
+ gl->BindFramebuffer(GL_FRAMEBUFFER, 0);
+ memset(&p->pass_tex, 0, sizeof(p->pass_tex));
}
-static void delete_program(GL *gl, GLuint *prog)
+// dst_fbo: this will be used for rendering; possibly reallocating the whole
+// FBO, if the required parameters have changed
+// w, h: required FBO target dimension, and also defines the target rectangle
+// used for rasterization
+// tex: the texture unit to load the result back into
+// flags: 0 or combination of FBOTEX_FUZZY_W/FBOTEX_FUZZY_H (setting the fuzzy
+// flags allows the FBO to be larger than the w/h parameters)
+static void finish_pass_fbo(struct gl_video *p, struct fbotex *dst_fbo,
+ int w, int h, int tex, int flags)
{
- gl->DeleteProgram(*prog);
- *prog = 0;
+ fbotex_change(dst_fbo, p->gl, p->log, w, h, p->opts.fbo_format, flags);
+
+ finish_pass_direct(p, dst_fbo->fbo, dst_fbo->tex_w, dst_fbo->tex_h,
+ &(struct mp_rect){0, 0, w, h}, 0);
+ pass_load_fbotex(p, dst_fbo, tex, w, h);
}
-static void delete_shaders(struct gl_video *p)
+static void uninit_scaler(struct gl_video *p, struct scaler *scaler)
{
GL *gl = p->gl;
-
- for (int n = 0; n < SUBBITMAP_COUNT; n++)
- delete_program(gl, &p->osd->programs[n]);
- delete_program(gl, &p->indirect_program);
- delete_program(gl, &p->scale_sep_program);
- delete_program(gl, &p->final_program);
- delete_program(gl, &p->inter_program);
+ fbotex_uninit(&scaler->sep_fbo);
+ gl->DeleteTextures(1, &scaler->gl_lut);
+ scaler->gl_lut = 0;
+ scaler->kernel = NULL;
+ scaler->initialized = false;
}
-static void get_scale_factors(struct gl_video *p, double xy[2])
+// Semantic equality
+static bool double_seq(double a, double b)
{
- xy[0] = (p->dst_rect.x1 - p->dst_rect.x0) /
- (double)(p->src_rect.x1 - p->src_rect.x0);
- xy[1] = (p->dst_rect.y1 - p->dst_rect.y0) /
- (double)(p->src_rect.y1 - p->src_rect.y0);
+ return (isnan(a) && isnan(b)) || a == b;
}
-static double get_scale_factor(struct gl_video *p)
+static bool scaler_fun_eq(struct scaler_fun a, struct scaler_fun b)
{
- double xy[2];
- get_scale_factors(p, xy);
- return FFMIN(xy[0], xy[1]);
+ if ((a.name && !b.name) || (b.name && !a.name))
+ return false;
+
+ return ((!a.name && !b.name) || strcmp(a.name, b.name) == 0) &&
+ double_seq(a.params[0], b.params[0]) &&
+ double_seq(a.params[1], b.params[1]) &&
+ a.blur == b.blur;
}
-static void update_scale_factor(struct gl_video *p, struct scaler *scaler)
+static bool scaler_conf_eq(struct scaler_config a, struct scaler_config b)
{
- double scale = 1.0;
- double xy[2];
- get_scale_factors(p, xy);
- double f = MPMIN(xy[0], xy[1]);
- if (p->opts.fancy_downscaling && scaler->index == 0 && f < 1.0 &&
- fabs(xy[0] - f) < 0.01 && fabs(xy[1] - f) < 0.01)
- {
- MP_VERBOSE(p, "Using fancy-downscaling (scaler %d).\n", scaler->index);
- scale = FFMAX(1.0, 1.0 / f);
- }
- scaler->insufficient = !mp_init_filter(scaler->kernel, filter_sizes, scale);
+ // Note: antiring isn't compared because it doesn't affect LUT
+ // generation
+ return scaler_fun_eq(a.kernel, b.kernel) &&
+ scaler_fun_eq(a.window, b.window) &&
+ a.radius == b.radius;
}
-static void init_scaler(struct gl_video *p, struct scaler *scaler)
+static void reinit_scaler(struct gl_video *p, struct scaler *scaler,
+ const struct scaler_config *conf,
+ double scale_factor,
+ int sizes[])
{
GL *gl = p->gl;
- assert(scaler->name);
+ if (scaler_conf_eq(scaler->conf, *conf) &&
+ scaler->scale_factor == scale_factor &&
+ scaler->initialized)
+ return;
+
+ uninit_scaler(p, scaler);
- scaler->kernel = NULL;
+ scaler->conf = *conf;
+ scaler->scale_factor = scale_factor;
scaler->insufficient = false;
+ scaler->initialized = true;
- const struct filter_kernel *t_kernel = mp_find_filter_kernel(scaler->name);
+ const struct filter_kernel *t_kernel = mp_find_filter_kernel(conf->kernel.name);
if (!t_kernel)
return;
scaler->kernel_storage = *t_kernel;
scaler->kernel = &scaler->kernel_storage;
+ const char *win = conf->window.name;
+ if (!win || !win[0])
+ win = t_kernel->window; // fall back to the scaler's default window
+ const struct filter_window *t_window = mp_find_filter_window(win);
+ if (t_window)
+ scaler->kernel->w = *t_window;
+
for (int n = 0; n < 2; n++) {
- if (!isnan(p->opts.scaler_params[scaler->index][n]))
- scaler->kernel->params[n] = p->opts.scaler_params[scaler->index][n];
+ if (!isnan(conf->kernel.params[n]))
+ scaler->kernel->f.params[n] = conf->kernel.params[n];
+ if (!isnan(conf->window.params[n]))
+ scaler->kernel->w.params[n] = conf->window.params[n];
}
- scaler->antiring = p->opts.scaler_antiring[scaler->index];
+ if (conf->kernel.blur > 0.0)
+ scaler->kernel->f.blur = conf->kernel.blur;
+ if (conf->window.blur > 0.0)
+ scaler->kernel->w.blur = conf->window.blur;
- if (scaler->kernel->radius < 0)
- scaler->kernel->radius = p->opts.scaler_radius[scaler->index];
+ if (scaler->kernel->f.resizable && conf->radius > 0.0)
+ scaler->kernel->f.radius = conf->radius;
- update_scale_factor(p, scaler);
+ scaler->insufficient = !mp_init_filter(scaler->kernel, sizes, scale_factor);
+
+ if (scaler->kernel->polar) {
+ scaler->gl_target = GL_TEXTURE_1D;
+ } else {
+ scaler->gl_target = GL_TEXTURE_2D;
+ }
int size = scaler->kernel->size;
int elems_per_pixel = 4;
@@ -1235,15 +978,7 @@ static void init_scaler(struct gl_video *p, struct scaler *scaler)
int width = size / elems_per_pixel;
assert(size == width * elems_per_pixel);
const struct fmt_entry *fmt = &gl_float16_formats[elems_per_pixel - 1];
- int target;
-
- if (scaler->kernel->polar) {
- target = GL_TEXTURE_1D;
- scaler->lut_name = scaler->index == 0 ? "lut_1d_l" : "lut_1d_c";
- } else {
- target = GL_TEXTURE_2D;
- scaler->lut_name = scaler->index == 0 ? "lut_2d_l" : "lut_2d_c";
- }
+ GLenum target = scaler->gl_target;
gl->ActiveTexture(GL_TEXTURE0 + TEXUNIT_SCALERS + scaler->index);
@@ -1276,559 +1011,1024 @@ static void init_scaler(struct gl_video *p, struct scaler *scaler)
debug_check_gl(p, "after initializing scaler");
}
-static void init_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;
- if (p->opts.dither_depth > 0)
- dst_depth = p->opts.dither_depth;
-
- if (p->opts.dither_depth < 0 || p->opts.dither_algo < 0)
- return;
-
- MP_VERBOSE(p, "Dither to %d.\n", dst_depth);
-
- int tex_size;
- void *tex_data;
- GLint tex_iformat;
- GLint tex_format;
- GLenum tex_type;
- unsigned char temp[256];
-
- if (p->opts.dither_algo == 0) {
- int sizeb = p->opts.dither_size;
- int size = 1 << sizeb;
-
- if (p->last_dither_matrix_size != size) {
- p->last_dither_matrix = talloc_realloc(p, p->last_dither_matrix,
- float, size * size);
- mp_make_fruit_dither_matrix(p->last_dither_matrix, sizeb);
- p->last_dither_matrix_size = size;
- }
-
- tex_size = size;
- tex_iformat = gl_float16_formats[0].internal_format;
- tex_format = gl_float16_formats[0].format;
- tex_type = GL_FLOAT;
- tex_data = p->last_dither_matrix;
- } else {
- assert(sizeof(temp) >= 8 * 8);
- mp_make_ordered_dither_matrix(temp, 8);
-
- const struct fmt_entry *fmt = find_tex_format(gl, 1, 1);
- tex_size = 8;
- tex_iformat = fmt->internal_format;
- tex_format = fmt->format;
- tex_type = fmt->type;
- tex_data = temp;
- }
-
- // This defines how many bits are considered significant for output on
- // screen. The superfluous bits will be used for rounding according to the
- // dither matrix. The precision of the source implicitly decides how many
- // dither patterns can be visible.
- p->dither_quantization = (1 << dst_depth) - 1;
- p->dither_center = 0.5 / (tex_size * tex_size);
- p->dither_size = tex_size;
-
- gl->ActiveTexture(GL_TEXTURE0 + TEXUNIT_DITHER);
- gl->GenTextures(1, &p->dither_texture);
- gl->BindTexture(GL_TEXTURE_2D, p->dither_texture);
- gl->PixelStorei(GL_UNPACK_ALIGNMENT, 1);
- gl->TexImage2D(GL_TEXTURE_2D, 0, tex_iformat, tex_size, tex_size, 0,
- tex_format, tex_type, tex_data);
- gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
- gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
- gl->PixelStorei(GL_UNPACK_ALIGNMENT, 4);
- gl->ActiveTexture(GL_TEXTURE0);
-
- debug_check_gl(p, "dither setup");
-}
-
-static void recreate_osd(struct gl_video *p)
+// Set up shared/commonly used variables
+static void sampler_prelude(struct gl_video *p, int tex_num)
{
- if (p->osd)
- mpgl_osd_destroy(p->osd);
- p->osd = mpgl_osd_init(p->gl, p->log, p->osd_state, p->osd_programs);
- p->osd->use_pbo = p->opts.pbo;
+ 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");
}
-static bool does_resize(struct mp_rect src, struct mp_rect dst)
+static void pass_sample_separated_get_weights(struct gl_video *p,
+ struct scaler *scaler)
{
- return src.x1 - src.x0 != dst.x1 - dst.x0 ||
- src.y1 - src.y0 != dst.y1 - dst.y0;
-}
+ gl_sc_uniform_sampler(p->sc, "lut", scaler->gl_target,
+ TEXUNIT_SCALERS + scaler->index);
-static const char *expected_scaler(struct gl_video *p, int unit)
-{
- if (p->opts.scaler_resizes_only && unit == 0 &&
- !does_resize(p->src_rect, p->dst_rect))
- {
- return "bilinear";
+ int N = scaler->kernel->size;
+ if (N == 2) {
+ GLSL(vec2 c1 = texture(lut, vec2(0.5, fcoord)).RG;)
+ GLSL(float weights[2] = float[](c1.r, c1.g);)
+ } else if (N == 6) {
+ GLSL(vec4 c1 = texture(lut, vec2(0.25, fcoord));)
+ GLSL(vec4 c2 = texture(lut, vec2(0.75, fcoord));)
+ GLSL(float weights[6] = float[](c1.r, c1.g, c1.b, c2.r, c2.g, c2.b);)
+ } else {
+ GLSLF("float weights[%d];\n", N);
+ for (int n = 0; n < N / 4; n++) {
+ GLSLF("c = texture(lut, vec2(1.0 / %d + %d / float(%d), fcoord));\n",
+ N / 2, n, N / 4);
+ GLSLF("weights[%d] = c.r;\n", n * 4 + 0);
+ GLSLF("weights[%d] = c.g;\n", n * 4 + 1);
+ GLSLF("weights[%d] = c.b;\n", n * 4 + 2);
+ GLSLF("weights[%d] = c.a;\n", n * 4 + 3);
+ }
}
- if (unit == 0 && p->opts.dscaler && get_scale_factor(p) < 1.0)
- return p->opts.dscaler;
- return p->opts.scalers[unit];
}
-static void update_settings(struct gl_video *p)
-{
- struct mp_csp_params params;
- mp_csp_copy_equalizer_values(&params, &p->video_eq);
-
- p->user_gamma = params.gamma * p->opts.gamma;
-
- // Lazy gamma shader initialization (a microoptimization)
- if (p->user_gamma != 1.0f && !p->user_gamma_enabled) {
- p->user_gamma_enabled = true;
- p->need_reinit_rendering = true;
+// Handle a single pass (either vertical or horizontal). The direction is given
+// by the vector (d_x, d_y). If the vector is 0, then planar interpolation is
+// used instead (samples from texture0 through textureN)
+static void pass_sample_separated_gen(struct gl_video *p, struct scaler *scaler,
+ int d_x, int d_y)
+{
+ 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);)
+ GLSLF("{\n");
+ if (!planar) {
+ GLSLF("vec2 dir = vec2(%d, %d);\n", d_x, d_y);
+ GLSL(pt *= dir;)
+ GLSL(float fcoord = dot(fract(pos * size - vec2(0.5)), dir);)
+ GLSLF("vec2 base = pos - fcoord * pt - pt * vec2(%d);\n", N / 2 - 1);
+ }
+ GLSL(vec4 c;)
+ if (use_ar) {
+ GLSL(vec4 hi = vec4(0.0);)
+ GLSL(vec4 lo = vec4(1.0);)
+ }
+ pass_sample_separated_get_weights(p, scaler);
+ GLSLF("// scaler samples\n");
+ for (int n = 0; n < N; n++) {
+ if (planar) {
+ GLSLF("c = texture(texture%d, texcoord%d);\n", n, n);
+ } else {
+ GLSLF("c = texture(tex, base + pt * vec2(%d));\n", n);
+ }
+ GLSLF("color += vec4(weights[%d]) * c;\n", n);
+ if (use_ar && (n == N/2-1 || n == N/2)) {
+ GLSL(lo = min(lo, c);)
+ GLSL(hi = max(hi, c);)
+ }
+ }
+ if (use_ar)
+ GLSLF("color = mix(color, clamp(color, lo, hi), %f);\n",
+ scaler->conf.antiring);
+ GLSLF("}\n");
+}
+
+static void pass_sample_separated(struct gl_video *p, int src_tex,
+ struct scaler *scaler, int w, int h,
+ struct gl_transform transform)
+{
+ // Keep the x components untouched for the first pass
+ struct mp_rect_f src_new = p->pass_tex[src_tex].src;
+ gl_transform_rect(transform, &src_new);
+ GLSLF("// pass 1\n");
+ p->pass_tex[src_tex].src.y0 = src_new.y0;
+ p->pass_tex[src_tex].src.y1 = src_new.y1;
+ pass_sample_separated_gen(p, scaler, 0, 1);
+ int src_w = p->pass_tex[src_tex].src.x1 - p->pass_tex[src_tex].src.x0;
+ finish_pass_fbo(p, &scaler->sep_fbo, src_w, h, src_tex, FBOTEX_FUZZY_H);
+ // Restore the sample source for the second pass
+ sampler_prelude(p, src_tex);
+ GLSLF("// pass 2\n");
+ p->pass_tex[src_tex].src.x0 = src_new.x0;
+ p->pass_tex[src_tex].src.x1 = src_new.x1;
+ pass_sample_separated_gen(p, scaler, 1, 0);
+}
+
+static void pass_sample_polar(struct gl_video *p, 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);)
+ GLSLF("{\n");
+ GLSL(vec2 fcoord = fract(pos * size - vec2(0.5));)
+ GLSL(vec2 base = pos - fcoord * pt;)
+ GLSL(vec4 c;)
+ GLSLF("float w, d, wsum = 0.0;\n");
+ if (use_ar) {
+ GLSL(vec4 lo = vec4(1.0);)
+ GLSL(vec4 hi = vec4(0.0);)
+ }
+ gl_sc_uniform_sampler(p->sc, "lut", scaler->gl_target,
+ TEXUNIT_SCALERS + scaler->index);
+ GLSLF("// scaler samples\n");
+ for (int y = 1-bound; y <= bound; y++) {
+ for (int x = 1-bound; x <= bound; x++) {
+ // Since we can't know the subpixel position in advance, assume a
+ // worst case scenario
+ int yy = y > 0 ? y-1 : y;
+ int xx = x > 0 ? x-1 : x;
+ double dmax = sqrt(xx*xx + yy*yy);
+ // Skip samples definitely outside the radius
+ if (dmax >= radius)
+ continue;
+ GLSLF("d = length(vec2(%d, %d) - fcoord)/%f;\n", x, y, radius);
+ // Check for samples that might be skippable
+ if (dmax >= radius - 1)
+ GLSLF("if (d < 1.0) {\n");
+ GLSL(w = texture1D(lut, d).r;)
+ GLSL(wsum += w;)
+ GLSLF("c = texture(tex, base + pt * vec2(%d, %d));\n", x, y);
+ GLSL(color += vec4(w) * c;)
+ if (use_ar && x >= 0 && y >= 0 && x <= 1 && y <= 1) {
+ GLSL(lo = min(lo, c);)
+ GLSL(hi = max(hi, c);)
+ }
+ if (dmax >= radius -1)
+ GLSLF("}\n");
+ }
}
+ GLSL(color = color / vec4(wsum);)
+ if (use_ar)
+ GLSLF("color = mix(color, clamp(color, lo, hi), %f);\n",
+ scaler->conf.antiring);
+ GLSLF("}\n");
+}
+
+static void bicubic_calcweights(struct gl_video *p, const char *t, const char *s)
+{
+ // Explanation of how bicubic scaling with only 4 texel fetches is done:
+ // http://www.mate.tue.nl/mate/pdfs/10318.pdf
+ // 'Efficient GPU-Based Texture Interpolation using Uniform B-Splines'
+ // Explanation why this algorithm normally always blurs, even with unit
+ // scaling:
+ // http://bigwww.epfl.ch/preprints/ruijters1001p.pdf
+ // 'GPU Prefilter for Accurate Cubic B-spline Interpolation'
+ GLSLF("vec4 %s = vec4(-0.5, 0.1666, 0.3333, -0.3333) * %s"
+ " + vec4(1, 0, -0.5, 0.5);\n", t, s);
+ GLSLF("%s = %s * %s + vec4(0, 0, -0.5, 0.5);\n", t, t, s);
+ GLSLF("%s = %s * %s + vec4(-0.6666, 0, 0.8333, 0.1666);\n", t, t, s);
+ GLSLF("%s.xy *= vec2(1, 1) / vec2(%s.z, %s.w);\n", t, t, t);
+ GLSLF("%s.xy += vec2(1 + %s, 1 - %s);\n", t, s, s);
+}
+
+static void pass_sample_bicubic_fast(struct gl_video *p)
+{
+ GLSL(vec4 color;)
+ GLSLF("{\n");
+ GLSL(vec2 fcoord = fract(pos * size + vec2(0.5, 0.5));)
+ bicubic_calcweights(p, "parmx", "fcoord.x");
+ bicubic_calcweights(p, "parmy", "fcoord.y");
+ GLSL(vec4 cdelta;)
+ GLSL(cdelta.xz = parmx.RG * vec2(-pt.x, pt.x);)
+ GLSL(cdelta.yw = parmy.RG * vec2(-pt.y, pt.y);)
+ // first y-interpolation
+ GLSL(vec4 ar = texture(tex, pos + cdelta.xy);)
+ GLSL(vec4 ag = texture(tex, pos + cdelta.xw);)
+ GLSL(vec4 ab = mix(ag, ar, parmy.b);)
+ // second y-interpolation
+ GLSL(vec4 br = texture(tex, pos + cdelta.zy);)
+ GLSL(vec4 bg = texture(tex, pos + cdelta.zw);)
+ GLSL(vec4 aa = mix(bg, br, parmy.b);)
+ // x-interpolation
+ GLSL(color = mix(aa, ab, parmx.b);)
+ GLSLF("}\n");
+}
+
+static void pass_sample_sharpen3(struct gl_video *p, struct scaler *scaler)
+{
+ GLSL(vec4 color;)
+ GLSLF("{\n");
+ GLSL(vec2 st = pt * 0.5;)
+ GLSL(vec4 p = texture(tex, pos);)
+ GLSL(vec4 sum = texture(tex, pos + st * vec2(+1, +1))
+ + texture(tex, pos + st * vec2(+1, -1))
+ + texture(tex, pos + st * vec2(-1, +1))
+ + texture(tex, pos + st * vec2(-1, -1));)
+ float param = scaler->conf.kernel.params[0];
+ param = isnan(param) ? 0.5 : param;
+ GLSLF("color = p + (p - 0.25 * sum) * %f;\n", param);
+ GLSLF("}\n");
+}
+
+static void pass_sample_sharpen5(struct gl_video *p, struct scaler *scaler)
+{
+ GLSL(vec4 color;)
+ GLSLF("{\n");
+ GLSL(vec2 st1 = pt * 1.2;)
+ GLSL(vec4 p = texture(tex, pos);)
+ GLSL(vec4 sum1 = texture(tex, pos + st1 * vec2(+1, +1))
+ + texture(tex, pos + st1 * vec2(+1, -1))
+ + texture(tex, pos + st1 * vec2(-1, +1))
+ + texture(tex, pos + st1 * vec2(-1, -1));)
+ GLSL(vec2 st2 = pt * 1.5;)
+ GLSL(vec4 sum2 = texture(tex, pos + st2 * vec2(+1, 0))
+ + texture(tex, pos + st2 * vec2( 0, +1))
+ + texture(tex, pos + st2 * vec2(-1, 0))
+ + texture(tex, pos + st2 * vec2( 0, -1));)
+ GLSL(vec4 t = p * 0.859375 + sum2 * -0.1171875 + sum1 * -0.09765625;)
+ float param = scaler->conf.kernel.params[0];
+ param = isnan(param) ? 0.5 : param;
+ GLSLF("color = p + t * %f;\n", param);
+ GLSLF("}\n");
+}
+
+static void pass_sample_oversample(struct gl_video *p, 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));)
+ // We only need to sample from the four corner pixels since we're using
+ // nearest neighbour and can compute the exact transition point
+ GLSL(vec2 baseNW = pos - fcoord * pt;)
+ GLSL(vec2 baseNE = baseNW + vec2(pt.x, 0.0);)
+ GLSL(vec2 baseSW = baseNW + vec2(0.0, pt.y);)
+ GLSL(vec2 baseSE = baseNW + pt;)
+ // Determine the mixing coefficient vector
+ gl_sc_uniform_vec2(p->sc, "output_size", (float[2]){w, h});
+ GLSL(vec2 coeff = vec2((baseSE - pos) * output_size);)
+ GLSL(coeff = clamp(coeff, 0.0, 1.0);)
+ float threshold = scaler->conf.kernel.params[0];
+ if (threshold > 0) { // also rules out NAN
+ GLSLF("coeff = mix(coeff, vec2(0.0), "
+ "lessThanEqual(coeff, vec2(%f)));\n", threshold);
+ GLSLF("coeff = mix(coeff, vec2(1.0), "
+ "greaterThanEqual(coeff, vec2(%f)));\n", threshold);
+ }
+ // Compute the right blend of colors
+ GLSL(vec4 left = mix(texture(tex, baseSW),
+ texture(tex, baseNW),
+ coeff.y);)
+ GLSL(vec4 right = mix(texture(tex, baseSE),
+ texture(tex, baseNE),
+ coeff.y);)
+ GLSL(color = mix(right, left, coeff.x);)
+ GLSLF("}\n");
+}
+
+// Sample. This samples from the texture ID given by src_tex. It's hardcoded to
+// use all variables and values associated with it (which includes textureN,
+// texcoordN and texture_sizeN).
+// 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.
+// 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,
+ const struct scaler_config *conf, double scale_factor,
+ int w, int h, struct gl_transform transform)
+{
+ reinit_scaler(p, scaler, conf, scale_factor, filter_sizes);
+ sampler_prelude(p, src_tex);
+
+ // Set up the transformation for everything other than separated scaling
+ if (!scaler->kernel || scaler->kernel->polar)
+ gl_transform_rect(transform, &p->pass_tex[src_tex].src);
+
+ // 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);)
+ } else if (strcmp(name, "bicubic_fast") == 0) {
+ pass_sample_bicubic_fast(p);
+ } else if (strcmp(name, "sharpen3") == 0) {
+ pass_sample_sharpen3(p, scaler);
+ } else if (strcmp(name, "sharpen5") == 0) {
+ pass_sample_sharpen5(p, scaler);
+ } else if (strcmp(name, "oversample") == 0) {
+ pass_sample_oversample(p, scaler, w, h);
+ } else if (scaler->kernel && scaler->kernel->polar) {
+ pass_sample_polar(p, scaler);
+ } else if (scaler->kernel) {
+ pass_sample_separated(p, src_tex, scaler, w, h, transform);
+ } else {
+ // Should never happen
+ abort();
+ }
+
+ // Micro-optimization: Avoid scaling unneeded channels
+ if (!p->has_alpha || p->opts.alpha_mode != 1)
+ GLSL(color.a = 1.0;)
}
-static void reinit_rendering(struct gl_video *p)
+// sample from video textures, set "color" variable to yuv value
+static void pass_read_video(struct gl_video *p)
{
- GL *gl = p->gl;
+ struct gl_transform chromafix;
+ pass_set_image_textures(p, &p->image, &chromafix);
- MP_VERBOSE(p, "Reinit rendering.\n");
-
- debug_check_gl(p, "before scaler initialization");
-
- uninit_rendering(p);
-
- if (!p->image_params.imgfmt)
+ if (p->plane_count == 1) {
+ GLSL(vec4 color = texture(texture0, texcoord0);)
return;
-
- update_settings(p);
-
- for (int n = 0; n < 2; n++)
- p->scalers[n].name = expected_scaler(p, n);
-
- init_dither(p);
-
- init_scaler(p, &p->scalers[0]);
- init_scaler(p, &p->scalers[1]);
-
- compile_shaders(p);
- update_all_uniforms(p);
-
- int w = p->image_w;
- int h = p->image_h;
-
- // Convolution filters don't need linear sampling, so using nearest is
- // often faster.
- GLenum filter = p->scalers[0].kernel ? GL_NEAREST : GL_LINEAR;
-
- if (p->indirect_program) {
- fbotex_init(&p->indirect_fbo, gl, p->log, w, h, p->gl_target, filter,
- p->opts.fbo_format);
}
- recreate_osd(p);
+ const struct scaler_config *cscale = &p->opts.scaler[2];
+ if (p->image_desc.flags & MP_IMGFLAG_SUBSAMPLED &&
+ strcmp(cscale->kernel.name, "bilinear") != 0) {
+ struct src_tex luma = p->pass_tex[0];
+ if (p->plane_count > 2) {
+ // For simplicity and performance, we merge the chroma planes
+ // into a single texture before scaling, so the scaler doesn't
+ // need to run multiple times.
+ GLSLF("// chroma merging\n");
+ GLSL(vec4 color = vec4(texture(texture1, texcoord1).r,
+ texture(texture2, texcoord2).r,
+ 0.0, 1.0);)
+ int c_w = p->pass_tex[1].src.x1 - p->pass_tex[1].src.x0;
+ int c_h = p->pass_tex[1].src.y1 - p->pass_tex[1].src.y0;
+ assert(c_w == p->pass_tex[2].src.x1 - p->pass_tex[2].src.x0);
+ 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);
+ }
+ GLSLF("// chroma scaling\n");
+ pass_sample(p, 1, &p->scaler[2], cscale, 1.0, p->image_w, p->image_h,
+ chromafix);
+ GLSL(vec2 chroma = color.rg;)
+ // Always force rendering to a FBO before main scaling, or we would
+ // scale chroma incorrectly.
+ p->use_indirect = true;
+ p->pass_tex[0] = luma; // Restore luma after scaling
+ } else {
+ GLSL(vec4 color;)
+ if (p->plane_count == 2) {
+ gl_transform_rect(chromafix, &p->pass_tex[1].src);
+ GLSL(vec2 chroma = texture(texture1, texcoord1).rg;) // NV formats
+ } else {
+ gl_transform_rect(chromafix, &p->pass_tex[1].src);
+ gl_transform_rect(chromafix, &p->pass_tex[2].src);
+ GLSL(vec2 chroma = vec2(texture(texture1, texcoord1).r,
+ texture(texture2, texcoord2).r);)
+ }
+ }
- p->need_reinit_rendering = false;
+ GLSL(color = vec4(texture(texture0, texcoord0).r, chroma, 1.0);)
+ if (p->has_alpha && p->plane_count >= 4)
+ GLSL(color.a = texture(texture3, texcoord3).r;)
}
-static void uninit_rendering(struct gl_video *p)
+// yuv conversion, and any other conversions before main up/down-scaling
+static void pass_convert_yuv(struct gl_video *p)
{
- GL *gl = p->gl;
+ struct gl_shader_cache *sc = p->sc;
- delete_shaders(p);
+ struct mp_csp_params cparams = MP_CSP_PARAMS_DEFAULTS;
+ cparams.gray = p->is_yuv && !p->is_packed_yuv && p->plane_count == 1;
+ cparams.input_bits = p->image_desc.component_bits;
+ cparams.texture_bits = (cparams.input_bits + 7) & ~7;
+ mp_csp_set_image_params(&cparams, &p->image_params);
+ mp_csp_copy_equalizer_values(&cparams, &p->video_eq);
+ p->user_gamma = 1.0 / (cparams.gamma * p->opts.gamma);
- for (int n = 0; n < 2; n++) {
- gl->DeleteTextures(1, &p->scalers[n].gl_lut);
- p->scalers[n].gl_lut = 0;
- p->scalers[n].lut_name = NULL;
- p->scalers[n].kernel = NULL;
- }
+ GLSLF("// color conversion\n");
- gl->DeleteTextures(1, &p->dither_texture);
- p->dither_texture = 0;
+ if (p->color_swizzle[0])
+ GLSLF("color = color.%s;\n", p->color_swizzle);
- fbotex_uninit(&p->indirect_fbo);
+ // Pre-colormatrix input gamma correction
+ if (p->image_desc.flags & MP_IMGFLAG_XYZ) {
+ cparams.colorspace = MP_CSP_XYZ;
+ cparams.input_bits = 8;
+ cparams.texture_bits = 8;
- for (int i = 0; i < FBOSURFACES_MAX; i++) {
- fbotex_uninit(&p->surfaces[i].fbotex);
- p->surfaces[i].valid = false;
+ // Pre-colormatrix input gamma correction. Note that this results in
+ // linear light
+ GLSL(color.rgb = pow(color.rgb, vec3(2.6));)
}
- fbotex_uninit(&p->scale_sep_fbo);
+ // Conversion from Y'CbCr or other linear spaces to RGB
+ if (!p->is_rgb) {
+ struct mp_cmat m = {{{0}}};
+ if (p->image_desc.flags & MP_IMGFLAG_XYZ) {
+ struct mp_csp_primaries csp = mp_get_csp_primaries(p->image_params.primaries);
+ mp_get_xyz2rgb_coeffs(&cparams, csp, MP_INTENT_RELATIVE_COLORIMETRIC, &m);
+ } else {
+ mp_get_yuv2rgb_coeffs(&cparams, &m);
+ }
+ gl_sc_uniform_mat3(sc, "colormatrix", true, &m.m[0][0]);
+ gl_sc_uniform_vec3(sc, "colormatrix_c", m.c);
+
+ GLSL(color.rgb = mat3(colormatrix) * color.rgb + colormatrix_c;)
+ }
+
+ if (p->image_params.colorspace == MP_CSP_BT_2020_C) {
+ p->use_indirect = true;
+ // Conversion for C'rcY'cC'bc via the BT.2020 CL system:
+ // C'bc = (B'-Y'c) / 1.9404 | C'bc <= 0
+ // = (B'-Y'c) / 1.5816 | C'bc > 0
+ //
+ // C'rc = (R'-Y'c) / 1.7184 | C'rc <= 0
+ // = (R'-Y'c) / 0.9936 | C'rc > 0
+ //
+ // as per the BT.2020 specification, table 4. This is a non-linear
+ // transformation because (constant) luminance receives non-equal
+ // contributions from the three different channels.
+ GLSLF("// constant luminance conversion\n");
+ GLSL(color.br = color.br * mix(vec2(1.5816, 0.9936),
+ vec2(1.9404, 1.7184),
+ lessThanEqual(color.br, vec2(0)))
+ + color.gg;)
+ // Expand channels to camera-linear light. This shader currently just
+ // assumes everything uses the BT.2020 12-bit gamma function, since the
+ // difference between 10 and 12-bit is negligible for anything other
+ // than 12-bit content.
+ GLSL(color.rgb = mix(color.rgb / vec3(4.5),
+ pow((color.rgb + vec3(0.0993))/vec3(1.0993), vec3(1.0/0.45)),
+ lessThanEqual(vec3(0.08145), color.rgb));)
+ // Calculate the green channel from the expanded RYcB
+ // The BT.2020 specification says Yc = 0.2627*R + 0.6780*G + 0.0593*B
+ GLSL(color.g = (color.g - 0.2627*color.r - 0.0593*color.b)/0.6780;)
+ // Recompress to receive the R'G'B' result, same as other systems
+ GLSL(color.rgb = mix(color.rgb * vec3(4.5),
+ vec3(1.0993) * pow(color.rgb, vec3(0.45)) - vec3(0.0993),
+ lessThanEqual(vec3(0.0181), color.rgb));)
+ }
+
+ if (!p->has_alpha || p->opts.alpha_mode == 0) { // none
+ GLSL(color.a = 1.0;)
+ } else if (p->opts.alpha_mode == 2) { // blend
+ GLSL(color = vec4(color.rgb * color.a, 1.0);)
+ }
}
-void gl_video_set_lut3d(struct gl_video *p, struct lut3d *lut3d)
+static void get_scale_factors(struct gl_video *p, double xy[2])
{
- GL *gl = p->gl;
+ xy[0] = (p->dst_rect.x1 - p->dst_rect.x0) /
+ (double)(p->src_rect.x1 - p->src_rect.x0);
+ xy[1] = (p->dst_rect.y1 - p->dst_rect.y0) /
+ (double)(p->src_rect.y1 - p->src_rect.y0);
+}
- if (!lut3d) {
- if (p->use_lut_3d) {
- p->use_lut_3d = false;
- reinit_rendering(p);
- }
+// Linearize (expand), given a TRC as input
+static void pass_linearize(struct gl_video *p, enum mp_csp_trc trc)
+{
+ if (trc == MP_CSP_TRC_LINEAR)
return;
- }
- if (!(gl->mpgl_caps & MPGL_CAP_3D_TEX))
+ GLSL(color.rgb = clamp(color.rgb, 0.0, 1.0);)
+ switch (trc) {
+ case MP_CSP_TRC_SRGB:
+ GLSL(color.rgb = mix(color.rgb / vec3(12.92),
+ pow((color.rgb + vec3(0.055))/vec3(1.055),
+ vec3(2.4)),
+ lessThan(vec3(0.04045), color.rgb));)
+ break;
+ case MP_CSP_TRC_BT_1886:
+ GLSL(color.rgb = pow(color.rgb, vec3(1.961));)
+ break;
+ case MP_CSP_TRC_GAMMA18:
+ GLSL(color.rgb = pow(color.rgb, vec3(1.8));)
+ break;
+ case MP_CSP_TRC_GAMMA22:
+ GLSL(color.rgb = pow(color.rgb, vec3(2.2));)
+ break;
+ case MP_CSP_TRC_GAMMA28:
+ GLSL(color.rgb = pow(color.rgb, vec3(2.8));)
+ break;
+ case MP_CSP_TRC_PRO_PHOTO:
+ GLSL(color.rgb = mix(color.rgb / vec3(16.0),
+ pow(color.rgb, vec3(1.8)),
+ lessThan(vec3(0.03125), color.rgb));)
+ break;
+ }
+}
+
+// Delinearize (compress), given a TRC as output
+static void pass_delinearize(struct gl_video *p, enum mp_csp_trc trc)
+{
+ if (trc == MP_CSP_TRC_LINEAR)
return;
- if (!p->lut_3d_texture)
- gl->GenTextures(1, &p->lut_3d_texture);
-
- gl->ActiveTexture(GL_TEXTURE0 + TEXUNIT_3DLUT);
- gl->BindTexture(GL_TEXTURE_3D, p->lut_3d_texture);
- gl->TexImage3D(GL_TEXTURE_3D, 0, GL_RGB16, lut3d->size[0], lut3d->size[1],
- lut3d->size[2], 0, GL_RGB, GL_UNSIGNED_SHORT, lut3d->data);
- gl->TexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- gl->TexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- gl->TexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- gl->TexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- gl->TexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
- gl->ActiveTexture(GL_TEXTURE0);
-
- p->use_lut_3d = true;
- check_gl_features(p);
-
- debug_check_gl(p, "after 3d lut creation");
+ GLSL(color.rgb = clamp(color.rgb, 0.0, 1.0);)
+ switch (trc) {
+ case MP_CSP_TRC_SRGB:
+ GLSL(color.rgb = mix(color.rgb * vec3(12.92),
+ vec3(1.055) * pow(color.rgb, vec3(1.0/2.4))
+ - vec3(0.055),
+ lessThanEqual(vec3(0.0031308), color.rgb));)
+ break;
+ case MP_CSP_TRC_BT_1886:
+ GLSL(color.rgb = pow(color.rgb, vec3(1.0/1.961));)
+ break;
+ case MP_CSP_TRC_GAMMA18:
+ GLSL(color.rgb = pow(color.rgb, vec3(1.0/1.8));)
+ break;
+ case MP_CSP_TRC_GAMMA22:
+ GLSL(color.rgb = pow(color.rgb, vec3(1.0/2.2));)
+ break;
+ case MP_CSP_TRC_GAMMA28:
+ GLSL(color.rgb = pow(color.rgb, vec3(1.0/2.8));)
+ break;
+ case MP_CSP_TRC_PRO_PHOTO:
+ GLSL(color.rgb = mix(color.rgb * vec3(16.0),
+ pow(color.rgb, vec3(1.0/1.8)),
+ lessThanEqual(vec3(0.001953), color.rgb));)
+ break;
+ }
+}
+
+// Takes care of the main scaling and pre/post-conversions
+static void pass_scale_main(struct gl_video *p)
+{
+ // Figure out the main scaler.
+ double xy[2];
+ get_scale_factors(p, xy);
+ bool downscaling = xy[0] < 1.0 || xy[1] < 1.0;
+ bool upscaling = !downscaling && (xy[0] > 1.0 || xy[1] > 1.0);
+ double scale_factor = 1.0;
- reinit_rendering(p);
-}
+ struct scaler *scaler = &p->scaler[0];
+ struct scaler_config scaler_conf = p->opts.scaler[0];
+ if (p->opts.scaler_resizes_only && !downscaling && !upscaling)
+ scaler_conf.kernel.name = "bilinear";
+ if (downscaling && p->opts.scaler[1].kernel.name) {
+ scaler_conf = p->opts.scaler[1];
+ scaler = &p->scaler[1];
+ }
-static void set_image_textures(struct gl_video *p, struct video_image *vimg,
- GLuint imgtex[4])
-{
- GL *gl = p->gl;
- GLuint dummy[4] = {0};
- if (!imgtex)
- imgtex = dummy;
+ double f = MPMIN(xy[0], xy[1]);
+ if (p->opts.fancy_downscaling && f < 1.0 &&
+ fabs(xy[0] - f) < 0.01 && fabs(xy[1] - f) < 0.01)
+ {
+ scale_factor = FFMAX(1.0, 1.0 / f);
+ }
+
+ // Pre-conversion, like linear light/sigmoidization
+ GLSLF("// scaler pre-conversion\n");
+ if (p->use_linear) {
+ p->use_indirect = true;
+ pass_linearize(p, p->image_params.gamma);
+ }
+
+ bool use_sigmoid = p->use_linear && p->opts.sigmoid_upscaling && upscaling;
+ float sig_center, sig_slope, sig_offset, sig_scale;
+ if (use_sigmoid) {
+ p->use_indirect = true;
+ // Coefficients for the sigmoidal transform are taken from the
+ // formula here: http://www.imagemagick.org/Usage/color_mods/#sigmoidal
+ sig_center = p->opts.sigmoid_center;
+ sig_slope = p->opts.sigmoid_slope;
+ // This function needs to go through (0,0) and (1,1) so we compute the
+ // values at 1 and 0, and then scale/shift them, respectively.
+ sig_offset = 1.0/(1+expf(sig_slope * sig_center));
+ sig_scale = 1.0/(1+expf(sig_slope * (sig_center-1))) - sig_offset;
+ GLSLF("color.rgb = %f - log(1.0/(color.rgb * %f + %f) - 1.0)/%f;\n",
+ sig_center, sig_scale, sig_offset, sig_slope);
+ }
+
+ // Compute the cropped and rotated transformation
+ float sx = (p->src_rect.x1 - p->src_rect.x0) / (float)p->image_w,
+ sy = (p->src_rect.y1 - p->src_rect.y0) / (float)p->image_h,
+ ox = p->src_rect.x0,
+ oy = p->src_rect.y0;
+ struct gl_transform transform = {{{sx,0.0}, {0.0,sy}}, {ox,oy}};
+
+ int xc = 0, yc = 1,
+ vp_w = p->dst_rect.x1 - p->dst_rect.x0,
+ vp_h = p->dst_rect.y1 - p->dst_rect.y0;
- assert(vimg->mpi);
+ if ((p->image_params.rotate % 180) == 90) {
+ MPSWAP(float, transform.m[0][xc], transform.m[0][yc]);
+ MPSWAP(float, transform.m[1][xc], transform.m[1][yc]);
+ MPSWAP(float, transform.t[0], transform.t[1]);
+ MPSWAP(int, xc, yc);
+ MPSWAP(int, vp_w, vp_h);
+ }
+
+ GLSLF("// main scaling\n");
+ if (!p->use_indirect && strcmp(scaler_conf.kernel.name, "bilinear") == 0) {
+ // implicitly scale in pass_video_to_screen, but set up the textures
+ // manually (for cropping etc.). Special care has to be taken for the
+ // chroma planes (everything except luma=tex0), to make sure the offset
+ // is scaled to the correct reference frame (in the case of subsampled
+ // input)
+ struct gl_transform tchroma = transform;
+ tchroma.t[xc] /= 1 << p->image_desc.chroma_xs;
+ tchroma.t[yc] /= 1 << p->image_desc.chroma_ys;
- if (p->hwdec_active) {
- p->hwdec->driver->map_image(p->hwdec, vimg->mpi, imgtex);
- } else {
for (int n = 0; n < p->plane_count; n++)
- imgtex[n] = vimg->planes[n].gl_texture;
+ gl_transform_rect(n > 0 ? tchroma : transform, &p->pass_tex[n].src);
+ } else {
+ finish_pass_fbo(p, &p->indirect_fbo, p->image_w, p->image_h, 0, 0);
+ pass_sample(p, 0, scaler, &scaler_conf, scale_factor, vp_w, vp_h,
+ transform);
}
- for (int n = 0; n < 4; n++) {
- gl->ActiveTexture(GL_TEXTURE0 + n);
- gl->BindTexture(p->gl_target, imgtex[n]);
+ GLSLF("// scaler post-conversion\n");
+ if (use_sigmoid) {
+ // Inverse of the transformation above
+ GLSLF("color.rgb = (1.0/(1.0 + exp(%f * (%f - color.rgb))) - %f) / %f;\n",
+ sig_slope, sig_center, sig_offset, sig_scale);
}
- gl->ActiveTexture(GL_TEXTURE0);
}
-static void unset_image_textures(struct gl_video *p)
+// Adapts the colors from the given color space to the display device's native
+// gamut.
+static void pass_colormanage(struct gl_video *p, enum mp_csp_prim prim_src,
+ enum mp_csp_trc trc_src)
{
- GL *gl = p->gl;
+ GLSLF("// color management\n");
+ enum mp_csp_trc trc_dst = p->opts.target_trc;
+ enum mp_csp_prim prim_dst = p->opts.target_prim;
- for (int n = 0; n < 4; n++) {
- gl->ActiveTexture(GL_TEXTURE0 + n);
- gl->BindTexture(p->gl_target, 0);
+ if (p->use_lut_3d) {
+ // The 3DLUT is hard-coded against BT.2020's gamut during creation, and
+ // we never want to adjust its output (so treat it as linear)
+ prim_dst = MP_CSP_PRIM_BT_2020;
+ trc_dst = MP_CSP_TRC_LINEAR;
}
- gl->ActiveTexture(GL_TEXTURE0);
-
- if (p->hwdec_active)
- p->hwdec->driver->unmap_image(p->hwdec);
-}
-
-static int align_pow2(int s)
-{
- int r = 1;
- while (r < s)
- r *= 2;
- return r;
-}
-
-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;
- 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");
- init_format(p->image_params.imgfmt, p);
- p->gl_target = p->hwdec->gl_texture_target;
+ if (prim_dst == MP_CSP_PRIM_AUTO)
+ prim_dst = prim_src;
+ if (trc_dst == MP_CSP_TRC_AUTO) {
+ trc_dst = trc_src;
+ // Avoid outputting linear light at all costs
+ if (trc_dst == MP_CSP_TRC_LINEAR)
+ trc_dst = p->image_params.gamma;
+ if (trc_dst == MP_CSP_TRC_LINEAR)
+ trc_dst = MP_CSP_TRC_GAMMA22;
}
- mp_image_params_guess_csp(&p->image_params);
-
- p->image_w = p->image_params.w;
- p->image_h = p->image_params.h;
-
- int eq_caps = MP_CSP_EQ_CAPS_GAMMA;
- if (p->is_yuv && 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;
- p->video_eq.capabilities = eq_caps;
-
- debug_check_gl(p, "before video texture creation");
-
- struct video_image *vimg = &p->image;
-
- for (int n = 0; n < p->plane_count; n++) {
- struct texplane *plane = &vimg->planes[n];
-
- plane->w = mp_chroma_div_up(p->image_w, p->image_desc.xs[n]);
- plane->h = mp_chroma_div_up(p->image_h, p->image_desc.ys[n]);
-
- plane->tex_w = plane->w;
- plane->tex_h = plane->h;
-
- if (!p->hwdec_active) {
- if (!p->opts.npot) {
- plane->tex_w = align_pow2(plane->tex_w);
- plane->tex_h = align_pow2(plane->tex_h);
- }
-
- gl->ActiveTexture(GL_TEXTURE0 + n);
- gl->GenTextures(1, &plane->gl_texture);
- gl->BindTexture(p->gl_target, plane->gl_texture);
-
- gl->TexImage2D(p->gl_target, 0, plane->gl_internal_format,
- plane->tex_w, plane->tex_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);
- 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);
- }
-
- MP_VERBOSE(p, "Texture for plane %d: %dx%d\n",
- n, plane->tex_w, plane->tex_h);
+ bool need_cms = prim_src != prim_dst || p->use_lut_3d;
+ bool need_gamma = trc_src != trc_dst || need_cms;
+ if (need_gamma)
+ pass_linearize(p, trc_src);
+ // Adapt to the right colorspace if necessary
+ if (prim_src != prim_dst) {
+ struct mp_csp_primaries csp_src = mp_get_csp_primaries(prim_src),
+ csp_dst = mp_get_csp_primaries(prim_dst);
+ float m[3][3] = {{0}};
+ mp_get_cms_matrix(csp_src, csp_dst, MP_INTENT_RELATIVE_COLORIMETRIC, m);
+ gl_sc_uniform_mat3(p->sc, "cms_matrix", true, &m[0][0]);
+ GLSL(color.rgb = cms_matrix * color.rgb;)
}
- gl->ActiveTexture(GL_TEXTURE0);
-
- // If the dimensions of the Y plane are not aligned on the luma.
- // Assume 4:2:0 with size (3,3). The last luma pixel is (2,2).
- // The last chroma pixel is (1,1), not (0,0). So for luma, the
- // coordinate range is [0,3), for chroma it is [0,2). This means the
- // texture coordinates for chroma are stretched by adding 1 luma pixel
- // to the range. Undo this.
- p->chroma_fix[0] = p->image.planes[0].tex_w / (double)p->image.planes[1].tex_w
- / (1 << p->image_desc.chroma_xs);
- p->chroma_fix[1] = p->image.planes[0].tex_h / (double)p->image.planes[1].tex_h
- / (1 << p->image_desc.chroma_ys);
-
- debug_check_gl(p, "after video texture creation");
-
- reinit_rendering(p);
-}
-
-static void uninit_video(struct gl_video *p)
-{
- GL *gl = p->gl;
-
- uninit_rendering(p);
-
- struct video_image *vimg = &p->image;
-
- for (int n = 0; n < 3; n++) {
- struct texplane *plane = &vimg->planes[n];
-
- gl->DeleteTextures(1, &plane->gl_texture);
- plane->gl_texture = 0;
- gl->DeleteBuffers(1, &plane->gl_buffer);
- plane->gl_buffer = 0;
- plane->buffer_ptr = NULL;
- plane->buffer_size = 0;
+ if (p->use_lut_3d) {
+ gl_sc_uniform_sampler(p->sc, "lut_3d", GL_TEXTURE_3D, TEXUNIT_3DLUT);
+ // For the 3DLUT we are arbitrarily using 2.4 as input gamma to reduce
+ // the severity of quantization errors.
+ GLSL(color.rgb = clamp(color.rgb, 0.0, 1.0);)
+ GLSL(color.rgb = pow(color.rgb, vec3(1.0/2.4));)
+ GLSL(color.rgb = texture3D(lut_3d, color.rgb).rgb;)
}
- mp_image_unrefp(&vimg->mpi);
-
- // Invalidate image_params to ensure that gl_video_config() will call
- // init_video() on uninitialized gl_video.
- p->real_image_params = (struct mp_image_params){0};
- p->image_params = p->real_image_params;
+ if (need_gamma)
+ pass_delinearize(p, trc_dst);
}
-static void change_dither_trafo(struct gl_video *p)
+static void pass_dither(struct gl_video *p)
{
GL *gl = p->gl;
- int program = p->final_program;
- int phase = p->frames_rendered % 8u;
- float r = phase * (M_PI / 2); // rotate
- float m = phase < 4 ? 1 : -1; // mirror
+ // Assume 8 bits per component if unknown.
+ int dst_depth = p->depth_g ? p->depth_g : 8;
+ if (p->opts.dither_depth > 0)
+ dst_depth = p->opts.dither_depth;
- gl->UseProgram(program);
+ if (p->opts.dither_depth < 0 || p->opts.dither_algo < 0)
+ return;
- float matrix[2][2] = {{cos(r), -sin(r) },
- {sin(r) * m, cos(r) * m}};
- gl->UniformMatrix2fv(gl->GetUniformLocation(program, "dither_trafo"),
- 1, GL_TRUE, &matrix[0][0]);
+ if (!p->dither_texture) {
+ MP_VERBOSE(p, "Dither to %d.\n", dst_depth);
+
+ int tex_size;
+ void *tex_data;
+ GLint tex_iformat;
+ GLint tex_format;
+ GLenum tex_type;
+ unsigned char temp[256];
+
+ if (p->opts.dither_algo == 0) {
+ int sizeb = p->opts.dither_size;
+ int size = 1 << sizeb;
+
+ if (p->last_dither_matrix_size != size) {
+ p->last_dither_matrix = talloc_realloc(p, p->last_dither_matrix,
+ float, size * size);
+ mp_make_fruit_dither_matrix(p->last_dither_matrix, sizeb);
+ p->last_dither_matrix_size = size;
+ }
- gl->UseProgram(0);
-}
+ tex_size = size;
+ tex_iformat = gl_float16_formats[0].internal_format;
+ tex_format = gl_float16_formats[0].format;
+ tex_type = GL_FLOAT;
+ tex_data = p->last_dither_matrix;
+ } else {
+ assert(sizeof(temp) >= 8 * 8);
+ mp_make_ordered_dither_matrix(temp, 8);
+
+ const struct fmt_entry *fmt = find_tex_format(gl, 1, 1);
+ tex_size = 8;
+ tex_iformat = fmt->internal_format;
+ tex_format = fmt->format;
+ tex_type = fmt->type;
+ tex_data = temp;
+ }
-struct pass {
- int num;
- // Not necessarily a FBO; we just abuse this struct because it's convenient.
- // It specifies the source texture/sub-rectangle for the next pass.
- struct fbotex f;
- // If true, render source (f) to dst, instead of the full dest. fbo viewport
- bool use_dst;
- struct mp_rect dst;
- int flags; // for write_quad
-};
+ p->dither_size = tex_size;
-// *chain contains the source, and is overwritten with a copy of the result
-// fbo is used as destination texture/render target.
-static void handle_pass(struct gl_video *p, struct pass *chain,
- struct fbotex *fbo, GLuint program)
-{
- GL *gl = p->gl;
+ gl->ActiveTexture(GL_TEXTURE0 + TEXUNIT_DITHER);
+ gl->GenTextures(1, &p->dither_texture);
+ gl->BindTexture(GL_TEXTURE_2D, p->dither_texture);
+ gl->PixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ gl->TexImage2D(GL_TEXTURE_2D, 0, tex_iformat, tex_size, tex_size, 0,
+ tex_format, tex_type, tex_data);
+ gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ gl->PixelStorei(GL_UNPACK_ALIGNMENT, 4);
+ gl->ActiveTexture(GL_TEXTURE0);
- if (!program)
- return;
+ debug_check_gl(p, "dither setup");
+ }
- gl->BindTexture(p->gl_target, chain->f.texture);
- gl->UseProgram(program);
+ GLSLF("// dithering\n");
- gl->Viewport(fbo->vp_x, fbo->vp_y, fbo->vp_w, fbo->vp_h);
- gl->BindFramebuffer(GL_FRAMEBUFFER, fbo->fbo);
+ // This defines how many bits are considered significant for output on
+ // screen. The superfluous bits will be used for rounding according to the
+ // dither matrix. The precision of the source implicitly decides how many
+ // dither patterns can be visible.
+ int dither_quantization = (1 << dst_depth) - 1;
- int tex_w = chain->f.tex_w;
- int tex_h = chain->f.tex_h;
- struct mp_rect src = {
- .x0 = chain->f.vp_x,
- .y0 = chain->f.vp_y,
- .x1 = chain->f.vp_x + chain->f.vp_w,
- .y1 = chain->f.vp_y + chain->f.vp_h,
- };
+ gl_sc_uniform_sampler(p->sc, "dither", GL_TEXTURE_2D, TEXUNIT_DITHER);
- struct mp_rect dst = {-1, -1, 1, 1};
- if (chain->use_dst)
- dst = chain->dst;
+ GLSLF("vec2 dither_pos = gl_FragCoord.xy / %d;\n", p->dither_size);
- MP_TRACE(p, "Pass %d: [%d,%d,%d,%d] -> [%d,%d,%d,%d][%d,%d@%dx%d/%dx%d] (%d)\n",
- chain->num, src.x0, src.y0, src.x1, src.y1,
- dst.x0, dst.y0, dst.x1, dst.y1,
- fbo->vp_x, fbo->vp_y, fbo->vp_w, fbo->vp_h,
- fbo->tex_w, fbo->tex_h, chain->flags);
+ if (p->opts.temporal_dither) {
+ int phase = p->frames_rendered % 8u;
+ float r = phase * (M_PI / 2); // rotate
+ float m = phase < 4 ? 1 : -1; // mirror
- draw_quad(p,
- dst.x0, dst.y0, dst.x1, dst.y1,
- src.x0, src.y0, src.x1, src.y1,
- tex_w, tex_h, chain->flags);
+ float matrix[2][2] = {{cos(r), -sin(r) },
+ {sin(r) * m, cos(r) * m}};
+ gl_sc_uniform_mat2(p->sc, "dither_trafo", true, &matrix[0][0]);
- *chain = (struct pass){
- .num = chain->num + 1,
- .f = *fbo,
- };
-}
+ GLSL(dither_pos = dither_trafo * dither_pos;)
+ }
-static size_t fbosurface_next(struct gl_video *p)
-{
- return (p->surface_idx + 1) % FBOSURFACES_MAX;
+ GLSL(float dither_value = texture(dither, dither_pos).r;)
+ GLSLF("color = floor(color * %d + dither_value + 0.5 / (%d * %d)) / %d;\n",
+ dither_quantization, p->dither_size, p->dither_size,
+ dither_quantization);
}
-// Handle all of the frame passes upto and including upscaling, assuming
-// upscaling is not part of the final pass
-static void gl_video_upscale_frame(struct gl_video *p, struct pass *chain, struct fbotex *inter_fbo)
+// Draws the OSD, in scene-referred colors.. If cms is true, subtitles are
+// instead adapted to the display's gamut.
+static void pass_draw_osd(struct gl_video *p, int draw_flags, double pts,
+ struct mp_osd_res rect, int vp_w, int vp_h, int fbo,
+ bool cms)
{
- // Order of processing: [indirect -> [scale_sep ->]] inter
- handle_pass(p, chain, &p->indirect_fbo, p->indirect_program);
+ mpgl_osd_generate(p->osd, rect, pts, p->image_params.stereo_out, draw_flags);
- // compensated for optional rotation
- struct mp_rect src_rect_rot = p->src_rect;
- if ((p->image_params.rotate % 180) == 90) {
- MPSWAP(int, src_rect_rot.x0, src_rect_rot.y0);
- MPSWAP(int, src_rect_rot.x1, src_rect_rot.y1);
- }
-
- // Clip to visible height so that separate scaling scales the visible part
- // only (and the target FBO texture can have a bounded size).
- // Don't clamp width; too hard to get correct final scaling on l/r borders.
- chain->f.vp_y = src_rect_rot.y0;
- chain->f.vp_h = src_rect_rot.y1 - src_rect_rot.y0;
-
- handle_pass(p, chain, &p->scale_sep_fbo, p->scale_sep_program);
-
- // For Y direction, use the whole source viewport; it has been fit to the
- // correct origin/height before.
- // For X direction, assume the texture wasn't scaled yet, so we can
- // select the correct portion, which will be scaled to screen.
- chain->f.vp_x = src_rect_rot.x0;
- chain->f.vp_w = src_rect_rot.x1 - src_rect_rot.x0;
-
- if (inter_fbo)
- handle_pass(p, chain, inter_fbo, p->inter_program);
-}
-
-static double gl_video_interpolate_frame(struct gl_video *p,
- struct pass *chain,
+ p->gl->BindFramebuffer(GL_FRAMEBUFFER, fbo);
+ for (int n = 0; n < MAX_OSD_PARTS; n++) {
+ enum sub_bitmap_format fmt = mpgl_osd_get_part_format(p->osd, n);
+ if (!fmt)
+ continue;
+ gl_sc_uniform_sampler(p->sc, "osdtex", GL_TEXTURE_2D, 0);
+ switch (fmt) {
+ case SUBBITMAP_RGBA: {
+ GLSLF("// OSD (RGBA)\n");
+ GLSL(vec4 color = texture(osdtex, texcoord).bgra;)
+ break;
+ }
+ case SUBBITMAP_LIBASS: {
+ GLSLF("// OSD (libass)\n");
+ GLSL(vec4 color =
+ vec4(ass_color.rgb, ass_color.a * texture(osdtex, texcoord).r);)
+ break;
+ }
+ default:
+ abort();
+ }
+ // Subtitle color management, they're assumed to be sRGB by default
+ if (cms)
+ pass_colormanage(p, MP_CSP_PRIM_BT_709, MP_CSP_TRC_SRGB);
+ 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, vp_w, vp_h, n);
+ }
+ gl_sc_set_vao(p->sc, &p->vao);
+}
+
+// The main rendering function, takes care of everything up to and including
+// upscaling
+static void pass_render_frame(struct gl_video *p)
+{
+ bool use_cms = p->use_lut_3d || p->opts.target_prim != MP_CSP_PRIM_AUTO
+ || p->opts.target_trc != MP_CSP_TRC_AUTO;
+ p->use_linear = p->opts.linear_scaling || p->opts.sigmoid_upscaling
+ || use_cms || p->image_params.gamma == MP_CSP_TRC_LINEAR;
+ p->use_indirect = false; // set to true as needed by pass_*
+ pass_read_video(p);
+ pass_convert_yuv(p);
+
+ // For subtitles
+ double vpts = p->image.mpi->pts;
+ if (vpts == MP_NOPTS_VALUE)
+ vpts = p->osd_pts;
+
+ if (p->osd && p->opts.blend_subs == 2) {
+ double scale[2];
+ get_scale_factors(p, scale);
+ struct mp_osd_res rect = {
+ .w = p->image_w, .h = p->image_h,
+ .display_par = scale[1] / scale[0], // counter compensate scaling
+ };
+ finish_pass_fbo(p, &p->blend_subs_fbo, p->image_w, p->image_h, 0, 0);
+ pass_draw_osd(p, OSD_DRAW_SUB_ONLY, vpts, rect, p->image_w, p->image_h,
+ p->blend_subs_fbo.fbo, false);
+ GLSL(vec4 color = texture(texture0, texcoord0);)
+ }
+
+ pass_scale_main(p);
+
+ if (p->osd && p->opts.blend_subs == 1) {
+ // Recreate the real video size from the src/dst rects
+ int vp_w = p->dst_rect.x1 - p->dst_rect.x0,
+ vp_h = p->dst_rect.y1 - p->dst_rect.y0;
+ struct mp_osd_res rect = {
+ .w = vp_w, .h = vp_h,
+ .ml = -p->src_rect.x0, .mr = p->src_rect.x1 - p->image_w,
+ .mt = -p->src_rect.y0, .mb = p->src_rect.y1 - p->image_h,
+ .display_par = 1.0,
+ };
+ // Adjust margins for scale
+ double scale[2];
+ get_scale_factors(p, scale);
+ rect.ml *= scale[0]; rect.mr *= scale[0];
+ rect.mt *= scale[1]; rect.mb *= scale[1];
+ // We should always blend subtitles in non-linear light
+ if (p->use_linear)
+ pass_delinearize(p, p->image_params.gamma);
+ finish_pass_fbo(p, &p->blend_subs_fbo, vp_w, vp_h, 0, FBOTEX_FUZZY);
+ pass_draw_osd(p, OSD_DRAW_SUB_ONLY, vpts, rect, vp_w, vp_h,
+ p->blend_subs_fbo.fbo, false);
+ GLSL(vec4 color = texture(texture0, texcoord0);)
+ if (p->use_linear)
+ pass_linearize(p, p->image_params.gamma);
+ }
+}
+
+static void pass_draw_to_screen(struct gl_video *p, int fbo)
+{
+ // Adjust the overall gamma before drawing to screen
+ if (p->user_gamma != 1) {
+ gl_sc_uniform_f(p->sc, "user_gamma", p->user_gamma);
+ GLSL(color.rgb = clamp(color.rgb, 0.0, 1.0);)
+ GLSL(color.rgb = pow(color.rgb, vec3(user_gamma));)
+ }
+ pass_colormanage(p, p->image_params.primaries,
+ p->use_linear ? MP_CSP_TRC_LINEAR : p->image_params.gamma);
+ pass_dither(p);
+ int flags = (p->image_params.rotate % 90 ? 0 : p->image_params.rotate / 90)
+ | (p->image.image_flipped ? 4 : 0);
+ finish_pass_direct(p, fbo, p->vp_w, p->vp_h, &p->dst_rect, flags);
+}
+
+// Draws an interpolate frame to fbo, based on the frame timing in t
+static void gl_video_interpolate_frame(struct gl_video *p, int fbo,
struct frame_timing *t)
{
- GL *gl = p->gl;
- double inter_coeff = 0.0;
- int64_t prev_pts = p->surfaces[fbosurface_next(p)].pts;
-
- // Make sure all surfaces are actually valid, and redraw them manually
- // if this is not the case
- for (int i = 0; i < FBOSURFACES_MAX; i++) {
- if (!p->surfaces[i].valid) {
- struct pass frame = { .f = chain->f };
- gl_video_upscale_frame(p, &frame, &p->surfaces[i].fbotex);
- p->surfaces[i].valid = true;
+ int vp_w = p->dst_rect.x1 - p->dst_rect.x0,
+ vp_h = p->dst_rect.y1 - p->dst_rect.y0;
+
+ // First of all, figure out if we have a frame availble at all, and draw
+ // it manually + reset the queue if not
+ if (!p->surfaces[p->surface_now].pts) {
+ pass_render_frame(p);
+ finish_pass_fbo(p, &p->surfaces[p->surface_now].fbotex,
+ vp_w, vp_h, 0, FBOTEX_FUZZY);
+ p->surfaces[p->surface_now].pts = t ? t->pts : 0;
+ p->surfaces[p->surface_now].vpts = p->image.mpi->pts;
+ p->surface_idx = p->surface_now;
+ }
+
+ // Figure out the queue size. For illustration, a filter radius of 2 would
+ // look like this: _ A [B] C D _
+ // A is surface_bse, B is surface_now, C is surface_nxt and D is
+ // surface_end.
+ struct scaler *tscale = &p->scaler[3];
+ reinit_scaler(p, tscale, &p->opts.scaler[3], 1, tscale_sizes);
+ bool oversample = strcmp(tscale->conf.kernel.name, "oversample") == 0;
+ int size;
+ if (oversample) {
+ size = 2;
+ } else {
+ assert(tscale->kernel && !tscale->kernel->polar);
+ size = ceil(tscale->kernel->size);
+ assert(size <= TEXUNIT_VIDEO_NUM);
+ }
+ int radius = size/2;
+
+ int surface_now = p->surface_now;
+ int surface_nxt = fbosurface_wrap(surface_now + 1);
+ int surface_bse = fbosurface_wrap(surface_now - (radius-1));
+ int surface_end = fbosurface_wrap(surface_now + radius);
+ assert(fbosurface_wrap(surface_bse + size-1) == surface_end);
+
+ // Render a new frame if it came in and there's room in the queue
+ int surface_dst = fbosurface_wrap(p->surface_idx+1);
+ if (t && surface_dst != surface_bse &&
+ p->surfaces[p->surface_idx].pts < t->pts) {
+ MP_STATS(p, "new-pts");
+ pass_render_frame(p);
+ finish_pass_fbo(p, &p->surfaces[surface_dst].fbotex,
+ vp_w, vp_h, 0, FBOTEX_FUZZY);
+ p->surfaces[surface_dst].pts = t->pts;
+ p->surfaces[surface_dst].vpts = p->image.mpi->pts;
+ p->surface_idx = surface_dst;
+ }
+
+ // Figure out whether the queue is "valid". A queue is invalid if the
+ // frames' PTS is not monotonically increasing. Anything else is invalid,
+ // so avoid blending incorrect data and just draw the latest frame as-is.
+ // Possible causes for failure of this condition include seeks, pausing,
+ // end of playback or start of playback.
+ bool valid = true;
+ for (int i = surface_bse, ii; valid && i != surface_end; i = ii) {
+ ii = fbosurface_wrap(i+1);
+ if (!p->surfaces[i].pts || !p->surfaces[ii].pts) {
+ valid = false;
+ } else if (p->surfaces[ii].pts < p->surfaces[i].pts) {
+ valid = false;
+ MP_DBG(p, "interpolation queue underrun\n");
}
}
- if (t && prev_pts < t->pts) {
- MP_STATS(p, "new-pts");
- gl_video_upscale_frame(p, chain, &p->surfaces[p->surface_idx].fbotex);
- p->surfaces[p->surface_idx].valid = true;
- p->surfaces[p->surface_idx].pts = t->pts;
- p->surface_idx = fbosurface_next(p);
- } else {
- // re-use the previously rendered surface as source
- chain->f = p->surfaces[fbosurface_next(p)].fbotex;
+ // Update OSD PTS to synchronize subtitles with the displayed frame
+ if (t) {
+ double vpts_now = p->surfaces[surface_now].vpts,
+ vpts_nxt = p->surfaces[surface_nxt].vpts,
+ vpts_new = p->image.mpi->pts;
+ if (vpts_now != MP_NOPTS_VALUE &&
+ vpts_nxt != MP_NOPTS_VALUE &&
+ vpts_new != MP_NOPTS_VALUE)
+ {
+ // Round to nearest neighbour
+ double vpts_vsync = (t->next_vsync - t->pts)/1e6 + vpts_new;
+ p->osd_pts = fabs(vpts_vsync-vpts_now) < fabs(vpts_vsync-vpts_nxt)
+ ? vpts_now : vpts_nxt;
+ }
}
- // fbosurface 0 is bound by handle_pass
- gl->ActiveTexture(GL_TEXTURE0 + 1);
- gl->BindTexture(p->gl_target, p->surfaces[p->surface_idx].fbotex.texture);
- gl->ActiveTexture(GL_TEXTURE0);
-
- if (!t) {
+ // Finally, draw the right mix of frames to the screen.
+ if (!t || !valid) {
+ // surface_now is guaranteed to be valid, so we can safely use it.
+ pass_load_fbotex(p, &p->surfaces[surface_now].fbotex, 0, vp_w, vp_h);
+ GLSL(vec4 color = texture(texture0, texcoord0);)
p->is_interpolated = false;
- return 0.0;
- }
-
- int64_t vsync_interval = t->next_vsync - t->prev_vsync;
-
- if (t->pts > t->next_vsync && t->pts < t->next_vsync + vsync_interval) {
- // current frame overlaps PTS boundary, blend
- double R = t->pts - t->next_vsync;
- float ts = p->opts.smoothmotion_threshold;
- inter_coeff = R / vsync_interval;
- inter_coeff = inter_coeff < 0.0 + ts ? 0.0 : inter_coeff;
- inter_coeff = inter_coeff > 1.0 - ts ? 1.0 : inter_coeff;
- MP_DBG(p, "inter frame ppts: %lld, pts: %lld, "
- "vsync: %lld, mix: %f\n",
- (long long)prev_pts, (long long)t->pts,
- (long long)t->next_vsync, inter_coeff);
+ } else {
+ int64_t pts_now = p->surfaces[surface_now].pts,
+ pts_nxt = p->surfaces[surface_nxt].pts;
+ double fscale = pts_nxt - pts_now, mix;
+ if (oversample) {
+ double vsync_interval = t->next_vsync - t->prev_vsync,
+ threshold = tscale->conf.kernel.params[0];
+ threshold = isnan(threshold) ? 0.0 : threshold;
+ mix = (pts_nxt - t->next_vsync) / vsync_interval;
+ mix = mix <= 0 + threshold ? 0 : mix;
+ 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);)
+ } else {
+ mix = (t->next_vsync - pts_now) / fscale;
+ gl_sc_uniform_f(p->sc, "fcoord", mix);
+ pass_sample_separated_gen(p, tscale, 0, 0);
+ }
+ for (int i = 0; i < size; i++) {
+ pass_load_fbotex(p, &p->surfaces[fbosurface_wrap(surface_bse+i)].fbotex,
+ i, vp_w, vp_h);
+ }
MP_STATS(p, "frame-mix");
-
- // the value is scaled to fit in the graph with the completely
- // unrelated "phase" value (which is stupid)
- MP_STATS(p, "value-timed %lld %f mix-value",
- (long long)t->pts, inter_coeff * 10000);
- } else if (t->pts > t->next_vsync) {
- // there's a new frame, but we haven't displayed or blended it yet,
- // so we still draw the old frame
- inter_coeff = 1.0;
+ MP_DBG(p, "inter frame ppts: %lld, pts: %lld, vsync: %lld, mix: %f\n",
+ (long long)pts_now, (long long)pts_nxt,
+ (long long)t->next_vsync, mix);
+ p->is_interpolated = true;
+ }
+ pass_draw_to_screen(p, fbo);
+
+ // Dequeue frames if necessary
+ if (t) {
+ int64_t vsync_interval = t->next_vsync - t->prev_vsync;
+ int64_t vsync_guess = t->next_vsync + vsync_interval;
+ if (p->surfaces[surface_nxt].pts > p->surfaces[p->surface_now].pts &&
+ p->surfaces[surface_nxt].pts < vsync_guess)
+ {
+ p->surface_now = surface_nxt;
+ }
}
-
- p->is_interpolated = inter_coeff > 0.0;
- return inter_coeff;
}
// (fbo==0 makes BindFramebuffer select the screen backbuffer)
@@ -1837,171 +2037,57 @@ void gl_video_render_frame(struct gl_video *p, int fbo, struct frame_timing *t)
GL *gl = p->gl;
struct video_image *vimg = &p->image;
- p->is_interpolated = false;
-
gl->BindFramebuffer(GL_FRAMEBUFFER, fbo);
- gl->Viewport(p->vp_x, p->vp_y, p->vp_w, p->vp_h);
- if (p->opts.temporal_dither)
- change_dither_trafo(p);
-
- if (p->dst_rect.x0 > p->vp_x || p->dst_rect.y0 > p->vp_y
- || p->dst_rect.x1 < p->vp_x + p->vp_w
- || p->dst_rect.y1 < p->vp_y + p->vp_h)
+ if (!vimg->mpi || p->dst_rect.x0 > 0 || p->dst_rect.y0 > 0 ||
+ p->dst_rect.x1 < p->vp_w || p->dst_rect.y1 < abs(p->vp_h))
{
+ struct m_color c = p->opts.background;
+ gl->ClearColor(c.r / 255.0, c.g / 255.0, c.b / 255.0, c.a / 255.0);
gl->Clear(GL_COLOR_BUFFER_BIT);
}
- if (!vimg->mpi) {
- gl->Clear(GL_COLOR_BUFFER_BIT);
- goto draw_osd;
- }
+ if (vimg->mpi) {
+ gl_sc_set_vao(p->sc, &p->vao);
- GLuint imgtex[4] = {0};
- set_image_textures(p, vimg, imgtex);
-
- struct pass chain = {
- .f = {
- .vp_w = p->image_w,
- .vp_h = p->image_h,
- .tex_w = vimg->planes[0].tex_w,
- .tex_h = vimg->planes[0].tex_h,
- .texture = imgtex[0],
- },
- };
+ if (p->opts.interpolation) {
+ gl_video_interpolate_frame(p, fbo, t);
+ } else {
+ // Skip interpolation if there's nothing to be done
+ pass_render_frame(p);
+ pass_draw_to_screen(p, fbo);
+ }
- double inter_coeff = 0.0;
- if (p->opts.smoothmotion) {
- inter_coeff = gl_video_interpolate_frame(p, &chain, t);
- } else {
- gl_video_upscale_frame(p, &chain, NULL);
+ debug_check_gl(p, "after video rendering");
}
- struct fbotex screen = {
- .vp_x = p->vp_x,
- .vp_y = p->vp_y,
- .vp_w = p->vp_w,
- .vp_h = p->vp_h,
- .fbo = fbo,
- };
-
- chain.use_dst = true;
- chain.dst = p->dst_rect;
- chain.flags = (p->image_params.rotate % 90 ? 0 : p->image_params.rotate / 90)
- | (vimg->image_flipped ? 4 : 0);
+ gl->BindFramebuffer(GL_FRAMEBUFFER, fbo);
- gl->UseProgram(p->final_program);
- GLint loc = gl->GetUniformLocation(p->final_program, "inter_coeff");
- gl->Uniform1f(loc, inter_coeff);
- handle_pass(p, &chain, &screen, p->final_program);
+ if (p->osd) {
+ pass_draw_osd(p, p->opts.blend_subs ? OSD_DRAW_OSD_ONLY : 0,
+ p->osd_pts, p->osd_rect, p->vp_w, p->vp_h, fbo, true);
+ debug_check_gl(p, "after OSD rendering");
+ }
gl->UseProgram(0);
-
- unset_image_textures(p);
-
- p->frames_rendered++;
-
- debug_check_gl(p, "after video rendering");
-
-draw_osd:
- mpgl_osd_draw(p->osd, p->osd_rect, p->osd_pts, p->image_params.stereo_out);
-
gl->BindFramebuffer(GL_FRAMEBUFFER, 0);
-}
-
-static void update_window_sized_objects(struct gl_video *p)
-{
- int w = p->dst_rect.x1 - p->dst_rect.x0;
- int h = p->dst_rect.y1 - p->dst_rect.y0;
- if ((p->image_params.rotate % 180) == 90)
- MPSWAP(int, w, h);
-
- // Round up to an arbitrary alignment to make window resizing or
- // panscan controls smoother (less texture reallocations).
- int width = FFALIGN(w, 256);
- int height = FFALIGN(h, 256);
-
- if (p->scale_sep_program) {
- if (h > p->scale_sep_fbo.tex_h) {
- fbotex_uninit(&p->scale_sep_fbo);
- fbotex_init(&p->scale_sep_fbo, p->gl, p->log, p->image_w, height,
- p->gl_target, GL_NEAREST, p->opts.fbo_format);
- }
- p->scale_sep_fbo.vp_w = p->image_w;
- p->scale_sep_fbo.vp_h = h;
- }
-
- if (p->opts.smoothmotion) {
- for (int i = 0; i < FBOSURFACES_MAX; i++) {
- struct fbotex *fbo = &p->surfaces[i].fbotex;
- if (w > fbo->tex_w || h > fbo->tex_h) {
- fbotex_uninit(fbo);
- fbotex_init(fbo, p->gl, p->log, width, height,
- p->gl_target, GL_NEAREST, p->opts.fbo_format);
- }
- fbo->vp_w = w;
- fbo->vp_h = h;
- p->surfaces[i].valid = false;
- }
- }
-}
-
-static void check_resize(struct gl_video *p)
-{
- bool need_scaler_reinit = false; // filter size change needed
- bool need_scaler_update = false; // filter LUT change needed
- bool too_small = false;
- for (int n = 0; n < 2; n++) {
- if (p->scalers[n].kernel) {
- struct filter_kernel old = *p->scalers[n].kernel;
- update_scale_factor(p, &p->scalers[n]);
- struct filter_kernel new = *p->scalers[n].kernel;
- need_scaler_reinit |= (new.size != old.size);
- need_scaler_update |= (new.inv_scale != old.inv_scale);
- too_small |= p->scalers[n].insufficient;
- }
- }
- for (int n = 0; n < 2; n++) {
- if (strcmp(p->scalers[n].name, expected_scaler(p, n)) != 0)
- need_scaler_reinit = true;
- }
- if (p->upscaling != (get_scale_factor(p) > 1.0)) {
- p->upscaling = !p->upscaling;
- // Switching between upscaling and downscaling also requires sigmoid
- // to be toggled
- need_scaler_reinit |= p->sigmoid_enabled;
- }
- if (need_scaler_reinit) {
- reinit_rendering(p);
- } else if (need_scaler_update) {
- init_scaler(p, &p->scalers[0]);
- init_scaler(p, &p->scalers[1]);
- }
- if (too_small) {
- MP_WARN(p, "Can't downscale that much, window "
- "output may look suboptimal.\n");
- }
- update_window_sized_objects(p);
- update_all_uniforms(p);
+ p->frames_rendered++;
}
-void gl_video_resize(struct gl_video *p, struct mp_rect *window,
+// vp_w/vp_h is the implicit size of the target framebuffer.
+// vp_h can be negative to flip the screen.
+void gl_video_resize(struct gl_video *p, int vp_w, int vp_h,
struct mp_rect *src, struct mp_rect *dst,
- struct mp_osd_res *osd, bool vflip)
+ struct mp_osd_res *osd)
{
p->src_rect = *src;
p->dst_rect = *dst;
p->osd_rect = *osd;
+ p->vp_w = vp_w;
+ p->vp_h = vp_h;
- p->vp_x = window->x0;
- p->vp_y = window->y0;
- p->vp_w = window->x1 - window->x0;
- p->vp_h = window->y1 - window->y0;
-
- p->vp_vflipped = vflip;
-
- check_resize(p);
+ gl_video_reset_surfaces(p);
}
static bool get_image(struct gl_video *p, struct mp_image *mpi)
@@ -2019,8 +2105,8 @@ static bool get_image(struct gl_video *p, struct mp_image *mpi)
for (int n = 0; n < p->plane_count; n++) {
struct texplane *plane = &vimg->planes[n];
- mpi->stride[n] = mpi->plane_w[n] * p->image_desc.bytes[n];
- int needed_size = mpi->plane_h[n] * mpi->stride[n];
+ mpi->stride[n] = mp_image_plane_w(mpi, n) * p->image_desc.bytes[n];
+ int needed_size = mp_image_plane_h(mpi, n) * mpi->stride[n];
if (!plane->gl_buffer)
gl->GenBuffers(1, &plane->gl_buffer);
gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, plane->gl_buffer);
@@ -2038,6 +2124,13 @@ static bool get_image(struct gl_video *p, struct mp_image *mpi)
return true;
}
+void gl_video_skip_image(struct gl_video *p, struct mp_image *mpi)
+{
+ struct video_image *vimg = &p->image;
+ talloc_free(vimg->mpi);
+ vimg->mpi = mpi;
+}
+
void gl_video_upload_image(struct gl_video *p, struct mp_image *mpi)
{
GL *gl = p->gl;
@@ -2058,8 +2151,9 @@ void gl_video_upload_image(struct gl_video *p, struct mp_image *mpi)
bool pbo = false;
if (!vimg->planes[0].buffer_ptr && get_image(p, &mpi2)) {
for (int n = 0; n < p->plane_count; n++) {
- int line_bytes = mpi->plane_w[n] * p->image_desc.bytes[n];
- memcpy_pic(mpi2.planes[n], mpi->planes[n], line_bytes, mpi->plane_h[n],
+ int line_bytes = mp_image_plane_w(mpi, n) * p->image_desc.bytes[n];
+ int plane_h = mp_image_plane_h(mpi, n);
+ memcpy_pic(mpi2.planes[n], mpi->planes[n], line_bytes, plane_h,
mpi2.stride[n], mpi->stride[n]);
}
pbo = true;
@@ -2096,9 +2190,7 @@ static bool test_fbo(struct gl_video *p, bool *success)
MP_VERBOSE(p, "Testing user-set FBO format (0x%x)\n",
(unsigned)p->opts.fbo_format);
struct fbotex fbo = {0};
- if (fbotex_init(&fbo, p->gl, p->log, 16, 16, p->gl_target, GL_LINEAR,
- p->opts.fbo_format))
- {
+ if (fbotex_init(&fbo, p->gl, p->log, 16, 16, p->opts.fbo_format)) {
gl->BindFramebuffer(GL_FRAMEBUFFER, fbo.fbo);
gl->BindFramebuffer(GL_FRAMEBUFFER, 0);
*success = true;
@@ -2114,33 +2206,28 @@ static void check_gl_features(struct gl_video *p)
GL *gl = p->gl;
bool have_float_tex = gl->mpgl_caps & MPGL_CAP_FLOAT_TEX;
bool have_fbo = gl->mpgl_caps & MPGL_CAP_FB;
- bool have_arrays = gl->mpgl_caps & MPGL_CAP_1ST_CLASS_ARRAYS;
bool have_1d_tex = gl->mpgl_caps & MPGL_CAP_1D_TEX;
bool have_3d_tex = gl->mpgl_caps & MPGL_CAP_3D_TEX;
bool have_mix = gl->glsl_version >= 130;
- char *disabled[10];
- int n_disabled = 0;
-
// Normally, we want to disable them by default if FBOs are unavailable,
// because they will be slow (not critically slow, but still slower).
// Without FP textures, we must always disable them.
// I don't know if luminance alpha float textures exist, so disregard them.
- for (int n = 0; n < 2; n++) {
- const struct filter_kernel *kernel = mp_find_filter_kernel(p->opts.scalers[n]);
+ for (int n = 0; n < 4; n++) {
+ const struct filter_kernel *kernel =
+ mp_find_filter_kernel(p->opts.scaler[n].kernel.name);
if (kernel) {
char *reason = NULL;
if (!test_fbo(p, &have_fbo))
- reason = "scaler (FBO)";
+ reason = "scaler (FBOs missing)";
if (!have_float_tex)
- reason = "scaler (float tex.)";
- if (!have_arrays)
- reason = "scaler (no GLSL support)";
+ reason = "scaler (float tex. missing)";
if (!have_1d_tex && kernel->polar)
- reason = "scaler (1D tex.)";
+ reason = "scaler (1D tex. missing)";
if (reason) {
- p->opts.scalers[n] = "bilinear";
- disabled[n_disabled++] = reason;
+ p->opts.scaler[n].kernel.name = "bilinear";
+ MP_WARN(p, "Disabling %s.\n", reason);
}
}
}
@@ -2149,49 +2236,47 @@ static void check_gl_features(struct gl_video *p)
// GLES2 doesn't even provide 3D textures
if (p->use_lut_3d && !(have_3d_tex && have_float_tex)) {
p->use_lut_3d = false;
- disabled[n_disabled++] = "color management (GLES unsupported)";
+ MP_WARN(p, "Disabling color management (GLES unsupported).\n");
}
// Missing float textures etc. (maybe ordered would actually work)
if (p->opts.dither_algo >= 0 && gl->es) {
p->opts.dither_algo = -1;
- disabled[n_disabled++] = "dithering (GLES unsupported)";
+ MP_WARN(p, "Disabling dithering (GLES unsupported).\n");
}
- int use_cms = p->opts.srgb || p->use_lut_3d;
+ int use_cms = p->opts.target_prim != MP_CSP_PRIM_AUTO ||
+ p->opts.target_trc != MP_CSP_TRC_AUTO || p->use_lut_3d;
- // srgb_compand() not available
- if (!have_mix && p->opts.srgb) {
- p->opts.srgb = false;
- disabled[n_disabled++] = "sRGB output (GLSL version)";
+ // mix() is needed for some gamma functions
+ if (!have_mix && (p->opts.linear_scaling || p->opts.sigmoid_upscaling)) {
+ p->opts.linear_scaling = false;
+ p->opts.sigmoid_upscaling = false;
+ MP_WARN(p, "Disabling linear/sigmoid scaling (GLSL version too old).\n");
+ }
+ if (!have_mix && use_cms) {
+ p->opts.target_prim = MP_CSP_PRIM_AUTO;
+ p->opts.target_trc = MP_CSP_TRC_AUTO;
+ p->use_lut_3d = false;
+ MP_WARN(p, "Disabling color management (GLSL version too old).\n");
}
if (use_cms && !test_fbo(p, &have_fbo)) {
- p->opts.srgb = false;
+ p->opts.target_prim = MP_CSP_PRIM_AUTO;
+ p->opts.target_trc = MP_CSP_TRC_AUTO;
p->use_lut_3d = false;
- disabled[n_disabled++] = "color management (FBO)";
+ MP_WARN(p, "Disabling color management (FBOs missing).\n");
}
- if (p->opts.smoothmotion && !test_fbo(p, &have_fbo)) {
- p->opts.smoothmotion = false;
- disabled[n_disabled++] = "smoothmotion (FBO)";
+ if (p->opts.interpolation && !test_fbo(p, &have_fbo)) {
+ p->opts.interpolation = false;
+ MP_WARN(p, "Disabling interpolation (FBOs missing).\n");
}
- // because of bt709_expand()
- if (!have_mix && p->use_lut_3d) {
- p->use_lut_3d = false;
- disabled[n_disabled++] = "color management (GLSL version)";
+ if (p->opts.blend_subs && !test_fbo(p, &have_fbo)) {
+ p->opts.blend_subs = 0;
+ MP_WARN(p, "Disabling subtitle blending (FBOs missing).\n");
}
if (gl->es && p->opts.pbo) {
p->opts.pbo = 0;
- disabled[n_disabled++] = "PBOs (GLES unsupported)";
- }
-
- if (n_disabled) {
- MP_ERR(p, "Some OpenGL extensions not detected, disabling: ");
- for (int n = 0; n < n_disabled; n++) {
- if (n)
- MP_ERR(p, ", ");
- MP_ERR(p, "%s", disabled[n]);
- }
- MP_ERR(p, ".\n");
+ MP_WARN(p, "Disabling PBOs (GLES unsupported).\n");
}
}
@@ -2247,6 +2332,8 @@ void gl_video_uninit(struct gl_video *p)
uninit_video(p);
+ gl_sc_destroy(p->sc);
+
gl_vao_uninit(&p->vao);
gl->DeleteTextures(1, &p->lut_3d_texture);
@@ -2262,8 +2349,6 @@ void gl_video_set_gl_state(struct gl_video *p)
{
GL *gl = p->gl;
- struct m_color c = p->opts.background;
- gl->ClearColor(c.r / 255.0, c.g / 255.0, c.b / 255.0, c.a / 255.0);
gl->ActiveTexture(GL_TEXTURE0);
if (gl->mpgl_caps & MPGL_CAP_ROW_LENGTH)
gl->PixelStorei(GL_UNPACK_ROW_LENGTH, 0);
@@ -2277,9 +2362,7 @@ void gl_video_unset_gl_state(struct gl_video *p)
void gl_video_reset(struct gl_video *p)
{
- for (int i = 0; i < FBOSURFACES_MAX; i++)
- p->surfaces[i].pts = 0;
- p->surface_idx = 0;
+ gl_video_reset_surfaces(p);
}
bool gl_video_showing_interpolated_frame(struct gl_video *p)
@@ -2448,7 +2531,7 @@ void gl_video_config(struct gl_video *p, struct mp_image_params *params)
init_video(p);
}
- check_resize(p);
+ gl_video_reset_surfaces(p);
}
void gl_video_set_output_depth(struct gl_video *p, int r, int g, int b)
@@ -2457,7 +2540,15 @@ void gl_video_set_output_depth(struct gl_video *p, int r, int g, int b)
p->depth_g = g;
}
-struct gl_video *gl_video_init(GL *gl, struct mp_log *log, struct osd_state *osd)
+void gl_video_set_osd_source(struct gl_video *p, struct osd_state *osd)
+{
+ mpgl_osd_destroy(p->osd);
+ p->osd = NULL;
+ p->osd_state = osd;
+ recreate_osd(p);
+}
+
+struct gl_video *gl_video_init(GL *gl, struct mp_log *log)
{
if (gl->version < 210 && gl->es < 200) {
mp_err(log, "At least OpenGL 2.1 or OpenGL ES 2.0 required.\n");
@@ -2468,16 +2559,11 @@ struct gl_video *gl_video_init(GL *gl, struct mp_log *log, struct osd_state *osd
*p = (struct gl_video) {
.gl = gl,
.log = log,
- .osd_state = osd,
.opts = gl_video_opts_def,
.gl_target = GL_TEXTURE_2D,
.texture_16bit_depth = 16,
- .user_gamma = 1.0f,
- .scalers = {
- { .index = 0, .name = "bilinear" },
- { .index = 1, .name = "bilinear" },
- },
- .scratch = talloc_zero_array(p, char *, 1),
+ .scaler = {{.index = 0}, {.index = 1}, {.index = 2}, {.index = 3}},
+ .sc = gl_sc_create(gl, log),
};
gl_video_set_debug(p, true);
init_gl(p);
@@ -2485,15 +2571,18 @@ struct gl_video *gl_video_init(GL *gl, struct mp_log *log, struct osd_state *osd
return p;
}
-// Get static string for scaler shader.
-static const char *handle_scaler_opt(const char *name)
+// Get static string for scaler shader. If "tscale" is set to true, the
+// scaler must be a separable convolution filter.
+static const char *handle_scaler_opt(const char *name, bool tscale)
{
if (name && name[0]) {
const struct filter_kernel *kernel = mp_find_filter_kernel(name);
- if (kernel)
- return kernel->name;
+ if (kernel && (!tscale || !kernel->polar))
+ return kernel->f.name;
- for (const char *const *filter = fixed_scale_filters; *filter; filter++) {
+ for (const char *const *filter = tscale ? fixed_tscale_filters
+ : fixed_scale_filters;
+ *filter; filter++) {
if (strcmp(*filter, name) == 0)
return *filter;
}
@@ -2503,22 +2592,31 @@ static const char *handle_scaler_opt(const char *name)
// Set the options, and possibly update the filter chain too.
// Note: assumes all options are valid and verified by the option parser.
-void gl_video_set_options(struct gl_video *p, struct gl_video_opts *opts)
+void gl_video_set_options(struct gl_video *p, struct gl_video_opts *opts,
+ int *queue_size)
{
p->opts = *opts;
- for (int n = 0; n < 2; n++) {
- p->opts.scalers[n] = (char *)handle_scaler_opt(p->opts.scalers[n]);
- p->opts.dscaler = (char *)handle_scaler_opt(p->opts.dscaler);
+ for (int n = 0; n < 4; n++) {
+ p->opts.scaler[n].kernel.name =
+ (char *)handle_scaler_opt(p->opts.scaler[n].kernel.name, n==3);
}
- check_gl_features(p);
- reinit_rendering(p);
- check_resize(p);
-}
+ // Figure out an adequate size for the interpolation queue. The larger
+ // the radius, the earlier we need to queue frames. This rough heuristic
+ // seems to work for now, but ideally we want to rework the pause/unpause
+ // logic to make larger queue sizes the default.
+ if (queue_size && p->opts.interpolation) {
+ const struct filter_kernel *kernel =
+ mp_find_filter_kernel(p->opts.scaler[3].kernel.name);
+ if (kernel) {
+ double radius = kernel->f.radius;
+ radius = radius > 0 ? radius : p->opts.scaler[3].radius;
+ *queue_size = 50e3 * ceil(radius);
+ }
+ }
-void gl_video_get_colorspace(struct gl_video *p, struct mp_image_params *params)
-{
- *params = p->image_params; // supports everything
+ check_gl_features(p);
+ uninit_rendering(p);
}
struct mp_csp_equalizer *gl_video_eq_ptr(struct gl_video *p)
@@ -2529,14 +2627,7 @@ struct mp_csp_equalizer *gl_video_eq_ptr(struct gl_video *p)
// Call when the mp_csp_equalizer returned by gl_video_eq_ptr() was changed.
void gl_video_eq_update(struct gl_video *p)
{
- update_settings(p);
-
- if (p->need_reinit_rendering) {
- reinit_rendering(p);
- check_resize(p);
- } else {
- update_all_uniforms(p);
- }
+ gl_video_reset_surfaces(p);
}
static int validate_scaler_opt(struct mp_log *log, const m_option_t *opt,
@@ -2544,25 +2635,55 @@ static int validate_scaler_opt(struct mp_log *log, const m_option_t *opt,
{
char s[20] = {0};
int r = 1;
+ bool tscale = bstr_equals0(name, "tscale");
if (bstr_equals0(param, "help")) {
r = M_OPT_EXIT - 1;
} else {
snprintf(s, sizeof(s), "%.*s", BSTR_P(param));
- if (!handle_scaler_opt(s))
+ if (!handle_scaler_opt(s, tscale))
r = M_OPT_INVALID;
}
if (r < 1) {
mp_info(log, "Available scalers:\n");
- for (const char *const *filter = fixed_scale_filters; *filter; filter++)
+ for (const char *const *filter = tscale ? fixed_tscale_filters
+ : fixed_scale_filters;
+ *filter; filter++) {
mp_info(log, " %s\n", *filter);
- for (int n = 0; mp_filter_kernels[n].name; n++)
- mp_info(log, " %s\n", mp_filter_kernels[n].name);
+ }
+ for (int n = 0; mp_filter_kernels[n].f.name; n++) {
+ if (!tscale || !mp_filter_kernels[n].polar)
+ mp_info(log, " %s\n", mp_filter_kernels[n].f.name);
+ }
if (s[0])
mp_fatal(log, "No scaler named '%s' found!\n", s);
}
return r;
}
+static int validate_window_opt(struct mp_log *log, const m_option_t *opt,
+ struct bstr name, struct bstr param)
+{
+ char s[20] = {0};
+ int r = 1;
+ if (bstr_equals0(param, "help")) {
+ r = M_OPT_EXIT - 1;
+ } else {
+ snprintf(s, sizeof(s), "%.*s", BSTR_P(param));
+ const struct filter_window *window = mp_find_filter_window(s);
+ if (!window)
+ r = M_OPT_INVALID;
+ }
+ if (r < 1) {
+ mp_info(log, "Available windows:\n");
+ for (int n = 0; mp_filter_windows[n].name; n++)
+ mp_info(log, " %s\n", mp_filter_windows[n].name);
+ if (s[0])
+ mp_fatal(log, "No window named '%s' found!\n", s);
+ }
+ return r;
+}
+
+
// Resize and redraw the contents of the window without further configuration.
// Intended to be used in situations where the frontend can't really be
// involved with reconfiguring the VO properly.
@@ -2574,6 +2695,31 @@ void gl_video_resize_redraw(struct gl_video *p, int w, int h)
gl_video_render_frame(p, 0, NULL);
}
+float gl_video_scale_ambient_lux(float lmin, float lmax,
+ float rmin, float rmax, float lux)
+{
+ assert(lmax > lmin);
+
+ float num = (rmax - rmin) * (log10(lux) - log10(lmin));
+ float den = log10(lmax) - log10(lmin);
+ float result = num / den + rmin;
+
+ // clamp the result
+ float max = MPMAX(rmax, rmin);
+ float min = MPMIN(rmax, rmin);
+ return MPMAX(MPMIN(result, max), min);
+}
+
+void gl_video_set_ambient_lux(struct gl_video *p, int lux)
+{
+ if (p->opts.gamma_auto) {
+ float gamma = gl_video_scale_ambient_lux(16.0, 64.0, 2.40, 1.961, lux);
+ MP_VERBOSE(p, "ambient light changed: %dlux (gamma: %f)\n", lux, gamma);
+ p->opts.gamma = MPMIN(1.0, 1.961 / gamma);
+ gl_video_eq_update(p);
+ }
+}
+
void gl_video_set_hwdec(struct gl_video *p, struct gl_hwdec *hwdec)
{
p->hwdec = hwdec;
diff --git a/video/out/gl_video.h b/video/out/gl_video.h
index 74a8b7f..3064715 100644
--- a/video/out/gl_video.h
+++ b/video/out/gl_video.h
@@ -28,14 +28,25 @@ struct lut3d {
int size[3];
};
+struct scaler_fun {
+ char *name;
+ float params[2];
+ float blur;
+};
+
+struct scaler_config {
+ struct scaler_fun kernel;
+ struct scaler_fun window;
+ float radius;
+ float antiring;
+};
+
struct gl_video_opts {
- char *scalers[2];
- char *dscaler;
- float scaler_params[2][2];
- float scaler_radius[2];
- float scaler_antiring[2];
+ struct scaler_config scaler[4];
float gamma;
- int srgb;
+ int gamma_auto;
+ int target_prim;
+ int target_trc;
int linear_scaling;
int fancy_downscaling;
int sigmoid_upscaling;
@@ -53,8 +64,8 @@ struct gl_video_opts {
int chroma_location;
int use_rectangle;
struct m_color background;
- int smoothmotion;
- float smoothmotion_threshold;
+ int interpolation;
+ int blend_subs;
};
extern const struct m_sub_options gl_video_conf;
@@ -63,19 +74,21 @@ extern const struct gl_video_opts gl_video_opts_def;
struct gl_video;
-struct gl_video *gl_video_init(GL *gl, struct mp_log *log, struct osd_state *osd);
+struct gl_video *gl_video_init(GL *gl, struct mp_log *log);
void gl_video_uninit(struct gl_video *p);
-void gl_video_set_options(struct gl_video *p, struct gl_video_opts *opts);
+void gl_video_set_osd_source(struct gl_video *p, struct osd_state *osd);
+void gl_video_set_options(struct gl_video *p, struct gl_video_opts *opts,
+ int *queue_size);
bool gl_video_check_format(struct gl_video *p, int mp_format);
void gl_video_config(struct gl_video *p, struct mp_image_params *params);
void gl_video_set_output_depth(struct gl_video *p, int r, int g, int b);
void gl_video_set_lut3d(struct gl_video *p, struct lut3d *lut3d);
+void gl_video_skip_image(struct gl_video *p, struct mp_image *mpi);
void gl_video_upload_image(struct gl_video *p, struct mp_image *img);
void gl_video_render_frame(struct gl_video *p, int fbo, struct frame_timing *t);
-void gl_video_resize(struct gl_video *p, struct mp_rect *window,
+void gl_video_resize(struct gl_video *p, int vp_w, int vp_h,
struct mp_rect *src, struct mp_rect *dst,
- struct mp_osd_res *osd, bool vflip);
-void gl_video_get_colorspace(struct gl_video *p, struct mp_image_params *params);
+ struct mp_osd_res *osd);
struct mp_csp_equalizer;
struct mp_csp_equalizer *gl_video_eq_ptr(struct gl_video *p);
void gl_video_eq_update(struct gl_video *p);
@@ -83,6 +96,10 @@ void gl_video_eq_update(struct gl_video *p);
void gl_video_set_debug(struct gl_video *p, bool enable);
void gl_video_resize_redraw(struct gl_video *p, int w, int h);
+float gl_video_scale_ambient_lux(float lmin, float lmax,
+ float rmin, float rmax, float lux);
+void gl_video_set_ambient_lux(struct gl_video *p, int lux);
+
void gl_video_set_gl_state(struct gl_video *p);
void gl_video_unset_gl_state(struct gl_video *p);
void gl_video_reset(struct gl_video *p);
diff --git a/video/out/gl_video_shaders.glsl b/video/out/gl_video_shaders.glsl
deleted file mode 100644
index 4a9e3fa..0000000
--- a/video/out/gl_video_shaders.glsl
+++ /dev/null
@@ -1,529 +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/>.
- *
- * 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.
- */
-
-// Note that this file is not directly passed as shader, but run through some
-// text processing functions, and in fact contains multiple vertex and fragment
-// shaders.
-
-// inserted at the beginning of all shaders
-#!section prelude
-
-#ifdef GL_ES
-precision mediump float;
-#endif
-
-// GLSL 1.20 compatibility layer
-// texture() should be assumed to always map to texture2D()
-#if __VERSION__ >= 130
-# define texture1D texture
-# define texture3D texture
-# define DECLARE_FRAGPARMS \
- out vec4 out_color;
-#else
-# define texture texture2D
-# define DECLARE_FRAGPARMS
-# define out_color gl_FragColor
-# define in varying
-#endif
-
-#if HAVE_RG
-#define RG rg
-#else
-#define RG ra
-#endif
-
-// Earlier GLSL doesn't support mix() with bvec
-#if __VERSION__ >= 130
-vec3 srgb_expand(vec3 v)
-{
- return mix(v / vec3(12.92), pow((v + vec3(0.055))/vec3(1.055), vec3(2.4)),
- lessThanEqual(vec3(0.04045), v));
-}
-
-vec3 srgb_compand(vec3 v)
-{
- return mix(v * vec3(12.92), vec3(1.055) * pow(v, vec3(1.0/2.4)) - vec3(0.055),
- lessThanEqual(vec3(0.0031308), v));
-}
-
-vec3 bt2020_expand(vec3 v)
-{
- return mix(v / vec3(4.5), pow((v + vec3(0.0993))/vec3(1.0993), vec3(1.0/0.45)),
- lessThanEqual(vec3(0.08145), v));
-}
-
-vec3 bt2020_compand(vec3 v)
-{
- return mix(v * vec3(4.5), vec3(1.0993) * pow(v, vec3(0.45)) - vec3(0.0993),
- lessThanEqual(vec3(0.0181), v));
-}
-#endif
-
-#!section vertex_all
-
-#if __VERSION__ < 130
-# undef in
-# define in attribute
-# define out varying
-#endif
-
-uniform mat3 transform;
-uniform vec3 translation;
-#if HAVE_3DTEX
-uniform sampler3D lut_3d;
-#endif
-uniform mat3 cms_matrix; // transformation from file's gamut to bt.2020
-
-in vec2 vertex_position;
-in vec4 vertex_color;
-out vec4 color;
-in vec2 vertex_texcoord;
-out vec2 texcoord;
-
-void main() {
- vec3 position = vec3(vertex_position, 1) + translation;
-#ifndef FIXED_SCALE
- position = transform * position;
-#endif
- gl_Position = vec4(position, 1);
- color = vertex_color;
-
- // Although we are not scaling in linear light, both 3DLUT and SRGB still
- // operate on linear light inputs so we have to convert to it before
- // either step can be applied.
-#ifdef USE_OSD_LINEAR_CONV_BT1886
- color.rgb = pow(color.rgb, vec3(1.961));
-#endif
-#ifdef USE_OSD_LINEAR_CONV_SRGB
- color.rgb = srgb_expand(color.rgb);
-#endif
-#ifdef USE_OSD_CMS_MATRIX
- // Convert to the right target gamut first (to BT.709 for sRGB,
- // and to BT.2020 for 3DLUT). Normal clamping here as perceptually
- // accurate colorimetry is probably not worth the performance trade-off
- // here.
- color.rgb = clamp(cms_matrix * color.rgb, 0.0, 1.0);
-#endif
-#ifdef USE_OSD_3DLUT
- color.rgb = pow(color.rgb, vec3(1.0/2.4)); // linear -> 2.4 3DLUT space
- color = vec4(texture3D(lut_3d, color.rgb).rgb, color.a);
-#endif
-#ifdef USE_OSD_SRGB
- color.rgb = srgb_compand(color.rgb);
-#endif
-
- texcoord = vertex_texcoord;
-}
-
-#!section frag_osd_libass
-uniform sampler2D texture0;
-
-in vec2 texcoord;
-in vec4 color;
-DECLARE_FRAGPARMS
-
-void main() {
- out_color = vec4(color.rgb, color.a * texture(texture0, texcoord).r);
-}
-
-#!section frag_osd_rgba
-uniform sampler2D texture0;
-
-in vec2 texcoord;
-DECLARE_FRAGPARMS
-
-void main() {
- out_color = texture(texture0, texcoord).bgra;
-}
-
-#!section frag_video
-uniform VIDEO_SAMPLER texture0;
-uniform VIDEO_SAMPLER texture1;
-uniform VIDEO_SAMPLER texture2;
-uniform VIDEO_SAMPLER texture3;
-uniform vec2 textures_size[4];
-uniform vec2 chroma_center_offset;
-uniform vec2 chroma_div;
-uniform vec2 chroma_fix;
-uniform sampler2D lut_2d_c;
-uniform sampler2D lut_2d_l;
-#if HAVE_1DTEX
-uniform sampler1D lut_1d_c;
-uniform sampler1D lut_1d_l;
-#endif
-#if HAVE_3DTEX
-uniform sampler3D lut_3d;
-#endif
-uniform sampler2D dither;
-uniform mat3 colormatrix;
-uniform vec3 colormatrix_c;
-uniform mat3 cms_matrix;
-uniform mat2 dither_trafo;
-uniform float inv_gamma;
-uniform float input_gamma;
-uniform float conv_gamma;
-uniform float sig_center;
-uniform float sig_slope;
-uniform float sig_scale;
-uniform float sig_offset;
-uniform float dither_quantization;
-uniform float dither_center;
-uniform float filter_param1_l;
-uniform float filter_param1_c;
-uniform float antiring_factor;
-uniform vec2 dither_size;
-uniform float inter_coeff;
-
-in vec2 texcoord;
-DECLARE_FRAGPARMS
-
-#define CONV_NV12 1
-#define CONV_PLANAR 2
-
-vec4 sample_bilinear(VIDEO_SAMPLER tex, vec2 texsize, vec2 texcoord, float param1) {
- return texture(tex, texcoord);
-}
-
-#define SAMPLE_TRIVIAL(tex, texsize, texcoord) texture(tex, texcoord)
-
-// Explanation how bicubic scaling with only 4 texel fetches is done:
-// http://www.mate.tue.nl/mate/pdfs/10318.pdf
-// 'Efficient GPU-Based Texture Interpolation using Uniform B-Splines'
-// Explanation why this algorithm normally always blurs, even with unit scaling:
-// http://bigwww.epfl.ch/preprints/ruijters1001p.pdf
-// 'GPU Prefilter for Accurate Cubic B-spline Interpolation'
-vec4 calcweights(float s) {
- vec4 t = vec4(-0.5, 0.1666, 0.3333, -0.3333) * s + vec4(1, 0, -0.5, 0.5);
- t = t * s + vec4(0, 0, -0.5, 0.5);
- t = t * s + vec4(-0.6666, 0, 0.8333, 0.1666);
- vec2 a = vec2(1, 1) / vec2(t.z, t.w);
- t.xy = t.xy * a + vec2(1, 1);
- t.x = t.x + s;
- t.y = t.y - s;
- return t;
-}
-
-vec4 sample_bicubic_fast(VIDEO_SAMPLER tex, vec2 texsize, vec2 texcoord, float param1) {
- vec2 pt = 1.0 / texsize;
- vec2 fcoord = fract(texcoord * texsize + vec2(0.5, 0.5));
- vec4 parmx = calcweights(fcoord.x);
- vec4 parmy = calcweights(fcoord.y);
- vec4 cdelta;
- cdelta.xz = parmx.RG * vec2(-pt.x, pt.x);
- cdelta.yw = parmy.RG * vec2(-pt.y, pt.y);
- // first y-interpolation
- vec4 ar = texture(tex, texcoord + cdelta.xy);
- vec4 ag = texture(tex, texcoord + cdelta.xw);
- vec4 ab = mix(ag, ar, parmy.b);
- // second y-interpolation
- vec4 br = texture(tex, texcoord + cdelta.zy);
- vec4 bg = texture(tex, texcoord + cdelta.zw);
- vec4 aa = mix(bg, br, parmy.b);
- // x-interpolation
- return mix(aa, ab, parmx.b);
-}
-
-#if HAVE_ARRAYS
-float[2] weights2(sampler2D lookup, float f) {
- vec2 c = texture(lookup, vec2(0.5, f)).RG;
- return float[2](c.r, c.g);
-}
-float[6] weights6(sampler2D lookup, float f) {
- vec4 c1 = texture(lookup, vec2(0.25, f));
- vec4 c2 = texture(lookup, vec2(0.75, f));
- return float[6](c1.r, c1.g, c1.b, c2.r, c2.g, c2.b);
-}
-
-// For N=n*4 with n>1.
-#define WEIGHTS_N(NAME, N) \
- float[N] NAME(sampler2D lookup, float f) { \
- float r[N]; \
- for (int n = 0; n < N / 4; n++) { \
- vec4 c = texture(lookup, \
- vec2(1.0 / (N / 2) + n / float(N / 4), f)); \
- r[n * 4 + 0] = c.r; \
- r[n * 4 + 1] = c.g; \
- r[n * 4 + 2] = c.b; \
- r[n * 4 + 3] = c.a; \
- } \
- return r; \
- }
-
-// The DIR parameter is (0, 1) or (1, 0), and we expect the shader compiler to
-// remove all the redundant multiplications and additions.
-#define SAMPLE_CONVOLUTION_SEP_N(NAME, DIR, N, LUT, WEIGHTS_FUNC) \
- vec4 NAME(VIDEO_SAMPLER tex, vec2 texsize, vec2 texcoord) { \
- vec2 pt = (vec2(1.0) / texsize) * DIR; \
- float fcoord = dot(fract(texcoord * texsize - vec2(0.5)), DIR); \
- vec2 base = texcoord - fcoord * pt - pt * vec2(N / 2 - 1); \
- float weights[N] = WEIGHTS_FUNC(LUT, fcoord); \
- vec4 res = vec4(0); \
- for (int n = 0; n < N; n++) { \
- res += vec4(weights[n]) * texture(tex, base + pt * vec2(n)); \
- } \
- return res; \
- }
-
-#define SAMPLE_CONVOLUTION_N(NAME, N, LUT, WEIGHTS_FUNC) \
- vec4 NAME(VIDEO_SAMPLER tex, vec2 texsize, vec2 texcoord) { \
- vec2 pt = vec2(1.0) / texsize; \
- vec2 fcoord = fract(texcoord * texsize - vec2(0.5)); \
- vec2 base = texcoord - fcoord * pt - pt * vec2(N / 2 - 1); \
- vec4 res = vec4(0); \
- float w_x[N] = WEIGHTS_FUNC(LUT, fcoord.x); \
- float w_y[N] = WEIGHTS_FUNC(LUT, fcoord.y); \
- for (int y = 0; y < N; y++) { \
- vec4 line = vec4(0); \
- for (int x = 0; x < N; x++) \
- line += vec4(w_x[x]) * texture(tex, base + pt * vec2(x, y));\
- res += vec4(w_y[y]) * line; \
- } \
- return res; \
- }
-
-#define SAMPLE_POLAR_HELPER(LUT, R, X, Y) \
- w = texture1D(LUT, length(vec2(X, Y) - fcoord)/R).r; \
- c = texture(tex, base + pt * vec2(X, Y)); \
- wsum += w; \
- res += vec4(w) * c;
-
-#define SAMPLE_POLAR_PRIMARY(LUT, R, X, Y) \
- SAMPLE_POLAR_HELPER(LUT, R, X, Y) \
- lo = min(lo, c); \
- hi = max(hi, c);
-
-#define SAMPLE_CONVOLUTION_POLAR_R(NAME, R, LUT, WEIGHTS_FN, ANTIRING) \
- vec4 NAME(VIDEO_SAMPLER tex, vec2 texsize, vec2 texcoord) { \
- vec2 pt = vec2(1.0) / texsize; \
- vec2 fcoord = fract(texcoord * texsize - vec2(0.5)); \
- vec2 base = texcoord - fcoord * pt; \
- vec4 res = vec4(0.0); \
- vec4 lo = vec4(1.0); \
- vec4 hi = vec4(0.0); \
- float wsum = 0.0; \
- float w; \
- vec4 c; \
- WEIGHTS_FN(LUT); \
- res = res / vec4(wsum); \
- return mix(res, clamp(res, lo, hi), ANTIRING); \
- }
-
-#endif /* HAVE_ARRAYS */
-
-#ifdef DEF_SCALER0
-DEF_SCALER0
-#endif
-#ifdef DEF_SCALER1
-DEF_SCALER1
-#endif
-
-// Unsharp masking
-vec4 sample_sharpen3(VIDEO_SAMPLER tex, vec2 texsize, vec2 texcoord, float param1) {
- vec2 pt = 1.0 / texsize;
- vec2 st = pt * 0.5;
- vec4 p = texture(tex, texcoord);
- vec4 sum = texture(tex, texcoord + st * vec2(+1, +1))
- + texture(tex, texcoord + st * vec2(+1, -1))
- + texture(tex, texcoord + st * vec2(-1, +1))
- + texture(tex, texcoord + st * vec2(-1, -1));
- return p + (p - 0.25 * sum) * param1;
-}
-
-vec4 sample_sharpen5(VIDEO_SAMPLER tex, vec2 texsize, vec2 texcoord, float param1) {
- vec2 pt = 1.0 / texsize;
- vec2 st1 = pt * 1.2;
- vec4 p = texture(tex, texcoord);
- vec4 sum1 = texture(tex, texcoord + st1 * vec2(+1, +1))
- + texture(tex, texcoord + st1 * vec2(+1, -1))
- + texture(tex, texcoord + st1 * vec2(-1, +1))
- + texture(tex, texcoord + st1 * vec2(-1, -1));
- vec2 st2 = pt * 1.5;
- vec4 sum2 = texture(tex, texcoord + st2 * vec2(+1, 0))
- + texture(tex, texcoord + st2 * vec2( 0, +1))
- + texture(tex, texcoord + st2 * vec2(-1, 0))
- + texture(tex, texcoord + st2 * vec2( 0, -1));
- vec4 t = p * 0.859375 + sum2 * -0.1171875 + sum1 * -0.09765625;
- return p + t * param1;
-}
-
-void main() {
- vec2 chr_texcoord = texcoord;
-#ifdef USE_CHROMA_FIX
- chr_texcoord = chr_texcoord * chroma_fix;
-#endif
-#ifdef USE_RECTANGLE
- chr_texcoord = chr_texcoord * chroma_div;
-#else
- // Texture coordinates are [0,1], and chroma plane coordinates are
- // magically rescaled.
-#endif
- chr_texcoord = chr_texcoord + chroma_center_offset;
-#ifndef USE_CONV
-#define USE_CONV 0
-#endif
-#ifndef USE_LINEAR_INTERPOLATION
-#define USE_LINEAR_INTERPOLATION 0
-#endif
-#if USE_LINEAR_INTERPOLATION == 1
- vec4 acolor = mix(
- texture(texture0, texcoord),
- texture(texture1, texcoord),
- inter_coeff);
-#elif USE_CONV == CONV_PLANAR
- vec4 acolor = vec4(SAMPLE(texture0, textures_size[0], texcoord).r,
- SAMPLE_C(texture1, textures_size[1], chr_texcoord).r,
- SAMPLE_C(texture2, textures_size[2], chr_texcoord).r,
- 1.0);
-#elif USE_CONV == CONV_NV12
- vec4 acolor = vec4(SAMPLE(texture0, textures_size[0], texcoord).r,
- SAMPLE_C(texture1, textures_size[1], chr_texcoord).RG,
- 1.0);
-#else
- vec4 acolor = SAMPLE(texture0, textures_size[0], texcoord);
-#endif
-#ifdef USE_COLOR_SWIZZLE
- acolor = acolor. USE_COLOR_SWIZZLE ;
-#endif
-#ifdef USE_ALPHA_PLANE
- acolor.a = SAMPLE(texture3, textures_size[3], texcoord).r;
-#endif
- vec3 color = acolor.rgb;
- float alpha = acolor.a;
-#ifdef USE_INPUT_GAMMA
- // Pre-colormatrix input gamma correction (eg. for MP_IMGFLAG_XYZ)
- color = pow(color, vec3(input_gamma));
-#endif
-#ifdef USE_COLORMATRIX
- // Conversion from Y'CbCr or other spaces to RGB
- color = mat3(colormatrix) * color + colormatrix_c;
-#endif
-#ifdef USE_CONV_GAMMA
- // Post-colormatrix converted gamma correction (eg. for MP_IMGFLAG_XYZ)
- color = pow(color, vec3(conv_gamma));
-#endif
-#ifdef USE_CONST_LUMA
- // Conversion from C'rcY'cC'bc to R'Y'cB' via the BT.2020 CL system:
- // C'bc = (B'-Y'c) / 1.9404 | C'bc <= 0
- // = (B'-Y'c) / 1.5816 | C'bc > 0
- //
- // C'rc = (R'-Y'c) / 1.7184 | C'rc <= 0
- // = (R'-Y'c) / 0.9936 | C'rc > 0
- //
- // as per the BT.2020 specification, table 4. This is a non-linear
- // transformation because (constant) luminance receives non-equal
- // contributions from the three different channels.
- color.br = color.br * mix(vec2(1.5816, 0.9936), vec2(1.9404, 1.7184),
- lessThanEqual(color.br, vec2(0))) + color.gg;
-
- // Expand channels to camera-linear light. This shader currently just
- // assumes everything uses the BT.2020 12-bit gamma function, since the
- // difference between 10 and 12-bit is negligible for anything other than
- // 12-bit content.
- color = bt2020_expand(color);
- // Calculate the green channel from the expanded RYcB
- // The BT.2020 specification says Yc = 0.2627*R + 0.6780*G + 0.0593*B
- color.g = (color.g - 0.2627*color.r - 0.0593*color.b)/0.6780;
- // Re-compand to receive the R'G'B' result, same as other systems
- color = bt2020_compand(color);
-#endif
-#ifdef USE_COLORMATRIX
- // CONST_LUMA involves numbers outside the [0,1] range so we make sure
- // to clip here, after the (possible) USE_CONST_LUMA calculations are done,
- // instead of immediately after the colormatrix conversion.
- color = clamp(color, 0.0, 1.0);
-#endif
- // If we are scaling in linear light (SRGB or 3DLUT option enabled), we
- // expand our source colors before scaling. We distinguish between
- // BT.1886 (typical video files) and sRGB (typical image files).
-#ifdef USE_LINEAR_LIGHT_BT1886
- // This calculation is derived from the BT.1886 recommendation which
- // is itself derived from the curves of typical CRT monitors. It claims
- // that a correct video playback environment should have a pure power
- // curve transfer function (in contrast to the complex BT.709 function)
- // with a gamma value of 2.40, but this includes the typical gamma boost
- // of ~1.2 for dark viewing environments. The figure used here instead
- // (1.961) is therefore a pure power curve but without the boost, which
- // is a very close approximation of the true BT.709 function.
- color = pow(color, vec3(1.961));
-#endif
-#ifdef USE_LINEAR_LIGHT_SRGB
- // This is not needed for most sRGB content since we can use GL_SRGB to
- // directly sample RGB texture in linear light, but for things which are
- // also sRGB but in a different format (such as JPEG's YUV), we need
- // to convert to linear light manually.
- color = srgb_expand(color);
-#endif
-#ifdef USE_SIGMOID
- color = sig_center - log(1.0/(color * sig_scale + sig_offset) - 1.0)/sig_slope;
-#endif
- // Image upscaling happens roughly here
-#ifdef USE_SIGMOID_INV
- // Inverse of USE_SIGMOID
- color = (1.0/(1.0 + exp(sig_slope * (sig_center - color))) - sig_offset) / sig_scale;
-#endif
-#ifdef USE_CMS_MATRIX
- // Convert to the right target gamut first (to BT.709 for sRGB,
- // and to BT.2020 for 3DLUT).
- color = cms_matrix * color;
-#endif
- // Clamp to the target gamut. This clamp is needed because the gamma
- // functions are not well-defined outside this range, which is related to
- // the fact that they're not representable on the target device.
- // TODO: Desaturate colorimetrically; this happens automatically for
- // 3dlut targets but not for sRGB mode. Not sure if this is a requirement.
- color = clamp(color, 0.0, 1.0);
-#ifdef USE_INV_GAMMA
- // User-defined gamma correction factor (via the gamma sub-option)
- color = pow(color, vec3(inv_gamma));
-#endif
-#ifdef USE_3DLUT
- // For the 3DLUT we are arbitrarily using 2.4 as input gamma to reduce
- // the amount of rounding errors, so we pull up to that space first and
- // then pass it through the 3D texture.
- color = pow(color, vec3(1.0/2.4));
- color = texture3D(lut_3d, color).rgb;
-#endif
-#ifdef USE_SRGB
- // Adapt and compand from the linear BT2020 source to the sRGB output
- color = srgb_compand(color);
-#endif
-#ifdef USE_INV_BT1886
- color = pow(color, vec3(1.0/1.961));
-#endif
-#ifdef USE_DITHER
- vec2 dither_pos = gl_FragCoord.xy / dither_size;
-#ifdef USE_TEMPORAL_DITHER
- dither_pos = dither_trafo * dither_pos;
-#endif
- float dither_value = texture(dither, dither_pos).r;
- color = floor(color * dither_quantization + dither_value + dither_center) /
- dither_quantization;
-#endif
-#ifdef USE_ALPHA_BLEND
- color = color * alpha;
-#endif
-#ifdef USE_ALPHA
- out_color = vec4(color, alpha);
-#else
- out_color = vec4(color, 1.0);
-#endif
-}
diff --git a/video/out/gl_w32.c b/video/out/gl_w32.c
index f2b7cf6..ccb7679 100644
--- a/video/out/gl_w32.c
+++ b/video/out/gl_w32.c
@@ -1,17 +1,18 @@
/*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * 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
@@ -24,10 +25,15 @@
#include "w32_common.h"
#include "gl_common.h"
+typedef HRESULT (WINAPI *DwmFlush_t)(void);
+
struct w32_context {
HGLRC context;
HDC hdc;
int flags;
+
+ HINSTANCE dwmapi_dll;
+ DwmFlush_t dwmflush;
};
static bool create_dc(struct MPGLContext *ctx, int flags)
@@ -202,6 +208,11 @@ static void create_ctx(void *ptr)
create_context_w32_gl3(ctx);
if (!w32_ctx->context)
create_context_w32_old(ctx);
+
+ w32_ctx->dwmapi_dll = LoadLibrary(L"Dwmapi.dll");
+ if (w32_ctx->dwmapi_dll)
+ w32_ctx->dwmflush = (DwmFlush_t)GetProcAddress(w32_ctx->dwmapi_dll, "DwmFlush");
+
wglMakeCurrent(w32_ctx->hdc, NULL);
}
@@ -240,6 +251,11 @@ static void releaseGlContext_w32(MPGLContext *ctx)
if (w32_ctx->context)
wglMakeCurrent(w32_ctx->hdc, 0);
vo_w32_run_on_thread(ctx->vo, destroy_gl, ctx);
+
+ w32_ctx->dwmflush = NULL;
+ if (w32_ctx->dwmapi_dll)
+ FreeLibrary(w32_ctx->dwmapi_dll);
+ w32_ctx->dwmapi_dll = NULL;
}
static void swapGlBuffers_w32(MPGLContext *ctx)
@@ -248,6 +264,32 @@ static void swapGlBuffers_w32(MPGLContext *ctx)
SwapBuffers(w32_ctx->hdc);
}
+// opt_dwmflush: 0 - never DwmFlush, 1 - only in windowed mode, 2 - always
+// return: the current (applied if modified) SwapInterval value.
+// DwmFlush waits on DWM vsync similar to SwapBuffers but a bit more noisy.
+// SwapBuffers still needs to be called, but we SwapInterval(0) when DwmFLush is
+// used (will get applied for the following SwapBuffers calls)
+static int DwmFlush_w32(MPGLContext *ctx, int opt_dwmflush,
+ int opt_swapinterval, int current_swapinterval)
+{
+ struct w32_context *w32_ctx = ctx->priv;
+ int new_swapinterval = opt_swapinterval; // default if we don't DwmFLush
+
+ if (w32_ctx->dwmflush &&
+ (opt_dwmflush == 2 || (opt_dwmflush == 1 && !ctx->vo->opts->fullscreen)) &&
+ S_OK == w32_ctx->dwmflush())
+ {
+ new_swapinterval = 0;
+ }
+
+ if ((new_swapinterval != current_swapinterval) && ctx->gl->SwapInterval) {
+ ctx->gl->SwapInterval(new_swapinterval);
+ MP_VERBOSE(ctx->vo, "DwmFlush: set SwapInterval(%d)\n", new_swapinterval);
+ }
+
+ return new_swapinterval;
+}
+
void mpgl_set_backend_w32(MPGLContext *ctx)
{
ctx->priv = talloc_zero(ctx, struct w32_context);
@@ -257,4 +299,5 @@ void mpgl_set_backend_w32(MPGLContext *ctx)
ctx->vo_init = vo_w32_init;
ctx->vo_uninit = vo_w32_uninit;
ctx->vo_control = vo_w32_control;
+ ctx->DwmFlush = DwmFlush_w32;
}
diff --git a/video/out/gl_wayland.c b/video/out/gl_wayland.c
index a1c04b1..0fcec82 100644
--- a/video/out/gl_wayland.c
+++ b/video/out/gl_wayland.c
@@ -196,7 +196,12 @@ static void releaseGlContext_wayland(MPGLContext *ctx)
static void swapGlBuffers_wayland(MPGLContext *ctx)
{
struct vo_wayland_state *wl = ctx->vo->wayland;
+
+ if (!wl->frame.pending)
+ return;
+
eglSwapBuffers(wl->egl_context.egl.dpy, wl->egl_context.egl_surface);
+ wl->frame.pending = false;
}
static int control(struct vo *vo, int *events, int request, void *data)
@@ -210,6 +215,12 @@ static int control(struct vo *vo, int *events, int request, void *data)
return r;
}
+static bool is_active(struct MPGLContext *ctx)
+{
+ struct vo_wayland_state *wl = ctx->vo->wayland;
+ return wl->frame.pending;
+}
+
void mpgl_set_backend_wayland(MPGLContext *ctx)
{
ctx->config_window = config_window_wayland;
@@ -218,4 +229,5 @@ void mpgl_set_backend_wayland(MPGLContext *ctx)
ctx->vo_control = control;
ctx->vo_init = vo_wayland_init;
ctx->vo_uninit = vo_wayland_uninit;
+ ctx->is_active = is_active;
}
diff --git a/video/out/gl_x11.c b/video/out/gl_x11.c
index 8a67e0e..8fb4e59 100644
--- a/video/out/gl_x11.c
+++ b/video/out/gl_x11.c
@@ -1,17 +1,18 @@
/*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * 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
@@ -231,6 +232,7 @@ static bool config_window_x11(struct MPGLContext *ctx, int flags)
MP_ERR(vo, "no GLX support present\n");
return false;
}
+ MP_VERBOSE(vo, "GLX chose FB config with ID 0x%x\n", (int)(intptr_t)fbc);
glx_ctx->fbc = fbc;
glx_ctx->vinfo = glXGetVisualFromFBConfig(vo->x11->display, fbc);
diff --git a/video/out/gl_x11egl.c b/video/out/gl_x11egl.c
index 2bd4def..1f6f328 100644
--- a/video/out/gl_x11egl.c
+++ b/video/out/gl_x11egl.c
@@ -44,7 +44,7 @@ static EGLConfig select_fb_config_egl(struct MPGLContext *ctx, bool es)
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_DEPTH_SIZE, 0,
- EGL_RENDERABLE_TYPE, es ? EGL_OPENGL_ES_BIT : EGL_OPENGL_BIT,
+ EGL_RENDERABLE_TYPE, es ? EGL_OPENGL_ES2_BIT : EGL_OPENGL_BIT,
EGL_NONE
};
@@ -67,7 +67,7 @@ static bool create_context_egl(MPGLContext *ctx, EGLConfig config,
struct priv *p = ctx->priv;
EGLint context_attributes[] = {
- EGL_CONTEXT_MAJOR_VERSION_KHR,
+ EGL_CONTEXT_CLIENT_VERSION, // aka EGL_CONTEXT_MAJOR_VERSION_KHR
es ? 2 : MPGL_VER_GET_MAJOR(ctx->requested_gl_version),
EGL_NONE
};
@@ -103,10 +103,12 @@ static bool config_window_x11_egl_(struct MPGLContext *ctx, int flags, bool es)
return true;
}
- eglBindAPI(es ? EGL_OPENGL_ES_API : EGL_OPENGL_API);
+ if (!eglBindAPI(es ? EGL_OPENGL_ES_API : EGL_OPENGL_API))
+ return false;
p->egl_display = eglGetDisplay(vo->x11->display);
- eglInitialize(p->egl_display, NULL, NULL);
+ if (!eglInitialize(p->egl_display, NULL, NULL))
+ return false;
EGLConfig config = select_fb_config_egl(ctx, es);
if (!config)
diff --git a/video/out/vo.c b/video/out/vo.c
index 55ff0b7..70f6822 100644
--- a/video/out/vo.c
+++ b/video/out/vo.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
@@ -58,53 +57,61 @@ extern const struct vo_driver video_out_null;
extern const struct vo_driver video_out_image;
extern const struct vo_driver video_out_lavc;
extern const struct vo_driver video_out_caca;
+extern const struct vo_driver video_out_drm;
extern const struct vo_driver video_out_direct3d;
extern const struct vo_driver video_out_direct3d_shaders;
extern const struct vo_driver video_out_sdl;
extern const struct vo_driver video_out_vaapi;
extern const struct vo_driver video_out_wayland;
+extern const struct vo_driver video_out_rpi;
const struct vo_driver *const video_out_drivers[] =
{
+#if HAVE_RPI
+ &video_out_rpi,
+#endif
#if HAVE_GL
- &video_out_opengl,
+ &video_out_opengl,
#endif
#if HAVE_VDPAU
- &video_out_vdpau,
+ &video_out_vdpau,
#endif
#if HAVE_DIRECT3D
- &video_out_direct3d_shaders,
- &video_out_direct3d,
+ &video_out_direct3d_shaders,
+ &video_out_direct3d,
#endif
#if HAVE_XV
- &video_out_xv,
+ &video_out_xv,
#endif
#if HAVE_SDL2
- &video_out_sdl,
+ &video_out_sdl,
#endif
#if HAVE_VAAPI
- &video_out_vaapi,
+ &video_out_vaapi,
#endif
#if HAVE_X11
- &video_out_x11,
+ &video_out_x11,
#endif
- &video_out_null,
- // should not be auto-selected
- &video_out_image,
+ &video_out_null,
+ // should not be auto-selected
+ &video_out_image,
#if HAVE_CACA
- &video_out_caca,
+ &video_out_caca,
+#endif
+#if HAVE_DRM
+ &video_out_drm,
#endif
#if HAVE_ENCODING
- &video_out_lavc,
+ &video_out_lavc,
#endif
#if HAVE_GL
- &video_out_opengl_hq,
- &video_out_opengl_cb,
+ &video_out_opengl_hq,
+ &video_out_opengl_cb,
#endif
#if HAVE_WAYLAND
- &video_out_wayland,
+ &video_out_wayland,
#endif
- NULL
+ NULL
};
struct vo_internal {
@@ -146,9 +153,10 @@ struct vo_internal {
int64_t frame_pts; // realtime of intended display
int64_t frame_duration; // realtime frame duration (for framedrop)
- int64_t vsync_interval;
+ double display_fps;
// --- The following fields can be accessed from the VO thread only
+ int64_t vsync_interval;
int64_t vsync_interval_approx;
int64_t last_flip;
char *window_title;
@@ -232,7 +240,6 @@ static struct vo *vo_create(bool probing, struct mpv_global *global,
talloc_steal(vo, log);
*vo->in = (struct vo_internal) {
.dispatch = mp_dispatch_create(vo),
- .internal_events = VO_EVENT_WIN_STATE,
};
mp_make_wakeup_pipe(vo->in->wakeup_pipe);
mp_dispatch_set_wakeup_fn(vo->in->dispatch, dispatch_wakeup_cb, vo);
@@ -312,18 +319,23 @@ static void update_display_fps(struct vo *vo)
pthread_mutex_unlock(&in->lock);
- double display_fps = 1000.0; // assume infinite if unset
+ double display_fps = 0;
if (vo->global->opts->frame_drop_fps > 0) {
display_fps = vo->global->opts->frame_drop_fps;
} else {
vo->driver->control(vo, VOCTRL_GET_DISPLAY_FPS, &display_fps);
}
- int64_t n_interval = MPMAX((int64_t)(1e6 / display_fps), 1);
pthread_mutex_lock(&in->lock);
- if (vo->in->vsync_interval != n_interval)
+
+ if (in->display_fps != display_fps) {
+ in->display_fps = display_fps;
MP_VERBOSE(vo, "Assuming %f FPS for framedrop.\n", display_fps);
- vo->in->vsync_interval = n_interval;
+
+ // make sure to update the player
+ in->queued_events |= VO_EVENT_WIN_STATE;
+ mp_input_wakeup(vo->input_ctx);
+ }
}
pthread_mutex_unlock(&in->lock);
}
@@ -567,6 +579,9 @@ static bool render_frame(struct vo *vo)
pthread_mutex_lock(&in->lock);
+ vo->in->vsync_interval = in->display_fps > 0 ? 1e6 / in->display_fps : 0;
+ vo->in->vsync_interval = MPMAX(vo->in->vsync_interval, 1);
+
int64_t pts = in->frame_pts;
int64_t duration = in->frame_duration;
struct mp_image *img = in->frame_queued;
@@ -593,7 +608,15 @@ static bool render_frame(struct vo *vo)
if (!in->hasframe_rendered)
duration = -1; // disable framedrop
- in->dropped_frame = duration >= 0 && end_time < next_vsync;
+ // if the clip and display have similar/identical fps, it's possible that
+ // we'll be very slightly late frequently due to timing jitter, or if the
+ // clip/container timestamps are not very accurate.
+ // so if we dropped the previous frame, keep dropping until we're aligned
+ // perfectly, else, allow some slack (1 vsync) to let it settle into a rhythm.
+ in->dropped_frame = duration >= 0 &&
+ ((in->dropped_frame && end_time < next_vsync) ||
+ (end_time < prev_vsync)); // hard threshold - 1 vsync late
+
in->dropped_frame &= !(vo->driver->caps & VO_CAP_FRAMEDROP);
in->dropped_frame &= (vo->global->opts->frame_dropping & 1);
// Even if we're hopelessly behind, rather degrade to 10 FPS playback,
@@ -603,7 +626,7 @@ static bool render_frame(struct vo *vo)
if (in->vsync_timed) {
// this is a heuristic that wakes the thread up some
// time before the next vsync
- target = next_vsync - MPMIN(in->vsync_interval / 3, 4e3);
+ target = next_vsync - MPMIN(in->vsync_interval / 2, 8e3);
// We are very late with the frame and using vsync timing: probably
// no new frames are coming in. This must be done whether or not
@@ -731,6 +754,9 @@ static void *vo_thread(void *ptr)
if (r < 0)
return NULL;
+ update_display_fps(vo);
+ vo_event(vo, VO_EVENT_WIN_STATE);
+
while (1) {
mp_dispatch_queue_process(vo->in->dispatch, 0);
if (in->terminate)
@@ -908,6 +934,7 @@ void vo_set_flip_queue_params(struct vo *vo, int64_t offset_us, bool vsync_timed
pthread_mutex_unlock(&in->lock);
}
+// to be called from the VO thread only
int64_t vo_get_vsync_interval(struct vo *vo)
{
struct vo_internal *in = vo->in;
@@ -917,6 +944,15 @@ int64_t vo_get_vsync_interval(struct vo *vo)
return res;
}
+double vo_get_display_fps(struct vo *vo)
+{
+ struct vo_internal *in = vo->in;
+ pthread_mutex_lock(&in->lock);
+ double res = vo->in->display_fps;
+ pthread_mutex_unlock(&in->lock);
+ return res;
+}
+
// Set specific event flags, and wakeup the playback core if needed.
// vo_query_and_reset_events() can retrieve the events again.
void vo_event(struct vo *vo, int event)
@@ -951,10 +987,10 @@ struct mp_image *vo_get_current_frame(struct vo *vo)
return r;
}
-/**
- * \brief lookup an integer in a table, table must have 0 as the last key
- * \param key key to search for
- * \result translation corresponding to key or "to" value of last mapping
+/*
+ * lookup an integer in a table, table must have 0 as the last key
+ * param: key key to search for
+ * returns translation corresponding to key or "to" value of last mapping
* if not found.
*/
int lookup_keymap_table(const struct mp_keymap *map, int key)
diff --git a/video/out/vo.h b/video/out/vo.h
index 55e0da2..36163ac 100644
--- a/video/out/vo.h
+++ b/video/out/vo.h
@@ -1,23 +1,23 @@
/*
* Copyright (C) Aaron Holtzman - Aug 1999
+ *
* Strongly modified, most parts rewritten: A'rpi/ESP-team - 2000-2001
* (C) MPlayer developers
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_VIDEO_OUT_H
@@ -38,6 +38,8 @@
#define VO_EVENT_ICC_PROFILE_CHANGED 4
// Some other window state changed (position, window state, fps)
#define VO_EVENT_WIN_STATE 8
+// The ambient light conditions changed and need to be reloaded
+#define VO_EVENT_AMBIENT_LIGHTING_CHANGED 16
// Set of events the player core may be interested in.
#define VO_EVENTS_USER (VO_EVENT_RESIZE | VO_EVENT_WIN_STATE)
@@ -92,17 +94,13 @@ enum mp_voctrl {
// names for displays the window is on
VOCTRL_GET_DISPLAY_NAMES,
- // The VO is supposed to set "known" fields, and leave the others
- // untouched or set to 0.
- // imgfmt/w/h/d_w/d_h can be omitted for convenience.
- VOCTRL_GET_COLORSPACE, // struct mp_image_params*
-
// Retrieve window contents. (Normal screenshots use vo_get_current_frame().)
VOCTRL_SCREENSHOT_WIN, // struct mp_image**
VOCTRL_SET_COMMAND_LINE, // char**
VOCTRL_GET_ICC_PROFILE, // bstr*
+ VOCTRL_GET_AMBIENT_LUX, // int*
VOCTRL_GET_DISPLAY_FPS, // double*
VOCTRL_GET_RECENT_FLIP_TIME, // int64_t* (using mp_time_us())
@@ -321,6 +319,8 @@ struct mp_image *vo_get_current_frame(struct vo *vo);
void vo_set_flip_queue_params(struct vo *vo, int64_t offset_us, bool vsync_timed);
int64_t vo_get_vsync_interval(struct vo *vo);
+double vo_get_display_fps(struct vo *vo);
+
void vo_wakeup(struct vo *vo);
const char *vo_get_window_title(struct vo *vo);
diff --git a/video/out/vo_caca.c b/video/out/vo_caca.c
index d9e478e..70c3a4f 100644
--- a/video/out/vo_caca.c
+++ b/video/out/vo_caca.c
@@ -7,21 +7,20 @@
*
* TODO: support draw_alpha?
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
@@ -37,7 +36,6 @@
#include "config.h"
#include "vo.h"
#include "video/mp_image.h"
-#include "video/memcpy_pic.h"
#include "input/keycodes.h"
#include "input/input.h"
diff --git a/video/out/vo_direct3d.c b/video/out/vo_direct3d.c
index 82a6363..54a9269 100644
--- a/video/out/vo_direct3d.c
+++ b/video/out/vo_direct3d.c
@@ -1,21 +1,20 @@
/*
* Copyright (c) 2008 Georgi Petrov (gogothebee) <gogothebee@gmail.com>
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <windows.h>
@@ -35,7 +34,6 @@
#include "video/csputils.h"
#include "video/mp_image.h"
#include "video/img_format.h"
-#include "video/memcpy_pic.h"
#include "common/msg.h"
#include "common/common.h"
#include "w32_common.h"
@@ -43,8 +41,8 @@
#include "bitmap_packer.h"
// shaders generated by fxc.exe from d3d_shader_yuv.hlsl
-#include "d3d_shader_yuv.h"
-
+#include "d3d_shader_420p.h"
+#include "d3d_shader_nv12.h"
#define IMGFMT_IS_Y(x) ((x) == IMGFMT_Y8 || (x) == IMGFMT_Y16)
#define IMGFMT_Y_DEPTH(x) ((x) == IMGFMT_Y8 ? 8 : 16)
@@ -103,13 +101,20 @@ struct texplane {
struct osdpart {
enum sub_bitmap_format format;
- int bitmap_id, bitmap_pos_id;
+ int change_id;
struct d3dtex texture;
int num_vertices;
vertex_osd *vertices;
struct bitmap_packer *packer;
};
+enum shaders {
+ SHADER_NONE = 0,
+ SHADER_420P,
+ SHADER_NV12,
+ NUM_SHADERS
+};
+
/* Global variables "priv" structure. I try to keep their count low.
*/
typedef struct d3d_priv {
@@ -144,13 +149,14 @@ typedef struct d3d_priv {
struct mp_image_params params;
bool use_textures; /**< use 3D texture rendering, instead of
StretchRect */
- bool use_shaders; /**< use shader for YUV color conversion
+ int use_shaders; /**< use shader for YUV color conversion, and
+ the SHADER_ id (0 is SHADER_NONE)
(or possibly for RGB video equalizers) */
int plane_count;
struct texplane planes[3];
- IDirect3DPixelShader9 *pixel_shader;
+ IDirect3DPixelShader9 *pixel_shaders[NUM_SHADERS];
D3DFORMAT movie_src_fmt; /**< Movie colorspace format (depends on
the movie's codec) */
@@ -216,6 +222,8 @@ static const struct fmt_entry fmt_table[] = {
// grayscale (can be considered both packed and planar)
{IMGFMT_Y8, D3DFMT_L8},
{IMGFMT_Y16, D3DFMT_L16},
+ // 2 channels (needed for NV12 shader)
+ {IMGFMT_YA8, D3DFMT_A8L8},
{0},
};
@@ -443,7 +451,7 @@ static void destroy_d3d_surfaces(d3d_priv *priv)
for (int n = 0; n < MAX_OSD_PARTS; n++) {
struct osdpart *osd = priv->osd[n];
d3dtex_release(priv, &osd->texture);
- osd->bitmap_id = osd->bitmap_pos_id = -1;
+ osd->change_id = -1;
}
if (priv->d3d_backbuf)
@@ -693,9 +701,11 @@ static void fill_d3d_presentparams(d3d_priv *priv,
// Create a new backbuffer. Create or Reset the D3D device.
static bool change_d3d_backbuffer(d3d_priv *priv)
{
- if (priv->pixel_shader)
- IDirect3DPixelShader9_Release(priv->pixel_shader);
- priv->pixel_shader = NULL;
+ for (int n = 0; n < NUM_SHADERS; n++) {
+ if (priv->pixel_shaders[n])
+ IDirect3DPixelShader9_Release(priv->pixel_shaders[n]);
+ priv->pixel_shaders[n] = NULL;
+ }
int window_w = priv->vo->dwidth;
int window_h = priv->vo->dheight;
@@ -740,12 +750,21 @@ static bool change_d3d_backbuffer(d3d_priv *priv)
present_params.BackBufferWidth, present_params.BackBufferHeight,
window_w, window_h);
- if (FAILED(IDirect3DDevice9_CreatePixelShader(priv->d3d_device,
- (DWORD *)d3d_shader_yuv, &priv->pixel_shader)))
- {
- priv->pixel_shader = NULL;
- if (!priv->opt_disable_shaders)
- MP_WARN(priv, "Shader could not be created - disabling shaders.\n");
+ const DWORD* shaders[NUM_SHADERS] = {
+ [SHADER_420P] = (DWORD *)d3d_shader_420p,
+ [SHADER_NV12] = (DWORD *)d3d_shader_nv12,
+ };
+
+ for (int n = 0; n < NUM_SHADERS; n++) {
+ if (!shaders[n])
+ continue;
+ if (FAILED(IDirect3DDevice9_CreatePixelShader(priv->d3d_device,
+ shaders[n], &priv->pixel_shaders[n])))
+ {
+ priv->pixel_shaders[n] = NULL;
+ if (!priv->opt_disable_shaders)
+ MP_WARN(priv, "Shader could not be created - disabling shaders.\n");
+ }
}
return 1;
@@ -755,9 +774,11 @@ static void destroy_d3d(d3d_priv *priv)
{
destroy_d3d_surfaces(priv);
- if (priv->pixel_shader)
- IDirect3DPixelShader9_Release(priv->pixel_shader);
- priv->pixel_shader = NULL;
+ for (int n = 0; n < NUM_SHADERS; n++) {
+ if (priv->pixel_shaders[n])
+ IDirect3DPixelShader9_Release(priv->pixel_shaders[n]);
+ priv->pixel_shaders[n] = NULL;
+ }
if (priv->d3d_device)
IDirect3DDevice9_Release(priv->d3d_device);
@@ -901,8 +922,9 @@ static uint32_t d3d_draw_frame(d3d_priv *priv)
}
}
- if (priv->pixel_shader) {
- IDirect3DDevice9_SetPixelShader(priv->d3d_device, priv->pixel_shader);
+ if (priv->use_shaders) {
+ IDirect3DDevice9_SetPixelShader(priv->d3d_device,
+ priv->pixel_shaders[priv->use_shaders]);
IDirect3DDevice9_SetPixelShaderConstantF(priv->d3d_device, 0,
&priv->d3d_colormatrix._11,
4);
@@ -1011,24 +1033,36 @@ static D3DFORMAT check_format(d3d_priv *priv, uint32_t movie_fmt,
}
// Check whether YUV conversion with shaders can be done.
-// Returns the format the individual planes should use (or 0 on failure)
-static D3DFORMAT check_shader_conversion(d3d_priv *priv, uint32_t fmt)
+static int check_shader_conversion(d3d_priv *priv, uint32_t fmt,
+ D3DFORMAT shader_d3dfmts[MP_MAX_PLANES])
{
if (priv->opt_disable_shaders)
return 0;
struct mp_imgfmt_desc desc = mp_imgfmt_get_desc(fmt);
- if (!(desc.flags & MP_IMGFLAG_YUV_P) || !(desc.flags & MP_IMGFLAG_NE))
- return 0;
- if (desc.num_planes != 3)
- return 0;
- int component_bits = desc.plane_bits;
- if (component_bits < 8 || component_bits > 16)
- return 0;
- bool is_8bit = component_bits == 8;
- if (!is_8bit && priv->opt_only_8bit)
- return 0;
- int texfmt = is_8bit ? IMGFMT_Y8 : IMGFMT_Y16;
- return check_format(priv, texfmt, true);
+ if ((desc.flags & MP_IMGFLAG_YUV_P) && (desc.flags & MP_IMGFLAG_NE)) {
+ if (desc.num_planes > MP_MAX_PLANES)
+ return 0;
+ int component_bits = desc.plane_bits;
+ if (component_bits < 8 || component_bits > 16)
+ return 0;
+ bool is_8bit = component_bits == 8;
+ if (!is_8bit && priv->opt_only_8bit)
+ return 0;
+ int texfmt = is_8bit ? IMGFMT_Y8 : IMGFMT_Y16;
+ D3DFORMAT d3dfmt = check_format(priv, texfmt, true);
+ if (d3dfmt) {
+ for (int n = 0; n < desc.num_planes; n++)
+ shader_d3dfmts[n] = d3dfmt;
+ return SHADER_420P;
+ }
+ }
+ if (fmt == IMGFMT_NV12) {
+ shader_d3dfmts[0] = check_format(priv, IMGFMT_Y8, true);
+ shader_d3dfmts[1] = check_format(priv, IMGFMT_YA8, true);
+ if (shader_d3dfmts[0] && shader_d3dfmts[1])
+ return SHADER_NV12;
+ }
+ return 0;
}
// Return if the image format can be used. If it can, decide which rendering
@@ -1040,41 +1074,42 @@ static bool init_rendering_mode(d3d_priv *priv, uint32_t fmt, bool initialize)
int n;
int blit_d3dfmt = check_format(priv, fmt, false);
int texture_d3dfmt = check_format(priv, fmt, true);
- int shader_d3dfmt = check_shader_conversion(priv, fmt);
+ D3DFORMAT shader_d3dfmts[MP_MAX_PLANES] = {0};
+ int shader = check_shader_conversion(priv, fmt, shader_d3dfmts);
if (priv->opt_disable_textures)
texture_d3dfmt = 0;
- if (priv->opt_disable_shaders || !priv->pixel_shader)
- shader_d3dfmt = 0;
+ if (priv->opt_disable_shaders || !priv->pixel_shaders[shader])
+ shader = 0;
if (priv->opt_disable_stretchrect)
blit_d3dfmt = 0;
- if (!(blit_d3dfmt || shader_d3dfmt || texture_d3dfmt))
+ if (!(blit_d3dfmt || shader || texture_d3dfmt))
return false;
MP_VERBOSE(priv, "Accepted rendering methods for "
- "format='%s': StretchRect=%#x, Texture=%#x, Texture+Shader=%#x.\n",
- vo_format_name(fmt), blit_d3dfmt, texture_d3dfmt, shader_d3dfmt);
+ "format='%s': StretchRect=%#x, Texture=%#x, Texture+Shader=%d.\n",
+ vo_format_name(fmt), blit_d3dfmt, texture_d3dfmt, shader);
if (!initialize)
return true;
// initialization doesn't fail beyond this point
- priv->use_shaders = false;
+ priv->use_shaders = 0;
priv->use_textures = false;
priv->movie_src_fmt = 0;
priv->plane_count = 0;
priv->image_format = fmt;
if (blit_d3dfmt && priv->opt_prefer_stretchrect)
- texture_d3dfmt = shader_d3dfmt = 0;
+ texture_d3dfmt = shader = 0;
if (texture_d3dfmt) {
priv->use_textures = true;
- } else if (shader_d3dfmt) {
+ } else if (shader) {
priv->use_textures = true;
- priv->use_shaders = true;
+ priv->use_shaders = shader;
} else {
assert(!!blit_d3dfmt);
}
@@ -1095,9 +1130,9 @@ static bool init_rendering_mode(d3d_priv *priv, uint32_t fmt, bool initialize)
MP_VERBOSE(priv, "Using YUV shaders.\n");
struct mp_imgfmt_desc desc = mp_imgfmt_get_desc(priv->image_format);
- priv->plane_count = 3;
- for (n = 0; n < 3; n++) {
- planes[n].d3d_format = shader_d3dfmt;
+ priv->plane_count = desc.num_planes;
+ for (n = 0; n < priv->plane_count; n++) {
+ planes[n].d3d_format = shader_d3dfmts[n];
planes[n].bits_per_pixel = desc.plane_bits;
planes[n].shift_x = desc.xs[n];
planes[n].shift_y = desc.ys[n];
@@ -1227,15 +1262,6 @@ static int control(struct vo *vo, uint32_t request, void *data)
case VOCTRL_REDRAW_FRAME:
d3d_draw_frame(priv);
return VO_TRUE;
- case VOCTRL_GET_COLORSPACE: {
- struct mp_image_params *p = data;
- if (priv->use_shaders) { // no idea what the heck D3D YUV uses
- p->colorspace = priv->params.colorspace;
- p->colorlevels = priv->params.colorlevels;
- p->outputlevels = priv->params.outputlevels;
- }
- return VO_TRUE;
- }
case VOCTRL_SET_EQUALIZER: {
if (!priv->use_shaders)
break;
@@ -1490,7 +1516,7 @@ static mp_image_t *get_window_screenshot(d3d_priv *priv)
if (width < 1 || height < 1)
goto error_exit;
- image = mp_image_alloc(IMGFMT_BGR32, width, height);
+ image = mp_image_alloc(IMGFMT_BGR0, width, height);
if (!image)
goto error_exit;
@@ -1581,14 +1607,11 @@ static struct osdpart *generate_osd(d3d_priv *priv, struct sub_bitmaps *imgs)
struct osdpart *osd = priv->osd[imgs->render_index];
- if (imgs->bitmap_pos_id != osd->bitmap_pos_id) {
- if (imgs->bitmap_id != osd->bitmap_id) {
- if (!upload_osd(priv, osd, imgs))
- osd->packer->count = 0;
- }
+ if (imgs->change_id != osd->change_id) {
+ if (!upload_osd(priv, osd, imgs))
+ osd->packer->count = 0;
- osd->bitmap_id = imgs->bitmap_id;
- osd->bitmap_pos_id = imgs->bitmap_pos_id;
+ osd->change_id = imgs->change_id;
osd->num_vertices = 0;
}
diff --git a/video/out/vo_drm.c b/video/out/vo_drm.c
new file mode 100644
index 0000000..64fdadb
--- /dev/null
+++ b/video/out/vo_drm.c
@@ -0,0 +1,641 @@
+/*
+ * video output driver for libdrm
+ *
+ * by rr- <rr-@sakuya.pl>
+ *
+ * This file is part of mpv.
+ *
+ * mpv is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * mpv is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <sys/mman.h>
+#include <sys/poll.h>
+#include <unistd.h>
+
+#include <libswscale/swscale.h>
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+
+#include "drm_common.h"
+
+#include "common/msg.h"
+#include "osdep/timer.h"
+#include "sub/osd.h"
+#include "video/fmt-conversion.h"
+#include "video/mp_image.h"
+#include "video/sws_utils.h"
+#include "vo.h"
+
+#define USE_MASTER 0
+#define BUF_COUNT 2
+
+struct modeset_buf {
+ uint32_t width;
+ uint32_t height;
+ uint32_t stride;
+ uint32_t size;
+ uint32_t handle;
+ uint8_t *map;
+ uint32_t fb;
+};
+
+struct modeset_dev {
+ struct modeset_buf bufs[BUF_COUNT];
+ drmModeModeInfo mode;
+ drmModeEncoder *enc;
+ uint32_t conn;
+ uint32_t crtc;
+ int front_buf;
+};
+
+struct priv {
+ char *device_path;
+ int connector_id;
+
+ int fd;
+ struct vt_switcher vt_switcher;
+ struct modeset_dev *dev;
+ drmModeCrtc *old_crtc;
+ drmEventContext ev;
+
+ bool active;
+ bool pflip_happening;
+
+ int32_t device_w;
+ int32_t device_h;
+ int32_t x, y;
+ struct mp_image *last_input;
+ struct mp_image *cur_frame;
+ struct mp_rect src;
+ struct mp_rect dst;
+ struct mp_osd_res osd;
+ struct mp_sws_context *sws;
+};
+
+static int modeset_open(struct vo *vo, int *out, const char *node)
+{
+ *out = -1;
+
+ int fd = open(node, O_RDWR | O_CLOEXEC);
+ if (fd < 0) {
+ MP_ERR(vo, "Cannot open \"%s\": %s.\n", node, mp_strerror(errno));
+ return -errno;
+ }
+
+ uint64_t has_dumb;
+ if (drmGetCap(fd, DRM_CAP_DUMB_BUFFER, &has_dumb) < 0) {
+ MP_ERR(vo, "Device \"%s\" does not support dumb buffers.\n", node);
+ return -EOPNOTSUPP;
+ }
+
+ *out = fd;
+ return 0;
+}
+
+static void modeset_destroy_fb(int fd, struct modeset_buf *buf)
+{
+ if (buf->map) {
+ munmap(buf->map, buf->size);
+ }
+ if (buf->fb) {
+ drmModeRmFB(fd, buf->fb);
+ }
+ if (buf->handle) {
+ struct drm_mode_destroy_dumb dreq = {
+ .handle = buf->handle,
+ };
+ drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq);
+ }
+}
+
+static int modeset_create_fb(struct vo *vo, int fd, struct modeset_buf *buf)
+{
+ int ret = 0;
+
+ buf->handle = 0;
+
+ // create dumb buffer
+ struct drm_mode_create_dumb creq = {
+ .width = buf->width,
+ .height = buf->height,
+ .bpp = 32,
+ };
+ ret = drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &creq);
+ if (ret < 0) {
+ MP_ERR(vo, "Cannot create dumb buffer: %s\n", mp_strerror(errno));
+ ret = -errno;
+ goto end;
+ }
+ buf->stride = creq.pitch;
+ buf->size = creq.size;
+ buf->handle = creq.handle;
+
+ // create framebuffer object for the dumb-buffer
+ ret = drmModeAddFB(fd, buf->width, buf->height, 24, 32, buf->stride,
+ buf->handle, &buf->fb);
+ if (ret) {
+ MP_ERR(vo, "Cannot create framebuffer: %s\n", mp_strerror(errno));
+ ret = -errno;
+ goto end;
+ }
+
+ // prepare buffer for memory mapping
+ struct drm_mode_map_dumb mreq = {
+ .handle = buf->handle,
+ };
+ ret = drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &mreq);
+ if (ret) {
+ MP_ERR(vo, "Cannot map dumb buffer: %s\n", mp_strerror(errno));
+ ret = -errno;
+ goto end;
+ }
+
+ // perform actual memory mapping
+ buf->map = mmap(0, buf->size, PROT_READ | PROT_WRITE, MAP_SHARED,
+ fd, mreq.offset);
+ if (buf->map == MAP_FAILED) {
+ MP_ERR(vo, "Cannot map dumb buffer: %s\n", mp_strerror(errno));
+ ret = -errno;
+ goto end;
+ }
+
+ memset(buf->map, 0, buf->size);
+
+end:
+ if (ret == 0) {
+ return 0;
+ }
+
+ modeset_destroy_fb(fd, buf);
+ return ret;
+}
+
+static int modeset_find_crtc(struct vo *vo, int fd, drmModeRes *res,
+ drmModeConnector *conn, struct modeset_dev *dev)
+{
+ for (unsigned int i = 0; i < conn->count_encoders; ++i) {
+ drmModeEncoder *enc = drmModeGetEncoder(fd, conn->encoders[i]);
+ if (!enc) {
+ MP_WARN(vo, "Cannot retrieve encoder %u:%u: %s\n",
+ i, conn->encoders[i], mp_strerror(errno));
+ continue;
+ }
+
+ // iterate all global CRTCs
+ for (unsigned int j = 0; j < res->count_crtcs; ++j) {
+ // check whether this CRTC works with the encoder
+ if (!(enc->possible_crtcs & (1 << j)))
+ continue;
+
+ dev->enc = enc;
+ dev->crtc = enc->crtc_id;
+ return 0;
+ }
+
+ drmModeFreeEncoder(enc);
+ }
+
+ MP_ERR(vo, "Connector %u has no suitable CRTC\n", conn->connector_id);
+ return -ENOENT;
+}
+
+static bool is_connector_valid(struct vo *vo, int conn_id,
+ drmModeConnector *conn, bool silent)
+{
+ if (!conn) {
+ if (!silent) {
+ MP_ERR(vo, "Cannot get connector %d: %s\n", conn_id,
+ mp_strerror(errno));
+ }
+ return false;
+ }
+
+ if (conn->connection != DRM_MODE_CONNECTED) {
+ if (!silent) {
+ MP_ERR(vo, "Connector %d is disconnected\n", conn_id);
+ }
+ return false;
+ }
+
+ if (conn->count_modes == 0) {
+ if (!silent) {
+ MP_ERR(vo, "Connector %d has no valid modes\n", conn_id);
+ }
+ return false;
+ }
+
+ return true;
+}
+
+static int modeset_prepare_dev(struct vo *vo, int fd, int conn_id,
+ struct modeset_dev **out)
+{
+ struct modeset_dev *dev = NULL;
+ drmModeConnector *conn = NULL;
+
+ int ret = 0;
+ *out = NULL;
+
+ drmModeRes *res = drmModeGetResources(fd);
+ if (!res) {
+ MP_ERR(vo, "Cannot retrieve DRM resources: %s\n", mp_strerror(errno));
+ ret = -errno;
+ goto end;
+ }
+
+ if (conn_id == -1) {
+ // get the first connected connector
+ for (int i = 0; i < res->count_connectors; i++) {
+ conn = drmModeGetConnector(fd, res->connectors[i]);
+ if (is_connector_valid(vo, i, conn, true)) {
+ conn_id = i;
+ break;
+ }
+ if (conn) {
+ drmModeFreeConnector(conn);
+ conn = NULL;
+ }
+ }
+ if (conn_id == -1) {
+ MP_ERR(vo, "No connected connectors found\n");
+ ret = -ENODEV;
+ goto end;
+ }
+ }
+
+ if (conn_id < 0 || conn_id >= res->count_connectors) {
+ MP_ERR(vo, "Bad connector ID. Max valid connector ID = %u\n",
+ res->count_connectors);
+ ret = -ENODEV;
+ goto end;
+ }
+
+ conn = drmModeGetConnector(fd, res->connectors[conn_id]);
+ if (!is_connector_valid(vo, conn_id, conn, false)) {
+ ret = -ENODEV;
+ goto end;
+ }
+
+ dev = talloc_zero(vo->priv, struct modeset_dev);
+ dev->conn = conn->connector_id;
+ dev->front_buf = 0;
+ dev->mode = conn->modes[0];
+ dev->bufs[0].width = conn->modes[0].hdisplay;
+ dev->bufs[0].height = conn->modes[0].vdisplay;
+ dev->bufs[1].width = conn->modes[0].hdisplay;
+ dev->bufs[1].height = conn->modes[0].vdisplay;
+
+ MP_INFO(vo, "Connector using mode %ux%u\n",
+ dev->bufs[0].width, dev->bufs[0].height);
+
+ ret = modeset_find_crtc(vo, fd, res, conn, dev);
+ if (ret) {
+ MP_ERR(vo, "Connector %d has no valid CRTC\n", conn_id);
+ goto end;
+ }
+
+ for (unsigned int i = 0; i < BUF_COUNT; i++) {
+ ret = modeset_create_fb(vo, fd, &dev->bufs[i]);
+ if (ret) {
+ MP_ERR(vo, "Cannot create framebuffer for connector %d\n",
+ conn_id);
+ for (unsigned int j = 0; j < i; j++) {
+ modeset_destroy_fb(fd, &dev->bufs[j]);
+ }
+ goto end;
+ }
+ }
+
+end:
+ if (conn) {
+ drmModeFreeConnector(conn);
+ conn = NULL;
+ }
+ if (res) {
+ drmModeFreeResources(res);
+ res = NULL;
+ }
+ if (ret == 0) {
+ *out = dev;
+ } else {
+ talloc_free(dev);
+ }
+ return ret;
+}
+
+static void modeset_page_flipped(int fd, unsigned int frame, unsigned int sec,
+ unsigned int usec, void *data)
+{
+ struct priv *p = data;
+ p->pflip_happening = false;
+}
+
+
+
+static int setup_vo_crtc(struct vo *vo)
+{
+ struct priv *p = vo->priv;
+ if (p->active)
+ return 0;
+ p->old_crtc = drmModeGetCrtc(p->fd, p->dev->crtc);
+ int ret = drmModeSetCrtc(p->fd, p->dev->crtc,
+ p->dev->bufs[p->dev->front_buf + BUF_COUNT - 1].fb,
+ 0, 0, &p->dev->conn, 1, &p->dev->mode);
+ p->active = true;
+ return ret;
+}
+
+static void release_vo_crtc(struct vo *vo)
+{
+ struct priv *p = vo->priv;
+
+ if (!p->active)
+ return;
+ p->active = false;
+
+ // wait for current page flip
+ while (p->pflip_happening) {
+ int ret = drmHandleEvent(p->fd, &p->ev);
+ if (ret) {
+ MP_ERR(vo, "drmHandleEvent failed: %i\n", ret);
+ break;
+ }
+ }
+
+ if (p->old_crtc) {
+ drmModeSetCrtc(p->fd,
+ p->old_crtc->crtc_id,
+ p->old_crtc->buffer_id,
+ p->old_crtc->x,
+ p->old_crtc->y,
+ &p->dev->conn,
+ 1,
+ &p->dev->mode);
+ drmModeFreeCrtc(p->old_crtc);
+ p->old_crtc = NULL;
+ }
+}
+
+static void release_vt(void *data)
+{
+ struct vo *vo = data;
+ release_vo_crtc(vo);
+ if (USE_MASTER) {
+ //this function enables support for switching to x, weston etc.
+ //however, for whatever reason, it can be called only by root users.
+ //until things change, this is commented.
+ struct priv *p = vo->priv;
+ if (drmDropMaster(p->fd)) {
+ MP_WARN(vo, "Failed to drop DRM master: %s\n", mp_strerror(errno));
+ }
+ }
+}
+
+static void acquire_vt(void *data)
+{
+ struct vo *vo = data;
+ if (USE_MASTER) {
+ struct priv *p = vo->priv;
+ if (drmSetMaster(p->fd)) {
+ MP_WARN(vo, "Failed to acquire DRM master: %s\n", mp_strerror(errno));
+ }
+ }
+
+ setup_vo_crtc(vo);
+}
+
+
+
+static int wait_events(struct vo *vo, int64_t until_time_us)
+{
+ struct priv *p = vo->priv;
+ int64_t wait_us = until_time_us - mp_time_us();
+ int timeout_ms = MPCLAMP((wait_us + 500) / 1000, 0, 10000);
+ vt_switcher_poll(&p->vt_switcher, timeout_ms);
+ return 0;
+}
+
+static void wakeup(struct vo *vo)
+{
+ struct priv *p = vo->priv;
+ vt_switcher_interrupt_poll(&p->vt_switcher);
+}
+
+static int reconfig(struct vo *vo, struct mp_image_params *params, int flags)
+{
+ struct priv *p = vo->priv;
+
+ vo->dwidth = p->device_w;
+ vo->dheight = p->device_h;
+ vo_get_src_dst_rects(vo, &p->src, &p->dst, &p->osd);
+
+ int32_t w = p->dst.x1 - p->dst.x0;
+ int32_t h = p->dst.y1 - p->dst.y0;
+
+ // p->osd contains the parameters assuming OSD rendering in window
+ // coordinates, but OSD can only be rendered in the intersection
+ // between window and video rectangle (i.e. not into panscan borders).
+ p->osd.w = w;
+ p->osd.h = h;
+ p->osd.mt = MPMIN(0, p->osd.mt);
+ p->osd.mb = MPMIN(0, p->osd.mb);
+ p->osd.mr = MPMIN(0, p->osd.mr);
+ p->osd.ml = MPMIN(0, p->osd.ml);
+
+ p->x = (p->device_w - w) >> 1;
+ p->y = (p->device_h - h) >> 1;
+
+ mp_sws_set_from_cmdline(p->sws, vo->opts->sws_opts);
+ p->sws->src = *params;
+ p->sws->dst = (struct mp_image_params) {
+ .imgfmt = IMGFMT_BGR0,
+ .w = w,
+ .h = h,
+ .d_w = w,
+ .d_h = h,
+ };
+
+ talloc_free(p->cur_frame);
+ p->cur_frame = mp_image_alloc(IMGFMT_BGR0, p->device_w, p->device_h);
+ mp_image_params_guess_csp(&p->sws->dst);
+ mp_image_set_params(p->cur_frame, &p->sws->dst);
+
+ if (mp_sws_reinit(p->sws) < 0)
+ return -1;
+
+ vo->want_redraw = true;
+ return 0;
+}
+
+static void draw_image(struct vo *vo, mp_image_t *mpi)
+{
+ struct priv *p = vo->priv;
+
+ if (p->active) {
+ struct mp_rect src_rc = p->src;
+ src_rc.x0 = MP_ALIGN_DOWN(src_rc.x0, mpi->fmt.align_x);
+ src_rc.y0 = MP_ALIGN_DOWN(src_rc.y0, mpi->fmt.align_y);
+ mp_image_crop_rc(mpi, src_rc);
+ mp_sws_scale(p->sws, p->cur_frame, mpi);
+ osd_draw_on_image(vo->osd, p->osd, mpi ? mpi->pts : 0, 0, p->cur_frame);
+
+ struct modeset_buf *front_buf = &p->dev->bufs[p->dev->front_buf];
+ int32_t shift = (p->device_w * p->y + p->x) * 4;
+ memcpy_pic(front_buf->map + shift,
+ p->cur_frame->planes[0],
+ (p->dst.x1 - p->dst.x0) * 4,
+ p->dst.y1 - p->dst.y0,
+ p->device_w * 4,
+ p->cur_frame->stride[0]);
+ }
+
+ if (mpi != p->last_input) {
+ talloc_free(p->last_input);
+ p->last_input = mpi;
+ }
+}
+
+static void flip_page(struct vo *vo)
+{
+ struct priv *p = vo->priv;
+ if (!p->active || p->pflip_happening)
+ return;
+
+ int ret = drmModePageFlip(p->fd, p->dev->crtc,
+ p->dev->bufs[p->dev->front_buf].fb,
+ DRM_MODE_PAGE_FLIP_EVENT, p);
+ if (ret) {
+ MP_WARN(vo, "Cannot flip page for connector\n");
+ } else {
+ p->dev->front_buf++;
+ p->dev->front_buf %= BUF_COUNT;
+ p->pflip_happening = true;
+ }
+
+ // poll page flip finish event
+ const int timeout_ms = 3000;
+ struct pollfd fds[1] = {
+ { .events = POLLIN, .fd = p->fd },
+ };
+ poll(fds, 1, timeout_ms);
+ if (fds[0].revents & POLLIN) {
+ ret = drmHandleEvent(p->fd, &p->ev);
+ if (ret != 0) {
+ MP_ERR(vo, "drmHandleEvent failed: %i\n", ret);
+ return;
+ }
+ }
+}
+
+static void uninit(struct vo *vo)
+{
+ struct priv *p = vo->priv;
+
+ if (p->dev) {
+ release_vo_crtc(vo);
+
+ modeset_destroy_fb(p->fd, &p->dev->bufs[1]);
+ modeset_destroy_fb(p->fd, &p->dev->bufs[0]);
+ drmModeFreeEncoder(p->dev->enc);
+ }
+
+ vt_switcher_destroy(&p->vt_switcher);
+ talloc_free(p->last_input);
+ talloc_free(p->cur_frame);
+ talloc_free(p->dev);
+ close(p->fd);
+}
+
+static int preinit(struct vo *vo)
+{
+ struct priv *p = vo->priv;
+ p->sws = mp_sws_alloc(vo);
+ p->fd = -1;
+ p->ev.version = DRM_EVENT_CONTEXT_VERSION;
+ p->ev.page_flip_handler = modeset_page_flipped;
+
+ if (vt_switcher_init(&p->vt_switcher, vo->log))
+ goto err;
+
+ vt_switcher_acquire(&p->vt_switcher, acquire_vt, vo);
+ vt_switcher_release(&p->vt_switcher, release_vt, vo);
+
+ if (modeset_open(vo, &p->fd, p->device_path))
+ goto err;
+
+ if (modeset_prepare_dev(vo, p->fd, p->connector_id, &p->dev))
+ goto err;
+
+ assert(p->dev);
+ p->device_w = p->dev->bufs[0].width;
+ p->device_h = p->dev->bufs[0].height;
+
+ if (setup_vo_crtc(vo)) {
+ MP_ERR(vo, "Cannot set CRTC for connector %u: %s\n", p->connector_id,
+ mp_strerror(errno));
+ goto err;
+ }
+
+ return 0;
+
+err:
+ uninit(vo);
+ return -1;
+}
+
+static int query_format(struct vo *vo, int format)
+{
+ return sws_isSupportedInput(imgfmt2pixfmt(format));
+}
+
+static int control(struct vo *vo, uint32_t request, void *data)
+{
+ struct priv *p = vo->priv;
+ switch (request) {
+ case VOCTRL_REDRAW_FRAME:
+ draw_image(vo, p->last_input);
+ return VO_TRUE;
+ }
+ return VO_NOTIMPL;
+}
+
+#define OPT_BASE_STRUCT struct priv
+
+const struct vo_driver video_out_drm = {
+ .name = "drm",
+ .description = "Direct Rendering Manager",
+ .preinit = preinit,
+ .query_format = query_format,
+ .reconfig = reconfig,
+ .control = control,
+ .draw_image = draw_image,
+ .flip_page = flip_page,
+ .uninit = uninit,
+ .wait_events = wait_events,
+ .wakeup = wakeup,
+ .priv_size = sizeof(struct priv),
+ .options = (const struct m_option[]) {
+ OPT_STRING("devpath", device_path, 0),
+ OPT_INT("connector", connector_id, 0),
+ {0},
+ },
+ .priv_defaults = &(const struct priv) {
+ .device_path = "/dev/dri/card0",
+ .connector_id = -1,
+ },
+};
diff --git a/video/out/vo_image.c b/video/out/vo_image.c
index 00f7bd6..4a87d82 100644
--- a/video/out/vo_image.c
+++ b/video/out/vo_image.c
@@ -1,18 +1,18 @@
/*
- * This file is part of mplayer.
+ * This file is part of mpv.
*
- * mplayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * mplayer is distributed in the hope that it will be useful,
+ * 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 mplayer. If not, see <http://www.gnu.org/licenses/>.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
diff --git a/video/out/vo_lavc.c b/video/out/vo_lavc.c
index 032f58c..3a6903b 100644
--- a/video/out/vo_lavc.c
+++ b/video/out/vo_lavc.c
@@ -1,23 +1,23 @@
/*
* video encoding using libavformat
+ *
* Copyright (C) 2010 Nicolas George <george@nsup.org>
* Copyright (C) 2011-2012 Rudolf Polzer <divVerent@xonotic.org>
*
* This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
@@ -54,8 +54,6 @@ struct priv {
AVRational worst_time_base;
int worst_time_base_is_stream;
- struct mp_image_params real_colorspace;
-
bool shutdown;
};
@@ -167,11 +165,6 @@ static int reconfig(struct vo *vo, struct mp_image_params *params, int flags)
if (encode_lavc_open_codec(vo->encode_lavc_ctx, vc->stream) < 0)
goto error;
- vc->real_colorspace.colorspace =
- encode_lavc_get_csp(vo->encode_lavc_ctx, vc->stream);
- vc->real_colorspace.colorlevels =
- encode_lavc_get_csp_levels(vo->encode_lavc_ctx, vc->stream);
-
vc->buffer_size = 6 * width * height + 200;
if (vc->buffer_size < FF_MIN_BUFFER_SIZE)
vc->buffer_size = FF_MIN_BUFFER_SIZE;
@@ -516,17 +509,7 @@ static void flip_page(struct vo *vo)
static int control(struct vo *vo, uint32_t request, void *data)
{
- struct priv *vc = vo->priv;
- int r = VO_NOTIMPL;
- pthread_mutex_lock(&vo->encode_lavc_ctx->lock);
- switch (request) {
- case VOCTRL_GET_COLORSPACE:
- *(struct mp_image_params *)data = vc->real_colorspace;
- r = 1;
- break;
- }
- pthread_mutex_unlock(&vo->encode_lavc_ctx->lock);
- return r;
+ return VO_NOTIMPL;
}
const struct vo_driver video_out_lavc = {
diff --git a/video/out/vo_null.c b/video/out/vo_null.c
index da9de61..6455210 100644
--- a/video/out/vo_null.c
+++ b/video/out/vo_null.c
@@ -3,21 +3,20 @@
*
* Copyright (C) Aaron Holtzman - June 2000
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
diff --git a/video/out/vo_opengl.c b/video/out/vo_opengl.c
index c580d0b..e409eaa 100644
--- a/video/out/vo_opengl.c
+++ b/video/out/vo_opengl.c
@@ -1,21 +1,20 @@
/*
- * This file is part of MPlayer.
- *
* Based on vo_gl.c by Reimar Doeffinger.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * 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
@@ -48,7 +47,6 @@
#include "gl_hwdec.h"
#include "gl_osd.h"
#include "filter_kernels.h"
-#include "video/memcpy_pic.h"
#include "video/hwdec.h"
#include "gl_video.h"
#include "gl_lcms.h"
@@ -73,6 +71,9 @@ struct gl_priv {
int use_gl_debug;
int allow_sw;
int swap_interval;
+ int current_swap_interval;
+ int dwm_flush;
+
char *backend;
int vo_flipped;
@@ -93,12 +94,11 @@ static void resize(struct gl_priv *p)
MP_VERBOSE(vo, "Resize: %dx%d\n", vo->dwidth, vo->dheight);
- struct mp_rect wnd = {0, 0, vo->dwidth, vo->dheight};
struct mp_rect src, dst;
struct mp_osd_res osd;
vo_get_src_dst_rects(vo, &src, &dst, &osd);
- gl_video_resize(p->renderer, &wnd, &src, &dst, &osd, false);
+ gl_video_resize(p->renderer, vo->dwidth, -vo->dheight, &src, &dst, &osd);
vo->want_redraw = true;
}
@@ -124,6 +124,9 @@ static void flip_page(struct vo *vo)
struct gl_priv *p = vo->priv;
GL *gl = p->gl;
+ if (p->glctx->is_active && !p->glctx->is_active(p->glctx))
+ return;
+
mpgl_lock(p->glctx);
p->glctx->swapGlBuffers(p->glctx);
@@ -153,6 +156,12 @@ static void flip_page(struct vo *vo)
}
}
+ if (p->glctx->DwmFlush) {
+ p->current_swap_interval = p->glctx->DwmFlush(p->glctx, p->dwm_flush,
+ p->swap_interval,
+ p->current_swap_interval);
+ }
+
mpgl_unlock(p->glctx);
}
@@ -162,6 +171,12 @@ static void draw_image_timed(struct vo *vo, mp_image_t *mpi,
struct gl_priv *p = vo->priv;
GL *gl = p->gl;
+ if (p->glctx->is_active && !p->glctx->is_active(p->glctx)) {
+ if (mpi)
+ gl_video_skip_image(p->renderer, mpi);
+ return;
+ }
+
if (p->vo_flipped)
mp_image_vflip(mpi);
@@ -198,7 +213,7 @@ static int query_format(struct vo *vo, int format)
static void video_resize_redraw_callback(struct vo *vo, int w, int h)
{
struct gl_priv *p = vo->priv;
- gl_video_resize_redraw(p->renderer, w, h);
+ gl_video_resize_redraw(p->renderer, w, -h);
}
@@ -278,6 +293,19 @@ static bool get_and_update_icc_profile(struct gl_priv *p, int *events)
return true;
}
+static void get_and_update_ambient_lighting(struct gl_priv *p, int *events)
+{
+ int lux;
+ int r = p->glctx->vo_control(p->vo, events, VOCTRL_GET_AMBIENT_LUX, &lux);
+ if (r == VO_TRUE) {
+ gl_video_set_ambient_lux(p->renderer, lux);
+ }
+ if (r != VO_TRUE && p->renderer_opts->gamma_auto) {
+ MP_ERR(p, "gamma_auto option provided, but querying for ambient"
+ " lighting is not supported on this platform\n");
+ }
+}
+
static bool reparse_cmdline(struct gl_priv *p, char *args)
{
struct m_config *cfg = NULL;
@@ -303,8 +331,9 @@ static bool reparse_cmdline(struct gl_priv *p, char *args)
if (r >= 0) {
mpgl_lock(p->glctx);
- gl_video_set_options(p->renderer, opts->renderer_opts);
- vo_set_flip_queue_params(p->vo, 0, opts->renderer_opts->smoothmotion);
+ int queue = 0;
+ gl_video_set_options(p->renderer, opts->renderer_opts, &queue);
+ vo_set_flip_queue_params(p->vo, queue, opts->renderer_opts->interpolation);
p->vo->want_redraw = true;
mpgl_unlock(p->glctx);
}
@@ -345,11 +374,6 @@ static int control(struct vo *vo, uint32_t request, void *data)
vo->want_redraw = true;
return r ? VO_TRUE : VO_NOTIMPL;
}
- case VOCTRL_GET_COLORSPACE:
- mpgl_lock(p->glctx);
- gl_video_get_colorspace(p->renderer, data);
- mpgl_unlock(p->glctx);
- return VO_TRUE;
case VOCTRL_SCREENSHOT_WIN:
mpgl_lock(p->glctx);
*(struct mp_image **)data = glGetWindowScreenshot(p->gl);
@@ -364,6 +388,9 @@ static int control(struct vo *vo, uint32_t request, void *data)
request_hwdec_api(p, data);
return true;
case VOCTRL_REDRAW_FRAME:
+ if (p->glctx->is_active && !p->glctx->is_active(p->glctx))
+ return true;
+
mpgl_lock(p->glctx);
gl_video_render_frame(p->renderer, 0, NULL);
mpgl_unlock(p->glctx);
@@ -392,6 +419,10 @@ static int control(struct vo *vo, uint32_t request, void *data)
get_and_update_icc_profile(p, &events);
vo->want_redraw = true;
}
+ if (events & VO_EVENT_AMBIENT_LIGHTING_CHANGED) {
+ get_and_update_ambient_lighting(p, &events);
+ vo->want_redraw = true;
+ }
if (events & VO_EVENT_RESIZE)
resize(p);
if (events & VO_EVENT_EXPOSE)
@@ -435,16 +466,22 @@ static int preinit(struct vo *vo)
mpgl_lock(p->glctx);
- if (p->gl->SwapInterval)
+ if (p->gl->SwapInterval) {
p->gl->SwapInterval(p->swap_interval);
+ } else {
+ MP_VERBOSE(vo, "swap_control extension missing.\n");
+ }
+ p->current_swap_interval = p->swap_interval;
- p->renderer = gl_video_init(p->gl, vo->log, vo->osd);
+ p->renderer = gl_video_init(p->gl, vo->log);
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);
- vo_set_flip_queue_params(vo, 0, p->renderer_opts->smoothmotion);
+ int queue = 0;
+ gl_video_set_options(p->renderer, p->renderer_opts, &queue);
+ vo_set_flip_queue_params(p->vo, queue, p->renderer_opts->interpolation);
p->cms = gl_lcms_init(p, vo->log, vo->global);
if (!p->cms)
@@ -470,6 +507,8 @@ static const struct m_option options[] = {
OPT_FLAG("glfinish", use_glFinish, 0),
OPT_FLAG("waitvsync", waitvsync, 0),
OPT_INT("swapinterval", swap_interval, 0, OPTDEF_INT(1)),
+ OPT_CHOICE("dwmflush", dwm_flush, 0,
+ ({"no", 0}, {"windowed", 1}, {"yes", 2})),
OPT_FLAG("debug", use_gl_debug, 0),
OPT_STRING_VALIDATE("backend", backend, 0, mpgl_validate_backend_opt),
OPT_FLAG("sw", allow_sw, 0),
diff --git a/video/out/vo_opengl_cb.c b/video/out/vo_opengl_cb.c
index cd04f52..2f2ac63 100644
--- a/video/out/vo_opengl_cb.c
+++ b/video/out/vo_opengl_cb.c
@@ -19,6 +19,7 @@
#include "vo.h"
#include "video/mp_image.h"
#include "sub/osd.h"
+#include "osdep/timer.h"
#include "common/global.h"
#include "player/client.h"
@@ -66,7 +67,7 @@ struct mpv_opengl_cb_context {
int queued_frames;
struct mp_image_params img_params;
bool reconfigured;
- struct mp_rect wnd;
+ int vp_w, vp_h;
bool flip;
bool force_update;
bool imgfmt_supported[IMGFMT_END - IMGFMT_START];
@@ -76,6 +77,7 @@ struct mpv_opengl_cb_context {
struct m_config *new_opts_cfg;
bool eq_changed;
struct mp_csp_equalizer eq;
+ int64_t recent_flip;
// --- All of these can only be accessed from the thread where the host
// application's OpenGL context is current - i.e. only while the
@@ -87,7 +89,6 @@ struct mpv_opengl_cb_context {
// --- Immutable or semi-threadsafe.
- struct osd_state *osd;
const char *hwapi;
struct vo *active;
@@ -162,7 +163,6 @@ static void free_ctx(void *ptr)
}
struct mpv_opengl_cb_context *mp_opengl_create(struct mpv_global *g,
- struct osd_state *osd,
struct mp_client_api *client_api)
{
mpv_opengl_cb_context *ctx = talloc_zero(NULL, mpv_opengl_cb_context);
@@ -172,7 +172,6 @@ struct mpv_opengl_cb_context *mp_opengl_create(struct mpv_global *g,
ctx->gl = talloc_zero(ctx, GL);
ctx->log = mp_log_new(ctx, g->log, "opengl-cb");
- ctx->osd = osd;
ctx->client_api = client_api;
switch (g->opts->hwdec_api) {
@@ -220,7 +219,7 @@ int mpv_opengl_cb_init_gl(struct mpv_opengl_cb_context *ctx, const char *exts,
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->osd);
+ ctx->renderer = gl_video_init(ctx->gl, ctx->log);
if (!ctx->renderer)
return MPV_ERROR_UNSUPPORTED;
@@ -270,7 +269,7 @@ int mpv_opengl_cb_uninit_gl(struct mpv_opengl_cb_context *ctx)
return 0;
}
-int mpv_opengl_cb_render(struct mpv_opengl_cb_context *ctx, int fbo, int vp[4])
+int mpv_opengl_cb_draw(mpv_opengl_cb_context *ctx, int fbo, int vp_w, int vp_h)
{
assert(ctx->renderer);
@@ -282,37 +281,32 @@ int mpv_opengl_cb_render(struct mpv_opengl_cb_context *ctx, int fbo, int vp[4])
ctx->force_update |= ctx->reconfigured;
- int h = vp[3];
- bool flip = h < 0 && h > INT_MIN;
- if (flip)
- h = -h;
- struct mp_rect wnd = {vp[0], vp[1], vp[0] + vp[2], vp[1] + h};
- if (wnd.x0 != ctx->wnd.x0 || wnd.y0 != ctx->wnd.y0 ||
- wnd.x1 != ctx->wnd.x1 || wnd.y1 != ctx->wnd.y1 ||
- ctx->flip != flip)
+ if (ctx->vp_w != vp_w || ctx->vp_h != vp_h)
ctx->force_update = true;
if (ctx->force_update && vo) {
ctx->force_update = false;
- ctx->wnd = wnd;
- ctx->flip = flip;
+ ctx->vp_w = vp_w;
+ ctx->vp_h = vp_h;
struct mp_rect src, dst;
struct mp_osd_res osd;
mp_get_src_dst_rects(ctx->log, &ctx->vo_opts, vo->driver->caps,
- &ctx->img_params, wnd.x1 - wnd.x0, wnd.y1 - wnd.y0,
+ &ctx->img_params, vp_w, abs(vp_h),
1.0, &src, &dst, &osd);
- gl_video_resize(ctx->renderer, &wnd, &src, &dst, &osd, !ctx->flip);
+ gl_video_resize(ctx->renderer, vp_w, vp_h, &src, &dst, &osd);
}
- if (ctx->reconfigured)
+ if (ctx->reconfigured) {
+ gl_video_set_osd_source(ctx->renderer, vo ? vo->osd : NULL);
gl_video_config(ctx->renderer, &ctx->img_params);
+ }
if (ctx->update_new_opts) {
struct vo_priv *p = vo ? vo->priv : NULL;
struct vo_priv *opts = ctx->new_opts ? ctx->new_opts : p;
if (opts) {
- gl_video_set_options(ctx->renderer, opts->renderer_opts);
+ gl_video_set_options(ctx->renderer, opts->renderer_opts, NULL);
ctx->gl->debug_context = opts->use_gl_debug;
gl_video_set_debug(ctx->renderer, opts->use_gl_debug);
frame_queue_shrink(ctx, opts->frame_queue_size);
@@ -349,6 +343,15 @@ int mpv_opengl_cb_render(struct mpv_opengl_cb_context *ctx, int fbo, int vp[4])
return left;
}
+int mpv_opengl_cb_report_flip(mpv_opengl_cb_context *ctx, int64_t time)
+{
+ pthread_mutex_lock(&ctx->lock);
+ ctx->recent_flip = time > 0 ? time : mp_time_us();
+ pthread_mutex_unlock(&ctx->lock);
+
+ return 0;
+}
+
static void draw_image(struct vo *vo, mp_image_t *mpi)
{
struct vo_priv *p = vo->priv;
@@ -412,7 +415,7 @@ static int reconfig(struct vo *vo, struct mp_image_params *params, int flags)
#define OPT_BASE_STRUCT struct vo_priv
static const struct m_option change_opts[] = {
OPT_FLAG("debug", use_gl_debug, 0),
- OPT_INTRANGE("frame-queue-size", frame_queue_size, 0, 1, 100, OPTDEF_INT(1)),
+ OPT_INTRANGE("frame-queue-size", frame_queue_size, 0, 1, 100, OPTDEF_INT(2)),
OPT_CHOICE("frame-drop-mode", frame_drop_mode, 0,
({"pop", FRAME_DROP_POP},
{"clear", FRAME_DROP_CLEAR})),
@@ -493,6 +496,16 @@ static int control(struct vo *vo, uint32_t request, void *data)
*arg = p->ctx ? &p->ctx->hwdec_info : NULL;
return true;
}
+ case VOCTRL_GET_RECENT_FLIP_TIME: {
+ int r = VO_FALSE;
+ pthread_mutex_lock(&p->ctx->lock);
+ if (p->ctx->recent_flip) {
+ *(int64_t *)data = p->ctx->recent_flip;
+ r = VO_TRUE;
+ }
+ pthread_mutex_unlock(&p->ctx->lock);
+ return r;
+ }
}
return VO_NOTIMPL;
@@ -529,7 +542,6 @@ static int preinit(struct vo *vo)
}
p->ctx->active = vo;
p->ctx->reconfigured = true;
- assert(vo->osd == p->ctx->osd);
copy_vo_opts(vo);
pthread_mutex_unlock(&p->ctx->lock);
@@ -539,7 +551,7 @@ static int preinit(struct vo *vo)
#define OPT_BASE_STRUCT struct vo_priv
static const struct m_option options[] = {
OPT_FLAG("debug", use_gl_debug, 0),
- OPT_INTRANGE("frame-queue-size", frame_queue_size, 0, 1, 100, OPTDEF_INT(1)),
+ OPT_INTRANGE("frame-queue-size", frame_queue_size, 0, 1, 100, OPTDEF_INT(2)),
OPT_CHOICE("frame-drop-mode", frame_drop_mode, 0,
({"pop", FRAME_DROP_POP},
{"clear", FRAME_DROP_CLEAR})),
diff --git a/video/out/vo_rpi.c b/video/out/vo_rpi.c
new file mode 100644
index 0000000..c60218d
--- /dev/null
+++ b/video/out/vo_rpi.c
@@ -0,0 +1,575 @@
+/*
+ * This file is part of mpv.
+ *
+ * mpv is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * mpv is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <stdbool.h>
+#include <assert.h>
+
+#include <bcm_host.h>
+#include <interface/mmal/mmal.h>
+#include <interface/mmal/util/mmal_util.h>
+#include <interface/mmal/util/mmal_default_components.h>
+#include <interface/mmal/vc/mmal_vc_api.h>
+
+#include <libavutil/rational.h>
+
+#include "common/common.h"
+#include "common/msg.h"
+#include "options/m_config.h"
+#include "vo.h"
+#include "video/mp_image.h"
+#include "sub/osd.h"
+#include "sub/img_convert.h"
+
+// In theory, the number of RGBA subbitmaps the OSD code could give us is
+// unlimited; but in practice there will be rarely many elements.
+#define MAX_OSD_ELEMS MP_SUB_BB_LIST_MAX
+
+struct osd_elem {
+ DISPMANX_RESOURCE_HANDLE_T resource;
+ DISPMANX_ELEMENT_HANDLE_T element;
+};
+
+struct osd_part {
+ struct osd_elem elems[MAX_OSD_ELEMS];
+ int num_elems;
+ int change_id;
+ bool needed;
+};
+
+struct priv {
+ DISPMANX_DISPLAY_HANDLE_T display;
+ DISPMANX_ELEMENT_HANDLE_T window;
+ DISPMANX_UPDATE_HANDLE_T update;
+ uint32_t w, h;
+
+ struct osd_part osd_parts[MAX_OSD_PARTS];
+ double osd_pts;
+ struct mp_osd_res osd_res;
+
+ MMAL_COMPONENT_T *renderer;
+ bool renderer_enabled;
+
+ struct mp_image *next_image;
+
+ // for RAM input
+ MMAL_POOL_T *swpool;
+
+ int background_layer;
+ int video_layer;
+ int osd_layer;
+
+ int display_nr;
+ int layer;
+};
+
+// Magic alignments (in pixels) expected by the MMAL internals.
+#define ALIGN_W 32
+#define ALIGN_H 16
+
+// Make mpi point to buffer, assuming MMAL_ENCODING_I420.
+// buffer can be NULL.
+// Return the required buffer space.
+static size_t layout_buffer(struct mp_image *mpi, MMAL_BUFFER_HEADER_T *buffer,
+ struct mp_image_params *params)
+{
+ assert(params->imgfmt == IMGFMT_420P);
+ mp_image_set_params(mpi, params);
+ int w = MP_ALIGN_UP(params->w, ALIGN_W);
+ int h = MP_ALIGN_UP(params->h, ALIGN_H);
+ uint8_t *cur = buffer ? buffer->data : NULL;
+ size_t size = 0;
+ for (int i = 0; i < 3; i++) {
+ int div = i ? 2 : 1;
+ mpi->planes[i] = cur;
+ mpi->stride[i] = w / div;
+ size_t plane_size = h / div * mpi->stride[i];
+ if (cur)
+ cur += plane_size;
+ size += plane_size;
+ }
+ return size;
+}
+
+static void wipe_osd_part(struct vo *vo, struct osd_part *part)
+{
+ struct priv *p = vo->priv;
+
+ for (int n = 0; n < part->num_elems; n++) {
+ vc_dispmanx_element_remove(p->update, part->elems[n].element);
+ vc_dispmanx_resource_delete(part->elems[n].resource);
+ }
+ part->num_elems = 0;
+ part->change_id = -1;
+}
+
+static void wipe_osd(struct vo *vo)
+{
+ struct priv *p = vo->priv;
+
+ for (int x = 0; x < MAX_OSD_PARTS; x++)
+ wipe_osd_part(vo, &p->osd_parts[x]);
+}
+
+static int add_element(struct vo *vo, struct osd_part *part, int index,
+ struct sub_bitmap *sub)
+{
+ struct priv *p = vo->priv;
+ VC_IMAGE_TYPE_T format = VC_IMAGE_ARGB8888; // assuming RPI is always LE
+
+ struct osd_elem *elem = &part->elems[index];
+ *elem = (struct osd_elem){0};
+
+ // I have no idea why stride must be passed in such a hacky way. It's not
+ // documented. Other software does it too. Other software claims aligning
+ // the width and "probably" the height is required too, but for me it works
+ // just fine without on rpi2. (See Weston's rpi renderer.)
+ elem->resource = vc_dispmanx_resource_create(format,
+ sub->w | (sub->stride << 16),
+ sub->h,
+ &(int32_t){0});
+ if (!elem->resource) {
+ MP_ERR(vo, "Could not create %dx%d sub-bitmap\n", sub->w, sub->h);
+ return -1;
+ }
+
+ VC_RECT_T rc = {.width = sub->w, .height = sub->h};
+ vc_dispmanx_resource_write_data(elem->resource, format,
+ sub->stride, sub->bitmap, &rc);
+ VC_RECT_T src = {.width = sub->w << 16, .height = sub->h << 16};
+ VC_RECT_T dst = {.x = sub->x, .y = sub->y, .width = sub->dw, .height = sub->dh};
+ VC_DISPMANX_ALPHA_T alpha = {
+ .flags = DISPMANX_FLAGS_ALPHA_FROM_SOURCE | DISPMANX_FLAGS_ALPHA_PREMULT,
+ .opacity = 0xFF,
+ };
+ elem->element = vc_dispmanx_element_add(p->update, p->display, p->osd_layer,
+ &dst, elem->resource, &src,
+ DISPMANX_PROTECTION_NONE,
+ &alpha, 0, 0);
+ if (!elem->element) {
+ MP_ERR(vo, "Could not create sub-bitmap element\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static void osd_draw_cb(void *ctx, struct sub_bitmaps *imgs)
+{
+ struct vo *vo = ctx;
+ struct priv *p = vo->priv;
+ struct osd_part *part = &p->osd_parts[imgs->render_index];
+
+ part->needed = true;
+
+ if (imgs->change_id == part->change_id)
+ return;
+
+ wipe_osd_part(vo, part);
+ part->change_id = imgs->change_id;
+
+ for (int n = 0; n < imgs->num_parts; n++) {
+ if (part->num_elems == MAX_OSD_ELEMS) {
+ MP_ERR(vo, "Too many OSD elements.\n");
+ break;
+ }
+ int index = part->num_elems++;
+ if (add_element(vo, part, index, &imgs->parts[n]) < 0)
+ break;
+ }
+}
+
+static void update_osd(struct vo *vo)
+{
+ struct priv *p = vo->priv;
+
+ for (int x = 0; x < MAX_OSD_PARTS; x++)
+ p->osd_parts[x].needed = false;
+
+ static const bool formats[SUBBITMAP_COUNT] = {[SUBBITMAP_RGBA] = true};
+ osd_draw(vo->osd, p->osd_res, p->osd_pts, 0, formats, osd_draw_cb, vo);
+
+ for (int x = 0; x < MAX_OSD_PARTS; x++) {
+ struct osd_part *part = &p->osd_parts[x];
+ if (!part->needed)
+ wipe_osd_part(vo, part);
+ }
+}
+
+static void resize(struct vo *vo)
+{
+ struct priv *p = vo->priv;
+ MMAL_PORT_T *input = p->renderer->input[0];
+
+ struct mp_rect src, dst;
+
+ vo_get_src_dst_rects(vo, &src, &dst, &p->osd_res);
+
+ MMAL_DISPLAYREGION_T dr = {
+ .hdr = { .id = MMAL_PARAMETER_DISPLAYREGION,
+ .size = sizeof(MMAL_DISPLAYREGION_T), },
+ .src_rect = { .x = src.x0, .y = src.y0,
+ .width = src.x1 - src.x0, .height = src.y1 - src.y0, },
+ .dest_rect = { .x = dst.x0, .y = dst.y0,
+ .width = dst.x1 - dst.x0, .height = dst.y1 - dst.y0, },
+ .layer = p->video_layer,
+ .display_num = p->display_nr,
+ .set = MMAL_DISPLAY_SET_SRC_RECT | MMAL_DISPLAY_SET_DEST_RECT |
+ MMAL_DISPLAY_SET_LAYER | MMAL_DISPLAY_SET_NUM,
+ };
+
+ if (mmal_port_parameter_set(input, &dr.hdr))
+ MP_WARN(vo, "could not set video rectangle\n");
+}
+
+static void flip_page(struct vo *vo)
+{
+ struct priv *p = vo->priv;
+ struct mp_image *mpi = p->next_image;
+ p->next_image = NULL;
+
+ // For OSD
+ vc_dispmanx_update_submit_sync(p->update);
+ p->update = vc_dispmanx_update_start(10);
+
+ if (mpi) {
+ MMAL_PORT_T *input = p->renderer->input[0];
+ MMAL_BUFFER_HEADER_T *ref = (void *)mpi->planes[3];
+
+ // Assume this field is free for use by us.
+ ref->user_data = mpi;
+
+ if (mmal_port_send_buffer(input, ref)) {
+ MP_ERR(vo, "could not queue picture!\n");
+ talloc_free(mpi);
+ }
+ }
+}
+
+static void free_mmal_buffer(void *arg)
+{
+ MMAL_BUFFER_HEADER_T *buffer = arg;
+ mmal_buffer_header_release(buffer);
+}
+
+static void draw_image(struct vo *vo, mp_image_t *mpi)
+{
+ struct priv *p = vo->priv;
+
+ talloc_free(p->next_image);
+ p->next_image = NULL;
+
+ p->osd_pts = mpi->pts;
+ update_osd(vo);
+
+ if (vo->params->imgfmt != IMGFMT_MMAL) {
+ MMAL_BUFFER_HEADER_T *buffer = mmal_queue_wait(p->swpool->queue);
+ if (!buffer) {
+ talloc_free(mpi);
+ MP_ERR(vo, "Can't allocate buffer.\n");
+ return;
+ }
+ mmal_buffer_header_reset(buffer);
+
+ struct mp_image *new_ref = mp_image_new_custom_ref(&(struct mp_image){0},
+ buffer,
+ free_mmal_buffer);
+ if (!new_ref) {
+ mmal_buffer_header_release(buffer);
+ talloc_free(mpi);
+ MP_ERR(vo, "Out of memory.\n");
+ return;
+ }
+
+ mp_image_setfmt(new_ref, IMGFMT_MMAL);
+ new_ref->planes[3] = (void *)buffer;
+
+ struct mp_image dmpi = {0};
+ buffer->length = layout_buffer(&dmpi, buffer, vo->params);
+ mp_image_copy(&dmpi, mpi);
+
+ talloc_free(mpi);
+ mpi = new_ref;
+ }
+
+ p->next_image = mpi;
+}
+
+static int query_format(struct vo *vo, int format)
+{
+ return format == IMGFMT_MMAL || format == IMGFMT_420P;
+}
+
+static MMAL_FOURCC_T map_csp(enum mp_csp csp)
+{
+ switch (csp) {
+ case MP_CSP_BT_601: return MMAL_COLOR_SPACE_ITUR_BT601;
+ case MP_CSP_BT_709: return MMAL_COLOR_SPACE_ITUR_BT709;
+ case MP_CSP_SMPTE_240M: return MMAL_COLOR_SPACE_SMPTE240M;
+ default: return MMAL_COLOR_SPACE_UNKNOWN;
+ }
+}
+
+static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
+{
+ mmal_buffer_header_release(buffer);
+}
+
+static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
+{
+ struct mp_image *mpi = buffer->user_data;
+ talloc_free(mpi);
+}
+
+static void disable_renderer(struct vo *vo)
+{
+ struct priv *p = vo->priv;
+
+ if (p->renderer_enabled) {
+ mmal_port_disable(p->renderer->control);
+ mmal_port_disable(p->renderer->input[0]);
+
+ mmal_port_flush(p->renderer->control);
+ mmal_port_flush(p->renderer->input[0]);
+
+ mmal_component_disable(p->renderer);
+ }
+ mmal_pool_destroy(p->swpool);
+ p->swpool = NULL;
+ p->renderer_enabled = false;
+}
+
+static int reconfig(struct vo *vo, struct mp_image_params *params, int flags)
+{
+ struct priv *p = vo->priv;
+ 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.color_space = map_csp(params->colorspace);
+
+ if (mmal_port_format_commit(input))
+ return -1;
+
+ input->buffer_num = MPMAX(input->buffer_num_min,
+ input->buffer_num_recommended) + 3;
+ input->buffer_size = MPMAX(input->buffer_size_min,
+ input->buffer_size_recommended);
+
+ if (!opaque) {
+ size_t size = layout_buffer(&(struct mp_image){0}, NULL, params);
+ if (input->buffer_size != size) {
+ MP_FATAL(vo, "We disagree with MMAL about buffer sizes.\n");
+ return -1;
+ }
+
+ p->swpool = mmal_pool_create(input->buffer_num, input->buffer_size);
+ if (!p->swpool) {
+ MP_FATAL(vo, "Could not allocate buffer pool.\n");
+ return -1;
+ }
+ }
+
+ resize(vo);
+
+ p->renderer_enabled = true;
+
+ if (mmal_port_enable(p->renderer->control, control_port_cb))
+ return -1;
+
+ if (mmal_port_enable(input, input_port_cb))
+ return -1;
+
+ if (mmal_component_enable(p->renderer)) {
+ MP_FATAL(vo, "Failed to enable video renderer.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static struct mp_image *take_screenshot(struct vo *vo)
+{
+ struct priv *p = vo->priv;
+
+ struct mp_image *img = mp_image_alloc(IMGFMT_BGR0, p->w, p->h);
+ if (!img)
+ return NULL;
+
+ DISPMANX_RESOURCE_HANDLE_T resource =
+ vc_dispmanx_resource_create(VC_IMAGE_ARGB8888,
+ img->w | ((img->w * 4) << 16), img->h,
+ &(int32_t){0});
+ if (!resource)
+ goto fail;
+
+ if (vc_dispmanx_snapshot(p->display, resource, 0))
+ goto fail;
+
+ VC_RECT_T rc = {.width = img->w, .height = img->h};
+ if (vc_dispmanx_resource_read_data(resource, &rc, img->planes[0], img->stride[0]))
+ goto fail;
+
+ vc_dispmanx_resource_delete(resource);
+ return img;
+
+fail:
+ vc_dispmanx_resource_delete(resource);
+ talloc_free(img);
+ return NULL;
+}
+
+static int control(struct vo *vo, uint32_t request, void *data)
+{
+ struct priv *p = vo->priv;
+
+ switch (request) {
+ case VOCTRL_GET_PANSCAN:
+ return VO_TRUE;
+ case VOCTRL_SET_PANSCAN:
+ if (p->renderer_enabled)
+ resize(vo);
+ return VO_TRUE;
+ case VOCTRL_REDRAW_FRAME:
+ update_osd(vo);
+ return VO_TRUE;
+ case VOCTRL_SCREENSHOT_WIN:
+ *(struct mp_image **)data = take_screenshot(vo);
+ return VO_TRUE;
+ }
+
+ return VO_NOTIMPL;
+}
+
+static void uninit(struct vo *vo)
+{
+ struct priv *p = vo->priv;
+
+ talloc_free(p->next_image);
+
+ wipe_osd(vo);
+
+ if (p->update)
+ vc_dispmanx_update_submit_sync(p->update);
+
+ if (p->renderer) {
+ disable_renderer(vo);
+ mmal_component_release(p->renderer);
+ }
+
+ if (p->display)
+ vc_dispmanx_display_close(p->display);
+
+ mmal_vc_deinit();
+}
+
+static int preinit(struct vo *vo)
+{
+ struct priv *p = vo->priv;
+
+ p->background_layer = p->layer;
+ p->video_layer = p->layer + 1;
+ p->osd_layer = p->layer + 2;
+
+ bcm_host_init();
+
+ if (mmal_vc_init()) {
+ MP_FATAL(vo, "Could not initialize MMAL.\n");
+ return -1;
+ }
+
+ p->display = vc_dispmanx_display_open(p->display_nr);
+ p->update = vc_dispmanx_update_start(0);
+ if (!p->display || !p->update) {
+ MP_FATAL(vo, "Could not get DISPMANX objects.\n");
+ goto fail;
+ }
+
+ if (mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, &p->renderer))
+ {
+ MP_FATAL(vo, "Could not create MMAL renderer.\n");
+ goto fail;
+ }
+
+ if (graphics_get_display_size(0, &p->w, &p->h) < 0) {
+ MP_FATAL(vo, "Could not get display size.\n");
+ goto fail;
+ }
+
+ MP_VERBOSE(vo, "Display size: %dx%d\n", p->w, p->h);
+
+ // Just use the whole screen.
+ VC_RECT_T dst = {.width = p->w, .height = p->h};
+ VC_RECT_T src = {.width = p->w << 16, .height = p->h << 16};
+ VC_DISPMANX_ALPHA_T alpha = {
+ .flags = DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS,
+ .opacity = 0xFF,
+ };
+ p->window = vc_dispmanx_element_add(p->update, p->display, p->background_layer,
+ &dst, 0, &src, DISPMANX_PROTECTION_NONE,
+ &alpha, 0, 0);
+ if (!p->window) {
+ MP_FATAL(vo, "Could not add DISPMANX element.\n");
+ goto fail;
+ }
+
+ vc_dispmanx_update_submit_sync(p->update);
+ p->update = vc_dispmanx_update_start(10);
+
+ return 0;
+
+fail:
+ uninit(vo);
+ return -1;
+}
+
+#define OPT_BASE_STRUCT struct priv
+static const struct m_option options[] = {
+ OPT_INT("display", display_nr, 0),
+ OPT_INT("layer", layer, 0, OPTDEF_INT(-10)),
+ {0},
+};
+
+const struct vo_driver video_out_rpi = {
+ .description = "Raspberry Pi (MMAL)",
+ .name = "rpi",
+ .preinit = preinit,
+ .query_format = query_format,
+ .reconfig = reconfig,
+ .control = control,
+ .draw_image = draw_image,
+ .flip_page = flip_page,
+ .uninit = uninit,
+ .priv_size = sizeof(struct priv),
+ .options = options,
+};
diff --git a/video/out/vo_sdl.c b/video/out/vo_sdl.c
index 9a06f2e..03a0484 100644
--- a/video/out/vo_sdl.c
+++ b/video/out/vo_sdl.c
@@ -1,22 +1,22 @@
/*
* video output driver for SDL 2.0+
+ *
* Copyright (C) 2012 Rudolf Polzer <divVerent@xonotic.org>
*
* This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
@@ -177,8 +177,7 @@ struct priv {
struct mp_osd_res osd_res;
struct formatmap_entry osd_format;
struct osd_bitmap_surface {
- int bitmap_id;
- int bitmap_pos_id;
+ int change_id;
struct osd_target {
SDL_Rect source;
SDL_Rect dest;
@@ -575,7 +574,7 @@ static int wait_events(struct vo *vo, int64_t until_time_us)
if (sdl_mod & (KMOD_LCTRL | KMOD_RCTRL))
mpv_mod |= MP_KEY_MODIFIER_CTRL;
if ((sdl_mod & KMOD_LALT) ||
- (sdl_mod & KMOD_RALT) && !mp_input_use_alt_gr(vo->input_ctx))
+ ((sdl_mod & KMOD_RALT) && !mp_input_use_alt_gr(vo->input_ctx)))
mpv_mod |= MP_KEY_MODIFIER_ALT;
if (sdl_mod & (KMOD_LGUI | KMOD_RGUI))
mpv_mod |= MP_KEY_MODIFIER_META;
@@ -702,7 +701,7 @@ static void generate_osd_part(struct vo *vo, struct sub_bitmaps *imgs)
if (imgs->format == SUBBITMAP_EMPTY || imgs->num_parts == 0)
return;
- if (imgs->bitmap_pos_id == sfc->bitmap_pos_id)
+ if (imgs->change_id == sfc->change_id)
return;
if (imgs->num_parts > sfc->targets_size) {
@@ -725,49 +724,46 @@ static void generate_osd_part(struct vo *vo, struct sub_bitmaps *imgs)
bmp->x, bmp->y, bmp->dw, bmp->dh
};
- if (imgs->bitmap_id != sfc->bitmap_id || !target->tex) {
- // tex: alpha blended texture
- if (target->tex) {
- SDL_DestroyTexture(target->tex);
- target->tex = NULL;
- }
- if (!target->tex)
- target->tex = SDL_CreateTexture(vc->renderer,
- vc->osd_format.sdl, SDL_TEXTUREACCESS_STREAMING,
- bmp->w, bmp->h);
- if (!target->tex) {
- MP_ERR(vo, "Could not create texture\n");
- }
- if (target->tex) {
- SDL_SetTextureBlendMode(target->tex,
- SDL_BLENDMODE_BLEND);
- SDL_SetTextureColorMod(target->tex, 0, 0, 0);
- subbitmap_to_texture(vo, target->tex, bmp, 0); // RGBA -> 000A
- }
+ // tex: alpha blended texture
+ if (target->tex) {
+ SDL_DestroyTexture(target->tex);
+ target->tex = NULL;
+ }
+ if (!target->tex)
+ target->tex = SDL_CreateTexture(vc->renderer,
+ vc->osd_format.sdl, SDL_TEXTUREACCESS_STREAMING,
+ bmp->w, bmp->h);
+ if (!target->tex) {
+ MP_ERR(vo, "Could not create texture\n");
+ }
+ if (target->tex) {
+ SDL_SetTextureBlendMode(target->tex,
+ SDL_BLENDMODE_BLEND);
+ SDL_SetTextureColorMod(target->tex, 0, 0, 0);
+ subbitmap_to_texture(vo, target->tex, bmp, 0); // RGBA -> 000A
+ }
- // tex2: added texture
- if (target->tex2) {
- SDL_DestroyTexture(target->tex2);
- target->tex2 = NULL;
- }
- if (!target->tex2)
- target->tex2 = SDL_CreateTexture(vc->renderer,
- vc->osd_format.sdl, SDL_TEXTUREACCESS_STREAMING,
- bmp->w, bmp->h);
- if (!target->tex2) {
- MP_ERR(vo, "Could not create texture\n");
- }
- if (target->tex2) {
- SDL_SetTextureBlendMode(target->tex2,
- SDL_BLENDMODE_ADD);
- subbitmap_to_texture(vo, target->tex2, bmp,
- 0xFF000000); // RGBA -> RGB1
- }
+ // tex2: added texture
+ if (target->tex2) {
+ SDL_DestroyTexture(target->tex2);
+ target->tex2 = NULL;
+ }
+ if (!target->tex2)
+ target->tex2 = SDL_CreateTexture(vc->renderer,
+ vc->osd_format.sdl, SDL_TEXTUREACCESS_STREAMING,
+ bmp->w, bmp->h);
+ if (!target->tex2) {
+ MP_ERR(vo, "Could not create texture\n");
+ }
+ if (target->tex2) {
+ SDL_SetTextureBlendMode(target->tex2,
+ SDL_BLENDMODE_ADD);
+ subbitmap_to_texture(vo, target->tex2, bmp,
+ 0xFF000000); // RGBA -> RGB1
}
}
- sfc->bitmap_id = imgs->bitmap_id;
- sfc->bitmap_pos_id = imgs->bitmap_pos_id;
+ sfc->change_id = imgs->change_id;
}
static void draw_osd_part(struct vo *vo, int index)
diff --git a/video/out/vo_vaapi.c b/video/out/vo_vaapi.c
index 8d0102b..b34acb1 100644
--- a/video/out/vo_vaapi.c
+++ b/video/out/vo_vaapi.c
@@ -4,21 +4,20 @@
* Copyright (C) 2008-2009 Splitted-Desktop Systems
* Gwenole Beauchesne <gbeauchesne@splitted-desktop.com>
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <assert.h>
@@ -32,7 +31,6 @@
#include "config.h"
#include "common/msg.h"
#include "video/out/vo.h"
-#include "video/memcpy_pic.h"
#include "video/mp_image_pool.h"
#include "sub/osd.h"
#include "sub/img_convert.h"
@@ -57,7 +55,7 @@ struct vaapi_subpic {
struct vaapi_osd_part {
bool active;
- int bitmap_pos_id;
+ int change_id;
struct vaapi_osd_image image;
struct vaapi_subpic subpic;
struct osd_conv_cache *conv_cache;
@@ -345,8 +343,8 @@ static void draw_osd_cb(void *pctx, struct sub_bitmaps *imgs)
struct priv *p = pctx;
struct vaapi_osd_part *part = &p->osd_parts[imgs->render_index];
- if (imgs->bitmap_pos_id != part->bitmap_pos_id) {
- part->bitmap_pos_id = imgs->bitmap_pos_id;
+ if (imgs->change_id != part->change_id) {
+ part->change_id = imgs->change_id;
osd_scale_rgba(part->conv_cache, imgs);
@@ -541,12 +539,6 @@ static int control(struct vo *vo, uint32_t request, void *data)
*arg = &p->hwdec_info;
return true;
}
- case VOCTRL_GET_COLORSPACE: {
- struct mp_image_params *params = data;
- if (va_get_colorspace_flag(p->image_params.colorspace))
- params->colorspace = p->image_params.colorspace;
- return true;
- }
case VOCTRL_SET_EQUALIZER: {
struct voctrl_set_equalizer_args *eq = data;
return set_equalizer(p, eq->name, eq->value);
diff --git a/video/out/vo_vdpau.c b/video/out/vo_vdpau.c
index 4c08786..098f449 100644
--- a/video/out/vo_vdpau.c
+++ b/video/out/vo_vdpau.c
@@ -4,21 +4,20 @@
* Copyright (C) 2008 NVIDIA (Rajib Mahapatra <rmahapatra@nvidia.com>)
* Copyright (C) 2009 Uoti Urpala
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
/*
@@ -136,8 +135,7 @@ struct vdpctx {
} *targets;
int targets_size;
int render_count;
- int bitmap_id;
- int bitmap_pos_id;
+ int change_id;
} osd_surfaces[MAX_OSD_PARTS];
// Video equalizer
@@ -164,19 +162,10 @@ static int render_video_to_output_surface(struct vo *vo,
CHECK_VDP_WARNING(vo, "Error when calling "
"vdp_presentation_queue_block_until_surface_idle");
- if (!mpi) {
- // At least clear the screen if there is nothing to render
- int flags = VDP_OUTPUT_SURFACE_RENDER_ROTATE_0;
- vdp_st = vdp->output_surface_render_output_surface(output_surface,
- NULL, vc->black_pixel,
- NULL, NULL, NULL,
- flags);
- return -1;
- }
-
- if (vc->rgb_mode) {
- // Clear the borders between video and window (if there are any).
- // For some reason, video_mixer_render doesn't need it for YUV.
+ // Clear the borders between video and window (if there are any).
+ // For some reason, video_mixer_render doesn't need it for YUV.
+ // Also, if there is nothing to render, at least clear the screen.
+ if (vc->rgb_mode || !mpi) {
int flags = VDP_OUTPUT_SURFACE_RENDER_ROTATE_0;
vdp_st = vdp->output_surface_render_output_surface(output_surface,
NULL, vc->black_pixel,
@@ -185,6 +174,9 @@ static int render_video_to_output_surface(struct vo *vo,
CHECK_VDP_WARNING(vo, "Error clearing screen");
}
+ if (!mpi)
+ return -1;
+
struct mp_vdpau_mixer_frame *frame = mp_vdpau_mixed_frame_get(mpi);
struct mp_vdpau_mixer_opts opts = {0};
if (frame)
@@ -388,7 +380,7 @@ static void mark_vdpau_objects_uninitialized(struct vo *vo)
for (int i = 0; i < MAX_OSD_PARTS; i++) {
struct osd_bitmap_surface *sfc = &vc->osd_surfaces[i];
talloc_free(sfc->packer);
- sfc->bitmap_id = sfc->bitmap_pos_id = 0;
+ sfc->change_id = 0;
*sfc = (struct osd_bitmap_surface){
.surface = VDP_INVALID_HANDLE,
};
@@ -512,7 +504,7 @@ static void generate_osd_part(struct vo *vo, struct sub_bitmaps *imgs)
struct osd_bitmap_surface *sfc = &vc->osd_surfaces[imgs->render_index];
bool need_upload = false;
- if (imgs->bitmap_pos_id == sfc->bitmap_pos_id)
+ if (imgs->change_id == sfc->change_id)
return; // Nothing changed and we still have the old data
sfc->render_count = 0;
@@ -520,9 +512,6 @@ static void generate_osd_part(struct vo *vo, struct sub_bitmaps *imgs)
if (imgs->format == SUBBITMAP_EMPTY || imgs->num_parts == 0)
return;
- if (imgs->bitmap_id == sfc->bitmap_id)
- goto osd_skip_upload;
-
need_upload = true;
VdpRGBAFormat format;
int format_size;
@@ -572,9 +561,9 @@ static void generate_osd_part(struct vo *vo, struct sub_bitmaps *imgs)
&(const void *){zeros}, &(uint32_t){0},
&(VdpRect){0, 0, sfc->packer->used_width,
sfc->packer->used_height});
+ CHECK_VDP_WARNING(vo, "OSD: error uploading OSD bitmap");
}
-osd_skip_upload:
if (sfc->surface == VDP_INVALID_HANDLE)
return;
if (sfc->packer->count > sfc->targets_size) {
@@ -610,8 +599,7 @@ osd_skip_upload:
sfc->render_count++;
}
- sfc->bitmap_id = imgs->bitmap_id;
- sfc->bitmap_pos_id = imgs->bitmap_pos_id;
+ sfc->change_id = imgs->change_id;
}
static void draw_osd_cb(void *ctx, struct sub_bitmaps *imgs)
@@ -778,7 +766,7 @@ static int flip_page_timed(struct vo *vo, int64_t pts_us, int duration)
if (pts < vsync + vc->vsync_interval / 4
&& (vsync - PREV_VSYNC(vc->last_queue_time)
> pts - vc->last_ideal_time + vc->vsync_interval / 2
- || vc->dropped_frame && vsync > vc->dropped_time))
+ || (vc->dropped_frame && vsync > vc->dropped_time)))
pts -= vc->vsync_interval / 2;
vc->dropped_time = ideal_pts;
@@ -842,7 +830,7 @@ static struct mp_image *read_output_surface(struct vo *vo,
if (!vo->params)
return NULL;
- struct mp_image *image = mp_image_alloc(IMGFMT_BGR32, width, height);
+ struct mp_image *image = mp_image_alloc(IMGFMT_BGR0, width, height);
if (!image)
return NULL;
@@ -1025,15 +1013,6 @@ static int control(struct vo *vo, uint32_t request, void *data)
struct voctrl_get_equalizer_args *args = data;
return get_equalizer(vo, args->name, args->valueptr);
}
- case VOCTRL_GET_COLORSPACE: {
- struct mp_image_params *params = data;
- if (vo->params && !vc->rgb_mode) {
- params->colorspace = vo->params->colorspace;
- params->colorlevels = vo->params->colorlevels;
- params->outputlevels = vo->params->outputlevels;
- }
- return true;
- }
case VOCTRL_REDRAW_FRAME:
if (status_ok(vo))
video_to_output_surface(vo);
diff --git a/video/out/vo_wayland.c b/video/out/vo_wayland.c
index 9da24dd..40f06aa 100644
--- a/video/out/vo_wayland.c
+++ b/video/out/vo_wayland.c
@@ -27,7 +27,6 @@
#include "vo.h"
#include "video/mp_image.h"
#include "video/sws_utils.h"
-#include "video/memcpy_pic.h"
#include "sub/osd.h"
#include "sub/img_convert.h"
#include "common/msg.h"
@@ -42,7 +41,6 @@
static void draw_image(struct vo *vo, mp_image_t *mpi);
static void draw_osd(struct vo *vo);
-static const struct wl_callback_listener frame_listener;
static const struct wl_buffer_listener buffer_listener;
// TODO: pay attention to the reported subpixel order
@@ -120,8 +118,6 @@ struct priv {
struct mp_sws_context *sws;
struct mp_image_params in_format;
- struct wl_callback *redraw_callback;
-
struct buffer_pool video_bufpool;
struct mp_image *original_image;
@@ -134,7 +130,7 @@ struct priv {
struct wl_subsurface *osd_subsurfaces[MAX_OSD_PARTS];
shm_buffer_t *osd_buffers[MAX_OSD_PARTS];
// this id tells us if the subtitle part has changed or not
- int bitmap_pos_id[MAX_OSD_PARTS];
+ int change_id[MAX_OSD_PARTS];
int64_t recent_flip_time; // last frame event
@@ -272,7 +268,7 @@ static bool resize(struct priv *p)
{
struct vo_wayland_state *wl = p->wl;
- if (SHM_BUFFER_IS_BUSY(p->video_bufpool.back_buffer))
+ if (!p->video_bufpool.back_buffer || SHM_BUFFER_IS_BUSY(p->video_bufpool.back_buffer))
return false; // skip resizing if we can't garantuee pixel perfectness!
int32_t x = wl->window.sh_x;
@@ -360,34 +356,6 @@ static const struct wl_buffer_listener buffer_listener = {
buffer_handle_release
};
-static void frame_handle_redraw(void *data,
- struct wl_callback *callback,
- uint32_t time)
-{
- struct priv *p = data;
- struct vo_wayland_state *wl = p->wl;
- shm_buffer_t *buf = buffer_pool_get_front(&p->video_bufpool);
-
- wl_surface_attach(wl->window.video_surface, buf->buffer, p->x, p->y);
- wl_surface_damage(wl->window.video_surface, 0, 0, p->dst_w, p->dst_h);
-
- if (callback)
- wl_callback_destroy(callback);
-
- p->redraw_callback = wl_surface_frame(wl->window.video_surface);
- wl_callback_add_listener(p->redraw_callback, &frame_listener, p);
- wl_surface_commit(wl->window.video_surface);
- buffer_finalise_front(buf);
-
- p->x = 0;
- p->y = 0;
- p->recent_flip_time = mp_time_us();
-}
-
-static const struct wl_callback_listener frame_listener = {
- frame_handle_redraw
-};
-
static void shm_handle_format(void *data,
struct wl_shm *wl_shm,
uint32_t format)
@@ -415,13 +383,17 @@ static const struct wl_shm_listener shm_listener = {
static void draw_image(struct vo *vo, mp_image_t *mpi)
{
struct priv *p = vo->priv;
- shm_buffer_t *buf = buffer_pool_get_back(&p->video_bufpool);
if (mpi) {
talloc_free(p->original_image);
p->original_image = mpi;
}
+ if (!p->wl->frame.pending)
+ return;
+
+ shm_buffer_t *buf = buffer_pool_get_back(&p->video_bufpool);
+
if (!buf) {
// TODO: use similar handling of busy buffers as the osd buffers
// if the need arises
@@ -455,8 +427,8 @@ static void draw_osd_cb(void *ctx, struct sub_bitmaps *imgs)
struct wl_surface *s = p->osd_surfaces[id];
- if (imgs->bitmap_pos_id != p->bitmap_pos_id[id]) {
- p->bitmap_pos_id[id] = imgs->bitmap_pos_id;
+ if (imgs->change_id != p->change_id[id]) {
+ p->change_id[id] = imgs->change_id;
struct mp_rect bb;
if (!mp_sub_bitmaps_bb(imgs, &bb))
@@ -536,12 +508,21 @@ static void flip_page(struct vo *vo)
{
struct priv *p = vo->priv;
+ if (!p->wl->frame.pending)
+ return;
+
buffer_pool_swap(&p->video_bufpool);
- if (!p->redraw_callback) {
- MP_DBG(p->wl, "restart frame callback\n");
- frame_handle_redraw(p, NULL, 0);
- }
+ shm_buffer_t *buf = buffer_pool_get_front(&p->video_bufpool);
+ wl_surface_attach(p->wl->window.video_surface, buf->buffer, p->x, p->y);
+ wl_surface_damage(p->wl->window.video_surface, 0, 0, p->dst_w, p->dst_h);
+ wl_surface_commit(p->wl->window.video_surface);
+ buffer_finalise_front(buf);
+
+ p->x = 0;
+ p->y = 0;
+ p->recent_flip_time = mp_time_us();
+ p->wl->frame.pending = false;
}
static int query_format(struct vo *vo, int format)
@@ -613,9 +594,6 @@ static void uninit(struct vo *vo)
struct priv *p = vo->priv;
buffer_pool_destroy(&p->video_bufpool);
- if (p->redraw_callback)
- wl_callback_destroy(p->redraw_callback);
-
talloc_free(p->original_image);
for (int i = 0; i < MAX_OSD_PARTS; ++i) {
diff --git a/video/out/vo_x11.c b/video/out/vo_x11.c
index aa578d4..eca6c2a 100644
--- a/video/out/vo_x11.c
+++ b/video/out/vo_x11.c
@@ -1,21 +1,20 @@
/*
- * This file is part of MPlayer.
- *
* Original author: Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
diff --git a/video/out/vo_xv.c b/video/out/vo_xv.c
index 654f657..e6578b1 100644
--- a/video/out/vo_xv.c
+++ b/video/out/vo_xv.c
@@ -1,23 +1,20 @@
/*
* X11 Xv interface
*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * Original author: Gerd Knorr <kraxel@goldbach.in-berlin.de>
- *
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
@@ -51,7 +48,6 @@
#include "video/mp_image.h"
#include "video/img_fourcc.h"
#include "x11_common.h"
-#include "video/memcpy_pic.h"
#include "sub/osd.h"
#include "sub/draw_bmp.h"
#include "video/csputils.h"
@@ -515,8 +511,7 @@ static bool allocate_xvimage(struct vo *vo, int foo)
ctx->Shminfo[foo].shmid = shmget(IPC_PRIVATE,
ctx->xvimage[foo]->data_size,
IPC_CREAT | 0777);
- ctx->Shminfo[foo].shmaddr = (char *) shmat(ctx->Shminfo[foo].shmid, 0,
- 0);
+ ctx->Shminfo[foo].shmaddr = shmat(ctx->Shminfo[foo].shmid, 0, 0);
if (ctx->Shminfo[foo].shmaddr == (void *)-1)
return false;
ctx->Shminfo[foo].readOnly = False;
@@ -823,12 +818,6 @@ static int control(struct vo *vo, uint32_t request, void *data)
struct voctrl_get_equalizer_args *args = data;
return xv_get_eq(vo, ctx->xv_port, args->name, args->valueptr);
}
- case VOCTRL_GET_COLORSPACE: {
- struct mp_image_params *params = data;
- read_xv_csp(vo);
- params->colorspace = ctx->cached_csp;
- return true;
- }
case VOCTRL_REDRAW_FRAME:
draw_image(vo, ctx->original_image);
return true;
diff --git a/video/out/w32_common.c b/video/out/w32_common.c
index c7dfd0d..075bda5 100644
--- a/video/out/w32_common.c
+++ b/video/out/w32_common.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
@@ -105,6 +104,9 @@ struct vo_w32_state {
int high_surrogate;
ITaskbarList2 *taskbar_list;
+
+ // updates on move/resize/displaychange
+ double display_fps;
};
typedef struct tagDropTarget {
@@ -563,6 +565,53 @@ static void wakeup_gui_thread(void *ctx)
PostMessage(w32->window, WM_USER, 0, 0);
}
+static double vo_w32_get_display_fps(struct vo_w32_state *w32)
+{
+ // Get the device name of the monitor containing the window
+ HMONITOR mon = MonitorFromWindow(w32->window, MONITOR_DEFAULTTOPRIMARY);
+ MONITORINFOEXW mi = { .cbSize = sizeof mi };
+ GetMonitorInfoW(mon, (MONITORINFO*)&mi);
+
+ DEVMODE dm = { .dmSize = sizeof dm };
+ if (!EnumDisplaySettingsW(mi.szDevice, ENUM_CURRENT_SETTINGS, &dm))
+ return -1;
+
+ // May return 0 or 1 which "represent the display hardware's default refresh rate"
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/dd183565%28v=vs.85%29.aspx
+ // mpv validates this value with a threshold of 1, so don't return exactly 1
+ if (dm.dmDisplayFrequency == 1)
+ return 0;
+
+ // dm.dmDisplayFrequency is an integer which is rounded down, so it's
+ // highly likely that 23 represents 24/1.001, 59 represents 60/1.001, etc.
+ // A caller can always reproduce the original value by using floor.
+ double rv = dm.dmDisplayFrequency;
+ switch (dm.dmDisplayFrequency) {
+ case 23:
+ case 29:
+ case 47:
+ case 59:
+ case 71:
+ case 89:
+ case 95:
+ case 119:
+ case 143:
+ rv = (rv + 1) / 1.001;
+ }
+
+ return rv;
+}
+
+static void update_display_fps(struct vo_w32_state *w32)
+{
+ double fps = vo_w32_get_display_fps(w32);
+ if (fps != w32->display_fps) {
+ w32->display_fps = fps;
+ signal_events(w32, VO_EVENT_WIN_STATE);
+ MP_VERBOSE(w32, "display-fps: %f\n", fps);
+ }
+}
+
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,
LPARAM lParam)
{
@@ -588,6 +637,7 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,
ClientToScreen(w32->window, &p);
w32->window_x = p.x;
w32->window_y = p.y;
+ update_display_fps(w32); // if we moved between monitors
MP_VERBOSE(w32, "move window: %d:%d\n", w32->window_x, w32->window_y);
break;
}
@@ -596,9 +646,13 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,
if (GetClientRect(w32->window, &r) && r.right > 0 && r.bottom > 0) {
w32->dw = r.right;
w32->dh = r.bottom;
+ update_display_fps(w32); // if we moved between monitors
signal_events(w32, VO_EVENT_RESIZE);
MP_VERBOSE(w32, "resize window: %d:%d\n", w32->dw, w32->dh);
}
+
+ // Window may have been minimized or restored
+ signal_events(w32, VO_EVENT_WIN_STATE);
break;
}
case WM_SIZING:
@@ -695,8 +749,10 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,
mp_input_put_key(w32->input_ctx, MP_KEY_MOUSE_LEAVE);
break;
case WM_MOUSEMOVE: {
- if (!w32->tracking)
+ if (!w32->tracking) {
w32->tracking = TrackMouseEvent(&w32->trackEvent);
+ mp_input_put_key(w32->input_ctx, MP_KEY_MOUSE_ENTER);
+ }
// Windows can send spurious mouse events, which would make the mpv
// core unhide the mouse cursor on completely unrelated events. See:
// https://blogs.msdn.com/b/oldnewthing/archive/2003/10/01/55108.aspx
@@ -740,6 +796,9 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,
mouse_button = HIWORD(wParam) == 1 ? MP_MOUSE_BTN5 : MP_MOUSE_BTN6;
mouse_button |= MP_KEY_STATE_UP;
break;
+ case WM_DISPLAYCHANGE:
+ update_display_fps(w32);
+ break;
}
if (mouse_button) {
@@ -1247,6 +1306,9 @@ static int gui_thread_control(struct vo_w32_state *w32, int request, void *arg)
reinit_window_state(w32);
return VO_TRUE;
}
+ case VOCTRL_GET_WIN_STATE:
+ *(int *)arg = IsIconic(w32->window) ? VO_WIN_STATE_MINIMIZED : 0;
+ return VO_TRUE;
case VOCTRL_SET_CURSOR_VISIBILITY:
w32->cursor_visible = *(bool *)arg;
@@ -1259,7 +1321,8 @@ static int gui_thread_control(struct vo_w32_state *w32, int request, void *arg)
return VO_TRUE;
case VOCTRL_KILL_SCREENSAVER:
w32->disable_screensaver = true;
- SetThreadExecutionState(ES_CONTINUOUS | ES_DISPLAY_REQUIRED);
+ SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED |
+ ES_DISPLAY_REQUIRED);
return VO_TRUE;
case VOCTRL_RESTORE_SCREENSAVER:
w32->disable_screensaver = false;
@@ -1271,6 +1334,10 @@ static int gui_thread_control(struct vo_w32_state *w32, int request, void *arg)
talloc_free(title);
return VO_TRUE;
}
+ case VOCTRL_GET_DISPLAY_FPS:
+ update_display_fps(w32);
+ *(double*) arg = w32->display_fps;
+ return VO_TRUE;
}
return VO_NOTIMPL;
}
diff --git a/video/out/w32_common.h b/video/out/w32_common.h
index d5c9f07..a491983 100644
--- a/video/out/w32_common.h
+++ b/video/out/w32_common.h
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_W32_COMMON_H
diff --git a/video/out/wayland_common.c b/video/out/wayland_common.c
index 8430498..aff3d7c 100644
--- a/video/out/wayland_common.c
+++ b/video/out/wayland_common.c
@@ -59,6 +59,8 @@ static void schedule_resize(struct vo_wayland_state *wl,
static void vo_wayland_fullscreen (struct vo *vo);
+static const struct wl_callback_listener frame_listener;
+
static const struct mp_keymap keymap[] = {
// special keys
{XKB_KEY_Pause, MP_KEY_PAUSE}, {XKB_KEY_Escape, MP_KEY_ESC},
@@ -379,6 +381,7 @@ static void pointer_handle_enter(void *data,
/* Release the left button on pointer enter again
* because after moving the shell surface no release event is sent */
+ mp_input_put_key(wl->vo->input_ctx, MP_KEY_MOUSE_ENTER);
mp_input_put_key(wl->vo->input_ctx, MP_MOUSE_BTN0 | MP_KEY_STATE_UP);
show_cursor(wl);
}
@@ -417,7 +420,7 @@ static void pointer_handle_button(void *data,
{
struct vo_wayland_state *wl = data;
- mp_input_put_key(wl->vo->input_ctx, MP_MOUSE_BTN0 + (button - BTN_LEFT) |
+ mp_input_put_key(wl->vo->input_ctx, (MP_MOUSE_BTN0 + (button - BTN_LEFT)) |
((state == WL_POINTER_BUTTON_STATE_PRESSED)
? MP_KEY_STATE_DOWN : MP_KEY_STATE_UP));
@@ -792,8 +795,41 @@ static void schedule_resize(struct vo_wayland_state *wl,
wl->window.events |= VO_EVENT_RESIZE;
wl->vo->dwidth = width;
wl->vo->dheight = height;
+
+ struct wl_region *region = wl_compositor_create_region(wl->display.compositor);
+
+ if (region) {
+ wl_region_add(region, x, y, width, height);
+ wl_surface_set_opaque_region(wl->window.video_surface, region);
+ wl_surface_commit(wl->window.video_surface);
+ wl_region_destroy(region);
+ }
}
+static void frame_callback(void *data,
+ struct wl_callback *callback,
+ uint32_t time)
+{
+ struct vo_wayland_state *wl = data;
+
+ if (callback)
+ wl_callback_destroy(callback);
+
+ wl->frame.callback = wl_surface_frame(wl->window.video_surface);
+
+ if (!wl->frame.callback) {
+ MP_ERR(wl, "wl_surface_frame failed\n");
+ return;
+ }
+
+ wl_callback_add_listener(wl->frame.callback, &frame_listener, wl);
+ wl->frame.pending = true;
+}
+
+static const struct wl_callback_listener frame_listener = {
+ frame_callback
+};
+
static bool create_display (struct vo_wayland_state *wl)
{
if (wl->vo->probing && !getenv("XDG_RUNTIME_DIR"))
@@ -877,6 +913,7 @@ static bool create_window (struct vo_wayland_state *wl)
wl_shell_surface_set_class(wl->window.shell_surface, "mpv");
}
+ frame_callback(wl, NULL, 0);
return true;
}
@@ -887,6 +924,9 @@ static void destroy_window (struct vo_wayland_state *wl)
if (wl->window.video_surface)
wl_surface_destroy(wl->window.video_surface);
+
+ if (wl->frame.callback)
+ wl_callback_destroy(wl->frame.callback);
}
static bool create_cursor (struct vo_wayland_state *wl)
diff --git a/video/out/wayland_common.h b/video/out/wayland_common.h
index 5f0947f..d7c5052 100644
--- a/video/out/wayland_common.h
+++ b/video/out/wayland_common.h
@@ -52,6 +52,11 @@ struct vo_wayland_state {
struct vo *vo;
struct mp_log* log;
+ struct {
+ struct wl_callback *callback;
+ bool pending;
+ } frame;
+
#if HAVE_GL_WAYLAND
struct {
EGLSurface egl_surface;
diff --git a/video/out/win_state.c b/video/out/win_state.c
index af54f82..f48f628 100644
--- a/video/out/win_state.c
+++ b/video/out/win_state.c
@@ -90,6 +90,8 @@ void vo_calc_window_geometry(struct vo *vo, const struct mp_rect *screen,
int d_h = params.d_h;
if ((vo->driver->caps & VO_CAP_ROTATE90) && params.rotate % 180 == 90)
MPSWAP(int, d_w, d_h);
+ d_w = MPCLAMP(d_w * opts->window_scale, 1, 16000);
+ d_h = MPCLAMP(d_h * opts->window_scale, 1, 16000);
int scr_w = screen->x1 - screen->x0;
int scr_h = screen->y1 - screen->y0;
diff --git a/video/out/x11_common.c b/video/out/x11_common.c
index d6988ab..36a4970 100644
--- a/video/out/x11_common.c
+++ b/video/out/x11_common.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
@@ -39,6 +38,10 @@
#include "vo.h"
#include "win_state.h"
#include "osdep/timer.h"
+#include "osdep/subprocess.h"
+
+// Specifically for mp_cancel
+#include "stream/stream.h"
#include <X11/Xmd.h>
#include <X11/Xlib.h>
@@ -471,6 +474,30 @@ static void vo_x11_get_bounding_monitors(struct vo_x11_state *x11, long b[4])
#endif
}
+static void *screensaver_thread(void *arg)
+{
+ struct vo_x11_state *x11 = arg;
+
+ for (;;) {
+ sem_wait(&x11->screensaver_sem);
+ // don't queue multiple wakeups
+ while (!sem_trywait(&x11->screensaver_sem)) {}
+
+ if (mp_cancel_test(x11->screensaver_terminate))
+ break;
+
+ char *args[] = {"xdg-screensaver", "reset", NULL};
+ if (mp_subprocess(args, x11->screensaver_terminate, NULL, NULL,
+ NULL, &(char*){0}))
+ {
+ MP_WARN(x11, "Disabling screensaver failed.\n");
+ break;
+ }
+ }
+
+ return NULL;
+}
+
int vo_x11_init(struct vo *vo)
{
struct mp_vo_opts *opts = vo->opts;
@@ -488,6 +515,14 @@ int vo_x11_init(struct vo *vo)
};
vo->x11 = x11;
+ x11->screensaver_terminate = mp_cancel_new(x11);
+ sem_init(&x11->screensaver_sem, 0, 0);
+ if (pthread_create(&x11->screensaver_thread, NULL, screensaver_thread, x11)) {
+ x11->screensaver_terminate = NULL;
+ sem_destroy(&x11->screensaver_sem);
+ goto error;
+ }
+
x11_error_output = x11->log;
XSetErrorHandler(x11_errorhandler);
@@ -499,13 +534,7 @@ int vo_x11_init(struct vo *vo)
if (!x11->display) {
MP_MSG(x11, vo->probing ? MSGL_V : MSGL_ERR,
"couldn't open the X11 display (%s)!\n", dispName);
-
- x11_error_output = NULL;
- XSetErrorHandler(NULL);
-
- talloc_free(x11);
- vo->x11 = NULL;
- return 0;
+ goto error;
}
x11->screen = DefaultScreen(x11->display); // screen ID
x11->rootwin = RootWindow(x11->display, x11->screen); // root window ID
@@ -544,6 +573,10 @@ int vo_x11_init(struct vo *vo)
vo_x11_update_geometry(vo);
return 1;
+
+error:
+ vo_x11_uninit(vo);
+ return 0;
}
static const struct mp_keymap keymap[] = {
@@ -692,9 +725,18 @@ void vo_x11_uninit(struct vo *vo)
MP_VERBOSE(x11, "uninit ...\n");
if (x11->xim)
XCloseIM(x11->xim);
- x11_error_output = NULL;
- XSetErrorHandler(NULL);
- XCloseDisplay(x11->display);
+ if (x11->display) {
+ x11_error_output = NULL;
+ XSetErrorHandler(NULL);
+ XCloseDisplay(x11->display);
+ }
+
+ if (x11->screensaver_terminate) {
+ mp_cancel_trigger(x11->screensaver_terminate);
+ sem_post(&x11->screensaver_sem);
+ pthread_join(x11->screensaver_thread, NULL);
+ sem_destroy(&x11->screensaver_sem);
+ }
talloc_free(x11);
vo->x11 = NULL;
@@ -936,6 +978,11 @@ int vo_x11_check_events(struct vo *vo)
x11->win_drag_button1_down = false;
mp_input_put_key(vo->input_ctx, MP_KEY_MOUSE_LEAVE);
break;
+ case EnterNotify:
+ if (Event.xcrossing.mode != NotifyNormal)
+ break;
+ mp_input_put_key(vo->input_ctx, MP_KEY_MOUSE_ENTER);
+ break;
case ButtonPress:
if (Event.xbutton.button == 1)
x11->win_drag_button1_down = true;
@@ -1334,7 +1381,7 @@ static void vo_x11_map_window(struct vo *vo, struct mp_rect rc)
// map window
int events = StructureNotifyMask | ExposureMask | PropertyChangeMask |
- LeaveWindowMask;
+ LeaveWindowMask | EnterWindowMask;
if (mp_input_mouse_enabled(vo->input_ctx))
events |= PointerMotionMask | ButtonPressMask | ButtonReleaseMask;
if (mp_input_vo_keyboard_enabled(vo->input_ctx))
@@ -1762,7 +1809,7 @@ static void xscreensaver_heartbeat(struct vo_x11_state *x11)
(time - x11->screensaver_time_last) >= 10)
{
x11->screensaver_time_last = time;
-
+ sem_post(&x11->screensaver_sem);
XResetScreenSaver(x11->display);
}
}
@@ -1786,7 +1833,7 @@ static int xss_suspend(Display *mDisplay, Bool suspend)
static void set_screensaver(struct vo_x11_state *x11, bool enabled)
{
Display *mDisplay = x11->display;
- if (x11->screensaver_enabled == enabled)
+ if (!mDisplay || x11->screensaver_enabled == enabled)
return;
MP_VERBOSE(x11, "%s screensaver.\n", enabled ? "Enabling" : "Disabling");
x11->screensaver_enabled = enabled;
diff --git a/video/out/x11_common.h b/video/out/x11_common.h
index 095872c..eeff773 100644
--- a/video/out/x11_common.h
+++ b/video/out/x11_common.h
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MPLAYER_X11_COMMON_H
@@ -21,9 +20,12 @@
#include <stdint.h>
#include <stdbool.h>
+#include <pthread.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
+#include "osdep/semaphore.h"
+
#include "common/common.h"
struct vo;
@@ -58,6 +60,9 @@ struct vo_x11_state {
bool screensaver_enabled;
bool dpms_touched;
double screensaver_time_last;
+ pthread_t screensaver_thread;
+ sem_t screensaver_sem;
+ struct mp_cancel *screensaver_terminate;
XIM xim;
XIC xic;
diff --git a/video/sws_utils.c b/video/sws_utils.c
index 6d8329c..8997b20 100644
--- a/video/sws_utils.c
+++ b/video/sws_utils.c
@@ -1,19 +1,18 @@
/*
- * This file is part of MPlayer.
+ * This file is part of mpv.
*
- * MPlayer is free software; you can redistribute it and/or modify
+ * 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.
*
- * MPlayer is distributed in the hope that it will be useful,
+ * 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <assert.h>
diff --git a/video/vdpau.c b/video/vdpau.c
index b55152b..2fd75a2 100644
--- a/video/vdpau.c
+++ b/video/vdpau.c
@@ -70,7 +70,7 @@ static struct mp_image *download_image(struct mp_hwdec_ctx *hwctx,
mpi, &in) < 0)
goto error;
- res = mp_image_pool_get(swpool, IMGFMT_BGR32, ctx->getimg_w, ctx->getimg_h);
+ res = mp_image_pool_get(swpool, IMGFMT_BGR0, ctx->getimg_w, ctx->getimg_h);
if (!res)
goto error;
@@ -164,8 +164,7 @@ static int win_x11_init_vdpau_procs(struct mp_vdpau_ctx *ctx)
ctx->vdp = vdp;
ctx->get_proc_address = get_proc_address;
- vdp_st = vdp.preemption_callback_register(ctx->vdp_device,
- preemption_callback, ctx);
+ vdp.preemption_callback_register(ctx->vdp_device, preemption_callback, ctx);
return 0;
}
diff --git a/waftools/checks/custom.py b/waftools/checks/custom.py
index d168d9e..97c65e3 100644
--- a/waftools/checks/custom.py
+++ b/waftools/checks/custom.py
@@ -54,7 +54,7 @@ def check_lua(ctx, dependency_identifier):
( '51', 'lua >= 5.1.0 lua < 5.2.0'),
( '51deb', 'lua5.1 >= 5.1.0'), # debian
( '51fbsd', 'lua-5.1 >= 5.1.0'), # FreeBSD
- ( '52', 'lua >= 5.2.0' ),
+ ( '52', 'lua >= 5.2.0 lua < 5.3.0' ),
( '52deb', 'lua5.2 >= 5.2.0'), # debian
( '52fbsd', 'lua-5.2 >= 5.2.0'), # FreeBSD
( 'luajit', 'luajit >= 2.0.0' ),
diff --git a/waftools/detections/compiler.py b/waftools/detections/compiler.py
index 7c25c3d..7cae75e 100644
--- a/waftools/detections/compiler.py
+++ b/waftools/detections/compiler.py
@@ -39,7 +39,7 @@ def __add_generic_flags__(ctx):
def __add_gcc_flags__(ctx):
ctx.env.CFLAGS += ["-Wall", "-Wundef", "-Wmissing-prototypes", "-Wshadow",
- "-Wno-switch", "-Wno-parentheses", "-Wpointer-arith",
+ "-Wno-switch", "-Wparentheses", "-Wpointer-arith",
"-Wredundant-decls", "-Wno-pointer-sign"]
def __add_clang_flags__(ctx):
@@ -53,8 +53,8 @@ def __add_mswin_flags__(ctx):
def __add_mingw_flags__(ctx):
__add_mswin_flags__(ctx)
- ctx.env.CFLAGS += ['-D__USE_MINGW_ANSI_STDIO=1']
- ctx.env.LAST_LINKFLAGS += ['-mwindows']
+ ctx.env.CFLAGS += ['-municode', '-D__USE_MINGW_ANSI_STDIO=1']
+ ctx.env.LAST_LINKFLAGS += ['-municode', '-mwindows']
def __add_cygwin_flags__(ctx):
__add_mswin_flags__(ctx)
diff --git a/waftools/generators/headers.py b/waftools/generators/headers.py
index 0046cbf..546dd03 100644
--- a/waftools/generators/headers.py
+++ b/waftools/generators/headers.py
@@ -39,11 +39,21 @@ def __write_version_h__(ctx):
def __escape_c_string(s):
return s.replace("\"", "\\\"").replace("\n", "\\n")
+def __get_features_string__(ctx):
+ from inflectors import DependencyInflector
+ stuff = []
+ for dependency_identifier in ctx.satisfied_deps:
+ defkey = DependencyInflector(dependency_identifier).define_key()
+ if ctx.is_defined(defkey) and ctx.get_define(defkey) == "1":
+ stuff.append(dependency_identifier)
+ stuff.sort()
+ return " ".join(stuff)
+
def __add_mpv_defines__(ctx):
from sys import argv
ctx.define("CONFIGURATION", " ".join(argv))
ctx.define("MPV_CONFDIR", ctx.env.CONFDIR)
- ctx.define("FULLCONFIG", "\\n" + __escape_c_string(ctx.get_config_header()) + "\\n")
+ ctx.define("FULLCONFIG", __escape_c_string(__get_features_string__(ctx)))
def configure(ctx):
__add_mpv_defines__(ctx)
diff --git a/waftools/generators/sources.py b/waftools/generators/sources.py
index 25b08e7..6f15210 100644
--- a/waftools/generators/sources.py
+++ b/waftools/generators/sources.py
@@ -2,15 +2,15 @@ from waflib.Build import BuildContext
import os
def __file2string_cmd__(ctx):
- return "${{BIN_PERL}} {0}/TOOLS/file2string.pl ${{SRC}} > ${{TGT}}" \
+ return '"${{BIN_PERL}}" "{0}/TOOLS/file2string.pl" "${{SRC}}" > "${{TGT}}"' \
.format(ctx.srcnode.abspath())
def __matroska_cmd__(ctx, argument):
- return "${{BIN_PERL}} {0}/TOOLS/matroska.pl {1} ${{SRC}} > ${{TGT}}" \
+ return '"${{BIN_PERL}}" "{0}/TOOLS/matroska.pl" "{1}" "${{SRC}}" > "${{TGT}}"' \
.format(ctx.srcnode.abspath(), argument)
def __zshcomp_cmd__(ctx, argument):
- return "${{BIN_PERL}} {0}/TOOLS/zsh.pl {1} > ${{TGT}}" \
+ return '"${{BIN_PERL}}" "{0}/TOOLS/zsh.pl" "{1}" > "${{TGT}}"' \
.format(ctx.srcnode.abspath(), argument)
def __file2string__(ctx, **kwargs):
diff --git a/wscript b/wscript
index d3cc354..56f0320 100644
--- a/wscript
+++ b/wscript
@@ -83,7 +83,7 @@ build_options = [
}, {
'name': '--test',
'desc': 'test suite (using cmocka)',
- 'func': check_pkg_config('cmocka >= 0.4.1'),
+ 'func': check_pkg_config('cmocka', '>= 0.4.1'),
'default': 'disable',
}, {
'name': '--clang-database',
@@ -120,17 +120,22 @@ main_dependencies = [
'struct pollfd pfd; poll(&pfd, 1, 0); fork(); int f[2]; pipe(f); munmap(f,0)'),
}, {
'name': 'posix-or-mingw',
- 'desc': 'programming environment',
+ 'desc': 'development environment',
'deps_any': [ 'posix', 'mingw' ],
'func': check_true,
'req': True,
'fmsg': 'Unable to find either POSIX or MinGW-w64 environment, ' \
'or compiler does not work.',
}, {
+ 'name': 'win32',
+ 'desc': 'win32',
+ 'deps_any': [ 'os-win32', 'os-cygwin' ],
+ 'func': check_cc(lib=['winmm', 'gdi32', 'ole32', 'uuid']),
+ }, {
'name': '--win32-internal-pthreads',
'desc': 'internal pthread wrapper for win32 (Vista+)',
'deps_neg': [ 'posix' ],
- 'deps': [ 'mingw' ],
+ 'deps': [ 'win32' ],
'func': check_true,
'default': 'disable',
}, {
@@ -208,7 +213,13 @@ iconv support use --disable-iconv.",
'name': 'posix-spawn',
'desc': 'POSIX spawnp()/kill()',
'func': check_statement(['spawn.h', 'signal.h'],
- 'posix_spawnp(0,0,0,0,0,0); kill(0,0)')
+ 'posix_spawnp(0,0,0,0,0,0); kill(0,0)'),
+ 'deps_neg': ['mingw'],
+ }, {
+ 'name': 'subprocess',
+ 'desc': 'posix_spawnp() or MinGW',
+ 'func': check_true,
+ 'deps_any': ['posix-spawn', 'mingw'],
}, {
'name': 'glob',
'desc': 'glob()',
@@ -220,6 +231,15 @@ iconv support use --disable-iconv.",
'deps_any': [ 'os-win32', 'os-cygwin' ],
'func': check_true
}, {
+ 'name': 'fchmod',
+ 'desc': 'fchmod()',
+ 'func': check_statement('sys/stat.h', 'fchmod(0, 0)'),
+ }, {
+ 'name': 'vt.h',
+ 'desc': 'vt.h',
+ 'func': check_statement(['sys/vt.h', 'sys/ioctl.h'],
+ 'int m; ioctl(0, VT_GETMODE, &m)'),
+ }, {
'name': 'glibc-thread-name',
'desc': 'GLIBC API for setting thread name',
'func': check_statement('pthread.h',
@@ -273,10 +293,11 @@ iconv support use --disable-iconv.",
}, {
'name': '--libass',
'desc': 'SSA/ASS support',
- 'func': check_pkg_config('libass'),
+ 'func': check_pkg_config('libass', '>= 0.12.1'),
'req': True,
- 'fmsg': "Unable to find development files for libass. Aborting. \
-If you really mean to compile without libass support use --disable-libass."
+ 'fmsg': "Unable to find development files for libass, or the version " +
+ "found is too old. Aborting. If you really mean to compile " +
+ "without libass support use --disable-libass."
}, {
'name': '--libass-osd',
'desc': 'libass OSD support',
@@ -299,15 +320,6 @@ If you really mean to compile without libass support use --disable-libass."
'desc' : 'Encoding',
'func': check_true,
}, {
- 'name' : '--joystick',
- 'desc' : 'joystick',
- 'func': check_cc(header_name='linux/joystick.h'),
- 'default': 'disable'
- }, {
- 'name' : '--lirc',
- 'desc' : 'lirc',
- 'func': check_cc(header_name='lirc/lirc_client.h', lib='lirc_client'),
- }, {
'name': '--libbluray',
'desc': 'Bluray support',
'func': check_pkg_config('libbluray', '>= 0.3.0'),
@@ -329,14 +341,14 @@ If you really mean to compile without libass support use --disable-libass."
'desc': 'ENCA support',
'func': check_statement('enca.h', 'enca_get_languages(NULL)', lib='enca'),
}, {
- 'name': '--mpg123',
- 'desc': 'mpg123 support',
- 'func': check_pkg_config('libmpg123', '>= 1.14.0'),
- }, {
'name': '--ladspa',
'desc': 'LADSPA plugin support',
'func': check_statement('ladspa.h', 'LADSPA_Descriptor ld = {0}'),
}, {
+ 'name': '--rubberband',
+ 'desc': 'librubberband support',
+ 'func': check_pkg_config('rubberband', '>= 1.8.0'),
+ }, {
'name': '--libbs2b',
'desc': 'libbs2b audio filter support',
'func': check_pkg_config('libbs2b'),
@@ -347,7 +359,7 @@ If you really mean to compile without libass support use --disable-libass."
}, {
'name': 'vapoursynth-core',
'desc': 'VapourSynth filter bridge (core)',
- 'func': check_pkg_config('vapoursynth >= 23'),
+ 'func': check_pkg_config('vapoursynth >= 24'),
}, {
'name': '--vapoursynth',
'desc': 'VapourSynth filter bridge (Python)',
@@ -362,11 +374,12 @@ If you really mean to compile without libass support use --disable-libass."
]
libav_pkg_config_checks = [
- 'libavutil', '>= 52.48.101',
- 'libavcodec', '>= 55.34.1',
- 'libavformat', '>= 55.12.0',
- 'libswscale', '>= 2.1.2'
+ 'libavutil', '>= 54.02.0',
+ 'libavcodec', '>= 56.1.0',
+ 'libavformat', '>= 56.01.0',
+ 'libswscale', '>= 2.1.3'
]
+libav_versions_string = "FFmpeg 2.4 or Libav 11"
libav_dependencies = [
{
@@ -375,15 +388,15 @@ libav_dependencies = [
'func': check_pkg_config(*libav_pkg_config_checks),
'req': True,
'fmsg': "Unable to find development files for some of the required \
-Libav libraries ({0}). Aborting.".format(" ".join(libav_pkg_config_checks))
+FFmpeg/Libav libraries. You need at least {0}. Aborting.".format(libav_versions_string)
}, {
'name': '--libswresample',
'desc': 'libswresample',
- 'func': check_pkg_config('libswresample', '>= 0.17.104'),
+ 'func': check_pkg_config('libswresample', '>= 1.1.100'),
}, {
'name': '--libavresample',
'desc': 'libavresample',
- 'func': check_pkg_config('libavresample', '>= 1.1.0'),
+ 'func': check_pkg_config('libavresample', '>= 2.1.0'),
'deps_neg': ['libswresample'],
}, {
'name': 'resampler',
@@ -395,11 +408,11 @@ Libav libraries ({0}). Aborting.".format(" ".join(libav_pkg_config_checks))
}, {
'name': '--libavfilter',
'desc': 'libavfilter',
- 'func': check_pkg_config('libavfilter', '>= 3.90.100'),
+ 'func': check_pkg_config('libavfilter', '>= 5.0.0'),
}, {
'name': '--libavdevice',
'desc': 'libavdevice',
- 'func': check_pkg_config('libavdevice', '>= 54.0.0'),
+ 'func': check_pkg_config('libavdevice', '>= 55.0.0'),
}, {
'name': 'avcodec-chroma-pos-api',
'desc': 'libavcodec avcodec_enum_to_chroma_pos API',
@@ -407,42 +420,6 @@ Libav libraries ({0}). Aborting.".format(" ".join(libav_pkg_config_checks))
avcodec_enum_to_chroma_pos(&x, &y, AVCHROMA_LOC_UNSPECIFIED)""",
use='libav')
}, {
- 'name': 'avcol-spc-bt2020',
- 'desc': 'libavcodec avcol_spc_bt2020 available',
- 'func': check_statement('libavcodec/avcodec.h',
- 'int x = AVCOL_SPC_BT2020_NCL',
- use='libav')
- }, {
- 'name': 'avcodec-vdpau-alloc-context',
- 'desc': 'libavcodec vdpau non-sense',
- 'func': check_statement('libavcodec/vdpau.h',
- 'AVVDPAUContext *x = av_vdpau_alloc_context()',
- use='libav')
- }, {
- 'name': 'avcodec-metadata-update-side-data',
- 'desc': 'libavcodec AV_PKT_DATA_METADATA_UPDATE side data type',
- 'func': check_statement('libavcodec/avcodec.h',
- 'enum AVPacketSideDataType type = AV_PKT_DATA_METADATA_UPDATE',
- use='libav')
- }, {
- 'name': 'avformat-metadata-update-flag',
- 'desc': "libavformat metadata update flags",
- 'func': check_statement('libavformat/avformat.h',
- 'int x = AVFMT_EVENT_FLAG_METADATA_UPDATED',
- use='libav')
- }, {
- 'name': 'avcodec-replaygain-side-data',
- 'desc': 'libavcodec AV_PKT_DATA_REPLAYGAIN side data type',
- 'func': check_statement('libavcodec/avcodec.h',
- 'enum AVPacketSideDataType type = AV_PKT_DATA_REPLAYGAIN',
- use='libav')
- }, {
- 'name': 'av-displaymatrix',
- 'desc': 'libavutil/libavcodec display matrix side data',
- 'func': check_statement('libavutil/frame.h',
- 'enum AVFrameSideDataType type = AV_FRAME_DATA_DISPLAYMATRIX',
- use='libav')
- },{
'name': 'avframe-metadata',
'desc': 'libavutil AVFrame metadata',
'func': check_statement('libavutil/frame.h',
@@ -454,6 +431,12 @@ Libav libraries ({0}). Aborting.".format(" ".join(libav_pkg_config_checks))
'func': check_statement('libavutil/frame.h',
'enum AVFrameSideDataType type = AV_FRAME_DATA_SKIP_SAMPLES',
use='libav')
+ }, {
+ 'name': 'av-pix-fmt-mmal',
+ 'desc': 'libavutil AV_PIX_FMT_MMAL',
+ 'func': check_statement('libavutil/pixfmt.h',
+ 'int x = AV_PIX_FMT_MMAL',
+ use='libav'),
}
]
@@ -528,7 +511,7 @@ audio_output_features = [
}, {
'name': '--alsa',
'desc': 'ALSA audio output',
- 'func': check_pkg_config('alsa >= 1.0.18'),
+ 'func': check_pkg_config('alsa', '>= 1.0.18'),
}, {
'name': '--coreaudio',
'desc': 'CoreAudio audio output',
@@ -543,8 +526,8 @@ audio_output_features = [
}, {
'name': '--wasapi',
'desc': 'WASAPI audio output',
- 'deps': ['atomics'],
- 'func': check_cc(fragment=load_fragment('wasapi.c'), lib='ole32'),
+ 'deps': ['win32', 'atomics'],
+ 'func': check_cc(fragment=load_fragment('wasapi.c')),
}
]
@@ -554,22 +537,6 @@ video_output_features = [
'desc': 'Cocoa',
'func': check_cocoa
} , {
- 'name': 'gdi',
- 'desc': 'GDI',
- 'func': check_cc(lib='gdi32')
- } , {
- 'name': 'winmm',
- 'desc': 'WinMM',
- 'func': check_cc(lib='winmm')
- } , {
- 'name': 'ole',
- 'desc': 'OLE',
- 'func': check_cc(lib='ole32')
- } , {
- 'name': 'uuid',
- 'desc': 'UUID',
- 'func': check_cc(lib='uuid')
- } , {
'name': '--wayland',
'desc': 'Wayland',
'func': check_pkg_config('wayland-client', '>= 1.6.0',
@@ -635,16 +602,11 @@ video_output_features = [
} , {
'name': '--gl-win32',
'desc': 'OpenGL Win32 Backend',
- 'deps': [ 'gdi', 'winmm', 'ole', 'uuid' ],
+ 'deps': [ 'win32' ],
'groups': [ 'gl' ],
'func': check_statement('windows.h', 'wglCreateContext(0)',
lib='opengl32')
} , {
- 'name': '--gl',
- 'desc': 'OpenGL video outputs',
- 'deps_any': [ 'gl-cocoa', 'gl-x11', 'gl-win32', 'gl-wayland' ],
- 'func': check_true
- } , {
'name': '--vdpau',
'desc': 'VDPAU acceleration',
'deps': [ 'x11' ],
@@ -669,12 +631,17 @@ video_output_features = [
'name': '--vaapi-glx',
'desc': 'VAAPI GLX',
'deps': [ 'vaapi', 'gl-x11' ],
- 'func': check_pkg_config('libva-glx', '>= 0.32.0'),
+ 'func': check_true,
}, {
'name': '--caca',
'desc': 'CACA',
'func': check_pkg_config('caca', '>= 0.99.beta18'),
}, {
+ 'name': '--drm',
+ 'desc': 'DRM',
+ 'deps': [ 'vt.h' ],
+ 'func': check_pkg_config('libdrm'),
+ }, {
'name': '--jpeg',
'desc': 'JPEG support',
'func': check_cc(header_name=['stdio.h', 'jpeglib.h'],
@@ -682,8 +649,41 @@ video_output_features = [
}, {
'name': '--direct3d',
'desc': 'Direct3D support',
- 'deps': [ 'gdi', 'winmm', 'ole', 'uuid' ],
+ 'deps': [ 'win32' ],
'func': check_cc(header_name='d3d9.h'),
+ }, {
+ # 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
+ # including them (compensate with -isystem and -fgnu89-inline).
+ 'name': '--rpi',
+ 'desc': 'Raspberry Pi support',
+ 'func':
+ check_cc(cflags="-isystem/opt/vc/include/ "+
+ "-isystem/opt/vc/include/interface/vcos/pthreads " +
+ "-isystem/opt/vc/include/interface/vmcs_host/linux " +
+ "-fgnu89-inline",
+ linkflags="-L/opt/vc/lib",
+ header_name="bcm_host.h",
+ lib=['mmal_core', 'mmal_util', 'mmal_vc_client', 'bcm_host']),
+ }, {
+ 'name': '--rpi-gles',
+ 'desc': 'GLES on Raspberry Pi',
+ 'groups': [ 'gl' ],
+ 'deps': ['rpi'],
+ # We still need all OpenGL symbols, because the vo_opengl code is
+ # generic and supports anything from GLES2/OpenGL 2.1 to OpenGL 4 core.
+ 'func': compose_checks(
+ check_cc(lib="EGL"),
+ check_cc(lib="GLESv2"),
+ 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': '--gl',
+ 'desc': 'OpenGL video outputs',
+ 'deps_any': [ 'gl-cocoa', 'gl-x11', 'gl-win32', 'gl-wayland', 'rpi-gles' ],
+ 'func': check_true
}
]
@@ -717,7 +717,7 @@ hwaccel_features = [
}, {
'name': '--dxva2-hwaccel',
'desc': 'libavcodec DXVA2 hwaccel',
- 'deps': [ 'gdi' ],
+ 'deps': [ 'win32' ],
'func': check_headers('libavcodec/dxva2.h', use='libav'),
}
]
diff --git a/wscript_build.py b/wscript_build.py
index ffb19a9..7328be1 100644
--- a/wscript_build.py
+++ b/wscript_build.py
@@ -54,10 +54,6 @@ def build(ctx):
target = "input/input_conf.h")
ctx.file2string(
- source = "video/out/gl_video_shaders.glsl",
- target = "video/out/gl_video_shaders.h")
-
- ctx.file2string(
source = "sub/osd_font.otf",
target = "sub/osd_font.h")
@@ -75,6 +71,10 @@ def build(ctx):
source = "demux/ebml.c",
target = "ebml_defs.c")
+ main_fn_c = {
+ 'win32': 'player/main-fn-win.c',
+ }.get(ctx.env.DEST_OS, "player/main-fn-unix.c")
+
getch2_c = {
'win32': 'osdep/terminal-win.c',
}.get(ctx.env.DEST_OS, "osdep/terminal-unix.c")
@@ -94,7 +94,6 @@ def build(ctx):
( "audio/format.c" ),
( "audio/mixer.c" ),
( "audio/decode/ad_lavc.c" ),
- ( "audio/decode/ad_mpg123.c", "mpg123" ),
( "audio/decode/ad_spdif.c" ),
( "audio/decode/dec_audio.c" ),
( "audio/filter/af.c" ),
@@ -109,7 +108,6 @@ def build(ctx):
( "audio/filter/af_equalizer.c" ),
( "audio/filter/af_export.c" ),
( "audio/filter/af_extrastereo.c" ),
- ( "audio/filter/af_forcespeed.c" ),
( "audio/filter/af_format.c" ),
( "audio/filter/af_hrtf.c" ),
( "audio/filter/af_karaoke.c" ),
@@ -118,6 +116,7 @@ def build(ctx):
( "audio/filter/af_lavfi.c", "libavfilter" ),
( "audio/filter/af_lavrresample.c" ),
( "audio/filter/af_pan.c" ),
+ ( "audio/filter/af_rubberband.c", "rubberband" ),
( "audio/filter/af_scaletempo.c" ),
( "audio/filter/af_sinesuppress.c" ),
( "audio/filter/af_sub.c" ),
@@ -175,6 +174,7 @@ def build(ctx):
( "demux/demux_mkv_timeline.c" ),
( "demux/demux_playlist.c" ),
( "demux/demux_raw.c" ),
+ ( "demux/demux_rar.c" ),
( "demux/demux_subreader.c" ),
( "demux/demux_tv.c", "tv" ),
( "demux/ebml.c" ),
@@ -189,8 +189,6 @@ def build(ctx):
( "input/ipc.c", "!mingw" ),
( "input/keycodes.c" ),
( "input/pipe-win32.c", "waio" ),
- ( "input/joystick.c", "joystick" ),
- ( "input/lirc.c", "lirc" ),
## Misc
( "misc/bstr.c" ),
@@ -289,6 +287,7 @@ def build(ctx):
( "video/vdpau_mixer.c", "vdpau" ),
( "video/decode/dec_video.c"),
( "video/decode/dxva2.c", "dxva2-hwaccel" ),
+ ( "video/decode/rpi.c", "rpi" ),
( "video/decode/vaapi.c", "vaapi-hwaccel" ),
( "video/decode/vd_lavc.c" ),
( "video/decode/vda.c", "vda-hwaccel" ),
@@ -306,13 +305,12 @@ def build(ctx):
( "video/filter/vf_gradfun.c", "libavfilter"),
( "video/filter/vf_hqdn3d.c", "libavfilter"),
( "video/filter/vf_lavfi.c", "libavfilter"),
- ( "video/filter/vf_mirror.c" ),
+ ( "video/filter/vf_mirror.c", "libavfilter"),
( "video/filter/vf_noformat.c" ),
( "video/filter/vf_noise.c", "libavfilter"),
( "video/filter/vf_pullup.c", "libavfilter"),
( "video/filter/vf_rotate.c", "libavfilter"),
( "video/filter/vf_scale.c" ),
- ( "video/filter/vf_screenshot.c" ),
( "video/filter/vf_stereo3d.c" ),
( "video/filter/vf_sub.c" ),
( "video/filter/vf_unsharp.c", "libavfilter"),
@@ -330,13 +328,14 @@ def build(ctx):
( "video/out/filter_kernels.c" ),
( "video/out/gl_cocoa.c", "gl-cocoa" ),
( "video/out/gl_common.c", "gl" ),
+ ( "video/out/gl_rpi.c", "rpi-gles" ),
( "video/out/gl_hwdec.c", "gl" ),
( "video/out/gl_hwdec_vaglx.c", "vaapi-glx" ),
( "video/out/gl_hwdec_vda.c", "vda-gl" ),
( "video/out/gl_hwdec_vdpau.c", "vdpau-gl-x11" ),
( "video/out/gl_lcms.c", "gl" ),
( "video/out/gl_osd.c", "gl" ),
- ( "video/out/gl_utils.c", "gl" ),
+ ( "video/out/gl_utils.c", "gl" ),
( "video/out/gl_video.c", "gl" ),
( "video/out/gl_w32.c", "gl-win32" ),
( "video/out/gl_wayland.c", "gl-wayland" ),
@@ -344,9 +343,11 @@ def build(ctx):
( "video/out/gl_x11egl.c", "egl-x11" ),
( "video/out/vo.c" ),
( "video/out/vo_caca.c", "caca" ),
+ ( "video/out/vo_drm.c", "drm" ),
( "video/out/vo_direct3d.c", "direct3d" ),
( "video/out/vo_image.c" ),
( "video/out/vo_lavc.c", "encoding" ),
+ ( "video/out/vo_rpi.c", "rpi" ),
( "video/out/vo_null.c" ),
( "video/out/vo_opengl.c", "gl" ),
( "video/out/vo_opengl_cb.c", "gl" ),
@@ -356,12 +357,13 @@ def build(ctx):
( "video/out/vo_wayland.c", "wayland" ),
( "video/out/vo_x11.c" , "x11" ),
( "video/out/vo_xv.c", "xv" ),
- ( "video/out/w32_common.c", "gdi" ),
+ ( "video/out/w32_common.c", "win32" ),
( "video/out/wayland_common.c", "wayland" ),
( "video/out/wayland/buffer.c", "wayland" ),
( "video/out/wayland/memfile.c", "wayland" ),
( "video/out/win_state.c"),
( "video/out/x11_common.c", "x11" ),
+ ( "video/out/drm_common.c", "drm" ),
## osdep
( getch2_c ),
@@ -374,6 +376,7 @@ def build(ctx):
( "osdep/macosx_application.m", "cocoa-application" ),
( "osdep/macosx_events.m", "cocoa" ),
( "osdep/semaphore_osx.c" ),
+ ( "osdep/subprocess.c" ),
( "osdep/subprocess-posix.c", "posix-spawn" ),
( "osdep/subprocess-win.c", "os-win32" ),
( "osdep/path-macosx.m", "cocoa" ),
@@ -421,7 +424,7 @@ def build(ctx):
if ctx.dependency_satisfied('cplayer'):
ctx(
target = "mpv",
- source = "player/main_fn.c",
+ source = main_fn_c,
use = ctx.dependencies_use() + ['objects'],
includes = _all_includes(ctx),
features = "c cprogram",