mirror of https://github.com/mpv-player/mpv.git
Merge branch 'master' into pr6360
Manual changes done: * Merged the interface-changes under the already master'd changes. * Moved the hwdec-related option changes to video/decode/vd_lavc.c.
This commit is contained in:
commit
199aabddcc
|
@ -47,6 +47,12 @@ 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.
|
||||
- add --hdr-peak-decay-rate and --hdr-scene-threshold-low/high
|
||||
- add --tone-mapping-max-boost
|
||||
- ipc: require that "request_id" fields are integers. Other types are still
|
||||
accepted for compatibility, but this will stop in the future. Also, if no
|
||||
request_id is provided, 0 will be assumed.
|
||||
|
|
|
@ -1817,7 +1817,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
|
||||
|
|
|
@ -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,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.
|
||||
|
||||
|
@ -1326,8 +1326,10 @@ Audio
|
|||
Since mpv 0.18.1, this always controls the internal mixer (aka "softvol").
|
||||
|
||||
``--replaygain=<no|track|album>``
|
||||
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=<db>``
|
||||
Pre-amplification gain in dB to apply to the selected replaygain gain
|
||||
|
@ -3988,8 +3990,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=<filename>``
|
||||
Instructs mpv to read the channels list from ``<filename>``. The default is
|
||||
|
@ -5073,7 +5075,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=<nits>``
|
||||
``--target-peak=<auto|nits>``
|
||||
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
|
||||
|
@ -5085,9 +5087,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::
|
||||
|
||||
|
@ -5164,6 +5166,14 @@ The following video options are currently all specific to ``--vo=gpu`` and
|
|||
linear
|
||||
Specifies the scale factor to use while stretching. Defaults to 1.0.
|
||||
|
||||
``--tone-mapping-max-boost=<1.0..10.0>``
|
||||
Upper limit for how much the tone mapping algorithm is allowed to boost
|
||||
the average brightness by over-exposing the image. The default value of 1.0
|
||||
allows no additional brightness boost. A value of 2.0 would allow
|
||||
over-exposing by a factor of 2, and so on. Raising this setting can help
|
||||
reveal details that would otherwise be hidden in dark scenes, but raising
|
||||
it too high will make dark scenes appear unnaturally bright.
|
||||
|
||||
``--hdr-compute-peak=<auto|yes|no>``
|
||||
Compute the HDR peak and frame average brightness per-frame instead of
|
||||
relying on tagged metadata. These values are averaged over local regions as
|
||||
|
@ -5174,17 +5184,50 @@ 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=<value>``
|
||||
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.
|
||||
``--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)
|
||||
|
||||
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.
|
||||
``--hdr-scene-threshold-low=<0.0..100.0>``, ``--hdr-scene-threshold-high=<0.0..100.0>``
|
||||
The lower and upper thresholds (in dB) for a brightness difference
|
||||
to be considered a scene change (default: 5.5 low, 10.0 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
|
||||
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
|
||||
|
@ -5245,12 +5288,14 @@ The following video options are currently all specific to ``--vo=gpu`` and
|
|||
Size of the 3D LUT generated from the ICC profile in each dimension.
|
||||
Default is 64x64x64. Sizes may range from 2 to 512.
|
||||
|
||||
``--icc-contrast=<0-1000000>``
|
||||
``--icc-contrast=<0-1000000|inf>``
|
||||
Specifies an upper limit on the target device's contrast ratio. This is
|
||||
detected automatically from the profile if possible, but for some profiles
|
||||
it might be missing, causing the contrast to be assumed as infinite. As a
|
||||
result, video may appear darker than intended. This only affects BT.1886
|
||||
content. The default of 0 means no limit.
|
||||
content. The default of 0 means no limit if the detected contrast is less
|
||||
than 100000, and limits to 1000 otherwise. Use ``--icc-contrast=inf`` to
|
||||
preserve the infinite contrast (most likely when using OLED displays).
|
||||
|
||||
``--blend-subtitles=<yes|video|no>``
|
||||
Blend subtitles directly onto upscaled video frames, before interpolation
|
||||
|
|
|
@ -1715,8 +1715,6 @@ static void prune_old_packets(struct demux_internal *in)
|
|||
}
|
||||
prev = prev->next;
|
||||
}
|
||||
|
||||
update_seek_ranges(range);
|
||||
}
|
||||
|
||||
bool done = false;
|
||||
|
@ -1725,6 +1723,8 @@ static void prune_old_packets(struct demux_internal *in)
|
|||
remove_head_packet(queue);
|
||||
}
|
||||
|
||||
update_seek_ranges(range);
|
||||
|
||||
if (range != in->current_range && range->seek_start == MP_NOPTS_VALUE)
|
||||
free_empty_cached_ranges(in);
|
||||
}
|
||||
|
@ -2100,12 +2100,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;
|
||||
}
|
||||
|
|
|
@ -80,8 +80,10 @@ static struct tl_parts *parse_edl(bstr str)
|
|||
{
|
||||
struct tl_parts *tl = talloc_zero(NULL, struct tl_parts);
|
||||
while (str.len) {
|
||||
if (bstr_eatstart0(&str, "#"))
|
||||
if (bstr_eatstart0(&str, "#")) {
|
||||
bstr_split_tok(str, "\n", &(bstr){0}, &str);
|
||||
continue;
|
||||
}
|
||||
if (bstr_eatstart0(&str, "\n") || bstr_eatstart0(&str, ";"))
|
||||
continue;
|
||||
bool is_header = bstr_eatstart0(&str, "!");
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* Copyright (C) 2004 Michael Niedermayer <michaelni@gmx.at>
|
||||
* Copyright (C) 2018 Google LLC
|
||||
*
|
||||
* This file is part of mpv.
|
||||
*
|
||||
|
@ -588,17 +589,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;
|
||||
// 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;
|
||||
|
||||
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;
|
||||
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.
|
||||
|
|
|
@ -399,7 +399,7 @@ static bool reorder_planes(struct mp_aframe *mpa, int *reorder,
|
|||
if (!mp_aframe_set_chmap(mpa, newmap))
|
||||
return false;
|
||||
|
||||
int num_planes = newmap->num;
|
||||
int num_planes = mp_aframe_get_planes(mpa);
|
||||
uint8_t **planes = mp_aframe_get_data_rw(mpa);
|
||||
uint8_t *old_planes[MP_NUM_CHANNELS];
|
||||
assert(num_planes <= MP_NUM_CHANNELS);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* This file is part of mpv.
|
||||
*
|
||||
* mpv is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* mpv is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import Cocoa
|
||||
|
||||
extension NSScreen {
|
||||
|
||||
public var displayID: CGDirectDisplayID {
|
||||
get {
|
||||
return deviceDescription["NSScreenNumber"] as! CGDirectDisplayID
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -4052,6 +4052,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;
|
||||
|
|
|
@ -752,7 +752,7 @@ int mp_add_external_file(struct MPContext *mpctx, char *filename,
|
|||
if (!demuxer)
|
||||
goto err_out;
|
||||
|
||||
if (opts->rebase_start_time)
|
||||
if (filter != STREAM_SUB && opts->rebase_start_time)
|
||||
demux_set_ts_offset(demuxer, -demuxer->start_time);
|
||||
|
||||
bool has_any = false;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
@ -457,7 +458,6 @@ local function add_file(s)
|
|||
indent=o.prefix_sep, no_prefix_markup=true})
|
||||
end
|
||||
end
|
||||
append_property(s, "file-size", {prefix="Size:"})
|
||||
end
|
||||
|
||||
|
||||
|
@ -536,9 +536,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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -406,8 +406,11 @@ char *filter_SDH(struct sd *sd, char *format, int n_ignored, char *data, int len
|
|||
line_with_text = true;
|
||||
}
|
||||
} else if (*rp && rp[0] != '\\') {
|
||||
if (rp[0] > 32 && rp[0] < 127 && rp[0] != '-')
|
||||
if ((rp[0] > 32 && rp[0] < 127 && rp[0] != '-') ||
|
||||
(unsigned char)rp[0] >= 0xC0)
|
||||
{
|
||||
line_with_text = true;
|
||||
}
|
||||
append(sd, buf, rp[0]);
|
||||
rp++;
|
||||
} else if (rp[0] == '\\' && rp[1] != 'N') {
|
||||
|
|
|
@ -140,13 +140,13 @@ const struct m_sub_options vd_lavc_conf = {
|
|||
.framedrop = AVDISCARD_NONREF,
|
||||
.dr = 1,
|
||||
.hwdec_api = HAVE_RPI ? "mmal" : "no",
|
||||
.hwdec_codecs = "h264,vc1,wmv3,hevc,mpeg2video,vp9",
|
||||
.hwdec_codecs = "h264,vc1,hevc,vp9",
|
||||
},
|
||||
};
|
||||
|
||||
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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -148,10 +148,9 @@ class CocoaCB: NSObject {
|
|||
func startDisplayLink(_ vo: UnsafeMutablePointer<vo>) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -423,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<UnsafeMutablePointer<Int8>?>?.self)
|
||||
var array: UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>? = 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)
|
||||
|
|
|
@ -83,7 +83,7 @@ const struct m_sub_options mp_icc_conf = {
|
|||
OPT_FLAG("icc-profile-auto", profile_auto, 0),
|
||||
OPT_STRING("icc-cache-dir", cache_dir, M_OPT_FILE),
|
||||
OPT_INT("icc-intent", intent, 0),
|
||||
OPT_INTRANGE("icc-contrast", contrast, 0, 0, 1000000),
|
||||
OPT_CHOICE_OR_INT("icc-contrast", contrast, 0, 0, 1000000, ({"inf", -1})),
|
||||
OPT_STRING_VALIDATE("icc-3dlut-size", size_str, 0, validate_3dlut_size_opt),
|
||||
|
||||
OPT_REPLACED("3dlut-size", "icc-3dlut-size"),
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "common/msg.h"
|
||||
#include "misc/ctype.h"
|
||||
|
@ -52,9 +53,11 @@ static bool parse_rpn_szexpr(struct bstr line, struct szexp out[MAX_SZEXP_SIZE])
|
|||
case '-': exp->tag = SZEXP_OP2; exp->val.op = SZEXP_OP_SUB; continue;
|
||||
case '*': exp->tag = SZEXP_OP2; exp->val.op = SZEXP_OP_MUL; continue;
|
||||
case '/': exp->tag = SZEXP_OP2; exp->val.op = SZEXP_OP_DIV; continue;
|
||||
case '%': exp->tag = SZEXP_OP2; exp->val.op = SZEXP_OP_MOD; continue;
|
||||
case '!': exp->tag = SZEXP_OP1; exp->val.op = SZEXP_OP_NOT; continue;
|
||||
case '>': exp->tag = SZEXP_OP2; exp->val.op = SZEXP_OP_GT; continue;
|
||||
case '<': exp->tag = SZEXP_OP2; exp->val.op = SZEXP_OP_LT; continue;
|
||||
case '=': exp->tag = SZEXP_OP2; exp->val.op = SZEXP_OP_EQ; continue;
|
||||
}
|
||||
|
||||
if (mp_isdigit(word.start[0])) {
|
||||
|
@ -118,8 +121,10 @@ bool eval_szexpr(struct mp_log *log, void *priv,
|
|||
case SZEXP_OP_SUB: res = op1 - op2; break;
|
||||
case SZEXP_OP_MUL: res = op1 * op2; break;
|
||||
case SZEXP_OP_DIV: res = op1 / op2; break;
|
||||
case SZEXP_OP_MOD: res = fmodf(op1, op2); break;
|
||||
case SZEXP_OP_GT: res = op1 > op2; break;
|
||||
case SZEXP_OP_LT: res = op1 < op2; break;
|
||||
case SZEXP_OP_EQ: res = op1 == op2; break;
|
||||
default: abort();
|
||||
}
|
||||
|
||||
|
|
|
@ -30,9 +30,11 @@ enum szexp_op {
|
|||
SZEXP_OP_SUB,
|
||||
SZEXP_OP_MUL,
|
||||
SZEXP_OP_DIV,
|
||||
SZEXP_OP_MOD,
|
||||
SZEXP_OP_NOT,
|
||||
SZEXP_OP_GT,
|
||||
SZEXP_OP_LT,
|
||||
SZEXP_OP_EQ,
|
||||
};
|
||||
|
||||
enum szexp_tag {
|
||||
|
|
|
@ -313,9 +313,16 @@ 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,
|
||||
.max_boost = 1.0,
|
||||
.decay_rate = 100.0,
|
||||
.scene_threshold_low = 5.5,
|
||||
.scene_threshold_high = 10.0,
|
||||
.desat = 0.75,
|
||||
.desat_exp = 1.5,
|
||||
},
|
||||
.early_flush = -1,
|
||||
.hwdec_interop = "auto",
|
||||
};
|
||||
|
@ -351,21 +358,30 @@ 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("tone-mapping", tone_mapping, 0,
|
||||
OPT_CHOICE_OR_INT("target-peak", target_peak, 0, 10, 10000,
|
||||
({"auto", 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_FLOATRANGE("hdr-peak-decay-rate", tone_map.decay_rate, 0, 1.0, 1000.0),
|
||||
OPT_FLOATRANGE("hdr-scene-threshold-low",
|
||||
tone_map.scene_threshold_low, 0, 0, 20.0),
|
||||
OPT_FLOATRANGE("hdr-scene-threshold-high",
|
||||
tone_map.scene_threshold_high, 0, 0, 20.0),
|
||||
OPT_FLOAT("tone-mapping-param", tone_map.curve_param, 0),
|
||||
OPT_FLOATRANGE("tone-mapping-max-boost", tone_map.max_boost, 0, 1.0, 10.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),
|
||||
|
@ -2056,6 +2072,23 @@ static void pass_read_video(struct gl_video *p)
|
|||
}
|
||||
}
|
||||
|
||||
// The basic idea is we assume the rgb/luma texture is the "reference" and
|
||||
// scale everything else to match, after all planes are finalized.
|
||||
// We find the reference texture first, in order to maintain texture offset
|
||||
// between hooks on different type of planes.
|
||||
int reference_tex_num = 0;
|
||||
for (int n = 0; n < 4; n++) {
|
||||
switch (img[n].type) {
|
||||
case PLANE_RGB:
|
||||
case PLANE_XYZ:
|
||||
case PLANE_LUMA: break;
|
||||
default: continue;
|
||||
}
|
||||
|
||||
reference_tex_num = n;
|
||||
break;
|
||||
}
|
||||
|
||||
// Dispatch the hooks for all of these textures, saving and perhaps
|
||||
// modifying them in the process
|
||||
for (int n = 0; n < 4; n++) {
|
||||
|
@ -2070,26 +2103,18 @@ static void pass_read_video(struct gl_video *p)
|
|||
}
|
||||
|
||||
img[n] = pass_hook(p, name, img[n], &offsets[n]);
|
||||
|
||||
if (reference_tex_num == n) {
|
||||
// The reference texture is finalized now.
|
||||
p->texture_w = img[n].w;
|
||||
p->texture_h = img[n].h;
|
||||
p->texture_offset = offsets[n];
|
||||
}
|
||||
}
|
||||
|
||||
// At this point all planes are finalized but they may not be at the
|
||||
// required size yet. Furthermore, they may have texture offsets that
|
||||
// require realignment. For lack of something better to do, we assume
|
||||
// the rgb/luma texture is the "reference" and scale everything else
|
||||
// to match.
|
||||
for (int n = 0; n < 4; n++) {
|
||||
switch (img[n].type) {
|
||||
case PLANE_RGB:
|
||||
case PLANE_XYZ:
|
||||
case PLANE_LUMA: break;
|
||||
default: continue;
|
||||
}
|
||||
|
||||
p->texture_w = img[n].w;
|
||||
p->texture_h = img[n].h;
|
||||
p->texture_offset = offsets[n];
|
||||
break;
|
||||
}
|
||||
// require realignment.
|
||||
|
||||
// Compute the reference rect
|
||||
struct mp_rect_f src = {0.0, 0.0, p->image_params.w, p->image_params.h};
|
||||
|
@ -2365,6 +2390,7 @@ static void pass_scale_main(struct gl_video *p)
|
|||
// 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;
|
||||
GLSL(color.rgb = clamp(color.rgb, 0.0, 1.0);)
|
||||
GLSLF("color.rgb = %f - log(1.0/(color.rgb * %f + %f) - 1.0) * 1.0/%f;\n",
|
||||
sig_center, sig_scale, sig_offset, sig_slope);
|
||||
pass_opt_hook_point(p, "SIGMOID", NULL);
|
||||
|
@ -2392,6 +2418,7 @@ static void pass_scale_main(struct gl_video *p)
|
|||
GLSLF("// scaler post-conversion\n");
|
||||
if (use_sigmoid) {
|
||||
// Inverse of the transformation above
|
||||
GLSL(color.rgb = clamp(color.rgb, 0.0, 1.0);)
|
||||
GLSLF("color.rgb = (1.0/(1.0 + exp(%f * (%f - color.rgb))) - %f) * 1.0/%f;\n",
|
||||
sig_slope, sig_center, sig_offset, sig_scale);
|
||||
}
|
||||
|
@ -2471,16 +2498,16 @@ 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)
|
||||
&& src.sig_peak > dst.sig_peak;
|
||||
|
||||
if (detect_peak && !p->hdr_peak_ssbo) {
|
||||
struct {
|
||||
float average[2];
|
||||
int32_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};
|
||||
|
||||
struct ra_buf_params params = {
|
||||
|
@ -2492,8 +2519,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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2501,22 +2528,15 @@ 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;"
|
||||
"int 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
|
||||
);
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
@ -3502,9 +3522,9 @@ static bool check_dumb_mode(struct gl_video *p)
|
|||
return false;
|
||||
|
||||
// otherwise, use auto-detection
|
||||
if (o->target_prim || o->target_trc || o->correct_downscaling ||
|
||||
o->linear_downscaling || o->linear_upscaling || o->sigmoid_upscaling ||
|
||||
o->interpolation || o->blend_subs || o->deband || o->unsharp)
|
||||
if (o->correct_downscaling || o->linear_downscaling ||
|
||||
o->linear_upscaling || o->sigmoid_upscaling || o->interpolation ||
|
||||
o->blend_subs || o->deband || o->unsharp)
|
||||
return false;
|
||||
// check remaining scalers (tscale is already implicitly excluded above)
|
||||
for (int i = 0; i < SCALER_COUNT; i++) {
|
||||
|
@ -3516,8 +3536,6 @@ static bool check_dumb_mode(struct gl_video *p)
|
|||
}
|
||||
if (o->user_shaders && o->user_shaders[0])
|
||||
return false;
|
||||
if (p->use_lut_3d)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -3582,12 +3600,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;
|
||||
|
@ -3609,7 +3627,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,
|
||||
|
@ -3617,12 +3634,13 @@ 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,
|
||||
.target_trc = p->opts.target_trc,
|
||||
.target_prim = p->opts.target_prim,
|
||||
.target_peak = p->opts.target_peak,
|
||||
};
|
||||
for (int n = 0; n < SCALER_COUNT; n++)
|
||||
p->opts.scaler[n] = gl_video_opts_def.scaler[n];
|
||||
|
|
|
@ -95,8 +95,18 @@ 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;
|
||||
float max_boost;
|
||||
int compute_peak;
|
||||
float decay_rate;
|
||||
float scene_threshold_low;
|
||||
float scene_threshold_high;
|
||||
float desat;
|
||||
float desat_exp;
|
||||
int gamut_warning; // bool
|
||||
};
|
||||
|
||||
struct gl_video_opts {
|
||||
int dumb_mode;
|
||||
|
@ -107,11 +117,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;
|
||||
|
|
|
@ -380,7 +380,7 @@ void pass_linearize(struct gl_shader_cache *sc, enum mp_csp_trc trc)
|
|||
GLSLF("color.rgb = max(color.rgb - vec3(%f), vec3(0.0)) \n"
|
||||
" / (vec3(%f) - vec3(%f) * color.rgb);\n",
|
||||
PQ_C1, PQ_C2, PQ_C3);
|
||||
GLSLF("color.rgb = pow(color.rgb, vec3(1.0/%f));\n", PQ_M1);
|
||||
GLSLF("color.rgb = pow(color.rgb, vec3(%f));\n", 1.0 / PQ_M1);
|
||||
// PQ's output range is 0-10000, but we need it to be relative to to
|
||||
// MP_REF_WHITE instead, so rescale
|
||||
GLSLF("color.rgb *= vec3(%f);\n", 10000 / MP_REF_WHITE);
|
||||
|
@ -567,123 +567,106 @@ 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;)
|
||||
// Update the sig_peak/sig_avg from the old SSBO state
|
||||
GLSL(if (average.y > 0.0) {)
|
||||
GLSL( sig_avg = max(1e-3, average.x);)
|
||||
GLSL( sig_peak = max(1.00, average.y);)
|
||||
GLSL(})
|
||||
|
||||
// Have each thread update the work group sum with the local value
|
||||
// Chosen to avoid overflowing on an 8K buffer
|
||||
const float log_min = 1e-3, log_scale = 400.0, sig_scale = 10000.0;
|
||||
|
||||
// For performance, and to avoid overflows, we tally up the sub-results per
|
||||
// pixel using shared memory first
|
||||
GLSLH(shared int wg_sum;)
|
||||
GLSLH(shared uint wg_max;)
|
||||
GLSL(wg_sum = 0; wg_max = 0;)
|
||||
GLSL(barrier();)
|
||||
GLSLF("atomicAdd(wg_sum, uint(sig * %f));\n", MP_REF_WHITE);
|
||||
GLSLF("float sig_log = log(max(sig_max, %f));\n", log_min);
|
||||
GLSLF("atomicAdd(wg_sum, int(sig_log * %f));\n", log_scale);
|
||||
GLSLF("atomicMax(wg_max, uint(sig_max * %f));\n", sig_scale);
|
||||
|
||||
// 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
|
||||
// more stable and smooth out tiny super-highlights.
|
||||
// Have one thread per work group update the global atomics
|
||||
GLSL(memoryBarrierShared();)
|
||||
GLSL(barrier();)
|
||||
GLSL(if (gl_LocalInvocationIndex == 0) {)
|
||||
GLSL( uint wg_avg = wg_sum / (gl_WorkGroupSize.x * gl_WorkGroupSize.y);)
|
||||
GLSL( atomicMax(frame_max[frame_idx], wg_avg);)
|
||||
GLSL( atomicAdd(frame_avg[frame_idx], wg_avg);)
|
||||
GLSL( int wg_avg = wg_sum / int(gl_WorkGroupSize.x * gl_WorkGroupSize.y);)
|
||||
GLSL( atomicAdd(frame_sum, wg_avg);)
|
||||
GLSL( atomicMax(frame_max, wg_max);)
|
||||
GLSL( memoryBarrierBuffer();)
|
||||
GLSL(})
|
||||
|
||||
const float refi = 1.0 / MP_REF_WHITE;
|
||||
|
||||
// Update the sig_peak/sig_avg from the old SSBO state
|
||||
GLSL(uint num_wg = gl_NumWorkGroups.x * gl_NumWorkGroups.y;)
|
||||
GLSL(if (frame_num > 0) {)
|
||||
GLSLF(" float peak = %f * float(total_max) / float(frame_num);\n", refi);
|
||||
GLSLF(" float avg = %f * float(total_avg) / float(frame_num);\n", refi);
|
||||
GLSLF(" sig_peak = max(1.0, peak);\n");
|
||||
GLSLF(" sig_avg = max(%f, avg);\n", sdr_avg);
|
||||
GLSL(});
|
||||
GLSL(barrier();)
|
||||
|
||||
// Finally, to update the global state, we increment a counter per dispatch
|
||||
GLSL(memoryBarrierBuffer();)
|
||||
GLSL(barrier();)
|
||||
GLSL(uint num_wg = gl_NumWorkGroups.x * gl_NumWorkGroups.y;)
|
||||
GLSL(if (gl_LocalInvocationIndex == 0 && atomicAdd(counter, 1) == num_wg - 1) {)
|
||||
|
||||
// Since we sum up all the workgroups, we also still need to divide the
|
||||
// average by the number of work groups
|
||||
GLSL( counter = 0;)
|
||||
GLSL( frame_avg[frame_idx] /= num_wg;)
|
||||
GLSL( uint cur_max = frame_max[frame_idx];)
|
||||
GLSL( uint cur_avg = frame_avg[frame_idx];)
|
||||
GLSL( vec2 cur = vec2(float(frame_sum) / float(num_wg), frame_max);)
|
||||
GLSLF(" cur *= vec2(1.0/%f, 1.0/%f);\n", log_scale, sig_scale);
|
||||
GLSL( cur.x = exp(cur.x);)
|
||||
GLSL( if (average.y == 0.0))
|
||||
GLSL( average = cur;)
|
||||
|
||||
// Scene change detection
|
||||
GLSL( int diff = int(frame_num * cur_avg) - int(total_avg);)
|
||||
GLSLF(" if (abs(diff) > frame_num * %d) {\n", scene_threshold);
|
||||
GLSL( frame_num = 0;)
|
||||
GLSL( total_max = total_avg = 0;)
|
||||
GLSLF(" for (uint i = 0; i < %d; i++)\n", PEAK_DETECT_FRAMES+1);
|
||||
GLSL( frame_max[i] = frame_avg[i] = 0;)
|
||||
GLSL( frame_max[frame_idx] = cur_max;)
|
||||
GLSL( frame_avg[frame_idx] = cur_avg;)
|
||||
GLSL( })
|
||||
// Use an IIR low-pass filter to smooth out the detected values, with a
|
||||
// configurable decay rate based on the desired time constant (tau)
|
||||
float a = 1.0 - cos(1.0 / opts->decay_rate);
|
||||
float decay = sqrt(a*a + 2*a) - a;
|
||||
GLSLF(" average += %f * (cur - average);\n", decay);
|
||||
|
||||
// Add the current frame, then subtract and reset the next frame
|
||||
GLSLF(" uint next = (frame_idx + 1) %% %d;\n", PEAK_DETECT_FRAMES+1);
|
||||
GLSL( total_max += cur_max - frame_max[next];)
|
||||
GLSL( total_avg += cur_avg - frame_avg[next];)
|
||||
GLSL( frame_max[next] = frame_avg[next] = 0;)
|
||||
// Scene change hysteresis
|
||||
float log_db = 10.0 / log(10.0);
|
||||
GLSLF(" float weight = smoothstep(%f, %f, abs(log(cur.x / average.x)));\n",
|
||||
opts->scene_threshold_low / log_db,
|
||||
opts->scene_threshold_high / log_db);
|
||||
GLSL( average = mix(average, cur, weight);)
|
||||
|
||||
// Update the index and count
|
||||
GLSL( frame_idx = next;)
|
||||
GLSLF(" frame_num = min(frame_num + 1, %d);\n", PEAK_DETECT_FRAMES);
|
||||
// Reset SSBO state for the next frame
|
||||
GLSL( frame_sum = 0; frame_max = 0;)
|
||||
GLSL( memoryBarrierBuffer();)
|
||||
GLSL(})
|
||||
}
|
||||
|
||||
// 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)
|
||||
hdr_update_peak(sc);
|
||||
if (opts->compute_peak >= 0)
|
||||
hdr_update_peak(sc, opts);
|
||||
|
||||
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;)
|
||||
GLSLF("float slope = min(1.0, %f / sig_avg);\n", sdr_avg);
|
||||
GLSL(float sig_orig = sig[sig_idx];)
|
||||
GLSLF("float slope = min(%f, %f / sig_avg);\n", opts->max_boost, 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 +680,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 +696,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 +728,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");
|
||||
|
||||
|
@ -791,6 +789,10 @@ void pass_color_map(struct gl_shader_cache *sc,
|
|||
if (need_ootf)
|
||||
pass_ootf(sc, src.light, src.sig_peak);
|
||||
|
||||
// Tone map to prevent clipping due to excessive brightness
|
||||
if (src.sig_peak > dst.sig_peak)
|
||||
pass_tone_map(sc, src.sig_peak, dst.sig_peak, opts);
|
||||
|
||||
// Adapt to the right colorspace if necessary
|
||||
if (src.primaries != dst.primaries) {
|
||||
struct mp_csp_primaries csp_src = mp_get_csp_primaries(src.primaries),
|
||||
|
@ -801,13 +803,6 @@ void pass_color_map(struct gl_shader_cache *sc,
|
|||
GLSL(color.rgb = cms_matrix * color.rgb;)
|
||||
}
|
||||
|
||||
// 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 (need_ootf)
|
||||
pass_inverse_ootf(sc, dst.light, dst.sig_peak);
|
||||
|
||||
|
@ -821,8 +816,9 @@ 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) {
|
||||
GLSL(if (any(greaterThan(color.rgb, vec3(1.01)))))
|
||||
if (opts->gamut_warning) {
|
||||
GLSL(if (any(greaterThan(color.rgb, vec3(1.01))) ||
|
||||
any(lessThan(color.rgb, vec3(0.0)))))
|
||||
GLSL(color.rgb = vec3(1.0) - color.rgb;) // invert
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -262,6 +262,7 @@ static const struct gl_functions gl_functions[] = {
|
|||
},
|
||||
{
|
||||
.ver_core = 320,
|
||||
.ver_es_core = 300,
|
||||
.extension = "GL_ARB_sync",
|
||||
.functions = (const struct gl_function[]) {
|
||||
DEF_FN(FenceSync),
|
||||
|
|
|
@ -50,8 +50,8 @@ struct gbm
|
|||
{
|
||||
struct gbm_surface *surface;
|
||||
struct gbm_device *device;
|
||||
struct gbm_bo *bo;
|
||||
struct gbm_bo *next_bo;
|
||||
struct gbm_bo **bo;
|
||||
unsigned int num_bos;
|
||||
};
|
||||
|
||||
struct egl
|
||||
|
@ -72,6 +72,9 @@ struct priv {
|
|||
struct gbm gbm;
|
||||
struct framebuffer *fb;
|
||||
|
||||
GLsync *vsync_fences;
|
||||
unsigned int num_vsync_fences;
|
||||
|
||||
uint32_t gbm_format;
|
||||
|
||||
bool active;
|
||||
|
@ -80,6 +83,9 @@ struct priv {
|
|||
bool vt_switcher_active;
|
||||
struct vt_switcher vt_switcher;
|
||||
|
||||
bool still;
|
||||
bool paused;
|
||||
|
||||
struct mpv_opengl_drm_params drm_params;
|
||||
struct mpv_opengl_drm_draw_surface_size draw_surface_size;
|
||||
};
|
||||
|
@ -355,15 +361,6 @@ static void crtc_release(struct ra_ctx *ctx)
|
|||
return;
|
||||
p->active = false;
|
||||
|
||||
// wait for current page flip
|
||||
while (p->waiting_for_flip) {
|
||||
int ret = drmHandleEvent(p->kms->fd, &p->ev);
|
||||
if (ret) {
|
||||
MP_ERR(ctx->vo, "drmHandleEvent failed: %i\n", ret);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (p->kms->atomic_context) {
|
||||
if (p->kms->atomic_context->old_state.saved) {
|
||||
if (!crtc_release_atomic(ctx))
|
||||
|
@ -414,44 +411,19 @@ static void acquire_vt(void *data)
|
|||
crtc_setup(ctx);
|
||||
}
|
||||
|
||||
static bool drm_atomic_egl_start_frame(struct ra_swapchain *sw, struct ra_fbo *out_fbo)
|
||||
{
|
||||
struct priv *p = sw->ctx->priv;
|
||||
if (p->kms->atomic_context) {
|
||||
if (!p->kms->atomic_context->request) {
|
||||
p->kms->atomic_context->request = drmModeAtomicAlloc();
|
||||
p->drm_params.atomic_request_ptr = &p->kms->atomic_context->request;
|
||||
}
|
||||
return ra_gl_ctx_start_frame(sw, out_fbo);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static const struct ra_swapchain_fns drm_atomic_swapchain = {
|
||||
.start_frame = drm_atomic_egl_start_frame,
|
||||
};
|
||||
|
||||
static void drm_egl_swap_buffers(struct ra_ctx *ctx)
|
||||
static void queue_flip(struct ra_ctx *ctx)
|
||||
{
|
||||
struct priv *p = ctx->priv;
|
||||
struct drm_atomic_context *atomic_ctx = p->kms->atomic_context;
|
||||
int ret;
|
||||
|
||||
if (!p->active)
|
||||
return;
|
||||
|
||||
eglSwapBuffers(p->egl.display, p->egl.surface);
|
||||
p->gbm.next_bo = gbm_surface_lock_front_buffer(p->gbm.surface);
|
||||
p->waiting_for_flip = true;
|
||||
update_framebuffer_from_bo(ctx, p->gbm.next_bo);
|
||||
|
||||
if (atomic_ctx) {
|
||||
drm_object_set_property(atomic_ctx->request, atomic_ctx->draw_plane, "FB_ID", p->fb->id);
|
||||
drm_object_set_property(atomic_ctx->request, atomic_ctx->draw_plane, "CRTC_ID", atomic_ctx->crtc->id);
|
||||
drm_object_set_property(atomic_ctx->request, atomic_ctx->draw_plane, "ZPOS", 1);
|
||||
|
||||
ret = drmModeAtomicCommit(p->kms->fd, atomic_ctx->request,
|
||||
DRM_MODE_ATOMIC_NONBLOCK | DRM_MODE_PAGE_FLIP_EVENT, NULL);
|
||||
DRM_MODE_ATOMIC_NONBLOCK | DRM_MODE_PAGE_FLIP_EVENT, p);
|
||||
if (ret)
|
||||
MP_WARN(ctx->vo, "Failed to commit atomic request (%d)\n", ret);
|
||||
} else {
|
||||
|
@ -461,30 +433,129 @@ static void drm_egl_swap_buffers(struct ra_ctx *ctx)
|
|||
MP_WARN(ctx->vo, "Failed to queue page flip: %s\n", mp_strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
// poll page flip finish event
|
||||
const int timeout_ms = 3000;
|
||||
struct pollfd fds[1] = { { .events = POLLIN, .fd = p->kms->fd } };
|
||||
poll(fds, 1, timeout_ms);
|
||||
if (fds[0].revents & POLLIN) {
|
||||
ret = drmHandleEvent(p->kms->fd, &p->ev);
|
||||
if (ret != 0) {
|
||||
MP_ERR(ctx->vo, "drmHandleEvent failed: %i\n", ret);
|
||||
p->waiting_for_flip = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
p->waiting_for_flip = false;
|
||||
p->waiting_for_flip = true;
|
||||
|
||||
if (atomic_ctx) {
|
||||
drmModeAtomicFree(atomic_ctx->request);
|
||||
atomic_ctx->request = drmModeAtomicAlloc();
|
||||
}
|
||||
|
||||
gbm_surface_release_buffer(p->gbm.surface, p->gbm.bo);
|
||||
p->gbm.bo = p->gbm.next_bo;
|
||||
}
|
||||
|
||||
static void wait_on_flip(struct ra_ctx *ctx)
|
||||
{
|
||||
struct priv *p = ctx->priv;
|
||||
|
||||
// poll page flip finish event
|
||||
while (p->waiting_for_flip) {
|
||||
const int timeout_ms = 3000;
|
||||
struct pollfd fds[1] = { { .events = POLLIN, .fd = p->kms->fd } };
|
||||
poll(fds, 1, timeout_ms);
|
||||
if (fds[0].revents & POLLIN) {
|
||||
const int ret = drmHandleEvent(p->kms->fd, &p->ev);
|
||||
if (ret != 0)
|
||||
MP_ERR(ctx->vo, "drmHandleEvent failed: %i\n", ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void swapchain_step(struct ra_ctx *ctx)
|
||||
{
|
||||
struct priv *p = ctx->priv;
|
||||
|
||||
if (p->gbm.num_bos > 0 && p->gbm.bo[0])
|
||||
gbm_surface_release_buffer(p->gbm.surface, p->gbm.bo[0]);
|
||||
MP_TARRAY_REMOVE_AT(p->gbm.bo, p->gbm.num_bos, 0);
|
||||
}
|
||||
|
||||
static void new_fence(struct ra_ctx *ctx)
|
||||
{
|
||||
struct priv *p = ctx->priv;
|
||||
|
||||
if (p->gl.FenceSync) {
|
||||
GLsync fence = p->gl.FenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
if (fence)
|
||||
MP_TARRAY_APPEND(p, p->vsync_fences, p->num_vsync_fences, fence);
|
||||
}
|
||||
}
|
||||
|
||||
static void wait_fence(struct ra_ctx *ctx)
|
||||
{
|
||||
struct priv *p = ctx->priv;
|
||||
|
||||
while (p->num_vsync_fences && (p->num_vsync_fences >= p->gbm.num_bos)) {
|
||||
p->gl.ClientWaitSync(p->vsync_fences[0], GL_SYNC_FLUSH_COMMANDS_BIT, 1e9);
|
||||
p->gl.DeleteSync(p->vsync_fences[0]);
|
||||
MP_TARRAY_REMOVE_AT(p->vsync_fences, p->num_vsync_fences, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static bool drm_egl_start_frame(struct ra_swapchain *sw, struct ra_fbo *out_fbo)
|
||||
{
|
||||
struct ra_ctx *ctx = sw->ctx;
|
||||
struct priv *p = ctx->priv;
|
||||
|
||||
if (p->kms->atomic_context && !p->kms->atomic_context->request) {
|
||||
p->kms->atomic_context->request = drmModeAtomicAlloc();
|
||||
p->drm_params.atomic_request_ptr = &p->kms->atomic_context->request;
|
||||
}
|
||||
|
||||
return ra_gl_ctx_start_frame(sw, out_fbo);
|
||||
}
|
||||
|
||||
static bool drm_egl_submit_frame(struct ra_swapchain *sw, const struct vo_frame *frame)
|
||||
{
|
||||
struct ra_ctx *ctx = sw->ctx;
|
||||
struct priv *p = ctx->priv;
|
||||
|
||||
p->still = frame->still;
|
||||
|
||||
return ra_gl_ctx_submit_frame(sw, frame);
|
||||
}
|
||||
|
||||
static void drm_egl_swap_buffers(struct ra_swapchain *sw)
|
||||
{
|
||||
struct ra_ctx *ctx = sw->ctx;
|
||||
struct priv *p = ctx->priv;
|
||||
const bool drain = p->paused || p->still; // True when we need to drain the swapchain
|
||||
|
||||
if (!p->active)
|
||||
return;
|
||||
|
||||
wait_fence(ctx);
|
||||
|
||||
eglSwapBuffers(p->egl.display, p->egl.surface);
|
||||
|
||||
struct gbm_bo *new_bo = gbm_surface_lock_front_buffer(p->gbm.surface);
|
||||
if (!new_bo) {
|
||||
MP_ERR(ctx->vo, "Couldn't lock front buffer\n");
|
||||
return;
|
||||
}
|
||||
MP_TARRAY_APPEND(p, p->gbm.bo, p->gbm.num_bos, new_bo);
|
||||
new_fence(ctx);
|
||||
|
||||
while (drain || p->gbm.num_bos > ctx->opts.swapchain_depth || !gbm_surface_has_free_buffers(p->gbm.surface)) {
|
||||
if (p->waiting_for_flip) {
|
||||
wait_on_flip(ctx);
|
||||
swapchain_step(ctx);
|
||||
}
|
||||
if (p->gbm.num_bos <= 1)
|
||||
break;
|
||||
if (!p->gbm.bo[1]) {
|
||||
MP_ERR(ctx->vo, "Hole in swapchain?\n");
|
||||
swapchain_step(ctx);
|
||||
continue;
|
||||
}
|
||||
update_framebuffer_from_bo(ctx, p->gbm.bo[1]);
|
||||
queue_flip(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct ra_swapchain_fns drm_egl_swapchain = {
|
||||
.start_frame = drm_egl_start_frame,
|
||||
.submit_frame = drm_egl_submit_frame,
|
||||
.swap_buffers = drm_egl_swap_buffers,
|
||||
};
|
||||
|
||||
static void drm_egl_uninit(struct ra_ctx *ctx)
|
||||
{
|
||||
struct priv *p = ctx->priv;
|
||||
|
@ -503,6 +574,12 @@ static void drm_egl_uninit(struct ra_ctx *ctx)
|
|||
if (p->vt_switcher_active)
|
||||
vt_switcher_destroy(&p->vt_switcher);
|
||||
|
||||
// According to GBM documentation all BO:s must be released before
|
||||
// gbm_surface_destroy can be called on the surface.
|
||||
while (p->gbm.num_bos) {
|
||||
swapchain_step(ctx);
|
||||
}
|
||||
|
||||
eglMakeCurrent(p->egl.display, EGL_NO_SURFACE, EGL_NO_SURFACE,
|
||||
EGL_NO_CONTEXT);
|
||||
eglDestroyContext(p->egl.display, p->egl.context);
|
||||
|
@ -565,6 +642,13 @@ static bool probe_gbm_format(struct ra_ctx *ctx, uint32_t argb_format, uint32_t
|
|||
return result;
|
||||
}
|
||||
|
||||
static void page_flipped(int fd, unsigned int frame, unsigned int sec,
|
||||
unsigned int usec, void *data)
|
||||
{
|
||||
struct priv *p = data;
|
||||
p->waiting_for_flip = false;
|
||||
}
|
||||
|
||||
static bool drm_egl_init(struct ra_ctx *ctx)
|
||||
{
|
||||
if (ctx->opts.probing) {
|
||||
|
@ -574,6 +658,7 @@ static bool drm_egl_init(struct ra_ctx *ctx)
|
|||
|
||||
struct priv *p = ctx->priv = talloc_zero(ctx, struct priv);
|
||||
p->ev.version = DRM_EVENT_CONTEXT_VERSION;
|
||||
p->ev.page_flip_handler = page_flipped;
|
||||
|
||||
p->vt_switcher_active = vt_switcher_init(&p->vt_switcher, ctx->vo->log);
|
||||
if (p->vt_switcher_active) {
|
||||
|
@ -644,12 +729,13 @@ static bool drm_egl_init(struct ra_ctx *ctx)
|
|||
eglSwapBuffers(p->egl.display, p->egl.surface);
|
||||
|
||||
MP_VERBOSE(ctx, "Preparing framebuffer\n");
|
||||
p->gbm.bo = gbm_surface_lock_front_buffer(p->gbm.surface);
|
||||
if (!p->gbm.bo) {
|
||||
struct gbm_bo *new_bo = gbm_surface_lock_front_buffer(p->gbm.surface);
|
||||
if (!new_bo) {
|
||||
MP_ERR(ctx, "Failed to lock GBM surface.\n");
|
||||
return false;
|
||||
}
|
||||
update_framebuffer_from_bo(ctx, p->gbm.bo);
|
||||
MP_TARRAY_APPEND(p, p->gbm.bo, p->gbm.num_bos, new_bo);
|
||||
update_framebuffer_from_bo(ctx, new_bo);
|
||||
if (!p->fb || !p->fb->id) {
|
||||
MP_ERR(ctx, "Failed to create framebuffer.\n");
|
||||
return false;
|
||||
|
@ -681,9 +767,7 @@ static bool drm_egl_init(struct ra_ctx *ctx)
|
|||
}
|
||||
|
||||
struct ra_gl_ctx_params params = {
|
||||
.swap_buffers = drm_egl_swap_buffers,
|
||||
.external_swapchain = p->kms->atomic_context ? &drm_atomic_swapchain :
|
||||
NULL,
|
||||
.external_swapchain = &drm_egl_swapchain,
|
||||
};
|
||||
if (!ra_gl_ctx_init(ctx, &p->gl, params))
|
||||
return false;
|
||||
|
@ -715,6 +799,13 @@ static int drm_egl_control(struct ra_ctx *ctx, int *events, int request,
|
|||
*(double*)arg = fps;
|
||||
return VO_TRUE;
|
||||
}
|
||||
case VOCTRL_PAUSE:
|
||||
ctx->vo->want_redraw = true;
|
||||
p->paused = true;
|
||||
return VO_TRUE;
|
||||
case VOCTRL_RESUME:
|
||||
p->paused = false;
|
||||
return VO_TRUE;
|
||||
}
|
||||
return VO_NOTIMPL;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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" ),
|
||||
|
@ -234,12 +235,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" ),
|
||||
|
|
Loading…
Reference in New Issue