Skip to content

Commit

Permalink
auto_profiles: add this script
Browse files Browse the repository at this point in the history
This is taken from a somewhat older proof-of-concept script. The basic
idea, and most of the implementation, is still the same. The way the
profiles are actually defined changed.

I still feel bad about this being a Lua script, and running user
expressions as Lua code in a vaguely defined environment, but I guess as
far as balance of effort/maintenance/results goes, this is fine.

It's a bit bloated (the Lua scripting state is at least 150KB or so in
total), so in order to enable this by default, I decided it should
unload itself by default if no auto-profiles are used. (And currently,
it does not actually rescan the profile list if a new config file is
loaded some time later, so the script would do nothing anyway if no auto
profiles were defined.)

This still requires defining inverse profiles for "unapplying" a
profile. Also this is still somewhat racy. Both will probably be
alleviated to some degree in the future.
  • Loading branch information
wm4 committed Aug 5, 2020
1 parent f457f38 commit 13d354e
Show file tree
Hide file tree
Showing 13 changed files with 326 additions and 7 deletions.
2 changes: 2 additions & 0 deletions DOCS/interface-changes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ Interface changes
- remove --video-sync-adrop-size option (implementation was changed, no
replacement for what this option did)
- undeprecate --video-sync=display-adrop
- deprecate legacy auto profiles (profiles starting with "extension." and
"protocol."). Use conditional auto profiles instead.
--- mpv 0.32.0 ---
- change behavior when using legacy option syntax with options that start
with two dashes (``--`` instead of a ``-``). Now, using the recommended
Expand Down
131 changes: 126 additions & 5 deletions DOCS/man/mpv.rst
Original file line number Diff line number Diff line change
Expand Up @@ -694,25 +694,146 @@ or at runtime with the ``apply-profile <name>`` command.
profile=big-cache


Auto profiles
-------------
Conditional auto profiles
-------------------------

Profiles which have the ``profile-cond`` option set are applied automatically
if the associated condition matches (unless auto profiles are disabled). The
option takes a string, which is interpreted as Lua condition. If evaluating the
expression returns true, the profile is applied, if it returns false, it is
ignored. This Lua code execution is not sandboxed.

Any variables in condition expressions can reference properties. If an
identifier is not already by defined by Lua or mpv, it is interpreted as
property. For example, ``pause`` would return the current pause status. If the
variable name contains any ``_`` characters, they are turned into ``-``. For
example, ``playback_time`` would return the property ``playback-time``.

A more robust way to access properties is using ``p.property_name`` or
``get("property-name", default_value)``. The automatic variable to property
magic will break if a new identifier with the same name is introduced (for
example, if a function named ``pause()`` were added, ``pause`` would return a
function value instead of the value of the ``pause`` property).

Note that if a property is not available, it will return ``nil``, which can
cause errors if used in expressions. These are logged in verbose mode, and the
expression is considered to be false.

Whenever a property referenced by a profile condition changes, the condition
is re-evaluated. If the return value of the condition changes from false or
error to true, the profile is applied.

Note that profiles cannot be "unapplied", so you may have to define inverse
profiles with inverse conditions do undo a profile.

.. admonition:: Example

Make only HD video look funny:

::

[something]
profile-desc=HD video sucks
profile-cond=width >= 1280
hue=-50

If you want the profile to be reverted if the condition goes to false again,
you need to do this by manually creating an inverse profile:

::

[something]
profile-desc=Flip video when entering fullscreen
profile-cond=fullscreen
vf=vflip

[something2]
profile-desc=Inverse of [something]
profile-cond=not fullscreen
vf=

This sets the video filter chain to ``vflip`` when entering fullscreen. The
first profile does not cause the filter to be removed when leaving
fullscreen. A second profile has to be defined, which is explicitly applied
on leaving fullscreen, and which explicitly clears the filter list. (This
would also clear the filter list at program start when starting the player
in windowed mode.)

.. warning::

Every time an involved property changes, the condition is evaluated again.
If your condition uses ``p.playback_time`` for example, the condition is
re-evaluated approximately on every video frame. This is probably slow.

This feature is managed by an internal Lua script. Conditions are executed as
Lua code within this script. Its environment contains at least the following
things:

``(function environment table)``
Every Lua function has an environment table. This is used for identifier
access. There is no named Lua symbol for it; it is implicit.

The environment does "magic" accesses to mpv properties. If an identifier
is not already defined in ``_G``, it retrieves the mpv property of the same
name. Any occurrences of ``_`` in the name are replaced with ``-`` before
reading the property. The returned value is as retrieved by
``mp.get_property_native(name)``. Internally, a cache of property values,
updated by observing the property is used instead, so properties that are
not observable will be stuck at the initial value forever.

If you want to access properties, that actually contain ``_`` in the name,
use ``get()`` (which does not perform transliteration).

Internally, the environment table has a ``__index`` meta method set, which
performs the access logic.

``p``
A "magic" table similar to the environment table. Unlike the latter, this
does not prefer accessing variables defined in ``_G`` - it always accesses
properties.

``get(name [, def])``
Read a property and return its value. If the property value is ``nil`` (e.g.
if the property does not exist), ``def`` is returned.

This is superficially similar to ``mp.get_property_native(name)``. An
important difference is that this accesses the property cache, and enables
the change detection logic (which is essential to the dynamic runtime
behavior of auto profiles). Also, it does not return an error value as
second return value.

The "magic" tables mentioned above use this function as backend. It does not
perform the ``_`` transliteration.

In addition, the same environment as in a blank mpv Lua script is present. For
example, ``math`` is defined and gives access to the Lua standard math library.

.. warning::

This feature is subject to change indefinitely. You might be forced to
adjust your profiles on mpv updates.

Legacy auto profiles
--------------------

Some profiles are loaded automatically. The following example demonstrates this:
Some profiles are loaded automatically using a legacy mechanism. The following
example demonstrates this:

.. admonition:: Auto profile loading

::

[extension.mkv]
profile-desc="profile for .mkv files"
vf=flip
vf=vflip

The profile name follows the schema ``type.name``, where type can be
``protocol`` for the input/output protocol in use (see ``--list-protocols``),
and ``extension`` for the extension of the path of the currently played file
(*not* the file format).

This feature is very limited, and there are no other auto profiles.
This feature is very limited, and is considered soft-deprecated. Use conditional
auto profiles.

Using mpv from other programs or scripts
========================================
Expand Down
5 changes: 5 additions & 0 deletions DOCS/man/options.rst
Original file line number Diff line number Diff line change
Expand Up @@ -999,6 +999,11 @@ Program Behavior
show the console, and ``ESC`` to hide it again. (This is based on a user
script called ``repl.lua``.)

``--load-auto-profiles=<yes|no|auto>``
Enable the builtin script that does auto profiles (default: auto). See
`Conditional auto profiles`_ for details. ``auto`` will load the script,
but immediately unload it if there are no conditional profiles.

``--player-operation-mode=<cplayer|pseudo-gui>``
For enabling "pseudo GUI mode", which means that the defaults for some
options are changed. This option should not normally be used directly, but
Expand Down
16 changes: 16 additions & 0 deletions options/m_config_frontend.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,10 @@ struct m_profile {
struct m_profile *next;
char *name;
char *desc;
char *cond;
int num_opts;
// Option/value pair array.
// name,value = opts[n*2+0],opts[n*2+1]
char **opts;
};

Expand Down Expand Up @@ -85,6 +87,10 @@ static int show_profile(struct m_config *config, bstr param)
MP_INFO(config, "Profile %s: %s\n", p->name,
p->desc ? p->desc : "");
config->profile_depth++;
if (p->cond) {
MP_INFO(config, "%*sprofile-cond=%s\n", config->profile_depth, "",
p->cond);
}
for (int i = 0; i < p->num_opts; i++) {
MP_INFO(config, "%*s%s=%s\n", config->profile_depth, "",
p->opts[2 * i], p->opts[2 * i + 1]);
Expand Down Expand Up @@ -884,6 +890,14 @@ void m_profile_set_desc(struct m_profile *p, bstr desc)
p->desc = bstrto0(p, desc);
}

void m_profile_set_cond(struct m_profile *p, bstr cond)
{
TA_FREEP(&p->cond);
cond = bstr_strip(cond);
if (cond.len)
p->cond = bstrto0(p, cond);
}

int m_config_set_profile_option(struct m_config *config, struct m_profile *p,
bstr name, bstr val)
{
Expand Down Expand Up @@ -944,6 +958,8 @@ struct mpv_node m_config_get_profiles(struct m_config *config)
node_map_add_string(entry, "name", profile->name);
if (profile->desc)
node_map_add_string(entry, "profile-desc", profile->desc);
if (profile->cond)
node_map_add_string(entry, "profile-cond", profile->cond);

struct mpv_node *opts =
node_map_add(entry, "options", MPV_FORMAT_NODE_ARRAY);
Expand Down
3 changes: 3 additions & 0 deletions options/m_config_frontend.h
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,9 @@ struct m_profile *m_config_add_profile(struct m_config *config, char *name);
*/
void m_profile_set_desc(struct m_profile *p, bstr desc);

// Set auto profile condition of a profile.
void m_profile_set_cond(struct m_profile *p, bstr cond);

/* Add an option to a profile.
* Used by the config file parser when defining a profile.
*
Expand Down
4 changes: 4 additions & 0 deletions options/options.c
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,9 @@ static const m_option_t mp_opts[] = {
.flags = UPDATE_BUILTIN_SCRIPTS},
{"load-osd-console", OPT_FLAG(lua_load_console),
.flags = UPDATE_BUILTIN_SCRIPTS},
{"load-auto-profiles",
OPT_CHOICE(lua_load_auto_profiles, {"no", 0}, {"yes", 1}, {"auto", -1}),
.flags = UPDATE_BUILTIN_SCRIPTS},
#endif

// ------------------------- stream options --------------------
Expand Down Expand Up @@ -944,6 +947,7 @@ static const struct MPOpts mp_default_opts = {
.lua_ytdl_raw_options = NULL,
.lua_load_stats = 1,
.lua_load_console = 1,
.lua_load_auto_profiles = -1,
#endif
.auto_load_scripts = 1,
.loop_times = 1,
Expand Down
1 change: 1 addition & 0 deletions options/options.h
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ typedef struct MPOpts {
char **lua_ytdl_raw_options;
int lua_load_stats;
int lua_load_console;
int lua_load_auto_profiles;

int auto_load_scripts;

Expand Down
3 changes: 3 additions & 0 deletions options/parse_configfile.c
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,9 @@ int m_config_parse(m_config_t *config, const char *location, bstr data,
if (bstr_equals0(option, "profile-desc")) {
m_profile_set_desc(profile, value);
res = 0;
} else if (bstr_equals0(option, "profile-cond")) {
m_profile_set_cond(profile, value);
res = 0;
} else {
res = m_config_set_profile_option(config, profile, option, value);
}
Expand Down
2 changes: 1 addition & 1 deletion player/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -445,7 +445,7 @@ typedef struct MPContext {

struct mp_ipc_ctx *ipc_ctx;

int64_t builtin_script_ids[4];
int64_t builtin_script_ids[5];

pthread_mutex_t abort_lock;

Expand Down
3 changes: 3 additions & 0 deletions player/lua.c
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ static const char * const builtin_lua_scripts[][2] = {
},
{"@console.lua",
# include "generated/player/lua/console.lua.inc"
},
{"@auto_profiles.lua",
# include "generated/player/lua/auto_profiles.lua.inc"
},
{0}
};
Expand Down

0 comments on commit 13d354e

Please sign in to comment.