diff options
Diffstat (limited to '')
73 files changed, 5261 insertions, 0 deletions
diff --git a/TOOLS/docutils-wrapper.py b/TOOLS/docutils-wrapper.py new file mode 100755 index 0000000..31ba976 --- /dev/null +++ b/TOOLS/docutils-wrapper.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 +""" +Wrapper around docutils rst2x commands, +converting their dependency files to a format understood by meson/ninja. +""" + +# +# This file is part of mpv. +# +# mpv is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# mpv is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with mpv. If not, see <http://www.gnu.org/licenses/>. +# + +import os +import subprocess +import sys + + +def convert_depfile(output, depfile): + with open(depfile, 'r') as f: + deps = f.readlines() + + with open(depfile, 'w') as f: + f.write(os.path.abspath(output)) + f.write(': \\\n') + for dep in deps: + dep = dep[:-1] + f.write('\t') + f.write(os.path.abspath(dep)) + f.write(' \\\n') + +def remove(path): + try: + os.remove(path) + except FileNotFoundError: + pass + +argv = sys.argv[1:] + +depfile = None +output = argv[-1] + +for opt, optarg in zip(argv, argv[1:]): + if opt == '--record-dependencies': + depfile = optarg + +try: + proc = subprocess.run(argv, check=True) + if depfile is not None: + convert_depfile(output, depfile) +except: + remove(output) + if depfile is not None: + remove(depfile) + sys.exit(1) + +sys.exit(proc.returncode) diff --git a/TOOLS/dylib-unhell.py b/TOOLS/dylib-unhell.py new file mode 100755 index 0000000..c41d200 --- /dev/null +++ b/TOOLS/dylib-unhell.py @@ -0,0 +1,181 @@ +#!/usr/bin/env python3 + +import re +import os +import sys +import shutil +import subprocess +from functools import partial + +sys_re = re.compile("^/System") +usr_re = re.compile("^/usr/lib/") +exe_re = re.compile("@executable_path") + +def is_user_lib(objfile, libname): + return not sys_re.match(libname) and \ + not usr_re.match(libname) and \ + not exe_re.match(libname) and \ + not "libobjc." in libname and \ + not "libSystem." in libname and \ + not "libc." in libname and \ + not "libgcc." in libname and \ + not os.path.basename(libname) == 'Python' and \ + not os.path.basename(objfile) in libname and \ + not "libswift" in libname + +def otool(objfile, rapths): + command = "otool -L '%s' | grep -e '\t' | awk '{ print $1 }'" % objfile + output = subprocess.check_output(command, shell = True, universal_newlines=True) + libs = set(filter(partial(is_user_lib, objfile), output.split())) + + libs_resolved = set() + libs_relative = set() + for lib in libs: + lib_path = resolve_lib_path(objfile, lib, rapths) + libs_resolved.add(lib_path) + if lib_path != lib: + libs_relative.add(lib) + + return libs_resolved, libs_relative + +def get_rapths(objfile): + rpaths = [] + command = "otool -l '%s' | grep -A2 LC_RPATH | grep path" % objfile + pathRe = re.compile("^\s*path (.*) \(offset \d*\)$") + + try: + result = subprocess.check_output(command, shell = True, universal_newlines=True) + except: + return rpaths + + for line in result.splitlines(): + rpaths.append(pathRe.search(line).group(1).strip()) + + return rpaths + +def get_rpaths_dev_tools(binary): + command = "otool -l '%s' | grep -A2 LC_RPATH | grep path | grep \"Xcode\|CommandLineTools\"" % binary + result = subprocess.check_output(command, shell = True, universal_newlines=True) + pathRe = re.compile("^\s*path (.*) \(offset \d*\)$") + output = [] + + for line in result.splitlines(): + output.append(pathRe.search(line).group(1).strip()) + + return output + +def resolve_lib_path(objfile, lib, rapths): + if os.path.exists(lib): + return lib + + if lib.startswith('@rpath/'): + lib = lib[len('@rpath/'):] + for rpath in rapths: + lib_path = os.path.join(rpath, lib) + if os.path.exists(lib_path): + return lib_path + elif lib.startswith('@loader_path/'): + lib = lib[len('@loader_path/'):] + lib_path = os.path.normpath(os.path.join(objfile, lib)) + if os.path.exists(lib_path): + return lib_path + + raise Exception('Could not resolve library: ' + lib) + +def install_name_tool_change(old, new, objfile): + subprocess.call(["install_name_tool", "-change", old, new, objfile], stderr=subprocess.DEVNULL) + +def install_name_tool_id(name, objfile): + subprocess.call(["install_name_tool", "-id", name, objfile], stderr=subprocess.DEVNULL) + +def install_name_tool_add_rpath(rpath, binary): + subprocess.call(["install_name_tool", "-add_rpath", rpath, binary]) + +def install_name_tool_delete_rpath(rpath, binary): + subprocess.call(["install_name_tool", "-delete_rpath", rpath, binary]) + +def libraries(objfile, result = dict(), result_relative = set(), rapths = []): + rapths = get_rapths(objfile) + rapths + libs_list, libs_relative = otool(objfile, rapths) + result[objfile] = libs_list + result_relative |= libs_relative + + for lib in libs_list: + if lib not in result: + libraries(lib, result, result_relative, rapths) + + return result, result_relative + +def lib_path(binary): + return os.path.join(os.path.dirname(binary), 'lib') + +def lib_name(lib): + return os.path.join("@executable_path", "lib", os.path.basename(lib)) + +def process_libraries(libs_dict, libs_dyn, binary): + libs_set = set(libs_dict) + # Remove binary from libs_set to prevent a duplicate of the binary being + # added to the libs directory. + libs_set.remove(binary) + + for src in libs_set: + name = lib_name(src) + dst = os.path.join(lib_path(binary), os.path.basename(src)) + + shutil.copy(src, dst) + os.chmod(dst, 0o755) + install_name_tool_id(name, dst) + + if src in libs_dict[binary]: + install_name_tool_change(src, name, binary) + + for p in libs_set: + if p in libs_dict[src]: + install_name_tool_change(p, lib_name(p), dst) + + for lib in libs_dyn: + install_name_tool_change(lib, lib_name(lib), dst) + + for lib in libs_dyn: + install_name_tool_change(lib, lib_name(lib), binary) + +def process_swift_libraries(binary): + command = ['xcrun', '--find', 'swift-stdlib-tool'] + swiftStdlibTool = subprocess.check_output(command, universal_newlines=True).strip() + # from xcode11 on the dynamic swift libs reside in a separate directory from + # the std one, might need versioned paths for future swift versions + swiftLibPath = os.path.join(swiftStdlibTool, '../../lib/swift-5.0/macosx') + swiftLibPath = os.path.abspath(swiftLibPath) + + command = [swiftStdlibTool, '--copy', '--platform', 'macosx', '--scan-executable', binary, '--destination', lib_path(binary)] + + if os.path.exists(swiftLibPath): + command.extend(['--source-libraries', swiftLibPath]) + + subprocess.check_output(command, universal_newlines=True) + + print(">> setting additional rpath for swift libraries") + install_name_tool_add_rpath("@executable_path/lib", binary) + +def remove_dev_tools_rapths(binary): + for path in get_rpaths_dev_tools(binary): + install_name_tool_delete_rpath(path, binary) + +def main(): + binary = os.path.abspath(sys.argv[1]) + if not os.path.exists(lib_path(binary)): + os.makedirs(lib_path(binary)) + print(">> gathering all linked libraries") + libs, libs_rel = libraries(binary) + + print(">> copying and processing all linked libraries") + process_libraries(libs, libs_rel, binary) + + print(">> removing rpath definitions towards dev tools") + remove_dev_tools_rapths(binary) + + print(">> copying and processing swift libraries") + process_swift_libraries(binary) + +if __name__ == "__main__": + main() diff --git a/TOOLS/file2string.py b/TOOLS/file2string.py new file mode 100755 index 0000000..5b1c4a9 --- /dev/null +++ b/TOOLS/file2string.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 + +# Convert the contents of a file into a C string constant. +# Note that the compiler will implicitly add an extra 0 byte at the end +# of every string, so code using the string may need to remove that to get +# the exact contents of the original file. + +# +# This file is part of mpv. +# +# mpv is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# mpv is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with mpv. If not, see <http://www.gnu.org/licenses/>. +# + +import sys + +def file2string(infilename, infile, outfile): + outfile.write("// Generated from %s\n\n" % infilename) + + conv = ["\\%03o" % c for c in range(256)] + safe_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" \ + "0123456789!#%&'()*+,-./:;<=>[]^_{|}~ " + + for c in safe_chars: + conv[ord(c)] = c + for c, esc in ("\nn", "\tt", r"\\", '""'): + conv[ord(c)] = '\\' + esc + for line in infile: + outfile.write('"' + ''.join(conv[c] for c in line) + '"\n') + +if __name__ == "__main__": + outfile = open(sys.argv[2], "w") + with open(sys.argv[1], 'rb') as infile: + file2string(sys.argv[1], infile, outfile) diff --git a/TOOLS/gen-osd-font.sh b/TOOLS/gen-osd-font.sh new file mode 100755 index 0000000..6838692 --- /dev/null +++ b/TOOLS/gen-osd-font.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +# This script is expected to be called as TOOLS/gen-osd-font.sh (it will access +# TOOLS/mpv-osd-symbols.sfdir), and it will write sub/osd_font.otf. + +# Needs fontforge with python scripting + +fontforge -lang=py -c 'f=open(argv[1]); f.generate(argv[2])' \ + TOOLS/mpv-osd-symbols.sfdir sub/osd_font.otf diff --git a/TOOLS/idet.sh b/TOOLS/idet.sh new file mode 100755 index 0000000..7fb51c1 --- /dev/null +++ b/TOOLS/idet.sh @@ -0,0 +1,158 @@ +#!/bin/sh + +: "${MPV:=mpv}" +: "${ILDETECT_MPV:=$MPV}" +: "${ILDETECT_MPVFLAGS:=--start=35% --length=35}" +: "${ILDETECT_DRY_RUN:=}" +: "${ILDETECT_QUIET:=}" +: "${ILDETECT_RUN_INTERLACED_ONLY:=}" +: "${ILDETECT_FORCE_RUN:=}" + +# This script uses ffmpeg's "idet" filter for interlace detection. In the +# long run this should replace ildetect.sh+ildetect.so. + +# exit status: +# 0 progressive +# 1 telecine +# 2 interlaced +# 8 unknown +# 16 detect fail +# 17+ mpv's status | 16 + +testfun() +{ + $ILDETECT_MPV "$@" \ + --vf-add=lavfi="[idet]" --msg-level=ffmpeg=v \ + --o= --vo=null --no-audio --untimed \ + $ILDETECT_MPVFLAGS \ + | { if [ -n "$ILDETECT_QUIET" ]; then cat; else tee /dev/stderr; fi } \ + | grep "Parsed_idet_0: Multi frame detection: " +} + +judge() +{ + tff=0 + bff=0 + progressive=0 + undetermined=0 + while read -r _ _ _ _ _ _ tff1 _ bff1 _ progressive1 _ undetermined1 _; do + case "$tff1$bff1$progressive1$undetermined1" in + *[!0-9]*) + printf >&2 'ERROR: Unrecognized idet output: %s\n' "$out" + exit 16 + ;; + esac + tff=$((tff + tff1)) + bff=$((bff + bff1)) + progressive=$((progressive + progressive1)) + undetermined=$((undetermined + undetermined1)) + done <<EOF +$(testfun "$@" | sed 's/:/: /g') +EOF + + interlaced=$((bff + tff)) + determined=$((interlaced + progressive)) + + if [ "$undetermined" -gt "$determined" ] || [ "$determined" -lt 250 ]; then + echo >&2 "ERROR: Less than 50% or 250 frames are determined." + [ -n "$ILDETECT_FORCE_RUN" ] || exit 8 + echo >&2 "Assuming interlacing." + if [ "$tff" -gt $((bff * 10)) ]; then + verdict="interlaced-tff" + elif [ "$bff" -gt $((tff * 10)) ]; then + verdict="interlaced-bff" + else + verdict="interlaced" + fi + elif [ $((interlaced * 20)) -gt "$progressive" ]; then + # At least 5% of the frames are interlaced! + if [ "$tff" -gt $((bff * 10)) ]; then + verdict="interlaced-tff" + elif [ "$bff" -gt $((tff * 10)) ]; then + verdict="interlaced-bff" + else + echo >&2 "ERROR: Content is interlaced, but can't determine field order." + [ -n "$ILDETECT_FORCE_RUN" ] || exit 8 + echo >&2 "Assuming interlacing with default field order." + verdict="interlaced" + fi + else + # Likely progressive + verdict="progressive" + fi + + printf '%s\n' "$verdict" +} + +judge "$@" --vf-clr +case "$verdict" in + progressive) + [ -n "$ILDETECT_DRY_RUN" ] || \ + [ -n "$ILDETECT_RUN_INTERLACED_ONLY" ] || \ + $ILDETECT_MPV "$@" + r=$? + [ $r -eq 0 ] || exit $((r | 16)) + exit 0 + ;; + interlaced-tff) + judge "$@" --vf-clr --vf-pre=lavfi=\[setfield=tff\],pullup + case "$verdict" in + progressive) + [ -n "$ILDETECT_DRY_RUN" ] || \ + $ILDETECT_MPV "$@" --vf-pre=lavfi=\[setfield=tff\],pullup + r=$? + [ $r -eq 0 ] || exit $((r | 16)) + exit 1 + ;; + *) + [ -n "$ILDETECT_DRY_RUN" ] || \ + $ILDETECT_MPV "$@" --vf-pre=lavfi=\[setfield=tff\],yadif + r=$? + [ $r -eq 0 ] || exit $((r | 16)) + exit 2 + ;; + esac + ;; + interlaced-bff) + judge "$@" --vf-clr --vf-pre=lavfi=\[setfield=bff\],pullup + case "$verdict" in + progressive) + [ -n "$ILDETECT_DRY_RUN" ] || \ + $ILDETECT_MPV "$@" --vf-pre=lavfi=\[setfield=bff\],pullup + r=$? + [ $r -eq 0 ] || exit $((r | 16)) + exit 1 + ;; + *) + [ -n "$ILDETECT_DRY_RUN" ] || \ + $ILDETECT_MPV "$@" --vf-pre=lavfi=\[setfield=bff\],yadif + r=$? + [ $r -eq 0 ] || exit $((r | 16)) + exit 2 + ;; + esac + ;; + interlaced) + judge "$@" --vf-clr --vf-pre=pullup + case "$verdict" in + progressive) + [ -n "$ILDETECT_DRY_RUN" ] || \ + $ILDETECT_MPV "$@" --vf-pre=pullup + r=$? + [ $r -eq 0 ] || exit $((r | 16)) + exit 1 + ;; + *) + [ -n "$ILDETECT_DRY_RUN" ] || \ + $ILDETECT_MPV "$@" --vf-pre=yadif + r=$? + [ $r -eq 0 ] || exit $((r | 16)) + exit 2 + ;; + esac + ;; + *) + echo >&2 "ERROR: Internal error." + exit 16 + ;; +esac diff --git a/TOOLS/lua/README.md b/TOOLS/lua/README.md new file mode 100644 index 0000000..79b67d6 --- /dev/null +++ b/TOOLS/lua/README.md @@ -0,0 +1,20 @@ +mpv lua scripts +=============== + +The lua scripts in this folder can be loaded on a one-time basis by +adding the option + + --script=/path/to/script.lua + +to mpv's command line. + +Where appropriate, they may also be placed in ~/.config/mpv/scripts/ from +where they will be automatically loaded when mpv starts. + +This is only a small selection of internally maintained scripts. Some of them +are just for testing mpv internals, or serve as examples. An extensive +user-edited list of 3rd party scripts is available here: + + https://github.com/mpv-player/mpv/wiki/User-Scripts + +(Anyone can add their own scripts to that list.) diff --git a/TOOLS/lua/acompressor.lua b/TOOLS/lua/acompressor.lua new file mode 100644 index 0000000..6a69140 --- /dev/null +++ b/TOOLS/lua/acompressor.lua @@ -0,0 +1,155 @@ +-- This script adds control to the dynamic range compression ffmpeg +-- filter including key bindings for adjusting parameters. +-- +-- See https://ffmpeg.org/ffmpeg-filters.html#acompressor for explanation +-- of the parameters. + +local mp = require 'mp' +local options = require 'mp.options' + +local o = { + default_enable = false, + show_osd = true, + osd_timeout = 4000, + filter_label = mp.get_script_name(), + + key_toggle = 'n', + key_increase_threshold = 'F1', + key_decrease_threshold = 'Shift+F1', + key_increase_ratio = 'F2', + key_decrease_ratio = 'Shift+F2', + key_increase_knee = 'F3', + key_decrease_knee = 'Shift+F3', + key_increase_makeup = 'F4', + key_decrease_makeup = 'Shift+F4', + key_increase_attack = 'F5', + key_decrease_attack = 'Shift+F5', + key_increase_release = 'F6', + key_decrease_release = 'Shift+F6', + + default_threshold = -25.0, + default_ratio = 3.0, + default_knee = 2.0, + default_makeup = 8.0, + default_attack = 20.0, + default_release = 250.0, + + step_threshold = -2.5, + step_ratio = 1.0, + step_knee = 1.0, + step_makeup = 1.0, + step_attack = 10.0, + step_release = 10.0, +} +options.read_options(o) + +local params = { + { name = 'attack', min=0.01, max=2000, hide_default=true, dB='' }, + { name = 'release', min=0.01, max=9000, hide_default=true, dB='' }, + { name = 'threshold', min= -30, max= 0, hide_default=false, dB='dB' }, + { name = 'ratio', min= 1, max= 20, hide_default=false, dB='' }, + { name = 'knee', min= 1, max= 10, hide_default=true, dB='dB' }, + { name = 'makeup', min= 0, max= 24, hide_default=false, dB='dB' }, +} + +local function parse_value(value) + -- Using nil here because tonumber differs between lua 5.1 and 5.2 when parsing fractions in combination with explicit base argument set to 10. + -- And we can't omit it because gsub returns 2 values which would get unpacked and cause more problems. Gotta love scripting languages. + return tonumber(value:gsub('dB$', ''), nil) +end + +local function format_value(value, dB) + return string.format('%g%s', value, dB) +end + +local function show_osd(filter) + if not o.show_osd then + return + end + + if not filter.enabled then + mp.commandv('show-text', 'Dynamic range compressor: disabled', o.osd_timeout) + return + end + + local pretty = {} + for _,param in ipairs(params) do + local value = parse_value(filter.params[param.name]) + if not (param.hide_default and value == o['default_' .. param.name]) then + pretty[#pretty+1] = string.format('%s: %g%s', param.name:gsub("^%l", string.upper), value, param.dB) + end + end + + if #pretty == 0 then + pretty = '' + else + pretty = '\n(' .. table.concat(pretty, ', ') .. ')' + end + + mp.commandv('show-text', 'Dynamic range compressor: enabled' .. pretty, o.osd_timeout) +end + +local function get_filter() + local af = mp.get_property_native('af', {}) + + for i = 1, #af do + if af[i].label == o.filter_label then + return af, i + end + end + + af[#af+1] = { + name = 'acompressor', + label = o.filter_label, + enabled = false, + params = {}, + } + + for _,param in pairs(params) do + af[#af].params[param.name] = format_value(o['default_' .. param.name], param.dB) + end + + return af, #af +end + +local function toggle_acompressor() + local af, i = get_filter() + af[i].enabled = not af[i].enabled + mp.set_property_native('af', af) + show_osd(af[i]) +end + +local function update_param(name, increment) + for _,param in pairs(params) do + if param.name == string.lower(name) then + local af, i = get_filter() + local value = parse_value(af[i].params[param.name]) + value = math.max(param.min, math.min(value + increment, param.max)) + af[i].params[param.name] = format_value(value, param.dB) + af[i].enabled = true + mp.set_property_native('af', af) + show_osd(af[i]) + return + end + end + + mp.msg.error('Unknown parameter "' .. name .. '"') +end + +mp.add_key_binding(o.key_toggle, "toggle-acompressor", toggle_acompressor) +mp.register_script_message('update-param', update_param) + +for _,param in pairs(params) do + for direction,step in pairs({increase=1, decrease=-1}) do + mp.add_key_binding(o['key_' .. direction .. '_' .. param.name], + 'acompressor-' .. direction .. '-' .. param.name, + function() update_param(param.name, step*o['step_' .. param.name]); end, + { repeatable = true }) + end +end + +if o.default_enable then + local af, i = get_filter() + af[i].enabled = true + mp.set_property_native('af', af) +end diff --git a/TOOLS/lua/ao-null-reload.lua b/TOOLS/lua/ao-null-reload.lua new file mode 100644 index 0000000..5b2330b --- /dev/null +++ b/TOOLS/lua/ao-null-reload.lua @@ -0,0 +1,20 @@ +-- Handles the edge case where previous attempts to init audio have failed, but +-- might start working due to a newly added device. This is required in +-- particular for ao=wasapi, since the internal IMMNotificationClient code that +-- normally triggers ao-reload will not be running in this case. + +function do_reload() + mp.command("ao-reload") + reloading = nil +end + +function on_audio_device_list_change() + if mp.get_property("current-ao") == "null" and not reloading then + mp.msg.verbose("audio-device-list changed: reloading audio") + -- avoid calling ao-reload too often + reloading = mp.add_timeout(0.5, do_reload) + end +end + +mp.set_property("options/audio-fallback-to-null", "yes") +mp.observe_property("audio-device-list", "native", on_audio_device_list_change) diff --git a/TOOLS/lua/audio-hotplug-test.lua b/TOOLS/lua/audio-hotplug-test.lua new file mode 100644 index 0000000..8dedc68 --- /dev/null +++ b/TOOLS/lua/audio-hotplug-test.lua @@ -0,0 +1,8 @@ +local utils = require("mp.utils") + +mp.observe_property("audio-device-list", "native", function(name, val) + print("Audio device list changed:") + for index, e in ipairs(val) do + print(" - '" .. e.name .. "' (" .. e.description .. ")") + end +end) diff --git a/TOOLS/lua/autocrop.lua b/TOOLS/lua/autocrop.lua new file mode 100644 index 0000000..b9e1120 --- /dev/null +++ b/TOOLS/lua/autocrop.lua @@ -0,0 +1,298 @@ +--[[ +This script uses the lavfi cropdetect filter and the video-crop property to +automatically crop the currently playing video with appropriate parameters. + +It automatically crops the video when playback starts. + +You can also manually crop the video by pressing the "C" (shift+c) key. +Pressing it again undoes the crop. + +The workflow is as follows: First, it inserts the cropdetect filter. After +<detect_seconds> (default is 1) seconds, it then sets video-crop based on the +vf-metadata values gathered by cropdetect. The cropdetect filter is removed +after video-crop is set as it is no longer needed. + +Since the crop parameters are determined from the 1 second of video between +inserting the cropdetect filter and setting video-crop, the "C" key should be +pressed at a position in the video where the crop region is unambiguous (i.e., +not a black frame, black background title card, or dark scene). + +If non-copy-back hardware decoding is in use, hwdec is temporarily disabled for +the duration of cropdetect as the filter would fail otherwise. + +These are the default options. They can be overridden by adding +script-opts-append=autocrop-<parameter>=<value> to mpv.conf. +--]] +local options = { + -- Whether to automatically apply crop at the start of playback. If you + -- don't want to crop automatically, add + -- script-opts-append=autocrop-auto=no to mpv.conf. + auto = true, + -- Delay before starting crop in auto mode. You can try to increase this + -- value to avoid dark scenes or fade ins at beginning. Automatic cropping + -- will not occur if the value is larger than the remaining playback time. + auto_delay = 4, + -- Black threshold for cropdetect. Smaller values will generally result in + -- less cropping. See limit of + -- https://ffmpeg.org/ffmpeg-filters.html#cropdetect + detect_limit = "24/255", + -- The value which the width/height should be divisible by. Smaller + -- values have better detection accuracy. If you have problems with + -- other filters, you can try to set it to 4 or 16. See round of + -- https://ffmpeg.org/ffmpeg-filters.html#cropdetect + detect_round = 2, + -- The ratio of the minimum clip size to the original. A number from 0 to + -- 1. If the picture is over cropped, try adjusting this value. + detect_min_ratio = 0.5, + -- How long to gather cropdetect data. Increasing this may be desirable to + -- allow cropdetect more time to collect data. + detect_seconds = 1, + -- Whether the OSD shouldn't be used when cropdetect and video-crop are + -- applied and removed. + suppress_osd = false, +} + +require "mp.options".read_options(options) + +local cropdetect_label = mp.get_script_name() .. "-cropdetect" + +timers = { + auto_delay = nil, + detect_crop = nil +} + +local hwdec_backup + +local command_prefix = options.suppress_osd and 'no-osd' or '' + +function is_enough_time(seconds) + + -- Plus 1 second for deviation. + local time_needed = seconds + 1 + local playtime_remaining = mp.get_property_native("playtime-remaining") + + return playtime_remaining and time_needed < playtime_remaining +end + +function is_cropable(time_needed) + if mp.get_property_native('current-tracks/video/image') ~= false then + mp.msg.warn("autocrop only works for videos.") + return false + end + + if not is_enough_time(time_needed) then + mp.msg.warn("Not enough time to detect crop.") + return false + end + + return true +end + +function remove_cropdetect() + for _, filter in pairs(mp.get_property_native("vf")) do + if filter.label == cropdetect_label then + mp.command( + string.format("%s vf remove @%s", command_prefix, filter.label)) + + return + end + end +end + +function restore_hwdec() + if hwdec_backup then + mp.set_property("hwdec", hwdec_backup) + hwdec_backup = nil + end +end + +function cleanup() + remove_cropdetect() + + -- Kill all timers. + for index, timer in pairs(timers) do + if timer then + timer:kill() + timers[index] = nil + end + end + + restore_hwdec() +end + +function detect_crop() + local time_needed = options.detect_seconds + + if not is_cropable(time_needed) then + return + end + + local hwdec_current = mp.get_property("hwdec-current") + if hwdec_current:find("-copy$") == nil and hwdec_current ~= "no" and + hwdec_current ~= "crystalhd" and hwdec_current ~= "rkmpp" then + hwdec_backup = mp.get_property("hwdec") + mp.set_property("hwdec", "no") + end + + -- Insert the cropdetect filter. + local limit = options.detect_limit + local round = options.detect_round + + mp.command( + string.format( + '%s vf pre @%s:cropdetect=limit=%s:round=%d:reset=0', + command_prefix, cropdetect_label, limit, round + ) + ) + + -- Wait to gather data. + timers.detect_crop = mp.add_timeout(time_needed, detect_end) +end + +function detect_end() + + -- Get the metadata and remove the cropdetect filter. + local cropdetect_metadata = mp.get_property_native( + "vf-metadata/" .. cropdetect_label) + remove_cropdetect() + + -- Remove the timer of detect crop. + if timers.detect_crop then + timers.detect_crop:kill() + timers.detect_crop = nil + end + + restore_hwdec() + + local meta = {} + + -- Verify the existence of metadata. + if cropdetect_metadata then + meta = { + w = cropdetect_metadata["lavfi.cropdetect.w"], + h = cropdetect_metadata["lavfi.cropdetect.h"], + x = cropdetect_metadata["lavfi.cropdetect.x"], + y = cropdetect_metadata["lavfi.cropdetect.y"], + } + else + mp.msg.error("No crop data.") + mp.msg.info("Was the cropdetect filter successfully inserted?") + mp.msg.info("Does your version of ffmpeg/libav support AVFrame metadata?") + return + end + + -- Verify that the metadata meets the requirements and convert it. + if meta.w and meta.h and meta.x and meta.y then + local width = mp.get_property_native("width") + local height = mp.get_property_native("height") + + meta = { + w = tonumber(meta.w), + h = tonumber(meta.h), + x = tonumber(meta.x), + y = tonumber(meta.y), + min_w = width * options.detect_min_ratio, + min_h = height * options.detect_min_ratio, + max_w = width, + max_h = height + } + else + mp.msg.error("Got empty crop data.") + mp.msg.info("You might need to increase detect_seconds.") + end + + apply_crop(meta) +end + +function apply_crop(meta) + + -- Verify if it is necessary to crop. + local is_effective = meta.w and meta.h and meta.x and meta.y and + (meta.x > 0 or meta.y > 0 + or meta.w < meta.max_w or meta.h < meta.max_h) + + -- Verify it is not over cropped. + local is_excessive = false + if is_effective and (meta.w < meta.min_w or meta.h < meta.min_h) then + mp.msg.info("The area to be cropped is too large.") + mp.msg.info("You might need to decrease detect_min_ratio.") + is_excessive = true + end + + if not is_effective or is_excessive then + -- Clear any existing crop. + mp.command(string.format("%s set file-local-options/video-crop ''", command_prefix)) + return + end + + -- Apply crop. + mp.command(string.format("%s set file-local-options/video-crop %sx%s+%s+%s", + command_prefix, meta.w, meta.h, meta.x, meta.y)) +end + +function on_start() + + -- Clean up at the beginning. + cleanup() + + -- If auto is not true, exit. + if not options.auto then + return + end + + -- If it is the beginning, wait for detect_crop + -- after auto_delay seconds, otherwise immediately. + local playback_time = mp.get_property_native("playback-time") + local is_delay_needed = playback_time + and options.auto_delay > playback_time + + if is_delay_needed then + + -- Verify if there is enough time for autocrop. + local time_needed = options.auto_delay + options.detect_seconds + + if not is_cropable(time_needed) then + return + end + + timers.auto_delay = mp.add_timeout(time_needed, + function() + detect_crop() + + -- Remove the timer of auto delay. + timers.auto_delay:kill() + timers.auto_delay = nil + end + ) + else + detect_crop() + end +end + +function on_toggle() + + -- If it is during auto_delay, kill the timer. + if timers.auto_delay then + timers.auto_delay:kill() + timers.auto_delay = nil + end + + -- Cropped => Remove it. + if mp.get_property("video-crop") ~= "" then + mp.command(string.format("%s set file-local-options/video-crop ''", command_prefix)) + return + end + + -- Detecting => Leave it. + if timers.detect_crop then + mp.msg.warn("Already cropdetecting!") + return + end + + -- Neither => Detect crop. + detect_crop() +end + +mp.add_key_binding("C", "toggle_crop", on_toggle) +mp.register_event("end-file", cleanup) +mp.register_event("file-loaded", on_start) diff --git a/TOOLS/lua/autodeint.lua b/TOOLS/lua/autodeint.lua new file mode 100644 index 0000000..b891c9a --- /dev/null +++ b/TOOLS/lua/autodeint.lua @@ -0,0 +1,156 @@ +-- This script uses the lavfi idet filter to automatically insert the +-- appropriate deinterlacing filter based on a short section of the +-- currently playing video. +-- +-- It registers the key-binding ctrl+d, which when pressed, inserts the filters +-- ``vf=idet,lavfi-pullup,idet``. After 4 seconds, it removes these +-- filters and decides whether the content is progressive, interlaced, or +-- telecined and the interlacing field dominance. +-- +-- Based on this information, it may set mpv's ``deinterlace`` property (which +-- usually inserts the yadif filter), or insert the ``pullup`` filter if the +-- content is telecined. It also sets field dominance with lavfi setfield. +-- +-- OPTIONS: +-- The default detection time may be overridden by adding +-- +-- --script-opts=autodeint.detect_seconds=<number of seconds> +-- +-- to mpv's arguments. This may be desirable to allow idet more +-- time to collect data. +-- +-- To see counts of the various types of frames for each detection phase, +-- the verbosity can be increased with +-- +-- --msg-level=autodeint=v + +require "mp.msg" + +script_name = mp.get_script_name() +detect_label = string.format("%s-detect", script_name) +pullup_label = string.format("%s", script_name) +dominance_label = string.format("%s-dominance", script_name) +ivtc_detect_label = string.format("%s-ivtc-detect", script_name) + +-- number of seconds to gather cropdetect data +detect_seconds = tonumber(mp.get_opt(string.format("%s.detect_seconds", script_name))) +if not detect_seconds then + detect_seconds = 4 +end + +function del_filter_if_present(label) + -- necessary because mp.command('vf del @label:filter') raises an + -- error if the filter doesn't exist + local vfs = mp.get_property_native("vf") + + for i,vf in pairs(vfs) do + if vf["label"] == label then + table.remove(vfs, i) + mp.set_property_native("vf", vfs) + return true + end + end + return false +end + +local function add_vf(label, filter) + return mp.command(('vf add @%s:%s'):format(label, filter)) +end + +function start_detect() + -- exit if detection is already in progress + if timer then + mp.msg.warn("already detecting!") + return + end + + mp.set_property("deinterlace","no") + del_filter_if_present(pullup_label) + del_filter_if_present(dominance_label) + + -- insert the detection filters + if not (add_vf(detect_label, 'idet') and + add_vf(dominance_label, 'setfield=mode=auto') and + add_vf(pullup_label, 'lavfi-pullup') and + add_vf(ivtc_detect_label, 'idet')) then + mp.msg.error("failed to insert detection filters") + return + end + + -- wait to gather data + timer = mp.add_timeout(detect_seconds, select_filter) +end + +function stop_detect() + del_filter_if_present(detect_label) + del_filter_if_present(ivtc_detect_label) + timer = nil +end + +progressive, interlaced_tff, interlaced_bff, interlaced = 0, 1, 2, 3, 4 + +function judge(label) + -- get the metadata + local result = mp.get_property_native(string.format("vf-metadata/%s", label)) + local num_tff = tonumber(result["lavfi.idet.multiple.tff"]) + local num_bff = tonumber(result["lavfi.idet.multiple.bff"]) + local num_progressive = tonumber(result["lavfi.idet.multiple.progressive"]) + local num_undetermined = tonumber(result["lavfi.idet.multiple.undetermined"]) + local num_interlaced = num_tff + num_bff + local num_determined = num_interlaced + num_progressive + + mp.msg.verbose(label.." progressive = "..num_progressive) + mp.msg.verbose(label.." interlaced-tff = "..num_tff) + mp.msg.verbose(label.." interlaced-bff = "..num_bff) + mp.msg.verbose(label.." undetermined = "..num_undetermined) + + if num_determined < num_undetermined then + mp.msg.warn("majority undetermined frames") + end + if num_progressive > 20*num_interlaced then + return progressive + elseif num_tff > 10*num_bff then + return interlaced_tff + elseif num_bff > 10*num_tff then + return interlaced_bff + else + return interlaced + end +end + +function select_filter() + -- handle the first detection filter results + local verdict = judge(detect_label) + local ivtc_verdict = judge(ivtc_detect_label) + local dominance = "auto" + if verdict == progressive then + mp.msg.info("progressive: doing nothing") + stop_detect() + del_filter_if_present(dominance_label) + del_filter_if_present(pullup_label) + return + else + if verdict == interlaced_tff then + dominance = "tff" + add_vf(dominance_label, 'setfield=mode='..dominance) + elseif verdict == interlaced_bff then + dominance = "bff" + add_vf(dominance_label, 'setfield=mode='..dominance) + else + del_filter_if_present(dominance_label) + end + end + + -- handle the ivtc detection filter results + if ivtc_verdict == progressive then + mp.msg.info(string.format("telecined with %s field dominance: using pullup", dominance)) + stop_detect() + else + mp.msg.info(string.format("interlaced with %s field dominance: setting deinterlace property", dominance)) + del_filter_if_present(pullup_label) + mp.set_property("deinterlace","yes") + stop_detect() + end +end + +mp.add_key_binding("ctrl+d", script_name, start_detect) diff --git a/TOOLS/lua/autoload.lua b/TOOLS/lua/autoload.lua new file mode 100644 index 0000000..4003cbc --- /dev/null +++ b/TOOLS/lua/autoload.lua @@ -0,0 +1,328 @@ +-- This script automatically loads playlist entries before and after the +-- the currently played file. It does so by scanning the directory a file is +-- located in when starting playback. It sorts the directory entries +-- alphabetically, and adds entries before and after the current file to +-- the internal playlist. (It stops if it would add an already existing +-- playlist entry at the same position - this makes it "stable".) +-- Add at most 5000 * 2 files when starting a file (before + after). + +--[[ +To configure this script use file autoload.conf in directory script-opts (the "script-opts" +directory must be in the mpv configuration directory, typically ~/.config/mpv/). + +Example configuration would be: + +disabled=no +images=no +videos=yes +audio=yes +additional_image_exts=list,of,ext +additional_video_exts=list,of,ext +additional_audio_exts=list,of,ext +ignore_hidden=yes +same_type=yes +directory_mode=recursive + +--]] + +MAXENTRIES = 5000 +MAXDIRSTACK = 20 + +local msg = require 'mp.msg' +local options = require 'mp.options' +local utils = require 'mp.utils' + +o = { + disabled = false, + images = true, + videos = true, + audio = true, + additional_image_exts = "", + additional_video_exts = "", + additional_audio_exts = "", + ignore_hidden = true, + same_type = false, + directory_mode = "auto" +} +options.read_options(o, nil, function(list) + split_option_exts(list.additional_video_exts, list.additional_audio_exts, list.additional_image_exts) + if list.videos or list.additional_video_exts or + list.audio or list.additional_audio_exts or + list.images or list.additional_image_exts then + create_extensions() + end + if list.directory_mode then + validate_directory_mode() + end +end) + +function Set (t) + local set = {} + for _, v in pairs(t) do set[v] = true end + return set +end + +function SetUnion (a,b) + for k in pairs(b) do a[k] = true end + return a +end + +function Split (s) + local set = {} + for v in string.gmatch(s, '([^,]+)') do set[v] = true end + return set +end + +EXTENSIONS_VIDEO = Set { + '3g2', '3gp', 'avi', 'flv', 'm2ts', 'm4v', 'mj2', 'mkv', 'mov', + 'mp4', 'mpeg', 'mpg', 'ogv', 'rmvb', 'webm', 'wmv', 'y4m' +} + +EXTENSIONS_AUDIO = Set { + 'aiff', 'ape', 'au', 'flac', 'm4a', 'mka', 'mp3', 'oga', 'ogg', + 'ogm', 'opus', 'wav', 'wma' +} + +EXTENSIONS_IMAGES = Set { + 'avif', 'bmp', 'gif', 'j2k', 'jp2', 'jpeg', 'jpg', 'jxl', 'png', + 'svg', 'tga', 'tif', 'tiff', 'webp' +} + +function split_option_exts(video, audio, image) + if video then o.additional_video_exts = Split(o.additional_video_exts) end + if audio then o.additional_audio_exts = Split(o.additional_audio_exts) end + if image then o.additional_image_exts = Split(o.additional_image_exts) end +end +split_option_exts(true, true, true) + +function create_extensions() + EXTENSIONS = {} + if o.videos then SetUnion(SetUnion(EXTENSIONS, EXTENSIONS_VIDEO), o.additional_video_exts) end + if o.audio then SetUnion(SetUnion(EXTENSIONS, EXTENSIONS_AUDIO), o.additional_audio_exts) end + if o.images then SetUnion(SetUnion(EXTENSIONS, EXTENSIONS_IMAGES), o.additional_image_exts) end +end +create_extensions() + +function validate_directory_mode() + if o.directory_mode ~= "recursive" and o.directory_mode ~= "lazy" and o.directory_mode ~= "ignore" then + o.directory_mode = nil + end +end +validate_directory_mode() + +function add_files(files) + local oldcount = mp.get_property_number("playlist-count", 1) + for i = 1, #files do + mp.commandv("loadfile", files[i][1], "append") + mp.commandv("playlist-move", oldcount + i - 1, files[i][2]) + end +end + +function get_extension(path) + match = string.match(path, "%.([^%.]+)$" ) + if match == nil then + return "nomatch" + else + return match + end +end + +table.filter = function(t, iter) + for i = #t, 1, -1 do + if not iter(t[i]) then + table.remove(t, i) + end + end +end + +table.append = function(t1, t2) + local t1_size = #t1 + for i = 1, #t2 do + t1[t1_size + i] = t2[i] + end +end + +-- alphanum sorting for humans in Lua +-- http://notebook.kulchenko.com/algorithms/alphanumeric-natural-sorting-for-humans-in-lua + +function alphanumsort(filenames) + local function padnum(n, d) + return #d > 0 and ("%03d%s%.12f"):format(#n, n, tonumber(d) / (10 ^ #d)) + or ("%03d%s"):format(#n, n) + end + + local tuples = {} + for i, f in ipairs(filenames) do + tuples[i] = {f:lower():gsub("0*(%d+)%.?(%d*)", padnum), f} + end + table.sort(tuples, function(a, b) + return a[1] == b[1] and #b[2] < #a[2] or a[1] < b[1] + end) + for i, tuple in ipairs(tuples) do filenames[i] = tuple[2] end + return filenames +end + +local autoloaded = nil +local added_entries = {} +local autoloaded_dir = nil + +function scan_dir(path, current_file, dir_mode, separator, dir_depth, total_files, extensions) + if dir_depth == MAXDIRSTACK then + return + end + msg.trace("scanning: " .. path) + local files = utils.readdir(path, "files") or {} + local dirs = dir_mode ~= "ignore" and utils.readdir(path, "dirs") or {} + local prefix = path == "." and "" or path + table.filter(files, function (v) + -- The current file could be a hidden file, ignoring it doesn't load other + -- files from the current directory. + if (o.ignore_hidden and not (prefix .. v == current_file) and string.match(v, "^%.")) then + return false + end + local ext = get_extension(v) + if ext == nil then + return false + end + return extensions[string.lower(ext)] + end) + table.filter(dirs, function(d) + return not ((o.ignore_hidden and string.match(d, "^%."))) + end) + alphanumsort(files) + alphanumsort(dirs) + + for i, file in ipairs(files) do + files[i] = prefix .. file + end + + table.append(total_files, files) + if dir_mode == "recursive" then + for _, dir in ipairs(dirs) do + scan_dir(prefix .. dir .. separator, current_file, dir_mode, + separator, dir_depth + 1, total_files, extensions) + end + else + for i, dir in ipairs(dirs) do + dirs[i] = prefix .. dir + end + table.append(total_files, dirs) + end +end + +function find_and_add_entries() + local path = mp.get_property("path", "") + local dir, filename = utils.split_path(path) + msg.trace(("dir: %s, filename: %s"):format(dir, filename)) + if o.disabled then + msg.debug("stopping: autoload disabled") + return + elseif #dir == 0 then + msg.debug("stopping: not a local path") + return + end + + local pl_count = mp.get_property_number("playlist-count", 1) + this_ext = get_extension(filename) + -- check if this is a manually made playlist + if (pl_count > 1 and autoloaded == nil) or + (pl_count == 1 and EXTENSIONS[string.lower(this_ext)] == nil) then + msg.debug("stopping: manually made playlist") + return + else + if pl_count == 1 then + autoloaded = true + autoloaded_dir = dir + added_entries = {} + end + end + + local extensions = {} + if o.same_type then + if EXTENSIONS_VIDEO[string.lower(this_ext)] ~= nil then + extensions = EXTENSIONS_VIDEO + elseif EXTENSIONS_AUDIO[string.lower(this_ext)] ~= nil then + extensions = EXTENSIONS_AUDIO + else + extensions = EXTENSIONS_IMAGES + end + else + extensions = EXTENSIONS + end + + local pl = mp.get_property_native("playlist", {}) + local pl_current = mp.get_property_number("playlist-pos-1", 1) + msg.trace(("playlist-pos-1: %s, playlist: %s"):format(pl_current, + utils.to_string(pl))) + + local files = {} + do + local dir_mode = o.directory_mode or mp.get_property("directory-mode", "lazy") + local separator = mp.get_property_native("platform") == "windows" and "\\" or "/" + scan_dir(autoloaded_dir, path, dir_mode, separator, 0, files, extensions) + end + + if next(files) == nil then + msg.debug("no other files or directories in directory") + return + end + + -- Find the current pl entry (dir+"/"+filename) in the sorted dir list + local current + for i = 1, #files do + if files[i] == path then + current = i + break + end + end + if current == nil then + return + end + msg.trace("current file position in files: "..current) + + -- treat already existing playlist entries, independent of how they got added + -- as if they got added by autoload + for _, entry in ipairs(pl) do + added_entries[entry.filename] = true + end + + local append = {[-1] = {}, [1] = {}} + for direction = -1, 1, 2 do -- 2 iterations, with direction = -1 and +1 + for i = 1, MAXENTRIES do + local pos = current + i * direction + local file = files[pos] + if file == nil or file[1] == "." then + break + end + + -- skip files that are/were already in the playlist + if not added_entries[file] then + if direction == -1 then + msg.verbose("Prepending " .. file) + table.insert(append[-1], 1, {file, pl_current + i * direction + 1}) + else + msg.verbose("Adding " .. file) + if pl_count > 1 then + table.insert(append[1], {file, pl_current + i * direction - 1}) + else + mp.commandv("loadfile", file, "append") + end + end + end + added_entries[file] = true + end + if pl_count == 1 and direction == -1 and #append[-1] > 0 then + for i = 1, #append[-1] do + mp.commandv("loadfile", append[-1][i][1], "append") + end + mp.commandv("playlist-move", 0, current) + end + end + + if pl_count > 1 then + add_files(append[1]) + add_files(append[-1]) + end +end + +mp.register_event("start-file", find_and_add_entries) diff --git a/TOOLS/lua/command-test.lua b/TOOLS/lua/command-test.lua new file mode 100644 index 0000000..877cacd --- /dev/null +++ b/TOOLS/lua/command-test.lua @@ -0,0 +1,124 @@ +-- Test script for some command API details. + +local utils = require("mp.utils") + +function join(sep, arr, count) + local r = "" + if count == nil then + count = #arr + end + for i = 1, count do + if i > 1 then + r = r .. sep + end + r = r .. utils.to_string(arr[i]) + end + return r +end + +mp.observe_property("vo-configured", "bool", function(_, v) + if v ~= true then + return + end + + print("async expand-text") + mp.command_native_async({"expand-text", "hello ${path}!"}, + function(res, val, err) + print("done async expand-text: " .. join(" ", {res, val, err})) + end) + + -- make screenshot writing very slow + mp.set_property("screenshot-format", "png") + mp.set_property("screenshot-png-compression", "9") + + timer = mp.add_periodic_timer(0.1, function() print("I'm alive") end) + timer:resume() + + print("Slow screenshot command...") + res, err = mp.command_native({"screenshot"}) + print("done, res: " .. utils.to_string(res)) + + print("Slow screenshot async command...") + res, err = mp.command_native_async({"screenshot"}, function(res) + print("done (async), res: " .. utils.to_string(res)) + timer:kill() + end) + print("done (sending), res: " .. utils.to_string(res)) + + print("Broken screenshot async command...") + mp.command_native_async({"screenshot-to-file", "/nonexistent/bogus.png"}, + function(res, val, err) + print("done err scr.: " .. join(" ", {res, val, err})) + end) + + mp.command_native_async({name = "subprocess", args = {"sh", "-c", "echo hi && sleep 10s"}, capture_stdout = true}, + function(res, val, err) + print("done subprocess: " .. join(" ", {res, val, err})) + end) + + local x = mp.command_native_async({name = "subprocess", args = {"sleep", "inf"}}, + function(res, val, err) + print("done sleep inf subprocess: " .. join(" ", {res, val, err})) + end) + mp.add_timeout(15, function() + print("aborting sleep inf subprocess after timeout") + mp.abort_async_command(x) + end) + + -- (assuming this "freezes") + local y = mp.command_native_async({name = "sub-add", url = "-"}, + function(res, val, err) + print("done sub-add stdin: " .. join(" ", {res, val, err})) + end) + mp.add_timeout(20, function() + print("aborting sub-add stdin after timeout") + mp.abort_async_command(y) + end) + + + mp.command_native_async({name = "subprocess", args = {"wc", "-c"}, + stdin_data = "hello", capture_stdout = true}, + function(res, val, err) + print("Should be '5': " .. val.stdout) + end) + -- blocking stdin by default + mp.command_native_async({name = "subprocess", args = {"cat"}, + capture_stdout = true}, + function(res, val, err) + print("Should be 0: " .. #val.stdout) + end) + -- stdin + detached + mp.command_native_async({name = "subprocess", + args = {"bash", "-c", "(sleep 5s ; cat)"}, + stdin_data = "this should appear after 5s.\n", + detach = true}, + function(res, val, err) + print("5s test: " .. val.status) + end) + + -- This should get killed on script exit. + mp.command_native_async({name = "subprocess", playback_only = false, + args = {"sleep", "inf"}}, function()end) + + -- Runs detached; should be killed on player exit (forces timeout) + mp.command_native({_flags={"async"}, name = "subprocess", + playback_only = false, args = {"sleep", "inf"}}) +end) + +function freeze_test(playback_only) + -- This "freezes" the script, should be killed via timeout. + counter = counter and counter + 1 or 0 + print("freeze! " .. counter) + local x = mp.command_native({name = "subprocess", + playback_only = playback_only, + args = {"sleep", "inf"}}) + print("done, killed=" .. utils.to_string(x.killed_by_us)) +end + +mp.register_event("shutdown", function() + freeze_test(false) +end) + +mp.register_event("idle", function() + freeze_test(true) +end) diff --git a/TOOLS/lua/cycle-deinterlace-pullup.lua b/TOOLS/lua/cycle-deinterlace-pullup.lua new file mode 100644 index 0000000..2902e40 --- /dev/null +++ b/TOOLS/lua/cycle-deinterlace-pullup.lua @@ -0,0 +1,56 @@ +-- This script cycles between deinterlacing, pullup (inverse +-- telecine), and both filters off. It uses the "deinterlace" property +-- so that a hardware deinterlacer will be used if available. +-- +-- It overrides the default deinterlace toggle keybinding "D" +-- (shift+d), so that rather than merely cycling the "deinterlace" property +-- between on and off, it adds a "pullup" step to the cycle. +-- +-- It provides OSD feedback as to the actual state of the two filters +-- after each cycle step/keypress. +-- +-- Note: if hardware decoding is enabled, pullup filter will likely +-- fail to insert. +-- +-- TODO: It might make sense to use hardware assisted vdpaupp=pullup, +-- if available, but I don't have hardware to test it. Patch welcome. + +script_name = mp.get_script_name() +pullup_label = string.format("%s-pullup", script_name) + +function pullup_on() + for i,vf in pairs(mp.get_property_native('vf')) do + if vf['label'] == pullup_label then + return "yes" + end + end + return "no" +end + +function do_cycle() + if pullup_on() == "yes" then + -- if pullup is on remove it + mp.command(string.format("vf del @%s:pullup", pullup_label)) + return + elseif mp.get_property("deinterlace") == "yes" then + -- if deinterlace is on, turn it off and insert pullup filter + mp.set_property("deinterlace", "no") + mp.command(string.format("vf add @%s:pullup", pullup_label)) + return + else + -- if neither is on, turn on deinterlace + mp.set_property("deinterlace", "yes") + return + end +end + +function cycle_deinterlace_pullup_handler() + do_cycle() + -- independently determine current state and give user feedback + mp.osd_message(string.format("deinterlace: %s\n".. + "pullup: %s", + mp.get_property("deinterlace"), + pullup_on())) +end + +mp.add_key_binding("D", "cycle-deinterlace-pullup", cycle_deinterlace_pullup_handler) diff --git a/TOOLS/lua/nan-test.lua b/TOOLS/lua/nan-test.lua new file mode 100644 index 0000000..d3f1c8c --- /dev/null +++ b/TOOLS/lua/nan-test.lua @@ -0,0 +1,37 @@ +-- Test a float property which internally uses NaN. +-- Run with --no-config (or just scale-param1 not set). + +local utils = require 'mp.utils' + +prop_name = "scale-param1" + +-- internal NaN, return string "default" instead of NaN +v = mp.get_property_native(prop_name, "fail") +print("Exp:", "string", "\"default\"") +print("Got:", type(v), utils.to_string(v)) + +v = mp.get_property(prop_name) +print("Exp:", "default") +print("Got:", v) + +-- not representable -> return provided fallback value +v = mp.get_property_number(prop_name, -100) +print("Exp:", -100) +print("Got:", v) + +mp.set_property_native(prop_name, 123) +v = mp.get_property_number(prop_name, -100) +print("Exp:", "number", 123) +print("Got:", type(v), utils.to_string(v)) + +-- try to set an actual NaN +st, msg = mp.set_property_number(prop_name, 0.0/0) +print("Exp:", nil, "<message>") +print("Got:", st, msg) + +-- set default +mp.set_property(prop_name, "default") + +v = mp.get_property(prop_name) +print("Exp:", "default") +print("Got:", v) diff --git a/TOOLS/lua/observe-all.lua b/TOOLS/lua/observe-all.lua new file mode 100644 index 0000000..0037439 --- /dev/null +++ b/TOOLS/lua/observe-all.lua @@ -0,0 +1,22 @@ +-- Test script for property change notification mechanism. +-- Note that watching/reading some properties can be very expensive, or +-- require the player to synchronously wait on network (when playing +-- remote files), so you should in general only watch properties you +-- are interested in. + +local utils = require("mp.utils") + +function observe(name) + mp.observe_property(name, "native", function(name, val) + print("property '" .. name .. "' changed to '" .. + utils.to_string(val) .. "'") + end) +end + +for i,name in ipairs(mp.get_property_native("property-list")) do + observe(name) +end + +for i,name in ipairs(mp.get_property_native("options")) do + observe("options/" .. name) +end diff --git a/TOOLS/lua/ontop-playback.lua b/TOOLS/lua/ontop-playback.lua new file mode 100644 index 0000000..b02716c --- /dev/null +++ b/TOOLS/lua/ontop-playback.lua @@ -0,0 +1,19 @@ +--makes mpv disable ontop when pausing and re-enable it again when resuming playback +--please note that this won't do anything if ontop was not enabled before pausing + +local was_ontop = false + +mp.observe_property("pause", "bool", function(name, value) + local ontop = mp.get_property_native("ontop") + if value then + if ontop then + mp.set_property_native("ontop", false) + was_ontop = true + end + else + if was_ontop and not ontop then + mp.set_property_native("ontop", true) + end + was_ontop = false + end +end) diff --git a/TOOLS/lua/osd-test.lua b/TOOLS/lua/osd-test.lua new file mode 100644 index 0000000..1b17819 --- /dev/null +++ b/TOOLS/lua/osd-test.lua @@ -0,0 +1,35 @@ +local assdraw = require 'mp.assdraw' +local utils = require 'mp.utils' + +things = {} +for i = 1, 2 do + things[i] = { + osd1 = mp.create_osd_overlay("ass-events"), + osd2 = mp.create_osd_overlay("ass-events") + } +end +things[1].text = "{\\an5}hello\\Nworld" +things[2].text = "{\\pos(400, 200)}something something" + +mp.add_periodic_timer(2, function() + for i, thing in ipairs(things) do + thing.osd1.data = thing.text + thing.osd1.compute_bounds = true + --thing.osd1.hidden = true + local res = thing.osd1:update() + print("res " .. i .. ": " .. utils.to_string(res)) + + thing.osd2.hidden = true + if res ~= nil and res.x0 ~= nil then + local draw = assdraw.ass_new() + draw:append("{\\alpha&H80}") + draw:draw_start() + draw:pos(0, 0) + draw:rect_cw(res.x0, res.y0, res.x1, res.y1) + draw:draw_stop() + thing.osd2.hidden = false + thing.osd2.data = draw.text + end + thing.osd2:update() + end +end) diff --git a/TOOLS/lua/pause-when-minimize.lua b/TOOLS/lua/pause-when-minimize.lua new file mode 100644 index 0000000..99add70 --- /dev/null +++ b/TOOLS/lua/pause-when-minimize.lua @@ -0,0 +1,20 @@ +-- This script pauses playback when minimizing the window, and resumes playback +-- if it's brought back again. If the player was already paused when minimizing, +-- then try not to mess with the pause state. + +local did_minimize = false + +mp.observe_property("window-minimized", "bool", function(name, value) + local pause = mp.get_property_native("pause") + if value == true then + if pause == false then + mp.set_property_native("pause", true) + did_minimize = true + end + elseif value == false then + if did_minimize and (pause == true) then + mp.set_property_native("pause", false) + end + did_minimize = false + end +end) diff --git a/TOOLS/lua/skip-logo.lua b/TOOLS/lua/skip-logo.lua new file mode 100644 index 0000000..8e1f9da --- /dev/null +++ b/TOOLS/lua/skip-logo.lua @@ -0,0 +1,265 @@ +--[[ + +Automatically skip in files if video frames with pre-supplied fingerprints are +detected. This will skip ahead by a pre-configured amount of time if a matching +video frame is detected. + +This requires the vf_fingerprint video filter to be compiled in. Read the +documentation of this filter for caveats (which will automatically apply to +this script as well), such as no support for zero-copy hardware decoding. + +You need to manually gather and provide fingerprints for video frames and add +them to a configuration file in script-opts/skip-logo.conf (the "script-opts" +directory must be in the mpv configuration directory, typically ~/.config/mpv/). + +Example script-opts/skip-logo.conf: + + + cases = { + { + -- Skip ahead 10 seconds if a black frame was detected + -- Note: this is dangerous non-sense. It's just for demonstration. + name = "black frame", -- print if matched + skip = 10, -- number of seconds to skip forward + score = 0.3, -- required score + fingerprint = "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + }, + { + -- Skip ahead 20 seconds if a white frame was detected + -- Note: this is dangerous non-sense. It's just for demonstration. + name = "fun2", + skip = 20, + fingerprint = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + }, + } + +This is actually a lua file. Lua was chosen because it seemed less of a pain to +parse. Future versions of this script may change the format. + +The fingerprint is a video frame, converted to "gray" (8 bit per pixels), full +range, each pixel concatenated into an array, converted to a hex string. You +can produce these fingerprints by running this manually: + + mpv --vf=fingerprint:print yourfile.mkv + +This will log the fingerprint of each video frame to the console, along with its +timestamp. You find the fingerprint of a unique-enough looking frame, and add +it as entry to skip-logo.conf. + +You can provide a score for "fuzziness". If no score is provided, a default +value of 0.3 is used. The score is inverse: 0 means exactly the same, while a +higher score means a higher difference. Currently, the score is computed as +euclidean distance between the video frame and the pre-provided fingerprint, +thus the highest score is 16. You probably want a score lower than 1 at least. +(This algorithm is very primitive, but also simple and fast to compute.) + +There's always the danger of false positives, which might be quite annoying. +It's up to you what you hate more, the logo, or random skips if false positives +are detected. Also, it's always active, and might eat too much CPU with files +that have a high resolution or framerate. To temporarily disable the script, +having a keybind like this in your input.conf will be helpful: + + ctrl+k vf toggle @skip-logo + +This will disable/enable the fingerprint filter, which the script automatically +adds at start. + +Another important caveat is that the script currently disables matching during +seeking or playback initialization, which means it cannot match the first few +frames of a video. This could be fixed, but the author was too lazy to do so. + +--]] + +local utils = require "mp.utils" +local msg = require "mp.msg" + +local label = "skip-logo" +local meta_property = string.format("vf-metadata/%s", label) + +local config = {} +local cases = {} +local cur_bmp +local seeking = false +local playback_start_pts = nil + +-- Convert a hex string to an array. Convert each byte to a [0,1] float by +-- interpreting it as normalized uint8_t. +-- The data parameter, if not nil, may be used as storage (avoiding garbage). +local function hex_to_norm8(hex, data) + local size = math.floor(#hex / 2) + if #hex ~= size * 2 then + return nil + end + local res + if (data ~= nil) and (#data == size) then + res = data + else + res = {} + end + for i = 1, size do + local num = tonumber(hex:sub(i * 2, i * 2 + 1), 16) + if num == nil then + return nil + end + res[i] = num / 255.0 + end + return res +end + +local function compare_bmp(a, b) + if #a ~= #b then + return nil -- can't compare + end + local sum = 0 + for i = 1, #a do + local diff = a[i] - b[i] + sum = sum + diff * diff + end + return math.sqrt(sum) +end + +local function load_config() + local conf_file = mp.find_config_file("script-opts/skip-logo.conf") + local conf_fn + local err = nil + if conf_file then + if setfenv then + conf_fn, err = loadfile(conf_file) + if conf_fn then + setfenv(conf_fn, config) + end + else + conf_fn, err = loadfile(conf_file, "t", config) + end + else + err = "config file not found" + end + + if conf_fn and (not err) then + local ok, err2 = pcall(conf_fn) + err = err2 + end + + if err then + msg.error("Failed to load config file:", err) + end + + if config.cases then + for n, case in ipairs(config.cases) do + local err = nil + case.bitmap = hex_to_norm8(case.fingerprint) + if case.bitmap == nil then + err = "invalid or missing fingerprint field" + end + if case.score == nil then + case.score = 0.3 + end + if type(case.score) ~= "number" then + err = "score field is not a number" + end + if type(case.skip) ~= "number" then + err = "skip field is not a number or missing" + end + if case.name == nil then + case.name = ("Entry %d"):format(n) + end + if err == nil then + cases[#cases + 1] = case + else + msg.error(("Entry %s: %s, ignoring."):format(case.name, err)) + end + end + end +end + +load_config() + +-- Returns true on match and if something was done. +local function check_fingerprint(hex, pts) + local bmp = hex_to_norm8(hex, cur_bmp) + cur_bmp = bmp + + -- If parsing the filter's result failed (well, it shouldn't). + assert(bmp ~= nil, "filter returned nonsense") + + for _, case in ipairs(cases) do + local score = compare_bmp(case.bitmap, bmp) + if (score ~= nil) and (score <= case.score) then + msg.warn(("Matching %s: score=%f (required: %f) at %s, skipping %f seconds"): + format(case.name, score, case.score, mp.format_time(pts), case.skip)) + mp.commandv("seek", pts + case.skip, "absolute+exact") + return true + end + end + + return false +end + +local function read_frames() + local result = mp.get_property_native(meta_property) + if result == nil then + return + end + + -- Try to get all entries. Out of laziness, assume that there are at most + -- 100 entries. (In fact, vf_fingerprint limits it to 10.) + for i = 0, 99 do + local prefix = string.format("fp%d.", i) + local hex = result[prefix .. "hex"] + + local pts = tonumber(result[prefix .. "pts"]) + if (hex == nil) or (pts == nil) then + break + end + + local skip = false -- blame Lua for not having "continue" or "goto", not me + + -- If seeking just stopped, there will be frames before the seek target, + -- ignore them by checking the timestamps. + if playback_start_pts ~= nil then + if pts >= playback_start_pts then + playback_start_pts = nil -- just for robustness + else + skip = true + end + end + + if not skip then + if check_fingerprint(hex, pts) then + break + end + end + end +end + +mp.observe_property(meta_property, "none", function() + -- Ignore frames that are decoded/filtered during seeking. + if seeking then + return + end + + read_frames() +end) + +mp.observe_property("seeking", "bool", function(name, val) + seeking = val + if seeking == false then + playback_start_pts = mp.get_property_number("playback-time") + read_frames() + end +end) + +local filters = mp.get_property_native("option-info/vf/choices", {}) +local found = false +for _, f in ipairs(filters) do + if f == "fingerprint" then + found = true + break + end +end + +if found then + mp.command(("no-osd vf add @%s:fingerprint"):format(label, filter)) +else + msg.warn("vf_fingerprint not found") +end diff --git a/TOOLS/lua/status-line.lua b/TOOLS/lua/status-line.lua new file mode 100644 index 0000000..e40dce2 --- /dev/null +++ b/TOOLS/lua/status-line.lua @@ -0,0 +1,92 @@ +-- Rebuild the terminal status line as a lua script +-- Be aware that this will require more cpu power! +-- Also, this is based on a rather old version of the +-- builtin mpv status line. + +-- Add a string to the status line +function atsl(s) + newStatus = newStatus .. s +end + +function update_status_line() + -- Reset the status line + newStatus = "" + + if mp.get_property_bool("pause") then + atsl("(Paused) ") + elseif mp.get_property_bool("paused-for-cache") then + atsl("(Buffering) ") + end + + if mp.get_property("aid") ~= "no" then + atsl("A") + end + if mp.get_property("vid") ~= "no" then + atsl("V") + end + + atsl(": ") + + atsl(mp.get_property_osd("time-pos")) + + atsl(" / "); + atsl(mp.get_property_osd("duration")); + + atsl(" (") + atsl(mp.get_property_osd("percent-pos", -1)) + atsl("%)") + + local r = mp.get_property_number("speed", -1) + if r ~= 1 then + atsl(string.format(" x%4.2f", r)) + end + + r = mp.get_property_number("avsync", nil) + if r ~= nil then + atsl(string.format(" A-V: %f", r)) + end + + r = mp.get_property("total-avsync-change", 0) + if math.abs(r) > 0.05 then + atsl(string.format(" ct:%7.3f", r)) + end + + r = mp.get_property_number("decoder-drop-frame-count", -1) + if r > 0 then + atsl(" Late: ") + atsl(r) + end + + r = mp.get_property_osd("video-bitrate") + if r ~= nil and r ~= "" then + atsl(" Vb: ") + atsl(r) + end + + r = mp.get_property_osd("audio-bitrate") + if r ~= nil and r ~= "" then + atsl(" Ab: ") + atsl(r) + end + + r = mp.get_property_number("cache", 0) + if r > 0 then + atsl(string.format(" Cache: %d%% ", r)) + end + + -- Set the new status line + mp.set_property("options/term-status-msg", newStatus) +end + +timer = mp.add_periodic_timer(1, update_status_line) + +function on_pause_change(name, value) + if value == false then + timer:resume() + else + timer:stop() + end + mp.add_timeout(0.1, update_status_line) +end +mp.observe_property("pause", "bool", on_pause_change) +mp.register_event("seek", update_status_line) diff --git a/TOOLS/lua/test-hooks.lua b/TOOLS/lua/test-hooks.lua new file mode 100644 index 0000000..4e84d9e --- /dev/null +++ b/TOOLS/lua/test-hooks.lua @@ -0,0 +1,32 @@ +local utils = require("mp.utils") + +function hardsleep() + os.execute("sleep 1s") +end + +local hooks = {"on_before_start_file", "on_load", "on_load_fail", + "on_preloaded", "on_unload", "on_after_end_file"} + +for _, name in ipairs(hooks) do + mp.add_hook(name, 0, function() + print("--- hook: " .. name) + hardsleep() + print(" ... continue") + end) +end + +local events = {"start-file", "end-file", "file-loaded", "seek", + "playback-restart", "idle", "shutdown"} +for _, name in ipairs(events) do + mp.register_event(name, function() + print("--- event: " .. name) + end) +end + +local props = {"path", "metadata"} +for _, name in ipairs(props) do + mp.observe_property(name, "native", function(name, val) + print("property '" .. name .. "' changed to '" .. + utils.to_string(val) .. "'") + end) +end diff --git a/TOOLS/macos-sdk-version.py b/TOOLS/macos-sdk-version.py new file mode 100755 index 0000000..12e1071 --- /dev/null +++ b/TOOLS/macos-sdk-version.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python3 + +# This checks for the sdk path, the sdk version, and +# the sdk build version. + +import re +import os +import string +import subprocess +import sys +from shutil import which +from subprocess import check_output + +def find_macos_sdk(): + sdk = os.environ.get('MACOS_SDK', '') + sdk_version = os.environ.get('MACOS_SDK_VERSION', '0.0') + xcrun = which('xcrun') + xcodebuild = which('xcodebuild') + + if not xcrun: + return sdk,sdk_version + + if not sdk: + sdk = check_output([xcrun, '--sdk', 'macosx', '--show-sdk-path'], + encoding="UTF-8") + + # find macOS SDK paths and version + if sdk_version == '0.0': + sdk_version = check_output([xcrun, '--sdk', 'macosx', '--show-sdk-version'], + encoding="UTF-8") + + # use xcode tools when installed, still necessary for xcode versions <12.0 + try: + sdk_version = check_output([xcodebuild, '-sdk', 'macosx', '-version', 'ProductVersion'], + encoding="UTF-8", stderr=subprocess.DEVNULL) + except: + pass + + if not isinstance(sdk_version, str): + sdk_version = '10.10.0' + + return sdk.strip(),sdk_version.strip() + +if __name__ == "__main__": + sdk_info = find_macos_sdk() + sys.stdout.write(','.join(sdk_info)) diff --git a/TOOLS/macos-swift-lib-directory.py b/TOOLS/macos-swift-lib-directory.py new file mode 100755 index 0000000..51e8cbe --- /dev/null +++ b/TOOLS/macos-swift-lib-directory.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 + +# Finds the macos swift library directory and prints the full path to stdout. +# First argument is the path to the swift executable. + +import os +import sys +from shutil import which +from subprocess import check_output + +def find_swift_lib(): + swift_lib_dir = os.environ.get('SWIFT_LIB_DYNAMIC', '') + if swift_lib_dir: + return swift_lib_dir + + # first check for lib dir relative to swift executable + xcode_dir = os.path.dirname(os.path.dirname(sys.argv[1])) + swift_lib_dir = os.path.join(xcode_dir, "lib", "swift", "macosx") + + if os.path.isdir(swift_lib_dir): + return swift_lib_dir + + # fallback to xcode-select path + xcode_select = which("xcode-select") + if not xcode_select: + sys.exit(1) + + xcode_path = check_output([xcode_select, "-p"], encoding="UTF-8") + + swift_lib_dir = os.path.join(xcode_path, "Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx") + if os.path.isdir(swift_lib_dir): + return swift_lib_dir + + # last resort if we still haven't found a path + swift_lib_dir = os.path.join(xcode_path, "usr/lib/swift/macosx") + if not os.path.isdir(swift_lib_dir): + sys.exit(1) + return swift_lib_dir + +if __name__ == "__main__": + swift_lib_dir = find_swift_lib() + sys.stdout.write(swift_lib_dir) diff --git a/TOOLS/matroska.py b/TOOLS/matroska.py new file mode 100755 index 0000000..52bac48 --- /dev/null +++ b/TOOLS/matroska.py @@ -0,0 +1,479 @@ +#!/usr/bin/env python3 +""" +Generate C definitions for parsing Matroska files. +Can also be used to directly parse Matroska files and display their contents. +""" + +# +# This file is part of mpv. +# +# mpv is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# mpv is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with mpv. If not, see <http://www.gnu.org/licenses/>. +# + + +elements_ebml = ( + 'EBML, 1a45dfa3, sub', ( + 'EBMLVersion, 4286, uint', + 'EBMLReadVersion, 42f7, uint', + 'EBMLMaxIDLength, 42f2, uint', + 'EBMLMaxSizeLength, 42f3, uint', + 'DocType, 4282, str', + 'DocTypeVersion, 4287, uint', + 'DocTypeReadVersion, 4285, uint', + ), + + 'CRC32, bf, binary', + 'Void, ec, binary', +) + +elements_matroska = ( + 'Segment, 18538067, sub', ( + + 'SeekHead*, 114d9b74, sub', ( + 'Seek*, 4dbb, sub', ( + 'SeekID, 53ab, ebml_id', + 'SeekPosition, 53ac, uint', + ), + ), + + 'Info*, 1549a966, sub', ( + 'SegmentUID, 73a4, binary', + 'PrevUID, 3cb923, binary', + 'NextUID, 3eb923, binary', + 'TimecodeScale, 2ad7b1, uint', + 'DateUTC, 4461, sint', + 'Title, 7ba9, str', + 'MuxingApp, 4d80, str', + 'WritingApp, 5741, str', + 'Duration, 4489, float', + ), + + 'Cluster*, 1f43b675, sub', ( + 'Timecode, e7, uint', + 'BlockGroup*, a0, sub', ( + 'Block, a1, binary', + 'BlockDuration, 9b, uint', + 'ReferenceBlock*, fb, sint', + 'DiscardPadding, 75A2, sint', + 'BlockAdditions, 75A1, sub', ( + 'BlockMore*, A6, sub', ( + 'BlockAddID, EE, uint', + 'BlockAdditional, A5, binary', + ), + ), + ), + 'SimpleBlock*, a3, binary', + ), + + 'Tracks*, 1654ae6b, sub', ( + 'TrackEntry*, ae, sub', ( + 'TrackNumber, d7, uint', + 'TrackUID, 73c5, uint', + 'TrackType, 83, uint', + 'FlagEnabled, b9, uint', + 'FlagDefault, 88, uint', + 'FlagForced, 55aa, uint', + 'FlagLacing, 9c, uint', + 'MinCache, 6de7, uint', + 'MaxCache, 6df8, uint', + 'DefaultDuration, 23e383, uint', + 'TrackTimecodeScale, 23314f, float', + 'MaxBlockAdditionID, 55ee, uint', + 'Name, 536e, str', + 'Language, 22b59c, str', + 'CodecID, 86, str', + 'CodecPrivate, 63a2, binary', + 'CodecName, 258688, str', + 'CodecDecodeAll, aa, uint', + 'CodecDelay, 56aa, uint', + 'SeekPreRoll, 56bb, uint', + 'Video, e0, sub', ( + 'FlagInterlaced, 9a, uint', + 'PixelWidth, b0, uint', + 'PixelHeight, ba, uint', + 'DisplayWidth, 54b0, uint', + 'DisplayHeight, 54ba, uint', + 'DisplayUnit, 54b2, uint', + 'PixelCropTop, 54bb, uint', + 'PixelCropLeft, 54cc, uint', + 'PixelCropRight, 54dd, uint', + 'PixelCropBottom, 54aa, uint', + 'FrameRate, 2383e3, float', + 'ColourSpace, 2eb524, binary', + 'StereoMode, 53b8, uint', + 'Colour, 55b0, sub', ( + 'MatrixCoefficients, 55B1, uint', + 'BitsPerChannel, 55B2, uint', + 'ChromaSubsamplingHorz, 55B3, uint', + 'ChromaSubsamplingVert, 55B4, uint', + 'CbSubsamplingHorz, 55B5, uint', + 'CbSubsamplingVert, 55B6, uint', + 'ChromaSitingHorz, 55B7, uint', + 'ChromaSitingVert, 55B8, uint', + 'Range, 55B9, uint', + 'TransferCharacteristics, 55BA, uint', + 'Primaries, 55BB, uint', + 'MaxCLL, 55BC, uint', + 'MaxFALL, 55BD, uint', + 'MasteringMetadata, 55D0, sub', ( + 'PrimaryRChromaticityX, 55D1, float', + 'PrimaryRChromaticityY, 55D2, float', + 'PrimaryGChromaticityX, 55D3, float', + 'PrimaryGChromaticityY, 55D4, float', + 'PrimaryBChromaticityX, 55D5, float', + 'PrimaryBChromaticityY, 55D6, float', + 'WhitePointChromaticityX, 55D7, float', + 'WhitePointChromaticityY, 55D8, float', + 'LuminanceMax, 55D9, float', + 'LuminanceMin, 55DA, float', + ), + ), + 'Projection, 7670, sub', ( + 'ProjectionType, 7671, uint', + 'ProjectionPrivate, 7672, binary', + 'ProjectionPoseYaw, 7673, float', + 'ProjectionPosePitch, 7674, float', + 'ProjectionPoseRoll, 7675, float', + ), + ), + 'Audio, e1, sub', ( + 'SamplingFrequency, b5, float', + 'OutputSamplingFrequency, 78b5, float', + 'Channels, 9f, uint', + 'BitDepth, 6264, uint', + ), + 'ContentEncodings, 6d80, sub', ( + 'ContentEncoding*, 6240, sub', ( + 'ContentEncodingOrder, 5031, uint', + 'ContentEncodingScope, 5032, uint', + 'ContentEncodingType, 5033, uint', + 'ContentCompression, 5034, sub', ( + 'ContentCompAlgo, 4254, uint', + 'ContentCompSettings, 4255, binary', + ), + ), + ), + ), + ), + + 'Cues, 1c53bb6b, sub', ( + 'CuePoint*, bb, sub', ( + 'CueTime, b3, uint', + 'CueTrackPositions*, b7, sub', ( + 'CueTrack, f7, uint', + 'CueClusterPosition, f1, uint', + 'CueRelativePosition, f0, uint', + 'CueDuration, b2, uint', + ), + ), + ), + + 'Attachments, 1941a469, sub', ( + 'AttachedFile*, 61a7, sub', ( + 'FileDescription, 467e, str', + 'FileName, 466e, str', + 'FileMimeType, 4660, str', + 'FileData, 465c, binary', + 'FileUID, 46ae, uint', + ), + ), + + 'Chapters, 1043a770, sub', ( + 'EditionEntry*, 45b9, sub', ( + 'EditionUID, 45bc, uint', + 'EditionFlagHidden, 45bd, uint', + 'EditionFlagDefault, 45db, uint', + 'EditionFlagOrdered, 45dd, uint', + 'ChapterAtom*, b6, sub', ( + 'ChapterUID, 73c4, uint', + 'ChapterTimeStart, 91, uint', + 'ChapterTimeEnd, 92, uint', + 'ChapterFlagHidden, 98, uint', + 'ChapterFlagEnabled, 4598, uint', + 'ChapterSegmentUID, 6e67, binary', + 'ChapterSegmentEditionUID, 6ebc, uint', + 'ChapterDisplay*, 80, sub', ( + 'ChapString, 85, str', + 'ChapLanguage*, 437c, str', + 'ChapCountry*, 437e, str', + ), + ), + ), + ), + 'Tags*, 1254c367, sub', ( + 'Tag*, 7373, sub', ( + 'Targets, 63c0, sub', ( + 'TargetTypeValue, 68ca, uint', + 'TargetType, 63ca, str', + 'TargetTrackUID, 63c5, uint', + 'TargetEditionUID, 63c9, uint', + 'TargetChapterUID, 63c4, uint', + 'TargetAttachmentUID, 63c6, uint', + ), + 'SimpleTag*, 67c8, sub', ( + 'TagName, 45a3, str', + 'TagLanguage, 447a, str', + 'TagString, 4487, str', + 'TagDefault, 4484, uint', + ), + ), + ), + ), +) + + +import sys +from math import ldexp +from binascii import hexlify + +def byte2num(s): + return int(hexlify(s), 16) + +class EOF(Exception): pass + +def camelcase_to_words(name): + parts = [] + start = 0 + for i in range(1, len(name)): + if name[i].isupper() and (name[i-1].islower() or + name[i+1:i+2].islower()): + parts.append(name[start:i]) + start = i + parts.append(name[start:]) + return '_'.join(parts).lower() + +class MatroskaElement(object): + + def __init__(self, name, elid, valtype, namespace): + self.name = name + self.definename = '{0}_ID_{1}'.format(namespace, name.upper()) + self.fieldname = camelcase_to_words(name) + self.structname = 'ebml_' + self.fieldname + self.elid = elid + self.valtype = valtype + if valtype == 'sub': + self.ebmltype = 'EBML_TYPE_SUBELEMENTS' + self.valname = 'struct ' + self.structname + else: + self.ebmltype = 'EBML_TYPE_' + valtype.upper() + try: + self.valname = {'uint': 'uint64_t', 'str': 'char *', + 'binary': 'bstr', 'ebml_id': 'uint32_t', + 'float': 'double', 'sint': 'int64_t', + }[valtype] + except KeyError: + raise SyntaxError('Unrecognized value type ' + valtype) + self.subelements = () + + def add_subelements(self, subelements): + self.subelements = subelements + self.subids = {x[0].elid for x in subelements} + +elementd = {} +elementlist = [] +def parse_elems(l, namespace): + subelements = [] + for el in l: + if isinstance(el, str): + name, hexid, eltype = [x.strip() for x in el.split(',')] + hexid = hexid.lower() + multiple = name.endswith('*') + name = name.strip('*') + new = MatroskaElement(name, hexid, eltype, namespace) + elementd[hexid] = new + elementlist.append(new) + subelements.append((new, multiple)) + else: + new.add_subelements(parse_elems(el, namespace)) + return subelements + +parse_elems(elements_ebml, 'EBML') +parse_elems(elements_matroska, 'MATROSKA') + +def printf(out, *args): + out.write(' '.join(str(x) for x in args)) + out.write('\n') + +def generate_C_header(out): + printf(out, '// Generated by TOOLS/matroska.py, do not edit manually') + printf(out) + + for el in elementlist: + printf(out, '#define {0.definename:40} 0x{0.elid}'.format(el)) + + printf(out) + + for el in reversed(elementlist): + if not el.subelements: + continue + printf(out) + printf(out, 'struct {0.structname} {{'.format(el)) + l = max(len(subel.valname) for subel, multiple in el.subelements)+1 + for subel, multiple in el.subelements: + printf(out, ' {e.valname:{l}} {star}{e.fieldname};'.format( + e=subel, l=l, star=' *'[multiple])) + printf(out) + for subel, multiple in el.subelements: + printf(out, ' int n_{0.fieldname};'.format(subel)) + printf(out, '};') + + for el in elementlist: + if not el.subelements: + continue + printf(out, 'extern const struct ebml_elem_desc {0.structname}_desc;'.format(el)) + + printf(out) + printf(out, '#define MAX_EBML_SUBELEMENTS', max(len(el.subelements) + for el in elementlist)) + + +def generate_C_definitions(out): + printf(out, '// Generated by TOOLS/matroska.py, do not edit manually') + printf(out) + for el in reversed(elementlist): + printf(out) + if el.subelements: + printf(out, '#define N', el.fieldname) + printf(out, 'E_S("{0}", {1})'.format(el.name, len(el.subelements))) + for subel, multiple in el.subelements: + printf(out, 'F({0.definename}, {0.fieldname}, {1})'.format( + subel, int(multiple))) + printf(out, '}};') + printf(out, '#undef N') + else: + printf(out, 'E("{0.name}", {0.fieldname}, {0.ebmltype})'.format(el)) + +def read(s, length): + t = s.read(length) + if len(t) != length: + raise EOF + return t + +def read_id(s): + t = read(s, 1) + i = 0 + mask = 128 + if ord(t) == 0: + raise SyntaxError + while not ord(t) & mask: + i += 1 + mask >>= 1 + t += read(s, i) + return t + +def read_vint(s): + t = read(s, 1) + i = 0 + mask = 128 + if ord(t) == 0: + raise SyntaxError + while not ord(t) & mask: + i += 1 + mask >>= 1 + t = bytes((ord(t) & (mask - 1),)) + t += read(s, i) + return i+1, byte2num(t) + +def read_str(s, length): + return read(s, length) + +def read_uint(s, length): + t = read(s, length) + return byte2num(t) + +def read_sint(s, length): + i = read_uint(s, length) + mask = 1 << (length * 8 - 1) + if i & mask: + i -= 2 * mask + return i + +def read_float(s, length): + t = read(s, length) + i = byte2num(t) + if length == 4: + f = ldexp((i & 0x7fffff) + (1 << 23), (i >> 23 & 0xff) - 150) + if i & (1 << 31): + f = -f + elif length == 8: + f = ldexp((i & ((1 << 52) - 1)) + (1 << 52), (i >> 52 & 0x7ff) - 1075) + if i & (1 << 63): + f = -f + else: + raise SyntaxError + return f + +def parse_one(s, depth, parent, maxlen): + elid = hexlify(read_id(s)).decode('ascii') + elem = elementd.get(elid) + size, length = read_vint(s) + this_length = len(elid) / 2 + size + length + if elem is not None: + if elem.valtype != 'skip': + print(" " * depth, '[' + elid + ']', elem.name, 'size:', length, 'value:', end=' ') + if elem.valtype == 'sub': + print('subelements:') + while length > 0: + length -= parse_one(s, depth + 1, elem, length) + if length < 0: + raise SyntaxError + elif elem.valtype == 'str': + print('string', repr(read_str(s, length).decode('utf8', 'replace'))) + elif elem.valtype in ('binary', 'ebml_id'): + t = read_str(s, length) + dec = '' + if elem.valtype == 'ebml_id': + idelem = elementd.get(hexlify(t).decode('ascii')) + if idelem is None: + dec = '(UNKNOWN)' + else: + dec = '({0.name})'.format(idelem) + if len(t) < 20: + t = hexlify(t).decode('ascii') + else: + t = '<{0} bytes>'.format(len(t)) + print('binary', t, dec) + elif elem.valtype == 'uint': + print('uint', read_uint(s, length)) + elif elem.valtype == 'sint': + print('sint', read_sint(s, length)) + elif elem.valtype == 'float': + print('float', read_float(s, length)) + elif elem.valtype == 'skip': + read(s, length) + else: + raise NotImplementedError + else: + print(" " * depth, '[' + elid + '] Unknown element! size:', length) + read(s, length) + return this_length + +if __name__ == "__main__": + def parse_toplevel(s): + parse_one(s, 0, None, 1 << 63) + + if sys.argv[1] == '--generate-header': + generate_C_header(open(sys.argv[2], "w")) + elif sys.argv[1] == '--generate-definitions': + generate_C_definitions(open(sys.argv[2], "w")) + else: + s = open(sys.argv[1], "rb") + while 1: + start = s.tell() + try: + parse_toplevel(s) + except EOF: + if s.tell() != start: + raise Exception("Unexpected end of file") + break diff --git a/TOOLS/mpv-osd-symbols.sfdir/.notdef.glyph b/TOOLS/mpv-osd-symbols.sfdir/.notdef.glyph new file mode 100644 index 0000000..99cdece --- /dev/null +++ b/TOOLS/mpv-osd-symbols.sfdir/.notdef.glyph @@ -0,0 +1,6 @@ +StartChar: .notdef +Encoding: 65536 -1 0 +Width: 400 +Flags: W +LayerCount: 2 +EndChar diff --git a/TOOLS/mpv-osd-symbols.sfdir/font.props b/TOOLS/mpv-osd-symbols.sfdir/font.props new file mode 100644 index 0000000..8198d27 --- /dev/null +++ b/TOOLS/mpv-osd-symbols.sfdir/font.props @@ -0,0 +1,77 @@ +SplineFontDB: 3.0 +FontName: mpv-osd-symbols-Regular +FullName: mpv-osd-symbols Regular +FamilyName: mpv-osd-symbols +Weight: Normal +Copyright: This is generated file. +Version: 001.000 +ItalicAngle: 0 +UnderlinePosition: -133 +UnderlineWidth: 50 +Ascent: 800 +Descent: 200 +InvalidEm: 0 +sfntRevision: 0x00010000 +LayerCount: 2 +Layer: 0 0 "Back" 1 +Layer: 1 0 "Fore" 0 +XUID: [1021 879 -1597228462 15927] +StyleMap: 0x0040 +FSType: 8 +OS2Version: 3 +OS2_WeightWidthSlopeOnly: 0 +OS2_UseTypoMetrics: 0 +CreationTime: 1408646554 +ModificationTime: 1576096543 +PfmFamily: 81 +TTFWeight: 400 +TTFWidth: 5 +LineGap: 0 +VLineGap: 0 +Panose: 0 0 5 0 0 0 0 0 0 0 +OS2TypoAscent: 800 +OS2TypoAOffset: 0 +OS2TypoDescent: -200 +OS2TypoDOffset: 0 +OS2TypoLinegap: 90 +OS2WinAscent: 1000 +OS2WinAOffset: 0 +OS2WinDescent: 200 +OS2WinDOffset: 0 +HheadAscent: 1000 +HheadAOffset: 0 +HheadDescent: -200 +HheadDOffset: 0 +OS2SubXSize: 650 +OS2SubYSize: 600 +OS2SubXOff: 0 +OS2SubYOff: 75 +OS2SupXSize: 650 +OS2SupYSize: 600 +OS2SupXOff: 0 +OS2SupYOff: 350 +OS2StrikeYSize: 50 +OS2StrikeYPos: 220 +OS2Vendor: 'PfEd' +OS2CodePages: 00000001.00000000 +OS2UnicodeRanges: 00000000.00000000.00000000.00000000 +DEI: 91125 +LangName: 1033 "" "" "Regular" "1.000;PfEd;mpv-osd-symbols-Regular" "mpv-osd-symbols" "Version 1.000;PS 001.000;hotconv 1.0.70;makeotf.lib2.5.58329" +Encoding: UnicodeBmp +UnicodeInterp: none +NameList: AGL For New Fonts +DisplaySize: -72 +AntiAlias: 1 +FitToEm: 0 +WinInfo: 57600 8 2 +BeginPrivate: 8 +BlueValues 31 [-10 0 640 650 720 730 800 810] +BlueScale 5 0.037 +BlueShift 1 0 +BlueFuzz 1 0 +StdHW 4 [65] +StdVW 4 [65] +StemSnapH 8 [65 800] +StemSnapV 8 [65 150] +EndPrivate +EndSplineFont diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE001.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE001.glyph new file mode 100644 index 0000000..ebfb715 --- /dev/null +++ b/TOOLS/mpv-osd-symbols.sfdir/uniE001.glyph @@ -0,0 +1,16 @@ +StartChar: uniE001 +Encoding: 57345 57345 1 +Width: 880 +GlyphClass: 2 +Flags: MW +HStem: 780 20G<200 200> +VStem: 200 375<400 400 400 800> +LayerCount: 2 +Fore +SplineSet +575 400 m 1 + 200 0 l 1 + 200 800 l 1 + 575 400 l 1 +EndSplineSet +EndChar diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE002.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE002.glyph new file mode 100644 index 0000000..f47c153 --- /dev/null +++ b/TOOLS/mpv-osd-symbols.sfdir/uniE002.glyph @@ -0,0 +1,22 @@ +StartChar: uniE002 +Encoding: 57346 57346 2 +Width: 880 +GlyphClass: 2 +Flags: MW +HStem: 0 800<200 350 200 350 200 500 500 650> +VStem: 200 150<0 800 0 800> 500 150<0 800 0 800> +LayerCount: 2 +Fore +SplineSet +350 800 m 1 + 350 0 l 1 + 200 0 l 1 + 200 800 l 1 + 350 800 l 1 +650 800 m 1 + 650 0 l 1 + 500 0 l 1 + 500 800 l 1 + 650 800 l 1 +EndSplineSet +EndChar diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE003.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE003.glyph new file mode 100644 index 0000000..ba45630 --- /dev/null +++ b/TOOLS/mpv-osd-symbols.sfdir/uniE003.glyph @@ -0,0 +1,17 @@ +StartChar: uniE003 +Encoding: 57347 57347 3 +Width: 880 +GlyphClass: 2 +Flags: MW +HStem: 0 720<110 775 110 775> +VStem: 110 665<0 720 0 720> +LayerCount: 2 +Fore +SplineSet +775 720 m 1 + 775 0 l 1 + 110 0 l 1 + 110 720 l 1 + 775 720 l 1 +EndSplineSet +EndChar diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE004.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE004.glyph new file mode 100644 index 0000000..62ce5f7 --- /dev/null +++ b/TOOLS/mpv-osd-symbols.sfdir/uniE004.glyph @@ -0,0 +1,20 @@ +StartChar: uniE004 +Encoding: 57348 57348 4 +Width: 880 +GlyphClass: 2 +Flags: MW +HStem: 780 20G<423 423 813 813> +VStem: 48 765<400 400> +LayerCount: 2 +Fore +SplineSet +423 800 m 1 + 423 0 l 1 + 48 400 l 1 + 423 800 l 1 +813 800 m 1 + 813 0 l 1 + 438 400 l 1 + 813 800 l 1 +EndSplineSet +EndChar diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE005.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE005.glyph new file mode 100644 index 0000000..26db474 --- /dev/null +++ b/TOOLS/mpv-osd-symbols.sfdir/uniE005.glyph @@ -0,0 +1,20 @@ +StartChar: uniE005 +Encoding: 57349 57349 5 +Width: 880 +GlyphClass: 2 +Flags: MW +HStem: 780 20G<95 95 485 485> +VStem: 95 765<400 400 400 800 400 800> +LayerCount: 2 +Fore +SplineSet +470 400 m 1 + 95 0 l 1 + 95 800 l 1 + 470 400 l 1 +860 400 m 1 + 485 0 l 1 + 485 800 l 1 + 860 400 l 1 +EndSplineSet +EndChar diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE006.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE006.glyph new file mode 100644 index 0000000..474d3c1 --- /dev/null +++ b/TOOLS/mpv-osd-symbols.sfdir/uniE006.glyph @@ -0,0 +1,47 @@ +StartChar: uniE006 +Encoding: 57350 57350 6 +Width: 880 +GlyphClass: 2 +Flags: MW +HStem: -10 11<396 461 396 543> 196 171<397 461 396 462> 401 31 801 9<397 462 397 397> +VStem: 20 9<369 434 369 513> 224 173<369 408 367 434 367 434> 363 34 397 65<367 404 367 408 367 404 606 801> 462 173<368 404 404 404> 830 10<368 433 433 433> +LayerCount: 2 +Fore +SplineSet +430 810 m 0x9940 + 656 810 840 626 840 400 c 0 + 840 174 656 -10 430 -10 c 0 + 204 -10 20 174 20 400 c 0 + 20 626 204 810 430 810 c 0x9940 +397 606 m 1x5280 + 462 606 l 1 + 462 801 l 1 + 397 801 l 1 + 397 606 l 1x5280 +462 367 m 1 + 462 404 l 1 + 710 627 l 1 + 683 657 l 1 + 439 437 l 1 + 363 592 l 1 + 318 570 l 1x4280 + 397 408 l 1 + 397 367 l 1x44 + 462 367 l 1 +224 434 m 1 + 29 434 l 1 + 29 369 l 1 + 224 369 l 1 + 224 434 l 1 +830 368 m 1 + 830 433 l 1 + 635 433 l 1 + 635 368 l 1 + 830 368 l 1 +461 196 m 1 + 396 196 l 1 + 396 1 l 1 + 461 1 l 1 + 461 196 l 1 +EndSplineSet +EndChar diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE007.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE007.glyph new file mode 100644 index 0000000..719d9fa --- /dev/null +++ b/TOOLS/mpv-osd-symbols.sfdir/uniE007.glyph @@ -0,0 +1,21 @@ +StartChar: uniE007 +Encoding: 57351 57351 7 +Width: 880 +GlyphClass: 2 +Flags: MW +HStem: -10 80<339 430 339 543> 730 80<339 430> +VStem: 20 80<309 491 309 513> +LayerCount: 2 +Fore +SplineSet +430 -10 m 0 + 204 -10 20 174 20 400 c 0 + 20 626 204 810 430 810 c 0 + 656 810 840 626 840 400 c 0 + 840 174 656 -10 430 -10 c 0 +430 70 m 1 + 430 730 l 1 + 248 730 100 582 100 400 c 0 + 100 218 248 70 430 70 c 1 +EndSplineSet +EndChar diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE008.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE008.glyph new file mode 100644 index 0000000..b36acfb --- /dev/null +++ b/TOOLS/mpv-osd-symbols.sfdir/uniE008.glyph @@ -0,0 +1,37 @@ +StartChar: uniE008 +Encoding: 57352 57352 8 +Width: 880 +GlyphClass: 2 +Flags: MW +HStem: -10 80<339 521 339 543> 240 160<269 313 546 590> 480 160<408 452> 730 80<339 521> +VStem: 20 80<309 491 309 513> 211 160<298 342> 350 160<538 582> 488 160<298 342> 760 80<309 491> +LayerCount: 2 +Fore +SplineSet +430 -10 m 0xfa80 + 204 -10 20 174 20 400 c 0 + 20 626 204 810 430 810 c 0 + 656 810 840 626 840 400 c 0 + 840 174 656 -10 430 -10 c 0xfa80 +430 70 m 0 + 612 70 760 218 760 400 c 0 + 760 582 612 730 430 730 c 0 + 248 730 100 582 100 400 c 0 + 100 218 248 70 430 70 c 0 +430 480 m 0 + 386 480 350 516 350 560 c 0 + 350 604 386 640 430 640 c 0 + 474 640 510 604 510 560 c 0 + 510 516 474 480 430 480 c 0 +291 240 m 0xfd80 + 247 240 211 276 211 320 c 0 + 211 364 247 400 291 400 c 0 + 335 400 371 364 371 320 c 0 + 371 276 335 240 291 240 c 0xfd80 +568 240 m 0 + 524 240 488 276 488 320 c 0 + 488 364 524 400 568 400 c 0 + 612 400 648 364 648 320 c 0 + 648 276 612 240 568 240 c 0 +EndSplineSet +EndChar diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE009.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE009.glyph new file mode 100644 index 0000000..26a2f37 --- /dev/null +++ b/TOOLS/mpv-osd-symbols.sfdir/uniE009.glyph @@ -0,0 +1,17 @@ +StartChar: uniE009 +Encoding: 57353 57353 9 +Width: 880 +Flags: HMW +LayerCount: 2 +Fore +SplineSet +716 10 m 1 + 378 244 l 1 + 165 244 l 1 + 165 559 l 1 + 379 559 l 1 + 716 793 l 1 + 716 10 l 1 +EndSplineSet +Validated: 1 +EndChar diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE00A.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE00A.glyph new file mode 100644 index 0000000..634249f --- /dev/null +++ b/TOOLS/mpv-osd-symbols.sfdir/uniE00A.glyph @@ -0,0 +1,50 @@ +StartChar: uniE00A +Encoding: 57354 57354 10 +Width: 880 +GlyphClass: 2 +Flags: MW +HStem: 252 48<406 406 456 456> 375 50<57 282 57 282 578 805> 500 48<406 406> 627 20G<149 149 713 713> +VStem: 282 48<375 375 425 425> 406 50<26 252 26 252 548 774> 530 48<375 375> +LayerCount: 2 +Fore +SplineSet +805 375 m 1 + 578 375 l 1 + 574 352 565 331 553 313 c 1 + 713 153 l 1 + 678 118 l 1 + 518 278 l 1 + 500 265 479 256 456 252 c 1 + 456 26 l 1 + 406 26 l 1 + 406 252 l 1 + 383 256 362 265 343 277 c 1 + 184 118 l 1 + 149 153 l 1 + 308 312 l 1 + 295 330 286 352 282 375 c 1 + 57 375 l 1 + 57 425 l 1 + 282 425 l 1 + 286 448 295 470 308 488 c 1 + 149 647 l 1 + 184 682 l 1 + 343 523 l 1 + 362 535 383 544 406 548 c 1 + 406 774 l 1 + 456 774 l 1 + 456 548 l 1 + 479 544 500 535 518 522 c 1 + 678 682 l 1 + 713 647 l 1 + 553 487 l 1 + 565 469 574 448 578 425 c 1 + 805 425 l 1 + 805 375 l 1 +430 300 m 0 + 485 300 530 345 530 400 c 0 + 530 455 485 500 430 500 c 0 + 375 500 330 455 330 400 c 0 + 330 345 375 300 430 300 c 0 +EndSplineSet +EndChar diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE00B.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE00B.glyph new file mode 100644 index 0000000..21e0966 --- /dev/null +++ b/TOOLS/mpv-osd-symbols.sfdir/uniE00B.glyph @@ -0,0 +1,27 @@ +StartChar: uniE00B +Encoding: 57355 57355 11 +Width: 880 +GlyphClass: 2 +Flags: MW +HStem: -10 244<341 519 341 543> 566 54<413.5 446.5 413.5 519> 740 70<413.5 446.5> +VStem: 20 88<354 446 354 513> 752 88<354 446> +LayerCount: 2 +Fore +SplineSet +430 -10 m 0 + 204 -10 20 174 20 400 c 0 + 20 626 204 810 430 810 c 0 + 656 810 840 626 840 400 c 0 + 840 174 656 -10 430 -10 c 0 +430 620 m 0 + 463 620 490 647 490 680 c 0 + 490 713 463 740 430 740 c 0 + 397 740 370 713 370 680 c 0 + 370 647 397 620 430 620 c 0 +430 234 m 0 + 608 234 752 308 752 400 c 0 + 752 492 608 566 430 566 c 0 + 252 566 108 492 108 400 c 0 + 108 308 252 234 430 234 c 0 +EndSplineSet +EndChar diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE010.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE010.glyph new file mode 100644 index 0000000..519e34a --- /dev/null +++ b/TOOLS/mpv-osd-symbols.sfdir/uniE010.glyph @@ -0,0 +1,21 @@ +StartChar: uniE010 +Encoding: 57360 57360 12 +Width: 334 +GlyphClass: 2 +Flags: MW +HStem: 0 90<221 258 221 258> 550 90<221 258 221 221> +VStem: 76 145<90 550 90 640 90 640> +LayerCount: 2 +Fore +SplineSet +258 640 m 1 + 258 550 l 1 + 221 550 l 1 + 221 90 l 1 + 258 90 l 1 + 258 0 l 1 + 76 0 l 1 + 76 640 l 1 + 258 640 l 1 +EndSplineSet +EndChar diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE011.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE011.glyph new file mode 100644 index 0000000..b3c7b56 --- /dev/null +++ b/TOOLS/mpv-osd-symbols.sfdir/uniE011.glyph @@ -0,0 +1,17 @@ +StartChar: uniE011 +Encoding: 57361 57361 13 +Width: 334 +GlyphClass: 2 +Flags: MW +HStem: 0 640<84 250 84 250> +VStem: 84 166<0 640 0 640> +LayerCount: 2 +Fore +SplineSet +250 640 m 1 + 250 0 l 1 + 84 0 l 1 + 84 640 l 1 + 250 640 l 1 +EndSplineSet +EndChar diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE012.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE012.glyph new file mode 100644 index 0000000..86c07cd --- /dev/null +++ b/TOOLS/mpv-osd-symbols.sfdir/uniE012.glyph @@ -0,0 +1,21 @@ +StartChar: uniE012 +Encoding: 57362 57362 14 +Width: 334 +GlyphClass: 2 +Flags: MW +HStem: 0 90<76 113 76 258 76 113> 550 90<76 113 76 258> +VStem: 113 145<90 550 550 550> +LayerCount: 2 +Fore +SplineSet +113 90 m 1 + 113 550 l 1 + 76 550 l 1 + 76 640 l 1 + 258 640 l 1 + 258 0 l 1 + 76 0 l 1 + 76 90 l 1 + 113 90 l 1 +EndSplineSet +EndChar diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE013.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE013.glyph new file mode 100644 index 0000000..b456777 --- /dev/null +++ b/TOOLS/mpv-osd-symbols.sfdir/uniE013.glyph @@ -0,0 +1,17 @@ +StartChar: uniE013 +Encoding: 57363 57363 15 +Width: 334 +GlyphClass: 2 +Flags: MW +HStem: 255 130<102 232 102 232> +VStem: 102 130<255 385 255 385> +LayerCount: 2 +Fore +SplineSet +232 385 m 1 + 232 255 l 1 + 102 255 l 1 + 102 385 l 1 + 232 385 l 1 +EndSplineSet +EndChar diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE101.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE101.glyph new file mode 100644 index 0000000..8e0d4b6 --- /dev/null +++ b/TOOLS/mpv-osd-symbols.sfdir/uniE101.glyph @@ -0,0 +1,16 @@ +StartChar: uniE101 +Encoding: 57601 57601 16 +Width: 880 +GlyphClass: 2 +Flags: MW +HStem: 780 20G<222 222> +VStem: 222 600<400 400 400 800 400 800> +LayerCount: 2 +Fore +SplineSet +822 400 m 1 + 222 0 l 1 + 222 800 l 1 + 822 400 l 1 +EndSplineSet +EndChar diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE104.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE104.glyph new file mode 100644 index 0000000..7c7ba13 --- /dev/null +++ b/TOOLS/mpv-osd-symbols.sfdir/uniE104.glyph @@ -0,0 +1,25 @@ +StartChar: uniE104 +Encoding: 57604 57604 17 +Width: 880 +GlyphClass: 2 +Flags: MW +HStem: 0 800<3 126 3 126 3 502 3 878> 780 20G<3 126 126 126 502 502 878 878> +VStem: 3 123<0 800 0 800> +LayerCount: 2 +Fore +SplineSet +126 800 m 1x60 + 126 0 l 1 + 3 0 l 1xa0 + 3 800 l 1 + 126 800 l 1x60 +502 800 m 1 + 502 0 l 1 + 127 400 l 1 + 502 800 l 1 +878 800 m 1 + 878 0 l 1 + 503 400 l 1 + 878 800 l 1 +EndSplineSet +EndChar diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE105.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE105.glyph new file mode 100644 index 0000000..bb0f87f --- /dev/null +++ b/TOOLS/mpv-osd-symbols.sfdir/uniE105.glyph @@ -0,0 +1,25 @@ +StartChar: uniE105 +Encoding: 57605 57605 18 +Width: 880 +GlyphClass: 2 +Flags: MW +HStem: 0 800<3 3 3 379 3 755 755 878> 780 20G<3 3 379 379 755 878 878 878> +VStem: 755 123<0 800> +LayerCount: 2 +Fore +SplineSet +3 0 m 1x60 + 3 800 l 1 + 378 400 l 1 + 3 0 l 1x60 +379 0 m 1xa0 + 379 800 l 1 + 754 400 l 1 + 379 0 l 1xa0 +878 0 m 1 + 755 0 l 1 + 755 800 l 1 + 878 800 l 1 + 878 0 l 1 +EndSplineSet +EndChar diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE106.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE106.glyph new file mode 100644 index 0000000..b7d6ca2 --- /dev/null +++ b/TOOLS/mpv-osd-symbols.sfdir/uniE106.glyph @@ -0,0 +1,22 @@ +StartChar: uniE106 +Encoding: 57606 57606 19 +Width: 880 +GlyphClass: 2 +Flags: MW +HStem: 0 649<68.5 556.5> +VStem: 0 880 +LayerCount: 2 +Fore +SplineSet +880 380 m 0 + 880 230 685 101 449 101 c 0 + 390 101 336 107 283 118 c 1 + 278 49 189 0 85 0 c 0 + 52 0 24 14 0 40 c 1 + 82 48 137 93 137 158 c 0 + 137 163 137 168 135 172 c 0 + 58 217 19 273 19 380 c 0 + 19 528 258 649 449 649 c 0 + 664 649 880 508 880 380 c 0 +EndSplineSet +EndChar diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE107.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE107.glyph new file mode 100644 index 0000000..01065b6 --- /dev/null +++ b/TOOLS/mpv-osd-symbols.sfdir/uniE107.glyph @@ -0,0 +1,56 @@ +StartChar: uniE107 +Encoding: 57607 57607 20 +Width: 880 +GlyphClass: 2 +Flags: MW +HStem: 0 60<0 60 60 820> 106 50<146 343 146 343 146 390 390 641 680 735> 207 50<99 317 99 317 99 363 363 488 529 781> 580 60<60 820 60 820> +VStem: 0 60<60 580> 99 218<207 257 207 257> 146 197<106 156 106 156> 363 125<207 257 207 257> 529 112<106 257 106 257> 680 55<106 156 106 156> 820 60<0 60 60 580> +LayerCount: 2 +Fore +SplineSet +0 60 m 2xfde0 + 0 580 l 2 + 0 610 30 640 60 640 c 2 + 820 640 l 2 + 850 640 880 610 880 580 c 2 + 880 60 l 2 + 880 30 850 0 820 0 c 2 + 60 0 l 2 + 30 0 0 30 0 60 c 2xfde0 +60 60 m 1 + 820 60 l 1 + 820 580 l 1 + 60 580 l 1 + 60 60 l 1 +317 257 m 1 + 317 207 l 1 + 99 207 l 1 + 99 257 l 1 + 317 257 l 1 +488 257 m 1 + 488 207 l 1 + 363 207 l 1 + 363 257 l 1 + 488 257 l 1 +781 257 m 1 + 781 207 l 1 + 529 207 l 1 + 529 257 l 1 + 781 257 l 1 +343 156 m 1x4280 + 343 106 l 1 + 146 106 l 1 + 146 156 l 1 + 343 156 l 1x4280 +641 156 m 1 + 641 106 l 1 + 390 106 l 1 + 390 156 l 1x41c0 + 641 156 l 1 +735 156 m 1 + 735 106 l 1 + 680 106 l 1 + 680 156 l 1 + 735 156 l 1 +EndSplineSet +EndChar diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE108.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE108.glyph new file mode 100644 index 0000000..83659e3 --- /dev/null +++ b/TOOLS/mpv-osd-symbols.sfdir/uniE108.glyph @@ -0,0 +1,39 @@ +StartChar: uniE108 +Encoding: 57608 57608 21 +Width: 880 +GlyphClass: 2 +Flags: MW +HStem: 80 60<0 60 60 820> 321 159<274 606 274 606> 660 60<60 820 60 820> +VStem: 0 60<140 660> 274 332<321 480 321 480> 820 60<80 140 140 660> +LayerCount: 2 +Fore +SplineSet +0 140 m 2 + 0 660 l 2 + 0 690 30 720 60 720 c 2 + 820 720 l 2 + 850 720 880 690 880 660 c 2 + 880 140 l 2 + 880 110 850 80 820 80 c 2 + 60 80 l 2 + 30 80 0 110 0 140 c 2 +60 140 m 1 + 820 140 l 1 + 820 660 l 1 + 60 660 l 1 + 60 140 l 1 +580 600 m 1 + 760 600 l 1 + 760 420 l 1 + 580 600 l 1 +606 480 m 1 + 606 321 l 1 + 274 321 l 1 + 274 480 l 1 + 606 480 l 1 +300 200 m 1 + 120 200 l 1 + 120 380 l 1 + 300 200 l 1 +EndSplineSet +EndChar diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE109.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE109.glyph new file mode 100644 index 0000000..4038f3d --- /dev/null +++ b/TOOLS/mpv-osd-symbols.sfdir/uniE109.glyph @@ -0,0 +1,39 @@ +StartChar: uniE109 +Encoding: 57609 57609 22 +Width: 880 +GlyphClass: 2 +Flags: MW +HStem: 80 60<0 60 60 820> 296 209<285 595 285 595> 660 60<60 820 60 820> +VStem: 0 60<140 660> 120 107<400 400 400 510> 285 310<296 505 296 505> 653 107<400 400> 820 60<80 140 140 660> +LayerCount: 2 +Fore +SplineSet +0 140 m 2 + 0 660 l 2 + 0 690 30 720 60 720 c 2 + 820 720 l 2 + 850 720 880 690 880 660 c 2 + 880 140 l 2 + 880 110 850 80 820 80 c 2 + 60 80 l 2 + 30 80 0 110 0 140 c 2 +60 140 m 1 + 820 140 l 1 + 820 660 l 1 + 60 660 l 1 + 60 140 l 1 +227 400 m 1 + 120 290 l 1 + 120 510 l 1 + 227 400 l 1 +760 290 m 1 + 653 400 l 1 + 760 510 l 1 + 760 290 l 1 +595 505 m 1 + 595 296 l 1 + 285 296 l 1 + 285 505 l 1 + 595 505 l 1 +EndSplineSet +EndChar diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE10A.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE10A.glyph new file mode 100644 index 0000000..029bf74 --- /dev/null +++ b/TOOLS/mpv-osd-symbols.sfdir/uniE10A.glyph @@ -0,0 +1,36 @@ +StartChar: uniE10A +Encoding: 57610 57610 23 +Width: 1977 +GlyphClass: 2 +Flags: HMWO +HStem: 242 248<2 155 2 156> +LayerCount: 2 +Fore +SplineSet +398 58 m 1 + 155 242 l 1 + 2 242 l 1 + 2 490 l 1 + 156 490 l 1 + 398 674 l 1 + 398 58 l 1 +809 524 m 0 + 814 524 819 523 823 519 c 0 + 831 511 831 499 823 491 c 2 + 697 365 l 1 + 823 239 l 2 + 830 232 830 221 823 213 c 0 + 816 206 804 206 797 213 c 2 + 671 339 l 1 + 545 213 l 2 + 538 206 525 206 518 213 c 0 + 510 221 510 233 518 241 c 2 + 643 367 l 1 + 517 493 l 2 + 510 500 510 511 517 519 c 0 + 525 526 536 526 544 519 c 2 + 670 393 l 1 + 796 519 l 2 + 799 523 804 524 809 524 c 0 +EndSplineSet +EndChar diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE10B.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE10B.glyph new file mode 100644 index 0000000..780b4e4 --- /dev/null +++ b/TOOLS/mpv-osd-symbols.sfdir/uniE10B.glyph @@ -0,0 +1,26 @@ +StartChar: uniE10B +Encoding: 57611 57611 24 +Width: 1977 +GlyphClass: 2 +Flags: MW +HStem: 242 248<2 155 2 156> +VStem: 603 47<326.5 405> +LayerCount: 2 +Fore +SplineSet +650 366 m 0 + 650 281 615 198 545 119 c 1 + 507 142 l 1 + 571 213 603 287 603 366 c 0 + 603 444 571 518 507 590 c 1 + 545 613 l 1 + 615 534 650 452 650 366 c 0 +398 58 m 1 + 155 242 l 1 + 2 242 l 1 + 2 490 l 1 + 156 490 l 1 + 398 674 l 1 + 398 58 l 1 +EndSplineSet +EndChar diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE10C.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE10C.glyph new file mode 100644 index 0000000..b17f57c --- /dev/null +++ b/TOOLS/mpv-osd-symbols.sfdir/uniE10C.glyph @@ -0,0 +1,33 @@ +StartChar: uniE10C +Encoding: 57612 57612 25 +Width: 1977 +GlyphClass: 2 +Flags: MW +HStem: 242 248<2 155 2 156> +VStem: 603 47<326.5 405> 717 47<316.5 416> +LayerCount: 2 +Fore +SplineSet +764 366 m 0 + 764 258 720 156 631 61 c 1 + 593 84 l 1 + 675 173 717 267 717 366 c 0 + 717 466 675 559 593 647 c 1 + 631 671 l 1 + 720 578 764 476 764 366 c 0 +650 366 m 0 + 650 281 615 198 545 119 c 1 + 507 142 l 1 + 571 213 603 287 603 366 c 0 + 603 444 571 518 507 590 c 1 + 545 613 l 1 + 615 534 650 452 650 366 c 0 +398 58 m 1 + 155 242 l 1 + 2 242 l 1 + 2 490 l 1 + 156 490 l 1 + 398 674 l 1 + 398 58 l 1 +EndSplineSet +EndChar diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE10D.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE10D.glyph new file mode 100644 index 0000000..24e0acf --- /dev/null +++ b/TOOLS/mpv-osd-symbols.sfdir/uniE10D.glyph @@ -0,0 +1,40 @@ +StartChar: uniE10D +Encoding: 57613 57613 26 +Width: 1977 +GlyphClass: 2 +Flags: MW +HStem: 242 248<2 155 2 156> 709 20G<717 717> +VStem: 603 47<326.5 405> 717 47<316.5 416> 830 47<304 426.5> +LayerCount: 2 +Fore +SplineSet +877 366 m 0 + 877 236 824 115 717 3 c 1 + 679 27 l 1 + 780 130 830 242 830 366 c 0 + 830 487 780 600 679 705 c 1 + 717 729 l 1 + 824 617 877 496 877 366 c 0 +764 366 m 0 + 764 258 720 156 631 61 c 1 + 593 84 l 1 + 675 173 717 267 717 366 c 0 + 717 466 675 559 593 647 c 1 + 631 671 l 1 + 720 578 764 476 764 366 c 0 +650 366 m 0 + 650 281 615 198 545 119 c 1 + 507 142 l 1 + 571 213 603 287 603 366 c 0 + 603 444 571 518 507 590 c 1 + 545 613 l 1 + 615 534 650 452 650 366 c 0 +398 58 m 1 + 155 242 l 1 + 2 242 l 1 + 2 490 l 1 + 156 490 l 1 + 398 674 l 1 + 398 58 l 1 +EndSplineSet +EndChar diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE10E.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE10E.glyph new file mode 100644 index 0000000..17b4af6 --- /dev/null +++ b/TOOLS/mpv-osd-symbols.sfdir/uniE10E.glyph @@ -0,0 +1,53 @@ +StartChar: uniE10E +Encoding: 57614 57614 27 +Width: 1977 +GlyphClass: 2 +Flags: MW +HStem: 49 95<806 818.5> 242 248<2 155 2 156> +VStem: 559 47<326.5 405> 673 47<316.5 416> 767 91<90 103.5 648 652> +LayerCount: 2 +Fore +SplineSet +720 366 m 0 + 720 258 676 156 587 61 c 1 + 549 84 l 1 + 631 173 673 267 673 366 c 0 + 673 466 631 559 549 647 c 1 + 587 671 l 1 + 676 578 720 476 720 366 c 0 +606 366 m 0 + 606 281 571 198 501 119 c 1 + 463 142 l 1 + 527 213 559 287 559 366 c 0 + 559 444 527 518 463 590 c 1 + 501 613 l 1 + 571 534 606 452 606 366 c 0 +398 58 m 1 + 155 242 l 1 + 2 242 l 1 + 2 490 l 1 + 156 490 l 1 + 398 674 l 1 + 398 58 l 1 +858 648 m 1 + 824 238 l 2 + 823 224 819 217 812 217 c 0 + 805 217 801 224 800 238 c 2 + 767 648 l 1 + 767 652 l 2 + 767 665 771 675 780 682 c 0 + 789 690 800 694 812 694 c 0 + 825 694 835 690 844 682 c 0 + 853 675 858 665 858 652 c 2 + 858 648 l 1 +858 97 m 0 + 858 83 853 72 845 63 c 0 + 836 53 825 49 812 49 c 0 + 800 49 789 53 780 63 c 0 + 771 72 767 83 767 97 c 0 + 767 110 771 121 780 130 c 0 + 789 140 800 144 812 144 c 0 + 825 144 836 140 845 130 c 0 + 853 121 858 110 858 97 c 0 +EndSplineSet +EndChar diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE110.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE110.glyph new file mode 100644 index 0000000..f4d22f5 --- /dev/null +++ b/TOOLS/mpv-osd-symbols.sfdir/uniE110.glyph @@ -0,0 +1,16 @@ +StartChar: uniE110 +Encoding: 57616 57616 28 +Width: 880 +GlyphClass: 2 +Flags: MW +HStem: 780 20G<656 656> +VStem: 56 600<400 400> +LayerCount: 2 +Fore +SplineSet +656 0 m 1 + 56 400 l 1 + 656 800 l 1 + 656 0 l 1 +EndSplineSet +EndChar diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE111.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE111.glyph new file mode 100644 index 0000000..370f803 --- /dev/null +++ b/TOOLS/mpv-osd-symbols.sfdir/uniE111.glyph @@ -0,0 +1,37 @@ +StartChar: uniE111 +Encoding: 57617 57617 29 +Width: 880 +GlyphClass: 2 +Flags: W +LayerCount: 2 +Fore +SplineSet +0 140 m 2 + 0 660 l 2 + 0 690 30 720 60 720 c 2 + 820 720 l 2 + 850 720 880 690 880 660 c 2 + 880 140 l 2 + 880 110 850 80 820 80 c 2 + 60 80 l 2 + 30 80 0 110 0 140 c 2 +60 140 m 1 + 820 140 l 1 + 820 660 l 1 + 60 660 l 1 + 60 140 l 1 +227 400 m 1 + 120 290 l 1 + 120 510 l 1 + 227 400 l 1 +760 290 m 1 + 653 400 l 1 + 760 510 l 1 + 760 290 l 1 +595 505 m 1 + 595 296 l 1 + 285 296 l 1 + 285 505 l 1 + 595 505 l 1 +EndSplineSet +EndChar diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE112.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE112.glyph new file mode 100644 index 0000000..90c29c2 --- /dev/null +++ b/TOOLS/mpv-osd-symbols.sfdir/uniE112.glyph @@ -0,0 +1,15 @@ +StartChar: uniE112 +Encoding: 57618 57618 30 +Width: 768 +VWidth: 1176 +Flags: HW +LayerCount: 2 +Fore +SplineSet +512 40 m 1 + 0 40 l 1 + 0 168 l 1 + 512 168 l 1 + 512 40 l 1 +EndSplineSet +EndChar diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE113.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE113.glyph new file mode 100644 index 0000000..6319b8f --- /dev/null +++ b/TOOLS/mpv-osd-symbols.sfdir/uniE113.glyph @@ -0,0 +1,20 @@ +StartChar: uniE113 +Encoding: 57619 57619 31 +Width: 622 +VWidth: 1178 +Flags: HW +LayerCount: 2 +Fore +SplineSet +768 42 m 5 + 0 42 l 5 + 0 746 l 5 + 768 746 l 5 + 768 42 l 5 +704 106 m 5 + 704 618 l 5 + 64 618 l 5 + 64 106 l 5 + 704 106 l 5 +EndSplineSet +EndChar diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE114.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE114.glyph new file mode 100644 index 0000000..36e6577 --- /dev/null +++ b/TOOLS/mpv-osd-symbols.sfdir/uniE114.glyph @@ -0,0 +1,31 @@ +StartChar: uniE114 +Encoding: 57620 57620 32 +Width: 896 +VWidth: 1178 +Flags: HW +LayerCount: 2 +Fore +SplineSet +768 298 m 1 + 576 298 l 1 + 576 42 l 1 + 0 42 l 1 + 0 490 l 1 + 192 490 l 1 + 192 746 l 1 + 768 746 l 1 + 768 298 l 1 +704 362 m 1 + 704 618 l 1 + 256 618 l 1 + 256 490 l 1 + 576 490 l 1 + 576 362 l 1 + 704 362 l 1 +512 106 m 1 + 512 362 l 1 + 64 362 l 1 + 64 106 l 1 + 512 106 l 1 +EndSplineSet +EndChar diff --git a/TOOLS/mpv-osd-symbols.sfdir/uniE115.glyph b/TOOLS/mpv-osd-symbols.sfdir/uniE115.glyph new file mode 100644 index 0000000..10c1195 --- /dev/null +++ b/TOOLS/mpv-osd-symbols.sfdir/uniE115.glyph @@ -0,0 +1,27 @@ +StartChar: uniE115 +Encoding: 57621 57621 33 +Width: 844 +VWidth: 1112 +Flags: HW +LayerCount: 2 +Fore +SplineSet +671 40 m 1 + 575 40 l 1 + 335 277 l 1 + 98 40 l 1 + -1 40 l 1 + -1 136 l 1 + 236 376 l 1 + -1 613 l 1 + -1 712 l 1 + 98 712 l 1 + 335 475 l 1 + 575 712 l 1 + 671 712 l 1 + 671 613 l 1 + 434 376 l 1 + 671 139 l 1 + 671 40 l 1 +EndSplineSet +EndChar diff --git a/TOOLS/mpv_identify.sh b/TOOLS/mpv_identify.sh new file mode 100755 index 0000000..fa0e134 --- /dev/null +++ b/TOOLS/mpv_identify.sh @@ -0,0 +1,149 @@ +#!/bin/sh + +# file identification script +# +# manual usage: +# mpv_identify.sh foo.mkv +# +# sh/dash/ksh/bash usage: +# . mpv_identify.sh FOO_ foo.mkv +# will fill properties into variables like FOO_length +# +# zsh usage: +# mpv_identify() { emulate -L sh; . mpv_identify.sh "$@"; } +# mpv_identify FOO_ foo.mkv +# will fill properties into variables like FOO_length +# +# When multiple files were specified, their info will be put into FOO_* for the +# first file, FOO_1_* for the second file, FOO_2_* for the third file, etc. + +__midentify__main() { + + case "$0" in + mpv_identify.sh|*/mpv_identify.sh) + # we are NOT being sourced + [ -n "$1" ] && set -- '' "$@" + ;; + esac + + if [ "$#" -lt 2 ]; then + cat >&2 <<EOF +Usage 1 (for humans only): $0 filename.mkv +will print all property values. +Note that this output really shouldn't be parsed, as the +format is subject to change. + +Usage 2 (for use by scripts): see top of this file + +NOTE: for mkv with ordered chapters, this may +not always identify the specified file, but the +file providing the first chapter. Specify +--no-ordered-chapters to prevent this. +EOF + return 2 + fi + + local LF=" +" + + local nextprefix="$1" + shift + + if [ -n "$nextprefix" ]; then + # in case of error, we always want this unset + unset "${nextprefix}path" + fi + + local allprops=" + filename + path + stream-start + stream-end + stream-length + + demuxer + + length + chapters + editions + titles + duration + + audio + audio-bitrate + audio-codec + audio-codec-name + + video + angle + video-bitrate + video-codec + video-format + video-params/aspect + container-fps + width + height + dwidth + dheight + + sub + " + # TODO add metadata support once mpv can do it + + local propstr="X-MIDENTIFY-START:$LF" + local key + for key in $allprops; do + propstr="${propstr}X-MIDENTIFY: $key \${=$key}$LF" + key="$(printf '%s\n' "$key" | tr - _)" + unset "$nextprefix$key" + done + + local fileindex=0 + local prefix= + local line + while IFS= read -r line; do + case "$line" in + X-MIDENTIFY-START:) + if [ -n "$nextprefix" ]; then + prefix="$nextprefix" + if [ "$fileindex" -gt 0 ]; then + nextprefix="${prefix%${fileindex}_}" + fi + fileindex="$((fileindex+1))" + nextprefix="${nextprefix}${fileindex}_" + for key in $allprops; do + key="$(printf '%s\n' "$key" | tr - _)" + unset "$nextprefix$key" + done + else + if [ "$fileindex" -gt 0 ]; then + printf '\n' + fi + fileindex="$((fileindex+1))" + fi + ;; + X-MIDENTIFY:\ *) + local key="${line#X-MIDENTIFY: }" + local value="${key#* }" + key="${key%% *}" + key="$(printf '%s\n' "$key" | tr - _)" + if [ -n "$nextprefix" ]; then + if [ -z "$prefix" ]; then + echo >&2 "Got X-MIDENTIFY: without X-MIDENTIFY-START:" + elif [ -n "$value" ]; then + eval "$prefix$key"='"$value"' + fi + else + if [ -n "$value" ]; then + printf '%s=%s\n' "$key" "$value" + fi + fi + ;; + esac + done <<EOF +$(${MPV:-mpv} --term-playing-msg="$propstr" --vo=null --ao=null \ + --frames=1 --quiet --no-cache --no-config -- "$@") +EOF +} + +__midentify__main "$@" diff --git a/TOOLS/osxbundle.py b/TOOLS/osxbundle.py new file mode 100755 index 0000000..98699e4 --- /dev/null +++ b/TOOLS/osxbundle.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python3 +import os +import shutil +import sys +import fileinput +from optparse import OptionParser + +def sh(command): + return os.popen(command).read().strip() + +def bundle_path(binary_name): + return "%s.app" % binary_name + +def bundle_name(binary_name): + return os.path.basename(bundle_path(binary_name)) + +def target_plist(binary_name): + return os.path.join(bundle_path(binary_name), 'Contents', 'Info.plist') + +def target_directory(binary_name): + return os.path.join(bundle_path(binary_name), 'Contents', 'MacOS') + +def target_binary(binary_name): + return os.path.join(target_directory(binary_name), + os.path.basename(binary_name)) + +def copy_bundle(binary_name): + if os.path.isdir(bundle_path(binary_name)): + shutil.rmtree(bundle_path(binary_name)) + shutil.copytree( + os.path.join('TOOLS', 'osxbundle', bundle_name(binary_name)), + bundle_path(binary_name)) + +def copy_binary(binary_name): + shutil.copy(binary_name, target_binary(binary_name)) + +def apply_plist_template(plist_file, version): + for line in fileinput.input(plist_file, inplace=1): + print(line.rstrip().replace('${VERSION}', version)) + +def sign_bundle(binary_name): + sh('codesign --force --deep -s - ' + bundle_path(binary_name)) + +def bundle_version(): + if os.path.exists('VERSION'): + x = open('VERSION') + version = x.read() + x.close() + else: + version = sh("./version.sh").strip() + return version + +def main(): + version = bundle_version().rstrip() + + usage = "usage: %prog [options] arg" + parser = OptionParser(usage) + parser.add_option("-s", "--skip-deps", action="store_false", dest="deps", + default=True, + help="don't bundle the dependencies") + + (options, args) = parser.parse_args() + + if len(args) != 1: + parser.error("incorrect number of arguments") + else: + binary_name = args[0] + + print("Creating Mac OS X application bundle (version: %s)..." % version) + print("> copying bundle skeleton") + copy_bundle(binary_name) + print("> copying binary") + copy_binary(binary_name) + print("> generating Info.plist") + apply_plist_template(target_plist(binary_name), version) + + if options.deps: + print("> bundling dependencies") + print(sh(" ".join(["TOOLS/dylib-unhell.py", target_binary(binary_name)]))) + + print("> signing bundle with ad-hoc pseudo identity") + sign_bundle(binary_name) + + print("done.") + +if __name__ == "__main__": + main() diff --git a/TOOLS/osxbundle/meson.build b/TOOLS/osxbundle/meson.build new file mode 100644 index 0000000..a271b41 --- /dev/null +++ b/TOOLS/osxbundle/meson.build @@ -0,0 +1,8 @@ +input = join_paths(source_root, 'TOOLS', 'osxbundle', + 'mpv.app', 'Contents', 'Resources', 'icon.icns') +osxbundle = custom_target('osxbundle', + input: input, + output: 'icon.icns.inc', + command: [file2string, '@INPUT@', '@OUTPUT@'], +) +sources += osxbundle diff --git a/TOOLS/osxbundle/mpv.app/Contents/Info.plist b/TOOLS/osxbundle/mpv.app/Contents/Info.plist new file mode 100644 index 0000000..e239dc7 --- /dev/null +++ b/TOOLS/osxbundle/mpv.app/Contents/Info.plist @@ -0,0 +1,874 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> + <dict> + <key>CFBundleDevelopmentRegion</key> + <string>English</string> + <key>CFBundleDocumentTypes</key> + <array> + <dict> + <key>CFBundleTypeExtensions</key> + <array> + <string>AAC</string> + <string>AC3</string> + <string>AIFF</string> + <string>M4A</string> + <string>MKA</string> + <string>MP3</string> + <string>OGG</string> + <string>PCM</string> + <string>VAW</string> + <string>WAV</string> + <string>WAW</string> + <string>WMA</string> + <string>aac</string> + <string>ac3</string> + <string>aiff</string> + <string>m4a</string> + <string>mka</string> + <string>mp3</string> + <string>ogg</string> + <string>pcm</string> + <string>vaw</string> + <string>wav</string> + <string>waw</string> + <string>wma</string> + </array> + <key>CFBundleTypeIconFile</key> + <string>document.icns</string> + <key>CFBundleTypeName</key> + <string>Audio file</string> + <key>CFBundleTypeRole</key> + <string>Viewer</string> + <key>LSTypeIsPackage</key> + <false/> + <key>NSPersistentStoreTypeKey</key> + <string>XML</string> + </dict> + <dict> + <key>CFBundleTypeExtensions</key> + <array> + <string>*</string> + <string>*</string> + <string>3GP</string> + <string>3IV</string> + <string>3gp</string> + <string>3iv</string> + <string>ASF</string> + <string>AVI</string> + <string>CPK</string> + <string>DAT</string> + <string>DIVX</string> + <string>DV</string> + <string>FLAC</string> + <string>FLI</string> + <string>FLV</string> + <string>H264</string> + <string>I263</string> + <string>M2TS</string> + <string>M4V</string> + <string>MKV</string> + <string>MOV</string> + <string>MP2</string> + <string>MP4</string> + <string>MPEG</string> + <string>MPG</string> + <string>MPG2</string> + <string>MPG4</string> + <string>NSV</string> + <string>NUT</string> + <string>NUV</string> + <string>OGG</string> + <string>OGM</string> + <string>QT</string> + <string>RM</string> + <string>RMVB</string> + <string>VCD</string> + <string>VFW</string> + <string>VOB</string> + <string>WEBM</string> + <string>WMV</string> + <string>MK3D</string> + <string>asf</string> + <string>avi</string> + <string>cpk</string> + <string>dat</string> + <string>divx</string> + <string>dv</string> + <string>flac</string> + <string>fli</string> + <string>flv</string> + <string>h264</string> + <string>i263</string> + <string>m2ts</string> + <string>m4v</string> + <string>mkv</string> + <string>mov</string> + <string>mp2</string> + <string>mp4</string> + <string>mpeg</string> + <string>mpg</string> + <string>mpg2</string> + <string>mpg4</string> + <string>nsv</string> + <string>nut</string> + <string>nuv</string> + <string>ogg</string> + <string>ogm</string> + <string>qt</string> + <string>rm</string> + <string>rmvb</string> + <string>vcd</string> + <string>vfw</string> + <string>vob</string> + <string>webm</string> + <string>wmv</string> + <string>mk3d</string> + </array> + <key>CFBundleTypeIconFile</key> + <string>document.icns</string> + <key>CFBundleTypeName</key> + <string>Movie file</string> + <key>CFBundleTypeRole</key> + <string>Viewer</string> + <key>LSTypeIsPackage</key> + <false/> + <key>NSPersistentStoreTypeKey</key> + <string>XML</string> + </dict> + <dict> + <key>CFBundleTypeExtensions</key> + <array> + <string>AQT</string> + <string>ASS</string> + <string>JSS</string> + <string>RT</string> + <string>SMI</string> + <string>SRT</string> + <string>SSA</string> + <string>SUB</string> + <string>TXT</string> + <string>UTF</string> + <string>aqt</string> + <string>ass</string> + <string>jss</string> + <string>rt</string> + <string>smi</string> + <string>srt</string> + <string>ssa</string> + <string>sub</string> + <string>txt</string> + <string>utf</string> + </array> + <key>CFBundleTypeIconFile</key> + <string>document.icns</string> + <key>CFBundleTypeName</key> + <string>Subtitles file</string> + <key>CFBundleTypeRole</key> + <string>Viewer</string> + <key>LSTypeIsPackage</key> + <false/> + <key>NSPersistentStoreTypeKey</key> + <string>XML</string> + </dict> + </array> + <key>CFBundleExecutable</key> + <string>mpv</string> + <key>CFBundleIconFile</key> + <string>icon</string> + <key>CFBundleIdentifier</key> + <string>io.mpv</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string>mpv</string> + <key>CFBundlePackageType</key> + <string>APPL</string> + <key>CFBundleShortVersionString</key> + <string>${VERSION}</string> + <key>NSHighResolutionCapable</key> + <true/> + <key>LSEnvironment</key> + <dict> + <key>MallocNanoZone</key> + <string>0</string> + <key>MPVBUNDLE</key> + <string>true</string> + </dict> + <key>CFBundleURLTypes</key> + <array> + <dict> + <key>CFBundleTypeRole</key> + <string>Viewer</string> + <key>CFBundleURLName</key> + <string>mpv Custom Protocol</string> + <key>CFBundleURLSchemes</key> + <array> + <string>mpv</string> + </array> + </dict> + <dict> + <key>CFBundleTypeRole</key> + <string>Viewer</string> + <key>CFBundleURLName</key> + <string>Streaming Protocol</string> + <key>CFBundleURLSchemes</key> + <array> + <string>mms</string> + <string>mmst</string> + <string>http</string> + <string>https</string> + <string>httpproxy</string> + <string>rtp</string> + <string>rtsp</string> + <string>ftp</string> + <string>udp</string> + <string>smb</string> + <string>srt</string> + <string>rist</string> + </array> + </dict> + <dict> + <key>CFBundleTypeRole</key> + <string>Viewer</string> + <key>CFBundleURLName</key> + <string>CD/DVD/Bluray Media</string> + <key>CFBundleURLSchemes</key> + <array> + <string>cdda</string> + <string>dvd</string> + <string>vcd</string> + <string>bd</string> + </array> + </dict> + </array> + <key>UTImportedTypeDeclarations</key> + <array> + <dict> + <key>UTTypeConformsTo</key> + <array> + <string>public.audio</string> + </array> + <key>UTTypeDescription</key> + <string>AC3 Audio</string> + <key>UTTypeIconFile</key> + <string>document.icns</string> + <key>UTTypeIdentifier</key> + <string>io.mpv.ac3</string> + <key>UTTypeReferenceURL</key> + <string>http://wiki.multimedia.cx/index.php?title=AC3</string> + <key>UTTypeTagSpecification</key> + <dict> + <key>public.filename-extension</key> + <array> + <string>ac3</string> + <string>a52</string> + <string>eac3</string> + </array> + </dict> + </dict> + <dict> + <key>UTTypeConformsTo</key> + <array> + <string>public.audio</string> + </array> + <key>UTTypeDescription</key> + <string>DTS Audio</string> + <key>UTTypeIconFile</key> + <string>document.icns</string> + <key>UTTypeIdentifier</key> + <string>io.mpv.dts</string> + <key>UTTypeReferenceURL</key> + <string>http://wiki.multimedia.cx/index.php?title=DTS</string> + <key>UTTypeTagSpecification</key> + <dict> + <key>public.filename-extension</key> + <array> + <string>dts</string> + </array> + </dict> + </dict> + <dict> + <key>UTTypeConformsTo</key> + <array> + <string>public.audio</string> + </array> + <key>UTTypeDescription</key> + <string>Free Lossless Audio Codec</string> + <key>UTTypeIconFile</key> + <string>document.icns</string> + <key>UTTypeIdentifier</key> + <string>io.mpv.flac</string> + <key>UTTypeReferenceURL</key> + <string>http://flac.sourceforge.net/format.html</string> + <key>UTTypeTagSpecification</key> + <dict> + <key>public.filename-extension</key> + <array> + <string>flac</string> + </array> + </dict> + </dict> + <dict> + <key>UTTypeConformsTo</key> + <array> + <string>public.audio</string> + </array> + <key>UTTypeDescription</key> + <string>Matroska Audio</string> + <key>UTTypeIconFile</key> + <string>document.icns</string> + <key>UTTypeIdentifier</key> + <string>io.mpv.mka</string> + <key>UTTypeReferenceURL</key> + <string>http://www.matroska.org</string> + <key>UTTypeTagSpecification</key> + <dict> + <key>public.filename-extension</key> + <array> + <string>mka</string> + </array> + </dict> + </dict> + <dict> + <key>UTTypeConformsTo</key> + <array> + <string>public.audio</string> + </array> + <key>UTTypeDescription</key> + <string>Ogg Audio</string> + <key>UTTypeIconFile</key> + <string>document.icns</string> + <key>UTTypeIdentifier</key> + <string>io.mpv.ogg-audio</string> + <key>UTTypeReferenceURL</key> + <string>http://xiph.org/ogg</string> + <key>UTTypeTagSpecification</key> + <dict> + <key>public.filename-extension</key> + <array> + <string>ogg</string> + </array> + </dict> + </dict> + <dict> + <key>UTTypeConformsTo</key> + <array> + <string>public.audio</string> + </array> + <key>UTTypeDescription</key> + <string>PCM Audio</string> + <key>UTTypeIconFile</key> + <string>document.icns</string> + <key>UTTypeIdentifier</key> + <string>io.mpv.pcm</string> + <key>UTTypeReferenceURL</key> + <string>http://en.wikipedia.org/wiki/Pulse-code_modulation</string> + <key>UTTypeTagSpecification</key> + <dict> + <key>public.filename-extension</key> + <array> + <string>pcm</string> + </array> + </dict> + </dict> + <dict> + <key>UTTypeConformsTo</key> + <array> + <string>public.audio</string> + </array> + <key>UTTypeDescription</key> + <string>Windows Media Audio</string> + <key>UTTypeIconFile</key> + <string>document.icns</string> + <key>UTTypeIdentifier</key> + <string>io.mpv.wma</string> + <key>UTTypeReferenceURL</key> + <string>http://en.wikipedia.org/wiki/Windows_Media_Audio</string> + <key>UTTypeTagSpecification</key> + <dict> + <key>public.filename-extension</key> + <array> + <string>wma</string> + </array> + </dict> + </dict> + <dict> + <key>UTTypeConformsTo</key> + <array> + <string>public.movie</string> + </array> + <key>UTTypeDescription</key> + <string>Audio Video Interleave</string> + <key>UTTypeIconFile</key> + <string>document.icns</string> + <key>UTTypeIdentifier</key> + <string>io.mpv.avi</string> + <key>UTTypeReferenceURL</key> + <string>http://www.the-labs.com/Video/odmlff2-avidef.pdf</string> + <key>UTTypeTagSpecification</key> + <dict> + <key>public.filename-extension</key> + <array> + <string>avi</string> + </array> + </dict> + </dict> + <dict> + <key>UTTypeConformsTo</key> + <array> + <string>public.movie</string> + </array> + <key>UTTypeDescription</key> + <string>DIVX Video</string> + <key>UTTypeIconFile</key> + <string>document.icns</string> + <key>UTTypeIdentifier</key> + <string>io.mpv.divx</string> + <key>UTTypeReferenceURL</key> + <string>http://www.divx.com</string> + <key>UTTypeTagSpecification</key> + <dict> + <key>public.filename-extension</key> + <array> + <string>divx</string> + </array> + </dict> + </dict> + <dict> + <key>UTTypeConformsTo</key> + <array> + <string>public.movie</string> + </array> + <key>UTTypeDescription</key> + <string>DV Video</string> + <key>UTTypeIconFile</key> + <string>document.icns</string> + <key>UTTypeIdentifier</key> + <string>io.mpv.dv</string> + <key>UTTypeReferenceURL</key> + <string>http://en.wikipedia.org/wiki/DV</string> + <key>UTTypeTagSpecification</key> + <dict> + <key>public.filename-extension</key> + <array> + <string>dv</string> + <string>hdv</string> + </array> + </dict> + </dict> + <dict> + <key>UTTypeConformsTo</key> + <array> + <string>public.movie</string> + </array> + <key>UTTypeDescription</key> + <string>Flash Video</string> + <key>UTTypeIconFile</key> + <string>document.icns</string> + <key>UTTypeIdentifier</key> + <string>io.mpv.flv</string> + <key>UTTypeReferenceURL</key> + <string>http://en.wikipedia.org/wiki/Flash_Video</string> + <key>UTTypeTagSpecification</key> + <dict> + <key>public.filename-extension</key> + <array> + <string>flv</string> + <string>f4v</string> + <string>f4p</string> + <string>swf</string> + </array> + </dict> + </dict> + <dict> + <key>UTTypeConformsTo</key> + <array> + <string>public.movie</string> + </array> + <key>UTTypeDescription</key> + <string>MPEG-2 Transport Stream</string> + <key>UTTypeIconFile</key> + <string>document.icns</string> + <key>UTTypeIdentifier</key> + <string>io.mpv.mts</string> + <key>UTTypeReferenceURL</key> + <string>http://en.wikipedia.org/wiki/.m2ts</string> + <key>UTTypeTagSpecification</key> + <dict> + <key>public.filename-extension</key> + <array> + <string>trp</string> + <string>m2t</string> + <string>m2ts</string> + <string>mts</string> + <string>mtv</string> + <string>ts</string> + </array> + </dict> + </dict> + <dict> + <key>UTTypeConformsTo</key> + <array> + <string>public.movie</string> + </array> + <key>UTTypeDescription</key> + <string>Matroska Video</string> + <key>UTTypeIconFile</key> + <string>document.icns</string> + <key>UTTypeIdentifier</key> + <string>io.mpv.mkv</string> + <key>UTTypeReferenceURL</key> + <string>http://www.matroska.org</string> + <key>UTTypeTagSpecification</key> + <dict> + <key>public.filename-extension</key> + <array> + <string>mkv</string> + </array> + </dict> + </dict> + <dict> + <key>UTTypeConformsTo</key> + <array> + <string>public.movie</string> + </array> + <key>UTTypeDescription</key> + <string>Matroska stereoscopic/3D video</string> + <key>UTTypeIconFile</key> + <string>document.icns</string> + <key>UTTypeIdentifier</key> + <string>io.mpv.mk3d</string> + <key>UTTypeReferenceURL</key> + <string>http://www.matroska.org</string> + <key>UTTypeTagSpecification</key> + <dict> + <key>public.filename-extension</key> + <array> + <string>mk3d</string> + </array> + </dict> + </dict> + <dict> + <key>UTTypeConformsTo</key> + <array> + <string>public.movie</string> + </array> + <key>UTTypeDescription</key> + <string>WebM Video</string> + <key>UTTypeIconFile</key> + <string>document.icns</string> + <key>UTTypeIdentifier</key> + <string>io.mpv.webm</string> + <key>UTTypeReferenceURL</key> + <string>http://www.webmproject.org</string> + <key>UTTypeTagSpecification</key> + <dict> + <key>public.filename-extension</key> + <array> + <string>webm</string> + </array> + </dict> + </dict> + <dict> + <key>UTTypeConformsTo</key> + <array> + <string>public.movie</string> + </array> + <key>UTTypeDescription</key> + <string>Ogg Video</string> + <key>UTTypeIconFile</key> + <string>document.icns</string> + <key>UTTypeIdentifier</key> + <string>io.mpv.ogv</string> + <key>UTTypeReferenceURL</key> + <string>http://xiph.org/ogg</string> + <key>UTTypeTagSpecification</key> + <dict> + <key>public.filename-extension</key> + <array> + <string>ogm</string> + <string>ogv</string> + </array> + </dict> + </dict> + <dict> + <key>UTTypeConformsTo</key> + <array> + <string>public.movie</string> + </array> + <key>UTTypeDescription</key> + <string>Real Media</string> + <key>UTTypeIconFile</key> + <string>document.icns</string> + <key>UTTypeIdentifier</key> + <string>io.mpv.rmvb</string> + <key>UTTypeReferenceURL</key> + <string>http://www.real.com</string> + <key>UTTypeTagSpecification</key> + <dict> + <key>public.filename-extension</key> + <array> + <string>rmvb</string> + <string>rm</string> + </array> + </dict> + </dict> + <dict> + <key>UTTypeConformsTo</key> + <array> + <string>public.movie</string> + </array> + <key>UTTypeDescription</key> + <string>Video Object</string> + <key>UTTypeIconFile</key> + <string>document.icns</string> + <key>UTTypeIdentifier</key> + <string>io.mpv.vob</string> + <key>UTTypeReferenceURL</key> + <string>http://en.wikipedia.org/wiki/VOB</string> + <key>UTTypeTagSpecification</key> + <dict> + <key>public.filename-extension</key> + <array> + <string>vob</string> + </array> + </dict> + </dict> + <dict> + <key>UTTypeConformsTo</key> + <array> + <string>public.movie</string> + </array> + <key>UTTypeDescription</key> + <string>Windows Media Video</string> + <key>UTTypeIconFile</key> + <string>document.icns</string> + <key>UTTypeIdentifier</key> + <string>io.mpv.wmv</string> + <key>UTTypeReferenceURL</key> + <string>http://en.wikipedia.org/wiki/Windows_Media_Video</string> + <key>UTTypeTagSpecification</key> + <dict> + <key>public.filename-extension</key> + <array> + <string>wmv</string> + </array> + </dict> + </dict> + <dict> + <key>UTTypeConformsTo</key> + <array> + <string>public.movie</string> + </array> + <key>UTTypeDescription</key> + <string>XVID Video</string> + <key>UTTypeIconFile</key> + <string>document.icns</string> + <key>UTTypeIdentifier</key> + <string>io.mpv.xvid</string> + <key>UTTypeReferenceURL</key> + <string>http://www.xvid.org</string> + <key>UTTypeTagSpecification</key> + <dict> + <key>public.filename-extension</key> + <array> + <string>xvid</string> + </array> + </dict> + </dict> + <dict> + <key>UTTypeConformsTo</key> + <array> + <string>public.movie</string> + </array> + <key>UTTypeDescription</key> + <string>AVC raw stream</string> + <key>UTTypeIconFile</key> + <string>document.icns</string> + <key>UTTypeIdentifier</key> + <string>io.mpv.h264</string> + <key>UTTypeReferenceURL</key> + <string>http://www.itu.int/rec/T-REC-H.264</string> + <key>UTTypeTagSpecification</key> + <dict> + <key>public.filename-extension</key> + <array> + <string>264</string> + </array> + </dict> + </dict> + <dict> + <key>UTTypeConformsTo</key> + <array> + <string>public.movie</string> + </array> + <key>UTTypeDescription</key> + <string>HEVC raw stream</string> + <key>UTTypeIconFile</key> + <string>document.icns</string> + <key>UTTypeIdentifier</key> + <string>io.mpv.hevc</string> + <key>UTTypeReferenceURL</key> + <string>http://hevc.info</string> + <key>UTTypeTagSpecification</key> + <dict> + <key>public.filename-extension</key> + <array> + <string>hevc</string> + </array> + </dict> + </dict> + <dict> + <key>UTTypeConformsTo</key> + <array> + <string>public.movie</string> + </array> + <key>UTTypeDescription</key> + <string>YUV stream</string> + <key>UTTypeIconFile</key> + <string>document.icns</string> + <key>UTTypeIdentifier</key> + <string>io.mpv.yuv</string> + <key>UTTypeReferenceURL</key> + <string>http://en.wikipedia.org/wiki/YUV</string> + <key>UTTypeTagSpecification</key> + <dict> + <key>public.filename-extension</key> + <array> + <string>yuv</string> + </array> + </dict> + </dict> + <dict> + <key>UTTypeConformsTo</key> + <array> + <string>public.movie</string> + </array> + <key>UTTypeDescription</key> + <string>YUV4MPEG2 stream</string> + <key>UTTypeIconFile</key> + <string>document.icns</string> + <key>UTTypeIdentifier</key> + <string>io.mpv.y4m</string> + <key>UTTypeReferenceURL</key> + <string>http://wiki.multimedia.cx/index.php?title=YUV4MPEG2</string> + <key>UTTypeTagSpecification</key> + <dict> + <key>public.filename-extension</key> + <array> + <string>y4m</string> + </array> + </dict> + </dict> + <dict> + <key>UTTypeConformsTo</key> + <array> + <string>public.plain-text</string> + </array> + <key>UTTypeDescription</key> + <string>SubRip Subtitle</string> + <key>UTTypeIconFile</key> + <string>document.icns</string> + <key>UTTypeIdentifier</key> + <string>io.mpv.subrip</string> + <key>UTTypeReferenceURL</key> + <string>http://en.wikipedia.org/wiki/SubRip</string> + <key>UTTypeTagSpecification</key> + <dict> + <key>public.filename-extension</key> + <array> + <string>srt</string> + </array> + </dict> + </dict> + <dict> + <key>UTTypeConformsTo</key> + <array> + <string>public.plain-text</string> + </array> + <key>UTTypeDescription</key> + <string>MicroDVD Subtitle</string> + <key>UTTypeIconFile</key> + <string>document.icns</string> + <key>UTTypeIdentifier</key> + <string>io.mpv.sub</string> + <key>UTTypeReferenceURL</key> + <string>http://en.wikipedia.org/wiki/MicroDVD</string> + <key>UTTypeTagSpecification</key> + <dict> + <key>public.filename-extension</key> + <array> + <string>sub</string> + </array> + </dict> + </dict> + <dict> + <key>UTTypeConformsTo</key> + <array> + <string>public.plain-text</string> + </array> + <key>UTTypeDescription</key> + <string>SubStation Alpha Subtitle</string> + <key>UTTypeIconFile</key> + <string>document.icns</string> + <key>UTTypeIdentifier</key> + <string>io.mpv.ass</string> + <key>UTTypeReferenceURL</key> + <string>https://github.com/libass/libass</string> + <key>UTTypeTagSpecification</key> + <dict> + <key>public.filename-extension</key> + <array> + <string>ass</string> + <string>ssa</string> + </array> + </dict> + </dict> + <dict> + <key>UTTypeConformsTo</key> + <array> + <string>public.data</string> + </array> + <key>UTTypeDescription</key> + <string>VobSub Subtitle</string> + <key>UTTypeIconFile</key> + <string>document.icns</string> + <key>UTTypeIdentifier</key> + <string>io.mpv.vobsub</string> + <key>UTTypeReferenceURL</key> + <string>http://en.wikipedia.org/wiki/DirectVobSub</string> + <key>UTTypeTagSpecification</key> + <dict> + <key>public.filename-extension</key> + <array> + <string>idx</string> + <string>sub</string> + </array> + </dict> + </dict> + <dict> + <key>UTTypeConformsTo</key> + <array> + <string>public.plain-text</string> + </array> + <key>UTTypeDescription</key> + <string>SAMI Subtitle</string> + <key>UTTypeIconFile</key> + <string>document.icns</string> + <key>UTTypeIdentifier</key> + <string>io.mpv.smi</string> + <key>UTTypeReferenceURL</key> + <string>http://en.wikipedia.org/wiki/SAMI</string> + <key>UTTypeTagSpecification</key> + <dict> + <key>public.filename-extension</key> + <array> + <string>smi</string> + <string>smil</string> + </array> + </dict> + </dict> + </array> + </dict> +</plist> diff --git a/TOOLS/osxbundle/mpv.app/Contents/MacOS/.gitkeep b/TOOLS/osxbundle/mpv.app/Contents/MacOS/.gitkeep new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/TOOLS/osxbundle/mpv.app/Contents/MacOS/.gitkeep diff --git a/TOOLS/osxbundle/mpv.app/Contents/MacOS/lib/.gitkeep b/TOOLS/osxbundle/mpv.app/Contents/MacOS/lib/.gitkeep new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/TOOLS/osxbundle/mpv.app/Contents/MacOS/lib/.gitkeep diff --git a/TOOLS/osxbundle/mpv.app/Contents/PkgInfo b/TOOLS/osxbundle/mpv.app/Contents/PkgInfo new file mode 100644 index 0000000..bd04210 --- /dev/null +++ b/TOOLS/osxbundle/mpv.app/Contents/PkgInfo @@ -0,0 +1 @@ +APPL????
\ No newline at end of file diff --git a/TOOLS/osxbundle/mpv.app/Contents/Resources/document.icns b/TOOLS/osxbundle/mpv.app/Contents/Resources/document.icns Binary files differnew file mode 100644 index 0000000..d616296 --- /dev/null +++ b/TOOLS/osxbundle/mpv.app/Contents/Resources/document.icns diff --git a/TOOLS/osxbundle/mpv.app/Contents/Resources/icon.icns b/TOOLS/osxbundle/mpv.app/Contents/Resources/icon.icns Binary files differnew file mode 100644 index 0000000..035a934 --- /dev/null +++ b/TOOLS/osxbundle/mpv.app/Contents/Resources/icon.icns diff --git a/TOOLS/osxbundle/mpv.app/Contents/Resources/mpv.conf b/TOOLS/osxbundle/mpv.app/Contents/Resources/mpv.conf new file mode 100644 index 0000000..618f87e --- /dev/null +++ b/TOOLS/osxbundle/mpv.app/Contents/Resources/mpv.conf @@ -0,0 +1,2 @@ +player-operation-mode=pseudo-gui +log-file=~/Library/Logs/mpv.log diff --git a/TOOLS/stats-conv.py b/TOOLS/stats-conv.py new file mode 100755 index 0000000..16d787a --- /dev/null +++ b/TOOLS/stats-conv.py @@ -0,0 +1,166 @@ +#!/usr/bin/env python3 +from pyqtgraph.Qt import QtGui, QtCore +import pyqtgraph as pg +import sys +import re + +filename = sys.argv[1] + +events = ".*" +if len(sys.argv) > 2: + events = sys.argv[2] +event_regex = re.compile(events) + +""" +This script is meant to display stats written by mpv --dump-stats=filename. +In general, each line in that file is an event of the form: + + <timestamp in microseconds> <text> '#' <comment> + +e.g.: + + 10474959 start flip #cplayer + +<text> is what MP_STATS(log, "...") writes. The rest is added by msg.c. + +Currently, the following event types are supported: + + 'signal' <name> singular event + 'start' <name> start of the named event + 'end' <name> end of the named event + 'value' <float> <name> a normal value (as opposed to event) + 'event-timed' <ts> <name> singular event at the given timestamp + 'value-timed' <ts> <float> <name> a value for an event at the given timestamp + 'range-timed' <ts1> <ts2> <name> like start/end, but explicit times + <name> singular event (same as 'signal') + +""" + +class G: + events = {} + start = None + markers = ["o", "s", "t", "d"] + curveno = {} + +def find_marker(): + if len(G.markers) == 0: + return "o" + m = G.markers[0] + G.markers = G.markers[1:] + return m + +class Event: + pass + +def get_event(event, evtype): + if event not in G.events: + e = Event() + e.name = event + e.vals = [] + e.type = evtype + e.marker = "o" + if e.type == "event-signal": + e.marker = find_marker() + if not event_regex.match(e.name): + return e + G.events[event] = e + return G.events[event] + +colors = [(0.0, 0.5, 0.0), (0.0, 0.0, 1.0), (0.0, 0.0, 0.0), (1.0, 0.0, 0.0), (0.75, 0.75, 0), (0.0, 0.75, 0.75), (0.75, 0, 0.75)] +def mkColor(t): + return pg.mkColor(int(t[0] * 255), int(t[1] * 255), int(t[2] * 255)) + +SCALE = 1e6 # microseconds to seconds + +for line in [line.split("#")[0].strip() for line in open(filename, "r")]: + line = line.strip() + if not line: + continue + ts, event = line.split(" ", 1) + ts = int(ts) / SCALE + if G.start is None: + G.start = ts + ts = ts - G.start + if event.startswith("start "): + e = get_event(event[6:], "event") + e.vals.append((ts, 0)) + e.vals.append((ts, 1)) + elif event.startswith("end "): + e = get_event(event[4:], "event") + e.vals.append((ts, 1)) + e.vals.append((ts, 0)) + elif event.startswith("value "): + _, val, name = event.split(" ", 2) + val = float(val) + e = get_event(name, "value") + e.vals.append((ts, val)) + elif event.startswith("event-timed "): + _, val, name = event.split(" ", 2) + val = int(val) / SCALE - G.start + e = get_event(name, "event-signal") + e.vals.append((val, 1)) + elif event.startswith("range-timed "): + _, ts1, ts2, name = event.split(" ", 3) + ts1 = int(ts1) / SCALE - G.start + ts2 = int(ts2) / SCALE - G.start + e = get_event(name, "event") + e.vals.append((ts1, 0)) + e.vals.append((ts1, 1)) + e.vals.append((ts2, 1)) + e.vals.append((ts2, 0)) + elif event.startswith("value-timed "): + _, tsval, val, name = event.split(" ", 3) + tsval = int(tsval) / SCALE - G.start + val = float(val) + e = get_event(name, "value") + e.vals.append((tsval, val)) + elif event.startswith("signal "): + name = event.split(" ", 2)[1] + e = get_event(name, "event-signal") + e.vals.append((ts, 1)) + else: + e = get_event(event, "event-signal") + e.vals.append((ts, 1)) + +# deterministically sort them; make sure the legend is sorted too +G.sevents = list(G.events.values()) +G.sevents.sort(key=lambda x: x.name) +hasval = False +for e, index in zip(G.sevents, range(len(G.sevents))): + m = len(G.sevents) + if e.type == "value": + hasval = True + else: + e.vals = [(x, y * (m - index) / m) for (x, y) in e.vals] + +pg.setConfigOption('background', 'w') +pg.setConfigOption('foreground', 'k') +app = QtGui.QApplication([]) +win = pg.GraphicsWindow() +#win.resize(1500, 900) + +ax = [None, None] +plots = 2 if hasval else 1 +ax[0] = win.addPlot() +if hasval: + win.nextRow() + ax[1] = win.addPlot() + ax[1].setXLink(ax[0]) +for cur in ax: + if cur is not None: + cur.addLegend(offset = (-1, 1)) +for e in G.sevents: + cur = ax[1 if e.type == "value" else 0] + if not cur in G.curveno: + G.curveno[cur] = 0 + args = {'name': e.name,'antialias':True} + color = mkColor(colors[G.curveno[cur] % len(colors)]) + if e.type == "event-signal": + args['symbol'] = e.marker + args['symbolBrush'] = pg.mkBrush(color, width=0) + else: + args['pen'] = pg.mkPen(color, width=0) + G.curveno[cur] += 1 + n = cur.plot([x for x,y in e.vals], [y for x,y in e.vals], **args) + +QtGui.QApplication.instance().exec_() diff --git a/TOOLS/umpv b/TOOLS/umpv new file mode 100755 index 0000000..1eece5d --- /dev/null +++ b/TOOLS/umpv @@ -0,0 +1,87 @@ +#!/usr/bin/env python3 + +""" +This script emulates "unique application" functionality on Linux. When starting +playback with this script, it will try to reuse an already running instance of +mpv (but only if that was started with umpv). Other mpv instances (not started +by umpv) are ignored, and the script doesn't know about them. + +This only takes filenames as arguments. Custom options can't be used; the script +interprets them as filenames. If mpv is already running, the files passed to +umpv are appended to mpv's internal playlist. If a file does not exist or is +otherwise not playable, mpv will skip the playlist entry when attempting to +play it (from the GUI perspective, it's silently ignored). + +If mpv isn't running yet, this script will start mpv and let it control the +current terminal. It will not write output to stdout/stderr, because this +will typically just fill ~/.xsession-errors with garbage. + +mpv will terminate if there are no more files to play, and running the umpv +script after that will start a new mpv instance. + +Note: you can supply custom mpv path and options with the MPV environment + variable. The environment variable will be split on whitespace, and the + first item is used as path to mpv binary and the rest is passed as options + _if_ the script starts mpv. If mpv is not started by the script (i.e. mpv + is already running), this will be ignored. +""" + +import sys +import os +import socket +import errno +import subprocess +import string +import shlex + +files = sys.argv[1:] + +# this is the same method mpv uses to decide this +def is_url(filename): + parts = filename.split("://", 1) + if len(parts) < 2: + return False + # protocol prefix has no special characters => it's an URL + allowed_symbols = string.ascii_letters + string.digits + '_' + prefix = parts[0] + return all(map(lambda c: c in allowed_symbols, prefix)) + +# make them absolute; also makes them safe against interpretation as options +def make_abs(filename): + if not is_url(filename): + return os.path.abspath(filename) + return filename +files = (make_abs(f) for f in files) + +SOCK = os.path.join(os.getenv("XDG_RUNTIME_DIR", os.getenv("HOME")), ".umpv_socket") + +sock = None +try: + sock = socket.socket(socket.AF_UNIX) + sock.connect(SOCK) +except socket.error as e: + if e.errno == errno.ECONNREFUSED: + sock = None + pass # abandoned socket + elif e.errno == errno.ENOENT: + sock = None + pass # doesn't exist + else: + raise e + +if sock: + # Unhandled race condition: what if mpv is terminating right now? + for f in files: + # escape: \ \n " + f = f.replace("\\", "\\\\").replace("\"", "\\\"").replace("\n", "\\n") + f = "\"" + f + "\"" + sock.send(("raw loadfile " + f + " append\n").encode("utf-8")) +else: + # Let mpv recreate socket if it doesn't already exist. + + opts = shlex.split(os.getenv("MPV") or "mpv") + opts.extend(["--no-terminal", "--force-window", "--input-ipc-server=" + SOCK, + "--"]) + opts.extend(files) + + subprocess.check_call(opts) diff --git a/TOOLS/uncrustify.cfg b/TOOLS/uncrustify.cfg new file mode 100644 index 0000000..837d9a1 --- /dev/null +++ b/TOOLS/uncrustify.cfg @@ -0,0 +1,165 @@ +# Usage: +# uncrustify -l C -c TOOLS/uncrustify.cfg --no-backup --replace file.c +# +# Keep in mind that this uncrustify configuration still produces some +# bad/broken formatting. +# + +code_width=80 +indent_align_string=false +indent_braces=false +indent_braces_no_func=false +indent_brace_parent=false +indent_namespace=false +indent_extern=false +indent_class=false +indent_class_colon=false +indent_else_if=false +indent_func_call_param=false +indent_func_def_param=false +indent_func_proto_param=false +indent_func_class_param=false +indent_func_ctor_var_param=false +indent_template_param=false +indent_func_param_double=false +indent_relative_single_line_comments=false +indent_col1_comment=false +indent_access_spec_body=false +indent_paren_nl=false +indent_comma_paren=false +indent_bool_paren=false +indent_square_nl=false +indent_preserve_sql=false +indent_align_assign=true +sp_balance_nested_parens=false +align_keep_tabs=false +align_with_tabs=false +align_on_tabstop=false +align_number_left=false +align_func_params=false +align_same_func_call_params=false +align_var_def_colon=false +align_var_def_attribute=false +align_var_def_inline=false +align_right_cmt_mix=false +align_on_operator=false +align_mix_var_proto=false +align_single_line_func=false +align_single_line_brace=false +align_nl_cont=false +align_left_shift=true +nl_collapse_empty_body=false +nl_assign_leave_one_liners=false +nl_class_leave_one_liners=false +nl_enum_leave_one_liners=false +nl_getset_leave_one_liners=false +nl_func_leave_one_liners=false +nl_if_leave_one_liners=false +nl_multi_line_cond=false +nl_multi_line_define=false +nl_before_case=false +nl_after_case=false +nl_after_return=false +nl_after_semicolon=true +nl_after_brace_open=true +nl_after_brace_open_cmt=false +nl_after_vbrace_open=true +nl_after_brace_close=false +nl_define_macro=false +nl_squeeze_ifdef=false +nl_ds_struct_enum_cmt=false +nl_ds_struct_enum_close_brace=false +nl_create_if_one_liner=false +nl_create_for_one_liner=false +nl_create_while_one_liner=false +ls_for_split_full=false +ls_func_split_full=false +nl_after_multiline_comment=false +eat_blanks_after_open_brace=false +eat_blanks_before_close_brace=false +mod_pawn_semicolon=false +mod_full_paren_if_bool=false +mod_remove_extra_semicolon=false +mod_sort_import=false +mod_sort_using=false +mod_sort_include=false +mod_move_case_break=false +mod_remove_empty_return=false +cmt_indent_multi=true +cmt_c_group=false +cmt_c_nl_start=false +cmt_c_nl_end=false +cmt_cpp_group=false +cmt_cpp_nl_start=false +cmt_cpp_nl_end=false +cmt_cpp_to_c=false +cmt_star_cont=false +cmt_multi_check_last=true +cmt_insert_before_preproc=false +pp_indent_at_level=false +pp_region_indent_code=false +pp_if_indent_code=false +pp_define_at_level=false +indent_columns=4 +nl_end_of_file_min=1 +mod_full_brace_nl=2 +indent_with_tabs=0 +sp_arith=add +sp_assign=add +sp_enum_assign=force +sp_bool=force +sp_compare=force +sp_inside_paren=remove +sp_paren_paren=remove +sp_before_ptr_star=force +sp_between_ptr_star=remove +sp_after_ptr_star=remove +sp_after_ptr_star_func=remove +sp_before_sparen=add +sp_inside_sparen=remove +sp_sparen_brace=force +sp_before_semi=remove +sp_after_semi_for_empty=remove +sp_before_square=remove +sp_inside_square=remove +sp_after_comma=force +sp_before_comma=remove +sp_inside_paren_cast=remove +sp_func_proto_paren=remove +sp_func_def_paren=remove +sp_inside_fparens=remove +sp_inside_fparen=remove +sp_square_fparen=remove +sp_func_call_paren=remove +sp_return_paren=force +sp_else_brace=force +sp_brace_else=force +sp_brace_typedef=add +sp_not=remove +sp_inv=remove +sp_addr=remove +sp_member=remove +sp_deref=remove +sp_sign=remove +sp_incdec=remove +sp_before_nl_cont=add +nl_end_of_file=force +nl_if_brace=remove +nl_brace_else=remove +nl_elseif_brace=remove +nl_else_brace=remove +nl_else_if=remove +nl_for_brace=remove +nl_while_brace=remove +nl_do_brace=remove +nl_brace_while=remove +nl_switch_brace=remove +nl_func_type_name=remove +nl_func_proto_type_name=remove +nl_func_paren=remove +nl_func_decl_start=remove +nl_fdef_brace=force +mod_full_brace_for=remove +mod_full_brace_if=remove +mod_full_brace_while=remove +mod_paren_on_return=remove |