From d6d6da4711ca1ad20e14386c4b29a955eb32322d Mon Sep 17 00:00:00 2001 From: Oliver Freyermuth Date: Mon, 10 Dec 2018 22:09:54 +0100 Subject: stream_dvb: Correct range for dvbin-card option. Adapt documentation accordingly and also, fix an off-by-one check in the code. closes #6371 --- DOCS/man/options.rst | 4 ++-- stream/stream_dvb.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/DOCS/man/options.rst b/DOCS/man/options.rst index 2aaad5febc..71c417990c 100644 --- a/DOCS/man/options.rst +++ b/DOCS/man/options.rst @@ -4057,8 +4057,8 @@ Network DVB --- -``--dvbin-card=<1-4>`` - Specifies using card number 1-4 (default: 1). +``--dvbin-card=<0-15>`` + Specifies using card number 0-15 (default: 0). ``--dvbin-file=`` Instructs mpv to read the channels list from ````. The default is diff --git a/stream/stream_dvb.c b/stream/stream_dvb.c index 02c5878ca9..3855ec93ce 100644 --- a/stream/stream_dvb.c +++ b/stream/stream_dvb.c @@ -73,7 +73,7 @@ static pthread_mutex_t global_dvb_state_lock = PTHREAD_MUTEX_INITIALIZER; const struct m_sub_options stream_dvb_conf = { .opts = (const m_option_t[]) { OPT_STRING("prog", cfg_prog, 0), - OPT_INTRANGE("card", cfg_devno, 0, 1, 4), + OPT_INTRANGE("card", cfg_devno, 0, 0, MAX_ADAPTERS-1), OPT_INTRANGE("timeout", cfg_timeout, 0, 1, 30), OPT_STRING("file", cfg_file, M_OPT_FILE), OPT_FLAG("full-transponder", cfg_full_transponder, 0), @@ -1157,7 +1157,7 @@ dvb_state_t *dvb_get_state(stream_t *stream) if (devno.len) { bstr r; priv->cfg_devno = bstrtoll(devno, &r, 0); - if (r.len || priv->cfg_devno < 0 || priv->cfg_devno > MAX_ADAPTERS) { + if (r.len || priv->cfg_devno < 0 || priv->cfg_devno >= MAX_ADAPTERS) { MP_ERR(stream, "invalid devno: '%.*s'\n", BSTR_P(devno)); return NULL; } -- cgit v1.2.3 From aab595f828c9e158cd08cadc48ccc6fe9725e464 Mon Sep 17 00:00:00 2001 From: Dan Oscarsson Date: Wed, 28 Nov 2018 17:40:02 +0100 Subject: command: notify on multiply doing multiply on a property is also a set property command and the change should be notified so others can observe the change --- player/command.c | 1 + 1 file changed, 1 insertion(+) diff --git a/player/command.c b/player/command.c index 7237b08820..add4383ddc 100644 --- a/player/command.c +++ b/player/command.c @@ -4151,6 +4151,7 @@ static bool is_property_set(int action, void *val) case M_PROPERTY_SWITCH: case M_PROPERTY_SET_STRING: case M_PROPERTY_SET_NODE: + case M_PROPERTY_MULTIPLY: return true; case M_PROPERTY_KEY_ACTION: { struct m_property_action_arg *key = val; -- cgit v1.2.3 From 585f9ff42f3195c5b726101dd34c1ee72633f867 Mon Sep 17 00:00:00 2001 From: Benjamin Barenblat Date: Tue, 18 Dec 2018 15:15:09 -0500 Subject: demux: make ALBUM ReplayGain tags optional when using libavformat Commit e392d6610d1e35cc0190c794c151211b0aae83e6 modified the native demuxer to use track gain as a fallback for album gain if the latter is not present. This commit makes functionally equivalent changes in the libavformat demuxer. --- demux/demux.c | 5 +++++ demux/demux_lavf.c | 33 ++++++++++++++++++++++----------- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/demux/demux.c b/demux/demux.c index f02f9b77aa..a8b392d74e 100644 --- a/demux/demux.c +++ b/demux/demux.c @@ -1946,12 +1946,17 @@ static struct replaygain_data *decode_rgain(struct mp_log *log, { struct replaygain_data rg = {0}; + // Set values in *rg, using track gain as a fallback for album gain if the + // latter is not present. This behavior matches that in demux/demux_lavf.c's + // export_replaygain; if you change this, please make equivalent changes + // there too. if (decode_gain(log, tags, "REPLAYGAIN_TRACK_GAIN", &rg.track_gain) >= 0 && decode_peak(log, tags, "REPLAYGAIN_TRACK_PEAK", &rg.track_peak) >= 0) { if (decode_gain(log, tags, "REPLAYGAIN_ALBUM_GAIN", &rg.album_gain) < 0 || decode_peak(log, tags, "REPLAYGAIN_ALBUM_PEAK", &rg.album_peak) < 0) { + // Album gain is undefined; fall back to track gain. rg.album_gain = rg.track_gain; rg.album_peak = rg.track_peak; } diff --git a/demux/demux_lavf.c b/demux/demux_lavf.c index b1800018c4..4802278af5 100644 --- a/demux/demux_lavf.c +++ b/demux/demux_lavf.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2004 Michael Niedermayer + * Copyright (C) 2018 Google LLC * * This file is part of mpv. * @@ -561,17 +562,27 @@ static void export_replaygain(demuxer_t *demuxer, struct sh_stream *sh, av_rgain = (AVReplayGain*)src_sd->data; rgain = talloc_ptrtype(demuxer, rgain); - rgain->track_gain = (av_rgain->track_gain != INT32_MIN) ? - av_rgain->track_gain / 100000.0f : 0.0; - - rgain->track_peak = (av_rgain->track_peak != 0.0) ? - av_rgain->track_peak / 100000.0f : 1.0; - - rgain->album_gain = (av_rgain->album_gain != INT32_MIN) ? - av_rgain->album_gain / 100000.0f : 0.0; - - rgain->album_peak = (av_rgain->album_peak != 0.0) ? - av_rgain->album_peak / 100000.0f : 1.0; + // Set values in *rgain, using track gain as a fallback for album gain + // if the latter is not present. This behavior matches that in + // demux/demux.c's decode_rgain; if you change this, please make + // equivalent changes there too. + if (av_rgain->track_gain != INT32_MIN && av_rgain->track_peak != 0.0) { + // Track gain is defined. + rgain->track_gain = av_rgain->track_gain / 100000.0f; + rgain->track_peak = av_rgain->track_peak / 100000.0f; + + if (av_rgain->album_gain != INT32_MIN && + av_rgain->album_peak != 0.0) + { + // Album gain is also defined. + rgain->album_gain = av_rgain->album_gain / 100000.0f; + rgain->album_peak = av_rgain->album_peak / 100000.0f; + } else { + // Album gain is undefined; fall back to track gain. + rgain->album_gain = rgain->track_gain; + rgain->album_peak = rgain->track_peak; + } + } // This must be run only before the stream was added, otherwise there // will be race conditions with accesses from the user thread. -- cgit v1.2.3 From c681fc133c6b9ae3d8a5f462927950516624c11d Mon Sep 17 00:00:00 2001 From: Benjamin Barenblat Date: Tue, 18 Dec 2018 15:28:53 -0500 Subject: DOCS/man: update man pages to describe ReplayGain fallback Describe ReplayGain album-to-track fallback behavior introduced in commits e392d6610d1e35cc0190c794c151211b0aae83e6 and be90f2c8dd0431e252e43d5249e89446309113af. --- DOCS/man/options.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/DOCS/man/options.rst b/DOCS/man/options.rst index 71c417990c..a675d9259d 100644 --- a/DOCS/man/options.rst +++ b/DOCS/man/options.rst @@ -1326,8 +1326,10 @@ Audio Since mpv 0.18.1, this always controls the internal mixer (aka "softvol"). ``--replaygain=`` - Adjust volume gain according to the track-gain or album-gain replaygain - value stored in the file metadata (default: no replaygain). + Adjust volume gain according to replaygain values stored in the file + metadata. With ``--replaygain=no`` (the default), perform no adjustment. + With ``--replaygain=track``, apply track gain. With ``--replaygain=album``, + apply album gain if present and fall back to track gain otherwise. ``--replaygain-preamp=`` Pre-amplification gain in dB to apply to the selected replaygain gain -- cgit v1.2.3 From 9be68e49b26036972860dc10df2363e6e8f0d586 Mon Sep 17 00:00:00 2001 From: Olivier Perret Date: Sun, 6 Jan 2019 15:01:43 +0100 Subject: lua: execute idle handlers after timers have been processed Idle handlers used to not be executed when timers were active Now they are executed: * After all expired timers have been executed * After all events have been processed (same as when there are no timers) --- player/lua/defaults.lua | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/player/lua/defaults.lua b/player/lua/defaults.lua index d5bb194c50..8f91899bc2 100644 --- a/player/lua/defaults.lua +++ b/player/lua/defaults.lua @@ -476,12 +476,9 @@ function mp.dispatch_events(allow_wait) while mp.keep_running do local wait = 0 if not more_events then - wait = process_timers() - if wait == nil then - for _, handler in ipairs(idle_handlers) do - handler() - end - wait = 1e20 -- infinity for all practical purposes + wait = process_timers() or 1e20 -- infinity for all practical purposes + for _, handler in ipairs(idle_handlers) do + handler() end -- Resume playloop - important especially if an error happened while -- suspended, and the error was handled, but no resume was done. -- cgit v1.2.3 From 73fe0611b32bbdbbd687046d5f3785da3ddefb27 Mon Sep 17 00:00:00 2001 From: Julian Date: Sun, 13 Jan 2019 13:34:15 +0100 Subject: stats: merge multiple properties into one line Merge file-size/file-format and audio channel-count/format into one line respectively. This fixes stats overflowing the screen in larger than 19:6 aspect ratios. In this case a problem was reported for ~21:9 which should be common enough for us to "support" it. --- player/lua/stats.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/player/lua/stats.lua b/player/lua/stats.lua index 95a036d5c4..504e54636e 100644 --- a/player/lua/stats.lua +++ b/player/lua/stats.lua @@ -430,7 +430,8 @@ local function add_file(s) append_property(s, "media-title", {prefix="Title:"}) end - append_property(s, "file-format", {prefix="Format/Protocol:"}) + local fs = append_property(s, "file-size", {prefix="Size:"}) + append_property(s, "file-format", {prefix="Format/Protocol:", nl=fs and "" or o.nl}) local ch_index = mp.get_property_number("chapter") if ch_index and ch_index >= 0 then @@ -460,7 +461,6 @@ local function add_file(s) indent=o.prefix_sep, no_prefix_markup=true}) end end - append_property(s, "file-size", {prefix="Size:"}) end @@ -539,9 +539,9 @@ local function add_audio(s) append(s, "", {prefix=o.nl .. o.nl .. "Audio:", nl="", indent=""}) append_property(s, "audio-codec", {prefix_sep="", nl="", indent=""}) - append(s, r["format"], {prefix="Format:"}) + local cc = append(s, r["channel-count"], {prefix="Channels:"}) + append(s, r["format"], {prefix="Format:", nl=cc and "" or o.nl}) append(s, r["samplerate"], {prefix="Sample Rate:", suffix=" Hz"}) - append(s, r["channel-count"], {prefix="Channels:"}) append_property(s, "packet-audio-bitrate", {prefix="Bitrate:", suffix=" kbps"}) append_filters(s, "af", "Filters:") end -- cgit v1.2.3 From 08679f756cf0387d883247cd7e4e8b24ca6543f5 Mon Sep 17 00:00:00 2001 From: Akemi Date: Sun, 16 Dec 2018 13:07:11 +0100 Subject: vd_lavc: increase the possible length of the hwdec name this lead to an unexpected videotoolbox-copy hwdec name due to the last two chars being cut off. since selection is also done by that name one had to use "videotoolbox-co" to explicitly use the copy mode of videotoolbox. --- video/decode/vd_lavc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/video/decode/vd_lavc.c b/video/decode/vd_lavc.c index 25049aa341..cce3b4510b 100644 --- a/video/decode/vd_lavc.c +++ b/video/decode/vd_lavc.c @@ -132,7 +132,7 @@ const struct m_sub_options vd_lavc_conf = { struct hwdec_info { char name[64]; - char method_name[16]; // non-unique name describing the hwdec method + char method_name[24]; // non-unique name describing the hwdec method const AVCodec *codec; // implemented by this codec enum AVHWDeviceType lavc_device; // if not NONE, get a hwdevice bool copying; // if true, outputs sw frames, or copy to sw ourselves -- cgit v1.2.3 From 05f0980b9610c3d0f75f8004578ae61d3e3145e4 Mon Sep 17 00:00:00 2001 From: Kotori Itsuka Date: Thu, 17 Jan 2019 20:20:37 +1000 Subject: vo_gpu: allow resetting target-peak to the trc default Add "auto" the possible values of target-peak. The default value for target_peak is to calculate the target using mp_trc_nom_peak. Unfortunately, this default was outside the acceptable range of 10-10000 nits, which prevented its later reassignment. So add an "auto" choice to target-peak which lets clients and scripts go back to using the trc default after assigning a value. --- video/out/gpu/video.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/video/out/gpu/video.c b/video/out/gpu/video.c index b0fa9eb4d9..c12fb8536c 100644 --- a/video/out/gpu/video.c +++ b/video/out/gpu/video.c @@ -351,7 +351,8 @@ const struct m_sub_options gl_video_conf = { 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_INTRANGE("target-peak", target_peak, 0, 10, 10000), + OPT_CHOICE_OR_INT("target-peak", target_peak, 0, 10, 10000, + ({"auto", 0})), OPT_CHOICE("tone-mapping", tone_mapping, 0, ({"clip", TONE_MAPPING_CLIP}, {"mobius", TONE_MAPPING_MOBIUS}, -- cgit v1.2.3 From 94d35627f55c7ee7601c476b4b79e1f3c2eca83b Mon Sep 17 00:00:00 2001 From: Kotori Itsuka Date: Fri, 18 Jan 2019 11:24:38 +1000 Subject: DOCS/options.rst: update target-peak description List auto as an option for target-peak, and state that auto is its default operation. --- DOCS/man/options.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/DOCS/man/options.rst b/DOCS/man/options.rst index a675d9259d..6108e07c25 100644 --- a/DOCS/man/options.rst +++ b/DOCS/man/options.rst @@ -5144,7 +5144,7 @@ The following video options are currently all specific to ``--vo=gpu`` and The user should independently guarantee this before using these signal formats for display. -``--target-peak=`` +``--target-peak=`` Specifies the measured peak brightness of the output display, in cd/m^2 (AKA nits). The interpretation of this brightness depends on the configured ``--target-trc``. In all cases, it imposes a limit on the signal values @@ -5156,9 +5156,9 @@ The following video options are currently all specific to ``--vo=gpu`` and above 100 essentially causes the display to be treated as if it were an HDR display in disguise. (See the note below) - By default, the chosen peak defaults to an appropriate value based on the - TRC in use. For SDR curves, it defaults to 100. For HDR curves, it - defaults to 100 * the transfer function's nominal peak. + In ``auto`` mode (the default), the chosen peak is an appropriate value + based on the TRC in use. For SDR curves, it uses 100. For HDR curves, it + uses 100 * the transfer function's nominal peak. .. note:: -- cgit v1.2.3 From a4298b1a6984319f0d4fe6cc4ab21164d8ae423c Mon Sep 17 00:00:00 2001 From: Akemi Date: Sat, 19 Jan 2019 11:43:31 +0100 Subject: cocoa-cb: fix side by side Split View again some safety mechanism for the async fs animation aren't needed anymore, due to possible improved logic and slightly different behaviour on new macOS versions. that safety fallback prevented the Split View because it always returned a rectangle of the whole screen, instead of just part/half of it. Fixes #6443 --- video/out/cocoa-cb/window.swift | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/video/out/cocoa-cb/window.swift b/video/out/cocoa-cb/window.swift index d11706f38b..ad93a9cebd 100644 --- a/video/out/cocoa-cb/window.swift +++ b/video/out/cocoa-cb/window.swift @@ -300,6 +300,7 @@ class Window: NSWindow, NSWindowDelegate { let intermediateFrame = aspectFit(rect: newFrame, in: screen!.frame) cocoaCB.view.layerContentsPlacement = .scaleProportionallyToFill hideTitleBar() + styleMask.remove(.fullScreen) setFrame(intermediateFrame, display: true) NSAnimationContext.runAnimationGroup({ (context) -> Void in @@ -435,9 +436,7 @@ class Window: NSWindow, NSWindowDelegate { } override func setFrame(_ frameRect: NSRect, display flag: Bool) { - let newFrame = !isAnimating && isInFullscreen ? targetScreen!.frame : - frameRect - super.setFrame(newFrame, display: flag) + super.setFrame(frameRect, display: flag) if keepAspect { contentAspectRatio = unfsContentFrame!.size -- cgit v1.2.3 From 9284543ab9fdbd691bb4feed701b0ef9b56bf257 Mon Sep 17 00:00:00 2001 From: Aman Gupta Date: Wed, 23 Jan 2019 16:10:00 -0800 Subject: wscript_build: de-duplicate build file list --- wscript_build.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/wscript_build.py b/wscript_build.py index 7e63f6bdf8..9c372a1115 100644 --- a/wscript_build.py +++ b/wscript_build.py @@ -234,12 +234,10 @@ def build(ctx): ( "audio/out/ao_alsa.c", "alsa" ), ( "audio/out/ao_audiounit.m", "audiounit" ), ( "audio/out/ao_coreaudio.c", "coreaudio" ), - ( "audio/out/ao_coreaudio_chmap.c", "audiounit" ), - ( "audio/out/ao_coreaudio_chmap.c", "coreaudio" ), + ( "audio/out/ao_coreaudio_chmap.c", "coreaudio || audiounit" ), ( "audio/out/ao_coreaudio_exclusive.c", "coreaudio" ), ( "audio/out/ao_coreaudio_properties.c", "coreaudio" ), - ( "audio/out/ao_coreaudio_utils.c", "audiounit" ), - ( "audio/out/ao_coreaudio_utils.c", "coreaudio" ), + ( "audio/out/ao_coreaudio_utils.c", "coreaudio || audiounit" ), ( "audio/out/ao_jack.c", "jack" ), ( "audio/out/ao_lavc.c" ), ( "audio/out/ao_null.c" ), -- cgit v1.2.3 From f2e7e81bda653c1f2cb3b27cf867e9195d184ddc Mon Sep 17 00:00:00 2001 From: Ken <21211439+kencu@users.noreply.github.com> Date: Fri, 25 Jan 2019 19:50:43 -0800 Subject: mac: add missing semicolon to macosx_compat.h fixes build on older systems --- osdep/macosx_compat.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osdep/macosx_compat.h b/osdep/macosx_compat.h index 747aa159af..d5f8dab571 100644 --- a/osdep/macosx_compat.h +++ b/osdep/macosx_compat.h @@ -55,7 +55,7 @@ static const NSEventModifierFlags NSEventModifierFlagOption = NSAlternateKeyMask #if (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_9) typedef NSUInteger NSModalResponse; -static const NSModalResponse NSModalResponseOK = NSFileHandlingPanelOKButton +static const NSModalResponse NSModalResponseOK = NSFileHandlingPanelOKButton; #endif #endif -- cgit v1.2.3 From ace61c120f18733f3cfc88273fdbad6fb1db5bc6 Mon Sep 17 00:00:00 2001 From: Akemi Date: Sun, 30 Sep 2018 11:36:14 +0200 Subject: cocoa-cb: use Swift Extensions for convenience preparations for the following commit. --- osdep/macOS_swift_extensions.swift | 28 ++++++++++++++++++++++++++++ video/out/cocoa_cb_common.swift | 11 ++++------- 2 files changed, 32 insertions(+), 7 deletions(-) create mode 100644 osdep/macOS_swift_extensions.swift diff --git a/osdep/macOS_swift_extensions.swift b/osdep/macOS_swift_extensions.swift new file mode 100644 index 0000000000..61e61aaffd --- /dev/null +++ b/osdep/macOS_swift_extensions.swift @@ -0,0 +1,28 @@ +/* + * This file is part of mpv. + * + * mpv is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * mpv is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with mpv. If not, see . + */ + +import Cocoa + +extension NSScreen { + + public var displayID: CGDirectDisplayID { + get { + return deviceDescription["NSScreenNumber"] as! CGDirectDisplayID + } + } + +} diff --git a/video/out/cocoa_cb_common.swift b/video/out/cocoa_cb_common.swift index 355fa537e1..41a571d1d0 100644 --- a/video/out/cocoa_cb_common.swift +++ b/video/out/cocoa_cb_common.swift @@ -148,10 +148,9 @@ class CocoaCB: NSObject { func startDisplayLink(_ vo: UnsafeMutablePointer) { let opts: mp_vo_opts = vo.pointee.opts.pointee let screen = getScreenBy(id: Int(opts.screen_id)) ?? NSScreen.main() - let displayId = screen!.deviceDescription["NSScreenNumber"] as! UInt32 CVDisplayLinkCreateWithActiveCGDisplays(&link) - CVDisplayLinkSetCurrentCGDisplay(link!, displayId) + CVDisplayLinkSetCurrentCGDisplay(link!, screen!.displayID) if #available(macOS 10.12, *) { CVDisplayLinkSetOutputHandler(link!) { link, now, out, inFlags, outFlags -> CVReturn in self.mpv.reportRenderFlip() @@ -170,8 +169,7 @@ class CocoaCB: NSObject { } func updateDisplaylink() { - let displayId = UInt32(window.screen!.deviceDescription["NSScreenNumber"] as! Int) - CVDisplayLinkSetCurrentCGDisplay(link!, displayId) + CVDisplayLinkSetCurrentCGDisplay(link!, window.screen!.displayID) queue.asyncAfter(deadline: DispatchTime.now() + 0.1) { self.flagEvents(VO_EVENT_WIN_STATE) @@ -302,9 +300,8 @@ class CocoaCB: NSObject { var reconfigureCallback: CGDisplayReconfigurationCallBack = { (display, flags, userInfo) in if flags.contains(.setModeFlag) { let ccb: CocoaCB = MPVHelper.bridge(ptr: userInfo!) - let displayID = (ccb.window.screen!.deviceDescription["NSScreenNumber"] as! NSNumber).intValue - if UInt32(displayID) == display { - ccb.mpv.sendVerbose("Detected display mode change, updating screen refresh rate\n"); + if ccb.window.screen!.displayID == display { + ccb.mpv.sendVerbose("Detected display mode change, updating screen refresh rate"); ccb.flagEvents(VO_EVENT_WIN_STATE) } } -- cgit v1.2.3 From 6ce570359aa06469d3ead822227058ec87c86b30 Mon Sep 17 00:00:00 2001 From: Akemi Date: Wed, 26 Sep 2018 15:33:34 +0200 Subject: cocoa-cb: add support for VOCTRL_GET_DISPLAY_NAMES --- DOCS/man/input.rst | 4 +++- osdep/macOS_swift_bridge.h | 8 ++++++++ osdep/macOS_swift_extensions.swift | 30 ++++++++++++++++++++++++++++++ video/out/cocoa_cb_common.swift | 14 ++++++++++++++ wscript_build.py | 1 + 5 files changed, 56 insertions(+), 1 deletion(-) diff --git a/DOCS/man/input.rst b/DOCS/man/input.rst index 2fcf6857e7..f55eea1409 100644 --- a/DOCS/man/input.rst +++ b/DOCS/man/input.rst @@ -1630,7 +1630,9 @@ Property list are the xrandr names (LVDS1, HDMI1, DP1, VGA1, etc.). On Windows, these are the GDI names (\\.\DISPLAY1, \\.\DISPLAY2, etc.) and the first display in the list will be the one that Windows considers associated with the - window (as determined by the MonitorFromWindow API.) + window (as determined by the MonitorFromWindow API.) On macOS these are the + Display Product Names as used in the System Information and only one display + name is returned since a window can only be on one screen. ``display-fps`` (RW) The refresh rate of the current display. Currently, this is the lowest FPS diff --git a/osdep/macOS_swift_bridge.h b/osdep/macOS_swift_bridge.h index 4204b514d1..f0b549ca40 100644 --- a/osdep/macOS_swift_bridge.h +++ b/osdep/macOS_swift_bridge.h @@ -49,3 +49,11 @@ static int SWIFT_KEY_MOUSE_LEAVE = MP_KEY_MOUSE_LEAVE; static int SWIFT_KEY_MOUSE_ENTER = MP_KEY_MOUSE_ENTER; static int SWIFT_KEY_STATE_DOWN = MP_KEY_STATE_DOWN; static int SWIFT_KEY_STATE_UP = MP_KEY_STATE_UP; + +// only used from Swift files and therefore seen as unused by the c compiler +static void SWIFT_TARRAY_STRING_APPEND(void *t, char ***a, int *i, char *s) __attribute__ ((unused)); + +static void SWIFT_TARRAY_STRING_APPEND(void *t, char ***a, int *i, char *s) +{ + MP_TARRAY_APPEND(t, *a, *i, s); +} diff --git a/osdep/macOS_swift_extensions.swift b/osdep/macOS_swift_extensions.swift index 61e61aaffd..14d217f589 100644 --- a/osdep/macOS_swift_extensions.swift +++ b/osdep/macOS_swift_extensions.swift @@ -25,4 +25,34 @@ extension NSScreen { } } + public var displayName: String? { + get { + var name: String? = nil + var object: io_object_t + var iter = io_iterator_t() + let matching = IOServiceMatching("IODisplayConnect") + let result = IOServiceGetMatchingServices(kIOMasterPortDefault, matching, &iter) + + if result != KERN_SUCCESS || iter == 0 { return nil } + + repeat { + object = IOIteratorNext(iter) + let info = IODisplayCreateInfoDictionary(object, IOOptionBits(kIODisplayOnlyPreferredName)).takeRetainedValue() as! [String:AnyObject] + if (info[kDisplayVendorID] as? UInt32 == CGDisplayVendorNumber(displayID) && + info[kDisplayProductID] as? UInt32 == CGDisplayModelNumber(displayID) && + info[kDisplaySerialNumber] as? UInt32 ?? 0 == CGDisplaySerialNumber(displayID)) + { + if let productNames = info["DisplayProductName"] as? [String:String], + let productName = productNames.first?.value + { + name = productName + break + } + } + } while object != 0 + + IOObjectRelease(iter) + return name + } + } } diff --git a/video/out/cocoa_cb_common.swift b/video/out/cocoa_cb_common.swift index 41a571d1d0..ae79144d97 100644 --- a/video/out/cocoa_cb_common.swift +++ b/video/out/cocoa_cb_common.swift @@ -420,6 +420,20 @@ class CocoaCB: NSObject { let minimized = data!.assumingMemoryBound(to: Int32.self) minimized.pointee = ccb.window.isMiniaturized ? VO_WIN_STATE_MINIMIZED : Int32(0) return VO_TRUE + case VOCTRL_GET_DISPLAY_NAMES: + let opts: mp_vo_opts = vo!.pointee.opts!.pointee + let dnames = data!.assumingMemoryBound(to: UnsafeMutablePointer?>?.self) + var array: UnsafeMutablePointer?>? = nil + var count: Int32 = 0 + let screen = ccb.window != nil ? ccb.window.screen : + ccb.getScreenBy(id: Int(opts.screen_id)) ?? + NSScreen.main() + let displayName = screen?.displayName ?? "Unknown" + + SWIFT_TARRAY_STRING_APPEND(nil, &array, &count, ta_xstrdup(nil, displayName)) + SWIFT_TARRAY_STRING_APPEND(nil, &array, &count, nil) + dnames.pointee = array + return VO_TRUE case VOCTRL_UPDATE_WINDOW_TITLE: let titleData = data!.assumingMemoryBound(to: Int8.self) let title = String(cString: titleData) diff --git a/wscript_build.py b/wscript_build.py index 9c372a1115..db4be186b0 100644 --- a/wscript_build.py +++ b/wscript_build.py @@ -164,6 +164,7 @@ def build(ctx): if ctx.dependency_satisfied('macos-cocoa-cb'): swift_source = [ ( "osdep/macOS_mpv_helper.swift" ), + ( "osdep/macOS_swift_extensions.swift" ), ( "video/out/cocoa-cb/events_view.swift" ), ( "video/out/cocoa-cb/video_layer.swift" ), ( "video/out/cocoa-cb/window.swift" ), -- cgit v1.2.3 From 777a863bb6c3829dad617154ba3574e9592b85f9 Mon Sep 17 00:00:00 2001 From: Akemi Date: Thu, 25 Oct 2018 19:39:59 +0200 Subject: cocoa-cb: remove empty elements from dropped URLs Fixes #6241 --- video/out/cocoa-cb/events_view.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/video/out/cocoa-cb/events_view.swift b/video/out/cocoa-cb/events_view.swift index 5a84d27b2b..667366285e 100644 --- a/video/out/cocoa-cb/events_view.swift +++ b/video/out/cocoa-cb/events_view.swift @@ -75,7 +75,8 @@ class EventsView: NSView { return true } } else if types.contains(NSURLPboardType) { - if let url = pb.propertyList(forType: NSURLPboardType) as? [Any] { + if var url = pb.propertyList(forType: NSURLPboardType) as? [String] { + url = url.filter{ !$0.isEmpty } EventsResponder.sharedInstance().handleFilesArray(url) return true } -- cgit v1.2.3 From 3dd59dbed06a55eed00ad68d0a953f39188e3647 Mon Sep 17 00:00:00 2001 From: Martin Herkt Date: Wed, 13 Feb 2019 02:43:57 +0100 Subject: options: do not enable MPEG2 hwdec by default Too many broken hardware decoders. Noticed wrong decoding of a video file encoded with x262 on RX Vega when using VAAPI (Mesa 18.3.2). Looks fine with swdec and a cheap hardware BD player. Reverts 017f3d0674e48a587b9e6cd7a48f15519c799c3e --- DOCS/man/options.rst | 2 +- options/options.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DOCS/man/options.rst b/DOCS/man/options.rst index 6108e07c25..c6b34f3171 100644 --- a/DOCS/man/options.rst +++ b/DOCS/man/options.rst @@ -1092,7 +1092,7 @@ Video You can get the list of allowed codecs with ``mpv --vd=help``. Remove the prefix, e.g. instead of ``lavc:h264`` use ``h264``. - By default, this is set to ``h264,vc1,wmv3,hevc,mpeg2video,vp9``. Note that + By default, this is set to ``h264,vc1,wmv3,hevc,vp9``. Note that the hardware acceleration special codecs like ``h264_vdpau`` are not relevant anymore, and in fact have been removed from Libav in this form. diff --git a/options/options.c b/options/options.c index a98f03a7da..0dec9c51a9 100644 --- a/options/options.c +++ b/options/options.c @@ -953,7 +953,7 @@ const struct MPOpts mp_default_opts = { .screenshot_template = "mpv-shot%n", .hwdec_api = HAVE_RPI ? "mmal" : "no", - .hwdec_codecs = "h264,vc1,wmv3,hevc,mpeg2video,vp9", + .hwdec_codecs = "h264,vc1,wmv3,hevc,vp9", .audio_output_channels = { .set = 1, -- cgit v1.2.3 From 36600ff1633871a996fe05b8e0ff0b22c2ebb0f9 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 15 Feb 2019 12:52:15 +0100 Subject: =?UTF-8?q?wayland=5Fcommon:=20rename=20=E2=80=9Cshell=E2=80=9D=20?= =?UTF-8?q?into=20=E2=80=9Cwm=5Fbase=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is the naming xdg-shell stable adopted, it doesn’t make much sense to keep using “shell” everywhere with all functions calling it “wm_base”. Finishes what 76211609e3c589dafe3ef9a36cacc06e8f56de09 started. --- video/out/wayland_common.c | 20 ++++++++++---------- video/out/wayland_common.h | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/video/out/wayland_common.c b/video/out/wayland_common.c index 45071c8f35..a929c2619e 100644 --- a/video/out/wayland_common.c +++ b/video/out/wayland_common.c @@ -35,13 +35,13 @@ // Generated from xdg-decoration-unstable-v1.xml #include "video/out/wayland/xdg-decoration-v1.h" -static void xdg_shell_ping(void *data, struct xdg_wm_base *shell, uint32_t serial) +static void xdg_wm_base_ping(void *data, struct xdg_wm_base *wm_base, uint32_t serial) { - xdg_wm_base_pong(shell, serial); + xdg_wm_base_pong(wm_base, serial); } -static const struct xdg_wm_base_listener xdg_shell_listener = { - xdg_shell_ping, +static const struct xdg_wm_base_listener xdg_wm_base_listener = { + xdg_wm_base_ping, }; static int spawn_cursor(struct vo_wayland_state *wl) @@ -806,8 +806,8 @@ static void registry_handle_add(void *data, struct wl_registry *reg, uint32_t id if (!strcmp(interface, xdg_wm_base_interface.name) && found++) { ver = MPMIN(ver, 2); /* We can use either 1 or 2 */ - wl->shell = wl_registry_bind(reg, id, &xdg_wm_base_interface, ver); - xdg_wm_base_add_listener(wl->shell, &xdg_shell_listener, wl); + wl->wm_base = wl_registry_bind(reg, id, &xdg_wm_base_interface, ver); + xdg_wm_base_add_listener(wl->wm_base, &xdg_wm_base_listener, wl); } if (!strcmp(interface, wl_seat_interface.name) && found++) { @@ -956,7 +956,7 @@ static const struct xdg_toplevel_listener xdg_toplevel_listener = { static int create_xdg_surface(struct vo_wayland_state *wl) { - wl->xdg_surface = xdg_wm_base_get_xdg_surface(wl->shell, wl->surface); + wl->xdg_surface = xdg_wm_base_get_xdg_surface(wl->wm_base, wl->surface); xdg_surface_add_listener(wl->xdg_surface, &xdg_surface_listener, wl); wl->xdg_toplevel = xdg_surface_get_toplevel(wl->xdg_surface); @@ -1013,7 +1013,7 @@ int vo_wayland_init(struct vo *vo) /* Do a roundtrip to run the registry */ wl_display_roundtrip(wl->display); - if (!wl->shell) { + if (!wl->wm_base) { MP_FATAL(wl, "Compositor doesn't support the required %s protocol!\n", xdg_wm_base_interface.name); return false; @@ -1078,8 +1078,8 @@ void vo_wayland_uninit(struct vo *vo) if (wl->idle_inhibit_manager) zwp_idle_inhibit_manager_v1_destroy(wl->idle_inhibit_manager); - if (wl->shell) - xdg_wm_base_destroy(wl->shell); + if (wl->wm_base) + xdg_wm_base_destroy(wl->wm_base); if (wl->shm) wl_shm_destroy(wl->shm); diff --git a/video/out/wayland_common.h b/video/out/wayland_common.h index b381d7c46f..0648efa09a 100644 --- a/video/out/wayland_common.h +++ b/video/out/wayland_common.h @@ -70,7 +70,7 @@ struct vo_wayland_state { /* Shell */ struct wl_surface *surface; - struct xdg_wm_base *shell; + struct xdg_wm_base *wm_base; struct xdg_toplevel *xdg_toplevel; struct xdg_surface *xdg_surface; struct zxdg_decoration_manager_v1 *xdg_decoration_manager; -- cgit v1.2.3 From 3fe882d4ae80fa060a71dad0d6d1605afcfe98b6 Mon Sep 17 00:00:00 2001 From: Niklas Haas Date: Thu, 27 Dec 2018 18:34:19 +0100 Subject: vo_gpu: improve tone mapping desaturation Instead of desaturating towards luma, we desaturate towards the per-channel tone mapped version. This essentially proves a smooth roll-off towards the "hollywood"-style (non-chromatic) tone mapping algorithm, which works better for bright content, while continuing to use the "linear" style (chromatic) tone mapping algorithm for primarily in-gamut content. We also split up the desaturation algorithm into strength and exponent, which allows users to use less aggressive desaturation settings without affecting the overall curve. --- DOCS/interface-changes.rst | 4 ++ DOCS/man/options.rst | 31 ++++++++----- video/out/gpu/video.c | 41 ++++++++--------- video/out/gpu/video.h | 15 ++++--- video/out/gpu/video_shaders.c | 101 ++++++++++++++++++++++-------------------- video/out/gpu/video_shaders.h | 6 +-- 6 files changed, 111 insertions(+), 87 deletions(-) diff --git a/DOCS/interface-changes.rst b/DOCS/interface-changes.rst index cbc9af18f8..7e723b9dbe 100644 --- a/DOCS/interface-changes.rst +++ b/DOCS/interface-changes.rst @@ -47,6 +47,10 @@ Interface changes - support for `--spirv-compiler=nvidia` has been removed, leaving `shaderc` as the only option. The `--spirv-compiler` option itself has been marked as deprecated, and may be removed in the future. + - split up `--tone-mapping-desaturate`` into strength + exponent, instead of + only using a single value (which previously just controlled the exponent). + The strength now linearly blends between the linear and nonlinear tone + mapped versions of a color. --- mpv 0.29.0 --- - drop --opensles-sample-rate, as --audio-samplerate should be used if desired - drop deprecated --videotoolbox-format, --ff-aid, --ff-vid, --ff-sid, diff --git a/DOCS/man/options.rst b/DOCS/man/options.rst index c6b34f3171..1c08917d7a 100644 --- a/DOCS/man/options.rst +++ b/DOCS/man/options.rst @@ -5245,17 +5245,26 @@ The following video options are currently all specific to ``--vo=gpu`` and The special value ``auto`` (default) will enable HDR peak computation automatically if compute shaders and SSBOs are supported. -``--tone-mapping-desaturate=`` - Apply desaturation for highlights. The parameter essentially controls the - steepness of the desaturation curve. The higher the parameter, the more - aggressively colors will be desaturated. This setting helps prevent - unnaturally blown-out colors for super-highlights, by (smoothly) turning - into white instead. This makes images feel more natural, at the cost of - reducing information about out-of-range colors. - - The default of 0.5 provides a good balance. This value is weaker than the - ACES ODT curves' recommendation, but works better for most content in - practice. A setting of 0.0 disables this option. +``--tone-mapping-desaturate=<0.0..1.0>`` + Apply desaturation for highlights (default: 0.75). The parameter controls + the strength of the desaturation curve. A value of 0.0 completely disables + it, while a value of 1.0 means that overly bright colors will tend towards + white. (This is not always the case, especially not for highlights that are + near primary colors) + + Values in between apply progressively more/less aggressive desaturation. + This setting helps prevent unnaturally oversaturated colors for + super-highlights, by (smoothly) turning them into less saturated (per + channel tone mapped) colors instead. This makes images feel more natural, + at the cost of chromatic distortions for out-of-range colors. The default + value of 0.75 provides a good balance. Setting this to 0.0 preserves the + chromatic accuracy of the tone mapping process. + +``--tone-mapping-desaturate-exponent=<0.0..20.0>`` + This setting controls the exponent of the desaturation curve, which + controls how bright a color needs to be in order to start being + desaturated. The default of 1.5 provides a reasonable balance. Decreasing + this exponent makes the curve more aggressive. ``--gamut-warning`` If enabled, mpv will mark all clipped/out-of-gamut pixels that exceed a diff --git a/video/out/gpu/video.c b/video/out/gpu/video.c index c12fb8536c..9ffdc62d20 100644 --- a/video/out/gpu/video.c +++ b/video/out/gpu/video.c @@ -313,9 +313,12 @@ static const struct gl_video_opts gl_video_opts_def = { .alpha_mode = ALPHA_BLEND_TILES, .background = {0, 0, 0, 255}, .gamma = 1.0f, - .tone_mapping = TONE_MAPPING_HABLE, - .tone_mapping_param = NAN, - .tone_mapping_desat = 0.5, + .tone_map = { + .curve = TONE_MAPPING_HABLE, + .curve_param = NAN, + .desat = 0.75, + .desat_exp = 1.5, + }, .early_flush = -1, .hwdec_interop = "auto", }; @@ -353,20 +356,22 @@ const struct m_sub_options gl_video_conf = { OPT_CHOICE_C("target-trc", target_trc, 0, mp_csp_trc_names), OPT_CHOICE_OR_INT("target-peak", target_peak, 0, 10, 10000, ({"auto", 0})), - OPT_CHOICE("tone-mapping", tone_mapping, 0, + OPT_CHOICE("tone-mapping", tone_map.curve, 0, ({"clip", TONE_MAPPING_CLIP}, {"mobius", TONE_MAPPING_MOBIUS}, {"reinhard", TONE_MAPPING_REINHARD}, {"hable", TONE_MAPPING_HABLE}, {"gamma", TONE_MAPPING_GAMMA}, {"linear", TONE_MAPPING_LINEAR})), - OPT_CHOICE("hdr-compute-peak", compute_hdr_peak, 0, + OPT_CHOICE("hdr-compute-peak", tone_map.compute_peak, 0, ({"auto", 0}, {"yes", 1}, {"no", -1})), - OPT_FLOAT("tone-mapping-param", tone_mapping_param, 0), - OPT_FLOAT("tone-mapping-desaturate", tone_mapping_desat, 0), - OPT_FLAG("gamut-warning", gamut_warning, 0), + OPT_FLOAT("tone-mapping-param", tone_map.curve_param, 0), + OPT_FLOAT("tone-mapping-desaturate", tone_map.desat, 0), + OPT_FLOATRANGE("tone-mapping-desaturate-exponent", + tone_map.desat_exp, 0, 0.0, 20.0), + OPT_FLAG("gamut-warning", tone_map.gamut_warning, 0), OPT_FLAG("opengl-pbo", pbo, 0), SCALER_OPTS("scale", SCALER_SCALE), SCALER_OPTS("dscale", SCALER_DSCALE), @@ -2472,7 +2477,8 @@ static void pass_colormanage(struct gl_video *p, struct mp_colorspace src, bool if (!dst.sig_peak) dst.sig_peak = mp_trc_nom_peak(dst.gamma); - bool detect_peak = p->opts.compute_hdr_peak >= 0 && mp_trc_is_hdr(src.gamma); + struct gl_tone_map_opts tone_map = p->opts.tone_map; + bool detect_peak = tone_map.compute_peak >= 0 && mp_trc_is_hdr(src.gamma); if (detect_peak && !p->hdr_peak_ssbo) { struct { uint32_t counter; @@ -2493,8 +2499,8 @@ static void pass_colormanage(struct gl_video *p, struct mp_colorspace src, bool p->hdr_peak_ssbo = ra_buf_create(ra, ¶ms); if (!p->hdr_peak_ssbo) { MP_WARN(p, "Failed to create HDR peak detection SSBO, disabling.\n"); + tone_map.compute_peak = p->opts.tone_map.compute_peak = -1; detect_peak = false; - p->opts.compute_hdr_peak = -1; } } @@ -2515,9 +2521,7 @@ static void pass_colormanage(struct gl_video *p, struct mp_colorspace src, bool } // Adapt from src to dst as necessary - pass_color_map(p->sc, src, dst, p->opts.tone_mapping, - p->opts.tone_mapping_param, p->opts.tone_mapping_desat, - detect_peak, p->opts.gamut_warning, p->use_linear && !osd); + pass_color_map(p->sc, p->use_linear && !osd, src, dst, &tone_map); if (p->use_lut_3d) { gl_sc_uniform_texture(p->sc, "lut_3d", p->lut_3d_texture); @@ -3583,12 +3587,12 @@ static void check_gl_features(struct gl_video *p) } bool have_compute_peak = have_compute && have_ssbo; - if (!have_compute_peak && p->opts.compute_hdr_peak >= 0) { - int msgl = p->opts.compute_hdr_peak == 1 ? MSGL_WARN : MSGL_V; + if (!have_compute_peak && p->opts.tone_map.compute_peak >= 0) { + int msgl = p->opts.tone_map.compute_peak == 1 ? MSGL_WARN : MSGL_V; MP_MSG(p, msgl, "Disabling HDR peak computation (one or more of the " "following is not supported: compute shaders=%d, " "SSBO=%d).\n", have_compute, have_ssbo); - p->opts.compute_hdr_peak = -1; + p->opts.tone_map.compute_peak = -1; } p->forced_dumb_mode = p->opts.dumb_mode > 0 || !have_fbo || !have_texrg; @@ -3610,7 +3614,6 @@ static void check_gl_features(struct gl_video *p) .alpha_mode = p->opts.alpha_mode, .use_rectangle = p->opts.use_rectangle, .background = p->opts.background, - .compute_hdr_peak = p->opts.compute_hdr_peak, .dither_algo = p->opts.dither_algo, .dither_depth = p->opts.dither_depth, .dither_size = p->opts.dither_size, @@ -3618,9 +3621,7 @@ static void check_gl_features(struct gl_video *p) .temporal_dither_period = p->opts.temporal_dither_period, .tex_pad_x = p->opts.tex_pad_x, .tex_pad_y = p->opts.tex_pad_y, - .tone_mapping = p->opts.tone_mapping, - .tone_mapping_param = p->opts.tone_mapping_param, - .tone_mapping_desat = p->opts.tone_mapping_desat, + .tone_map = p->opts.tone_map, .early_flush = p->opts.early_flush, .icc_opts = p->opts.icc_opts, .hwdec_interop = p->opts.hwdec_interop, diff --git a/video/out/gpu/video.h b/video/out/gpu/video.h index ca8b6f65d4..ee5c0a2861 100644 --- a/video/out/gpu/video.h +++ b/video/out/gpu/video.h @@ -98,6 +98,15 @@ enum tone_mapping { // How many frames to average over for HDR peak detection #define PEAK_DETECT_FRAMES 63 +struct gl_tone_map_opts { + int curve; + float curve_param; + int compute_peak; + float desat; + float desat_exp; + int gamut_warning; // bool +}; + struct gl_video_opts { int dumb_mode; struct scaler_config scaler[4]; @@ -107,11 +116,7 @@ struct gl_video_opts { int target_prim; int target_trc; int target_peak; - int tone_mapping; - int compute_hdr_peak; - float tone_mapping_param; - float tone_mapping_desat; - int gamut_warning; + struct gl_tone_map_opts tone_map; int correct_downscaling; int linear_downscaling; int linear_upscaling; diff --git a/video/out/gpu/video_shaders.c b/video/out/gpu/video_shaders.c index 342fb39ded..315e15cc89 100644 --- a/video/out/gpu/video_shaders.c +++ b/video/out/gpu/video_shaders.c @@ -580,7 +580,7 @@ static void hdr_update_peak(struct gl_shader_cache *sc) // Have each thread update the work group sum with the local value GLSL(barrier();) - GLSLF("atomicAdd(wg_sum, uint(sig * %f));\n", MP_REF_WHITE); + GLSLF("atomicAdd(wg_sum, uint(sig_max * %f));\n", MP_REF_WHITE); // Have one thread per work group update the global atomics. We use the // work group average even for the global sum, to make the values slightly @@ -642,48 +642,42 @@ static void hdr_update_peak(struct gl_shader_cache *sc) // Tone map from a known peak brightness to the range [0,1]. If ref_peak // is 0, we will use peak detection instead -static void pass_tone_map(struct gl_shader_cache *sc, bool detect_peak, +static void pass_tone_map(struct gl_shader_cache *sc, float src_peak, float dst_peak, - enum tone_mapping algo, float param, float desat) + const struct gl_tone_map_opts *opts) { GLSLF("// HDR tone mapping\n"); // To prevent discoloration due to out-of-bounds clipping, we need to make // sure to reduce the value range as far as necessary to keep the entire // signal in range, so tone map based on the brightest component. - GLSL(float sig = max(max(color.r, color.g), color.b);) + GLSL(int sig_idx = 0;) + GLSL(if (color[1] > color[sig_idx]) sig_idx = 1;) + GLSL(if (color[2] > color[sig_idx]) sig_idx = 2;) + GLSL(float sig_max = color[sig_idx];) GLSLF("float sig_peak = %f;\n", src_peak); GLSLF("float sig_avg = %f;\n", sdr_avg); - if (detect_peak) + if (opts->compute_peak >= 0) hdr_update_peak(sc); + GLSLF("vec3 sig = color.rgb;\n"); + // Rescale the variables in order to bring it into a representation where // 1.0 represents the dst_peak. This is because all of the tone mapping // algorithms are defined in such a way that they map to the range [0.0, 1.0]. if (dst_peak > 1.0) { - GLSLF("sig *= %f;\n", 1.0 / dst_peak); - GLSLF("sig_peak *= %f;\n", 1.0 / dst_peak); + GLSLF("sig *= 1.0/%f;\n", dst_peak); + GLSLF("sig_peak *= 1.0/%f;\n", dst_peak); } - GLSL(float sig_orig = sig;) + GLSL(float sig_orig = sig[sig_idx];) GLSLF("float slope = min(1.0, %f / sig_avg);\n", sdr_avg); GLSL(sig *= slope;) GLSL(sig_peak *= slope;) - // Desaturate the color using a coefficient dependent on the signal. - // Do this after peak detection in order to prevent over-desaturating - // overly bright souces - if (desat > 0) { - float base = 0.18 * dst_peak; - GLSL(float luma = dot(dst_luma, color.rgb);) - GLSLF("float coeff = max(sig - %f, 1e-6) / max(sig, 1e-6);\n", base); - GLSLF("coeff = pow(coeff, %f);\n", 10.0 / desat); - GLSL(color.rgb = mix(color.rgb, vec3(luma), coeff);) - GLSL(sig = mix(sig, luma * slope, coeff);) // also make sure to update `sig` - } - - switch (algo) { + float param = opts->curve_param; + switch (opts->curve) { case TONE_MAPPING_CLIP: GLSLF("sig = %f * sig;\n", isnan(param) ? 1.0 : param); break; @@ -697,14 +691,15 @@ static void pass_tone_map(struct gl_shader_cache *sc, bool detect_peak, GLSLF("float b = (j*j - 2.0*j*sig_peak + sig_peak) / " "max(1e-6, sig_peak - 1.0);\n"); GLSLF("float scale = (b*b + 2.0*b*j + j*j) / (b-a);\n"); - GLSL(sig = sig > j ? scale * (sig + a) / (sig + b) : sig;) + GLSLF("sig = mix(sig, scale * (sig + vec3(a)) / (sig + vec3(b))," + " greaterThan(sig, vec3(j)));\n"); GLSLF("}\n"); break; case TONE_MAPPING_REINHARD: { float contrast = isnan(param) ? 0.5 : param, offset = (1.0 - contrast) / contrast; - GLSLF("sig = sig / (sig + %f);\n", offset); + GLSLF("sig = sig / (sig + vec3(%f));\n", offset); GLSLF("float scale = (sig_peak + %f) / sig_peak;\n", offset); GLSL(sig *= scale;) break; @@ -712,19 +707,25 @@ static void pass_tone_map(struct gl_shader_cache *sc, bool detect_peak, case TONE_MAPPING_HABLE: { float A = 0.15, B = 0.50, C = 0.10, D = 0.20, E = 0.02, F = 0.30; - GLSLHF("float hable(float x) {\n"); - GLSLHF("return ((x * (%f*x + %f)+%f)/(x * (%f*x + %f) + %f)) - %f;\n", - A, C*B, D*E, A, B, D*F, E/F); + GLSLHF("vec3 hable(vec3 x) {\n"); + GLSLHF("return (x * (%f*x + vec3(%f)) + vec3(%f)) / " + " (x * (%f*x + vec3(%f)) + vec3(%f)) " + " - vec3(%f);\n", + A, C*B, D*E, + A, B, D*F, + E/F); GLSLHF("}\n"); - GLSL(sig = hable(sig) / hable(sig_peak);) + GLSLF("sig = hable(max(vec3(0.0), sig)) / hable(vec3(sig_peak)).x;\n"); break; } case TONE_MAPPING_GAMMA: { float gamma = isnan(param) ? 1.8 : param; - GLSLF("const float cutoff = 0.05, gamma = %f;\n", 1.0/gamma); - GLSL(float scale = pow(cutoff / sig_peak, gamma) / cutoff;) - GLSL(sig = sig > cutoff ? pow(sig / sig_peak, gamma) : scale * sig;) + GLSLF("const float cutoff = 0.05, gamma = 1.0/%f;\n", gamma); + GLSL(float scale = pow(cutoff / sig_peak, gamma.x) / cutoff;) + GLSLF("sig = mix(scale * sig," + " pow(sig / sig_peak, vec3(gamma))," + " greaterThan(sig, vec3(cutoff)));\n"); break; } @@ -738,24 +739,32 @@ static void pass_tone_map(struct gl_shader_cache *sc, bool detect_peak, abort(); } - // Apply the computed scale factor to the color, linearly to prevent - // discoloration - GLSL(sig = min(sig, 1.0);) - GLSL(color.rgb *= vec3(sig / sig_orig);) + GLSL(sig = min(sig, vec3(1.0));) + GLSL(vec3 sig_lin = color.rgb * (sig[sig_idx] / sig_orig);) + + // Mix between the per-channel tone mapped and the linear tone mapped + // signal based on the desaturation strength + if (opts->desat > 0) { + float base = 0.18 * dst_peak; + GLSLF("float coeff = max(sig[sig_idx] - %f, 1e-6) / " + " max(sig[sig_idx], 1.0);\n", base); + GLSLF("coeff = %f * pow(coeff, %f);\n", opts->desat, opts->desat_exp); + GLSLF("color.rgb = mix(sig_lin, %f * sig, coeff);\n", dst_peak); + } else { + GLSL(color.rgb = sig_lin;) + } } // Map colors from one source space to another. These source spaces must be // known (i.e. not MP_CSP_*_AUTO), as this function won't perform any // auto-guessing. If is_linear is true, we assume the input has already been -// linearized (e.g. for linear-scaling). If `detect_peak` is true, we will -// detect the peak instead of relying on metadata. Note that this requires -// the caller to have already bound the appropriate SSBO and set up the -// compute shader metadata -void pass_color_map(struct gl_shader_cache *sc, +// linearized (e.g. for linear-scaling). If `opts->compute_peak` is true, we +// will detect the peak instead of relying on metadata. Note that this requires +// the caller to have already bound the appropriate SSBO and set up the compute +// shader metadata +void pass_color_map(struct gl_shader_cache *sc, bool is_linear, struct mp_colorspace src, struct mp_colorspace dst, - enum tone_mapping algo, float tone_mapping_param, - float tone_mapping_desat, bool detect_peak, - bool gamut_warning, bool is_linear) + const struct gl_tone_map_opts *opts) { GLSLF("// color mapping\n"); @@ -803,10 +812,8 @@ void pass_color_map(struct gl_shader_cache *sc, // Tone map to prevent clipping when the source signal peak exceeds the // encodable range or we've reduced the gamut - if (src.sig_peak > dst.sig_peak) { - pass_tone_map(sc, detect_peak, src.sig_peak, dst.sig_peak, algo, - tone_mapping_param, tone_mapping_desat); - } + if (src.sig_peak > dst.sig_peak) + pass_tone_map(sc, src.sig_peak, dst.sig_peak, opts); if (need_ootf) pass_inverse_ootf(sc, dst.light, dst.sig_peak); @@ -821,7 +828,7 @@ void pass_color_map(struct gl_shader_cache *sc, GLSLF("color.rgb *= vec3(%f);\n", 1.0 / dst_range); // Warn for remaining out-of-gamut colors is enabled - if (gamut_warning) { + if (opts->gamut_warning) { GLSL(if (any(greaterThan(color.rgb, vec3(1.01))))) GLSL(color.rgb = vec3(1.0) - color.rgb;) // invert } diff --git a/video/out/gpu/video_shaders.h b/video/out/gpu/video_shaders.h index cd395d6377..f20d643e99 100644 --- a/video/out/gpu/video_shaders.h +++ b/video/out/gpu/video_shaders.h @@ -40,11 +40,9 @@ void pass_sample_oversample(struct gl_shader_cache *sc, struct scaler *scaler, void pass_linearize(struct gl_shader_cache *sc, enum mp_csp_trc trc); void pass_delinearize(struct gl_shader_cache *sc, enum mp_csp_trc trc); -void pass_color_map(struct gl_shader_cache *sc, +void pass_color_map(struct gl_shader_cache *sc, bool is_linear, struct mp_colorspace src, struct mp_colorspace dst, - enum tone_mapping algo, float tone_mapping_param, - float tone_mapping_desat, bool use_detected_peak, - bool gamut_warning, bool is_linear); + const struct gl_tone_map_opts *opts); void pass_sample_deband(struct gl_shader_cache *sc, struct deband_opts *opts, AVLFG *lfg, enum mp_csp_trc trc); -- cgit v1.2.3 From 6179dcbb798aa9e3501af82ae46975e881d80626 Mon Sep 17 00:00:00 2001 From: Niklas Haas Date: Tue, 1 Jan 2019 07:30:00 +0100 Subject: vo_gpu: redesign peak detection algorithm The previous approach of using an FIR with tunable hard threshold for scene changes had several problems: - the FIR involved annoying hard-coded buffer sizes, high VRAM usage, and the FIR sum was prone to numerical overflow which limited the number of frames we could average over. We also totally redesign the scene change detection. - the hard scene change detection was prone to both false positives and false negatives, each with their own (annoying) issues. Scrap this entirely and switch to a dual approach of using a simple single-pole IIR low pass filter to smooth out noise, while using a softer scene change curve (with tunable low and high thresholds), based on `smoothstep`. The IIR filter is extremely simple in its implementation and has an arbitrarily user-tunable cutoff frequency, while the smoothstep-based scene change curve provides a good, tunable tradeoff between adaptation speed and stability - without exhibiting either of the traditional issues associated with the hard cutoff. Another way to think about the new options is that the "low threshold" provides a margin of error within which we don't care about small fluctuations in the scene (which will therefore be smoothed out by the IIR filter). --- DOCS/interface-changes.rst | 1 + DOCS/man/options.rst | 24 +++++++++++ video/out/gpu/video.c | 36 ++++++++-------- video/out/gpu/video.h | 6 +-- video/out/gpu/video_shaders.c | 96 +++++++++++++++++-------------------------- 5 files changed, 86 insertions(+), 77 deletions(-) diff --git a/DOCS/interface-changes.rst b/DOCS/interface-changes.rst index 7e723b9dbe..ce7e33176a 100644 --- a/DOCS/interface-changes.rst +++ b/DOCS/interface-changes.rst @@ -51,6 +51,7 @@ Interface changes only using a single value (which previously just controlled the exponent). The strength now linearly blends between the linear and nonlinear tone mapped versions of a color. + - add --hdr-peak-decay-rate and --hdr-scene-threshold-low/high --- mpv 0.29.0 --- - drop --opensles-sample-rate, as --audio-samplerate should be used if desired - drop deprecated --videotoolbox-format, --ff-aid, --ff-vid, --ff-sid, diff --git a/DOCS/man/options.rst b/DOCS/man/options.rst index 1c08917d7a..0f7007bf89 100644 --- a/DOCS/man/options.rst +++ b/DOCS/man/options.rst @@ -5245,6 +5245,30 @@ The following video options are currently all specific to ``--vo=gpu`` and The special value ``auto`` (default) will enable HDR peak computation automatically if compute shaders and SSBOs are supported. +``--hdr-peak-decay-rate=<1.0..1000.0>`` + The decay rate used for the HDR peak detection algorithm (default: 100.0). + This is only relevant when ``--hdr-compute-peak`` is enabled. Higher values + make the peak decay more slowly, leading to more stable values at the cost + of more "eye adaptation"-like effects (although this is mitigated somewhat + by ``--hdr-scene-threshold``). A value of 1.0 (the lowest possible) disables + all averaging, meaning each frame's value is used directly as measured, + but doing this is not recommended for "noisy" sources since it may lead + to excessive flicker. (In signal theory terms, this controls the time + constant "tau" of an IIR low pass filter) + +``--hdr-scene-threshold-low=<0..10000>``, ``--hdr-scene-threshold-high=<0..10000>`` + The lower and upper thresholds (in cd/m^2) for a brightness difference to + be considered a scene change (default: 50 low, 200 high). This is only + relevant when ``--hdr-compute-peak`` is enabled. Normally, small + fluctuations in the frame brightness are compensated for by the peak + averaging mechanism, but for large jumps in the brightness this can result + in the frame remaining too bright or too dark for up to several seconds, + depending on the value of ``--hdr-peak-decay-rate``. To counteract this, + when the brightness between the running average and the current frame + exceeds the low threshold, mpv will make the averaging filter more + aggressive, up to the limit of the high threshold (at which point the + filter becomes instant). + ``--tone-mapping-desaturate=<0.0..1.0>`` Apply desaturation for highlights (default: 0.75). The parameter controls the strength of the desaturation curve. A value of 0.0 completely disables diff --git a/video/out/gpu/video.c b/video/out/gpu/video.c index 9ffdc62d20..a29f09bc3d 100644 --- a/video/out/gpu/video.c +++ b/video/out/gpu/video.c @@ -316,6 +316,9 @@ static const struct gl_video_opts gl_video_opts_def = { .tone_map = { .curve = TONE_MAPPING_HABLE, .curve_param = NAN, + .decay_rate = 100.0, + .scene_threshold_low = 50, + .scene_threshold_high = 200, .desat = 0.75, .desat_exp = 1.5, }, @@ -367,6 +370,11 @@ const struct m_sub_options gl_video_conf = { ({"auto", 0}, {"yes", 1}, {"no", -1})), + OPT_FLOATRANGE("hdr-peak-decay-rate", tone_map.decay_rate, 0, 1.0, 1000.0), + OPT_INTRANGE("hdr-scene-threshold-low", + tone_map.scene_threshold_low, 0, 0, 10000), + OPT_INTRANGE("hdr-scene-threshold-high", + tone_map.scene_threshold_high, 0, 0, 10000), OPT_FLOAT("tone-mapping-param", tone_map.curve_param, 0), OPT_FLOAT("tone-mapping-desaturate", tone_map.desat, 0), OPT_FLOATRANGE("tone-mapping-desaturate-exponent", @@ -2478,17 +2486,18 @@ static void pass_colormanage(struct gl_video *p, struct mp_colorspace src, bool dst.sig_peak = mp_trc_nom_peak(dst.gamma); struct gl_tone_map_opts tone_map = p->opts.tone_map; - bool detect_peak = tone_map.compute_peak >= 0 && mp_trc_is_hdr(src.gamma); + bool detect_peak = tone_map.compute_peak >= 0 && mp_trc_is_hdr(src.gamma) + && src.sig_peak > dst.sig_peak; + if (detect_peak && !p->hdr_peak_ssbo) { struct { + float average[2]; + uint32_t frame_sum; + uint32_t frame_max; uint32_t counter; - uint32_t frame_idx; - uint32_t frame_num; - uint32_t frame_max[PEAK_DETECT_FRAMES+1]; - uint32_t frame_sum[PEAK_DETECT_FRAMES+1]; - uint32_t total_max; - uint32_t total_sum; - } peak_ssbo = {0}; + } peak_ssbo = { + .average = { 0.25, src.sig_peak }, + }; struct ra_buf_params params = { .type = RA_BUF_TYPE_SHADER_STORAGE, @@ -2508,15 +2517,10 @@ static void pass_colormanage(struct gl_video *p, struct mp_colorspace src, bool pass_describe(p, "detect HDR peak"); pass_is_compute(p, 8, 8, true); // 8x8 is good for performance gl_sc_ssbo(p->sc, "PeakDetect", p->hdr_peak_ssbo, + "vec2 average;" + "uint frame_sum;" + "uint frame_max;" "uint counter;" - "uint frame_idx;" - "uint frame_num;" - "uint frame_max[%d];" - "uint frame_avg[%d];" - "uint total_max;" - "uint total_avg;", - PEAK_DETECT_FRAMES + 1, - PEAK_DETECT_FRAMES + 1 ); } diff --git a/video/out/gpu/video.h b/video/out/gpu/video.h index ee5c0a2861..077f69332f 100644 --- a/video/out/gpu/video.h +++ b/video/out/gpu/video.h @@ -95,13 +95,13 @@ enum tone_mapping { TONE_MAPPING_LINEAR, }; -// How many frames to average over for HDR peak detection -#define PEAK_DETECT_FRAMES 63 - struct gl_tone_map_opts { int curve; float curve_param; int compute_peak; + float decay_rate; + int scene_threshold_low; + int scene_threshold_high; float desat; float desat_exp; int gamut_warning; // bool diff --git a/video/out/gpu/video_shaders.c b/video/out/gpu/video_shaders.c index 315e15cc89..0fff8f05f2 100644 --- a/video/out/gpu/video_shaders.c +++ b/video/out/gpu/video_shaders.c @@ -567,75 +567,55 @@ static void pass_inverse_ootf(struct gl_shader_cache *sc, enum mp_csp_light ligh // under a typical presentation gamma of about 2.0. static const float sdr_avg = 0.25; -// The threshold for which to consider an average luminance difference to be -// a sign of a scene change. -static const int scene_threshold = 0.2 * MP_REF_WHITE; - -static void hdr_update_peak(struct gl_shader_cache *sc) +static void hdr_update_peak(struct gl_shader_cache *sc, + const struct gl_tone_map_opts *opts) { - // For performance, we want to do as few atomic operations on global - // memory as possible, so use an atomic in shmem for the work group. - GLSLH(shared uint wg_sum;); - GLSL(wg_sum = 0;) - - // Have each thread update the work group sum with the local value + // Update the sig_peak/sig_avg from the old SSBO state + GLSL(sig_avg = max(1e-3, average.x);) + GLSL(sig_peak = max(1.00, average.y);) + + // For performance, and to avoid overfl