1
mirror of https://github.com/mpv-player/mpv synced 2024-10-26 07:22:17 +02: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:
wm4 2012-10-12 10:17:55 +02:00
commit 85d185441a
33 changed files with 2200 additions and 2409 deletions

View File

@ -84,7 +84,7 @@ Command line switches
``-no-opt``, or better ``--no-opt``. ``-no-opt``, or better ``--no-opt``.
* Per-file options are not the default anymore. You can explicitly specify * Per-file options are not the default anymore. You can explicitly specify
file local options. See ``Usage`` section. file local options. See ``Usage`` section.
* Table of renamed switches: * Table of renamed/replaced switches:
=================================== =================================== =================================== ===================================
Old New Old New
@ -92,20 +92,53 @@ Command line switches
-nosound --no-audio -nosound --no-audio
-use-filename-title --title="${filename}" -use-filename-title --title="${filename}"
-loop 0 --loop=inf -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 input.conf and slave commands
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* Table of renamed slave commands: * Table of renamed input commands:
=================================== =================================== This lists only commands that are not always gracefully handled by the
Old New internal legacy translation layer. If an input.conf contains any legacy
=================================== =================================== commands, they will be displayed with ``-v`` when it is loaded, and show
pt_step 1 b playlist_next b and the replacement commands.
pt_step -1 b playlist_prev b
pt_clear playlist_clear 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 Other
~~~~~ ~~~~~

308
DOCS/man/en/input.rst Normal file
View 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
=========================== = ==================================================

View File

@ -409,6 +409,8 @@ OPTIONS
.. include:: encode.rst .. include:: encode.rst
.. include:: input.rst
Taking screenshots Taking screenshots
================== ==================

View File

@ -176,6 +176,14 @@
rendering text subtitles. The syntax of the file is exactly like the ``[V4 rendering text subtitles. The syntax of the file is exactly like the ``[V4
Styles]`` / ``[V4+ Styles]`` section of SSA/ASS. 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> --ass-top-margin=<value>
Adds a black band at the top of the frame. The SSA/ASS renderer can place Adds a black band at the top of the frame. The SSA/ASS renderer can place
toptitles there (with ``--ass-use-margins``). toptitles there (with ``--ass-use-margins``).
@ -472,7 +480,7 @@
will stay hidden. Supported by video output drivers which use X11 or will stay hidden. Supported by video output drivers which use X11 or
OS X Cocoa. OS X Cocoa.
--delay=<sec> --audio-delay=<sec>
audio delay in seconds (positive or negative float value). Negative values audio delay in seconds (positive or negative float value). Negative values
delay the audio, and positive values delay the video. 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 there is a change in video parameters, video stream or file. This used to
be the default behavior. Currently only affects X11 VOs. 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. Display only forced subtitles for the DVD subtitle stream selected by e.g.
``--slang``. ``--slang``.
@ -643,11 +651,14 @@
--fps=<float> --fps=<float>
Override video framerate. Useful if the original value is wrong or missing. 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 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 filters are not applied to such frames. For B-frames even decoding is
skipped completely. May produce unwatchably choppy output. See also skipped completely. May produce unwatchably choppy output. With ``hard``,
``--hardframedrop``. 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> --frames=<number>
Play/convert only first <number> frames, then quit. Play/convert only first <number> frames, then quit.
@ -656,6 +667,7 @@
Specifies the character set that will be passed to FriBiDi when decoding Specifies the character set that will be passed to FriBiDi when decoding
non-UTF-8 subtitles (default: ISO8859-8). non-UTF-8 subtitles (default: ISO8859-8).
--fullscreen
--fs --fs
Fullscreen playback (centers movie, and paints black bands around it). 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 ``--no-grabpointer`` tells the player to not grab the mouse pointer after a
video mode change (``--vm``). Useful for multihead setups. video mode change (``--vm``). Useful for multihead setups.
--hardframedrop
More intense frame dropping (breaks decoding). Leads to image distortion!
--heartbeat-cmd --heartbeat-cmd
Command that is executed every 30 seconds during playback via *system()* - Command that is executed every 30 seconds during playback via *system()* -
i.e. using the shell. i.e. using the shell.
@ -1294,7 +1303,7 @@
--osd-fractions --osd-fractions
Show OSD times with fractions of seconds. Show OSD times with fractions of seconds.
--osdlevel=<0-3> --osd-level=<0-3>
Specifies which mode the OSD should start in. Specifies which mode the OSD should start in.
:0: subtitles only :0: subtitles only
@ -1312,9 +1321,6 @@
controls how much of the image is cropped. May not work with all video controls how much of the image is cropped. May not work with all video
output drivers. 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> --panscanrange=<-19.0-99.0>
(experimental) (experimental)
Change the range of the pan-and-scan functionality (default: 1). Positive Change the range of the pan-and-scan functionality (default: 1). Positive
@ -1328,15 +1334,37 @@
See also ``--user``. See also ``--user``.
--playing-msg=<string> --playing-msg=<string>
Print out a string before starting playback. The following expansions are Print out a string before starting playback. The string is expanded for
supported: properties, e.g. ``--playing-msg=file: ${filename}`` will print the string
``file: `` followed by the currently played filename.
The following expansions are supported:
${NAME} ${NAME}
Expand to the value of the property ``NAME``. Expands to the value of the property ``NAME``. If ``NAME`` starts with
?(NAME:TEXT) ``=``, use the raw value of the property. If retrieving the property
Expand ``TEXT`` only if the property ``NAME`` is available. fails, expand to an error string. (Use ``${NAME:}`` with a trailing
?(!NAME:TEXT) ``:`` to expand to an empty string instead.)
Expand ``TEXT`` only if the property ``NAME`` is not available. ${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> --playlist=<filename>
Play files according to a playlist file (ASX, Winamp, SMIL, or 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 - ``--subcp=enca:pl:cp1250`` guess the encoding for Polish, fall back on
cp1250. cp1250.
--subdelay=<sec> --sub-delay=<sec>
Delays subtitles by <sec> seconds. Can be negative. Delays subtitles by <sec> seconds. Can be negative.
--subfile=<filename> --subfile=<filename>
@ -1939,7 +1967,7 @@
*NOTE*: <rate> > movie fps speeds the subtitles up for frame-based *NOTE*: <rate> > movie fps speeds the subtitles up for frame-based
subtitle files and slows them down for time-based ones. 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 Specify the position of subtitles on the screen. The value is the vertical
position of the subtitle in % of the screen height. position of the subtitle in % of the screen height.
Can be useful with ``--vf=expand``. Can be useful with ``--vf=expand``.
@ -1980,7 +2008,7 @@
the line used for the OSD and clear it (default: ``^[[A\r^[[K``). the line used for the OSD and clear it (default: ``^[[A\r^[[K``).
--title --title
Set the window title. The string can contain property names. Set the window title. Properties are expanded (see ``--playing-msg``).
--tv=<option1:option2:...> --tv=<option1:option2:...>
This option tunes various properties of the TV capture module. For This option tunes various properties of the TV capture module. For

View File

@ -635,7 +635,7 @@ opengl-old
(no-)osd (no-)osd
Enable or disable support for OSD rendering via OpenGL (default: Enable or disable support for OSD rendering via OpenGL (default:
enabled). This option is for testing; to disable the OSD use enabled). This option is for testing; to disable the OSD use
``--osdlevel=0`` instead. ``--osd-level=0`` instead.
sw sw
Continue even if a software renderer is detected. Continue even if a software renderer is detected.

View File

@ -59,30 +59,29 @@ fi
__midentify__allprops=" __midentify__allprops="
filename filename
path path
stream_start stream-start
stream_end stream-end
stream_length stream-length
demuxer demuxer
switch_program
length length
chapters chapters
editions editions
titles titles
switch_audio audio
audio_bitrate audio-bitrate
audio_codec audio-codec
audio_format audio-format
channels channels
samplerate samplerate
switch_video video
angle angle
video_bitrate video-bitrate
video_codec video-codec
video_format video-format
aspect aspect
fps fps
width width

2
bstr.h
View File

@ -162,7 +162,7 @@ static inline int bstr_find0(struct bstr haystack, const char *needle)
return bstr_find(haystack, bstr0(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)); return bstr_eatstart(s, bstr0(prefix));
} }

View File

@ -382,7 +382,7 @@ const m_option_t common_opts[] = {
{"frames", &play_n_frames_mf, CONF_TYPE_INT, CONF_MIN, 0, 0, NULL}, {"frames", &play_n_frames_mf, CONF_TYPE_INT, CONF_MIN, 0, 0, NULL},
// seek to byte/seconds position // 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), OPT_TIME("ss", seek_to_sec, 0),
// start paused // start paused
@ -460,7 +460,7 @@ const m_option_t common_opts[] = {
OPT_FLOATRANGE("speed", playback_speed, 0, 0.01, 100.0), OPT_FLOATRANGE("speed", playback_speed, 0, 0.01, 100.0),
// set a-v distance // 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 header-specified delay (dwStart)
{"ignore-start", &ignore_start, CONF_TYPE_FLAG, 0, 0, 1, NULL}, {"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_STRINGLIST("sub", sub_name, 0),
OPT_PATHLIST("sub-paths", sub_paths, 0), OPT_PATHLIST("sub-paths", sub_paths, 0),
{"subcp", &sub_cp, CONF_TYPE_STRING, 0, 0, 0, NULL}, {"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}, {"subfps", &sub_fps, CONF_TYPE_FLOAT, 0, 0.0, 10.0, NULL},
OPT_MAKE_FLAGS("autosub", sub_auto, 0), OPT_MAKE_FLAGS("autosub", sub_auto, 0),
{"unicode", &sub_unicode, CONF_TYPE_FLAG, 0, 0, 1, NULL}, {"unicode", &sub_unicode, CONF_TYPE_FLAG, 0, 0, 1, NULL},
{"utf8", &sub_utf8, 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 // specify IFO file for VOBSUB subtitle
{"ifo", &spudec_ifo, CONF_TYPE_STRING, 0, 0, 0, NULL}, {"ifo", &spudec_ifo, CONF_TYPE_STRING, 0, 0, 0, NULL},
// enable Closed Captioning display // enable Closed Captioning display
@ -530,7 +530,7 @@ const m_option_t common_opts[] = {
{"font", &font_name, CONF_TYPE_STRING, 0, 0, 0, NULL}, {"font", &font_name, CONF_TYPE_STRING, 0, 0, 0, NULL},
{"subfont", &sub_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}, {"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}, {"subwidth", &sub_width_p, CONF_TYPE_INT, CONF_RANGE, 10, 100, NULL},
{"spualign", &spu_alignment, CONF_TYPE_INT, CONF_RANGE, -1, 2, NULL}, {"spualign", &spu_alignment, CONF_TYPE_INT, CONF_RANGE, -1, 2, NULL},
{"spuaa", &spu_aamode, CONF_TYPE_INT, CONF_RANGE, 0, 31, 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-border-color", ass_border_color, 0),
OPT_STRING("ass-styles", ass_styles_file, 0), OPT_STRING("ass-styles", ass_styles_file, 0),
OPT_INTRANGE("ass-hinting", ass_hinting, 0, 0, 7), 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} {NULL, NULL, 0, 0, 0, 0, NULL}
}; };
@ -622,6 +624,7 @@ const m_option_t mplayer_opts[]={
// video mode switching: (x11,xv,dga) // video mode switching: (x11,xv,dga)
OPT_MAKE_FLAGS("vm", vidmode, 0), OPT_MAKE_FLAGS("vm", vidmode, 0),
// start in fullscreen mode: // start in fullscreen mode:
OPT_MAKE_FLAGS("fullscreen", fullscreen, CONF_GLOBAL),
OPT_MAKE_FLAGS("fs", fullscreen, CONF_GLOBAL), OPT_MAKE_FLAGS("fs", fullscreen, CONF_GLOBAL),
// set fullscreen switch method (workaround for buggy WMs) // set fullscreen switch method (workaround for buggy WMs)
{"fsmode-dontuse", &vo_fsmode, CONF_TYPE_INT, CONF_RANGE, 0, 31, NULL}, {"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}, {"double", &vo_doublebuffering, CONF_TYPE_FLAG, 0, 0, 1, NULL},
// wait for v-sync (gl) // wait for v-sync (gl)
{"vsync", &vo_vsync, CONF_TYPE_FLAG, 0, 0, 1, NULL}, {"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_FLOATRANGE("panscanrange", vo_panscanrange, 0, -19.0, 99.0),
OPT_CHOICE("colormatrix", requested_colorspace, 0, OPT_CHOICE("colormatrix", requested_colorspace, 0,
({"auto", MP_CSP_AUTO}, ({"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}, {"use-filedir-conf", &use_filedir_conf, CONF_TYPE_FLAG, CONF_GLOBAL, 0, 1, NULL},
OPT_INTRANGE("osdlevel", osd_level, 0, 0, 3), 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_INTRANGE("osd-duration", osd_duration, 0, 0, 3600000),
OPT_MAKE_FLAGS("osd-fractions", osd_fractions, 0), OPT_MAKE_FLAGS("osd-fractions", osd_fractions, 0),
OPT_STRING("vobsub", vobsub_name, 0), OPT_STRING("vobsub", vobsub_name, 0),
{"vobsubid", &vobsub_id, CONF_TYPE_INT, CONF_RANGE, 0, 31, NULL}, {"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}, OPT_CHOICE("framedrop", frame_dropping, 0,
{"hardframedrop", &frame_dropping, CONF_TYPE_FLAG, 0, 0, 2, NULL}, ({"no", 0},
{"yes", 1}, {"", 1},
{"hard", 2})),
OPT_FLAG_ON("untimed", untimed, 0), 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("term-osd-esc", term_osd_esc, 0, OPTDEF_STR("\x1b[A\r\x1b[K")),
OPT_STRING("playing-msg", playing_msg, 0), 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}, {"slave-broken", &slave_mode, CONF_TYPE_FLAG,CONF_GLOBAL , 0, 1, NULL},
OPT_MAKE_FLAGS("idle", player_idle_mode, CONF_GLOBAL), OPT_MAKE_FLAGS("idle", player_idle_mode, CONF_GLOBAL),

1753
command.c

File diff suppressed because it is too large Load Diff

View File

@ -23,7 +23,10 @@ struct MPContext;
struct mp_cmd; struct mp_cmd;
void run_command(struct MPContext *mpctx, struct mp_cmd *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); 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 */ #endif /* MPLAYER_COMMAND_H */

View File

@ -55,6 +55,7 @@ void set_default_mplayer_options(struct MPOpts *opts)
#endif #endif
.ass_font_scale = 1, .ass_font_scale = 1,
.ass_vsfilter_aspect_compat = 1, .ass_vsfilter_aspect_compat = 1,
.ass_style_override = 1,
.use_embedded_fonts = 1, .use_embedded_fonts = 1,
.lavc_param = { .lavc_param = {

View File

@ -11,27 +11,25 @@
# #
# Note that merely removing default key bindings from this file won't remove # Note that merely removing default key bindings from this file won't remove
# the default bindings mpv was compiled with, unless # the default bindings mpv was compiled with, unless
# --input=nodefault-bindings # --input=no-default-bindings
# is specified. # is specified.
# #
# Lines starting with # are comments. Use SHARP to assign the # key. # Lines starting with # are comments. Use SHARP to assign the # key.
# #
# Some characters need to be escaped. In particular, if you want to display # Strings need to be quoted and escaped:
# a '\' character as part of an osd_show_property_text OSD message, you have to # KEY show_text "This is a single backslash: \\ and a quote: \" !"
# escape 2 times:
# key osd_show_property_text "This is a single backslash: \\\\!"
# #
# You can use modifier-key combinations like Shift+Left or Ctrl+Alt+x with # 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 # modifiers Shift, Ctrl, Alt and Meta, but note that currently reading
# key combinations is only supported through the video windows of certain # key combinations is only supported through the video windows of certain
# output drivers (not in output windows of other drivers or in a terminal). # output drivers (not in output windows of other drivers or in a terminal).
MOUSE_BTN0_DBL vo_fullscreen # toggle fullscreen on/off MOUSE_BTN0_DBL cycle fullscreen # toggle fullscreen on/off
MOUSE_BTN2 pause # toggle pause on/off MOUSE_BTN2 cycle pause # toggle pause on/off
MOUSE_BTN3 seek 10 MOUSE_BTN3 seek 10
MOUSE_BTN4 seek -10 MOUSE_BTN4 seek -10
MOUSE_BTN5 volume 1 MOUSE_BTN5 add volume 1
MOUSE_BTN6 volume -1 MOUSE_BTN6 add volume -1
# Seek units are in seconds, but note that these are limited by keyframes # Seek units are in seconds, but note that these are limited by keyframes
RIGHT seek 10 RIGHT seek 10
@ -39,95 +37,95 @@ LEFT seek -10
UP seek 60 UP seek 60
DOWN seek -60 DOWN seek -60
# Do smaller, always exact (non-keyframe-limited), seeks with shift. # Do smaller, always exact (non-keyframe-limited), seeks with shift.
Shift+RIGHT seek 1 0 1 # Don't show them on the OSD (no-osd).
Shift+LEFT seek -1 0 1 Shift+RIGHT no-osd seek 1 - exact
Shift+UP seek 5 0 1 Shift+LEFT no-osd seek -1 - exact
Shift+DOWN seek -5 0 1 Shift+UP no-osd seek 5 - exact
Shift+DOWN no-osd seek -5 - exact
PGUP seek 600 PGUP seek 600
PGDWN seek -600 PGDWN seek -600
+ audio_delay 0.100 # this changes audio/video sync + add audio-delay 0.100 # this changes audio/video sync
- audio_delay -0.100 - add audio-delay -0.100
[ speed_mult 0.9091 # scale playback speed [ speed_mult 0.9091 # scale playback speed
] speed_mult 1.1 ] speed_mult 1.1
{ speed_mult 0.5 { speed_mult 0.5
} speed_mult 2.0 } 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 quit
q {encode} quit q {encode} quit
ESC quit ESC quit
p pause # toggle pause/playback mode p cycle pause # toggle pause/playback mode
. frame_step # advance one frame and pause . frame_step # advance one frame and pause
SPACE pause SPACE cycle pause
> playlist_next # skip to next file > 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 < playlist_prev # skip to previous file
o osd # cycle through OSD mode o osd # cycle through OSD mode
I osd_show_property_text "${filename}" # display filename in osd I show_text "${filename}" # display filename in osd
P osd_show_progression P show_progress
z sub_delay -0.1 # subtract 100 ms delay from subs z add sub-delay -0.1 # subtract 100 ms delay from subs
x sub_delay +0.1 # add x add sub-delay +0.1 # add
g sub_step -1 # immediately display next subtitle g sub_step -1 # immediately display next subtitle
y sub_step +1 # previous y sub_step +1 # previous
9 volume -1 9 add volume -1
/ volume -1 / add volume -1
0 volume 1 0 add volume 1
* volume 1 * add volume 1
( balance -0.1 # adjust audio balance in favor of left ( add balance -0.1 # adjust audio balance in favor of left
) balance 0.1 # right ) add balance 0.1 # right
m mute m cycle mute
1 contrast -1 1 add contrast -1
2 contrast 1 2 add contrast 1
3 brightness -1 3 add brightness -1
4 brightness 1 4 add brightness 1
5 hue -1 5 add hue -1
6 hue 1 6 add hue 1
7 saturation -1 7 add saturation -1
8 saturation 1 8 add saturation 1
d frame_drop # cycle through framedrop modes d cycle framedrop # cycle through framedrop modes
# toggle deinterlacer; requires either vdpau output, -vf yadif or kerndeint # toggle deinterlacer; requires either vdpau output, -vf yadif or kerndeint
D step_property_osd deinterlace D cycle deinterlace
c step_property_osd colormatrix c cycle colormatrix
# Next 3 currently only work with --no-ass # Next 3 currently only work with --no-ass
r sub_pos -1 # move subtitles up r add sub-pos -1 # move subtitles up
t sub_pos +1 # down t add sub-pos +1 # down
v sub_visibility v cycle sub-visibility
# stretch SSA/ASS subtitles with anamorphic videos to match historical # stretch SSA/ASS subtitles with anamorphic videos to match historical
V step_property_osd ass_vsfilter_aspect_compat V cycle ass-vsfilter-aspect-compat
j sub_select # cycle through subtitles j cycle sub # cycle through subtitles
J sub_select -3 # ...backwards J cycle sub down # ...backwards
F forced_subs_only F cycle sub-forced-only
SHARP switch_audio # switch audio streams SHARP cycle audio # switch audio streams
_ step_property switch_video _ cycle video
TAB step_property switch_program TAB cycle program
i edl_mark # for use with --edlout mode i edl_mark # for use with --edlout mode
T vo_ontop # toggle video window ontop of other windows T cycle ontop # toggle video window ontop of other windows
f vo_fullscreen # toggle fullscreen f cycle fullscreen # toggle fullscreen
C step_property_osd capturing s screenshot # take a png screenshot
s screenshot 0 # take a png screenshot S screenshot each-frame # ...on every frame
S screenshot 1 # ...on every frame Alt+s screenshot - window # take a screenshot of window contents
Alt+s screenshot 0 1 # take a screenshot of window contents Alt+S screenshot each-frame window # ...on every frame
Alt+S screenshot 1 1 # ...on every frame w add panscan -0.1 # zoom out with -panscan 0 -fs
w panscan -0.1 # zoom out with -panscan 0 -fs e add panscan +0.1 # in
e panscan +0.1 # in
POWER quit POWER quit
MENU osd MENU cycle osd
PLAY pause PLAY cycle pause
PAUSE pause PAUSE cycle pause
PLAYPAUSE pause PLAYPAUSE cycle pause
STOP quit STOP quit
FORWARD seek 60 FORWARD seek 60
REWIND seek -60 REWIND seek -60
NEXT playlist_next NEXT playlist_next
PREV playlist_prev PREV playlist_prev
VOLUME_UP volume 1 VOLUME_UP add volume 1
VOLUME_DOWN volume -1 VOLUME_DOWN add volume -1
MUTE mute MUTE cycle mute
CLOSE_WIN quit CLOSE_WIN quit
CLOSE_WIN {encode} quit CLOSE_WIN {encode} quit
! seek_chapter -1 # skip to previous chapter ! add chapter -1 # skip to previous chapter
@ seek_chapter 1 # next @ add chapter 1 # next
E step_property_osd edition # next edition E cycle edition # next edition
A switch_angle 1 A cycle angle
U stop U stop
# TV # TV
@ -140,16 +138,16 @@ u tv_step_chanlist
# Apple Remote section # Apple Remote section
# #
AR_PLAY pause AR_PLAY cycle pause
AR_PLAY_HOLD quit AR_PLAY_HOLD quit
AR_NEXT seek 30 AR_NEXT seek 30
AR_NEXT_HOLD seek 120 AR_NEXT_HOLD seek 120
AR_PREV seek -10 AR_PREV seek -10
AR_PREV_HOLD seek -120 AR_PREV_HOLD seek -120
AR_MENU osd AR_MENU cycle osd
AR_MENU_HOLD mute AR_MENU_HOLD cycle mute
AR_VUP volume 1 AR_VUP add volume 1
AR_VDOWN volume -1 AR_VDOWN add volume -1
# #
# Joystick section # Joystick section
@ -161,15 +159,15 @@ JOY_AXIS0_PLUS seek 10
JOY_AXIS0_MINUS seek -10 JOY_AXIS0_MINUS seek -10
JOY_AXIS1_MINUS seek 60 JOY_AXIS1_MINUS seek 60
JOY_AXIS1_PLUS seek -60 JOY_AXIS1_PLUS seek -60
JOY_BTN0 pause JOY_BTN0 cycle pause
JOY_BTN1 osd JOY_BTN1 cycle osd
JOY_BTN2 volume 1 JOY_BTN2 add volume 1
JOY_BTN3 volume -1 JOY_BTN3 add volume -1
# #
# Not assigned by default # Not assigned by default
# (not an exhaustive list of unbound commands) # (not an exhaustive list of unbound commands)
# #
#? sub_scale +0.1 # increase subtitle font size #? add sub-scale +0.1 # increase subtitle font size
#? sub_scale -0.1 # decrease subtitle font size #? add sub-scale -0.1 # decrease subtitle font size

View File

@ -85,79 +85,64 @@ struct key_name {
* argument value if the user didn't give enough arguments to specify it. * 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). * 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_INT { .type = {"", NULL, &m_option_type_int} }
#define ARG_FLOAT { .type = MP_CMD_ARG_FLOAT } #define ARG_FLOAT { .type = {"", NULL, &m_option_type_float} }
#define OARG_FLOAT(def) { .type = MP_CMD_ARG_FLOAT, .optional = true, .v.f = def } #define ARG_STRING { .type = {"", NULL, &m_option_type_string} }
#define ARG_STRING { .type = MP_CMD_ARG_STRING } #define ARG_CHOICE(c) { .type = {"", NULL, &m_option_type_choice, \
#define OARG_STRING(def) { .type = MP_CMD_ARG_STRING, .optional = true, .v.s = def } 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[] = { static const mp_cmd_t mp_cmds[] = {
{ MP_CMD_IGNORE, "ignore", },
#ifdef CONFIG_RADIO #ifdef CONFIG_RADIO
{ MP_CMD_RADIO_STEP_CHANNEL, "radio_step_channel", { ARG_INT } }, { MP_CMD_RADIO_STEP_CHANNEL, "radio_step_channel", { ARG_INT } },
{ MP_CMD_RADIO_SET_CHANNEL, "radio_set_channel", { ARG_STRING } }, { MP_CMD_RADIO_SET_CHANNEL, "radio_set_channel", { ARG_STRING } },
{ MP_CMD_RADIO_SET_FREQ, "radio_set_freq", { ARG_FLOAT } }, { MP_CMD_RADIO_SET_FREQ, "radio_set_freq", { ARG_FLOAT } },
{ MP_CMD_RADIO_STEP_FREQ, "radio_step_freq", {ARG_FLOAT } }, { MP_CMD_RADIO_STEP_FREQ, "radio_step_freq", {ARG_FLOAT } },
#endif #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_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_MULT, "speed_mult", { ARG_FLOAT } },
{ MP_CMD_SPEED_SET, "speed_set", { ARG_FLOAT } },
{ MP_CMD_QUIT, "quit", { OARG_INT(0) } }, { MP_CMD_QUIT, "quit", { OARG_INT(0) } },
{ MP_CMD_STOP, "stop", }, { MP_CMD_STOP, "stop", },
{ MP_CMD_PAUSE, "pause", },
{ MP_CMD_FRAME_STEP, "frame_step", }, { MP_CMD_FRAME_STEP, "frame_step", },
{ MP_CMD_PLAYLIST_NEXT, "playlist_next", { OARG_INT(0) } }, { MP_CMD_PLAYLIST_NEXT, "playlist_next", {
{ MP_CMD_PLAYLIST_PREV, "playlist_prev", { OARG_INT(0) } }, OARG_CHOICE(0, ({"weak", 0}, {"0", 0},
{ MP_CMD_LOOP, "loop", { ARG_INT, OARG_INT(0) } }, {"force", 1}, {"1", 1})),
{ 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_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, "osd", { OARG_INT(-1) } },
{ MP_CMD_OSD_SHOW_TEXT, "osd_show_text", { ARG_STRING, OARG_INT(-1), OARG_INT(0) } }, { MP_CMD_PRINT_TEXT, "print_text", { ARG_STRING } },
{ MP_CMD_OSD_SHOW_PROPERTY_TEXT, "osd_show_property_text", { ARG_STRING, OARG_INT(-1), OARG_INT(0) } }, { MP_CMD_SHOW_TEXT, "show_text", { ARG_STRING, OARG_INT(-1), OARG_INT(0) } },
{ MP_CMD_OSD_SHOW_PROGRESSION, "osd_show_progression", }, { MP_CMD_SHOW_PROGRESS, "show_progress", },
{ 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_SUB_LOAD, "sub_load", { ARG_STRING } }, { 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 #ifdef CONFIG_TV
{ MP_CMD_TV_START_SCAN, "tv_start_scan", }, { MP_CMD_TV_START_SCAN, "tv_start_scan", },
{ MP_CMD_TV_STEP_CHANNEL, "tv_step_channel", { ARG_INT } }, { 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_SET_FREQ, "tv_set_freq", { ARG_FLOAT } },
{ MP_CMD_TV_STEP_FREQ, "tv_step_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_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 #endif
{ MP_CMD_SUB_FORCED_ONLY, "forced_subs_only", { OARG_INT(-1) } },
#ifdef CONFIG_DVBIN #ifdef CONFIG_DVBIN
{ MP_CMD_DVB_SET_CHANNEL, "dvb_set_channel", { ARG_INT, ARG_INT } }, { MP_CMD_DVB_SET_CHANNEL, "dvb_set_channel", { ARG_INT, ARG_INT } },
#endif #endif
{ MP_CMD_SWITCH_RATIO, "switch_ratio", { OARG_FLOAT(0) } }, { MP_CMD_SCREENSHOT, "screenshot", {
{ MP_CMD_VO_FULLSCREEN, "vo_fullscreen", { OARG_INT(-1) } }, OARG_CHOICE(0, ({"single", 0}, {"0", 0},
{ MP_CMD_VO_ONTOP, "vo_ontop", { OARG_INT(-1) } }, {"each-frame", 1}, {"1", 1})),
{ MP_CMD_VO_ROOTWIN, "vo_rootwin", { OARG_INT(-1) } }, OARG_CHOICE(0, ({"video", 0}, {"0", 0},
{ MP_CMD_VO_BORDER, "vo_border", { OARG_INT(-1) } }, {"window", 1}, {"1", 1})),
{ MP_CMD_SCREENSHOT, "screenshot", { OARG_INT(0), OARG_INT(0) } }, }},
{ MP_CMD_PANSCAN, "panscan", { ARG_FLOAT, OARG_INT(0) } }, { MP_CMD_LOADFILE, "loadfile", {
{ MP_CMD_SWITCH_VSYNC, "switch_vsync", { OARG_INT(0) } }, ARG_STRING,
{ MP_CMD_LOADFILE, "loadfile", { ARG_STRING, OARG_INT(0) } }, OARG_CHOICE(0, ({"replace", 0}, {"0", 0},
{ MP_CMD_LOADLIST, "loadlist", { ARG_STRING, OARG_INT(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_PLAYLIST_CLEAR, "playlist_clear", },
{ MP_CMD_RUN, "run", { ARG_STRING } }, { 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_KEYDOWN_EVENTS, "key_down_event", { ARG_INT } },
{ MP_CMD_SET_PROPERTY, "set_property", { ARG_STRING, ARG_STRING } }, { MP_CMD_SET, "set", { ARG_STRING, ARG_STRING } },
{ MP_CMD_SET_PROPERTY_OSD, "set_property_osd", { ARG_STRING, ARG_STRING } },
{ MP_CMD_GET_PROPERTY, "get_property", { 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_ADD, "add", { ARG_STRING, OARG_FLOAT(0) } },
{ MP_CMD_STEP_PROPERTY_OSD, "step_property_osd", { ARG_STRING, OARG_FLOAT(0), OARG_INT(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_SET_MOUSE_POS, "set_mouse_pos", { ARG_INT, ARG_INT } },
{ MP_CMD_AF_SWITCH, "af_switch", { ARG_STRING } }, { 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_CLR, "af_clr", },
{ MP_CMD_AF_CMDLINE, "af_cmdline", { ARG_STRING, ARG_STRING } }, { MP_CMD_AF_CMDLINE, "af_cmdline", { ARG_STRING, ARG_STRING } },
{ MP_CMD_SHOW_CHAPTERS, "show_chapters_osd", }, { MP_CMD_SHOW_CHAPTERS, "show_chapters", },
{ MP_CMD_SHOW_TRACKS, "show_tracks_osd", }, { MP_CMD_SHOW_TRACKS, "show_tracks", },
{ MP_CMD_VO_CMDLINE, "vo_cmdline", { ARG_STRING } }, { MP_CMD_VO_CMDLINE, "vo_cmdline", { ARG_STRING } },
{0} {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 /// The names of the keys as used in input.conf
/// If you add some new keys, you also need to add them here /// 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; 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; int pausing = 0;
char *ptr; int on_osd = MP_ON_OSD_AUTO;
const mp_cmd_t *cmd_def; struct mp_cmd *cmd = NULL;
void *tmp = talloc_new(NULL);
// Ignore heading spaces. if (eat_token(&str, "pausing")) {
while (str[0] == ' ' || str[0] == '\t')
++str;
if (strncmp(str, "pausing ", 8) == 0) {
pausing = 1; pausing = 1;
str = &str[8]; } else if (eat_token(&str, "pausing_keep")) {
} else if (strncmp(str, "pausing_keep ", 13) == 0) {
pausing = 2; pausing = 2;
str = &str[13]; } else if (eat_token(&str, "pausing_toggle")) {
} else if (strncmp(str, "pausing_toggle ", 15) == 0) {
pausing = 3; pausing = 3;
str = &str[15]; } else if (eat_token(&str, "pausing_keep_force")) {
} else if (strncmp(str, "pausing_keep_force ", 19) == 0) {
pausing = 4; pausing = 4;
str = &str[19];
} }
ptr = str + strcspn(str, "\t "); str = bstr_lstrip(str);
if (*ptr != 0) for (const struct legacy_cmd *entry = legacy_cmds; entry->old; entry++) {
l = ptr - str; size_t old_len = strlen(entry->old);
else if (bstrcasecmp(bstr_splice(str, 0, old_len),
l = strlen(str); (bstr) {(char *)entry->old, old_len}) == 0)
{
if (l == 0) mp_tmsg(MSGT_INPUT, MSGL_V, "Warning: command '%s' is "
return NULL; "deprecated, replaced with '%s'. Fix your input.conf!\n",
entry->old, entry->new);
for (i = 0; mp_cmds[i].name != NULL; i++) { bstr s = bstr_cut(str, old_len);
if (strncasecmp(mp_cmds[i].name, str, l) == 0) str = bstr0(talloc_asprintf(tmp, "%s%.*s", entry->new, BSTR_P(s)));
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;
break; break;
} }
case 0:
ptr = NULL;
break;
default:
mp_tmsg(MSGT_INPUT, MSGL_ERR, "Unknown argument %d\n", i);
}
} }
cmd->nargs = i;
int min_args; if (eat_token(&str, "no-osd")) {
for (min_args = 0; min_args < MP_CMD_MAX_ARGS on_osd = MP_ON_OSD_NO;
&& cmd_def->args[min_args].type } else if (eat_token(&str, "osd-bar")) {
&& !cmd_def->args[min_args].optional; min_args++); on_osd = MP_ON_OSD_BAR;
if (cmd->nargs < min_args) { } else if (eat_token(&str, "osd-msg")) {
mp_tmsg(MSGT_INPUT, MSGL_ERR, "Command \"%s\" requires at least %d " on_osd = MP_ON_OSD_MSG;
"arguments, we found only %d so far.\n", cmd_def->name, } else if (eat_token(&str, "osd-msg-bar")) {
min_args, cmd->nargs); 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; goto error;
} }
for (; i < MP_CMD_MAX_ARGS && cmd_def->args[i].type; i++) { cmd = talloc_ptrtype(NULL, cmd);
memcpy(&cmd->args[i], &cmd_def->args[i], sizeof(struct mp_cmd_arg)); *cmd = mp_cmds[cmd_idx];
if (cmd_def->args[i].type == MP_CMD_ARG_STRING cmd->pausing = pausing;
&& cmd_def->args[i].v.s != NULL) cmd->on_osd = on_osd;
cmd->args[i].v.s = talloc_strdup(cmd, cmd_def->args[i].v.s);
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) bstr dummy;
cmd->args[i].type = 0; 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; return cmd;
error: error:
mp_cmd_free(cmd); talloc_free(cmd);
talloc_free(tmp);
return NULL; 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, static struct cmd_bind_section *get_bind_section(struct input_ctx *ictx,
bool builtin, bool builtin, bstr section)
char *section)
{ {
struct cmd_bind_section *bind_section = ictx->cmd_bind_sections; struct cmd_bind_section *bind_section = ictx->cmd_bind_sections;
if (section == NULL) if (section.len == 0)
section = "default"; section = bstr0("default");
while (bind_section) { while (bind_section) {
if (strcmp(section, bind_section->section) == 0 if (bstrcmp0(section, bind_section->section) == 0
&& builtin == bind_section->is_builtin) && builtin == bind_section->is_builtin)
return bind_section; return bind_section;
if (bind_section->next == NULL) 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 = ictx->cmd_bind_sections;
} }
bind_section->cmd_binds = NULL; 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->is_builtin = builtin;
bind_section->next = NULL; bind_section->next = NULL;
return bind_section; return bind_section;
@ -1005,9 +1142,9 @@ static char *section_find_bind_for_key(struct input_ctx *ictx,
bool builtin, char *section, bool builtin, char *section,
int n, int *keys) int n, int *keys)
{ {
struct cmd_bind_section *bs = get_bind_section(ictx, builtin, section); struct cmd_bind_section *bs = get_bind_section(ictx, builtin,
const struct cmd_bind *binds = bs->cmd_binds; bstr0(section));
return binds ? find_bind_for_key(binds, n, keys) : NULL; 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) 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); talloc_free(key_buf);
return NULL; return NULL;
} }
if (strcmp(cmd, "ignore") == 0) ret = mp_input_parse_cmd(bstr0(cmd));
return NULL;
ret = mp_input_parse_cmd(cmd);
if (!ret) { if (!ret) {
char *key_buf = get_key_combo_name(keys, n); char *key_buf = get_key_combo_name(keys, n);
mp_tmsg(MSGT_INPUT, MSGL_ERR, 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; char *text;
while ((r = read_cmd(cmd_fd, &text)) >= 0) { while ((r = read_cmd(cmd_fd, &text)) >= 0) {
ictx->got_new_events = true; 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); talloc_free(text);
if (cmd) if (cmd)
queue_add(&ictx->control_cmd_queue, cmd, false); 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) mp_cmd_t *mp_input_get_cmd(struct input_ctx *ictx, int time, int peek_only)
{ {
if (async_quit_request) 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) if (ictx->control_cmd_queue.first || ictx->key_cmd_queue.first)
time = 0; 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 = talloc_memdup(NULL, cmd, sizeof(mp_cmd_t));
ret->name = talloc_strdup(ret, cmd->name); ret->name = talloc_strdup(ret, cmd->name);
for (i = 0; i < MP_CMD_MAX_ARGS && cmd->args[i].type; i++) { for (i = 0; i < MP_CMD_MAX_ARGS; i++) {
if (cmd->args[i].type == MP_CMD_ARG_STRING && cmd->args[i].v.s != NULL) if (cmd->args[i].type.type == &m_option_type_string)
ret->args[i].v.s = talloc_strdup(ret, cmd->args[i].v.s); 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; 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) const int keys[MP_MAX_KEY_DOWN + 1], bstr command)
{ {
int i = 0, j; int i = 0, j;
struct cmd_bind *bind = NULL; struct cmd_bind *bind = NULL;
struct cmd_bind_section *bind_section = 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); bind_section = get_bind_section(ictx, builtin, section);
talloc_free(section);
if (bind_section->cmd_binds) { if (bind_section->cmd_binds) {
for (i = 0; bind_section->cmd_binds[i].cmd != NULL; i++) { 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; continue;
} }
talloc_free(name); 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++; n_binds++;
// Print warnings if invalid commands are encountered.
talloc_free(mp_input_parse_cmd(command));
} }
return n_binds; 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; const mp_cmd_t *cmd;
int i, j; int i, j;
const char *type;
for (i = 0; (cmd = &mp_cmds[i])->name != NULL; i++) { for (i = 0; (cmd = &mp_cmds[i])->name != NULL; i++) {
printf("%-20.20s", cmd->name); printf("%-20.20s", cmd->name);
for (j = 0; j < MP_CMD_MAX_ARGS && cmd->args[j].type; j++) { for (j = 0; j < MP_CMD_MAX_ARGS && cmd->args[j].type.type; j++) {
switch (cmd->args[j].type) { const char *type = cmd->args[j].type.type->name;
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 = "??";
}
if (cmd->args[j].optional) if (cmd->args[j].optional)
printf(" [%s]", type); printf(" [%s]", type);
else else

View File

@ -20,106 +20,49 @@
#define MPLAYER_INPUT_H #define MPLAYER_INPUT_H
#include <stdbool.h> #include <stdbool.h>
#include "bstr.h"
#include "m_option.h"
// All command IDs // All command IDs
enum mp_command_type { enum mp_command_type {
MP_CMD_IGNORE,
MP_CMD_SEEK, MP_CMD_SEEK,
MP_CMD_AUDIO_DELAY,
MP_CMD_QUIT, 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_NEXT,
MP_CMD_PLAYLIST_PREV, MP_CMD_PLAYLIST_PREV,
MP_CMD_SUB_DELAY,
MP_CMD_OSD, 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_CHANNEL,
MP_CMD_TV_STEP_NORM, MP_CMD_TV_STEP_NORM,
MP_CMD_TV_STEP_CHANNEL_LIST, MP_CMD_TV_STEP_CHANNEL_LIST,
MP_CMD_VO_FULLSCREEN,
MP_CMD_SUB_POS,
MP_CMD_SCREENSHOT, MP_CMD_SCREENSHOT,
MP_CMD_PANSCAN,
MP_CMD_MUTE,
MP_CMD_LOADFILE, MP_CMD_LOADFILE,
MP_CMD_LOADLIST, MP_CMD_LOADLIST,
MP_CMD_PLAYLIST_CLEAR, 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_SUB_STEP,
MP_CMD_TV_SET_CHANNEL, MP_CMD_TV_SET_CHANNEL,
MP_CMD_EDL_MARK, MP_CMD_EDL_MARK,
MP_CMD_SUB_ALIGNMENT,
MP_CMD_TV_LAST_CHANNEL, MP_CMD_TV_LAST_CHANNEL,
MP_CMD_OSD_SHOW_TEXT,
MP_CMD_TV_SET_FREQ, MP_CMD_TV_SET_FREQ,
MP_CMD_TV_SET_NORM, 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_FRAME_STEP,
MP_CMD_SPEED_INCR,
MP_CMD_SPEED_MULT, MP_CMD_SPEED_MULT,
MP_CMD_SPEED_SET,
MP_CMD_RUN, MP_CMD_RUN,
MP_CMD_SWITCH_AUDIO,
MP_CMD_GET_TIME_POS,
MP_CMD_SUB_LOAD, MP_CMD_SUB_LOAD,
MP_CMD_KEYDOWN_EVENTS, MP_CMD_KEYDOWN_EVENTS,
MP_CMD_VO_BORDER, MP_CMD_SET,
MP_CMD_SET_PROPERTY,
MP_CMD_SET_PROPERTY_OSD,
MP_CMD_GET_PROPERTY, MP_CMD_GET_PROPERTY,
MP_CMD_OSD_SHOW_PROPERTY_TEXT, MP_CMD_PRINT_TEXT,
MP_CMD_OSD_SHOW_PROGRESSION, MP_CMD_SHOW_TEXT,
MP_CMD_SEEK_CHAPTER, MP_CMD_SHOW_PROGRESS,
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_RADIO_STEP_CHANNEL, MP_CMD_RADIO_STEP_CHANNEL,
MP_CMD_RADIO_SET_CHANNEL, MP_CMD_RADIO_SET_CHANNEL,
MP_CMD_RADIO_SET_FREQ, MP_CMD_RADIO_SET_FREQ,
MP_CMD_SET_MOUSE_POS, MP_CMD_SET_MOUSE_POS,
MP_CMD_STEP_PROPERTY, MP_CMD_ADD,
MP_CMD_STEP_PROPERTY_OSD, MP_CMD_CYCLE,
MP_CMD_RADIO_STEP_FREQ, MP_CMD_RADIO_STEP_FREQ,
MP_CMD_TV_STEP_FREQ, MP_CMD_TV_STEP_FREQ,
MP_CMD_LOOP,
MP_CMD_BALANCE,
MP_CMD_SUB_SCALE,
MP_CMD_TV_START_SCAN, MP_CMD_TV_START_SCAN,
MP_CMD_SWITCH_ANGLE,
MP_CMD_ASS_USE_MARGINS,
MP_CMD_SWITCH_TITLE,
MP_CMD_STOP, MP_CMD_STOP,
/// DVB commands /// DVB commands
@ -139,11 +82,6 @@ enum mp_command_type {
MP_CMD_VO_CMDLINE, 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 #define MP_CMD_MAX_ARGS 10
// Error codes for the drivers // 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 // Key FIFO was full - release events may be lost, zero button-down status
#define MP_INPUT_RELEASE_ALL -5 #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 { enum mp_input_section_flags {
// If a key binding is not defined in the current section, search the // If a key binding is not defined in the current section, search the
// default section for it ("default" refers to bindings with no section // default section for it ("default" refers to bindings with no section
@ -169,7 +114,7 @@ enum mp_input_section_flags {
struct input_ctx; struct input_ctx;
struct mp_cmd_arg { struct mp_cmd_arg {
int type; struct m_option type;
bool optional; bool optional;
union { union {
int i; int i;
@ -184,6 +129,7 @@ typedef struct mp_cmd {
struct mp_cmd_arg args[MP_CMD_MAX_ARGS]; struct mp_cmd_arg args[MP_CMD_MAX_ARGS];
int nargs; int nargs;
int pausing; int pausing;
enum mp_on_osd on_osd;
struct mp_cmd *queue_next; struct mp_cmd *queue_next;
} mp_cmd_t; } mp_cmd_t;
@ -235,7 +181,7 @@ struct mp_cmd *mp_input_get_cmd(struct input_ctx *ictx, int time,
int peek_only); int peek_only);
/* Parse text and return corresponding struct mp_cmd. */ /* 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 // After getting a command from mp_input_get_cmd you need to free it using this
// function // function

View File

@ -509,5 +509,5 @@ void vo_mouse_movement(struct vo *vo, int posx, int posy)
if (!enable_mouse_movements) if (!enable_mouse_movements)
return; return;
snprintf(cmd_str, sizeof(cmd_str), "set_mouse_pos %i %i", posx, posy); 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)));
} }

View File

@ -365,8 +365,8 @@ int m_config_register_options(struct m_config *config,
return 1; return 1;
} }
static struct m_config_option *m_config_get_co(const struct m_config *config, struct m_config_option *m_config_get_co(const struct m_config *config,
struct bstr name) struct bstr name)
{ {
struct m_config_option *co; struct m_config_option *co;

View File

@ -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, const struct m_option *m_config_get_option(const struct m_config *config,
struct bstr name); 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. /* Print a list of all registered options.
* \param config The config object. * \param config The config object.
*/ */

View File

@ -26,15 +26,18 @@
#include <math.h> #include <math.h>
#include <stdio.h> #include <stdio.h>
#include <stdarg.h> #include <stdarg.h>
#include <limits.h>
#include <inttypes.h> #include <inttypes.h>
#include <unistd.h> #include <unistd.h>
#include <assert.h> #include <assert.h>
#include <libavutil/common.h>
#include <libavutil/avstring.h>
#include "talloc.h" #include "talloc.h"
#include "m_option.h" #include "m_option.h"
#include "mp_msg.h" #include "mp_msg.h"
#include "stream/url.h" #include "stream/url.h"
#include "libavutil/avstring.h"
char *m_option_strerror(int code) 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)) #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, static int parse_flag(const m_option_t *opt, struct bstr name,
struct bstr param, void *dst) 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"); 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 = { const m_option_type_t m_option_type_flag = {
// need yes or no in config files // need yes or no in config files
.name = "Flag", .name = "Flag",
@ -128,10 +148,30 @@ const m_option_type_t m_option_type_flag = {
.parse = parse_flag, .parse = parse_flag,
.print = print_flag, .print = print_flag,
.copy = copy_opt, .copy = copy_opt,
.add = add_flag,
.clamp = clamp_flag,
}; };
// Integer // 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, static int parse_longlong(const m_option_t *opt, struct bstr name,
struct bstr param, void *dst) struct bstr param, void *dst)
{ {
@ -169,6 +209,22 @@ static int parse_longlong(const m_option_t *opt, struct bstr name,
return 1; 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, static int parse_int(const m_option_t *opt, struct bstr name,
struct bstr param, void *dst) struct bstr param, void *dst)
{ {
@ -189,12 +245,39 @@ static int parse_int64(const m_option_t *opt, struct bstr name,
return r; return r;
} }
static char *print_int(const m_option_t *opt, const void *val) static char *print_int(const m_option_t *opt, const void *val)
{ {
if (opt->type->size == sizeof(int64_t)) if (opt->type->size == sizeof(int64_t))
return talloc_asprintf(NULL, "%"PRId64, *(const int64_t *)val); 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 = { 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, .parse = parse_int,
.print = print_int, .print = print_int,
.copy = copy_opt, .copy = copy_opt,
.add = add_int,
.clamp = clamp_int,
}; };
const m_option_type_t m_option_type_int64 = { 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, .parse = parse_int64,
.print = print_int, .print = print_int,
.copy = copy_opt, .copy = copy_opt,
.add = add_int64,
.clamp = clamp_int64,
}; };
static int parse_intpair(const struct m_option *opt, struct bstr name, 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, .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, static int parse_choice(const struct m_option *opt, struct bstr name,
struct bstr param, void *dst) struct bstr param, void *dst)
{ {
@ -303,12 +405,75 @@ static char *print_choice(const m_option_t *opt, const void *val)
abort(); 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 = { const struct m_option_type m_option_type_choice = {
.name = "String", // same as arbitrary strings in option list for now .name = "String", // same as arbitrary strings in option list for now
.size = sizeof(int), .size = sizeof(int),
.parse = parse_choice, .parse = parse_choice,
.print = print_choice, .print = print_choice,
.copy = copy_opt, .copy = copy_opt,
.add = add_choice,
.clamp = clamp_choice,
}; };
// Float // Float
@ -316,6 +481,22 @@ const struct m_option_type m_option_type_choice = {
#undef VAL #undef VAL
#define VAL(x) (*(double *)(x)) #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, static int parse_double(const m_option_t *opt, struct bstr name,
struct bstr param, void *dst) 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) static char *print_double(const m_option_t *opt, const void *val)
{ {
opt = NULL;
return talloc_asprintf(NULL, "%f", VAL(val)); 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 = { const m_option_type_t m_option_type_double = {
// double precision float or ratio (numerator[:/]denominator) // double precision float or ratio (numerator[:/]denominator)
.name = "Double", .name = "Double",
.size = sizeof(double), .size = sizeof(double),
.parse = parse_double, .parse = parse_double,
.print = print_double, .print = print_double,
.pretty_print = print_double_f2,
.copy = copy_opt, .copy = copy_opt,
.clamp = clamp_double,
}; };
#undef VAL #undef VAL
#define VAL(x) (*(float *)(x)) #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, static int parse_float(const m_option_t *opt, struct bstr name,
struct bstr param, void *dst) 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) static char *print_float(const m_option_t *opt, const void *val)
{ {
opt = NULL;
return talloc_asprintf(NULL, "%f", VAL(val)); 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 = { const m_option_type_t m_option_type_float = {
// floating point number or ratio (numerator[:/]denominator) // floating point number or ratio (numerator[:/]denominator)
.name = "Float", .name = "Float",
.size = sizeof(float), .size = sizeof(float),
.parse = parse_float, .parse = parse_float,
.print = print_float, .print = print_float,
.pretty_print = print_float_f2,
.copy = copy_opt, .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 ///////////// String
#undef VAL #undef VAL
#define VAL(x) (*(char **)(x)) #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, static int parse_str(const m_option_t *opt, struct bstr name,
struct bstr param, void *dst) struct bstr param, void *dst)
{ {
@ -507,6 +715,7 @@ const m_option_type_t m_option_type_string = {
.print = print_str, .print = print_str,
.copy = copy_str, .copy = copy_str,
.free = free_str, .free = free_str,
.clamp = clamp_str,
}; };
//////////// String list //////////// String list
@ -1024,12 +1233,20 @@ static int parse_time(const m_option_t *opt, struct bstr name,
return 1; 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 = { const m_option_type_t m_option_type_time = {
.name = "Time", .name = "Time",
.size = sizeof(double), .size = sizeof(double),
.parse = parse_time, .parse = parse_time,
.print = print_double, .print = print_double,
.pretty_print = pretty_print_time,
.copy = copy_opt, .copy = copy_opt,
.add = add_double,
.clamp = clamp_double,
}; };

View File

@ -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_double;
extern const m_option_type_t m_option_type_string; 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_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;
extern const m_option_type_t m_option_type_time_size; extern const m_option_type_t m_option_type_time_size;
extern const m_option_type_t m_option_type_choice; 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_PRINT_FUNC (&m_option_type_print_func)
#define CONF_TYPE_SUBCONFIG (&m_option_type_subconfig) #define CONF_TYPE_SUBCONFIG (&m_option_type_subconfig)
#define CONF_TYPE_STRING_LIST (&m_option_type_string_list) #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_IMGFMT (&m_option_type_imgfmt)
#define CONF_TYPE_AFMT (&m_option_type_afmt) #define CONF_TYPE_AFMT (&m_option_type_afmt)
#define CONF_TYPE_SPAN (&m_option_type_span) #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); 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. // Copy data between two locations. Deep copy if the data has pointers.
/** \param opt The option to copy. /** \param opt The option to copy.
* \param dst Pointer to the destination memory. * \param dst Pointer to the destination memory.
@ -243,6 +246,18 @@ struct m_option_type {
* set to NULL. * set to NULL.
*/ */
void (*free)(void *dst); 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 // 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); 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. // Helper to parse options, see \ref m_option_type::parse.
static inline int m_option_parse(const m_option_t *opt, struct bstr name, static inline int m_option_parse(const m_option_t *opt, struct bstr name,
struct bstr param, void *dst) 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; 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. // Helper around \ref m_option_type::copy.
static inline void m_option_copy(const m_option_t *opt, void *dst, static inline void m_option_copy(const m_option_t *opt, void *dst,
const void *src) 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_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_AUDIOFORMAT(...) OPT_GENERAL(__VA_ARGS__, .type = &m_option_type_afmt)
#define OPT_HELPER_REMOVEPAREN(...) __VA_ARGS__ #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(...) 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 // Union of choices and an int range. The choice values can be included in the
// int range, or be completely separate - both works. // 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(...) 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_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})) #define OPT_TRACKCHOICE(name, var) OPT_CHOICE_OR_INT(name, var, 0, 0, 8190, ({"no", -2}, {"auto", -1}))

View File

@ -25,7 +25,9 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <inttypes.h> #include <inttypes.h>
#include <unistd.h> #include <assert.h>
#include <libavutil/common.h>
#include "talloc.h" #include "talloc.h"
#include "m_option.h" #include "m_option.h"
@ -33,173 +35,254 @@
#include "mp_msg.h" #include "mp_msg.h"
#include "mpcommon.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, static int do_action(const m_option_t *prop_list, const char *name,
int action, void *arg, void *ctx) int action, void *arg, void *ctx)
{ {
const char *sep; const char *sep;
const m_option_t *prop; const m_option_t *prop;
m_property_action_t ka;
int r;
if ((sep = strchr(name, '/')) && sep[1]) { if ((sep = strchr(name, '/')) && sep[1]) {
int len = sep - name; int len = sep - name;
char base[len + 1]; char base[len + 1];
memcpy(base, name, len); memcpy(base, name, len);
base[len] = 0; base[len] = 0;
prop = m_option_list_find(prop_list, base); prop = m_option_list_find(prop_list, base);
ka.key = sep + 1; struct m_property_action_arg ka = {
ka.action = action; .key = sep + 1,
ka.arg = arg; .action = action,
.arg = arg,
};
action = M_PROPERTY_KEY_ACTION; action = M_PROPERTY_KEY_ACTION;
arg = &ka; arg = &ka;
} else } else
prop = m_option_list_find(prop_list, name); prop = m_option_list_find(prop_list, name);
if (!prop) if (!prop)
return M_PROPERTY_UNKNOWN; return M_PROPERTY_UNKNOWN;
r = ((m_property_ctrl_f)prop->p)(prop, action, arg, ctx); int (*control)(const m_option_t*, int, void*, void*) = prop->p;
if (action == M_PROPERTY_GET_TYPE && r < 0) { int r = control(prop, action, arg, ctx);
if (!arg) if (action == M_PROPERTY_GET_TYPE && r < 0 &&
return M_PROPERTY_ERROR; prop->type != &m_option_type_dummy)
*(const m_option_t **)arg = prop; {
*(struct m_option *)arg = *prop;
return M_PROPERTY_OK; return M_PROPERTY_OK;
} }
return r; 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) int action, void *arg, void *ctx)
{ {
const m_option_t *opt;
union m_option_value val = {0}; union m_option_value val = {0};
int r; 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) { switch (action) {
case M_PROPERTY_PRINT: case M_PROPERTY_PRINT: {
if ((r = do_action(prop_list, name, M_PROPERTY_PRINT, arg, ctx)) >= 0) if ((r = do_action(prop_list, name, M_PROPERTY_PRINT, arg, ctx)) >= 0)
return r; return r;
// fallback on the default print for this type // Fallback to m_option
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;
if ((r = do_action(prop_list, name, M_PROPERTY_GET, &val, ctx)) <= 0) if ((r = do_action(prop_list, name, M_PROPERTY_GET, &val, ctx)) <= 0)
return r; return r;
if (!arg) char *str = m_option_pretty_print(&opt, &val);
return M_PROPERTY_ERROR; m_option_free(&opt, &val);
char *str = m_option_print(opt, &val);
*(char **)arg = str; *(char **)arg = str;
return str != NULL; return str != NULL;
case M_PROPERTY_PARSE: }
// try the property own parsing func case M_PROPERTY_GET_STRING: {
if ((r = do_action(prop_list, name, M_PROPERTY_PARSE, arg, ctx)) != if ((r = do_action(prop_list, name, M_PROPERTY_GET, &val, ctx)) <= 0)
M_PROPERTY_NOT_IMPLEMENTED)
return r; return r;
// fallback on the options API, get the type and parse. char *str = m_option_print(&opt, &val);
if ((r = m_option_free(&opt, &val);
do_action(prop_list, name, M_PROPERTY_GET_TYPE, &opt, ctx)) <= 0) *(char **)arg = str;
return r; return str != NULL;
if (!arg) }
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; 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); r = do_action(prop_list, name, M_PROPERTY_SET, &val, ctx);
m_option_free(opt, &val); m_option_free(&opt, &val);
return r; 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) void *ctx)
{ {
int l, fr = 0, pos = 0, size = strlen(str) + 512; char *ret = NULL;
char *p = NULL, *e, *ret = malloc(size), num_val; int ret_len = 0;
int skip = 0, lvl = 0, skip_lvl = 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) { if (!skip) {
int is_not = str[2] == '!'; bool cond_yes = bstr_eatstart0(&name, "?");
int pl = e - str - (is_not ? 3 : 2); bool cond_no = !cond_yes && bstr_eatstart0(&name, "!");
char pname[pl + 1]; bool raw = bstr_eatstart0(&name, "=");
memcpy(pname, str + (is_not ? 3 : 2), pl); int method = (raw || cond_yes || cond_no)
pname[pl] = 0; ? M_PROPERTY_GET_STRING : M_PROPERTY_PRINT;
if (m_property_do(prop_list, pname, M_PROPERTY_GET, NULL, ctx) < 0) {
if (!is_not) char *s = NULL;
skip = 1, skip_lvl = lvl; int r = m_property_do_bstr(prop_list, name, method, &s, ctx);
} else if (is_not) if (cond_yes || cond_no) {
skip = 1, skip_lvl = lvl; 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 if (level == 0 && bstr_eatstart0(&str, "$>")) {
} else append_str(&ret, &ret_len, str);
p = str, l = 1, str++; break;
} else {
char c;
if (skip || l <= 0) // Other combinations, e.g. "$x", are added verbatim
continue; 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) { if (!skip)
size = pos + l + 512; MP_TARRAY_APPEND(NULL, ret, ret_len, c);
ret = realloc(ret, size);
} }
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; 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); 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, int m_property_int_ro(const m_option_t *prop, int action,
void *arg, int var) void *arg, int var)
{ {
switch (action) { if (action == M_PROPERTY_GET) {
case M_PROPERTY_GET:
if (!arg)
return 0;
*(int *)arg = var; *(int *)arg = var;
return 1; return M_PROPERTY_OK;
} }
return M_PROPERTY_NOT_IMPLEMENTED; 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, int m_property_float_ro(const m_option_t *prop, int action,
void *arg, float var) void *arg, float var)
{ {
switch (action) { if (action == M_PROPERTY_GET) {
case M_PROPERTY_GET:
if (!arg)
return 0;
*(float *)arg = var; *(float *)arg = var;
return 1; return M_PROPERTY_OK;
case M_PROPERTY_PRINT:
if (!arg)
return 0;
*(char **)arg = talloc_asprintf(NULL, "%.2f", var);
return 1;
} }
return M_PROPERTY_NOT_IMPLEMENTED; 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, int m_property_double_ro(const m_option_t *prop, int action,
void *arg, double var) void *arg, double var)
{ {
switch (action) { if (action == M_PROPERTY_GET) {
case M_PROPERTY_GET:
if (!arg)
return 0;
*(double *)arg = var; *(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_OK;
} }
return m_property_int_ro(prop, action, arg, rate); return M_PROPERTY_NOT_IMPLEMENTED;
} }

View File

@ -19,209 +19,119 @@
#ifndef MPLAYER_M_PROPERTY_H #ifndef MPLAYER_M_PROPERTY_H
#define MPLAYER_M_PROPERTY_H #define MPLAYER_M_PROPERTY_H
#include "m_option.h" #include <stdbool.h>
/// \defgroup Properties struct m_option;
///
/// 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).
///@{
/// \file extern const struct m_option_type m_option_type_dummy;
/// \defgroup PropertyActions Property actions enum mp_property_action {
/// \ingroup Properties // 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. // Get the current value.
/** \param arg Pointer to a variable of the right type. // arg: pointer to a variable of the type according to the property type
*/ M_PROPERTY_GET,
#define M_PROPERTY_GET 0
/// Get a string representing the current value. // Set a new value. The property wrapper will make sure that only valid
/** Set the variable to a newly allocated string or NULL. // values are set (e.g. according to the property type's min/max range).
* \param arg Pointer to a char* variable. // If unimplemented, the property is read-only.
*/ // arg: pointer to a variable of the type according to the property type
#define M_PROPERTY_PRINT 1 M_PROPERTY_SET,
/// Set a new value. // Get human readable string representing the current value.
/** The variable is updated to the value actually set. // If unimplemented, the property wrapper uses the property type as
* \param arg Pointer to a variable of the right type. // fallback.
*/ // arg: char**
#define M_PROPERTY_SET 2 M_PROPERTY_PRINT,
/// Set a new value from a string. // Switch the property up/down by a given value.
/** \param arg String containing the value. // If unimplemented, the property wrapper uses the property type as
*/ // fallback.
#define M_PROPERTY_PARSE 3 // arg: struct m_property_switch_arg*
M_PROPERTY_SWITCH,
/// Increment the current value. // Get a string containing a parsable representation.
/** The sign of the argument is also taken into account if applicable. // Can't be overridden by property implementations.
* \param arg Pointer to a variable of the right type or NULL. // arg: char**
*/ M_PROPERTY_GET_STRING,
#define M_PROPERTY_STEP_UP 4
/// Decrement the current value. // Set a new value from a string. The property wrapper parses this using the
/** The sign of the argument is also taken into account if applicable. // parse function provided by the property type.
* \param arg Pointer to a variable of the right type or NULL. // Can't be overridden by property implementations.
*/ // arg: char*
#define M_PROPERTY_STEP_DOWN 5 M_PROPERTY_SET_STRING,
/// Get a string containg a parsable representation. // Pass down an action to a sub-property.
/** Set the variable to a newly allocated string or NULL. // arg: struct m_property_action_arg*
* \param arg Pointer to a char* variable. M_PROPERTY_KEY_ACTION,
*/ };
#define M_PROPERTY_TO_STRING 6
/// Pass down an action to a sub-property. // Argument for M_PROPERTY_SWITCH
#define M_PROPERTY_KEY_ACTION 7 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. // Argument for M_PROPERTY_KEY_ACTION
#define M_PROPERTY_GET_TYPE 8 struct m_property_action_arg {
///@}
/// \defgroup PropertyActionsArg Property actions argument type
/// \ingroup Properties
/// \brief Types used as action argument.
///@{
/// Argument for \ref M_PROPERTY_KEY_ACTION
typedef struct {
const char* key; const char* key;
int action; int action;
void* arg; void* arg;
} m_property_action_t; };
///@} enum mp_property_return {
// Returned on success.
M_PROPERTY_OK = 1,
/// \defgroup PropertyActionsReturn Property actions return code // Returned on error.
/// \ingroup Properties M_PROPERTY_ERROR = 0,
/// \brief Return values for the control function.
///@{
/// Returned on success. // Returned when the property can't be used, for example video related
#define M_PROPERTY_OK 1 // properties while playing audio only.
M_PROPERTY_UNAVAILABLE = -1,
/// Returned on error. // Returned if the requested action is not implemented.
#define M_PROPERTY_ERROR 0 M_PROPERTY_NOT_IMPLEMENTED = -2,
/// \brief Returned when the property can't be used, for example something about // Returned when asking for a property that doesn't exist.
/// the subs while playing audio only M_PROPERTY_UNKNOWN = -3,
#define M_PROPERTY_UNAVAILABLE -1 };
/// Returned if the requested action is not implemented. // Access a property.
#define M_PROPERTY_NOT_IMPLEMENTED -2 // action: one of m_property_action
// ctx: opaque value passed through to property implementation
/// Returned when asking for a property that doesn't exist. // returns: one of mp_property_return
#define M_PROPERTY_UNKNOWN -3 int m_property_do(const struct m_option* prop_list, const char* property_name,
/// 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,
int action, void* arg, void *ctx); int action, void* arg, void *ctx);
/// Print a list of properties. // Print a list of properties.
void m_properties_print_help_list(const m_option_t* list); void m_properties_print_help_list(const struct m_option* list);
/// Expand a property string. // Expand a property string.
/** This function allows to print strings containing property values. // This function allows to print strings containing property values.
* ${NAME} is expanded to the value of property NAME or an empty // ${NAME} is expanded to the value of property NAME.
* string in case of error. $(NAME:STR) expand STR only if the property // If NAME starts with '=', use the raw value of the property.
* NAME is available. // ${NAME:STR} expands to the property, or STR if the property is not
* // available.
* \param prop_list An array of \ref m_option describing the available // ${?NAME:STR} expands to STR if the property is available.
* properties. // ${!NAME:STR} expands to STR if the property is not available.
* \param str The string to expand. // General syntax: "${" ["?" | "!"] ["="] NAME ":" STR "}"
* \return The newly allocated expanded string. // STR is recursively expanded using the same rules.
*/ // "$$" can be used to escape "$", and "$}" to escape "}".
char* m_properties_expand_string(const m_option_t* prop_list,char* str, void *ctx); // "$>" 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 // Trivial helpers for implementing properties.
int m_property_int_ro(const struct m_option* prop, int action, void* arg,
/// Do an action with an MPlayer property. int var);
int mp_property_do(const char* name,int action, void* val, void *ctx); int m_property_float_ro(const struct m_option* prop, int action, void* arg,
float var);
/// Get the value of a property as a string suitable for display in an UI. int m_property_double_ro(const struct m_option* prop, int action, void* arg,
char* mp_property_print(const char *name, void* ctx); double var);
/// \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);
///@}
///@}
#endif /* MPLAYER_M_PROPERTY_H */ #endif /* MPLAYER_M_PROPERTY_H */

View File

@ -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 */

View File

@ -77,6 +77,13 @@ struct chapter {
char *name; 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 { struct track {
enum stream_type type; enum stream_type type;
// The type specific ID, also called aid (audio), sid (subs), vid (video). // The type specific ID, also called aid (audio), sid (subs), vid (video).
@ -127,7 +134,7 @@ typedef struct MPContext {
char *terminal_osd_text; char *terminal_osd_text;
subtitle subs; // subtitle list used when reading subtitles from demuxer 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; unsigned int osd_visible;
int osd_function; int osd_function;

View File

@ -19,7 +19,6 @@
#ifndef MPLAYER_MP_OSD_H #ifndef MPLAYER_MP_OSD_H
#define MPLAYER_MP_OSD_H #define MPLAYER_MP_OSD_H
#define OSD_MSG_TV_CHANNEL 0
#define OSD_MSG_TEXT 1 #define OSD_MSG_TEXT 1
#define OSD_MSG_SUB_DELAY 2 #define OSD_MSG_SUB_DELAY 2
#define OSD_MSG_SPEED 3 #define OSD_MSG_SPEED 3
@ -27,12 +26,14 @@
#define OSD_MSG_BAR 5 #define OSD_MSG_BAR 5
#define OSD_MSG_PAUSE 6 #define OSD_MSG_PAUSE 6
#define OSD_MSG_RADIO_CHANNEL 7 #define OSD_MSG_RADIO_CHANNEL 7
#define OSD_MSG_TV_CHANNEL 8
/// Base id for messages generated from the commmand to property bridge. /// Base id for messages generated from the commmand to property bridge.
#define OSD_MSG_PROPERTY 0x100 #define OSD_MSG_PROPERTY 0x100
#define OSD_MSG_SUB_BASE 0x1000 #define OSD_MSG_SUB_BASE 0x1000
#define MAX_OSD_LEVEL 3 #define MAX_OSD_LEVEL 3
#define MAX_TERM_OSD_LEVEL 1 #define MAX_TERM_OSD_LEVEL 1
#define OSD_LEVEL_INVISIBLE 4
struct MPContext; 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_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 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 rm_osd_msg(struct MPContext *mpctx, int id);
void mp_show_osd_progression(struct MPContext *mpctx);
#endif /* MPLAYER_MP_OSD_H */ #endif /* MPLAYER_MP_OSD_H */

View File

@ -41,7 +41,7 @@
do { \ do { \
size_t nextidx_ = (nextidx); \ size_t nextidx_ = (nextidx); \
size_t nelems_ = MP_TALLOC_ELEMS(p); \ size_t nelems_ = MP_TALLOC_ELEMS(p); \
if (nextidx_ <= nelems_) \ if (nextidx_ >= nelems_) \
p = talloc_realloc_size((ctx), p, \ p = talloc_realloc_size((ctx), p, \
(nextidx_ + 1) * sizeof((p)[0]) * 2);\ (nextidx_ + 1) * sizeof((p)[0]) * 2);\
} while (0) } while (0)

252
mplayer.c
View File

@ -204,8 +204,8 @@ static const char av_desync_help_text[] = _(
static int drop_frame_cnt; // total number of dropped frames static int drop_frame_cnt; // total number of dropped frames
// seek: // seek:
static off_t seek_to_byte; static int64_t seek_to_byte;
static off_t step_sec; static double step_sec;
static m_time_size_t end_at = { .type = END_AT_NONE, .pos = 0 }; 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; double force_fps = 0;
static int force_srate = 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 = -1;
static int play_n_frames_mf = -1; static int play_n_frames_mf = -1;
@ -247,8 +246,6 @@ int use_filedir_conf;
#include "mpcommon.h" #include "mpcommon.h"
#include "command.h" #include "command.h"
#include "metadata.h"
static void reset_subtitles(struct MPContext *mpctx); static void reset_subtitles(struct MPContext *mpctx);
static void reinit_subs(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; 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) static void print_stream(struct MPContext *mpctx, struct track *t, int id)
{ {
struct sh_stream *s = t->stream; struct sh_stream *s = t->stream;
@ -1124,7 +998,7 @@ void init_vo_spudec(struct MPContext *mpctx)
if (vo_spudec != NULL) { if (vo_spudec != NULL) {
mpctx->initialized_flags |= INITIALIZED_SPUDEC; 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); mpctx);
} }
} }
@ -1164,6 +1038,30 @@ static void sadd_percentage(char *buf, int len, int percent) {
saddf(buf, len, " (%d%%)", 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) static void print_status(struct MPContext *mpctx, double a_pos, bool at_frame)
{ {
struct MPOpts *opts = &mpctx->opts; 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) if (opts->quiet)
return; return;
int width; if (opts->status_msg) {
char *line; char *r = mp_property_expand_string(mpctx, opts->status_msg);
get_screen_size(); write_status_line(mpctx, r);
if (screen_width > 0) talloc_free(r);
width = screen_width; return;
else }
width = 80;
#if defined(__MINGW32__) || defined(__CYGWIN__) // one additional char for the terminating null
/* Windows command line is broken (MinGW's rxvt works, but we int width = get_term_width() + 1;
* should not depend on that). */ char *line = malloc(width);
width--;
#endif
line = malloc(width + 1); // one additional char for the terminating null
line[0] = '\0'; line[0] = '\0';
// Playback status // Playback status
@ -1278,15 +1173,7 @@ static void print_status(struct MPContext *mpctx, double a_pos, bool at_frame)
#endif #endif
// end // end
if (erase_to_end_of_line) { write_status_line(mpctx, 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);
}
free(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, static void set_osd_msg_va(struct MPContext *mpctx, int id, int level, int time,
const char *fmt, va_list ap) const char *fmt, va_list ap)
{ {
if (level == OSD_LEVEL_INVISIBLE)
return;
mp_osd_msg_t *msg = add_osd_msg(mpctx, id, level, time); mp_osd_msg_t *msg = add_osd_msg(mpctx, id, level, time);
msg->msg = talloc_vasprintf(msg, fmt, ap); 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. * \brief Update the OSD message line.
* *
@ -1559,10 +1477,7 @@ static void update_osd_msg(struct MPContext *mpctx)
struct MPOpts *opts = &mpctx->opts; struct MPOpts *opts = &mpctx->opts;
struct osd_state *osd = mpctx->osd; struct osd_state *osd = mpctx->osd;
if (mpctx->add_osd_seek_info) { add_seek_osd_messages(mpctx);
set_osd_bar(mpctx, 0, "Position", 0, 100, get_percent_pos(mpctx));
mpctx->add_osd_seek_info = false;
}
// Look if we have a msg // Look if we have a msg
mp_osd_msg_t *msg = get_osd_msg(mpctx); 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) void reinit_audio_chain(struct MPContext *mpctx)
{ {
struct MPOpts *opts = &mpctx->opts; struct MPOpts *opts = &mpctx->opts;
@ -1916,7 +1822,7 @@ static int check_framedrop(struct MPContext *mpctx, double frame_time)
&& !mpctx->restart_playback) { && !mpctx->restart_playback) {
++drop_frame_cnt; ++drop_frame_cnt;
++dropped_frames; ++dropped_frames;
return frame_dropping; return mpctx->opts.frame_dropping;
} else } else
dropped_frames = 0; dropped_frames = 0;
} }
@ -2292,10 +2198,9 @@ static void vo_update_window_title(struct MPContext *mpctx)
{ {
if (!mpctx->video_out) if (!mpctx->video_out)
return; 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); talloc_free(mpctx->video_out->window_title);
mpctx->video_out->window_title = talloc_strdup(mpctx->video_out, title); mpctx->video_out->window_title = talloc_steal(mpctx, title);
free(title);
} }
int reinit_video_chain(struct MPContext *mpctx) 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; mpctx->initialized_flags |= INITIALIZED_VOBSUB;
// TODO: let frontend do the selection // TODO: let frontend do the selection
vobsub_set_from_lang(vo_vobsub, mpctx->opts.sub_lang); 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); mpctx);
for (int i = 0; i < vobsub_get_indexes_count(vo_vobsub); i++) { 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); encode_lavc_discontinuity(mpctx->encode_lavc_ctx);
#endif #endif
mpctx->add_osd_seek_info &= OSD_SEEK_INFO_EDITION;
m_config_enter_file_local(mpctx->mconfig); m_config_enter_file_local(mpctx->mconfig);
load_per_protocol_config(mpctx->mconfig, mpctx->filename); load_per_protocol_config(mpctx->mconfig, mpctx->filename);
@ -3828,7 +3735,8 @@ static void play_current_file(struct MPContext *mpctx)
} }
#ifdef CONFIG_ASS #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 #endif
if (mpctx->video_out && mpctx->video_out->config_ok) if (mpctx->video_out && mpctx->video_out->config_ok)
vo_control(mpctx->video_out, VOCTRL_RESUME, NULL); vo_control(mpctx->video_out, VOCTRL_RESUME, NULL);
@ -3996,9 +3904,9 @@ goto_enable_cache:
} }
if (opts->playing_msg) { 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); mp_msg(MSGT_CPLAYER, MSGL_INFO, "%s", msg);
free(msg); talloc_free(msg);
} }
// Disable the term OSD in verbose mode // Disable the term OSD in verbose mode

View File

@ -31,10 +31,6 @@ extern float audio_delay;
extern double force_fps; extern double force_fps;
extern int frame_dropping;
extern int auto_quality;
extern int vobsub_id; extern int vobsub_id;
struct MPContext; struct MPContext;

View File

@ -41,7 +41,6 @@ typedef struct MPOpts {
int osd_duration; int osd_duration;
int osd_fractions; int osd_fractions;
char *vobsub_name; char *vobsub_name;
int auto_quality;
int untimed; int untimed;
int loop_times; int loop_times;
int ordered_chapters; int ordered_chapters;
@ -61,9 +60,11 @@ typedef struct MPOpts {
float hr_seek_demuxer_offset; float hr_seek_demuxer_offset;
int autosync; int autosync;
int softsleep; int softsleep;
int frame_dropping;
int term_osd; int term_osd;
char *term_osd_esc; char *term_osd_esc;
char *playing_msg; char *playing_msg;
char *status_msg;
int player_idle_mode; int player_idle_mode;
int consolecontrols; int consolecontrols;
int doubleclick_time; int doubleclick_time;
@ -115,6 +116,7 @@ typedef struct MPOpts {
char *ass_color; char *ass_color;
char *ass_border_color; char *ass_border_color;
char *ass_styles_file; char *ass_styles_file;
int ass_style_override;
int ass_hinting; int ass_hinting;
struct lavc_param { struct lavc_param {
int workaround_bugs; int workaround_bugs;

View File

@ -27,10 +27,9 @@
#include "talloc.h" #include "talloc.h"
#include "screenshot.h" #include "screenshot.h"
#include "mp_core.h" #include "mp_core.h"
#include "m_property.h" #include "command.h"
#include "bstr.h" #include "bstr.h"
#include "mp_msg.h" #include "mp_msg.h"
#include "metadata.h"
#include "path.h" #include "path.h"
#include "libmpcodecs/mp_image.h" #include "libmpcodecs/mp_image.h"
#include "libmpcodecs/dec_video.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); 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 #ifdef _WIN32
#define ILLEGAL_FILENAME_CHARS "?\"/\\<>*|:" #define ILLEGAL_FILENAME_CHARS "?\"/\\<>*|:"
#else #else
@ -154,14 +140,13 @@ static char *create_fname(struct MPContext *mpctx, char *template,
} }
case 'f': case 'f':
case 'F': { case 'F': {
char *video_file = get_metadata(mpctx, META_NAME); char *video_file = mp_basename(mpctx->filename);
if (video_file) { if (video_file) {
char *name = video_file; char *name = video_file;
if (fmt == 'F') if (fmt == 'F')
name = stripext(res, video_file); name = stripext(res, video_file);
append_filename(&res, name); append_filename(&res, name);
} }
talloc_free(video_file);
break; break;
} }
case 'p': case 'p':
@ -188,11 +173,13 @@ static char *create_fname(struct MPContext *mpctx, char *template,
if (!end) if (!end)
goto error_exit; goto error_exit;
struct bstr prop = bstr_splice(bstr0(template), 0, end - template); struct bstr prop = bstr_splice(bstr0(template), 0, end - template);
template = end + 1; char *tmp = talloc_asprintf(NULL, "${%.*s}", BSTR_P(prop));
char *s = do_format_property(mpctx, prop); char *s = mp_property_expand_string(mpctx, tmp);
talloc_free(tmp);
if (s) if (s)
append_filename(&res, s); append_filename(&res, s);
talloc_free(s); talloc_free(s);
template = end + 1;
break; break;
} }
case '%': case '%':

View File

@ -47,7 +47,7 @@ ASS_Track *mp_ass_default_track(ASS_Library *library, struct MPOpts *opts)
track->PlayResY = 288; track->PlayResY = 288;
track->WrapStyle = 0; 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); ass_read_styles(track, opts->ass_styles_file, sub_cp);
if (track->n_styles == 0) { if (track->n_styles == 0) {
@ -95,7 +95,9 @@ ASS_Track *mp_ass_default_track(ASS_Library *library, struct MPOpts *opts)
style->ScaleY = 1.; style->ScaleY = 1.;
} }
ass_process_force_style(track); if (opts->ass_style_override)
ass_process_force_style(track);
return 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, void mp_ass_configure(ASS_Renderer *priv, struct MPOpts *opts,
struct mp_eosd_res *dim, bool unscaled) struct mp_eosd_res *dim, bool unscaled)
{ {
int hinting;
ass_set_frame_size(priv, dim->w, dim->h); ass_set_frame_size(priv, dim->w, dim->h);
ass_set_margins(priv, dim->mt, dim->mb, dim->ml, dim->mr); 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); int set_use_margins = 0;
if (!unscaled && (opts->ass_hinting & 4)) int set_sub_pos = 0;
hinting = 0; float set_line_spacing = 0;
else float set_font_scale = 1;
hinting = opts->ass_hinting & 3; int set_hinting = 0;
ass_set_hinting(priv, hinting); if (opts->ass_style_override) {
ass_set_line_spacing(priv, opts->ass_line_spacing); 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) void mp_ass_configure_fonts(ASS_Renderer *priv)

View File

@ -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; 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); 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, ""); char *text = talloc_strdup(NULL, "");

View File

@ -135,7 +135,9 @@ static void get_bitmaps(struct sh_sub *sh, struct osd_state *osd,
return; return;
double scale = osd->normal_scale; 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; scale = osd->vsfilter_scale;
ASS_Renderer *renderer = osd->ass_renderer; ASS_Renderer *renderer = osd->ass_renderer;
mp_ass_configure(renderer, opts, &osd->dim, osd->unscaled); mp_ass_configure(renderer, opts, &osd->dim, osd->unscaled);

View File

@ -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) char *talloc_strndup_append(char *s, const char *a, size_t n)
{ {
if (unlikely(!s)) { if (unlikely(!s)) {
return talloc_strdup(NULL, a); return talloc_strndup(NULL, a, n);
} }
if (unlikely(!a)) { if (unlikely(!a)) {
@ -1440,7 +1440,7 @@ char *talloc_strndup_append_buffer(char *s, const char *a, size_t n)
size_t slen; size_t slen;
if (unlikely(!s)) { if (unlikely(!s)) {
return talloc_strdup(NULL, a); return talloc_strndup(NULL, a, n);
} }
if (unlikely(!a)) { if (unlikely(!a)) {