mirror of
https://github.com/mpv-player/mpv
synced 2025-01-05 03:06:28 +01:00
Merge branch 'input_changes' into master
Conflicts: DOCS/man/en/vo.rst etc/input.conf input/input.c m_property.c
This commit is contained in:
commit
85d185441a
@ -84,7 +84,7 @@ Command line switches
|
||||
``-no-opt``, or better ``--no-opt``.
|
||||
* Per-file options are not the default anymore. You can explicitly specify
|
||||
file local options. See ``Usage`` section.
|
||||
* Table of renamed switches:
|
||||
* Table of renamed/replaced switches:
|
||||
|
||||
=================================== ===================================
|
||||
Old New
|
||||
@ -92,20 +92,53 @@ Command line switches
|
||||
-nosound --no-audio
|
||||
-use-filename-title --title="${filename}"
|
||||
-loop 0 --loop=inf
|
||||
-hardframedrop --framedrop=hard
|
||||
-osdlevel --osd-level
|
||||
-delay --audio-delay
|
||||
-subdelay --sub-delay
|
||||
-subpos --sub-pos
|
||||
-forcedsubsonly --sub-forced-only
|
||||
=================================== ===================================
|
||||
|
||||
input.conf and slave commands
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* Table of renamed slave commands:
|
||||
* Table of renamed input commands:
|
||||
|
||||
=================================== ===================================
|
||||
Old New
|
||||
=================================== ===================================
|
||||
pt_step 1 b playlist_next b
|
||||
pt_step -1 b playlist_prev b
|
||||
pt_clear playlist_clear
|
||||
=================================== ===================================
|
||||
This lists only commands that are not always gracefully handled by the
|
||||
internal legacy translation layer. If an input.conf contains any legacy
|
||||
commands, they will be displayed with ``-v`` when it is loaded, and show
|
||||
and the replacement commands.
|
||||
|
||||
Properties containing ``_`` to separate words use ``-`` instead.
|
||||
|
||||
+--------------------------------+----------------------------------------+
|
||||
| Old | New |
|
||||
+================================+========================================+
|
||||
| pt_step 1 [0|1] | playlist_next [weak|force] |
|
||||
| | (translation layer can't deal with |
|
||||
| | whitespace) |
|
||||
+--------------------------------+----------------------------------------+
|
||||
| pt_step -1 [0|1] | playlist_prev [weak|force] (same) |
|
||||
+--------------------------------+----------------------------------------+
|
||||
| switch_ratio [<ratio>] | set aspect <ratio> |
|
||||
| | set aspect 0 (to reset aspect) |
|
||||
+--------------------------------+----------------------------------------+
|
||||
| step_property_osd <prop> <step>| cycle <prop> <step> (wraps), |
|
||||
| <dir> | add <prop> <step> (clamps). |
|
||||
| | <dir> parameter unsupported. Use |
|
||||
| | a negative step instead. |
|
||||
+--------------------------------+----------------------------------------+
|
||||
| step_property <prop> <step> | Prefix cycle or add with no-osd: |
|
||||
| <dur> | no-osd cycle <prop> <step> |
|
||||
+--------------------------------+----------------------------------------+
|
||||
| osd_show_property_text <text> | show_text <text> |
|
||||
| | The property expansion format string |
|
||||
| | syntax slightly changed. |
|
||||
+--------------------------------+----------------------------------------+
|
||||
| osd_show_text | Now does the same as |
|
||||
| | osd_show_property_text. |
|
||||
+--------------------------------+----------------------------------------+
|
||||
|
||||
Other
|
||||
~~~~~
|
||||
|
308
DOCS/man/en/input.rst
Normal file
308
DOCS/man/en/input.rst
Normal file
@ -0,0 +1,308 @@
|
||||
.. _input:
|
||||
|
||||
INPUT.CONF
|
||||
==========
|
||||
|
||||
The input.conf file consists of a list of key bindings, for example:
|
||||
|
||||
| s screenshot # take a screenshot with the s key
|
||||
|
||||
Each line maps a key to an input command. Keys are specified with their literal
|
||||
value (upper case if combined with ``Shift``), or a name for special keys. For
|
||||
example, ``a`` maps to the ``a`` key without shift, and ``A`` maps to ``a``
|
||||
with shift.
|
||||
|
||||
A list of special keys can be obtained with
|
||||
|
||||
| **mplayer** --input=keylist
|
||||
|
||||
In general, keys can be combined with ``Shift``, ``Ctrl`` and ``Alt``:
|
||||
|
||||
| ctrl+q quit
|
||||
|
||||
General input command syntax
|
||||
----------------------------
|
||||
|
||||
`[Shift+][Ctrl+][Alt+][Meta+]<key> [<prefix>] <command> (<argument>)*`
|
||||
|
||||
Newlines always start a new binding. ``#`` starts a comment (outside of quoted
|
||||
string arguments). To bind commands to the ``#`` key, ``SHARP`` can be used.
|
||||
|
||||
<key> is either the literal character the key produces (ASCII or unicode
|
||||
character), or a symbol name.
|
||||
|
||||
Arguments are separated by whitespace. This applies even to string arguments.
|
||||
For this reason, string arguments should be quoted with ``"``. Inside quotes,
|
||||
C style escaping can be used.
|
||||
|
||||
Optional arguments can be skipped with ``-``.
|
||||
|
||||
List of input commands
|
||||
----------------------
|
||||
|
||||
ignore
|
||||
Use this to "block" keys that should be unbound, and do nothing. Useful for
|
||||
disabling default bindings, without disabling all bindings with
|
||||
``--input=default-bindings=no``.
|
||||
|
||||
seek <seconds> [relative|absolute|absolute-percent] [default-precise|exact|keyframes]
|
||||
Change the playback position. By default, seeks by a relative amount of
|
||||
seconds.
|
||||
|
||||
The second argument sets the seek mode:
|
||||
|
||||
relative (default)
|
||||
Seek relative to current position (a negative value seeks backwards).
|
||||
absolute
|
||||
Seek to a given time.
|
||||
absolute-percent
|
||||
Seek to agiven percent position.
|
||||
|
||||
The third argument defines how exact the seek is:
|
||||
|
||||
default-precise (default)
|
||||
Follow the default behavior as set by ``--hr-seek``, which by default
|
||||
does imprecise seeks (like ``keyframes``).
|
||||
exact
|
||||
Always do exact/hr/precise seeks (slow).
|
||||
keyframes
|
||||
Always restart playback at keyframe boundaries (fast).
|
||||
|
||||
frame_step
|
||||
Basically seek forward by one frame. Actually this plays one frame, then
|
||||
pauses again.
|
||||
|
||||
set <property> "<value>"
|
||||
Set the given property to the given value.
|
||||
|
||||
add <property> [<value>]
|
||||
Add the given value to the property. On overflow or underflow, clamp the
|
||||
property to the maximum. If <value> is omitted, assume ``1``.
|
||||
|
||||
cycle <property> [up|down]
|
||||
Cycle the given property. ``up`` and ``down`` set the cycle direction. On
|
||||
overflow, set the property back to the minimum, on underflow set it to the
|
||||
maximum. If ``up`` or ``down`` is omitted, assume ``up``.
|
||||
|
||||
speed_mult <value>
|
||||
Multiply the ``speed`` property by the given value.
|
||||
|
||||
screenshot [single|each-frame] [video|window]
|
||||
Take a screenshot.
|
||||
|
||||
First argument:
|
||||
|
||||
<single> (default)
|
||||
Take a single screenshot.
|
||||
<each-frame>
|
||||
Take a screenshot each frame. Issue this command again to stop taking
|
||||
screenshots.
|
||||
|
||||
Second argument:
|
||||
|
||||
<video> (default)
|
||||
Save the video image, in its original resolution. Typically without
|
||||
OSD or subtitles, but the exact behavior depends on the selected video
|
||||
output.
|
||||
<window>
|
||||
Save the contents of the mplayer window. Typically scaled, with OSD and
|
||||
subtitles. The exact behavior depends on the selected video output, and
|
||||
if not support is available, this will act like ``video``.
|
||||
|
||||
playlist_next [weak|force]
|
||||
Go to the next entry on the playlist.
|
||||
|
||||
weak (default)
|
||||
If the last file on the playlist is currently played, do nothing.
|
||||
force
|
||||
Terminate playback if there are no more files on the playlist.
|
||||
|
||||
playlist_prev [weak|force]
|
||||
Go to the previous entry on the playlist.
|
||||
|
||||
weak (default)
|
||||
If the first file on the playlist is currently played, do nothing.
|
||||
force
|
||||
Terminate playback if the first file is being played.
|
||||
|
||||
loadfile "<file>" [replace|append]
|
||||
Load the given file and play it.
|
||||
|
||||
Second argument:
|
||||
|
||||
<replace> (default)
|
||||
Stop playback of the current file, and play the new file immediately.
|
||||
<append>
|
||||
Append the file to the playlist.
|
||||
|
||||
loadlist "<playlist>" [replace|append]
|
||||
Load the given playlist file (like ``--playlist``).
|
||||
|
||||
playlist_clear
|
||||
Clear the playlist, except the currently played file.
|
||||
|
||||
run "<command>"
|
||||
Run the given command with ``/bin/sh -c``. The string is expanded like in
|
||||
``--playing-msg`` before
|
||||
|
||||
quit [<code>]
|
||||
Exit the player using the given exit code.
|
||||
|
||||
sub_load "<file>"
|
||||
Load the given subtitle file. It's not selected as current subtitle after
|
||||
loading.
|
||||
|
||||
sub_step <skip>
|
||||
Change subtitle timing such, that the subtitle event after the next <skip>
|
||||
subtitle events is displayed. <skip> can be negative to step back.
|
||||
|
||||
osd [<level>]
|
||||
Toggle OSD level. If <level> is specified, set the OSD mode
|
||||
(see ``--osd-level`` for valid values).
|
||||
|
||||
print_text "<string>"
|
||||
Print text to stdout. The string can contain properties, which are expanded
|
||||
like in ``--playing-msg``.
|
||||
|
||||
show_text "<string>" [<duration>] [<level>]
|
||||
Show text on the OSD. The string can contain properties, which are expanded
|
||||
like in ``--playing-msg``. This can be used to show playback time, filename,
|
||||
and so on.
|
||||
|
||||
<duration> is the time in ms to show the message. By default, it uses the
|
||||
same value as ``--osd-duration``.
|
||||
|
||||
<level> is the minimum OSD level to show the text (see ``--osd-level``).
|
||||
|
||||
show_progress
|
||||
Show the progress bar, the elapsed time and the total duration of the file
|
||||
on the OSD.
|
||||
|
||||
show_chapters
|
||||
Show a list of chapters on the OSD.
|
||||
|
||||
show_tracks
|
||||
Show a list of video/audio/subtitle tracks on the OSD.
|
||||
|
||||
|
||||
|
||||
Undocumented properties: tv_start_scan, tv_step_channel, tv_step_norm,
|
||||
tv_step_chanlist, tv_set_channel, tv_last_channel, tv_set_freq, tv_step_freq,
|
||||
tv_set_norm, dvb_set_channel, radio_step_channel, radio_set_channel,
|
||||
radio_set_freq, radio_step_freq (all of these should be replaced by properties),
|
||||
edl_mark, stop (questionable use), get_property (?), af_switch, af_add, af_del,
|
||||
af_clr, af_cmdline, vo_cmdline (experimental).
|
||||
|
||||
Input command prefixes
|
||||
----------------------
|
||||
|
||||
osd-auto (default)
|
||||
Use the default behavior for this command.
|
||||
no-osd
|
||||
Do not use any OSD for this command.
|
||||
osd-bar
|
||||
If possible, show a bar with this command. Seek commands will show the
|
||||
progress bar, property changing commands may show the newly set value.
|
||||
osd-msg
|
||||
If possible, show an OSD message with this command. The seek command shows
|
||||
the current playback time (like ``show_progress``), property changing
|
||||
commands show the newly set value as text.
|
||||
osd-msg-bar
|
||||
Combine osd-bar and osd-msg.
|
||||
|
||||
|
||||
|
||||
All of these are still overridden by the global ``--osd-level`` settings.
|
||||
|
||||
Undocumented prefixes: pausing, pausing_keep, pausing_toggle,
|
||||
pausing_keep_force. (Should these be made official?)
|
||||
|
||||
Properties
|
||||
----------
|
||||
|
||||
Properties are used to set mplayer options during runtime, or to query arbitrary
|
||||
information. They can be manipulated with the ``set``/``add``/``cycle``
|
||||
commands, and retrieved with ``show_text``, or anything else that uses property
|
||||
expansion. (See ``--playing-msg`` how properties are expanded.)
|
||||
|
||||
``W`` indicates whether the property is generally writeable. If an option
|
||||
is referenced, the property should take/return exactly the same values as the
|
||||
option.
|
||||
|
||||
=========================== = ==================================================
|
||||
Name W Comment
|
||||
=========================== = ==================================================
|
||||
osd-level x see ``--osd-level``
|
||||
loop x see ``--loop``
|
||||
speed x see ``--speed``
|
||||
filename currently played file (path stripped)
|
||||
path currently played file (full path)
|
||||
demuxer
|
||||
stream-pos x byte position in source stream
|
||||
stream-start start byte offset in source stream
|
||||
stream-end end position in bytes in source stream
|
||||
stream-length length in bytes (${stream-end} - ${stream-start})
|
||||
stream-time-pos x time position in source stream (also see time-pos)
|
||||
length length of the current file in seconds
|
||||
percent-pos x position in current file (0-100)
|
||||
time-pos x position in current file in seconds
|
||||
chapter x current chapter number
|
||||
edition x current MKV edition number
|
||||
titles number of DVD titles
|
||||
chapters number of chapters
|
||||
editions number of MKV editions
|
||||
angle current DVD angle
|
||||
metadata metadata key/value pairs
|
||||
metadata/<key> value of metedata entry <key>
|
||||
pause x pause status (bool)
|
||||
pts-association-mode x see ``--pts-association-mode``
|
||||
hr-seek x see ``--hr-seek``
|
||||
volume x current volume (0-100)
|
||||
mute x current mute status (bool)
|
||||
audio-delay x see ``--audio-delay``
|
||||
audio-format audio format (codec tag)
|
||||
audio-codec audio codec selected for decoding
|
||||
audio-bitrate audio bitrate
|
||||
samplerate audio samplerate
|
||||
channels number of audio channels
|
||||
audio x current audio track (similar to ``--aid``)
|
||||
balance x audio channel balance
|
||||
fullscreen x see ``--fullscreen``
|
||||
deinterlace x deinterlacing, if available (bool)
|
||||
colormatrix x see ``--colormatrix``
|
||||
colormatrix-input-range x see ``--colormatrix-input-range``
|
||||
colormatrix-output-range x see ``--colormatrix-output-range``
|
||||
ontop x see ``--ontop``
|
||||
rootwin x see ``--rootwin``
|
||||
border x see ``--border``
|
||||
framedrop x see ``--framedrop``
|
||||
gamma x see ``--gamma``
|
||||
brightness x see ``--brightness``
|
||||
contrast x see ``--contrast``
|
||||
saturation x see ``--saturation``
|
||||
hue x see ``--hue``
|
||||
panscan x see ``--panscan``
|
||||
vsync x see ``--vsync``
|
||||
video-format video format (integer FourCC)
|
||||
video-codec video codec selected for decoding
|
||||
video-bitrate video bitrate
|
||||
width video width
|
||||
height video height
|
||||
fps FPS (may contain bogus values)
|
||||
aspect x video aspect
|
||||
video x current video track (similar to ``--vid``)
|
||||
program x switch TS program (write-only)
|
||||
sub x current subttitle track (similar to ``--sid``)
|
||||
sub-delay x see ``--sub-delay``
|
||||
sub-pos x see ``--sub-pos``
|
||||
sub-visibility x whether current subtitle is rendered
|
||||
sub-forced-only x see ``--sub-forced-only``
|
||||
sub-scale x subtitle font size multiplicator
|
||||
ass-use-margins x see ``--ass-use-margins``
|
||||
ass-vsfilter-aspect-compat x see ``--ass-vsfilter-aspect-compat``
|
||||
ass-style-override x see ``--ass-style-override``
|
||||
tv-brightness
|
||||
tv-contrast
|
||||
tv-saturation
|
||||
tv-hue
|
||||
=========================== = ==================================================
|
@ -409,6 +409,8 @@ OPTIONS
|
||||
|
||||
.. include:: encode.rst
|
||||
|
||||
.. include:: input.rst
|
||||
|
||||
Taking screenshots
|
||||
==================
|
||||
|
||||
|
@ -176,6 +176,14 @@
|
||||
rendering text subtitles. The syntax of the file is exactly like the ``[V4
|
||||
Styles]`` / ``[V4+ Styles]`` section of SSA/ASS.
|
||||
|
||||
--ass-style-override=<yes|no>
|
||||
Control whether user style overrides should be applied.
|
||||
|
||||
:yes: Apply all the ``--ass-*`` style override options. Changing the default
|
||||
for any of these options can lead to incorrect subtitle rendering.
|
||||
(Default.)
|
||||
:no: Render subtitles as forced by subtitle scripts.
|
||||
|
||||
--ass-top-margin=<value>
|
||||
Adds a black band at the top of the frame. The SSA/ASS renderer can place
|
||||
toptitles there (with ``--ass-use-margins``).
|
||||
@ -472,7 +480,7 @@
|
||||
will stay hidden. Supported by video output drivers which use X11 or
|
||||
OS X Cocoa.
|
||||
|
||||
--delay=<sec>
|
||||
--audio-delay=<sec>
|
||||
audio delay in seconds (positive or negative float value). Negative values
|
||||
delay the audio, and positive values delay the video.
|
||||
|
||||
@ -624,7 +632,7 @@
|
||||
there is a change in video parameters, video stream or file. This used to
|
||||
be the default behavior. Currently only affects X11 VOs.
|
||||
|
||||
--forcedsubsonly
|
||||
--sub-forced-only
|
||||
Display only forced subtitles for the DVD subtitle stream selected by e.g.
|
||||
``--slang``.
|
||||
|
||||
@ -643,11 +651,14 @@
|
||||
--fps=<float>
|
||||
Override video framerate. Useful if the original value is wrong or missing.
|
||||
|
||||
--framedrop
|
||||
--framedrop=<no|yes|hard>
|
||||
Skip displaying some frames to maintain A/V sync on slow systems. Video
|
||||
filters are not applied to such frames. For B-frames even decoding is
|
||||
skipped completely. May produce unwatchably choppy output. See also
|
||||
``--hardframedrop``.
|
||||
skipped completely. May produce unwatchably choppy output. With ``hard``,
|
||||
decoding and output of any frame can be skipped, and will lead to an even
|
||||
worse playback experience.
|
||||
|
||||
Practical use of this feature is questionable. Disabled by default.
|
||||
|
||||
--frames=<number>
|
||||
Play/convert only first <number> frames, then quit.
|
||||
@ -656,6 +667,7 @@
|
||||
Specifies the character set that will be passed to FriBiDi when decoding
|
||||
non-UTF-8 subtitles (default: ISO8859-8).
|
||||
|
||||
--fullscreen
|
||||
--fs
|
||||
Fullscreen playback (centers movie, and paints black bands around it).
|
||||
|
||||
@ -753,9 +765,6 @@
|
||||
``--no-grabpointer`` tells the player to not grab the mouse pointer after a
|
||||
video mode change (``--vm``). Useful for multihead setups.
|
||||
|
||||
--hardframedrop
|
||||
More intense frame dropping (breaks decoding). Leads to image distortion!
|
||||
|
||||
--heartbeat-cmd
|
||||
Command that is executed every 30 seconds during playback via *system()* -
|
||||
i.e. using the shell.
|
||||
@ -1294,7 +1303,7 @@
|
||||
--osd-fractions
|
||||
Show OSD times with fractions of seconds.
|
||||
|
||||
--osdlevel=<0-3>
|
||||
--osd-level=<0-3>
|
||||
Specifies which mode the OSD should start in.
|
||||
|
||||
:0: subtitles only
|
||||
@ -1312,9 +1321,6 @@
|
||||
controls how much of the image is cropped. May not work with all video
|
||||
output drivers.
|
||||
|
||||
*NOTE*: Values between -1 and 0 are allowed as well, but highly
|
||||
experimental and may crash or worse. Use at your own risk!
|
||||
|
||||
--panscanrange=<-19.0-99.0>
|
||||
(experimental)
|
||||
Change the range of the pan-and-scan functionality (default: 1). Positive
|
||||
@ -1328,15 +1334,37 @@
|
||||
See also ``--user``.
|
||||
|
||||
--playing-msg=<string>
|
||||
Print out a string before starting playback. The following expansions are
|
||||
supported:
|
||||
Print out a string before starting playback. The string is expanded for
|
||||
properties, e.g. ``--playing-msg=file: ${filename}`` will print the string
|
||||
``file: `` followed by the currently played filename.
|
||||
|
||||
The following expansions are supported:
|
||||
|
||||
${NAME}
|
||||
Expand to the value of the property ``NAME``.
|
||||
?(NAME:TEXT)
|
||||
Expand ``TEXT`` only if the property ``NAME`` is available.
|
||||
?(!NAME:TEXT)
|
||||
Expand ``TEXT`` only if the property ``NAME`` is not available.
|
||||
Expands to the value of the property ``NAME``. If ``NAME`` starts with
|
||||
``=``, use the raw value of the property. If retrieving the property
|
||||
fails, expand to an error string. (Use ``${NAME:}`` with a trailing
|
||||
``:`` to expand to an empty string instead.)
|
||||
${NAME:STR}
|
||||
Expands to the value of the property ``NAME``, or ``STR`` if the
|
||||
property can't be retrieved. ``STR`` is expanded recursively.
|
||||
${!NAME:STR}
|
||||
Expands to ``STR`` (recursively) if the property ``NAME`` can't be
|
||||
retrieved.
|
||||
${?NAME:STR}
|
||||
Expands to ``STR`` (recursively) if the property ``NAME`` is available.
|
||||
$$
|
||||
Expands to ``$``.
|
||||
$}
|
||||
Expands to ``}``. (To produce this character inside rexursive
|
||||
expansion.)
|
||||
$>
|
||||
Disable property expansion and special handling of ``$`` for the rest
|
||||
of the string.
|
||||
|
||||
--status-msg=<string>
|
||||
Print out a custom string during playback instead of the standard status
|
||||
line. Expands properties. See ``--playing-msg``.
|
||||
|
||||
--playlist=<filename>
|
||||
Play files according to a playlist file (ASX, Winamp, SMIL, or
|
||||
@ -1892,7 +1920,7 @@
|
||||
- ``--subcp=enca:pl:cp1250`` guess the encoding for Polish, fall back on
|
||||
cp1250.
|
||||
|
||||
--subdelay=<sec>
|
||||
--sub-delay=<sec>
|
||||
Delays subtitles by <sec> seconds. Can be negative.
|
||||
|
||||
--subfile=<filename>
|
||||
@ -1939,7 +1967,7 @@
|
||||
*NOTE*: <rate> > movie fps speeds the subtitles up for frame-based
|
||||
subtitle files and slows them down for time-based ones.
|
||||
|
||||
--subpos=<0-100>
|
||||
--sub-pos=<0-100>
|
||||
Specify the position of subtitles on the screen. The value is the vertical
|
||||
position of the subtitle in % of the screen height.
|
||||
Can be useful with ``--vf=expand``.
|
||||
@ -1980,7 +2008,7 @@
|
||||
the line used for the OSD and clear it (default: ``^[[A\r^[[K``).
|
||||
|
||||
--title
|
||||
Set the window title. The string can contain property names.
|
||||
Set the window title. Properties are expanded (see ``--playing-msg``).
|
||||
|
||||
--tv=<option1:option2:...>
|
||||
This option tunes various properties of the TV capture module. For
|
||||
|
@ -635,7 +635,7 @@ opengl-old
|
||||
(no-)osd
|
||||
Enable or disable support for OSD rendering via OpenGL (default:
|
||||
enabled). This option is for testing; to disable the OSD use
|
||||
``--osdlevel=0`` instead.
|
||||
``--osd-level=0`` instead.
|
||||
|
||||
sw
|
||||
Continue even if a software renderer is detected.
|
||||
|
@ -59,30 +59,29 @@ fi
|
||||
__midentify__allprops="
|
||||
filename
|
||||
path
|
||||
stream_start
|
||||
stream_end
|
||||
stream_length
|
||||
stream-start
|
||||
stream-end
|
||||
stream-length
|
||||
|
||||
demuxer
|
||||
|
||||
switch_program
|
||||
length
|
||||
chapters
|
||||
editions
|
||||
titles
|
||||
|
||||
switch_audio
|
||||
audio_bitrate
|
||||
audio_codec
|
||||
audio_format
|
||||
audio
|
||||
audio-bitrate
|
||||
audio-codec
|
||||
audio-format
|
||||
channels
|
||||
samplerate
|
||||
|
||||
switch_video
|
||||
video
|
||||
angle
|
||||
video_bitrate
|
||||
video_codec
|
||||
video_format
|
||||
video-bitrate
|
||||
video-codec
|
||||
video-format
|
||||
aspect
|
||||
fps
|
||||
width
|
||||
|
2
bstr.h
2
bstr.h
@ -162,7 +162,7 @@ static inline int bstr_find0(struct bstr haystack, const char *needle)
|
||||
return bstr_find(haystack, bstr0(needle));
|
||||
}
|
||||
|
||||
static inline int bstr_eatstart0(struct bstr *s, char *prefix)
|
||||
static inline int bstr_eatstart0(struct bstr *s, const char *prefix)
|
||||
{
|
||||
return bstr_eatstart(s, bstr0(prefix));
|
||||
}
|
||||
|
@ -382,7 +382,7 @@ const m_option_t common_opts[] = {
|
||||
{"frames", &play_n_frames_mf, CONF_TYPE_INT, CONF_MIN, 0, 0, NULL},
|
||||
|
||||
// seek to byte/seconds position
|
||||
{"sb", &seek_to_byte, CONF_TYPE_POSITION, CONF_MIN, 0, 0, NULL},
|
||||
{"sb", &seek_to_byte, CONF_TYPE_INT64, CONF_MIN, 0, 0, NULL},
|
||||
OPT_TIME("ss", seek_to_sec, 0),
|
||||
|
||||
// start paused
|
||||
@ -460,7 +460,7 @@ const m_option_t common_opts[] = {
|
||||
OPT_FLOATRANGE("speed", playback_speed, 0, 0.01, 100.0),
|
||||
|
||||
// set a-v distance
|
||||
{"delay", &audio_delay, CONF_TYPE_FLOAT, CONF_RANGE, -100.0, 100.0, NULL},
|
||||
{"audio-delay", &audio_delay, CONF_TYPE_FLOAT, CONF_RANGE, -100.0, 100.0, NULL},
|
||||
|
||||
// ignore header-specified delay (dwStart)
|
||||
{"ignore-start", &ignore_start, CONF_TYPE_FLAG, 0, 0, 1, NULL},
|
||||
@ -513,12 +513,12 @@ const m_option_t common_opts[] = {
|
||||
OPT_STRINGLIST("sub", sub_name, 0),
|
||||
OPT_PATHLIST("sub-paths", sub_paths, 0),
|
||||
{"subcp", &sub_cp, CONF_TYPE_STRING, 0, 0, 0, NULL},
|
||||
{"subdelay", &sub_delay, CONF_TYPE_FLOAT, 0, 0.0, 10.0, NULL},
|
||||
{"sub-delay", &sub_delay, CONF_TYPE_FLOAT, 0, 0.0, 10.0, NULL},
|
||||
{"subfps", &sub_fps, CONF_TYPE_FLOAT, 0, 0.0, 10.0, NULL},
|
||||
OPT_MAKE_FLAGS("autosub", sub_auto, 0),
|
||||
{"unicode", &sub_unicode, CONF_TYPE_FLAG, 0, 0, 1, NULL},
|
||||
{"utf8", &sub_utf8, CONF_TYPE_FLAG, 0, 0, 1, NULL},
|
||||
{"forcedsubsonly", &forced_subs_only, CONF_TYPE_FLAG, 0, 0, 1, NULL},
|
||||
{"sub-forced-only", &forced_subs_only, CONF_TYPE_FLAG, 0, 0, 1, NULL},
|
||||
// specify IFO file for VOBSUB subtitle
|
||||
{"ifo", &spudec_ifo, CONF_TYPE_STRING, 0, 0, 0, NULL},
|
||||
// enable Closed Captioning display
|
||||
@ -530,7 +530,7 @@ const m_option_t common_opts[] = {
|
||||
{"font", &font_name, CONF_TYPE_STRING, 0, 0, 0, NULL},
|
||||
{"subfont", &sub_font_name, CONF_TYPE_STRING, 0, 0, 0, NULL},
|
||||
{"ffactor", &font_factor, CONF_TYPE_FLOAT, CONF_RANGE, 0.0, 10.0, NULL},
|
||||
{"subpos", &sub_pos, CONF_TYPE_INT, CONF_RANGE, 0, 100, NULL},
|
||||
{"sub-pos", &sub_pos, CONF_TYPE_INT, CONF_RANGE, 0, 100, NULL},
|
||||
{"subwidth", &sub_width_p, CONF_TYPE_INT, CONF_RANGE, 10, 100, NULL},
|
||||
{"spualign", &spu_alignment, CONF_TYPE_INT, CONF_RANGE, -1, 2, NULL},
|
||||
{"spuaa", &spu_aamode, CONF_TYPE_INT, CONF_RANGE, 0, 31, NULL},
|
||||
@ -554,6 +554,8 @@ const m_option_t common_opts[] = {
|
||||
OPT_STRING("ass-border-color", ass_border_color, 0),
|
||||
OPT_STRING("ass-styles", ass_styles_file, 0),
|
||||
OPT_INTRANGE("ass-hinting", ass_hinting, 0, 0, 7),
|
||||
OPT_CHOICE("ass-style-override", ass_style_override, 0,
|
||||
({"no", 0}, {"yes", 1})),
|
||||
{NULL, NULL, 0, 0, 0, 0, NULL}
|
||||
};
|
||||
|
||||
@ -622,6 +624,7 @@ const m_option_t mplayer_opts[]={
|
||||
// video mode switching: (x11,xv,dga)
|
||||
OPT_MAKE_FLAGS("vm", vidmode, 0),
|
||||
// start in fullscreen mode:
|
||||
OPT_MAKE_FLAGS("fullscreen", fullscreen, CONF_GLOBAL),
|
||||
OPT_MAKE_FLAGS("fs", fullscreen, CONF_GLOBAL),
|
||||
// set fullscreen switch method (workaround for buggy WMs)
|
||||
{"fsmode-dontuse", &vo_fsmode, CONF_TYPE_INT, CONF_RANGE, 0, 31, NULL},
|
||||
@ -632,7 +635,7 @@ const m_option_t mplayer_opts[]={
|
||||
{"double", &vo_doublebuffering, CONF_TYPE_FLAG, 0, 0, 1, NULL},
|
||||
// wait for v-sync (gl)
|
||||
{"vsync", &vo_vsync, CONF_TYPE_FLAG, 0, 0, 1, NULL},
|
||||
{"panscan", &vo_panscan, CONF_TYPE_FLOAT, CONF_RANGE, -1.0, 1.0, NULL},
|
||||
{"panscan", &vo_panscan, CONF_TYPE_FLOAT, CONF_RANGE, 0, 1.0, NULL},
|
||||
OPT_FLOATRANGE("panscanrange", vo_panscanrange, 0, -19.0, 99.0),
|
||||
OPT_CHOICE("colormatrix", requested_colorspace, 0,
|
||||
({"auto", MP_CSP_AUTO},
|
||||
@ -674,16 +677,20 @@ const m_option_t mplayer_opts[]={
|
||||
|
||||
{"use-filedir-conf", &use_filedir_conf, CONF_TYPE_FLAG, CONF_GLOBAL, 0, 1, NULL},
|
||||
OPT_INTRANGE("osdlevel", osd_level, 0, 0, 3),
|
||||
OPT_CHOICE("osd-level", osd_level, 0,
|
||||
({"0", 0}, {"1", 1}, {"2", 2}, {"3", 3})),
|
||||
OPT_INTRANGE("osd-duration", osd_duration, 0, 0, 3600000),
|
||||
OPT_MAKE_FLAGS("osd-fractions", osd_fractions, 0),
|
||||
|
||||
OPT_STRING("vobsub", vobsub_name, 0),
|
||||
{"vobsubid", &vobsub_id, CONF_TYPE_INT, CONF_RANGE, 0, 31, NULL},
|
||||
|
||||
{"sstep", &step_sec, CONF_TYPE_INT, CONF_MIN, 0, 0, NULL},
|
||||
{"sstep", &step_sec, CONF_TYPE_DOUBLE, CONF_MIN, 0, 0, NULL},
|
||||
|
||||
{"framedrop", &frame_dropping, CONF_TYPE_FLAG, 0, 0, 1, NULL},
|
||||
{"hardframedrop", &frame_dropping, CONF_TYPE_FLAG, 0, 0, 2, NULL},
|
||||
OPT_CHOICE("framedrop", frame_dropping, 0,
|
||||
({"no", 0},
|
||||
{"yes", 1}, {"", 1},
|
||||
{"hard", 2})),
|
||||
|
||||
OPT_FLAG_ON("untimed", untimed, 0),
|
||||
|
||||
@ -726,6 +733,7 @@ const m_option_t mplayer_opts[]={
|
||||
|
||||
OPT_STRING("term-osd-esc", term_osd_esc, 0, OPTDEF_STR("\x1b[A\r\x1b[K")),
|
||||
OPT_STRING("playing-msg", playing_msg, 0),
|
||||
OPT_STRING("status-msg", status_msg, 0),
|
||||
|
||||
{"slave-broken", &slave_mode, CONF_TYPE_FLAG,CONF_GLOBAL , 0, 1, NULL},
|
||||
OPT_MAKE_FLAGS("idle", player_idle_mode, CONF_GLOBAL),
|
||||
|
@ -23,7 +23,10 @@ struct MPContext;
|
||||
struct mp_cmd;
|
||||
|
||||
void run_command(struct MPContext *mpctx, struct mp_cmd *cmd);
|
||||
char *property_expand_string(struct MPContext *mpctx, char *str);
|
||||
char *mp_property_expand_string(struct MPContext *mpctx, char *str);
|
||||
void property_print_help(void);
|
||||
int mp_property_do(const char* name, int action, void* val,
|
||||
struct MPContext *mpctx);
|
||||
char* mp_property_print(const char *name, struct MPContext *mpctx);
|
||||
|
||||
#endif /* MPLAYER_COMMAND_H */
|
||||
|
@ -55,6 +55,7 @@ void set_default_mplayer_options(struct MPOpts *opts)
|
||||
#endif
|
||||
.ass_font_scale = 1,
|
||||
.ass_vsfilter_aspect_compat = 1,
|
||||
.ass_style_override = 1,
|
||||
.use_embedded_fonts = 1,
|
||||
|
||||
.lavc_param = {
|
||||
|
162
etc/input.conf
162
etc/input.conf
@ -11,27 +11,25 @@
|
||||
#
|
||||
# Note that merely removing default key bindings from this file won't remove
|
||||
# the default bindings mpv was compiled with, unless
|
||||
# --input=nodefault-bindings
|
||||
# --input=no-default-bindings
|
||||
# is specified.
|
||||
#
|
||||
# Lines starting with # are comments. Use SHARP to assign the # key.
|
||||
#
|
||||
# Some characters need to be escaped. In particular, if you want to display
|
||||
# a '\' character as part of an osd_show_property_text OSD message, you have to
|
||||
# escape 2 times:
|
||||
# key osd_show_property_text "This is a single backslash: \\\\!"
|
||||
# Strings need to be quoted and escaped:
|
||||
# KEY show_text "This is a single backslash: \\ and a quote: \" !"
|
||||
#
|
||||
# You can use modifier-key combinations like Shift+Left or Ctrl+Alt+x with
|
||||
# modifiers Shift, Ctrl, Alt and Meta, but note that currently reading
|
||||
# key combinations is only supported through the video windows of certain
|
||||
# output drivers (not in output windows of other drivers or in a terminal).
|
||||
|
||||
MOUSE_BTN0_DBL vo_fullscreen # toggle fullscreen on/off
|
||||
MOUSE_BTN2 pause # toggle pause on/off
|
||||
MOUSE_BTN0_DBL cycle fullscreen # toggle fullscreen on/off
|
||||
MOUSE_BTN2 cycle pause # toggle pause on/off
|
||||
MOUSE_BTN3 seek 10
|
||||
MOUSE_BTN4 seek -10
|
||||
MOUSE_BTN5 volume 1
|
||||
MOUSE_BTN6 volume -1
|
||||
MOUSE_BTN5 add volume 1
|
||||
MOUSE_BTN6 add volume -1
|
||||
|
||||
# Seek units are in seconds, but note that these are limited by keyframes
|
||||
RIGHT seek 10
|
||||
@ -39,95 +37,95 @@ LEFT seek -10
|
||||
UP seek 60
|
||||
DOWN seek -60
|
||||
# Do smaller, always exact (non-keyframe-limited), seeks with shift.
|
||||
Shift+RIGHT seek 1 0 1
|
||||
Shift+LEFT seek -1 0 1
|
||||
Shift+UP seek 5 0 1
|
||||
Shift+DOWN seek -5 0 1
|
||||
# Don't show them on the OSD (no-osd).
|
||||
Shift+RIGHT no-osd seek 1 - exact
|
||||
Shift+LEFT no-osd seek -1 - exact
|
||||
Shift+UP no-osd seek 5 - exact
|
||||
Shift+DOWN no-osd seek -5 - exact
|
||||
PGUP seek 600
|
||||
PGDWN seek -600
|
||||
+ audio_delay 0.100 # this changes audio/video sync
|
||||
- audio_delay -0.100
|
||||
+ add audio-delay 0.100 # this changes audio/video sync
|
||||
- add audio-delay -0.100
|
||||
[ speed_mult 0.9091 # scale playback speed
|
||||
] speed_mult 1.1
|
||||
{ speed_mult 0.5
|
||||
} speed_mult 2.0
|
||||
BS speed_set 1.0 # reset speed to normal
|
||||
BS set speed 1.0 # reset speed to normal
|
||||
q quit
|
||||
q {encode} quit
|
||||
ESC quit
|
||||
p pause # toggle pause/playback mode
|
||||
p cycle pause # toggle pause/playback mode
|
||||
. frame_step # advance one frame and pause
|
||||
SPACE pause
|
||||
SPACE cycle pause
|
||||
> playlist_next # skip to next file
|
||||
ENTER playlist_next 1 # skip to next file or quit
|
||||
ENTER playlist_next force # skip to next file or quit
|
||||
< playlist_prev # skip to previous file
|
||||
o osd # cycle through OSD mode
|
||||
I osd_show_property_text "${filename}" # display filename in osd
|
||||
P osd_show_progression
|
||||
z sub_delay -0.1 # subtract 100 ms delay from subs
|
||||
x sub_delay +0.1 # add
|
||||
I show_text "${filename}" # display filename in osd
|
||||
P show_progress
|
||||
z add sub-delay -0.1 # subtract 100 ms delay from subs
|
||||
x add sub-delay +0.1 # add
|
||||
g sub_step -1 # immediately display next subtitle
|
||||
y sub_step +1 # previous
|
||||
9 volume -1
|
||||
/ volume -1
|
||||
0 volume 1
|
||||
* volume 1
|
||||
( balance -0.1 # adjust audio balance in favor of left
|
||||
) balance 0.1 # right
|
||||
m mute
|
||||
1 contrast -1
|
||||
2 contrast 1
|
||||
3 brightness -1
|
||||
4 brightness 1
|
||||
5 hue -1
|
||||
6 hue 1
|
||||
7 saturation -1
|
||||
8 saturation 1
|
||||
d frame_drop # cycle through framedrop modes
|
||||
9 add volume -1
|
||||
/ add volume -1
|
||||
0 add volume 1
|
||||
* add volume 1
|
||||
( add balance -0.1 # adjust audio balance in favor of left
|
||||
) add balance 0.1 # right
|
||||
m cycle mute
|
||||
1 add contrast -1
|
||||
2 add contrast 1
|
||||
3 add brightness -1
|
||||
4 add brightness 1
|
||||
5 add hue -1
|
||||
6 add hue 1
|
||||
7 add saturation -1
|
||||
8 add saturation 1
|
||||
d cycle framedrop # cycle through framedrop modes
|
||||
# toggle deinterlacer; requires either vdpau output, -vf yadif or kerndeint
|
||||
D step_property_osd deinterlace
|
||||
c step_property_osd colormatrix
|
||||
D cycle deinterlace
|
||||
c cycle colormatrix
|
||||
# Next 3 currently only work with --no-ass
|
||||
r sub_pos -1 # move subtitles up
|
||||
t sub_pos +1 # down
|
||||
v sub_visibility
|
||||
r add sub-pos -1 # move subtitles up
|
||||
t add sub-pos +1 # down
|
||||
v cycle sub-visibility
|
||||
# stretch SSA/ASS subtitles with anamorphic videos to match historical
|
||||
V step_property_osd ass_vsfilter_aspect_compat
|
||||
j sub_select # cycle through subtitles
|
||||
J sub_select -3 # ...backwards
|
||||
F forced_subs_only
|
||||
SHARP switch_audio # switch audio streams
|
||||
_ step_property switch_video
|
||||
TAB step_property switch_program
|
||||
V cycle ass-vsfilter-aspect-compat
|
||||
j cycle sub # cycle through subtitles
|
||||
J cycle sub down # ...backwards
|
||||
F cycle sub-forced-only
|
||||
SHARP cycle audio # switch audio streams
|
||||
_ cycle video
|
||||
TAB cycle program
|
||||
i edl_mark # for use with --edlout mode
|
||||
T vo_ontop # toggle video window ontop of other windows
|
||||
f vo_fullscreen # toggle fullscreen
|
||||
C step_property_osd capturing
|
||||
s screenshot 0 # take a png screenshot
|
||||
S screenshot 1 # ...on every frame
|
||||
Alt+s screenshot 0 1 # take a screenshot of window contents
|
||||
Alt+S screenshot 1 1 # ...on every frame
|
||||
w panscan -0.1 # zoom out with -panscan 0 -fs
|
||||
e panscan +0.1 # in
|
||||
T cycle ontop # toggle video window ontop of other windows
|
||||
f cycle fullscreen # toggle fullscreen
|
||||
s screenshot # take a png screenshot
|
||||
S screenshot each-frame # ...on every frame
|
||||
Alt+s screenshot - window # take a screenshot of window contents
|
||||
Alt+S screenshot each-frame window # ...on every frame
|
||||
w add panscan -0.1 # zoom out with -panscan 0 -fs
|
||||
e add panscan +0.1 # in
|
||||
POWER quit
|
||||
MENU osd
|
||||
PLAY pause
|
||||
PAUSE pause
|
||||
PLAYPAUSE pause
|
||||
MENU cycle osd
|
||||
PLAY cycle pause
|
||||
PAUSE cycle pause
|
||||
PLAYPAUSE cycle pause
|
||||
STOP quit
|
||||
FORWARD seek 60
|
||||
REWIND seek -60
|
||||
NEXT playlist_next
|
||||
PREV playlist_prev
|
||||
VOLUME_UP volume 1
|
||||
VOLUME_DOWN volume -1
|
||||
MUTE mute
|
||||
VOLUME_UP add volume 1
|
||||
VOLUME_DOWN add volume -1
|
||||
MUTE cycle mute
|
||||
CLOSE_WIN quit
|
||||
CLOSE_WIN {encode} quit
|
||||
! seek_chapter -1 # skip to previous chapter
|
||||
@ seek_chapter 1 # next
|
||||
E step_property_osd edition # next edition
|
||||
A switch_angle 1
|
||||
! add chapter -1 # skip to previous chapter
|
||||
@ add chapter 1 # next
|
||||
E cycle edition # next edition
|
||||
A cycle angle
|
||||
U stop
|
||||
|
||||
# TV
|
||||
@ -140,16 +138,16 @@ u tv_step_chanlist
|
||||
# Apple Remote section
|
||||
#
|
||||
|
||||
AR_PLAY pause
|
||||
AR_PLAY cycle pause
|
||||
AR_PLAY_HOLD quit
|
||||
AR_NEXT seek 30
|
||||
AR_NEXT_HOLD seek 120
|
||||
AR_PREV seek -10
|
||||
AR_PREV_HOLD seek -120
|
||||
AR_MENU osd
|
||||
AR_MENU_HOLD mute
|
||||
AR_VUP volume 1
|
||||
AR_VDOWN volume -1
|
||||
AR_MENU cycle osd
|
||||
AR_MENU_HOLD cycle mute
|
||||
AR_VUP add volume 1
|
||||
AR_VDOWN add volume -1
|
||||
|
||||
#
|
||||
# Joystick section
|
||||
@ -161,15 +159,15 @@ JOY_AXIS0_PLUS seek 10
|
||||
JOY_AXIS0_MINUS seek -10
|
||||
JOY_AXIS1_MINUS seek 60
|
||||
JOY_AXIS1_PLUS seek -60
|
||||
JOY_BTN0 pause
|
||||
JOY_BTN1 osd
|
||||
JOY_BTN2 volume 1
|
||||
JOY_BTN3 volume -1
|
||||
JOY_BTN0 cycle pause
|
||||
JOY_BTN1 cycle osd
|
||||
JOY_BTN2 add volume 1
|
||||
JOY_BTN3 add volume -1
|
||||
|
||||
#
|
||||
# Not assigned by default
|
||||
# (not an exhaustive list of unbound commands)
|
||||
#
|
||||
|
||||
#? sub_scale +0.1 # increase subtitle font size
|
||||
#? sub_scale -0.1 # decrease subtitle font size
|
||||
#? add sub-scale +0.1 # increase subtitle font size
|
||||
#? add sub-scale -0.1 # decrease subtitle font size
|
||||
|
613
input/input.c
613
input/input.c
@ -85,79 +85,64 @@ struct key_name {
|
||||
* argument value if the user didn't give enough arguments to specify it.
|
||||
* A command can take a maximum of MP_CMD_MAX_ARGS arguments (10).
|
||||
*/
|
||||
#define ARG_INT { .type = MP_CMD_ARG_INT }
|
||||
#define OARG_INT(def) { .type = MP_CMD_ARG_INT, .optional = true, .v.i = def }
|
||||
#define ARG_FLOAT { .type = MP_CMD_ARG_FLOAT }
|
||||
#define OARG_FLOAT(def) { .type = MP_CMD_ARG_FLOAT, .optional = true, .v.f = def }
|
||||
#define ARG_STRING { .type = MP_CMD_ARG_STRING }
|
||||
#define OARG_STRING(def) { .type = MP_CMD_ARG_STRING, .optional = true, .v.s = def }
|
||||
|
||||
#define ARG_INT { .type = {"", NULL, &m_option_type_int} }
|
||||
#define ARG_FLOAT { .type = {"", NULL, &m_option_type_float} }
|
||||
#define ARG_STRING { .type = {"", NULL, &m_option_type_string} }
|
||||
#define ARG_CHOICE(c) { .type = {"", NULL, &m_option_type_choice, \
|
||||
M_CHOICES(c)} }
|
||||
|
||||
#define OARG_FLOAT(def) { .type = {"", NULL, &m_option_type_float}, \
|
||||
.optional = true, .v.f = def }
|
||||
#define OARG_INT(def) { .type = {"", NULL, &m_option_type_int}, \
|
||||
.optional = true, .v.i = def }
|
||||
#define OARG_CHOICE(def, c) { .type = {"", NULL, &m_option_type_choice, \
|
||||
M_CHOICES(c)}, \
|
||||
.optional = true, .v.i = def }
|
||||
|
||||
static int parse_cycle_dir(const struct m_option *opt, struct bstr name,
|
||||
struct bstr param, void *dst);
|
||||
static const struct m_option_type m_option_type_cycle_dir = {
|
||||
.name = "up|down",
|
||||
.parse = parse_cycle_dir,
|
||||
};
|
||||
|
||||
static const mp_cmd_t mp_cmds[] = {
|
||||
{ MP_CMD_IGNORE, "ignore", },
|
||||
#ifdef CONFIG_RADIO
|
||||
{ MP_CMD_RADIO_STEP_CHANNEL, "radio_step_channel", { ARG_INT } },
|
||||
{ MP_CMD_RADIO_SET_CHANNEL, "radio_set_channel", { ARG_STRING } },
|
||||
{ MP_CMD_RADIO_SET_FREQ, "radio_set_freq", { ARG_FLOAT } },
|
||||
{ MP_CMD_RADIO_STEP_FREQ, "radio_step_freq", {ARG_FLOAT } },
|
||||
#endif
|
||||
{ MP_CMD_SEEK, "seek", { ARG_FLOAT, OARG_INT(0), OARG_INT(0) } },
|
||||
{ MP_CMD_SEEK, "seek", {
|
||||
ARG_FLOAT,
|
||||
OARG_CHOICE(0, ({"relative", 0}, {"0", 0},
|
||||
{"absolute-percent", 1}, {"1", 1},
|
||||
{"absolute", 2}, {"2", 2})),
|
||||
OARG_CHOICE(0, ({"default-precise", 0}, {"0", 0},
|
||||
{"exact", 1}, {"1", 1},
|
||||
{"keyframes", -1}, {"-1", -1})),
|
||||
}},
|
||||
{ MP_CMD_EDL_MARK, "edl_mark", },
|
||||
{ MP_CMD_AUDIO_DELAY, "audio_delay", { ARG_FLOAT, OARG_INT(0) } },
|
||||
{ MP_CMD_SPEED_INCR, "speed_incr", { ARG_FLOAT } },
|
||||
{ MP_CMD_SPEED_MULT, "speed_mult", { ARG_FLOAT } },
|
||||
{ MP_CMD_SPEED_SET, "speed_set", { ARG_FLOAT } },
|
||||
{ MP_CMD_QUIT, "quit", { OARG_INT(0) } },
|
||||
{ MP_CMD_STOP, "stop", },
|
||||
{ MP_CMD_PAUSE, "pause", },
|
||||
{ MP_CMD_FRAME_STEP, "frame_step", },
|
||||
{ MP_CMD_PLAYLIST_NEXT, "playlist_next", { OARG_INT(0) } },
|
||||
{ MP_CMD_PLAYLIST_PREV, "playlist_prev", { OARG_INT(0) } },
|
||||
{ MP_CMD_LOOP, "loop", { ARG_INT, OARG_INT(0) } },
|
||||
{ MP_CMD_SUB_DELAY, "sub_delay", { ARG_FLOAT, OARG_INT(0) } },
|
||||
{ MP_CMD_SUB_STEP, "sub_step", { ARG_INT, OARG_INT(0) } },
|
||||
{ MP_CMD_PLAYLIST_NEXT, "playlist_next", {
|
||||
OARG_CHOICE(0, ({"weak", 0}, {"0", 0},
|
||||
{"force", 1}, {"1", 1})),
|
||||
}},
|
||||
{ MP_CMD_PLAYLIST_PREV, "playlist_prev", {
|
||||
OARG_CHOICE(0, ({"weak", 0}, {"0", 0},
|
||||
{"force", 1}, {"1", 1})),
|
||||
}},
|
||||
{ MP_CMD_SUB_STEP, "sub_step", { ARG_INT } },
|
||||
{ MP_CMD_OSD, "osd", { OARG_INT(-1) } },
|
||||
{ MP_CMD_OSD_SHOW_TEXT, "osd_show_text", { ARG_STRING, OARG_INT(-1), OARG_INT(0) } },
|
||||
{ MP_CMD_OSD_SHOW_PROPERTY_TEXT, "osd_show_property_text", { ARG_STRING, OARG_INT(-1), OARG_INT(0) } },
|
||||
{ MP_CMD_OSD_SHOW_PROGRESSION, "osd_show_progression", },
|
||||
{ MP_CMD_VOLUME, "volume", { ARG_FLOAT, OARG_INT(0) } },
|
||||
{ MP_CMD_BALANCE, "balance", { ARG_FLOAT, OARG_INT(0) } },
|
||||
{ MP_CMD_MIXER_USEMASTER, "use_master", },
|
||||
{ MP_CMD_MUTE, "mute", { OARG_INT(-1) } },
|
||||
{ MP_CMD_CONTRAST, "contrast", { ARG_INT, OARG_INT(0) } },
|
||||
{ MP_CMD_GAMMA, "gamma", { ARG_INT, OARG_INT(0) } },
|
||||
{ MP_CMD_BRIGHTNESS, "brightness", { ARG_INT, OARG_INT(0) } },
|
||||
{ MP_CMD_HUE, "hue", { ARG_INT, OARG_INT(0) } },
|
||||
{ MP_CMD_SATURATION, "saturation", { ARG_INT, OARG_INT(0) } },
|
||||
{ MP_CMD_FRAMEDROPPING, "frame_drop", { OARG_INT(-1) } },
|
||||
{ MP_CMD_SUB_POS, "sub_pos", { ARG_INT, OARG_INT(0) } },
|
||||
{ MP_CMD_SUB_ALIGNMENT, "sub_alignment", { OARG_INT(-1) } },
|
||||
{ MP_CMD_SUB_VISIBILITY, "sub_visibility", { OARG_INT(-1) } },
|
||||
{ MP_CMD_PRINT_TEXT, "print_text", { ARG_STRING } },
|
||||
{ MP_CMD_SHOW_TEXT, "show_text", { ARG_STRING, OARG_INT(-1), OARG_INT(0) } },
|
||||
{ MP_CMD_SHOW_PROGRESS, "show_progress", },
|
||||
{ MP_CMD_SUB_LOAD, "sub_load", { ARG_STRING } },
|
||||
{ MP_CMD_SUB_SELECT, "vobsub_lang", { OARG_INT(-2) } }, // for compatibility
|
||||
{ MP_CMD_SUB_SELECT, "sub_select", { OARG_INT(-2) } },
|
||||
{ MP_CMD_SUB_SCALE, "sub_scale", { ARG_FLOAT, OARG_INT(0) } },
|
||||
#ifdef CONFIG_ASS
|
||||
{ MP_CMD_ASS_USE_MARGINS, "ass_use_margins", { OARG_INT(-1) } },
|
||||
#endif
|
||||
{ MP_CMD_GET_PERCENT_POS, "get_percent_pos", },
|
||||
{ MP_CMD_GET_TIME_POS, "get_time_pos", },
|
||||
{ MP_CMD_GET_TIME_LENGTH, "get_time_length", },
|
||||
{ MP_CMD_GET_FILENAME, "get_file_name", },
|
||||
{ MP_CMD_GET_VIDEO_CODEC, "get_video_codec", },
|
||||
{ MP_CMD_GET_VIDEO_BITRATE, "get_video_bitrate", },
|
||||
{ MP_CMD_GET_VIDEO_RESOLUTION, "get_video_resolution", },
|
||||
{ MP_CMD_GET_AUDIO_CODEC, "get_audio_codec", },
|
||||
{ MP_CMD_GET_AUDIO_BITRATE, "get_audio_bitrate", },
|
||||
{ MP_CMD_GET_AUDIO_SAMPLES, "get_audio_samples", },
|
||||
{ MP_CMD_GET_META_TITLE, "get_meta_title", },
|
||||
{ MP_CMD_GET_META_ARTIST, "get_meta_artist", },
|
||||
{ MP_CMD_GET_META_ALBUM, "get_meta_album", },
|
||||
{ MP_CMD_GET_META_YEAR, "get_meta_year", },
|
||||
{ MP_CMD_GET_META_COMMENT, "get_meta_comment", },
|
||||
{ MP_CMD_GET_META_TRACK, "get_meta_track", },
|
||||
{ MP_CMD_GET_META_GENRE, "get_meta_genre", },
|
||||
{ MP_CMD_SWITCH_AUDIO, "switch_audio", { OARG_INT(-1) } },
|
||||
{ MP_CMD_SWITCH_ANGLE, "switch_angle", { OARG_INT(-1) } },
|
||||
{ MP_CMD_SWITCH_TITLE, "switch_title", { OARG_INT(-1) } },
|
||||
#ifdef CONFIG_TV
|
||||
{ MP_CMD_TV_START_SCAN, "tv_start_scan", },
|
||||
{ MP_CMD_TV_STEP_CHANNEL, "tv_step_channel", { ARG_INT } },
|
||||
@ -168,38 +153,40 @@ static const mp_cmd_t mp_cmds[] = {
|
||||
{ MP_CMD_TV_SET_FREQ, "tv_set_freq", { ARG_FLOAT } },
|
||||
{ MP_CMD_TV_STEP_FREQ, "tv_step_freq", { ARG_FLOAT } },
|
||||
{ MP_CMD_TV_SET_NORM, "tv_set_norm", { ARG_STRING } },
|
||||
{ MP_CMD_TV_SET_BRIGHTNESS, "tv_set_brightness", { ARG_INT, OARG_INT(1) } },
|
||||
{ MP_CMD_TV_SET_CONTRAST, "tv_set_contrast", { ARG_INT, OARG_INT(1) } },
|
||||
{ MP_CMD_TV_SET_HUE, "tv_set_hue", { ARG_INT, OARG_INT(1) } },
|
||||
{ MP_CMD_TV_SET_SATURATION, "tv_set_saturation", { ARG_INT, OARG_INT(1) } },
|
||||
#endif
|
||||
{ MP_CMD_SUB_FORCED_ONLY, "forced_subs_only", { OARG_INT(-1) } },
|
||||
#ifdef CONFIG_DVBIN
|
||||
{ MP_CMD_DVB_SET_CHANNEL, "dvb_set_channel", { ARG_INT, ARG_INT } },
|
||||
#endif
|
||||
{ MP_CMD_SWITCH_RATIO, "switch_ratio", { OARG_FLOAT(0) } },
|
||||
{ MP_CMD_VO_FULLSCREEN, "vo_fullscreen", { OARG_INT(-1) } },
|
||||
{ MP_CMD_VO_ONTOP, "vo_ontop", { OARG_INT(-1) } },
|
||||
{ MP_CMD_VO_ROOTWIN, "vo_rootwin", { OARG_INT(-1) } },
|
||||
{ MP_CMD_VO_BORDER, "vo_border", { OARG_INT(-1) } },
|
||||
{ MP_CMD_SCREENSHOT, "screenshot", { OARG_INT(0), OARG_INT(0) } },
|
||||
{ MP_CMD_PANSCAN, "panscan", { ARG_FLOAT, OARG_INT(0) } },
|
||||
{ MP_CMD_SWITCH_VSYNC, "switch_vsync", { OARG_INT(0) } },
|
||||
{ MP_CMD_LOADFILE, "loadfile", { ARG_STRING, OARG_INT(0) } },
|
||||
{ MP_CMD_LOADLIST, "loadlist", { ARG_STRING, OARG_INT(0) } },
|
||||
{ MP_CMD_SCREENSHOT, "screenshot", {
|
||||
OARG_CHOICE(0, ({"single", 0}, {"0", 0},
|
||||
{"each-frame", 1}, {"1", 1})),
|
||||
OARG_CHOICE(0, ({"video", 0}, {"0", 0},
|
||||
{"window", 1}, {"1", 1})),
|
||||
}},
|
||||
{ MP_CMD_LOADFILE, "loadfile", {
|
||||
ARG_STRING,
|
||||
OARG_CHOICE(0, ({"replace", 0}, {"0", 0},
|
||||
{"append", 1}, {"1", 1})),
|
||||
}},
|
||||
{ MP_CMD_LOADLIST, "loadlist", {
|
||||
ARG_STRING,
|
||||
OARG_CHOICE(0, ({"replace", 0}, {"0", 0},
|
||||
{"append", 1}, {"1", 1})),
|
||||
}},
|
||||
{ MP_CMD_PLAYLIST_CLEAR, "playlist_clear", },
|
||||
{ MP_CMD_RUN, "run", { ARG_STRING } },
|
||||
|
||||
{ MP_CMD_GET_VO_FULLSCREEN, "get_vo_fullscreen", },
|
||||
{ MP_CMD_GET_SUB_VISIBILITY, "get_sub_visibility", },
|
||||
{ MP_CMD_KEYDOWN_EVENTS, "key_down_event", { ARG_INT } },
|
||||
{ MP_CMD_SET_PROPERTY, "set_property", { ARG_STRING, ARG_STRING } },
|
||||
{ MP_CMD_SET_PROPERTY_OSD, "set_property_osd", { ARG_STRING, ARG_STRING } },
|
||||
{ MP_CMD_SET, "set", { ARG_STRING, ARG_STRING } },
|
||||
{ MP_CMD_GET_PROPERTY, "get_property", { ARG_STRING } },
|
||||
{ MP_CMD_STEP_PROPERTY, "step_property", { ARG_STRING, OARG_FLOAT(0), OARG_INT(0) } },
|
||||
{ MP_CMD_STEP_PROPERTY_OSD, "step_property_osd", { ARG_STRING, OARG_FLOAT(0), OARG_INT(0) } },
|
||||
{ MP_CMD_ADD, "add", { ARG_STRING, OARG_FLOAT(0) } },
|
||||
{ MP_CMD_CYCLE, "cycle", {
|
||||
ARG_STRING,
|
||||
{ .type = {"", NULL, &m_option_type_cycle_dir},
|
||||
.optional = true,
|
||||
.v.f = 1 },
|
||||
}},
|
||||
|
||||
{ MP_CMD_SEEK_CHAPTER, "seek_chapter", { ARG_INT, OARG_INT(0) } },
|
||||
{ MP_CMD_SET_MOUSE_POS, "set_mouse_pos", { ARG_INT, ARG_INT } },
|
||||
|
||||
{ MP_CMD_AF_SWITCH, "af_switch", { ARG_STRING } },
|
||||
@ -208,14 +195,71 @@ static const mp_cmd_t mp_cmds[] = {
|
||||
{ MP_CMD_AF_CLR, "af_clr", },
|
||||
{ MP_CMD_AF_CMDLINE, "af_cmdline", { ARG_STRING, ARG_STRING } },
|
||||
|
||||
{ MP_CMD_SHOW_CHAPTERS, "show_chapters_osd", },
|
||||
{ MP_CMD_SHOW_TRACKS, "show_tracks_osd", },
|
||||
{ MP_CMD_SHOW_CHAPTERS, "show_chapters", },
|
||||
{ MP_CMD_SHOW_TRACKS, "show_tracks", },
|
||||
|
||||
{ MP_CMD_VO_CMDLINE, "vo_cmdline", { ARG_STRING } },
|
||||
|
||||
{0}
|
||||
};
|
||||
|
||||
// Map legacy commands to proper commands
|
||||
struct legacy_cmd {
|
||||
const char *old, *new;
|
||||
};
|
||||
static const struct legacy_cmd legacy_cmds[] = {
|
||||
{"loop", "cycle loop"},
|
||||
{"seek_chapter", "add chapter"},
|
||||
{"switch_angle", "cycle angle"},
|
||||
{"pause", "cycle pause"},
|
||||
{"volume", "add volume"},
|
||||
{"mute", "cycle mute"},
|
||||
{"audio_delay", "add audio-delay"},
|
||||
{"switch_audio", "cycle audio"},
|
||||
{"balance", "add balance"},
|
||||
{"vo_fullscreen", "no-osd cycle fullscreen"},
|
||||
{"panscan", "add panscan"},
|
||||
{"vo_ontop", "cycle ontop"},
|
||||
{"vo_rootwin", "cycle rootwin"},
|
||||
{"vo_border", "cycle border"},
|
||||
{"frame_drop", "cycle framedrop"},
|
||||
{"gamma", "add gamma"},
|
||||
{"brightness", "add brightness"},
|
||||
{"contrast", "add contrast"},
|
||||
{"saturation", "add saturation"},
|
||||
{"hue", "add hue"},
|
||||
{"switch_vsync", "cycle vsync"},
|
||||
{"sub_select", "cycle sub"},
|
||||
{"sub_pos", "add sub-pos"},
|
||||
{"sub_delay", "add sub-delay"},
|
||||
{"sub_visibility", "cycle sub-visibility"},
|
||||
{"forced_subs_only", "cycle sub-forced-only"},
|
||||
{"sub_scale", "add sub-scale"},
|
||||
{"ass_use_margins", "cycle ass-use-margins"},
|
||||
{"tv_set_brightness", "add tv-brightness"},
|
||||
{"tv_set_hue", "add tv-hue"},
|
||||
{"tv_set_saturation", "add tv-saturation"},
|
||||
{"tv_set_contrast", "add tv-contrast"},
|
||||
{"step_property_osd", "cycle"},
|
||||
{"step_property", "no-osd cycle"},
|
||||
{"set_property", "no-osd set"},
|
||||
{"set_property_osd", "set"},
|
||||
{"speed_set", "set speed"},
|
||||
{"osd_show_text", "show_text"},
|
||||
{"osd_show_property_text", "show_text"},
|
||||
{"osd_show_progression", "show_progress"},
|
||||
{"show_chapters_osd", "show_chapters"},
|
||||
{"show_tracks_osd", "show_tracks"},
|
||||
// Approximate (can fail if user added additional whitespace)
|
||||
{"pt_step 1", "playlist_next"},
|
||||
{"pt_step -1", "playlist_prev"},
|
||||
// Switch_ratio without argument resets aspect ratio
|
||||
{"switch_ratio ", "set aspect "},
|
||||
{"switch_ratio", "set aspect 0"},
|
||||
{0}
|
||||
};
|
||||
|
||||
|
||||
/// The names of the keys as used in input.conf
|
||||
/// If you add some new keys, you also need to add them here
|
||||
|
||||
@ -698,146 +742,240 @@ int mp_input_add_key_fd(struct input_ctx *ictx, int fd, int select,
|
||||
return 1;
|
||||
}
|
||||
|
||||
mp_cmd_t *mp_input_parse_cmd(char *str)
|
||||
static int parse_cycle_dir(const struct m_option *opt, struct bstr name,
|
||||
struct bstr param, void *dst)
|
||||
{
|
||||
float val;
|
||||
if (bstrcmp0(param, "up") == 0) {
|
||||
val = +1;
|
||||
} else if (bstrcmp0(param, "down") == 0) {
|
||||
val = -1;
|
||||
} else {
|
||||
return m_option_type_float.parse(opt, name, param, dst);
|
||||
}
|
||||
*(float *)dst = val;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool read_token(bstr str, bstr *out_rest, bstr *out_token)
|
||||
{
|
||||
bstr t = bstr_lstrip(str);
|
||||
int next = bstrcspn(t, WHITESPACE "#");
|
||||
// Handle comments
|
||||
if (t.start[next] == '#')
|
||||
t = bstr_splice(t, 0, next);
|
||||
if (!t.len)
|
||||
return false;
|
||||
*out_token = bstr_splice(t, 0, next);
|
||||
*out_rest = bstr_cut(t, next);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool eat_token(bstr *str, const char *tok)
|
||||
{
|
||||
bstr rest, token;
|
||||
if (read_token(*str, &rest, &token) && bstrcmp0(token, tok) == 0) {
|
||||
*str = rest;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool append_escape(bstr *code, char **str)
|
||||
{
|
||||
if (code->len < 1)
|
||||
return false;
|
||||
char replace = 0;
|
||||
switch (code->start[0]) {
|
||||
case '"': replace = '"'; break;
|
||||
case '\\': replace = '\\'; break;
|
||||
case 'b': replace = '\b'; break;
|
||||
case 'f': replace = '\f'; break;
|
||||
case 'n': replace = '\n'; break;
|
||||
case 'r': replace = '\r'; break;
|
||||
case 't': replace = '\t'; break;
|
||||
case 'e': replace = '\x1b'; break;
|
||||
case '\'': replace = '\''; break;
|
||||
}
|
||||
if (replace) {
|
||||
*str = talloc_strndup_append_buffer(*str, &replace, 1);
|
||||
*code = bstr_cut(*code, 1);
|
||||
return true;
|
||||
}
|
||||
if (code->start[0] == 'x' && code->len >= 3) {
|
||||
bstr num = bstr_splice(*code, 1, 3);
|
||||
char c = bstrtoll(num, &num, 16);
|
||||
if (!num.len)
|
||||
return false;
|
||||
*str = talloc_strndup_append_buffer(*str, &c, 1);
|
||||
*code = bstr_cut(*code, 3);
|
||||
return true;
|
||||
}
|
||||
if (code->start[0] == 'u' && code->len >= 5) {
|
||||
bstr num = bstr_splice(*code, 1, 5);
|
||||
int c = bstrtoll(num, &num, 16);
|
||||
if (num.len)
|
||||
return false;
|
||||
*str = append_utf8_buffer(*str, c);
|
||||
*code = bstr_cut(*code, 5);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool read_escaped_string(void *talloc_ctx, bstr *str, bstr *literal)
|
||||
{
|
||||
bstr t = *str;
|
||||
char *new = talloc_strdup(talloc_ctx, "");
|
||||
while (t.len) {
|
||||
if (t.start[0] == '"')
|
||||
break;
|
||||
if (t.start[0] == '\\') {
|
||||
t = bstr_cut(t, 1);
|
||||
if (!append_escape(&t, &new))
|
||||
goto error;
|
||||
} else {
|
||||
new = talloc_strndup_append_buffer(new, t.start, 1);
|
||||
t = bstr_cut(t, 1);
|
||||
}
|
||||
}
|
||||
int len = str->len - t.len;
|
||||
*literal = new ? bstr0(new) : bstr_splice(*str, 0, len);
|
||||
*str = bstr_cut(*str, len);
|
||||
return true;
|
||||
error:
|
||||
talloc_free(new);
|
||||
return false;
|
||||
}
|
||||
|
||||
mp_cmd_t *mp_input_parse_cmd(bstr str)
|
||||
{
|
||||
int i, l;
|
||||
int pausing = 0;
|
||||
char *ptr;
|
||||
const mp_cmd_t *cmd_def;
|
||||
int on_osd = MP_ON_OSD_AUTO;
|
||||
struct mp_cmd *cmd = NULL;
|
||||
void *tmp = talloc_new(NULL);
|
||||
|
||||
// Ignore heading spaces.
|
||||
while (str[0] == ' ' || str[0] == '\t')
|
||||
++str;
|
||||
|
||||
if (strncmp(str, "pausing ", 8) == 0) {
|
||||
if (eat_token(&str, "pausing")) {
|
||||
pausing = 1;
|
||||
str = &str[8];
|
||||
} else if (strncmp(str, "pausing_keep ", 13) == 0) {
|
||||
} else if (eat_token(&str, "pausing_keep")) {
|
||||
pausing = 2;
|
||||
str = &str[13];
|
||||
} else if (strncmp(str, "pausing_toggle ", 15) == 0) {
|
||||
} else if (eat_token(&str, "pausing_toggle")) {
|
||||
pausing = 3;
|
||||
str = &str[15];
|
||||
} else if (strncmp(str, "pausing_keep_force ", 19) == 0) {
|
||||
} else if (eat_token(&str, "pausing_keep_force")) {
|
||||
pausing = 4;
|
||||
str = &str[19];
|
||||
}
|
||||
|
||||
ptr = str + strcspn(str, "\t ");
|
||||
if (*ptr != 0)
|
||||
l = ptr - str;
|
||||
else
|
||||
l = strlen(str);
|
||||
|
||||
if (l == 0)
|
||||
return NULL;
|
||||
|
||||
for (i = 0; mp_cmds[i].name != NULL; i++) {
|
||||
if (strncasecmp(mp_cmds[i].name, str, l) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (mp_cmds[i].name == NULL)
|
||||
return NULL;
|
||||
|
||||
cmd_def = &mp_cmds[i];
|
||||
|
||||
mp_cmd_t *cmd = talloc_ptrtype(NULL, cmd);
|
||||
*cmd = (mp_cmd_t){
|
||||
.id = cmd_def->id,
|
||||
.name = talloc_strdup(cmd, cmd_def->name),
|
||||
.pausing = pausing,
|
||||
};
|
||||
|
||||
ptr = str;
|
||||
|
||||
for (i = 0; ptr && i < MP_CMD_MAX_ARGS; i++) {
|
||||
while (ptr[0] != ' ' && ptr[0] != '\t' && ptr[0] != '\0')
|
||||
ptr++;
|
||||
if (ptr[0] == '\0')
|
||||
break;
|
||||
while (ptr[0] == ' ' || ptr[0] == '\t')
|
||||
ptr++;
|
||||
if (ptr[0] == '\0' || ptr[0] == '#')
|
||||
break;
|
||||
cmd->args[i].type = cmd_def->args[i].type;
|
||||
switch (cmd_def->args[i].type) {
|
||||
case MP_CMD_ARG_INT:
|
||||
errno = 0;
|
||||
cmd->args[i].v.i = atoi(ptr);
|
||||
if (errno != 0) {
|
||||
mp_tmsg(MSGT_INPUT, MSGL_ERR, "Command %s: argument %d "
|
||||
"isn't an integer.\n", cmd_def->name, i + 1);
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
case MP_CMD_ARG_FLOAT:
|
||||
errno = 0;
|
||||
cmd->args[i].v.f = atof(ptr);
|
||||
if (errno != 0) {
|
||||
mp_tmsg(MSGT_INPUT, MSGL_ERR, "Command %s: argument %d "
|
||||
"isn't a float.\n", cmd_def->name, i + 1);
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
case MP_CMD_ARG_STRING: {
|
||||
int term = ' ';
|
||||
if (*ptr == '\'' || *ptr == '"')
|
||||
term = *ptr++;
|
||||
char *argptr = talloc_size(cmd, strlen(ptr) + 1);
|
||||
cmd->args[i].v.s = argptr;
|
||||
while (1) {
|
||||
if (*ptr == 0) {
|
||||
if (term == ' ')
|
||||
break;
|
||||
mp_tmsg(MSGT_INPUT, MSGL_ERR, "Command %s: argument %d is "
|
||||
"unterminated.\n", cmd_def->name, i + 1);
|
||||
goto error;
|
||||
}
|
||||
if (*ptr == term || (*ptr == '\t' && term == ' '))
|
||||
break;
|
||||
if (*ptr == '\\')
|
||||
ptr++;
|
||||
if (*ptr != 0)
|
||||
*argptr++ = *ptr++;
|
||||
}
|
||||
*argptr = 0;
|
||||
str = bstr_lstrip(str);
|
||||
for (const struct legacy_cmd *entry = legacy_cmds; entry->old; entry++) {
|
||||
size_t old_len = strlen(entry->old);
|
||||
if (bstrcasecmp(bstr_splice(str, 0, old_len),
|
||||
(bstr) {(char *)entry->old, old_len}) == 0)
|
||||
{
|
||||
mp_tmsg(MSGT_INPUT, MSGL_V, "Warning: command '%s' is "
|
||||
"deprecated, replaced with '%s'. Fix your input.conf!\n",
|
||||
entry->old, entry->new);
|
||||
bstr s = bstr_cut(str, old_len);
|
||||
str = bstr0(talloc_asprintf(tmp, "%s%.*s", entry->new, BSTR_P(s)));
|
||||
break;
|
||||
}
|
||||
case 0:
|
||||
ptr = NULL;
|
||||
break;
|
||||
default:
|
||||
mp_tmsg(MSGT_INPUT, MSGL_ERR, "Unknown argument %d\n", i);
|
||||
}
|
||||
}
|
||||
cmd->nargs = i;
|
||||
|
||||
int min_args;
|
||||
for (min_args = 0; min_args < MP_CMD_MAX_ARGS
|
||||
&& cmd_def->args[min_args].type
|
||||
&& !cmd_def->args[min_args].optional; min_args++);
|
||||
if (cmd->nargs < min_args) {
|
||||
mp_tmsg(MSGT_INPUT, MSGL_ERR, "Command \"%s\" requires at least %d "
|
||||
"arguments, we found only %d so far.\n", cmd_def->name,
|
||||
min_args, cmd->nargs);
|
||||
if (eat_token(&str, "no-osd")) {
|
||||
on_osd = MP_ON_OSD_NO;
|
||||
} else if (eat_token(&str, "osd-bar")) {
|
||||
on_osd = MP_ON_OSD_BAR;
|
||||
} else if (eat_token(&str, "osd-msg")) {
|
||||
on_osd = MP_ON_OSD_MSG;
|
||||
} else if (eat_token(&str, "osd-msg-bar")) {
|
||||
on_osd = MP_ON_OSD_MSG | MP_ON_OSD_BAR;
|
||||
} else if (eat_token(&str, "osd-auto")) {
|
||||
// default
|
||||
}
|
||||
|
||||
int cmd_idx = 0;
|
||||
while (mp_cmds[cmd_idx].name != NULL) {
|
||||
if (eat_token(&str, mp_cmds[cmd_idx].name))
|
||||
break;
|
||||
cmd_idx++;
|
||||
}
|
||||
|
||||
if (mp_cmds[cmd_idx].name == NULL) {
|
||||
mp_tmsg(MSGT_INPUT, MSGL_ERR, "Command '%.*s' not found.\n",
|
||||
BSTR_P(str));
|
||||
goto error;
|
||||
}
|
||||
|
||||
for (; i < MP_CMD_MAX_ARGS && cmd_def->args[i].type; i++) {
|
||||
memcpy(&cmd->args[i], &cmd_def->args[i], sizeof(struct mp_cmd_arg));
|
||||
if (cmd_def->args[i].type == MP_CMD_ARG_STRING
|
||||
&& cmd_def->args[i].v.s != NULL)
|
||||
cmd->args[i].v.s = talloc_strdup(cmd, cmd_def->args[i].v.s);
|
||||
cmd = talloc_ptrtype(NULL, cmd);
|
||||
*cmd = mp_cmds[cmd_idx];
|
||||
cmd->pausing = pausing;
|
||||
cmd->on_osd = on_osd;
|
||||
|
||||
for (int i = 0; i < MP_CMD_MAX_ARGS; i++) {
|
||||
struct mp_cmd_arg *cmdarg = &cmd->args[i];
|
||||
if (!cmdarg->type.type)
|
||||
break;
|
||||
cmd->nargs++;
|
||||
str = bstr_lstrip(str);
|
||||
bstr arg = {0};
|
||||
if (cmdarg->type.type == &m_option_type_string &&
|
||||
bstr_eatstart0(&str, "\""))
|
||||
{
|
||||
if (!read_escaped_string(tmp, &str, &arg)) {
|
||||
mp_tmsg(MSGT_INPUT, MSGL_ERR, "Command %s: argument %d "
|
||||
"has broken string escapes.\n", cmd->name, i + 1);
|
||||
goto error;
|
||||
}
|
||||
if (!bstr_eatstart0(&str, "\"")) {
|
||||
mp_tmsg(MSGT_INPUT, MSGL_ERR, "Command %s: argument %d is "
|
||||
"unterminated.\n", cmd->name, i + 1);
|
||||
goto error;
|
||||
}
|
||||
} else {
|
||||
if (!read_token(str, &str, &arg))
|
||||
break;
|
||||
if (cmdarg->optional && bstrcmp0(arg, "-") == 0)
|
||||
continue;
|
||||
}
|
||||
// Prevent option API from trying to deallocate static strings
|
||||
cmdarg->v = ((struct mp_cmd_arg) {{0}}).v;
|
||||
int r = m_option_parse(&cmdarg->type, bstr0(cmd->name), arg, &cmdarg->v);
|
||||
if (r < 0) {
|
||||
mp_tmsg(MSGT_INPUT, MSGL_ERR, "Command %s: argument %d "
|
||||
"can't be parsed: %s.\n", cmd->name, i + 1,
|
||||
m_option_strerror(r));
|
||||
goto error;
|
||||
}
|
||||
if (cmdarg->type.type == &m_option_type_string)
|
||||
cmdarg->v.s = talloc_steal(cmd, cmdarg->v.s);
|
||||
}
|
||||
|
||||
if (i < MP_CMD_MAX_ARGS)
|
||||
cmd->args[i].type = 0;
|
||||
bstr dummy;
|
||||
if (read_token(str, &dummy, &dummy)) {
|
||||
mp_tmsg(MSGT_INPUT, MSGL_ERR, "Command %s has trailing unused "
|
||||
"arguments: '%.*s'.\n", cmd->name, BSTR_P(str));
|
||||
// Better make it fatal to make it clear something is wrong.
|
||||
goto error;
|
||||
}
|
||||
|
||||
int min_args = 0;
|
||||
while (min_args < MP_CMD_MAX_ARGS && cmd->args[min_args].type.type
|
||||
&& !cmd->args[min_args].optional)
|
||||
{
|
||||
min_args++;
|
||||
}
|
||||
if (cmd->nargs < min_args) {
|
||||
mp_tmsg(MSGT_INPUT, MSGL_ERR, "Command %s requires at least %d "
|
||||
"arguments, we found only %d so far.\n", cmd->name, min_args,
|
||||
cmd->nargs);
|
||||
goto error;
|
||||
}
|
||||
|
||||
talloc_free(tmp);
|
||||
return cmd;
|
||||
|
||||
error:
|
||||
mp_cmd_free(cmd);
|
||||
error:
|
||||
talloc_free(cmd);
|
||||
talloc_free(tmp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -972,15 +1110,14 @@ static char *find_bind_for_key(const struct cmd_bind *binds, int n, int *keys)
|
||||
}
|
||||
|
||||
static struct cmd_bind_section *get_bind_section(struct input_ctx *ictx,
|
||||
bool builtin,
|
||||
char *section)
|
||||
bool builtin, bstr section)
|
||||
{
|
||||
struct cmd_bind_section *bind_section = ictx->cmd_bind_sections;
|
||||
|
||||
if (section == NULL)
|
||||
section = "default";
|
||||
if (section.len == 0)
|
||||
section = bstr0("default");
|
||||
while (bind_section) {
|
||||
if (strcmp(section, bind_section->section) == 0
|
||||
if (bstrcmp0(section, bind_section->section) == 0
|
||||
&& builtin == bind_section->is_builtin)
|
||||
return bind_section;
|
||||
if (bind_section->next == NULL)
|
||||
@ -995,7 +1132,7 @@ static struct cmd_bind_section *get_bind_section(struct input_ctx *ictx,
|
||||
bind_section = ictx->cmd_bind_sections;
|
||||
}
|
||||
bind_section->cmd_binds = NULL;
|
||||
bind_section->section = talloc_strdup(bind_section, section);
|
||||
bind_section->section = bstrdup0(bind_section, section);
|
||||
bind_section->is_builtin = builtin;
|
||||
bind_section->next = NULL;
|
||||
return bind_section;
|
||||
@ -1005,9 +1142,9 @@ static char *section_find_bind_for_key(struct input_ctx *ictx,
|
||||
bool builtin, char *section,
|
||||
int n, int *keys)
|
||||
{
|
||||
struct cmd_bind_section *bs = get_bind_section(ictx, builtin, section);
|
||||
const struct cmd_bind *binds = bs->cmd_binds;
|
||||
return binds ? find_bind_for_key(binds, n, keys) : NULL;
|
||||
struct cmd_bind_section *bs = get_bind_section(ictx, builtin,
|
||||
bstr0(section));
|
||||
return bs->cmd_binds ? find_bind_for_key(bs->cmd_binds, n, keys) : NULL;
|
||||
}
|
||||
|
||||
static mp_cmd_t *get_cmd_from_keys(struct input_ctx *ictx, int n, int *keys)
|
||||
@ -1032,9 +1169,7 @@ static mp_cmd_t *get_cmd_from_keys(struct input_ctx *ictx, int n, int *keys)
|
||||
talloc_free(key_buf);
|
||||
return NULL;
|
||||
}
|
||||
if (strcmp(cmd, "ignore") == 0)
|
||||
return NULL;
|
||||
ret = mp_input_parse_cmd(cmd);
|
||||
ret = mp_input_parse_cmd(bstr0(cmd));
|
||||
if (!ret) {
|
||||
char *key_buf = get_key_combo_name(keys, n);
|
||||
mp_tmsg(MSGT_INPUT, MSGL_ERR,
|
||||
@ -1176,7 +1311,7 @@ static void read_cmd_fd(struct input_ctx *ictx, struct input_fd *cmd_fd)
|
||||
char *text;
|
||||
while ((r = read_cmd(cmd_fd, &text)) >= 0) {
|
||||
ictx->got_new_events = true;
|
||||
struct mp_cmd *cmd = mp_input_parse_cmd(text);
|
||||
struct mp_cmd *cmd = mp_input_parse_cmd(bstr0(text));
|
||||
talloc_free(text);
|
||||
if (cmd)
|
||||
queue_add(&ictx->control_cmd_queue, cmd, false);
|
||||
@ -1323,7 +1458,7 @@ int mp_input_queue_cmd(struct input_ctx *ictx, mp_cmd_t *cmd)
|
||||
mp_cmd_t *mp_input_get_cmd(struct input_ctx *ictx, int time, int peek_only)
|
||||
{
|
||||
if (async_quit_request)
|
||||
return mp_input_parse_cmd("quit 1");
|
||||
return mp_input_parse_cmd(bstr0("quit 1"));
|
||||
|
||||
if (ictx->control_cmd_queue.first || ictx->key_cmd_queue.first)
|
||||
time = 0;
|
||||
@ -1358,8 +1493,8 @@ mp_cmd_t *mp_cmd_clone(mp_cmd_t *cmd)
|
||||
|
||||
ret = talloc_memdup(NULL, cmd, sizeof(mp_cmd_t));
|
||||
ret->name = talloc_strdup(ret, cmd->name);
|
||||
for (i = 0; i < MP_CMD_MAX_ARGS && cmd->args[i].type; i++) {
|
||||
if (cmd->args[i].type == MP_CMD_ARG_STRING && cmd->args[i].v.s != NULL)
|
||||
for (i = 0; i < MP_CMD_MAX_ARGS; i++) {
|
||||
if (cmd->args[i].type.type == &m_option_type_string)
|
||||
ret->args[i].v.s = talloc_strdup(ret, cmd->args[i].v.s);
|
||||
}
|
||||
|
||||
@ -1428,24 +1563,14 @@ static int get_input_from_name(char *name, int *keys)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void bind_keys(struct input_ctx *ictx, bool builtin,
|
||||
static void bind_keys(struct input_ctx *ictx, bool builtin, bstr section,
|
||||
const int keys[MP_MAX_KEY_DOWN + 1], bstr command)
|
||||
{
|
||||
int i = 0, j;
|
||||
struct cmd_bind *bind = NULL;
|
||||
struct cmd_bind_section *bind_section = NULL;
|
||||
char *section = NULL;
|
||||
|
||||
if (bstr_startswith0(command, "{")) {
|
||||
int p = bstrchr(command, '}');
|
||||
if (p != -1) {
|
||||
bstr bsection = bstr_strip(bstr_splice(command, 1, p));
|
||||
section = bstrdup0(NULL, bsection);
|
||||
command = bstr_lstrip(bstr_cut(command, p + 1));
|
||||
}
|
||||
}
|
||||
bind_section = get_bind_section(ictx, builtin, section);
|
||||
talloc_free(section);
|
||||
|
||||
if (bind_section->cmd_binds) {
|
||||
for (i = 0; bind_section->cmd_binds[i].cmd != NULL; i++) {
|
||||
@ -1496,8 +1621,21 @@ static int parse_config(struct input_ctx *ictx, bool builtin, bstr data)
|
||||
continue;
|
||||
}
|
||||
talloc_free(name);
|
||||
bind_keys(ictx, builtin, keys, command);
|
||||
|
||||
bstr section = {0};
|
||||
if (bstr_startswith0(command, "{")) {
|
||||
int p = bstrchr(command, '}');
|
||||
if (p != -1) {
|
||||
section = bstr_strip(bstr_splice(command, 1, p));
|
||||
command = bstr_lstrip(bstr_cut(command, p + 1));
|
||||
}
|
||||
}
|
||||
|
||||
bind_keys(ictx, builtin, section, keys, command);
|
||||
n_binds++;
|
||||
|
||||
// Print warnings if invalid commands are encountered.
|
||||
talloc_free(mp_input_parse_cmd(command));
|
||||
}
|
||||
|
||||
return n_binds;
|
||||
@ -1702,24 +1840,11 @@ static int print_cmd_list(m_option_t *cfg, char *optname, char *optparam)
|
||||
{
|
||||
const mp_cmd_t *cmd;
|
||||
int i, j;
|
||||
const char *type;
|
||||
|
||||
for (i = 0; (cmd = &mp_cmds[i])->name != NULL; i++) {
|
||||
printf("%-20.20s", cmd->name);
|
||||
for (j = 0; j < MP_CMD_MAX_ARGS && cmd->args[j].type; j++) {
|
||||
switch (cmd->args[j].type) {
|
||||
case MP_CMD_ARG_INT:
|
||||
type = "Integer";
|
||||
break;
|
||||
case MP_CMD_ARG_FLOAT:
|
||||
type = "Float";
|
||||
break;
|
||||
case MP_CMD_ARG_STRING:
|
||||
type = "String";
|
||||
break;
|
||||
default:
|
||||
type = "??";
|
||||
}
|
||||
for (j = 0; j < MP_CMD_MAX_ARGS && cmd->args[j].type.type; j++) {
|
||||
const char *type = cmd->args[j].type.type->name;
|
||||
if (cmd->args[j].optional)
|
||||
printf(" [%s]", type);
|
||||
else
|
||||
|
@ -20,106 +20,49 @@
|
||||
#define MPLAYER_INPUT_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "bstr.h"
|
||||
#include "m_option.h"
|
||||
|
||||
// All command IDs
|
||||
enum mp_command_type {
|
||||
MP_CMD_IGNORE,
|
||||
MP_CMD_SEEK,
|
||||
MP_CMD_AUDIO_DELAY,
|
||||
MP_CMD_QUIT,
|
||||
MP_CMD_PAUSE,
|
||||
MP_CMD_GRAB_FRAMES, // deprecated: was a no-op command for years
|
||||
MP_CMD_PLAYLIST_NEXT,
|
||||
MP_CMD_PLAYLIST_PREV,
|
||||
MP_CMD_SUB_DELAY,
|
||||
MP_CMD_OSD,
|
||||
MP_CMD_VOLUME,
|
||||
MP_CMD_MIXER_USEMASTER,
|
||||
MP_CMD_CONTRAST,
|
||||
MP_CMD_BRIGHTNESS,
|
||||
MP_CMD_HUE,
|
||||
MP_CMD_SATURATION,
|
||||
MP_CMD_FRAMEDROPPING,
|
||||
MP_CMD_TV_STEP_CHANNEL,
|
||||
MP_CMD_TV_STEP_NORM,
|
||||
MP_CMD_TV_STEP_CHANNEL_LIST,
|
||||
MP_CMD_VO_FULLSCREEN,
|
||||
MP_CMD_SUB_POS,
|
||||
MP_CMD_SCREENSHOT,
|
||||
MP_CMD_PANSCAN,
|
||||
MP_CMD_MUTE,
|
||||
MP_CMD_LOADFILE,
|
||||
MP_CMD_LOADLIST,
|
||||
MP_CMD_PLAYLIST_CLEAR,
|
||||
MP_CMD_GAMMA,
|
||||
MP_CMD_SUB_VISIBILITY,
|
||||
MP_CMD_VOBSUB_LANG, // deprecated: combined with SUB_SELECT
|
||||
MP_CMD_GET_TIME_LENGTH,
|
||||
MP_CMD_GET_PERCENT_POS,
|
||||
MP_CMD_SUB_STEP,
|
||||
MP_CMD_TV_SET_CHANNEL,
|
||||
MP_CMD_EDL_MARK,
|
||||
MP_CMD_SUB_ALIGNMENT,
|
||||
MP_CMD_TV_LAST_CHANNEL,
|
||||
MP_CMD_OSD_SHOW_TEXT,
|
||||
MP_CMD_TV_SET_FREQ,
|
||||
MP_CMD_TV_SET_NORM,
|
||||
MP_CMD_TV_SET_BRIGHTNESS,
|
||||
MP_CMD_TV_SET_CONTRAST,
|
||||
MP_CMD_TV_SET_HUE,
|
||||
MP_CMD_TV_SET_SATURATION,
|
||||
MP_CMD_GET_VO_FULLSCREEN,
|
||||
MP_CMD_GET_SUB_VISIBILITY,
|
||||
MP_CMD_SUB_FORCED_ONLY,
|
||||
MP_CMD_VO_ONTOP,
|
||||
MP_CMD_SUB_SELECT,
|
||||
MP_CMD_VO_ROOTWIN,
|
||||
MP_CMD_SWITCH_VSYNC,
|
||||
MP_CMD_SWITCH_RATIO,
|
||||
MP_CMD_FRAME_STEP,
|
||||
MP_CMD_SPEED_INCR,
|
||||
MP_CMD_SPEED_MULT,
|
||||
MP_CMD_SPEED_SET,
|
||||
MP_CMD_RUN,
|
||||
MP_CMD_SWITCH_AUDIO,
|
||||
MP_CMD_GET_TIME_POS,
|
||||
MP_CMD_SUB_LOAD,
|
||||
MP_CMD_KEYDOWN_EVENTS,
|
||||
MP_CMD_VO_BORDER,
|
||||
MP_CMD_SET_PROPERTY,
|
||||
MP_CMD_SET_PROPERTY_OSD,
|
||||
MP_CMD_SET,
|
||||
MP_CMD_GET_PROPERTY,
|
||||
MP_CMD_OSD_SHOW_PROPERTY_TEXT,
|
||||
MP_CMD_OSD_SHOW_PROGRESSION,
|
||||
MP_CMD_SEEK_CHAPTER,
|
||||
MP_CMD_GET_FILENAME,
|
||||
MP_CMD_GET_VIDEO_CODEC,
|
||||
MP_CMD_GET_VIDEO_BITRATE,
|
||||
MP_CMD_GET_VIDEO_RESOLUTION,
|
||||
MP_CMD_GET_AUDIO_CODEC,
|
||||
MP_CMD_GET_AUDIO_BITRATE,
|
||||
MP_CMD_GET_AUDIO_SAMPLES,
|
||||
MP_CMD_GET_META_TITLE,
|
||||
MP_CMD_GET_META_ARTIST,
|
||||
MP_CMD_GET_META_ALBUM,
|
||||
MP_CMD_GET_META_YEAR,
|
||||
MP_CMD_GET_META_COMMENT,
|
||||
MP_CMD_GET_META_TRACK,
|
||||
MP_CMD_GET_META_GENRE,
|
||||
MP_CMD_PRINT_TEXT,
|
||||
MP_CMD_SHOW_TEXT,
|
||||
MP_CMD_SHOW_PROGRESS,
|
||||
MP_CMD_RADIO_STEP_CHANNEL,
|
||||
MP_CMD_RADIO_SET_CHANNEL,
|
||||
MP_CMD_RADIO_SET_FREQ,
|
||||
MP_CMD_SET_MOUSE_POS,
|
||||
MP_CMD_STEP_PROPERTY,
|
||||
MP_CMD_STEP_PROPERTY_OSD,
|
||||
MP_CMD_ADD,
|
||||
MP_CMD_CYCLE,
|
||||
MP_CMD_RADIO_STEP_FREQ,
|
||||
MP_CMD_TV_STEP_FREQ,
|
||||
MP_CMD_LOOP,
|
||||
MP_CMD_BALANCE,
|
||||
MP_CMD_SUB_SCALE,
|
||||
MP_CMD_TV_START_SCAN,
|
||||
MP_CMD_SWITCH_ANGLE,
|
||||
MP_CMD_ASS_USE_MARGINS,
|
||||
MP_CMD_SWITCH_TITLE,
|
||||
MP_CMD_STOP,
|
||||
|
||||
/// DVB commands
|
||||
@ -139,11 +82,6 @@ enum mp_command_type {
|
||||
MP_CMD_VO_CMDLINE,
|
||||
};
|
||||
|
||||
// The arg types
|
||||
#define MP_CMD_ARG_INT 1
|
||||
#define MP_CMD_ARG_FLOAT 2
|
||||
#define MP_CMD_ARG_STRING 3
|
||||
|
||||
#define MP_CMD_MAX_ARGS 10
|
||||
|
||||
// Error codes for the drivers
|
||||
@ -159,6 +97,13 @@ enum mp_command_type {
|
||||
// Key FIFO was full - release events may be lost, zero button-down status
|
||||
#define MP_INPUT_RELEASE_ALL -5
|
||||
|
||||
enum mp_on_osd {
|
||||
MP_ON_OSD_NO = 0, // prefer not using OSD
|
||||
MP_ON_OSD_AUTO = 1, // use default behavior of the specific command
|
||||
MP_ON_OSD_BAR = 2, // force a bar, if applicable
|
||||
MP_ON_OSD_MSG = 4, // force a message, if applicable
|
||||
};
|
||||
|
||||
enum mp_input_section_flags {
|
||||
// If a key binding is not defined in the current section, search the
|
||||
// default section for it ("default" refers to bindings with no section
|
||||
@ -169,7 +114,7 @@ enum mp_input_section_flags {
|
||||
struct input_ctx;
|
||||
|
||||
struct mp_cmd_arg {
|
||||
int type;
|
||||
struct m_option type;
|
||||
bool optional;
|
||||
union {
|
||||
int i;
|
||||
@ -184,6 +129,7 @@ typedef struct mp_cmd {
|
||||
struct mp_cmd_arg args[MP_CMD_MAX_ARGS];
|
||||
int nargs;
|
||||
int pausing;
|
||||
enum mp_on_osd on_osd;
|
||||
struct mp_cmd *queue_next;
|
||||
} mp_cmd_t;
|
||||
|
||||
@ -235,7 +181,7 @@ struct mp_cmd *mp_input_get_cmd(struct input_ctx *ictx, int time,
|
||||
int peek_only);
|
||||
|
||||
/* Parse text and return corresponding struct mp_cmd. */
|
||||
struct mp_cmd *mp_input_parse_cmd(char *str);
|
||||
struct mp_cmd *mp_input_parse_cmd(bstr str);
|
||||
|
||||
// After getting a command from mp_input_get_cmd you need to free it using this
|
||||
// function
|
||||
|
@ -509,5 +509,5 @@ void vo_mouse_movement(struct vo *vo, int posx, int posy)
|
||||
if (!enable_mouse_movements)
|
||||
return;
|
||||
snprintf(cmd_str, sizeof(cmd_str), "set_mouse_pos %i %i", posx, posy);
|
||||
mp_input_queue_cmd(vo->input_ctx, mp_input_parse_cmd(cmd_str));
|
||||
mp_input_queue_cmd(vo->input_ctx, mp_input_parse_cmd(bstr0(cmd_str)));
|
||||
}
|
||||
|
@ -365,8 +365,8 @@ int m_config_register_options(struct m_config *config,
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct m_config_option *m_config_get_co(const struct m_config *config,
|
||||
struct bstr name)
|
||||
struct m_config_option *m_config_get_co(const struct m_config *config,
|
||||
struct bstr name)
|
||||
{
|
||||
struct m_config_option *co;
|
||||
|
||||
|
@ -152,6 +152,9 @@ int m_config_parse_suboptions(struct m_config *config, char *name,
|
||||
const struct m_option *m_config_get_option(const struct m_config *config,
|
||||
struct bstr name);
|
||||
|
||||
struct m_config_option *m_config_get_co(const struct m_config *config,
|
||||
struct bstr name);
|
||||
|
||||
/* Print a list of all registered options.
|
||||
* \param config The config object.
|
||||
*/
|
||||
|
285
m_option.c
285
m_option.c
@ -26,15 +26,18 @@
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <limits.h>
|
||||
#include <inttypes.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <libavutil/common.h>
|
||||
#include <libavutil/avstring.h>
|
||||
|
||||
#include "talloc.h"
|
||||
#include "m_option.h"
|
||||
#include "mp_msg.h"
|
||||
#include "stream/url.h"
|
||||
#include "libavutil/avstring.h"
|
||||
|
||||
char *m_option_strerror(int code)
|
||||
{
|
||||
@ -87,6 +90,14 @@ static void copy_opt(const m_option_t *opt, void *dst, const void *src)
|
||||
|
||||
#define VAL(x) (*(int *)(x))
|
||||
|
||||
static int clamp_flag(const m_option_t *opt, void *val)
|
||||
{
|
||||
if (VAL(val) == opt->min || VAL(val) == opt->max)
|
||||
return 0;
|
||||
VAL(val) = opt->min;
|
||||
return M_OPT_OUT_OF_RANGE;
|
||||
}
|
||||
|
||||
static int parse_flag(const m_option_t *opt, struct bstr name,
|
||||
struct bstr param, void *dst)
|
||||
{
|
||||
@ -120,6 +131,15 @@ static char *print_flag(const m_option_t *opt, const void *val)
|
||||
return talloc_strdup(NULL, "yes");
|
||||
}
|
||||
|
||||
static void add_flag(const m_option_t *opt, void *val, double add, bool wrap)
|
||||
{
|
||||
if (fabs(add) < 0.5)
|
||||
return;
|
||||
bool state = VAL(val) != opt->min;
|
||||
state = wrap ? !state : add > 0;
|
||||
VAL(val) = state ? opt->max : opt->min;
|
||||
}
|
||||
|
||||
const m_option_type_t m_option_type_flag = {
|
||||
// need yes or no in config files
|
||||
.name = "Flag",
|
||||
@ -128,10 +148,30 @@ const m_option_type_t m_option_type_flag = {
|
||||
.parse = parse_flag,
|
||||
.print = print_flag,
|
||||
.copy = copy_opt,
|
||||
.add = add_flag,
|
||||
.clamp = clamp_flag,
|
||||
};
|
||||
|
||||
// Integer
|
||||
|
||||
#undef VAL
|
||||
|
||||
static int clamp_longlong(const m_option_t *opt, void *val)
|
||||
{
|
||||
long long v = *(long long *)val;
|
||||
int r = 0;
|
||||
if ((opt->flags & M_OPT_MAX) && (v > opt->max)) {
|
||||
v = opt->max;
|
||||
r = M_OPT_OUT_OF_RANGE;
|
||||
}
|
||||
if ((opt->flags & M_OPT_MIN) && (v < opt->min)) {
|
||||
v = opt->min;
|
||||
r = M_OPT_OUT_OF_RANGE;
|
||||
}
|
||||
*(long long *)val = v;
|
||||
return r;
|
||||
}
|
||||
|
||||
static int parse_longlong(const m_option_t *opt, struct bstr name,
|
||||
struct bstr param, void *dst)
|
||||
{
|
||||
@ -169,6 +209,22 @@ static int parse_longlong(const m_option_t *opt, struct bstr name,
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int clamp_int(const m_option_t *opt, void *val)
|
||||
{
|
||||
long long tmp = *(int *)val;
|
||||
int r = clamp_longlong(opt, &tmp);
|
||||
*(int *)val = tmp;
|
||||
return r;
|
||||
}
|
||||
|
||||
static int clamp_int64(const m_option_t *opt, void *val)
|
||||
{
|
||||
long long tmp = *(int64_t *)val;
|
||||
int r = clamp_longlong(opt, &tmp);
|
||||
*(int64_t *)val = tmp;
|
||||
return r;
|
||||
}
|
||||
|
||||
static int parse_int(const m_option_t *opt, struct bstr name,
|
||||
struct bstr param, void *dst)
|
||||
{
|
||||
@ -189,12 +245,39 @@ static int parse_int64(const m_option_t *opt, struct bstr name,
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
static char *print_int(const m_option_t *opt, const void *val)
|
||||
{
|
||||
if (opt->type->size == sizeof(int64_t))
|
||||
return talloc_asprintf(NULL, "%"PRId64, *(const int64_t *)val);
|
||||
return talloc_asprintf(NULL, "%d", VAL(val));
|
||||
return talloc_asprintf(NULL, "%d", *(const int *)val);
|
||||
}
|
||||
|
||||
static void add_int64(const m_option_t *opt, void *val, double add, bool wrap)
|
||||
{
|
||||
int64_t v = *(int64_t *)val;
|
||||
|
||||
v = v + add;
|
||||
|
||||
bool is64 = opt->type->size == sizeof(int64_t);
|
||||
int64_t nmin = is64 ? INT64_MIN : INT_MIN;
|
||||
int64_t nmax = is64 ? INT64_MAX : INT_MAX;
|
||||
|
||||
int64_t min = (opt->flags & M_OPT_MIN) ? opt->min : nmin;
|
||||
int64_t max = (opt->flags & M_OPT_MAX) ? opt->max : nmax;
|
||||
|
||||
if (v < min)
|
||||
v = wrap ? max : min;
|
||||
if (v > max)
|
||||
v = wrap ? min : max;
|
||||
|
||||
*(int64_t *)val = v;
|
||||
}
|
||||
|
||||
static void add_int(const m_option_t *opt, void *val, double add, bool wrap)
|
||||
{
|
||||
int64_t tmp = *(int *)val;
|
||||
add_int64(opt, &tmp, add, wrap);
|
||||
*(int *)val = tmp;
|
||||
}
|
||||
|
||||
const m_option_type_t m_option_type_int = {
|
||||
@ -203,6 +286,8 @@ const m_option_type_t m_option_type_int = {
|
||||
.parse = parse_int,
|
||||
.print = print_int,
|
||||
.copy = copy_opt,
|
||||
.add = add_int,
|
||||
.clamp = clamp_int,
|
||||
};
|
||||
|
||||
const m_option_type_t m_option_type_int64 = {
|
||||
@ -211,6 +296,8 @@ const m_option_type_t m_option_type_int64 = {
|
||||
.parse = parse_int64,
|
||||
.print = print_int,
|
||||
.copy = copy_opt,
|
||||
.add = add_int64,
|
||||
.clamp = clamp_int64,
|
||||
};
|
||||
|
||||
static int parse_intpair(const struct m_option *opt, struct bstr name,
|
||||
@ -256,6 +343,21 @@ const struct m_option_type m_option_type_intpair = {
|
||||
.copy = copy_opt,
|
||||
};
|
||||
|
||||
static int clamp_choice(const m_option_t *opt, void *val)
|
||||
{
|
||||
int v = *(int *)val;
|
||||
if ((opt->flags & M_OPT_MIN) && (opt->flags & M_OPT_MAX)) {
|
||||
if (v >= opt->min && v <= opt->max)
|
||||
return 0;
|
||||
}
|
||||
;
|
||||
for (struct m_opt_choice_alternatives *alt = opt->priv; alt->name; alt++) {
|
||||
if (alt->value == v)
|
||||
return 0;
|
||||
}
|
||||
return M_OPT_INVALID;
|
||||
}
|
||||
|
||||
static int parse_choice(const struct m_option *opt, struct bstr name,
|
||||
struct bstr param, void *dst)
|
||||
{
|
||||
@ -303,12 +405,75 @@ static char *print_choice(const m_option_t *opt, const void *val)
|
||||
abort();
|
||||
}
|
||||
|
||||
static void choice_get_min_max(const struct m_option *opt, int *min, int *max)
|
||||
{
|
||||
assert(opt->type == &m_option_type_choice);
|
||||
*min = INT_MAX;
|
||||
*max = INT_MIN;
|
||||
for (struct m_opt_choice_alternatives *alt = opt->priv; alt->name; alt++) {
|
||||
*min = FFMIN(*min, alt->value);
|
||||
*max = FFMAX(*max, alt->value);
|
||||
}
|
||||
if ((opt->flags & M_OPT_MIN) && (opt->flags & M_OPT_MAX)) {
|
||||
*min = FFMIN(*min, opt->min);
|
||||
*max = FFMAX(*max, opt->max);
|
||||
}
|
||||
}
|
||||
|
||||
static void check_choice(int dir, int val, bool *found, int *best, int choice)
|
||||
{
|
||||
if ((dir == -1 && (!(*found) || choice > (*best)) && choice < val) ||
|
||||
(dir == +1 && (!(*found) || choice < (*best)) && choice > val))
|
||||
{
|
||||
*found = true;
|
||||
*best = choice;
|
||||
}
|
||||
}
|
||||
|
||||
static void add_choice(const m_option_t *opt, void *val, double add, bool wrap)
|
||||
{
|
||||
assert(opt->type == &m_option_type_choice);
|
||||
int dir = add > 0 ? +1 : -1;
|
||||
bool found = false;
|
||||
int ival = *(int *)val;
|
||||
int best = 0; // init. value unused
|
||||
|
||||
if (fabs(add) < 0.5)
|
||||
return;
|
||||
|
||||
if ((opt->flags & M_OPT_MIN) && (opt->flags & M_OPT_MAX)) {
|
||||
int newval = ival + add;
|
||||
if (ival >= opt->min && ival <= opt->max &&
|
||||
newval >= opt->min && newval <= opt->max)
|
||||
{
|
||||
found = true;
|
||||
best = newval;
|
||||
} else {
|
||||
check_choice(dir, ival, &found, &best, opt->min);
|
||||
check_choice(dir, ival, &found, &best, opt->max);
|
||||
}
|
||||
}
|
||||
|
||||
for (struct m_opt_choice_alternatives *alt = opt->priv; alt->name; alt++)
|
||||
check_choice(dir, ival, &found, &best, alt->value);
|
||||
|
||||
if (!found) {
|
||||
int min, max;
|
||||
choice_get_min_max(opt, &min, &max);
|
||||
best = (dir == -1) ^ wrap ? min : max;
|
||||
}
|
||||
|
||||
*(int *)val = best;
|
||||
}
|
||||
|
||||
const struct m_option_type m_option_type_choice = {
|
||||
.name = "String", // same as arbitrary strings in option list for now
|
||||
.size = sizeof(int),
|
||||
.parse = parse_choice,
|
||||
.print = print_choice,
|
||||
.copy = copy_opt,
|
||||
.add = add_choice,
|
||||
.clamp = clamp_choice,
|
||||
};
|
||||
|
||||
// Float
|
||||
@ -316,6 +481,22 @@ const struct m_option_type m_option_type_choice = {
|
||||
#undef VAL
|
||||
#define VAL(x) (*(double *)(x))
|
||||
|
||||
static int clamp_double(const m_option_t *opt, void *val)
|
||||
{
|
||||
double v = VAL(val);
|
||||
int r = 0;
|
||||
if ((opt->flags & M_OPT_MAX) && (v > opt->max)) {
|
||||
v = opt->max;
|
||||
r = M_OPT_OUT_OF_RANGE;
|
||||
}
|
||||
if ((opt->flags & M_OPT_MIN) && (v < opt->min)) {
|
||||
v = opt->min;
|
||||
r = M_OPT_OUT_OF_RANGE;
|
||||
}
|
||||
VAL(val) = v;
|
||||
return r;
|
||||
}
|
||||
|
||||
static int parse_double(const m_option_t *opt, struct bstr name,
|
||||
struct bstr param, void *dst)
|
||||
{
|
||||
@ -374,22 +555,53 @@ static int parse_double(const m_option_t *opt, struct bstr name,
|
||||
|
||||
static char *print_double(const m_option_t *opt, const void *val)
|
||||
{
|
||||
opt = NULL;
|
||||
return talloc_asprintf(NULL, "%f", VAL(val));
|
||||
}
|
||||
|
||||
static char *print_double_f2(const m_option_t *opt, const void *val)
|
||||
{
|
||||
return talloc_asprintf(NULL, "%.2f", VAL(val));
|
||||
}
|
||||
|
||||
static void add_double(const m_option_t *opt, void *val, double add, bool wrap)
|
||||
{
|
||||
double v = VAL(val);
|
||||
|
||||
v = v + add;
|
||||
|
||||
double min = (opt->flags & M_OPT_MIN) ? opt->min : -INFINITY;
|
||||
double max = (opt->flags & M_OPT_MAX) ? opt->max : +INFINITY;
|
||||
|
||||
if (v < min)
|
||||
v = wrap ? max : min;
|
||||
if (v > max)
|
||||
v = wrap ? min : max;
|
||||
|
||||
VAL(val) = v;
|
||||
}
|
||||
|
||||
const m_option_type_t m_option_type_double = {
|
||||
// double precision float or ratio (numerator[:/]denominator)
|
||||
.name = "Double",
|
||||
.size = sizeof(double),
|
||||
.parse = parse_double,
|
||||
.print = print_double,
|
||||
.pretty_print = print_double_f2,
|
||||
.copy = copy_opt,
|
||||
.clamp = clamp_double,
|
||||
};
|
||||
|
||||
#undef VAL
|
||||
#define VAL(x) (*(float *)(x))
|
||||
|
||||
static int clamp_float(const m_option_t *opt, void *val)
|
||||
{
|
||||
double tmp = VAL(val);
|
||||
int r = clamp_double(opt, &tmp);
|
||||
VAL(val) = tmp;
|
||||
return r;
|
||||
}
|
||||
|
||||
static int parse_float(const m_option_t *opt, struct bstr name,
|
||||
struct bstr param, void *dst)
|
||||
{
|
||||
@ -402,53 +614,49 @@ static int parse_float(const m_option_t *opt, struct bstr name,
|
||||
|
||||
static char *print_float(const m_option_t *opt, const void *val)
|
||||
{
|
||||
opt = NULL;
|
||||
return talloc_asprintf(NULL, "%f", VAL(val));
|
||||
}
|
||||
|
||||
static char *print_float_f2(const m_option_t *opt, const void *val)
|
||||
{
|
||||
return talloc_asprintf(NULL, "%.2f", VAL(val));
|
||||
}
|
||||
|
||||
static void add_float(const m_option_t *opt, void *val, double add, bool wrap)
|
||||
{
|
||||
double tmp = VAL(val);
|
||||
add_double(opt, &tmp, add, wrap);
|
||||
VAL(val) = tmp;
|
||||
}
|
||||
|
||||
const m_option_type_t m_option_type_float = {
|
||||
// floating point number or ratio (numerator[:/]denominator)
|
||||
.name = "Float",
|
||||
.size = sizeof(float),
|
||||
.parse = parse_float,
|
||||
.print = print_float,
|
||||
.pretty_print = print_float_f2,
|
||||
.copy = copy_opt,
|
||||
.add = add_float,
|
||||
.clamp = clamp_float,
|
||||
};
|
||||
|
||||
///////////// Position
|
||||
#undef VAL
|
||||
#define VAL(x) (*(off_t *)(x))
|
||||
|
||||
static int parse_position(const m_option_t *opt, struct bstr name,
|
||||
struct bstr param, void *dst)
|
||||
{
|
||||
long long tmp;
|
||||
int r = parse_longlong(opt, name, param, &tmp);
|
||||
if (r >= 0 && dst)
|
||||
*(off_t *)dst = tmp;
|
||||
return r;
|
||||
}
|
||||
|
||||
static char *print_position(const m_option_t *opt, const void *val)
|
||||
{
|
||||
return talloc_asprintf(NULL, "%"PRId64, (int64_t)VAL(val));
|
||||
}
|
||||
|
||||
const m_option_type_t m_option_type_position = {
|
||||
// Integer (off_t)
|
||||
.name = "Position",
|
||||
.size = sizeof(off_t),
|
||||
.parse = parse_position,
|
||||
.print = print_position,
|
||||
.copy = copy_opt,
|
||||
};
|
||||
|
||||
|
||||
///////////// String
|
||||
|
||||
#undef VAL
|
||||
#define VAL(x) (*(char **)(x))
|
||||
|
||||
static int clamp_str(const m_option_t *opt, void *val)
|
||||
{
|
||||
char *v = VAL(val);
|
||||
int len = v ? strlen(v) : 0;
|
||||
if ((opt->flags & M_OPT_MIN) && (len < opt->min))
|
||||
return M_OPT_OUT_OF_RANGE;
|
||||
if ((opt->flags & M_OPT_MAX) && (len > opt->max))
|
||||
return M_OPT_OUT_OF_RANGE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_str(const m_option_t *opt, struct bstr name,
|
||||
struct bstr param, void *dst)
|
||||
{
|
||||
@ -507,6 +715,7 @@ const m_option_type_t m_option_type_string = {
|
||||
.print = print_str,
|
||||
.copy = copy_str,
|
||||
.free = free_str,
|
||||
.clamp = clamp_str,
|
||||
};
|
||||
|
||||
//////////// String list
|
||||
@ -1024,12 +1233,20 @@ static int parse_time(const m_option_t *opt, struct bstr name,
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char *pretty_print_time(const m_option_t *opt, const void *val)
|
||||
{
|
||||
return mp_format_time(*(double *)val, false);
|
||||
}
|
||||
|
||||
const m_option_type_t m_option_type_time = {
|
||||
.name = "Time",
|
||||
.size = sizeof(double),
|
||||
.parse = parse_time,
|
||||
.print = print_double,
|
||||
.pretty_print = pretty_print_time,
|
||||
.copy = copy_opt,
|
||||
.add = add_double,
|
||||
.clamp = clamp_double,
|
||||
};
|
||||
|
||||
|
||||
|
39
m_option.h
39
m_option.h
@ -43,7 +43,6 @@ extern const m_option_type_t m_option_type_float;
|
||||
extern const m_option_type_t m_option_type_double;
|
||||
extern const m_option_type_t m_option_type_string;
|
||||
extern const m_option_type_t m_option_type_string_list;
|
||||
extern const m_option_type_t m_option_type_position;
|
||||
extern const m_option_type_t m_option_type_time;
|
||||
extern const m_option_type_t m_option_type_time_size;
|
||||
extern const m_option_type_t m_option_type_choice;
|
||||
@ -167,7 +166,6 @@ struct m_sub_options {
|
||||
#define CONF_TYPE_PRINT_FUNC (&m_option_type_print_func)
|
||||
#define CONF_TYPE_SUBCONFIG (&m_option_type_subconfig)
|
||||
#define CONF_TYPE_STRING_LIST (&m_option_type_string_list)
|
||||
#define CONF_TYPE_POSITION (&m_option_type_position)
|
||||
#define CONF_TYPE_IMGFMT (&m_option_type_imgfmt)
|
||||
#define CONF_TYPE_AFMT (&m_option_type_afmt)
|
||||
#define CONF_TYPE_SPAN (&m_option_type_span)
|
||||
@ -230,6 +228,11 @@ struct m_option_type {
|
||||
*/
|
||||
char *(*print)(const m_option_t *opt, const void *val);
|
||||
|
||||
// Print the value in a human readable form. Unlike print(), it doesn't
|
||||
// necessarily return the exact value, and is generally not parseable with
|
||||
// parse().
|
||||
char *(*pretty_print)(const m_option_t *opt, const void *val);
|
||||
|
||||
// Copy data between two locations. Deep copy if the data has pointers.
|
||||
/** \param opt The option to copy.
|
||||
* \param dst Pointer to the destination memory.
|
||||
@ -243,6 +246,18 @@ struct m_option_type {
|
||||
* set to NULL.
|
||||
*/
|
||||
void (*free)(void *dst);
|
||||
|
||||
// Add the value add to the value in val. For types that are not numeric,
|
||||
// add gives merely the direction. The wrap parameter determines whether
|
||||
// the value is clipped, or wraps around to the opposite max/min.
|
||||
void (*add)(const m_option_t *opt, void *val, double add, bool wrap);
|
||||
|
||||
// Clamp the value in val to the option's valid value range.
|
||||
// Return values:
|
||||
// M_OPT_OUT_OF_RANGE: val was invalid, and modified (clamped) to be valid
|
||||
// M_OPT_INVALID: val was invalid, and can't be made valid
|
||||
// 0: val was already valid and is unchanged
|
||||
int (*clamp)(const m_option_t *opt, void *val);
|
||||
};
|
||||
|
||||
// Option description
|
||||
@ -408,12 +423,6 @@ char *m_option_strerror(int code);
|
||||
*/
|
||||
const m_option_t *m_option_list_find(const m_option_t *list, const char *name);
|
||||
|
||||
static inline void *m_option_get_ptr(const struct m_option *opt,
|
||||
void *optstruct)
|
||||
{
|
||||
return opt->new ? (char *) optstruct + opt->offset : opt->p;
|
||||
}
|
||||
|
||||
// Helper to parse options, see \ref m_option_type::parse.
|
||||
static inline int m_option_parse(const m_option_t *opt, struct bstr name,
|
||||
struct bstr param, void *dst)
|
||||
@ -430,6 +439,15 @@ static inline char *m_option_print(const m_option_t *opt, const void *val_ptr)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline char *m_option_pretty_print(const m_option_t *opt,
|
||||
const void *val_ptr)
|
||||
{
|
||||
if (opt->type->pretty_print)
|
||||
return opt->type->pretty_print(opt, val_ptr);
|
||||
else
|
||||
return m_option_print(opt, val_ptr);
|
||||
}
|
||||
|
||||
// Helper around \ref m_option_type::copy.
|
||||
static inline void m_option_copy(const m_option_t *opt, void *dst,
|
||||
const void *src)
|
||||
@ -483,12 +501,13 @@ static inline void m_option_free(const m_option_t *opt, void *dst)
|
||||
#define OPT_SETTINGSLIST(optname, varname, flags, objlist) OPT_GENERAL(optname, varname, flags, .type = &m_option_type_obj_settings_list, .priv = objlist)
|
||||
#define OPT_AUDIOFORMAT(...) OPT_GENERAL(__VA_ARGS__, .type = &m_option_type_afmt)
|
||||
#define OPT_HELPER_REMOVEPAREN(...) __VA_ARGS__
|
||||
#define M_CHOICES(choices) .priv = (void *)&(const struct m_opt_choice_alternatives[]){OPT_HELPER_REMOVEPAREN choices, {NULL}}
|
||||
#define OPT_CHOICE(...) OPT_CHOICE_(__VA_ARGS__, .type = &m_option_type_choice)
|
||||
#define OPT_CHOICE_(optname, varname, flags, choices, ...) OPT_GENERAL(optname, varname, flags, .priv = (void *)&(const struct m_opt_choice_alternatives[]){OPT_HELPER_REMOVEPAREN choices, {NULL}}, __VA_ARGS__)
|
||||
#define OPT_CHOICE_(optname, varname, flags, choices, ...) OPT_GENERAL(optname, varname, flags, M_CHOICES(choices), __VA_ARGS__)
|
||||
// Union of choices and an int range. The choice values can be included in the
|
||||
// int range, or be completely separate - both works.
|
||||
#define OPT_CHOICE_OR_INT(...) OPT_CHOICE_OR_INT_(__VA_ARGS__, .type = &m_option_type_choice)
|
||||
#define OPT_CHOICE_OR_INT_(optname, varname, flags, minval, maxval, choices, ...) OPT_GENERAL(optname, varname, (flags) | CONF_RANGE, .min = minval, .max = maxval, .priv = (void *)&(const struct m_opt_choice_alternatives[]){OPT_HELPER_REMOVEPAREN choices, {NULL}}, __VA_ARGS__)
|
||||
#define OPT_CHOICE_OR_INT_(optname, varname, flags, minval, maxval, choices, ...) OPT_GENERAL(optname, varname, (flags) | CONF_RANGE, .min = minval, .max = maxval, M_CHOICES(choices), __VA_ARGS__)
|
||||
#define OPT_TIME(...) OPT_GENERAL(__VA_ARGS__, .type = &m_option_type_time)
|
||||
|
||||
#define OPT_TRACKCHOICE(name, var) OPT_CHOICE_OR_INT(name, var, 0, 0, 8190, ({"no", -2}, {"auto", -1}))
|
||||
|
490
m_property.c
490
m_property.c
@ -25,7 +25,9 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <libavutil/common.h>
|
||||
|
||||
#include "talloc.h"
|
||||
#include "m_option.h"
|
||||
@ -33,173 +35,254 @@
|
||||
#include "mp_msg.h"
|
||||
#include "mpcommon.h"
|
||||
|
||||
const struct m_option_type m_option_type_dummy = {
|
||||
.name = "Unknown",
|
||||
};
|
||||
|
||||
struct legacy_prop {
|
||||
const char *old, *new;
|
||||
};
|
||||
static const struct legacy_prop legacy_props[] = {
|
||||
{"switch_video", "video"},
|
||||
{"switch_audio", "audio"},
|
||||
{"switch_program", "program"},
|
||||
{"framedropping", "framedrop"},
|
||||
{"osdlevel", "osd-level"},
|
||||
{0}
|
||||
};
|
||||
|
||||
static bool translate_legacy_property(const char *name, char *buffer,
|
||||
size_t buffer_size)
|
||||
{
|
||||
if (strlen(name) + 1 > buffer_size)
|
||||
return false;
|
||||
|
||||
const char *old_name = name;
|
||||
|
||||
for (int n = 0; legacy_props[n].new; n++) {
|
||||
if (strcmp(name, legacy_props[n].old) == 0) {
|
||||
name = legacy_props[n].new;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
snprintf(buffer, buffer_size, "%s", name);
|
||||
|
||||
// Old names used "_" instead of "-"
|
||||
for (int n = 0; buffer[n]; n++) {
|
||||
if (buffer[n] == '_')
|
||||
buffer[n] = '-';
|
||||
}
|
||||
|
||||
if (strcmp(old_name, buffer) != 0) {
|
||||
mp_msg(MSGT_CPLAYER, MSGL_V, "Warning: property '%s' is deprecated, "
|
||||
"replaced with '%s'. Fix your input.conf!\n", old_name, buffer);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int do_action(const m_option_t *prop_list, const char *name,
|
||||
int action, void *arg, void *ctx)
|
||||
{
|
||||
const char *sep;
|
||||
const m_option_t *prop;
|
||||
m_property_action_t ka;
|
||||
int r;
|
||||
if ((sep = strchr(name, '/')) && sep[1]) {
|
||||
int len = sep - name;
|
||||
char base[len + 1];
|
||||
memcpy(base, name, len);
|
||||
base[len] = 0;
|
||||
prop = m_option_list_find(prop_list, base);
|
||||
ka.key = sep + 1;
|
||||
ka.action = action;
|
||||
ka.arg = arg;
|
||||
struct m_property_action_arg ka = {
|
||||
.key = sep + 1,
|
||||
.action = action,
|
||||
.arg = arg,
|
||||
};
|
||||
action = M_PROPERTY_KEY_ACTION;
|
||||
arg = &ka;
|
||||
} else
|
||||
prop = m_option_list_find(prop_list, name);
|
||||
if (!prop)
|
||||
return M_PROPERTY_UNKNOWN;
|
||||
r = ((m_property_ctrl_f)prop->p)(prop, action, arg, ctx);
|
||||
if (action == M_PROPERTY_GET_TYPE && r < 0) {
|
||||
if (!arg)
|
||||
return M_PROPERTY_ERROR;
|
||||
*(const m_option_t **)arg = prop;
|
||||
int (*control)(const m_option_t*, int, void*, void*) = prop->p;
|
||||
int r = control(prop, action, arg, ctx);
|
||||
if (action == M_PROPERTY_GET_TYPE && r < 0 &&
|
||||
prop->type != &m_option_type_dummy)
|
||||
{
|
||||
*(struct m_option *)arg = *prop;
|
||||
return M_PROPERTY_OK;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
int m_property_do(const m_option_t *prop_list, const char *name,
|
||||
int m_property_do(const m_option_t *prop_list, const char *in_name,
|
||||
int action, void *arg, void *ctx)
|
||||
{
|
||||
const m_option_t *opt;
|
||||
union m_option_value val = {0};
|
||||
int r;
|
||||
|
||||
char name[64];
|
||||
if (!translate_legacy_property(in_name, name, sizeof(name)))
|
||||
return M_PROPERTY_UNKNOWN;
|
||||
|
||||
struct m_option opt = {0};
|
||||
r = do_action(prop_list, name, M_PROPERTY_GET_TYPE, &opt, ctx);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
assert(opt.type);
|
||||
|
||||
switch (action) {
|
||||
case M_PROPERTY_PRINT:
|
||||
case M_PROPERTY_PRINT: {
|
||||
if ((r = do_action(prop_list, name, M_PROPERTY_PRINT, arg, ctx)) >= 0)
|
||||
return r;
|
||||
// fallback on the default print for this type
|
||||
case M_PROPERTY_TO_STRING:
|
||||
if ((r = do_action(prop_list, name, M_PROPERTY_TO_STRING, arg, ctx)) !=
|
||||
M_PROPERTY_NOT_IMPLEMENTED)
|
||||
return r;
|
||||
// fallback on the options API. Get the type, value and print.
|
||||
if ((r =
|
||||
do_action(prop_list, name, M_PROPERTY_GET_TYPE, &opt, ctx)) <= 0)
|
||||
return r;
|
||||
// Fallback to m_option
|
||||
if ((r = do_action(prop_list, name, M_PROPERTY_GET, &val, ctx)) <= 0)
|
||||
return r;
|
||||
if (!arg)
|
||||
return M_PROPERTY_ERROR;
|
||||
char *str = m_option_print(opt, &val);
|
||||
char *str = m_option_pretty_print(&opt, &val);
|
||||
m_option_free(&opt, &val);
|
||||
*(char **)arg = str;
|
||||
return str != NULL;
|
||||
case M_PROPERTY_PARSE:
|
||||
// try the property own parsing func
|
||||
if ((r = do_action(prop_list, name, M_PROPERTY_PARSE, arg, ctx)) !=
|
||||
M_PROPERTY_NOT_IMPLEMENTED)
|
||||
}
|
||||
case M_PROPERTY_GET_STRING: {
|
||||
if ((r = do_action(prop_list, name, M_PROPERTY_GET, &val, ctx)) <= 0)
|
||||
return r;
|
||||
// fallback on the options API, get the type and parse.
|
||||
if ((r =
|
||||
do_action(prop_list, name, M_PROPERTY_GET_TYPE, &opt, ctx)) <= 0)
|
||||
return r;
|
||||
if (!arg)
|
||||
char *str = m_option_print(&opt, &val);
|
||||
m_option_free(&opt, &val);
|
||||
*(char **)arg = str;
|
||||
return str != NULL;
|
||||
}
|
||||
case M_PROPERTY_SET_STRING: {
|
||||
// (reject 0 return value: success, but empty string with flag)
|
||||
if (m_option_parse(&opt, bstr0(name), bstr0(arg), &val) <= 0)
|
||||
return M_PROPERTY_ERROR;
|
||||
if ((r = m_option_parse(opt, bstr0(opt->name), bstr0(arg), &val)) <= 0)
|
||||
return r;
|
||||
r = do_action(prop_list, name, M_PROPERTY_SET, &val, ctx);
|
||||
m_option_free(opt, &val);
|
||||
m_option_free(&opt, &val);
|
||||
return r;
|
||||
}
|
||||
return do_action(prop_list, name, action, arg, ctx);
|
||||
case M_PROPERTY_SWITCH: {
|
||||
struct m_property_switch_arg *sarg = arg;
|
||||
if ((r = do_action(prop_list, name, M_PROPERTY_SWITCH, arg, ctx)) !=
|
||||
M_PROPERTY_NOT_IMPLEMENTED)
|
||||
return r;
|
||||
// Fallback to m_option
|
||||
if (!opt.type->add)
|
||||
return M_PROPERTY_NOT_IMPLEMENTED;
|
||||
if ((r = do_action(prop_list, name, M_PROPERTY_GET, &val, ctx)) <= 0)
|
||||
return r;
|
||||
opt.type->add(&opt, &val, sarg->inc, sarg->wrap);
|
||||
r = do_action(prop_list, name, M_PROPERTY_SET, &val, ctx);
|
||||
m_option_free(&opt, &val);
|
||||
return r;
|
||||
}
|
||||
case M_PROPERTY_SET: {
|
||||
if (!opt.type->clamp) {
|
||||
mp_msg(MSGT_CPLAYER, MSGL_WARN, "Property '%s' without clamp().\n",
|
||||
name);
|
||||
} else {
|
||||
m_option_copy(&opt, &val, arg);
|
||||
r = opt.type->clamp(&opt, arg);
|
||||
m_option_free(&opt, &val);
|
||||
if (r != 0) {
|
||||
mp_msg(MSGT_CPLAYER, MSGL_ERR,
|
||||
"Property '%s': invalid value.\n", name);
|
||||
return M_PROPERTY_ERROR;
|
||||
}
|
||||
}
|
||||
return do_action(prop_list, name, M_PROPERTY_SET, arg, ctx);
|
||||
}
|
||||
default:
|
||||
return do_action(prop_list, name, action, arg, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
char *m_properties_expand_string(const m_option_t *prop_list, char *str,
|
||||
static int m_property_do_bstr(const m_option_t *prop_list, bstr name,
|
||||
int action, void *arg, void *ctx)
|
||||
{
|
||||
char name0[64];
|
||||
if (name.len >= sizeof(name0))
|
||||
return M_PROPERTY_UNKNOWN;
|
||||
snprintf(name0, sizeof(name0), "%.*s", BSTR_P(name));
|
||||
return m_property_do(prop_list, name0, action, arg, ctx);
|
||||
}
|
||||
|
||||
static void append_str(char **s, int *len, bstr append)
|
||||
{
|
||||
MP_TARRAY_GROW(NULL, *s, *len + append.len);
|
||||
memcpy(*s + *len, append.start, append.len);
|
||||
*len = *len + append.len;
|
||||
}
|
||||
|
||||
char *m_properties_expand_string(const m_option_t *prop_list, char *str0,
|
||||
void *ctx)
|
||||
{
|
||||
int l, fr = 0, pos = 0, size = strlen(str) + 512;
|
||||
char *p = NULL, *e, *ret = malloc(size), num_val;
|
||||
int skip = 0, lvl = 0, skip_lvl = 0;
|
||||
char *ret = NULL;
|
||||
int ret_len = 0;
|
||||
bool skip = false;
|
||||
int level = 0, skip_level = 0;
|
||||
bstr str = bstr0(str0);
|
||||
|
||||
while (str.len) {
|
||||
if (level > 0 && bstr_eatstart0(&str, "}")) {
|
||||
if (skip && level <= skip_level)
|
||||
skip = false;
|
||||
level--;
|
||||
} else if (bstr_startswith0(str, "${") && bstr_find0(str, "}") >= 0) {
|
||||
str = bstr_cut(str, 2);
|
||||
level++;
|
||||
|
||||
// Assume ":" and "}" can't be part of the property name
|
||||
// => if ":" comes before "}", it must be for the fallback
|
||||
int term_pos = bstrcspn(str, ":}");
|
||||
bstr name = bstr_splice(str, 0, term_pos < 0 ? str.len : term_pos);
|
||||
str = bstr_cut(str, term_pos);
|
||||
bool have_fallback = bstr_eatstart0(&str, ":");
|
||||
|
||||
while (str[0]) {
|
||||
if (str[0] == '\\') {
|
||||
int sl = 1;
|
||||
switch (str[1]) {
|
||||
case 'e':
|
||||
p = "\x1b", l = 1; break;
|
||||
case 'n':
|
||||
p = "\n", l = 1; break;
|
||||
case 'r':
|
||||
p = "\r", l = 1; break;
|
||||
case 't':
|
||||
p = "\t", l = 1; break;
|
||||
case 'x':
|
||||
if (str[2]) {
|
||||
char num[3] = { str[2], str[3], 0 };
|
||||
char *end = num;
|
||||
num_val = strtol(num, &end, 16);
|
||||
sl = end - num + 1;
|
||||
l = 1;
|
||||
p = &num_val;
|
||||
} else
|
||||
l = 0;
|
||||
break;
|
||||
default:
|
||||
p = str + 1, l = 1;
|
||||
}
|
||||
str += 1 + sl;
|
||||
} else if (lvl > 0 && str[0] == ')') {
|
||||
if (skip && lvl <= skip_lvl)
|
||||
skip = 0;
|
||||
lvl--, str++, l = 0;
|
||||
} else if (str[0] == '$' && str[1] == '{'
|
||||
&& (e = strchr(str + 2, '}'))) {
|
||||
str += 2;
|
||||
int method = M_PROPERTY_PRINT;
|
||||
if (str[0] == '=') {
|
||||
str += 1;
|
||||
method = M_PROPERTY_TO_STRING;
|
||||
}
|
||||
int pl = e - str;
|
||||
char pname[pl + 1];
|
||||
memcpy(pname, str, pl);
|
||||
pname[pl] = 0;
|
||||
if (m_property_do(prop_list, pname, method, &p, ctx) >= 0 && p)
|
||||
l = strlen(p), fr = 1;
|
||||
else
|
||||
l = 0;
|
||||
str = e + 1;
|
||||
} else if (str[0] == '?' && str[1] == '('
|
||||
&& (e = strchr(str + 2, ':'))) {
|
||||
lvl++;
|
||||
if (!skip) {
|
||||
int is_not = str[2] == '!';
|
||||
int pl = e - str - (is_not ? 3 : 2);
|
||||
char pname[pl + 1];
|
||||
memcpy(pname, str + (is_not ? 3 : 2), pl);
|
||||
pname[pl] = 0;
|
||||
if (m_property_do(prop_list, pname, M_PROPERTY_GET, NULL, ctx) < 0) {
|
||||
if (!is_not)
|
||||
skip = 1, skip_lvl = lvl;
|
||||
} else if (is_not)
|
||||
skip = 1, skip_lvl = lvl;
|
||||
bool cond_yes = bstr_eatstart0(&name, "?");
|
||||
bool cond_no = !cond_yes && bstr_eatstart0(&name, "!");
|
||||
bool raw = bstr_eatstart0(&name, "=");
|
||||
int method = (raw || cond_yes || cond_no)
|
||||
? M_PROPERTY_GET_STRING : M_PROPERTY_PRINT;
|
||||
|
||||
char *s = NULL;
|
||||
int r = m_property_do_bstr(prop_list, name, method, &s, ctx);
|
||||
if (cond_yes || cond_no) {
|
||||
skip = (!!s != cond_yes);
|
||||
} else {
|
||||
skip = !!s;
|
||||
char *append = s;
|
||||
if (!s && !have_fallback && !raw) {
|
||||
append = r == M_PROPERTY_UNAVAILABLE
|
||||
? "(unavailable)" : "(error)";
|
||||
}
|
||||
append_str(&ret, &ret_len, bstr0(append));
|
||||
}
|
||||
talloc_free(s);
|
||||
if (skip)
|
||||
skip_level = level;
|
||||
}
|
||||
str = e + 1, l = 0;
|
||||
} else
|
||||
p = str, l = 1, str++;
|
||||
} else if (level == 0 && bstr_eatstart0(&str, "$>")) {
|
||||
append_str(&ret, &ret_len, str);
|
||||
break;
|
||||
} else {
|
||||
char c;
|
||||
|
||||
if (skip || l <= 0)
|
||||
continue;
|
||||
// Other combinations, e.g. "$x", are added verbatim
|
||||
if (bstr_eatstart0(&str, "$$")) {
|
||||
c = '$';
|
||||
} else if (bstr_eatstart0(&str, "$}")) {
|
||||
c = '}';
|
||||
} else {
|
||||
c = str.start[0];
|
||||
str = bstr_cut(str, 1);
|
||||
}
|
||||
|
||||
if (pos + l + 1 > size) {
|
||||
size = pos + l + 512;
|
||||
ret = realloc(ret, size);
|
||||
if (!skip)
|
||||
MP_TARRAY_APPEND(NULL, ret, ret_len, c);
|
||||
}
|
||||
memcpy(ret + pos, p, l);
|
||||
pos += l;
|
||||
if (fr)
|
||||
talloc_free(p), fr = 0;
|
||||
}
|
||||
|
||||
ret[pos] = 0;
|
||||
MP_TARRAY_APPEND(NULL, ret, ret_len, '\0');
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -231,193 +314,32 @@ void m_properties_print_help_list(const m_option_t *list)
|
||||
mp_tmsg(MSGT_CFGPARSER, MSGL_INFO, "\nTotal: %d properties\n", count);
|
||||
}
|
||||
|
||||
// Some generic property implementations
|
||||
|
||||
int m_property_int_ro(const m_option_t *prop, int action,
|
||||
void *arg, int var)
|
||||
{
|
||||
switch (action) {
|
||||
case M_PROPERTY_GET:
|
||||
if (!arg)
|
||||
return 0;
|
||||
if (action == M_PROPERTY_GET) {
|
||||
*(int *)arg = var;
|
||||
return 1;
|
||||
return M_PROPERTY_OK;
|
||||
}
|
||||
return M_PROPERTY_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
int m_property_int_range(const m_option_t *prop, int action,
|
||||
void *arg, int *var)
|
||||
{
|
||||
switch (action) {
|
||||
case M_PROPERTY_SET:
|
||||
if (!arg)
|
||||
return 0;
|
||||
M_PROPERTY_CLAMP(prop, *(int *)arg);
|
||||
*var = *(int *)arg;
|
||||
return 1;
|
||||
case M_PROPERTY_STEP_UP:
|
||||
case M_PROPERTY_STEP_DOWN:
|
||||
*var += (arg ? *(int *)arg : 1) *
|
||||
(action == M_PROPERTY_STEP_DOWN ? -1 : 1);
|
||||
M_PROPERTY_CLAMP(prop, *var);
|
||||
return 1;
|
||||
}
|
||||
return m_property_int_ro(prop, action, arg, *var);
|
||||
}
|
||||
|
||||
int m_property_choice(const m_option_t *prop, int action,
|
||||
void *arg, int *var)
|
||||
{
|
||||
switch (action) {
|
||||
case M_PROPERTY_STEP_UP:
|
||||
case M_PROPERTY_STEP_DOWN:
|
||||
*var += action == M_PROPERTY_STEP_UP ? 1 : prop->max;
|
||||
*var %= (int)prop->max + 1;
|
||||
return 1;
|
||||
}
|
||||
return m_property_int_range(prop, action, arg, var);
|
||||
}
|
||||
|
||||
int m_property_flag_ro(const m_option_t *prop, int action,
|
||||
void *arg, int var)
|
||||
{
|
||||
switch (action) {
|
||||
case M_PROPERTY_PRINT:
|
||||
if (!arg)
|
||||
return 0;
|
||||
*(char **)arg = talloc_strdup(NULL, (var > prop->min) ?
|
||||
mp_gtext("enabled") : mp_gtext("disabled"));
|
||||
return 1;
|
||||
}
|
||||
return m_property_int_ro(prop, action, arg, var);
|
||||
}
|
||||
|
||||
int m_property_flag(const m_option_t *prop, int action,
|
||||
void *arg, int *var)
|
||||
{
|
||||
switch (action) {
|
||||
case M_PROPERTY_STEP_UP:
|
||||
case M_PROPERTY_STEP_DOWN:
|
||||
*var = *var == prop->min ? prop->max : prop->min;
|
||||
return 1;
|
||||
case M_PROPERTY_PRINT:
|
||||
return m_property_flag_ro(prop, action, arg, *var);
|
||||
}
|
||||
return m_property_int_range(prop, action, arg, var);
|
||||
}
|
||||
|
||||
int m_property_float_ro(const m_option_t *prop, int action,
|
||||
void *arg, float var)
|
||||
{
|
||||
switch (action) {
|
||||
case M_PROPERTY_GET:
|
||||
if (!arg)
|
||||
return 0;
|
||||
if (action == M_PROPERTY_GET) {
|
||||
*(float *)arg = var;
|
||||
return 1;
|
||||
case M_PROPERTY_PRINT:
|
||||
if (!arg)
|
||||
return 0;
|
||||
*(char **)arg = talloc_asprintf(NULL, "%.2f", var);
|
||||
return 1;
|
||||
return M_PROPERTY_OK;
|
||||
}
|
||||
return M_PROPERTY_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
int m_property_float_range(const m_option_t *prop, int action,
|
||||
void *arg, float *var)
|
||||
{
|
||||
switch (action) {
|
||||
case M_PROPERTY_SET:
|
||||
if (!arg)
|
||||
return 0;
|
||||
M_PROPERTY_CLAMP(prop, *(float *)arg);
|
||||
*var = *(float *)arg;
|
||||
return 1;
|
||||
case M_PROPERTY_STEP_UP:
|
||||
case M_PROPERTY_STEP_DOWN:
|
||||
*var += (arg ? *(float *)arg : 0.1) *
|
||||
(action == M_PROPERTY_STEP_DOWN ? -1 : 1);
|
||||
M_PROPERTY_CLAMP(prop, *var);
|
||||
return 1;
|
||||
}
|
||||
return m_property_float_ro(prop, action, arg, *var);
|
||||
}
|
||||
|
||||
int m_property_delay(const m_option_t *prop, int action,
|
||||
void *arg, float *var)
|
||||
{
|
||||
switch (action) {
|
||||
case M_PROPERTY_PRINT:
|
||||
if (!arg)
|
||||
return 0;
|
||||
*(char **)arg = talloc_asprintf(NULL, "%d ms", ROUND((*var) * 1000));
|
||||
return 1;
|
||||
default:
|
||||
return m_property_float_range(prop, action, arg, var);
|
||||
}
|
||||
}
|
||||
|
||||
int m_property_double_ro(const m_option_t *prop, int action,
|
||||
void *arg, double var)
|
||||
{
|
||||
switch (action) {
|
||||
case M_PROPERTY_GET:
|
||||
if (!arg)
|
||||
return 0;
|
||||
if (action == M_PROPERTY_GET) {
|
||||
*(double *)arg = var;
|
||||
return 1;
|
||||
case M_PROPERTY_PRINT:
|
||||
if (!arg)
|
||||
return 0;
|
||||
*(char **)arg = talloc_asprintf(NULL, "%.2f", var);
|
||||
return 1;
|
||||
}
|
||||
return M_PROPERTY_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
int m_property_time_ro(const m_option_t *prop, int action,
|
||||
void *arg, double var)
|
||||
{
|
||||
switch (action) {
|
||||
case M_PROPERTY_PRINT:
|
||||
if (!arg)
|
||||
return M_PROPERTY_ERROR;
|
||||
else {
|
||||
*(char **)arg = mp_format_time(var, false);
|
||||
return M_PROPERTY_OK;
|
||||
}
|
||||
}
|
||||
return m_property_double_ro(prop, action, arg, var);
|
||||
}
|
||||
|
||||
int m_property_string_ro(const m_option_t *prop, int action, void *arg,
|
||||
char *str)
|
||||
{
|
||||
switch (action) {
|
||||
case M_PROPERTY_GET:
|
||||
if (!arg)
|
||||
return 0;
|
||||
*(char **)arg = str;
|
||||
return 1;
|
||||
case M_PROPERTY_PRINT:
|
||||
if (!arg)
|
||||
return 0;
|
||||
*(char **)arg = talloc_strdup(NULL, str);
|
||||
return 1;
|
||||
}
|
||||
return M_PROPERTY_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
int m_property_bitrate(const m_option_t *prop, int action, void *arg, int rate)
|
||||
{
|
||||
switch (action) {
|
||||
case M_PROPERTY_PRINT:
|
||||
if (!arg)
|
||||
return M_PROPERTY_ERROR;
|
||||
*(char **)arg = talloc_asprintf(NULL, "%d kbps", rate * 8 / 1000);
|
||||
return M_PROPERTY_OK;
|
||||
}
|
||||
return m_property_int_ro(prop, action, arg, rate);
|
||||
return M_PROPERTY_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
268
m_property.h
268
m_property.h
@ -19,209 +19,119 @@
|
||||
#ifndef MPLAYER_M_PROPERTY_H
|
||||
#define MPLAYER_M_PROPERTY_H
|
||||
|
||||
#include "m_option.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
/// \defgroup Properties
|
||||
///
|
||||
/// Properties provide an interface to query and set the state of various
|
||||
/// things in MPlayer. The API is based on the \ref Options API like the
|
||||
/// \ref Config, but instead of using variables, properties use an ioctl like
|
||||
/// function. The function is used to perform various actions like get and set
|
||||
/// (see \ref PropertyActions).
|
||||
///@{
|
||||
struct m_option;
|
||||
|
||||
/// \file
|
||||
extern const struct m_option_type m_option_type_dummy;
|
||||
|
||||
/// \defgroup PropertyActions Property actions
|
||||
/// \ingroup Properties
|
||||
///@{
|
||||
enum mp_property_action {
|
||||
// Get the property type. This defines the fundamental data type read from
|
||||
// or written to the property.
|
||||
// If unimplemented, the m_option entry that defines the property is used.
|
||||
// arg: m_option*
|
||||
M_PROPERTY_GET_TYPE,
|
||||
|
||||
/// Get the current value.
|
||||
/** \param arg Pointer to a variable of the right type.
|
||||
*/
|
||||
#define M_PROPERTY_GET 0
|
||||
// Get the current value.
|
||||
// arg: pointer to a variable of the type according to the property type
|
||||
M_PROPERTY_GET,
|
||||
|
||||
/// Get a string representing the current value.
|
||||
/** Set the variable to a newly allocated string or NULL.
|
||||
* \param arg Pointer to a char* variable.
|
||||
*/
|
||||
#define M_PROPERTY_PRINT 1
|
||||
// Set a new value. The property wrapper will make sure that only valid
|
||||
// values are set (e.g. according to the property type's min/max range).
|
||||
// If unimplemented, the property is read-only.
|
||||
// arg: pointer to a variable of the type according to the property type
|
||||
M_PROPERTY_SET,
|
||||
|
||||
/// Set a new value.
|
||||
/** The variable is updated to the value actually set.
|
||||
* \param arg Pointer to a variable of the right type.
|
||||
*/
|
||||
#define M_PROPERTY_SET 2
|
||||
// Get human readable string representing the current value.
|
||||
// If unimplemented, the property wrapper uses the property type as
|
||||
// fallback.
|
||||
// arg: char**
|
||||
M_PROPERTY_PRINT,
|
||||
|
||||
/// Set a new value from a string.
|
||||
/** \param arg String containing the value.
|
||||
*/
|
||||
#define M_PROPERTY_PARSE 3
|
||||
// Switch the property up/down by a given value.
|
||||
// If unimplemented, the property wrapper uses the property type as
|
||||
// fallback.
|
||||
// arg: struct m_property_switch_arg*
|
||||
M_PROPERTY_SWITCH,
|
||||
|
||||
/// Increment the current value.
|
||||
/** The sign of the argument is also taken into account if applicable.
|
||||
* \param arg Pointer to a variable of the right type or NULL.
|
||||
*/
|
||||
#define M_PROPERTY_STEP_UP 4
|
||||
// Get a string containing a parsable representation.
|
||||
// Can't be overridden by property implementations.
|
||||
// arg: char**
|
||||
M_PROPERTY_GET_STRING,
|
||||
|
||||
/// Decrement the current value.
|
||||
/** The sign of the argument is also taken into account if applicable.
|
||||
* \param arg Pointer to a variable of the right type or NULL.
|
||||
*/
|
||||
#define M_PROPERTY_STEP_DOWN 5
|
||||
// Set a new value from a string. The property wrapper parses this using the
|
||||
// parse function provided by the property type.
|
||||
// Can't be overridden by property implementations.
|
||||
// arg: char*
|
||||
M_PROPERTY_SET_STRING,
|
||||
|
||||
/// Get a string containg a parsable representation.
|
||||
/** Set the variable to a newly allocated string or NULL.
|
||||
* \param arg Pointer to a char* variable.
|
||||
*/
|
||||
#define M_PROPERTY_TO_STRING 6
|
||||
// Pass down an action to a sub-property.
|
||||
// arg: struct m_property_action_arg*
|
||||
M_PROPERTY_KEY_ACTION,
|
||||
};
|
||||
|
||||
/// Pass down an action to a sub-property.
|
||||
#define M_PROPERTY_KEY_ACTION 7
|
||||
// Argument for M_PROPERTY_SWITCH
|
||||
struct m_property_switch_arg {
|
||||
double inc; // value to add to property, or cycle direction
|
||||
bool wrap; // whether value should wrap around on over/underflow
|
||||
};
|
||||
|
||||
/// Get a m_option describing the property.
|
||||
#define M_PROPERTY_GET_TYPE 8
|
||||
|
||||
///@}
|
||||
|
||||
/// \defgroup PropertyActionsArg Property actions argument type
|
||||
/// \ingroup Properties
|
||||
/// \brief Types used as action argument.
|
||||
///@{
|
||||
|
||||
/// Argument for \ref M_PROPERTY_KEY_ACTION
|
||||
typedef struct {
|
||||
// Argument for M_PROPERTY_KEY_ACTION
|
||||
struct m_property_action_arg {
|
||||
const char* key;
|
||||
int action;
|
||||
void* arg;
|
||||
} m_property_action_t;
|
||||
};
|
||||
|
||||
///@}
|
||||
enum mp_property_return {
|
||||
// Returned on success.
|
||||
M_PROPERTY_OK = 1,
|
||||
|
||||
/// \defgroup PropertyActionsReturn Property actions return code
|
||||
/// \ingroup Properties
|
||||
/// \brief Return values for the control function.
|
||||
///@{
|
||||
// Returned on error.
|
||||
M_PROPERTY_ERROR = 0,
|
||||
|
||||
/// Returned on success.
|
||||
#define M_PROPERTY_OK 1
|
||||
// Returned when the property can't be used, for example video related
|
||||
// properties while playing audio only.
|
||||
M_PROPERTY_UNAVAILABLE = -1,
|
||||
|
||||
/// Returned on error.
|
||||
#define M_PROPERTY_ERROR 0
|
||||
// Returned if the requested action is not implemented.
|
||||
M_PROPERTY_NOT_IMPLEMENTED = -2,
|
||||
|
||||
/// \brief Returned when the property can't be used, for example something about
|
||||
/// the subs while playing audio only
|
||||
#define M_PROPERTY_UNAVAILABLE -1
|
||||
// Returned when asking for a property that doesn't exist.
|
||||
M_PROPERTY_UNKNOWN = -3,
|
||||
};
|
||||
|
||||
/// Returned if the requested action is not implemented.
|
||||
#define M_PROPERTY_NOT_IMPLEMENTED -2
|
||||
|
||||
/// Returned when asking for a property that doesn't exist.
|
||||
#define M_PROPERTY_UNKNOWN -3
|
||||
|
||||
/// Returned when the action can't be done (like setting the volume when edl mute).
|
||||
#define M_PROPERTY_DISABLED -4
|
||||
|
||||
///@}
|
||||
|
||||
/// \ingroup Properties
|
||||
/// \brief Property action callback.
|
||||
typedef int(*m_property_ctrl_f)(const m_option_t* prop,int action,void* arg,void *ctx);
|
||||
|
||||
/// Do an action on a property.
|
||||
/** \param prop_list The list of properties.
|
||||
* \param prop The path of the property.
|
||||
* \param action See \ref PropertyActions.
|
||||
* \param arg Argument, usually a pointer to the data type used by the property.
|
||||
* \return See \ref PropertyActionsReturn.
|
||||
*/
|
||||
int m_property_do(const m_option_t* prop_list, const char* prop,
|
||||
// Access a property.
|
||||
// action: one of m_property_action
|
||||
// ctx: opaque value passed through to property implementation
|
||||
// returns: one of mp_property_return
|
||||
int m_property_do(const struct m_option* prop_list, const char* property_name,
|
||||
int action, void* arg, void *ctx);
|
||||
|
||||
/// Print a list of properties.
|
||||
void m_properties_print_help_list(const m_option_t* list);
|
||||
// Print a list of properties.
|
||||
void m_properties_print_help_list(const struct m_option* list);
|
||||
|
||||
/// Expand a property string.
|
||||
/** This function allows to print strings containing property values.
|
||||
* ${NAME} is expanded to the value of property NAME or an empty
|
||||
* string in case of error. $(NAME:STR) expand STR only if the property
|
||||
* NAME is available.
|
||||
*
|
||||
* \param prop_list An array of \ref m_option describing the available
|
||||
* properties.
|
||||
* \param str The string to expand.
|
||||
* \return The newly allocated expanded string.
|
||||
*/
|
||||
char* m_properties_expand_string(const m_option_t* prop_list,char* str, void *ctx);
|
||||
// Expand a property string.
|
||||
// This function allows to print strings containing property values.
|
||||
// ${NAME} is expanded to the value of property NAME.
|
||||
// If NAME starts with '=', use the raw value of the property.
|
||||
// ${NAME:STR} expands to the property, or STR if the property is not
|
||||
// available.
|
||||
// ${?NAME:STR} expands to STR if the property is available.
|
||||
// ${!NAME:STR} expands to STR if the property is not available.
|
||||
// General syntax: "${" ["?" | "!"] ["="] NAME ":" STR "}"
|
||||
// STR is recursively expanded using the same rules.
|
||||
// "$$" can be used to escape "$", and "$}" to escape "}".
|
||||
// "$>" disables parsing of "$" for the rest of the string.
|
||||
char* m_properties_expand_string(const struct m_option* prop_list, char *str,
|
||||
void *ctx);
|
||||
|
||||
// Helpers to use MPlayer's properties
|
||||
|
||||
/// Do an action with an MPlayer property.
|
||||
int mp_property_do(const char* name,int action, void* val, void *ctx);
|
||||
|
||||
/// Get the value of a property as a string suitable for display in an UI.
|
||||
char* mp_property_print(const char *name, void* ctx);
|
||||
|
||||
/// \defgroup PropertyImplHelper Property implementation helpers
|
||||
/// \ingroup Properties
|
||||
/// \brief Helper functions for common property types.
|
||||
///@{
|
||||
|
||||
/// Clamp a value according to \ref m_option::min and \ref m_option::max.
|
||||
#define M_PROPERTY_CLAMP(prop,val) do { \
|
||||
if(((prop)->flags & M_OPT_MIN) && (val) < (prop)->min) \
|
||||
(val) = (prop)->min; \
|
||||
else if(((prop)->flags & M_OPT_MAX) && (val) > (prop)->max) \
|
||||
(val) = (prop)->max; \
|
||||
} while(0)
|
||||
|
||||
/// Implement get.
|
||||
int m_property_int_ro(const m_option_t* prop,int action,
|
||||
void* arg,int var);
|
||||
|
||||
/// Implement set, get and step up/down.
|
||||
int m_property_int_range(const m_option_t* prop,int action,
|
||||
void* arg,int* var);
|
||||
|
||||
/// Same as m_property_int_range but cycle.
|
||||
int m_property_choice(const m_option_t* prop,int action,
|
||||
void* arg,int* var);
|
||||
|
||||
int m_property_flag_ro(const m_option_t* prop,int action,
|
||||
void* arg,int var);
|
||||
|
||||
/// Switch betwen min and max.
|
||||
int m_property_flag(const m_option_t* prop,int action,
|
||||
void* arg,int* var);
|
||||
|
||||
/// Implement get, print.
|
||||
int m_property_float_ro(const m_option_t* prop,int action,
|
||||
void* arg,float var);
|
||||
|
||||
/// Implement set, get and step up/down
|
||||
int m_property_float_range(const m_option_t* prop,int action,
|
||||
void* arg,float* var);
|
||||
|
||||
/// float with a print function which print the time in ms
|
||||
int m_property_delay(const m_option_t* prop,int action,
|
||||
void* arg,float* var);
|
||||
|
||||
/// Implement get, print
|
||||
int m_property_double_ro(const m_option_t* prop,int action,
|
||||
void* arg,double var);
|
||||
|
||||
/// Implement print
|
||||
int m_property_time_ro(const m_option_t* prop,int action,
|
||||
void* arg,double var);
|
||||
|
||||
/// get/print the string
|
||||
int m_property_string_ro(const m_option_t* prop,int action,void* arg, char* str);
|
||||
|
||||
/// get/print a bitrate
|
||||
int m_property_bitrate(const m_option_t* prop,int action,void* arg,int rate);
|
||||
|
||||
///@}
|
||||
|
||||
///@}
|
||||
// Trivial helpers for implementing properties.
|
||||
int m_property_int_ro(const struct m_option* prop, int action, void* arg,
|
||||
int var);
|
||||
int m_property_float_ro(const struct m_option* prop, int action, void* arg,
|
||||
float var);
|
||||
int m_property_double_ro(const struct m_option* prop, int action, void* arg,
|
||||
double var);
|
||||
|
||||
#endif /* MPLAYER_M_PROPERTY_H */
|
||||
|
55
metadata.h
55
metadata.h
@ -1,55 +0,0 @@
|
||||
/*
|
||||
* set of helper routines for stream metadata and properties retrieval
|
||||
*
|
||||
* Copyright (C) 2006 Benjamin Zores
|
||||
*
|
||||
* This file is part of MPlayer.
|
||||
*
|
||||
* MPlayer is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MPlayer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPLAYER_METADATA_H
|
||||
#define MPLAYER_METADATA_H
|
||||
|
||||
typedef enum metadata_s metadata_t;
|
||||
enum metadata_s {
|
||||
/* common info */
|
||||
META_NAME = 0,
|
||||
|
||||
/* video stream properties */
|
||||
META_VIDEO_CODEC,
|
||||
META_VIDEO_BITRATE,
|
||||
META_VIDEO_RESOLUTION,
|
||||
|
||||
/* audio stream properties */
|
||||
META_AUDIO_CODEC,
|
||||
META_AUDIO_BITRATE,
|
||||
META_AUDIO_SAMPLES,
|
||||
|
||||
/* ID3 tags and other stream infos */
|
||||
META_INFO_TITLE,
|
||||
META_INFO_ARTIST,
|
||||
META_INFO_ALBUM,
|
||||
META_INFO_YEAR,
|
||||
META_INFO_COMMENT,
|
||||
META_INFO_TRACK,
|
||||
META_INFO_GENRE
|
||||
};
|
||||
|
||||
struct MPContext;
|
||||
|
||||
char *get_metadata(struct MPContext *mpctx, metadata_t type);
|
||||
|
||||
#endif /* MPLAYER_METADATA_H */
|
@ -77,6 +77,13 @@ struct chapter {
|
||||
char *name;
|
||||
};
|
||||
|
||||
enum mp_osd_seek_info {
|
||||
OSD_SEEK_INFO_BAR = 1,
|
||||
OSD_SEEK_INFO_TEXT = 2,
|
||||
OSD_SEEK_INFO_CHAPTER_TEXT = 4,
|
||||
OSD_SEEK_INFO_EDITION = 8,
|
||||
};
|
||||
|
||||
struct track {
|
||||
enum stream_type type;
|
||||
// The type specific ID, also called aid (audio), sid (subs), vid (video).
|
||||
@ -127,7 +134,7 @@ typedef struct MPContext {
|
||||
char *terminal_osd_text;
|
||||
subtitle subs; // subtitle list used when reading subtitles from demuxer
|
||||
|
||||
bool add_osd_seek_info;
|
||||
int add_osd_seek_info; // bitfield of enum mp_osd_seek_info
|
||||
unsigned int osd_visible;
|
||||
|
||||
int osd_function;
|
||||
|
4
mp_osd.h
4
mp_osd.h
@ -19,7 +19,6 @@
|
||||
#ifndef MPLAYER_MP_OSD_H
|
||||
#define MPLAYER_MP_OSD_H
|
||||
|
||||
#define OSD_MSG_TV_CHANNEL 0
|
||||
#define OSD_MSG_TEXT 1
|
||||
#define OSD_MSG_SUB_DELAY 2
|
||||
#define OSD_MSG_SPEED 3
|
||||
@ -27,12 +26,14 @@
|
||||
#define OSD_MSG_BAR 5
|
||||
#define OSD_MSG_PAUSE 6
|
||||
#define OSD_MSG_RADIO_CHANNEL 7
|
||||
#define OSD_MSG_TV_CHANNEL 8
|
||||
/// Base id for messages generated from the commmand to property bridge.
|
||||
#define OSD_MSG_PROPERTY 0x100
|
||||
#define OSD_MSG_SUB_BASE 0x1000
|
||||
|
||||
#define MAX_OSD_LEVEL 3
|
||||
#define MAX_TERM_OSD_LEVEL 1
|
||||
#define OSD_LEVEL_INVISIBLE 4
|
||||
|
||||
struct MPContext;
|
||||
|
||||
@ -40,6 +41,5 @@ void set_osd_bar(struct MPContext *mpctx, int type,const char* name,double min,d
|
||||
void set_osd_msg(struct MPContext *mpctx, int id, int level, int time, const char* fmt, ...);
|
||||
void set_osd_tmsg(struct MPContext *mpctx, int id, int level, int time, const char* fmt, ...);
|
||||
void rm_osd_msg(struct MPContext *mpctx, int id);
|
||||
void mp_show_osd_progression(struct MPContext *mpctx);
|
||||
|
||||
#endif /* MPLAYER_MP_OSD_H */
|
||||
|
@ -41,7 +41,7 @@
|
||||
do { \
|
||||
size_t nextidx_ = (nextidx); \
|
||||
size_t nelems_ = MP_TALLOC_ELEMS(p); \
|
||||
if (nextidx_ <= nelems_) \
|
||||
if (nextidx_ >= nelems_) \
|
||||
p = talloc_realloc_size((ctx), p, \
|
||||
(nextidx_ + 1) * sizeof((p)[0]) * 2);\
|
||||
} while (0)
|
||||
|
252
mplayer.c
252
mplayer.c
@ -204,8 +204,8 @@ static const char av_desync_help_text[] = _(
|
||||
static int drop_frame_cnt; // total number of dropped frames
|
||||
|
||||
// seek:
|
||||
static off_t seek_to_byte;
|
||||
static off_t step_sec;
|
||||
static int64_t seek_to_byte;
|
||||
static double step_sec;
|
||||
|
||||
static m_time_size_t end_at = { .type = END_AT_NONE, .pos = 0 };
|
||||
|
||||
@ -230,7 +230,6 @@ static int ignore_start = 0;
|
||||
|
||||
double force_fps = 0;
|
||||
static int force_srate = 0;
|
||||
int frame_dropping = 0; // option 0=no drop 1= drop vo 2= drop decode
|
||||
static int play_n_frames = -1;
|
||||
static int play_n_frames_mf = -1;
|
||||
|
||||
@ -247,8 +246,6 @@ int use_filedir_conf;
|
||||
#include "mpcommon.h"
|
||||
#include "command.h"
|
||||
|
||||
#include "metadata.h"
|
||||
|
||||
static void reset_subtitles(struct MPContext *mpctx);
|
||||
static void reinit_subs(struct MPContext *mpctx);
|
||||
|
||||
@ -260,129 +257,6 @@ static float get_relative_time(struct MPContext *mpctx)
|
||||
return delta * 0.000001;
|
||||
}
|
||||
|
||||
static int is_valid_metadata_type(struct MPContext *mpctx, metadata_t type)
|
||||
{
|
||||
switch (type) {
|
||||
/* check for valid video stream */
|
||||
case META_VIDEO_CODEC:
|
||||
case META_VIDEO_BITRATE:
|
||||
case META_VIDEO_RESOLUTION:
|
||||
if (!mpctx->sh_video)
|
||||
return 0;
|
||||
break;
|
||||
|
||||
/* check for valid audio stream */
|
||||
case META_AUDIO_CODEC:
|
||||
case META_AUDIO_BITRATE:
|
||||
case META_AUDIO_SAMPLES:
|
||||
if (!mpctx->sh_audio)
|
||||
return 0;
|
||||
break;
|
||||
|
||||
/* check for valid demuxer */
|
||||
case META_INFO_TITLE:
|
||||
case META_INFO_ARTIST:
|
||||
case META_INFO_ALBUM:
|
||||
case META_INFO_YEAR:
|
||||
case META_INFO_COMMENT:
|
||||
case META_INFO_TRACK:
|
||||
case META_INFO_GENRE:
|
||||
if (!mpctx->master_demuxer)
|
||||
return 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char *get_demuxer_info(struct MPContext *mpctx, char *tag)
|
||||
{
|
||||
char **info = mpctx->master_demuxer->info;
|
||||
int n;
|
||||
|
||||
if (!info || !tag)
|
||||
return talloc_strdup(NULL, "");
|
||||
|
||||
for (n = 0; info[2 * n] != NULL; n++)
|
||||
if (!strcasecmp(info[2 * n], tag))
|
||||
break;
|
||||
|
||||
return talloc_strdup(NULL, info[2 * n + 1] ? info[2 * n + 1] : "");
|
||||
}
|
||||
|
||||
char *get_metadata(struct MPContext *mpctx, metadata_t type)
|
||||
{
|
||||
sh_audio_t * const sh_audio = mpctx->sh_audio;
|
||||
sh_video_t * const sh_video = mpctx->sh_video;
|
||||
|
||||
if (!is_valid_metadata_type(mpctx, type))
|
||||
return NULL;
|
||||
|
||||
switch (type) {
|
||||
case META_NAME:
|
||||
return talloc_strdup(NULL, mp_basename(mpctx->filename));
|
||||
case META_VIDEO_CODEC:
|
||||
if (sh_video->format == 0x10000001)
|
||||
return talloc_strdup(NULL, "mpeg1");
|
||||
else if (sh_video->format == 0x10000002)
|
||||
return talloc_strdup(NULL, "mpeg2");
|
||||
else if (sh_video->format == 0x10000004)
|
||||
return talloc_strdup(NULL, "mpeg4");
|
||||
else if (sh_video->format == 0x10000005)
|
||||
return talloc_strdup(NULL, "h264");
|
||||
else if (sh_video->format >= 0x20202020)
|
||||
return talloc_asprintf(NULL, "%.4s", (char *) &sh_video->format);
|
||||
else
|
||||
return talloc_asprintf(NULL, "0x%08X", sh_video->format);
|
||||
case META_VIDEO_BITRATE:
|
||||
return talloc_asprintf(NULL, "%d kbps",
|
||||
(int) (sh_video->i_bps * 8 / 1024));
|
||||
case META_VIDEO_RESOLUTION:
|
||||
return talloc_asprintf(NULL, "%d x %d", sh_video->disp_w,
|
||||
sh_video->disp_h);
|
||||
case META_AUDIO_CODEC:
|
||||
if (sh_audio->codec && sh_audio->codec->name)
|
||||
return talloc_strdup(NULL, sh_audio->codec->name);
|
||||
return talloc_strdup(NULL, "");
|
||||
case META_AUDIO_BITRATE:
|
||||
return talloc_asprintf(NULL, "%d kbps",
|
||||
(int) (sh_audio->i_bps * 8 / 1000));
|
||||
case META_AUDIO_SAMPLES:
|
||||
return talloc_asprintf(NULL, "%d Hz, %d ch.", sh_audio->samplerate,
|
||||
sh_audio->channels);
|
||||
|
||||
/* check for valid demuxer */
|
||||
case META_INFO_TITLE:
|
||||
return get_demuxer_info(mpctx, "Title");
|
||||
|
||||
case META_INFO_ARTIST:
|
||||
return get_demuxer_info(mpctx, "Artist");
|
||||
|
||||
case META_INFO_ALBUM:
|
||||
return get_demuxer_info(mpctx, "Album");
|
||||
|
||||
case META_INFO_YEAR:
|
||||
return get_demuxer_info(mpctx, "Year");
|
||||
|
||||
case META_INFO_COMMENT:
|
||||
return get_demuxer_info(mpctx, "Comment");
|
||||
|
||||
case META_INFO_TRACK:
|
||||
return get_demuxer_info(mpctx, "Track");
|
||||
|
||||
case META_INFO_GENRE:
|
||||
return get_demuxer_info(mpctx, "Genre");
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return talloc_strdup(NULL, "");
|
||||
}
|
||||
|
||||
static void print_stream(struct MPContext *mpctx, struct track *t, int id)
|
||||
{
|
||||
struct sh_stream *s = t->stream;
|
||||
@ -1124,7 +998,7 @@ void init_vo_spudec(struct MPContext *mpctx)
|
||||
|
||||
if (vo_spudec != NULL) {
|
||||
mpctx->initialized_flags |= INITIALIZED_SPUDEC;
|
||||
mp_property_do("sub_forced_only", M_PROPERTY_SET, &forced_subs_only,
|
||||
mp_property_do("sub-forced-only", M_PROPERTY_SET, &forced_subs_only,
|
||||
mpctx);
|
||||
}
|
||||
}
|
||||
@ -1164,6 +1038,30 @@ static void sadd_percentage(char *buf, int len, int percent) {
|
||||
saddf(buf, len, " (%d%%)", percent);
|
||||
}
|
||||
|
||||
static int get_term_width(void)
|
||||
{
|
||||
get_screen_size();
|
||||
int width = screen_width > 0 ? screen_width : 80;
|
||||
#if defined(__MINGW32__) || defined(__CYGWIN__)
|
||||
/* Windows command line is broken (MinGW's rxvt works, but we
|
||||
* should not depend on that). */
|
||||
width--;
|
||||
#endif
|
||||
return width;
|
||||
}
|
||||
|
||||
static void write_status_line(struct MPContext *mpctx, const char *line)
|
||||
{
|
||||
if (erase_to_end_of_line) {
|
||||
mp_msg(MSGT_STATUSLINE, MSGL_STATUS,
|
||||
"%s%s\r", line, erase_to_end_of_line);
|
||||
} else {
|
||||
int pos = strlen(line);
|
||||
int width = get_term_width() - pos;
|
||||
mp_msg(MSGT_STATUSLINE, MSGL_STATUS, "%s%*s\r", line, width, "");
|
||||
}
|
||||
}
|
||||
|
||||
static void print_status(struct MPContext *mpctx, double a_pos, bool at_frame)
|
||||
{
|
||||
struct MPOpts *opts = &mpctx->opts;
|
||||
@ -1187,19 +1085,16 @@ static void print_status(struct MPContext *mpctx, double a_pos, bool at_frame)
|
||||
if (opts->quiet)
|
||||
return;
|
||||
|
||||
int width;
|
||||
char *line;
|
||||
get_screen_size();
|
||||
if (screen_width > 0)
|
||||
width = screen_width;
|
||||
else
|
||||
width = 80;
|
||||
#if defined(__MINGW32__) || defined(__CYGWIN__)
|
||||
/* Windows command line is broken (MinGW's rxvt works, but we
|
||||
* should not depend on that). */
|
||||
width--;
|
||||
#endif
|
||||
line = malloc(width + 1); // one additional char for the terminating null
|
||||
if (opts->status_msg) {
|
||||
char *r = mp_property_expand_string(mpctx, opts->status_msg);
|
||||
write_status_line(mpctx, r);
|
||||
talloc_free(r);
|
||||
return;
|
||||
}
|
||||
|
||||
// one additional char for the terminating null
|
||||
int width = get_term_width() + 1;
|
||||
char *line = malloc(width);
|
||||
line[0] = '\0';
|
||||
|
||||
// Playback status
|
||||
@ -1278,15 +1173,7 @@ static void print_status(struct MPContext *mpctx, double a_pos, bool at_frame)
|
||||
#endif
|
||||
|
||||
// end
|
||||
if (erase_to_end_of_line) {
|
||||
mp_msg(MSGT_STATUSLINE, MSGL_STATUS,
|
||||
"%s%s\r", line, erase_to_end_of_line);
|
||||
} else {
|
||||
int pos = strlen(line);
|
||||
memset(&line[pos], ' ', width - pos);
|
||||
line[width] = 0;
|
||||
mp_msg(MSGT_STATUSLINE, MSGL_STATUS, "%s\r", line);
|
||||
}
|
||||
write_status_line(mpctx, line);
|
||||
free(line);
|
||||
}
|
||||
|
||||
@ -1361,6 +1248,8 @@ static mp_osd_msg_t *add_osd_msg(struct MPContext *mpctx, int id, int level,
|
||||
static void set_osd_msg_va(struct MPContext *mpctx, int id, int level, int time,
|
||||
const char *fmt, va_list ap)
|
||||
{
|
||||
if (level == OSD_LEVEL_INVISIBLE)
|
||||
return;
|
||||
mp_osd_msg_t *msg = add_osd_msg(mpctx, id, level, time);
|
||||
msg->msg = talloc_vasprintf(msg, fmt, ap);
|
||||
}
|
||||
@ -1545,6 +1434,35 @@ static void sadd_osd_status(char *buffer, int len, struct MPContext *mpctx,
|
||||
}
|
||||
}
|
||||
|
||||
// OSD messages initated by seeking commands are added lazily with this
|
||||
// function, because multiple successive seek commands can be coalesced.
|
||||
static void add_seek_osd_messages(struct MPContext *mpctx)
|
||||
{
|
||||
if (mpctx->add_osd_seek_info & OSD_SEEK_INFO_BAR)
|
||||
set_osd_bar(mpctx, 0, "Position", 0, 100, get_percent_pos(mpctx));
|
||||
if (mpctx->add_osd_seek_info & OSD_SEEK_INFO_TEXT) {
|
||||
mp_osd_msg_t *msg = add_osd_msg(mpctx, OSD_MSG_TEXT, 1,
|
||||
mpctx->opts.osd_duration);
|
||||
msg->show_position = true;
|
||||
}
|
||||
if (mpctx->add_osd_seek_info & OSD_SEEK_INFO_CHAPTER_TEXT) {
|
||||
char *chapter = chapter_display_name(mpctx, get_current_chapter(mpctx));
|
||||
set_osd_tmsg(mpctx, OSD_MSG_TEXT, 1, mpctx->opts.osd_duration,
|
||||
"Chapter: %s", chapter);
|
||||
talloc_free(chapter);
|
||||
}
|
||||
assert(mpctx->master_demuxer);
|
||||
if ((mpctx->add_osd_seek_info & OSD_SEEK_INFO_EDITION)
|
||||
&& mpctx->master_demuxer)
|
||||
{
|
||||
set_osd_tmsg(mpctx, OSD_MSG_TEXT, 1, mpctx->opts.osd_duration,
|
||||
"Playing edition %d of %d.",
|
||||
mpctx->master_demuxer->edition + 1,
|
||||
mpctx->master_demuxer->num_editions);
|
||||
}
|
||||
mpctx->add_osd_seek_info = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Update the OSD message line.
|
||||
*
|
||||
@ -1559,10 +1477,7 @@ static void update_osd_msg(struct MPContext *mpctx)
|
||||
struct MPOpts *opts = &mpctx->opts;
|
||||
struct osd_state *osd = mpctx->osd;
|
||||
|
||||
if (mpctx->add_osd_seek_info) {
|
||||
set_osd_bar(mpctx, 0, "Position", 0, 100, get_percent_pos(mpctx));
|
||||
mpctx->add_osd_seek_info = false;
|
||||
}
|
||||
add_seek_osd_messages(mpctx);
|
||||
|
||||
// Look if we have a msg
|
||||
mp_osd_msg_t *msg = get_osd_msg(mpctx);
|
||||
@ -1603,15 +1518,6 @@ static void update_osd_msg(struct MPContext *mpctx)
|
||||
}
|
||||
}
|
||||
|
||||
void mp_show_osd_progression(struct MPContext *mpctx)
|
||||
{
|
||||
mp_osd_msg_t *msg = add_osd_msg(mpctx, OSD_MSG_TEXT, 1,
|
||||
mpctx->opts.osd_duration);
|
||||
msg->show_position = true;
|
||||
|
||||
set_osd_bar(mpctx, 0, "Position", 0, 100, get_percent_pos(mpctx));
|
||||
}
|
||||
|
||||
void reinit_audio_chain(struct MPContext *mpctx)
|
||||
{
|
||||
struct MPOpts *opts = &mpctx->opts;
|
||||
@ -1916,7 +1822,7 @@ static int check_framedrop(struct MPContext *mpctx, double frame_time)
|
||||
&& !mpctx->restart_playback) {
|
||||
++drop_frame_cnt;
|
||||
++dropped_frames;
|
||||
return frame_dropping;
|
||||
return mpctx->opts.frame_dropping;
|
||||
} else
|
||||
dropped_frames = 0;
|
||||
}
|
||||
@ -2292,10 +2198,9 @@ static void vo_update_window_title(struct MPContext *mpctx)
|
||||
{
|
||||
if (!mpctx->video_out)
|
||||
return;
|
||||
char *title = property_expand_string(mpctx, mpctx->opts.vo_wintitle);
|
||||
char *title = mp_property_expand_string(mpctx, mpctx->opts.vo_wintitle);
|
||||
talloc_free(mpctx->video_out->window_title);
|
||||
mpctx->video_out->window_title = talloc_strdup(mpctx->video_out, title);
|
||||
free(title);
|
||||
mpctx->video_out->window_title = talloc_steal(mpctx, title);
|
||||
}
|
||||
|
||||
int reinit_video_chain(struct MPContext *mpctx)
|
||||
@ -3605,7 +3510,7 @@ static void open_vobsubs_from_options(struct MPContext *mpctx)
|
||||
mpctx->initialized_flags |= INITIALIZED_VOBSUB;
|
||||
// TODO: let frontend do the selection
|
||||
vobsub_set_from_lang(vo_vobsub, mpctx->opts.sub_lang);
|
||||
mp_property_do("sub_forced_only", M_PROPERTY_SET, &forced_subs_only,
|
||||
mp_property_do("sub-forced-only", M_PROPERTY_SET, &forced_subs_only,
|
||||
mpctx);
|
||||
|
||||
for (int i = 0; i < vobsub_get_indexes_count(vo_vobsub); i++) {
|
||||
@ -3798,6 +3703,8 @@ static void play_current_file(struct MPContext *mpctx)
|
||||
encode_lavc_discontinuity(mpctx->encode_lavc_ctx);
|
||||
#endif
|
||||
|
||||
mpctx->add_osd_seek_info &= OSD_SEEK_INFO_EDITION;
|
||||
|
||||
m_config_enter_file_local(mpctx->mconfig);
|
||||
|
||||
load_per_protocol_config(mpctx->mconfig, mpctx->filename);
|
||||
@ -3828,7 +3735,8 @@ static void play_current_file(struct MPContext *mpctx)
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ASS
|
||||
ass_set_style_overrides(mpctx->ass_library, opts->ass_force_style_list);
|
||||
if (opts->ass_style_override)
|
||||
ass_set_style_overrides(mpctx->ass_library, opts->ass_force_style_list);
|
||||
#endif
|
||||
if (mpctx->video_out && mpctx->video_out->config_ok)
|
||||
vo_control(mpctx->video_out, VOCTRL_RESUME, NULL);
|
||||
@ -3996,9 +3904,9 @@ goto_enable_cache:
|
||||
}
|
||||
|
||||
if (opts->playing_msg) {
|
||||
char *msg = property_expand_string(mpctx, opts->playing_msg);
|
||||
char *msg = mp_property_expand_string(mpctx, opts->playing_msg);
|
||||
mp_msg(MSGT_CPLAYER, MSGL_INFO, "%s", msg);
|
||||
free(msg);
|
||||
talloc_free(msg);
|
||||
}
|
||||
|
||||
// Disable the term OSD in verbose mode
|
||||
|
@ -31,10 +31,6 @@ extern float audio_delay;
|
||||
|
||||
extern double force_fps;
|
||||
|
||||
extern int frame_dropping;
|
||||
|
||||
extern int auto_quality;
|
||||
|
||||
extern int vobsub_id;
|
||||
|
||||
struct MPContext;
|
||||
|
@ -41,7 +41,6 @@ typedef struct MPOpts {
|
||||
int osd_duration;
|
||||
int osd_fractions;
|
||||
char *vobsub_name;
|
||||
int auto_quality;
|
||||
int untimed;
|
||||
int loop_times;
|
||||
int ordered_chapters;
|
||||
@ -61,9 +60,11 @@ typedef struct MPOpts {
|
||||
float hr_seek_demuxer_offset;
|
||||
int autosync;
|
||||
int softsleep;
|
||||
int frame_dropping;
|
||||
int term_osd;
|
||||
char *term_osd_esc;
|
||||
char *playing_msg;
|
||||
char *status_msg;
|
||||
int player_idle_mode;
|
||||
int consolecontrols;
|
||||
int doubleclick_time;
|
||||
@ -115,6 +116,7 @@ typedef struct MPOpts {
|
||||
char *ass_color;
|
||||
char *ass_border_color;
|
||||
char *ass_styles_file;
|
||||
int ass_style_override;
|
||||
int ass_hinting;
|
||||
struct lavc_param {
|
||||
int workaround_bugs;
|
||||
|
25
screenshot.c
25
screenshot.c
@ -27,10 +27,9 @@
|
||||
#include "talloc.h"
|
||||
#include "screenshot.h"
|
||||
#include "mp_core.h"
|
||||
#include "m_property.h"
|
||||
#include "command.h"
|
||||
#include "bstr.h"
|
||||
#include "mp_msg.h"
|
||||
#include "metadata.h"
|
||||
#include "path.h"
|
||||
#include "libmpcodecs/mp_image.h"
|
||||
#include "libmpcodecs/dec_video.h"
|
||||
@ -67,19 +66,6 @@ static char *stripext(void *talloc_ctx, const char *s)
|
||||
return talloc_asprintf(talloc_ctx, "%.*s", end - s, s);
|
||||
}
|
||||
|
||||
static char *do_format_property(struct MPContext *mpctx, struct bstr s) {
|
||||
struct bstr prop_name = s;
|
||||
int fallbackpos = bstrchr(s, ':');
|
||||
if (fallbackpos >= 0)
|
||||
prop_name = bstr_splice(prop_name, 0, fallbackpos);
|
||||
char *pn = bstrdup0(NULL, prop_name);
|
||||
char *res = mp_property_print(pn, mpctx);
|
||||
talloc_free(pn);
|
||||
if (!res && fallbackpos >= 0)
|
||||
res = bstrdup0(NULL, bstr_cut(s, fallbackpos + 1));
|
||||
return res;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
#define ILLEGAL_FILENAME_CHARS "?\"/\\<>*|:"
|
||||
#else
|
||||
@ -154,14 +140,13 @@ static char *create_fname(struct MPContext *mpctx, char *template,
|
||||
}
|
||||
case 'f':
|
||||
case 'F': {
|
||||
char *video_file = get_metadata(mpctx, META_NAME);
|
||||
char *video_file = mp_basename(mpctx->filename);
|
||||
if (video_file) {
|
||||
char *name = video_file;
|
||||
if (fmt == 'F')
|
||||
name = stripext(res, video_file);
|
||||
append_filename(&res, name);
|
||||
}
|
||||
talloc_free(video_file);
|
||||
break;
|
||||
}
|
||||
case 'p':
|
||||
@ -188,11 +173,13 @@ static char *create_fname(struct MPContext *mpctx, char *template,
|
||||
if (!end)
|
||||
goto error_exit;
|
||||
struct bstr prop = bstr_splice(bstr0(template), 0, end - template);
|
||||
template = end + 1;
|
||||
char *s = do_format_property(mpctx, prop);
|
||||
char *tmp = talloc_asprintf(NULL, "${%.*s}", BSTR_P(prop));
|
||||
char *s = mp_property_expand_string(mpctx, tmp);
|
||||
talloc_free(tmp);
|
||||
if (s)
|
||||
append_filename(&res, s);
|
||||
talloc_free(s);
|
||||
template = end + 1;
|
||||
break;
|
||||
}
|
||||
case '%':
|
||||
|
39
sub/ass_mp.c
39
sub/ass_mp.c
@ -47,7 +47,7 @@ ASS_Track *mp_ass_default_track(ASS_Library *library, struct MPOpts *opts)
|
||||
track->PlayResY = 288;
|
||||
track->WrapStyle = 0;
|
||||
|
||||
if (opts->ass_styles_file)
|
||||
if (opts->ass_styles_file && opts->ass_style_override)
|
||||
ass_read_styles(track, opts->ass_styles_file, sub_cp);
|
||||
|
||||
if (track->n_styles == 0) {
|
||||
@ -95,7 +95,9 @@ ASS_Track *mp_ass_default_track(ASS_Library *library, struct MPOpts *opts)
|
||||
style->ScaleY = 1.;
|
||||
}
|
||||
|
||||
ass_process_force_style(track);
|
||||
if (opts->ass_style_override)
|
||||
ass_process_force_style(track);
|
||||
|
||||
return track;
|
||||
}
|
||||
|
||||
@ -228,17 +230,32 @@ ASS_Track *mp_ass_read_stream(ASS_Library *library, const char *fname,
|
||||
void mp_ass_configure(ASS_Renderer *priv, struct MPOpts *opts,
|
||||
struct mp_eosd_res *dim, bool unscaled)
|
||||
{
|
||||
int hinting;
|
||||
ass_set_frame_size(priv, dim->w, dim->h);
|
||||
ass_set_margins(priv, dim->mt, dim->mb, dim->ml, dim->mr);
|
||||
ass_set_use_margins(priv, opts->ass_use_margins);
|
||||
ass_set_font_scale(priv, opts->ass_font_scale);
|
||||
if (!unscaled && (opts->ass_hinting & 4))
|
||||
hinting = 0;
|
||||
else
|
||||
hinting = opts->ass_hinting & 3;
|
||||
ass_set_hinting(priv, hinting);
|
||||
ass_set_line_spacing(priv, opts->ass_line_spacing);
|
||||
|
||||
int set_use_margins = 0;
|
||||
int set_sub_pos = 0;
|
||||
float set_line_spacing = 0;
|
||||
float set_font_scale = 1;
|
||||
int set_hinting = 0;
|
||||
if (opts->ass_style_override) {
|
||||
set_use_margins = opts->ass_use_margins;
|
||||
set_sub_pos = 100 - sub_pos;
|
||||
set_line_spacing = opts->ass_line_spacing;
|
||||
set_font_scale = opts->ass_font_scale;
|
||||
if (!unscaled && (opts->ass_hinting & 4))
|
||||
set_hinting = 0;
|
||||
else
|
||||
set_hinting = opts->ass_hinting & 3;
|
||||
}
|
||||
|
||||
ass_set_use_margins(priv, set_use_margins);
|
||||
#if LIBASS_VERSION >= 0x01010000
|
||||
ass_set_line_position(priv, set_sub_pos);
|
||||
#endif
|
||||
ass_set_font_scale(priv, set_font_scale);
|
||||
ass_set_hinting(priv, set_hinting);
|
||||
ass_set_line_spacing(priv, set_line_spacing);
|
||||
}
|
||||
|
||||
void mp_ass_configure_fonts(ASS_Renderer *priv)
|
||||
|
@ -345,8 +345,10 @@ void vo_update_text_sub(struct osd_state *osd, mp_osd_obj_t* obj)
|
||||
|
||||
ASS_Style *style = obj->osd_track->styles + obj->osd_track->default_style;
|
||||
|
||||
style->MarginV = obj->osd_track->PlayResY * ((100 - sub_pos)/110.0);
|
||||
update_font_scale(obj->osd_track, style, text_font_scale_factor);
|
||||
#if LIBASS_VERSION >= 0x01010000
|
||||
ass_set_line_position(osd->osd_render, 100 - sub_pos);
|
||||
#endif
|
||||
|
||||
char *text = talloc_strdup(NULL, "");
|
||||
|
||||
|
@ -135,7 +135,9 @@ static void get_bitmaps(struct sh_sub *sh, struct osd_state *osd,
|
||||
return;
|
||||
|
||||
double scale = osd->normal_scale;
|
||||
if (ctx->vsfilter_aspect && opts->ass_vsfilter_aspect_compat)
|
||||
bool use_vs_aspect = opts->ass_style_override
|
||||
? opts->ass_vsfilter_aspect_compat : 1;
|
||||
if (ctx->vsfilter_aspect && use_vs_aspect)
|
||||
scale = osd->vsfilter_scale;
|
||||
ASS_Renderer *renderer = osd->ass_renderer;
|
||||
mp_ass_configure(renderer, opts, &osd->dim, osd->unscaled);
|
||||
|
4
talloc.c
4
talloc.c
@ -1421,7 +1421,7 @@ char *talloc_strdup_append_buffer(char *s, const char *a)
|
||||
char *talloc_strndup_append(char *s, const char *a, size_t n)
|
||||
{
|
||||
if (unlikely(!s)) {
|
||||
return talloc_strdup(NULL, a);
|
||||
return talloc_strndup(NULL, a, n);
|
||||
}
|
||||
|
||||
if (unlikely(!a)) {
|
||||
@ -1440,7 +1440,7 @@ char *talloc_strndup_append_buffer(char *s, const char *a, size_t n)
|
||||
size_t slen;
|
||||
|
||||
if (unlikely(!s)) {
|
||||
return talloc_strdup(NULL, a);
|
||||
return talloc_strndup(NULL, a, n);
|
||||
}
|
||||
|
||||
if (unlikely(!a)) {
|
||||
|
Loading…
Reference in New Issue
Block a user