diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 93ab36d2ed4fe..1e0c396c19109 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -501,7 +501,6 @@ jobs: fail-fast: false matrix: sys: - - clang32 - clang64 - mingw32 - mingw64 @@ -528,6 +527,7 @@ jobs: lcms2:p libarchive:p libass:p + libcdio-paranoia:p libdvdnav:p libjpeg-turbo:p libplacebo:p @@ -538,13 +538,14 @@ jobs: python:p shaderc:p spirv-cross:p + uchardet:p + vapoursynth:p vulkan-devel:p - name: Install dependencies - if: ${{ matrix.sys != 'clang32' && matrix.sys != 'mingw32' }} + if: ${{ matrix.sys != 'mingw32' }} run: | - pacboy --noconfirm -S angleproject cppwinrt libcdio-paranoia rst2pdf \ - rubberband uchardet vapoursynth + pacboy --noconfirm -S {angleproject,cppwinrt,rst2pdf,rubberband}:p - name: Build with meson id: build diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 425e8d1a07c46..3aece2f4d71b1 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -33,3 +33,9 @@ jobs: steps: - uses: actions/checkout@v4 - uses: lunarmodules/luacheck@v1 + + python-lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: chartboost/ruff-action@v1 diff --git a/DOCS/client-api-changes.rst b/DOCS/client-api-changes.rst index ec2e3b87f9d1b..8826b875d2251 100644 --- a/DOCS/client-api-changes.rst +++ b/DOCS/client-api-changes.rst @@ -286,4 +286,3 @@ API changes - macOS: the "coreaudio" AO spdif code is split into a separate AO --- mpv 0.4.0 --- 1.0 - the API is declared stable - diff --git a/DOCS/compile-windows.md b/DOCS/compile-windows.md index c4fff4ab4871b..2f21f9a1c2700 100644 --- a/DOCS/compile-windows.md +++ b/DOCS/compile-windows.md @@ -76,7 +76,7 @@ which bootstraps a MinGW-w64 toolchain and builds mpv along with its dependencie cat < subprojects/ffmpeg.wrap [wrap-git] url = https://gitlab.freedesktop.org/gstreamer/meson-ports/ffmpeg.git - revision = meson-6.1 + revision = meson-7.1 depth = 1 [provide] libavcodec = libavcodec_dep diff --git a/DOCS/interface-changes/display-page-toggle.txt b/DOCS/interface-changes/display-page-toggle.txt new file mode 100644 index 0000000000000..0352821074062 --- /dev/null +++ b/DOCS/interface-changes/display-page-toggle.txt @@ -0,0 +1 @@ +add `display-page-n-toggle` script bindings to stats.lua, where n is a page number diff --git a/DOCS/interface-changes/geometry-behavior.txt b/DOCS/interface-changes/geometry-behavior.txt new file mode 100644 index 0000000000000..cab61a830a536 --- /dev/null +++ b/DOCS/interface-changes/geometry-behavior.txt @@ -0,0 +1 @@ +change `--geometry` so that it no longer unconditionally moves the window on platforms where that is possible. Use `--force-window-position` or add `+50%+50%` to your geometry command to get the old behavior back. diff --git a/DOCS/man/input.rst b/DOCS/man/input.rst index 44ce61a5e3bf2..13cd36af72258 100644 --- a/DOCS/man/input.rst +++ b/DOCS/man/input.rst @@ -2778,7 +2778,7 @@ Property list in the list will be the one that Windows considers associated with the window (as determined by the MonitorFromWindow API.) On macOS these are the Display Product Names as used in the System Information with a serial number - in brackets and only one display name is returned since a window can only be + in parentheses and only one display name is returned since a window can only be on one screen. On Wayland, these are the wl_output names if protocol version >= 4 is used (LVDS-1, HDMI-A-1, X11-1, etc.), or the wl_output model reported by the geometry event if protocol version < 4 is used. diff --git a/DOCS/man/stats.rst b/DOCS/man/stats.rst index 0e42f44496d90..4d22f36ff34f9 100644 --- a/DOCS/man/stats.rst +++ b/DOCS/man/stats.rst @@ -233,7 +233,7 @@ Additional keys can be configured in ``input.conf`` to display the stats:: And to display a certain page directly:: i script-binding stats/display-page-1 - e script-binding stats/display-page-2 + h script-binding stats/display-page-4-toggle Active key bindings page ~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/DOCS/man/vf.rst b/DOCS/man/vf.rst index 0331c7e15175a..3a27014b575aa 100644 --- a/DOCS/man/vf.rst +++ b/DOCS/man/vf.rst @@ -840,4 +840,3 @@ Available mpv-only filters are: Do not use this with ``--vo=gpu``. It will apply filtering twice, since most ``--vo=gpu`` options are unconditionally applied to the ``gpu`` filter. There is no mechanism in mpv to prevent this. - diff --git a/README.md b/README.md index 8f1caf5fe8bfb..7f5a374415761 100644 --- a/README.md +++ b/README.md @@ -120,7 +120,7 @@ Essential dependencies (incomplete list): Libass dependencies (when building libass): -- gcc or clang, yasm on x86 and x86_64 +- gcc or clang, nasm on x86 and x86_64 - fribidi, freetype, fontconfig development headers (for libass) - harfbuzz (required for correct rendering of combining characters, particularly for correct rendering of non-English text on macOS, and Arabic/Indic scripts on @@ -128,7 +128,7 @@ Libass dependencies (when building libass): FFmpeg dependencies (when building FFmpeg): -- gcc or clang, yasm on x86 and x86_64 +- gcc or clang, nasm on x86 and x86_64 - OpenSSL or GnuTLS (have to be explicitly enabled when compiling FFmpeg) - libx264/libmp3lame/libfdk-aac if you want to use encoding (have to be explicitly enabled when compiling FFmpeg) diff --git a/TOOLS/docutils-wrapper.py b/TOOLS/docutils-wrapper.py index 31ba976e64f8b..23c3552fd229e 100755 --- a/TOOLS/docutils-wrapper.py +++ b/TOOLS/docutils-wrapper.py @@ -27,17 +27,17 @@ def convert_depfile(output, depfile): - with open(depfile, 'r') as f: + with open(depfile) as f: deps = f.readlines() - with open(depfile, 'w') as f: + with open(depfile, "w") as f: f.write(os.path.abspath(output)) - f.write(': \\\n') + f.write(": \\\n") for dep in deps: dep = dep[:-1] - f.write('\t') + f.write("\t") f.write(os.path.abspath(dep)) - f.write(' \\\n') + f.write(" \\\n") def remove(path): try: @@ -51,14 +51,14 @@ def remove(path): output = argv[-1] for opt, optarg in zip(argv, argv[1:]): - if opt == '--record-dependencies': + if opt == "--record-dependencies": depfile = optarg try: proc = subprocess.run(argv, check=True) if depfile is not None: convert_depfile(output, depfile) -except: +except Exception: remove(output) if depfile is not None: remove(depfile) diff --git a/TOOLS/dylib_unhell.py b/TOOLS/dylib_unhell.py index c8859692845b3..cecef2a5f3758 100755 --- a/TOOLS/dylib_unhell.py +++ b/TOOLS/dylib_unhell.py @@ -1,11 +1,11 @@ #!/usr/bin/env python3 -import re +import json import os -import sys +import re import shutil import subprocess -import json +import sys from functools import partial sys_re = re.compile("^/System") @@ -16,17 +16,17 @@ 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 + "libobjc." not in libname and \ + "libSystem." not in libname and \ + "libc." not in libname and \ + "libgcc." not in libname and \ + os.path.basename(libname) != "Python" and \ + os.path.basename(objfile) not in libname and \ + "libswift" not 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) + command = f"otool -L '{objfile}' | grep -e '\t' | awk '{{ print $1 }}'" + output = subprocess.check_output(command, shell=True, universal_newlines=True) libs = set(filter(partial(is_user_lib, objfile), output.split())) libs_resolved = set() @@ -41,75 +41,91 @@ def otool(objfile, rapths): def get_rapths(objfile): rpaths = [] - command = "otool -l '%s' | grep -A2 LC_RPATH | grep path" % objfile - pathRe = re.compile(r"^\s*path (.*) \(offset \d*\)$") + command = f"otool -l '{objfile}' | grep -A2 LC_RPATH | grep path" + path_re = re.compile(r"^\s*path (.*) \(offset \d*\)$") try: - result = subprocess.check_output(command, shell = True, universal_newlines=True) - except: + result = subprocess.check_output(command, shell=True, universal_newlines=True) + except Exception: return rpaths for line in result.splitlines(): - line_clean = pathRe.search(line).group(1).strip() + line_clean = path_re.search(line).group(1).strip() # resolve @loader_path - if line_clean.startswith('@loader_path/'): - line_clean = line_clean[len('@loader_path/'):] - line_clean = os.path.normpath(os.path.join(os.path.dirname(objfile), line_clean)) + if line_clean.startswith("@loader_path/"): + line_clean = line_clean[len("@loader_path/"):] + line_clean = os.path.join(os.path.dirname(objfile), line_clean) + line_clean = os.path.normpath(line_clean) rpaths.append(line_clean) return rpaths def get_rpaths_dev_tools(binary): - command = "otool -l '%s' | grep -A2 LC_RPATH | grep path | grep \"Xcode\\|CommandLineTools\"" % binary + command = ( + f"otool -l '{binary}' | grep -A2 LC_RPATH | grep path | " + 'grep "Xcode\\|CommandLineTools"' + ) result = subprocess.check_output(command, shell = True, universal_newlines=True) - pathRe = re.compile(r"^\s*path (.*) \(offset \d*\)$") - output = [] - - for line in result.splitlines(): - output.append(pathRe.search(line).group(1).strip()) - - return output + path_re = re.compile(r"^\s*path (.*) \(offset \d*\)$") + return [ + path_re.search(line).group(1).strip() + for line in result.splitlines() + ] def resolve_lib_path(objfile, lib, rapths): if os.path.exists(lib): return lib - if lib.startswith('@rpath/'): - lib = lib[len('@rpath/'):] + 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/'):] + 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) + raise Exception("Could not resolve library: " + lib) def check_vulkan_max_version(version): try: - result = subprocess.check_output("pkg-config vulkan --max-version=" + version, shell = True) + subprocess.check_output( + f"pkg-config vulkan --max-version={version}", + shell=True, + ) return True - except: + except Exception: return False def get_homebrew_prefix(): - # set default to standard ARM path, intel path is already in the vulkan loader search array + # set default to standard ARM path, intel path is already in the vulkan + # loader search array result = "/opt/homebrew" try: - result = subprocess.check_output("brew --prefix", universal_newlines=True, shell=True, stderr=subprocess.DEVNULL).strip() - except: + result = subprocess.check_output( + ["brew", "--prefix"], + universal_newlines=True, + stderr=subprocess.DEVNULL, + ).strip() + except Exception: pass return result def install_name_tool_change(old, new, objfile): - subprocess.call(["install_name_tool", "-change", old, new, objfile], stderr=subprocess.DEVNULL) + 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) + 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]) @@ -117,7 +133,16 @@ def 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 = []): +def libraries(objfile, result=None, result_relative=None, rapths=None): + if result is None: + result = {} + + if result_relative is None: + result_relative = set() + + if rapths is None: + rapths = [] + rapths = get_rapths(objfile) + rapths libs_list, libs_relative = otool(objfile, rapths) result[objfile] = libs_list @@ -130,10 +155,10 @@ def libraries(objfile, result = dict(), result_relative = set(), rapths = []): return result, result_relative def lib_path(binary): - return os.path.join(os.path.dirname(binary), 'lib') + return os.path.join(os.path.dirname(binary), "lib") def resources_path(binary): - return os.path.join(os.path.dirname(binary), '../Resources') + return os.path.join(os.path.dirname(binary), "../Resources") def lib_name(lib): return os.path.join("@executable_path", "lib", os.path.basename(lib)) @@ -166,78 +191,88 @@ def process_libraries(libs_dict, libs_dyn, binary): 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() + swift_stdlib_tool = subprocess.check_output( + ["xcrun", "--find", "swift-stdlib-tool"], + 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) + swift_lib_path = os.path.join(swift_stdlib_tool, "../../lib/swift-5.0/macosx") + swift_lib_path = os.path.abspath(swift_lib_path) - command = [swiftStdlibTool, '--copy', '--platform', 'macosx', '--scan-executable', binary, '--destination', lib_path(binary)] + command = [ + swift_stdlib_tool, "--copy", "--platform", "macosx", + "--scan-executable", binary, "--destination", lib_path(binary), + ] - if os.path.exists(swiftLibPath): - command.extend(['--source-libraries', swiftLibPath]) + if os.path.exists(swift_lib_path): + command.extend(["--source-libraries", swift_lib_path]) subprocess.check_output(command, universal_newlines=True) print(">> setting additional rpath for swift libraries") install_name_tool_add_rpath("@executable_path/lib", binary) -def process_vulkan_loader(binary, loaderName, loaderRelativeFolder, libraryNode): +def process_vulkan_loader(binary, loader_name, loader_relative_folder, library_node): # https://github.com/KhronosGroup/Vulkan-Loader/blob/main/docs/LoaderDriverInterface.md#example-macos-driver-search-path # https://github.com/KhronosGroup/Vulkan-Loader/blob/main/docs/LoaderLayerInterface.md#macos-layer-discovery - loaderSystemSearchFolders = [ - os.path.join(os.path.expanduser("~"), ".config", loaderRelativeFolder), - os.path.join("/etc/xdg", loaderRelativeFolder), - os.path.join("/usr/local/etc", loaderRelativeFolder), - os.path.join("/etc", loaderRelativeFolder), - os.path.join(os.path.expanduser("~"), ".local/share", loaderRelativeFolder), - os.path.join("/usr/local/share", loaderRelativeFolder), - os.path.join("/usr/share/vulkan", loaderRelativeFolder), - os.path.join(get_homebrew_prefix(), 'share', loaderRelativeFolder), + loader_system_search_folders = [ + os.path.join(os.path.expanduser("~"), ".config", loader_relative_folder), + os.path.join("/etc/xdg", loader_relative_folder), + os.path.join("/usr/local/etc", loader_relative_folder), + os.path.join("/etc", loader_relative_folder), + os.path.join(os.path.expanduser("~"), ".local/share", loader_relative_folder), + os.path.join("/usr/local/share", loader_relative_folder), + os.path.join("/usr/share/vulkan", loader_relative_folder), + os.path.join(get_homebrew_prefix(), "share", loader_relative_folder), ] - loaderSystemFolder = "" - for loaderSystemSearchFolder in loaderSystemSearchFolders: - if os.path.exists(loaderSystemSearchFolder): - loaderSystemFolder = loaderSystemSearchFolder + loader_system_folder = "" + for loader_system_search_folder in loader_system_search_folders: + if os.path.exists(loader_system_search_folder): + loader_system_folder = loader_system_search_folder break - if not loaderSystemFolder: - print(">>> could not find loader folder " + loaderRelativeFolder) + if not loader_system_folder: + print(">>> could not find loader folder " + loader_relative_folder) return - loaderBundleFolder = os.path.join(resources_path(binary), loaderRelativeFolder) - loaderSystemPath = os.path.join(loaderSystemFolder, loaderName) - loaderBundlePath = os.path.join(loaderBundleFolder, loaderName) - libraryRelativeFolder = "../../../Frameworks/" + loader_bundle_folder = os.path.join(resources_path(binary), loader_relative_folder) + loader_system_path = os.path.join(loader_system_folder, loader_name) + loader_bundle_path = os.path.join(loader_bundle_folder, loader_name) + library_relative_folder = "../../../Frameworks/" - if not os.path.exists(loaderSystemPath): - print(">>> could not find loader " + loaderName) + if not os.path.exists(loader_system_path): + print(">>> could not find loader " + loader_name) return - if not os.path.exists(loaderBundleFolder): - os.makedirs(loaderBundleFolder) + if not os.path.exists(loader_bundle_folder): + os.makedirs(loader_bundle_folder) - loaderSystemFile = open(loaderSystemPath, 'r') - loaderJsonData = json.load(loaderSystemFile) - librarySystemPath = os.path.join(loaderSystemFolder, loaderJsonData[libraryNode]["library_path"]) + loader_system_file = open(loader_system_path) + loader_json_data = json.load(loader_system_file) + library_path = loader_json_data[library_node]["library_path"] + library_system_path = os.path.join(loader_system_folder, library_path) - if not os.path.exists(librarySystemPath): - print(">>> could not find loader library " + librarySystemPath) + if not os.path.exists(library_system_path): + print(">>> could not find loader library " + library_system_path) return - print(">>> modifiying and writing loader json " + loaderName) - loaderBundleFile = open(loaderBundlePath, 'w') - loaderLibraryName = os.path.basename(librarySystemPath) - loaderJsonData[libraryNode]["library_path"] = os.path.join(libraryRelativeFolder, loaderLibraryName) - json.dump(loaderJsonData, loaderBundleFile, indent=4) - - print(">>> copying loader library " + loaderLibraryName) - frameworkBundleFolder = os.path.join(loaderBundleFolder, libraryRelativeFolder) - if not os.path.exists(frameworkBundleFolder): - os.makedirs(frameworkBundleFolder) - shutil.copy(librarySystemPath, os.path.join(frameworkBundleFolder, loaderLibraryName)) + print(">>> modifiying and writing loader json " + loader_name) + loader_bundle_file = open(loader_bundle_path, "w") + loader_library_name = os.path.basename(library_system_path) + library_path = os.path.join(library_relative_folder, loader_library_name) + loader_json_data[library_node]["library_path"] = library_path + json.dump(loader_json_data, loader_bundle_file, indent=4) + + print(">>> copying loader library " + loader_library_name) + framework_bundle_folder = os.path.join( + loader_bundle_folder, library_relative_folder, + ) + if not os.path.exists(framework_bundle_folder): + os.makedirs(framework_bundle_folder) + library_target_path = os.path.join(framework_bundle_folder, loader_library_name) + shutil.copy(library_system_path, library_target_path) def remove_dev_tools_rapths(binary): for path in get_rpaths_dev_tools(binary): @@ -262,7 +297,12 @@ def process(binary): print(">> copying and processing vulkan loader") process_vulkan_loader(binary, "MoltenVK_icd.json", "vulkan/icd.d", "ICD") if check_vulkan_max_version("1.3.261.1"): - process_vulkan_loader(binary, "VkLayer_khronos_synchronization2.json", "vulkan/explicit_layer.d", "layer") + process_vulkan_loader( + binary, + "VkLayer_khronos_synchronization2.json", + "vulkan/explicit_layer.d", + "layer", + ) if __name__ == "__main__": process(sys.argv[1]) diff --git a/TOOLS/file2string.py b/TOOLS/file2string.py index 93741324c8e85..93efcc0ee6876 100755 --- a/TOOLS/file2string.py +++ b/TOOLS/file2string.py @@ -25,21 +25,21 @@ import os import sys + def file2string(infilename, infile, outfile): - outfile.write("// Generated from %s\n\n" % infilename) + outfile.write(f"// Generated from {infilename}\n\n") - conv = ["\\%03o" % c for c in range(256)] + conv = [f"\\{c:03o}" 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 + conv[ord(c)] = "\\" + esc for line in infile: - outfile.write('"' + ''.join(conv[c] for c in line) + '"\n') + outfile.write('"' + "".join(conv[c] for c in line) + '"\n') if __name__ == "__main__": - - with open(sys.argv[1], 'rb') as infile, open(sys.argv[2], "w") as outfile: + with open(sys.argv[1], "rb") as infile, open(sys.argv[2], "w") as outfile: file2string(os.path.relpath(sys.argv[1], sys.argv[3]), infile, outfile) diff --git a/TOOLS/gen-interface-changes.py b/TOOLS/gen-interface-changes.py index e7747630f03c3..affbe92054008 100755 --- a/TOOLS/gen-interface-changes.py +++ b/TOOLS/gen-interface-changes.py @@ -26,11 +26,12 @@ from shutil import which from subprocess import check_output + def add_new_entries(docs_dir, out, git): changes_dir = pathlib.Path(docs_dir) / "interface-changes" files = [] for f in pathlib.Path(changes_dir).glob("*.txt"): - if f.is_file() and not f.name == "example.txt": + if f.is_file() and f.name != "example.txt": timestamp = check_output([git, "log", "--format=%ct", "-n", "1", "--", f], encoding="UTF-8") if timestamp: @@ -53,7 +54,7 @@ def add_new_entries(docs_dir, out, git): print(f"Usage: {sys.argv[0]} ") sys.exit(1) - git = which('git') + git = which("git") if not git: print("Unable to find git binary") sys.exit(1) @@ -73,7 +74,7 @@ def add_new_entries(docs_dir, out, git): docs_dir = pathlib.Path(sys.argv[0]).resolve().parents[1] / "DOCS" interface_changes = docs_dir / "interface-changes.rst" - with open(interface_changes, "r") as f: + with open(interface_changes) as f: lines = [line.rstrip() for line in f] ver_line = " --- mpv 0." + major_version + ".0 ---" diff --git a/TOOLS/gen-mpv-desktop.py b/TOOLS/gen-mpv-desktop.py index 7bbb33e5be0fb..681633acda3e8 100755 --- a/TOOLS/gen-mpv-desktop.py +++ b/TOOLS/gen-mpv-desktop.py @@ -24,20 +24,28 @@ from subprocess import check_output if __name__ == "__main__": - with open(sys.argv[1], "r", encoding="UTF-8") as f: + with open(sys.argv[1], encoding="UTF-8") as f: next(f) mpv_desktop = dict([line.split("=", 1) for line in f]) if not mpv_desktop["X-KDE-Protocols"]: raise ValueError("Missing X-KDE-Protocols entry in mpv.desktop file") - mpv_protocols = check_output([sys.argv[2], "--no-config", "--list-protocols"], encoding="UTF-8") - mpv_protocols = set(line.strip(" :/") for line in mpv_protocols.splitlines() if "://" in line) + mpv_protocols = check_output( + [sys.argv[2], "--no-config", "--list-protocols"], + encoding="UTF-8", + ) + mpv_protocols = { + line.strip(" :/") + for line in mpv_protocols.splitlines() + if "://" in line + } if len(mpv_protocols) == 0: raise ValueError("Unable to parse any protocols from mpv '--list-protocols'") protocol_list = set(mpv_desktop["X-KDE-Protocols"].strip().split(",")) - mpv_desktop["X-KDE-Protocols"] = ",".join(sorted(mpv_protocols & protocol_list)) + "\n" + compatible_protocols = sorted(mpv_protocols & protocol_list) + mpv_desktop["X-KDE-Protocols"] = ",".join(compatible_protocols) + "\n" with open(sys.argv[3], "w", encoding="UTF-8") as f: f.write("[Desktop Entry]" + "\n") diff --git a/TOOLS/macos-sdk-version.py b/TOOLS/macos-sdk-version.py index 12e1071d7588f..55e8975b77336 100755 --- a/TOOLS/macos-sdk-version.py +++ b/TOOLS/macos-sdk-version.py @@ -3,44 +3,46 @@ # 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') + 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'], + 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'], + 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: + sdk_version = check_output( + [xcodebuild, "-sdk", "macosx", "-version", "ProductVersion"], + encoding="UTF-8", + stderr=subprocess.DEVNULL, + ) + except Exception: pass if not isinstance(sdk_version, str): - sdk_version = '10.10.0' + 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)) + sys.stdout.write(",".join(sdk_info)) diff --git a/TOOLS/macos-swift-lib-directory.py b/TOOLS/macos-swift-lib-directory.py index 51e8cbe96f099..a7aca05b9cbc8 100755 --- a/TOOLS/macos-swift-lib-directory.py +++ b/TOOLS/macos-swift-lib-directory.py @@ -8,8 +8,9 @@ from shutil import which from subprocess import check_output + def find_swift_lib(): - swift_lib_dir = os.environ.get('SWIFT_LIB_DYNAMIC', '') + swift_lib_dir = os.environ.get("SWIFT_LIB_DYNAMIC", "") if swift_lib_dir: return swift_lib_dir @@ -27,7 +28,10 @@ def find_swift_lib(): 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") + 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 diff --git a/TOOLS/matroska.py b/TOOLS/matroska.py index 6abece6e95886..37ae86df90215 100755 --- a/TOOLS/matroska.py +++ b/TOOLS/matroska.py @@ -4,6 +4,10 @@ Can also be used to directly parse Matroska files and display their contents. """ +import sys +from binascii import hexlify +from math import ldexp + # # This file is part of mpv. # @@ -23,218 +27,218 @@ 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', + "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', + "CRC32, bf, binary", + "Void, ec, binary", ) elements_matroska = ( - 'Segment, 18538067, sub', ( + "Segment, 18538067, sub", ( - 'SeekHead*, 114d9b74, sub', ( - 'Seek*, 4dbb, sub', ( - 'SeekID, 53ab, ebml_id', - 'SeekPosition, 53ac, uint', + "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', + "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', + "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', + "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', - 'LanguageBCP47, 22b59d, 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', + "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", + "LanguageBCP47, 22b59d, 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', + "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', + "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', + "ContentEncodings, 6d80, sub", ( + "ContentEncoding*, 6240, sub", ( + "ContentEncodingOrder, 5031, uint", + "ContentEncodingScope, 5032, uint", + "ContentEncodingType, 5033, uint", + "ContentCompression, 5034, sub", ( + "ContentCompAlgo, 4254, uint", + "ContentCompSettings, 4255, binary", ), ), ), - 'BlockAdditionMapping*, 41e4, sub', ( - 'BlockAddIDValue, 41f0, uint', - 'BlockAddIDName, 41a4, str', - 'BlockAddIDType, 41e7, uint', - 'BlockAddIDExtraData, 41ed, binary', + "BlockAdditionMapping*, 41e4, sub", ( + "BlockAddIDValue, 41f0, uint", + "BlockAddIDName, 41a4, str", + "BlockAddIDType, 41e7, uint", + "BlockAddIDExtraData, 41ed, 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', + "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', + "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', - 'ChapLanguageBCP47*, 437d, str', - 'ChapCountry*, 437e, str', + "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", + "ChapLanguageBCP47*, 437d, 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', + "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', - 'TagLanguageBCP47, 447b, str', - 'TagString, 4487, str', - 'TagDefault, 4484, uint', + "SimpleTag*, 67c8, sub", ( + "TagName, 45a3, str", + "TagLanguage, 447a, str", + "TagLanguageBCP47, 447b, str", + "TagString, 4487, str", + "TagDefault, 4484, uint", ), ), ), @@ -242,15 +246,9 @@ ) -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 @@ -260,29 +258,29 @@ def camelcase_to_words(name): parts.append(name[start:i]) start = i parts.append(name[start:]) - return '_'.join(parts).lower() + return "_".join(parts).lower() -class MatroskaElement(object): +class MatroskaElement: def __init__(self, name, elid, valtype, namespace): self.name = name - self.definename = '{0}_ID_{1}'.format(namespace, name.upper()) + self.definename = f"{namespace}_ID_{name.upper()}" self.fieldname = camelcase_to_words(name) - self.structname = 'ebml_' + self.fieldname + self.structname = "ebml_" + self.fieldname self.elid = elid self.valtype = valtype - if valtype == 'sub': - self.ebmltype = 'EBML_TYPE_SUBELEMENTS' - self.valname = 'struct ' + self.structname + if valtype == "sub": + self.ebmltype = "EBML_TYPE_SUBELEMENTS" + self.valname = "struct " + self.structname else: - self.ebmltype = 'EBML_TYPE_' + valtype.upper() + 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', + 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) + raise SyntaxError("Unrecognized value type " + valtype) self.subelements = () def add_subelements(self, subelements): @@ -291,14 +289,14 @@ def add_subelements(self, subelements): elementd = {} elementlist = [] -def parse_elems(l, namespace): +def parse_elems(elements, namespace): subelements = [] - for el in l: + for el in elements: if isinstance(el, str): - name, hexid, eltype = [x.strip() for x in el.split(',')] + name, hexid, eltype = (x.strip() for x in el.split(",")) hexid = hexid.lower() - multiple = name.endswith('*') - name = name.strip('*') + multiple = name.endswith("*") + name = name.strip("*") new = MatroskaElement(name, hexid, eltype, namespace) elementd[hexid] = new elementlist.append(new) @@ -307,19 +305,19 @@ def parse_elems(l, namespace): new.add_subelements(parse_elems(el, namespace)) return subelements -parse_elems(elements_ebml, 'EBML') -parse_elems(elements_matroska, 'MATROSKA') +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') + 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') +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, f"#define {el.definename:40} 0x{el.elid}") printf(out) @@ -327,46 +325,46 @@ def generate_C_header(out): 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 + printf(out, f"struct {el.structname} {{") + length = 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, " {e.valname:{length}} {star}{e.fieldname};".format( + e=subel, length=length, star=" *"[multiple])) printf(out) for subel, multiple in el.subelements: - printf(out, ' int n_{0.fieldname};'.format(subel)) - printf(out, '};') + printf(out, f" int n_{subel.fieldname};") + 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, f"extern const struct ebml_elem_desc {el.structname}_desc;") printf(out) - printf(out, '#define MAX_EBML_SUBELEMENTS', max(len(el.subelements) + 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') +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))) + printf(out, "#define N", el.fieldname) + printf(out, f'E_S("{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') + msg = f"F({subel.definename}, {subel.fieldname}, {int(multiple)})" + printf(out, msg) + printf(out, "}};") + printf(out, "#undef N") else: - printf(out, 'E("{0.name}", {0.fieldname}, {0.ebmltype})'.format(el)) + printf(out, f'E("{el.name}", {el.fieldname}, {el.ebmltype})') def read(s, length): t = s.read(length) if len(t) != length: - raise EOF + raise EOFError return t def read_id(s): @@ -424,47 +422,48 @@ def read_float(s, length): return f def parse_one(s, depth, parent, maxlen): - elid = hexlify(read_id(s)).decode('ascii') + 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:') + if elem.valtype != "skip": + indent = " " * depth + print(f"{indent} [{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'): + 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')) + dec = "" + if elem.valtype == "ebml_id": + idelem = elementd.get(hexlify(t).decode("ascii")) if idelem is None: - dec = '(UNKNOWN)' + dec = "(UNKNOWN)" else: - dec = '({0.name})'.format(idelem) + dec = f"({idelem.name})" if len(t) < 20: - t = hexlify(t).decode('ascii') + 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': + t = f"<{len(t)} bytes>" + 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) + print(" " * depth, "[" + elid + "] Unknown element! size:", length) read(s, length) return this_length @@ -472,17 +471,17 @@ def parse_one(s, depth, parent, maxlen): 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")) + 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: + except EOFError: if s.tell() != start: raise Exception("Unexpected end of file") break diff --git a/TOOLS/osxbundle.py b/TOOLS/osxbundle.py index f096c3856e266..bf144fe0129a9 100755 --- a/TOOLS/osxbundle.py +++ b/TOOLS/osxbundle.py @@ -1,22 +1,24 @@ #!/usr/bin/env python3 +import fileinput import os import shutil -import fileinput -import dylib_unhell import subprocess from optparse import OptionParser +import dylib_unhell + + def bundle_path(binary_name): - return "%s.app" % binary_name + return f"{binary_name}.app" 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') + 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') + return os.path.join(bundle_path(binary_name), "Contents", "MacOS") def target_binary(binary_name): return os.path.join(target_directory(binary_name), @@ -26,7 +28,7 @@ def copy_bundle(binary_name, src_path): if os.path.isdir(bundle_path(binary_name)): shutil.rmtree(bundle_path(binary_name)) shutil.copytree( - os.path.join(src_path, 'TOOLS', 'osxbundle', bundle_name(binary_name)), + os.path.join(src_path, "TOOLS", "osxbundle", bundle_name(binary_name)), bundle_path(binary_name)) def copy_binary(binary_name): @@ -34,20 +36,21 @@ def copy_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)) + print(line.rstrip().replace("${VERSION}", version)) def sign_bundle(binary_name): - sign_directories = ['Contents/Frameworks', 'Contents/MacOS'] - for dir in sign_directories: - resolved_dir = os.path.join(bundle_path(binary_name), dir) + sign_directories = ["Contents/Frameworks", "Contents/MacOS"] + for sign_dir in sign_directories: + resolved_dir = os.path.join(bundle_path(binary_name), sign_dir) for root, _dirs, files in os.walk(resolved_dir): for f in files: - subprocess.run(['codesign', '--force', '-s', '-', os.path.join(root, f)]) - subprocess.run(['codesign', '--force', '-s', '-', bundle_path(binary_name)]) + path = os.path.join(root, f) + subprocess.run(["codesign", "--force", "-s", "-", path]) + subprocess.run(["codesign", "--force", "-s", "-", bundle_path(binary_name)]) def bundle_version(src_path): - version = 'UNKNOWN' - version_path = os.path.join(src_path, 'MPV_VERSION') + version = "UNKNOWN" + version_path = os.path.join(src_path, "MPV_VERSION") if os.path.exists(version_path): x = open(version_path) version = x.read() @@ -71,7 +74,7 @@ def main(): version = bundle_version(src_path).rstrip() - print("Creating macOS application bundle (version: %s)..." % version) + print(f"Creating macOS application bundle (version: {version})...") print("> copying bundle skeleton") copy_bundle(binary_name, src_path) print("> copying binary") diff --git a/TOOLS/stats-conv.py b/TOOLS/stats-conv.py index 0a9cf4ee9eb25..e217570a634f1 100755 --- a/TOOLS/stats-conv.py +++ b/TOOLS/stats-conv.py @@ -1,8 +1,9 @@ #!/usr/bin/env python3 -from PyQt6 import QtWidgets -import pyqtgraph as pg -import sys import re +import sys + +import pyqtgraph as pg +from PyQt6 import QtWidgets filename = sys.argv[1] @@ -70,13 +71,22 @@ def get_event(event, evtype): 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): +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 mk_color(t): return pg.mkColor(int(t[0] * 255), int(t[1] * 255), int(t[2] * 255)) SCALE = 1e6 # microseconds to seconds -with open(filename, "r") as file: +with open(filename) as file: for line in file: line = line.split("#")[0].strip() if not line: @@ -140,8 +150,8 @@ def mkColor(t): else: e.vals = [(x, y * (m - index) / m) for (x, y) in e.vals] -pg.setConfigOption('background', 'w') -pg.setConfigOption('foreground', 'k') +pg.setConfigOption("background", "w") +pg.setConfigOption("foreground", "k") app = QtWidgets.QApplication([]) win = pg.GraphicsLayoutWidget() win.show() @@ -161,15 +171,15 @@ def mkColor(t): for e in G.sevents: cur = ax[1 if e.type == "value" else 0] - if not cur in G.curveno: + if cur not in G.curveno: G.curveno[cur] = 0 - args = {'name': e.name,'antialias':True} - color = mkColor(colors[G.curveno[cur] % len(colors)]) + args = {"name": e.name,"antialias":True} + color = mk_color(colors[G.curveno[cur] % len(colors)]) if e.type == "event-signal": - args['symbol'] = e.marker - args['symbolBrush'] = pg.mkBrush(color, width=0) + args["symbol"] = e.marker + args["symbolBrush"] = pg.mkBrush(color, width=0) else: - args['pen'] = pg.mkPen(color, width=0) + args["pen"] = pg.mkPen(color, width=0) G.curveno[cur] += 1 cur.plot(*zip(*e.vals), **args) diff --git a/ci/build-msys2.sh b/ci/build-msys2.sh index 2d150913d6648..c5a7da2800c1d 100755 --- a/ci/build-msys2.sh +++ b/ci/build-msys2.sh @@ -3,14 +3,13 @@ args=( --werror -Dc_args='-Wno-error=deprecated -Wno-error=deprecated-declarations' - -D{d3d-hwaccel,d3d11,dvdnav,jpeg,lcms2,libarchive}=enabled - -D{libbluray,lua,shaderc,spirv-cross}=enabled + -D{cdda,d3d-hwaccel,d3d11,dvdnav,jpeg,lcms2,libarchive}=enabled + -D{libbluray,lua,shaderc,spirv-cross,uchardet,vapoursynth}=enabled -D{libmpv,tests}=true ) -[[ "$SYS" != "clang32" && "$SYS" != "mingw32" ]] && args+=( - -D{cdda,egl-angle-lib,egl-angle-win32,pdf-build}=enabled - -D{rubberband,uchardet,vapoursynth,win32-smtc}=enabled +[[ "$SYS" != "mingw32" ]] && args+=( + -D{egl-angle-lib,egl-angle-win32,pdf-build,rubberband,win32-smtc}=enabled ) meson setup build "${args[@]}" diff --git a/ci/build-openbsd.sh b/ci/build-openbsd.sh index b48de5020e2df..26550a7bdc1ed 100755 --- a/ci/build-openbsd.sh +++ b/ci/build-openbsd.sh @@ -7,7 +7,7 @@ mkdir -p subprojects cat < subprojects/ffmpeg.wrap [wrap-git] url = https://gitlab.freedesktop.org/gstreamer/meson-ports/ffmpeg.git -revision = meson-6.1 +revision = meson-7.1 depth = 1 [provide] libavcodec = libavcodec_dep diff --git a/ci/build-win32.ps1 b/ci/build-win32.ps1 index 489e12c794869..6fd2f9b696695 100644 --- a/ci/build-win32.ps1 +++ b/ci/build-win32.ps1 @@ -115,7 +115,7 @@ $projects = @( @{ Path = "$subprojects/ffmpeg.wrap" URL = "https://gitlab.freedesktop.org/gstreamer/meson-ports/ffmpeg.git" - Revision = "meson-7.0" + Revision = "meson-7.1" Provides = @( "libavcodec = libavcodec_dep", "libavdevice = libavdevice_dep", diff --git a/ci/lint-commit-msg.py b/ci/lint-commit-msg.py index 3ceaa24fbf0c1..a6ed9ecc41de9 100755 --- a/ci/lint-commit-msg.py +++ b/ci/lint-commit-msg.py @@ -1,108 +1,118 @@ #!/usr/bin/env python3 -import os, sys, json, subprocess, re -from typing import Dict, Tuple, Callable, Optional +import json +import os +import re +import subprocess +import sys +from typing import Callable, Dict, Optional, Tuple + def call(cmd) -> str: - sys.stdout.flush() - ret = subprocess.run(cmd, check=True, stdout=subprocess.PIPE, text=True) - return ret.stdout + sys.stdout.flush() + ret = subprocess.run(cmd, check=True, stdout=subprocess.PIPE, text=True) + return ret.stdout lint_rules: Dict[str, Tuple[Callable, str]] = {} # A lint rule should return True if everything is okay def lint_rule(description: str): - def f(func): - assert func.__name__ not in lint_rules.keys() - lint_rules[func.__name__] = (func, description) - return f + def f(func): + assert func.__name__ not in lint_rules + lint_rules[func.__name__] = (func, description) + return f def get_commit_range() -> Optional[str]: - if len(sys.argv) > 1: - return sys.argv[1] - # https://github.com/actions/runner/issues/342#issuecomment-590670059 - event_name = os.environ["GITHUB_EVENT_NAME"] - with open(os.environ["GITHUB_EVENT_PATH"], "rb") as f: - event = json.load(f) - if event_name == "push": - if event["created"] or event["forced"]: - print("Skipping logic on branch creation or force-push") - return None - return event["before"] + "..." + event["after"] - elif event_name == "pull_request": - return event["pull_request"]["base"]["sha"] + ".." + event["pull_request"]["head"]["sha"] - return None + if len(sys.argv) > 1: + return sys.argv[1] + # https://github.com/actions/runner/issues/342#issuecomment-590670059 + event_name = os.environ["GITHUB_EVENT_NAME"] + with open(os.environ["GITHUB_EVENT_PATH"], "rb") as f: + event = json.load(f) + if event_name == "push": + if event["created"] or event["forced"]: + print("Skipping logic on branch creation or force-push") + return None + return event["before"] + "..." + event["after"] + elif event_name == "pull_request": + base = event["pull_request"]["base"]["sha"] + head = event["pull_request"]["head"]["sha"] + return f"{base}..{head}" + return None def do_lint(commit_range: str) -> bool: - commits = call(["git", "log", "--pretty=format:%H %s", commit_range]).splitlines() - print(f"Linting {len(commits)} commit(s):") - any_failed = False - for commit in commits: - sha, _, _ = commit.partition(' ') - body = call(["git", "show", "-s", "--format=%B", sha]).splitlines() - failed = [] - if len(body) == 0: - failed.append("* Commit message must not be empty") - else: - for k, v in lint_rules.items(): - if not v[0](body): - failed.append(f"* {v[1]} [{k}]") - if failed: - any_failed = True - print("-" * 40) - sys.stdout.flush() - subprocess.run(["git", "-P", "show", "-s", sha]) - print("\nhas the following issues:") - print("\n".join(failed)) - print("-" * 40) - return any_failed + commits = call(["git", "log", "--pretty=format:%H %s", commit_range]).splitlines() + print(f"Linting {len(commits)} commit(s):") + any_failed = False + for commit in commits: + sha, _, _ = commit.partition(" ") + body = call(["git", "show", "-s", "--format=%B", sha]).splitlines() + failed = [] + if len(body) == 0: + failed.append("* Commit message must not be empty") + else: + for k, v in lint_rules.items(): + if not v[0](body): + failed.append(f"* {v[1]} [{k}]") + if failed: + any_failed = True + print("-" * 40) + sys.stdout.flush() + subprocess.run(["git", "-P", "show", "-s", sha]) + print("\nhas the following issues:") + print("\n".join(failed)) + print("-" * 40) + return any_failed ################################################################################ -NO_PREFIX_WHITELIST = r"^Revert \"(.*)\"|^Reapply \"(.*)\"|^Release [0-9]|^Update MPV_VERSION$" +NO_PREFIX_WHITELIST = \ + r"^Revert \"(.*)\"|^Reapply \"(.*)\"|^Release [0-9]|^Update MPV_VERSION$" @lint_rule("Subject line must contain a prefix identifying the sub system") def subsystem_prefix(body): - return (re.search(NO_PREFIX_WHITELIST, body[0]) or - re.search(r"^[\w/\.{},-]+: ", body[0])) + return (re.search(NO_PREFIX_WHITELIST, body[0]) or + re.search(r"^[\w/\.{},-]+: ", body[0])) @lint_rule("First word after : must be lower case") def description_lowercase(body): - # Allow all caps for acronyms and options with -- - return (re.search(NO_PREFIX_WHITELIST, body[0]) or - re.search(r": (?:[A-Z]{2,} |--[a-z]|[a-z0-9])", body[0])) + # Allow all caps for acronyms and options with -- + return (re.search(NO_PREFIX_WHITELIST, body[0]) or + re.search(r": (?:[A-Z]{2,} |--[a-z]|[a-z0-9])", body[0])) @lint_rule("Subject line must not end with a full stop") def no_dot(body): - return not body[0].rstrip().endswith('.') + return not body[0].rstrip().endswith(".") @lint_rule("There must be an empty line between subject and extended description") def empty_line(body): - return len(body) == 1 or body[1].strip() == "" + return len(body) == 1 or body[1].strip() == "" # been seeing this one all over github lately, must be the webshits @lint_rule("Do not use 'conventional commits' style") def no_cc(body): - return not re.search(r"(?i)^(feat|fix|chore|refactor)[!:(]", body[0]) + return not re.search(r"(?i)^(feat|fix|chore|refactor)[!:(]", body[0]) @lint_rule("History must be linear, no merge commits") def no_merge(body): - return not body[0].startswith("Merge ") + return not body[0].startswith("Merge ") @lint_rule("Subject line should be shorter than 72 characters") def line_too_long(body): - revert = re.search(r"^Revert \"(.*)\"|^Reapply \"(.*)\"", body[0]) - return revert or len(body[0]) <= 72 + revert = re.search(r"^Revert \"(.*)\"|^Reapply \"(.*)\"", body[0]) + return revert or len(body[0]) <= 72 -@lint_rule("Prefix should not include file extension (use `vo_gpu: ...` not `vo_gpu.c: ...`)") +@lint_rule( + "Prefix should not include file extension (use `vo_gpu: ...` not `vo_gpu.c: ...`)", +) def no_file_exts(body): - return not re.search(r"[a-z0-9]\.([chm]|cpp|swift|py): ", body[0]) + return not re.search(r"[a-z0-9]\.([chm]|cpp|swift|py): ", body[0]) ################################################################################ if __name__ == "__main__": - commit_range = get_commit_range() - if commit_range is None: - exit(0) - print("Commit range:", commit_range) - any_failed = do_lint(commit_range) - exit(1 if any_failed else 0) + commit_range = get_commit_range() + if commit_range is None: + exit(0) + print("Commit range:", commit_range) + any_failed = do_lint(commit_range) + exit(1 if any_failed else 0) diff --git a/filters/f_hwtransfer.c b/filters/f_hwtransfer.c index f52bdff61efe5..f39a28f749a66 100644 --- a/filters/f_hwtransfer.c +++ b/filters/f_hwtransfer.c @@ -44,8 +44,8 @@ struct priv { int num_map_fmts; // If the selected hwdec has a conversion filter available for converting - // between sw formats in hardware, the name will be set. NULL otherwise. - const char *conversion_filter_name; + // between sw formats in hardware, the getter will be set. NULL otherwise. + struct mp_conversion_filter *(*get_conversion_filter)(int imgfmt); }; struct hwmap_pairs { @@ -110,32 +110,43 @@ static bool select_format(struct priv *p, int input_fmt, return true; } - // First find the closest hw input fmt. Some hwdec APIs return crazy lists of - // "supported" formats, which then are not supported or crash (???), so - // the this is a good way to avoid problems. - // (Actually we should just have hardcoded everything instead of relying on - // this fragile bullshit FFmpeg API and the fragile bullshit hwdec drivers.) - int hw_input_fmt = mp_imgfmt_select_best_list(p->fmts, p->num_fmts, input_fmt); - if (!hw_input_fmt) + // Select best output format from the available hw formats + int hw_output_fmt = mp_imgfmt_select_best_list(p->fmts, p->num_fmts, input_fmt); + if (!hw_output_fmt) return false; // Dumb, but find index for p->fmts[index]==hw_input_fmt. int index = -1; for (int n = 0; n < p->num_fmts; n++) { - if (p->fmts[n] == hw_input_fmt) + if (p->fmts[n] == hw_output_fmt) index = n; } if (index < 0) return false; - // Now check the available output formats. This is the format our sw frame - // will be in after the upload (probably). + // Now check the available upload formats. This is the sw format which will + // be uploaded to given output format. int *upload_fmts = &p->upload_fmts[p->fmt_upload_index[index]]; int num_upload_fmts = p->fmt_upload_num[index]; - int hw_output_fmt = mp_imgfmt_select_best_list(upload_fmts, num_upload_fmts, - input_fmt); - if (!hw_output_fmt) + // Select the best input format from the available upload formats. + int hw_input_fmt = mp_imgfmt_select_best_list(upload_fmts, num_upload_fmts, input_fmt); + + // If the input format is not directly uploadable, conversion will be needed. + // Attempt to convert directly to the output format to avoid double conversion. + if (input_fmt != hw_input_fmt) { + int upload_output_fmt = mp_imgfmt_select_best_list(upload_fmts, num_upload_fmts, hw_output_fmt); + // Use this format only if it avoids double conversion, i.e., if we can + // upload the output format directly. If that's not the case, just use + // the best format for the input format selected earlier, as double + // conversion is unavoidable anyway. This approach prefers a closer + // conversion before upload and do remaining conversion during upload, + // which may be hardware-accelerated. + if (upload_output_fmt == hw_output_fmt) + hw_input_fmt = upload_output_fmt; + } + + if (!hw_input_fmt) return false; *out_hw_input_fmt = hw_input_fmt; @@ -270,6 +281,19 @@ static bool vo_supports(struct mp_hwdec_ctx *ctx, int hw_fmt, int sw_fmt) return false; } +static bool upload_supports(struct mp_hwdec_ctx *ctx, int fmt) +{ + if (!ctx->supported_hwupload_formats) + return true; // if unset, all formats are allowed + + for (int i = 0; ctx->supported_hwupload_formats[i]; i++) { + if (ctx->supported_hwupload_formats[i] == fmt) + return true; + } + + return false; +} + /** * Some hwcontexts do not implement constraints, and so cannot * report supported formats, so cobble something together from our @@ -392,12 +416,9 @@ static bool probe_formats(struct mp_filter *f, int hw_imgfmt, bool use_conversio ctx->conversion_config); } - int hw_transfer_index = 0; for (int n = 0; cstr->valid_sw_formats && cstr->valid_sw_formats[n] != AV_PIX_FMT_NONE; n++) { - int *not_supported_by_vo = NULL; - int num_not_supported = 0; int imgfmt = pixfmt2imgfmt(cstr->valid_sw_formats[n]); if (!imgfmt) continue; @@ -414,6 +435,11 @@ static bool probe_formats(struct mp_filter *f, int hw_imgfmt, bool use_conversio continue; } + if (!vo_supports(ctx, hw_imgfmt, imgfmt)) { + MP_DBG(f, " not supported by VO\n"); + continue; + } + if (use_conversion_filter) { // The conversion constraints are universal, and do not vary with // source format, so we will associate the same set of target formats @@ -445,7 +471,6 @@ static bool probe_formats(struct mp_filter *f, int hw_imgfmt, bool use_conversio for (int i = 0; ctx->supported_formats[i]; i++) { int fmt = ctx->supported_formats[i]; if (fmt == imgfmt) { - MP_DBG(f, " vo accepts %s\n", mp_imgfmt_to_name(fmt)); MP_TARRAY_APPEND(p, p->upload_fmts, p->num_upload_fmts, fmt); } } @@ -458,19 +483,9 @@ static bool probe_formats(struct mp_filter *f, int hw_imgfmt, bool use_conversio int fmt = pixfmt2imgfmt(fmts[i]); if (!fmt) continue; - if (!vo_supports(ctx, hw_imgfmt, fmt)) { - MP_TARRAY_APPEND(p, not_supported_by_vo, num_not_supported, fmt); - continue; - } MP_DBG(f, " %s", mp_imgfmt_to_name(fmt)); MP_TARRAY_APPEND(p, p->upload_fmts, p->num_upload_fmts, fmt); } - if (num_not_supported) { - MP_DBG(f, "\n not supported by VO:"); - for (int i = 0; i < num_not_supported; i++) { - MP_DBG(f, " %s", mp_imgfmt_to_name(not_supported_by_vo[i])); - } - } MP_DBG(f, "\n"); p->fmt_upload_num[index] = @@ -490,44 +505,37 @@ static bool probe_formats(struct mp_filter *f, int hw_imgfmt, bool use_conversio AV_HWFRAME_TRANSFER_DIRECTION_TO, &fmts, 0) >= 0 && fmts[0] != AV_PIX_FMT_NONE) { - // Don't add the format to p->fmts if we know it cannot be used. - if (ctx->supported_hwupload_formats) { - for (int i = 0; ctx->supported_hwupload_formats[i]; i++) { - if (ctx->supported_hwupload_formats[i] == imgfmt) - MP_TARRAY_APPEND(p, p->fmts, p->num_fmts, imgfmt); - } - } else { - MP_TARRAY_APPEND(p, p->fmts, p->num_fmts, imgfmt); - } - - int index = hw_transfer_index; - hw_transfer_index++; - + int index = p->num_fmts; + MP_TARRAY_APPEND(p, p->fmts, p->num_fmts, imgfmt); MP_TARRAY_GROW(p, p->fmt_upload_index, index); MP_TARRAY_GROW(p, p->fmt_upload_num, index); p->fmt_upload_index[index] = p->num_upload_fmts; + int *upload_not_supported = NULL; + int num_upload_not_supported = 0; + MP_DBG(f, " supports:"); for (int i = 0; fmts[i] != AV_PIX_FMT_NONE; i++) { int fmt = pixfmt2imgfmt(fmts[i]); if (!fmt) continue; - if (!vo_supports(ctx, hw_imgfmt, fmt)) { - MP_TARRAY_APPEND(p, not_supported_by_vo, num_not_supported, fmt); + if (!upload_supports(ctx, fmt)) { + MP_TARRAY_APPEND(NULL, upload_not_supported, num_upload_not_supported, fmt); continue; } MP_DBG(f, " %s", mp_imgfmt_to_name(fmt)); MP_TARRAY_APPEND(p, p->upload_fmts, p->num_upload_fmts, fmt); } - if (num_not_supported) { - MP_DBG(f, "\n not supported by VO:"); - for (int i = 0; i < num_not_supported; i++) { - MP_DBG(f, " %s", mp_imgfmt_to_name(not_supported_by_vo[i])); - } + if (num_upload_not_supported) { + MP_DBG(f, "\n upload not supported:"); + for (int i = 0; i < num_upload_not_supported; ++i) + MP_DBG(f, " %s", mp_imgfmt_to_name(upload_not_supported[i])); } MP_DBG(f, "\n"); + talloc_free(upload_not_supported); + p->fmt_upload_num[index] = p->num_upload_fmts - p->fmt_upload_index[index]; @@ -536,7 +544,6 @@ static bool probe_formats(struct mp_filter *f, int hw_imgfmt, bool use_conversio av_buffer_unref(&frames); } - talloc_free(not_supported_by_vo); } av_hwframe_constraints_free(&cstr); @@ -544,7 +551,7 @@ static bool probe_formats(struct mp_filter *f, int hw_imgfmt, bool use_conversio p->av_device_ctx = av_buffer_ref(ctx->av_device_ref); if (!p->av_device_ctx) return false; - p->conversion_filter_name = ctx->conversion_filter_name; + p->get_conversion_filter = ctx->get_conversion_filter; /* * In the case of needing to do hardware conversion vs uploading, we will @@ -554,7 +561,7 @@ static bool probe_formats(struct mp_filter *f, int hw_imgfmt, bool use_conversio * through whatever format we are given. */ return p->num_upload_fmts > 0 || - (use_conversion_filter && !p->conversion_filter_name); + (use_conversion_filter && !p->get_conversion_filter); } struct mp_hwupload mp_hwupload_create(struct mp_filter *parent, int hw_imgfmt, @@ -582,7 +589,7 @@ struct mp_hwupload mp_hwupload_create(struct mp_filter *parent, int hw_imgfmt, } if (src_is_same_hw) { - if (p->conversion_filter_name) { + if (p->get_conversion_filter) { /* * If we are converting from one sw format to another within the same * hw format, we will use that hw format's conversion filter rather @@ -590,33 +597,24 @@ struct mp_hwupload mp_hwupload_create(struct mp_filter *parent, int hw_imgfmt, */ u.selected_sw_imgfmt = hw_output_fmt; if (sw_imgfmt != u.selected_sw_imgfmt) { - enum AVPixelFormat pixfmt = imgfmt2pixfmt(u.selected_sw_imgfmt); - const char *avfmt_name = av_get_pix_fmt_name(pixfmt); - char *args[] = {"format", (char *)avfmt_name, NULL}; - MP_VERBOSE(f, "Hardware conversion: %s -> %s\n", - p->conversion_filter_name, avfmt_name); + struct mp_conversion_filter *desc = p->get_conversion_filter(u.selected_sw_imgfmt); + if (!desc) + goto fail; + MP_VERBOSE(f, "Hardware conversion: %s (%s -> %s)\n", + desc->name, + mp_imgfmt_to_name(sw_imgfmt), + mp_imgfmt_to_name(u.selected_sw_imgfmt)); struct mp_filter *sv = mp_create_user_filter(parent, MP_OUTPUT_CHAIN_VIDEO, - p->conversion_filter_name, args); + desc->name, desc->args); + talloc_free(desc); u.f = sv; talloc_free(f); } } } else { u.f = f; - /* - * In the case where the imgfmt is not natively supported, it must be - * converted, either before or during upload. If the imgfmt is supported - * as a hw input format, then prefer that, and if the upload has to do - * implicit conversion, that's fine. On the other hand, if the imgfmt is - * not a supported input format, then pick the output format as the - * conversion target to avoid doing two conversions (one before upload, - * and one during upload). Note that for most hardware types, there is - * no ability to convert during upload, and the two formats will always - * be the same. - */ - u.selected_sw_imgfmt = - sw_imgfmt == hw_input_fmt ? hw_input_fmt : hw_output_fmt; + u.selected_sw_imgfmt = hw_input_fmt; } u.successful_init = true; diff --git a/meson.build b/meson.build index 2e0b705ae29d5..1b56eb4713aed 100644 --- a/meson.build +++ b/meson.build @@ -1540,6 +1540,10 @@ swift = get_option('swift-build').require( ) features += {'swift': swift.allowed()} +if features['cocoa'] and not features['swift'] and get_option('cplayer') + error('cplayer enabled but no suitable swift version could be found or was disabled!') +endif + swift_sources = [] if features['cocoa'] and features['swift'] swift_sources += files('osdep/mac/application.swift', diff --git a/misc/bstr.c b/misc/bstr.c index 120abef8fe16b..0b82244bcaf5f 100644 --- a/misc/bstr.c +++ b/misc/bstr.c @@ -361,11 +361,6 @@ static void resize_append(void *talloc_ctx, bstr *s, size_t append_min) } } -// Append the string, so that *s = *s + append. s->start is expected to be -// a talloc allocation (which can be realloced) or NULL. -// This function will always implicitly append a \0 after the new string for -// convenience. -// talloc_ctx will be used as parent context, if s->start is NULL. void bstr_xappend(void *talloc_ctx, bstr *s, bstr append) { if (!append.len) @@ -385,7 +380,6 @@ int bstr_xappend_asprintf(void *talloc_ctx, bstr *s, const char *fmt, ...) return ret; } -// Exactly as bstr_xappend(), but with a formatted string. int bstr_xappend_vasprintf(void *talloc_ctx, bstr *s, const char *fmt, va_list ap) { diff --git a/misc/bstr.h b/misc/bstr.h index d279a3a088526..a586ef3442c5d 100644 --- a/misc/bstr.h +++ b/misc/bstr.h @@ -135,9 +135,52 @@ static inline struct bstr bstr_getline(struct bstr str, struct bstr *rest) // and will remove the trailing \n or \r\n sequence. struct bstr bstr_strip_linebreaks(struct bstr str); +/** + * @brief Append a string to the existing bstr. + * + * This function appends the content of the `append` bstr to the `s` bstr. + * `s->start` is expected to be a talloc allocation (which can be resized) or NULL. + * A null terminator ('\0') is always appended for convenience. If `s->start` + * is NULL, the `talloc_ctx` will be used as the parent context to allocate + * memory. + * + * @param talloc_ctx The parent talloc context. + * @param s The destination bstr to which the `append` string is appended. + * @param append The string to append to `s`. + */ void bstr_xappend(void *talloc_ctx, bstr *s, bstr append); + +/** + * @brief Append a formatted string to the existing bstr. + * + * This function works like bstr_xappend() but appends a formatted string using + * a format string and additional arguments. The formatted string is created + * using vsnprintf. The function takes care of resizing the destination + * buffer if necessary. + * + * @param talloc_ctx The parent talloc context. + * @param s The destination bstr to which the formatted string is appended. + * @param fmt The format string (same as in vsnprintf). + * @param ... Additional arguments for the format string. + * @return The number of characters added (excluding the null terminator) + * or a negative value on error. + */ int bstr_xappend_asprintf(void *talloc_ctx, bstr *s, const char *fmt, ...) PRINTF_ATTRIBUTE(3, 4); + +/** + * @brief Append a formatted string to the existing bstr using a va_list. + * + * This function is identical to bstr_xappend_asprintf() but takes a `va_list` + * instead of a variable number of arguments. + * + * @param talloc_ctx The parent talloc context. + * @param s The destination bstr to which the formatted string is appended. + * @param fmt The format string (same as in printf). + * @param ap The `va_list` containing the arguments for the format string. + * @return The number of characters added (excluding the null terminator) + * or a negative value on error. + */ int bstr_xappend_vasprintf(void *talloc_ctx, bstr *s, const char *fmt, va_list va) PRINTF_ATTRIBUTE(3, 0); diff --git a/options/m_option.c b/options/m_option.c index a6f8bcd45c256..51e08c21e8652 100644 --- a/options/m_option.c +++ b/options/m_option.c @@ -2219,10 +2219,6 @@ void m_geometry_apply(int *xpos, int *ypos, int *widw, int *widh, } else if (!(gm->w > 0) && gm->h > 0) { *widw = *widh * asp; } - // Center window after resize. If valid x:y values are passed to - // geometry, then those values will be overridden. - *xpos += prew / 2 - *widw / 2; - *ypos += preh / 2 - *widh / 2; } if (gm->xy_valid) { diff --git a/osdep/mac/app_bridge.m b/osdep/mac/app_bridge.m index bea1ecae3edbe..ead9603c9d62c 100644 --- a/osdep/mac/app_bridge.m +++ b/osdep/mac/app_bridge.m @@ -110,6 +110,7 @@ void app_bridge_tarray_append(void *t, char ***a, int *i, char *s) return &vo_sub_opts; } +#if HAVE_SWIFT void cocoa_init_media_keys(void) { [[AppHub shared] startRemote]; @@ -139,4 +140,4 @@ int cocoa_main(int argc, char *argv[]) { return [(Application *)[Application sharedApplication] main:argc :argv]; } - +#endif diff --git a/osdep/mac/swift_extensions.swift b/osdep/mac/swift_extensions.swift index e49806aaa0d15..8f3de98802b2a 100644 --- a/osdep/mac/swift_extensions.swift +++ b/osdep/mac/swift_extensions.swift @@ -73,7 +73,7 @@ extension mp_keymap { extension mpv_event_id: CustomStringConvertible { public var description: String { switch self { - case MPV_EVENT_NONE: return "MPV_EVENT_NONE2" + case MPV_EVENT_NONE: return "MPV_EVENT_NONE" case MPV_EVENT_SHUTDOWN: return "MPV_EVENT_SHUTDOWN" case MPV_EVENT_LOG_MESSAGE: return "MPV_EVENT_LOG_MESSAGE" case MPV_EVENT_GET_PROPERTY_REPLY: return "MPV_EVENT_GET_PROPERTY_REPLY" diff --git a/player/javascript/defaults.js b/player/javascript/defaults.js index e49bc80614d22..247c88ad993db 100644 --- a/player/javascript/defaults.js +++ b/player/javascript/defaults.js @@ -670,25 +670,10 @@ function register_event_handler(t) { mp.input = { get: function(t) { mp.commandv("script-message-to", "console", "get-input", mp.script_name, - JSON.stringify({ - prompt: t.prompt, - default_text: t.default_text, - cursor_position: t.cursor_position, - id: t.id, - })); + JSON.stringify(t)); register_event_handler(t) }, - select: function (t) { - mp.commandv("script-message-to", "console", "get-input", mp.script_name, - JSON.stringify({ - prompt: t.prompt, - items: t.items, - default_item: t.default_item, - })); - - register_event_handler(t); - }, terminate: function () { mp.commandv("script-message-to", "console", "disable"); }, @@ -708,6 +693,7 @@ mp.input = { JSON.stringify(log)); } } +mp.input.select = mp.input.get /********************************************************************** * various diff --git a/player/lua/console.lua b/player/lua/console.lua index b232b878c41d4..bfd19da0e5583 100644 --- a/player/lua/console.lua +++ b/player/lua/console.lua @@ -93,6 +93,7 @@ local history_pos = 1 local searching_history = false local log_buffers = {[id] = {}} local key_bindings = {} +local dont_bind_up_down = false local global_margins = { t = 0, b = 0 } local input_caller @@ -1624,10 +1625,12 @@ local function define_key_bindings() return end for _, bind in ipairs(get_bindings()) do - -- Generate arbitrary name for removing the bindings later. - local name = "_console_" .. (#key_bindings + 1) - key_bindings[#key_bindings + 1] = name - mp.add_forced_key_binding(bind[1], name, bind[2], {repeatable = true}) + if not (dont_bind_up_down and (bind[1] == 'up' or bind[1] == 'down')) then + -- Generate arbitrary name for removing the bindings later. + local name = "_console_" .. (#key_bindings + 1) + key_bindings[#key_bindings + 1] = name + mp.add_forced_key_binding(bind[1], name, bind[2], {repeatable = true}) + end end mp.add_forced_key_binding("any_unicode", "_console_text", text_input, {repeatable = true, complex = true}) @@ -1675,6 +1678,7 @@ set_active = function (active) line = '' cursor = 1 selectable_items = nil + dont_bind_up_down = false end collectgarbage() end @@ -1734,6 +1738,7 @@ mp.register_script_message('get-input', function (script_name, args) line = args.default_text or '' cursor = args.cursor_position or line:len() + 1 id = args.id or script_name .. prompt + dont_bind_up_down = args.dont_bind_up_down if histories[id] == nil then histories[id] = {} log_buffers[id] = {} diff --git a/player/lua/input.lua b/player/lua/input.lua index 3c1584b2efb52..aadab383ccab0 100644 --- a/player/lua/input.lua +++ b/player/lua/input.lua @@ -18,6 +18,18 @@ License along with mpv. If not, see . local utils = require "mp.utils" local input = {} +local function get_non_callbacks(t) + local non_callbacks = {} + + for key, value in pairs(t) do + if type(value) ~= "function" then + non_callbacks[key] = value + end + end + + return non_callbacks +end + local function register_event_handler(t) mp.register_script_message("input-event", function (type, args) if t[type] then @@ -38,26 +50,12 @@ end function input.get(t) mp.commandv("script-message-to", "console", "get-input", - mp.get_script_name(), utils.format_json({ - prompt = t.prompt, - default_text = t.default_text, - cursor_position = t.cursor_position, - id = t.id, - })) + mp.get_script_name(), utils.format_json(get_non_callbacks(t))) register_event_handler(t) end -function input.select(t) - mp.commandv("script-message-to", "console", "get-input", - mp.get_script_name(), utils.format_json({ - prompt = t.prompt, - items = t.items, - default_item = t.default_item, - })) - - register_event_handler(t) -end +input.select = input.get function input.terminate() mp.commandv("script-message-to", "console", "disable") diff --git a/player/lua/stats.lua b/player/lua/stats.lua index e8ba263b55406..bee666467a492 100644 --- a/player/lua/stats.lua +++ b/player/lua/stats.lua @@ -474,7 +474,7 @@ local function get_kbinfo_lines() and bind.section ~= "input_forced_console" and ( searched_text == nil or - (bind.key .. bind.cmd):lower():find(searched_text, 1, true) + (bind.key .. bind.cmd .. (bind.comment or "")):lower():find(searched_text, 1, true) ) then active[bind.key] = bind @@ -1619,6 +1619,7 @@ local function filter_bindings() end end end, + dont_bind_up_down = true, }) end @@ -1749,14 +1750,20 @@ mp.add_key_binding(nil, "display-stats", function() process_key_binding(true) en mp.add_key_binding(nil, "display-stats-toggle", function() process_key_binding(false) end, {repeatable=false}) --- Single invocation bindings without key, can be used in input.conf to create --- bindings for a specific page: "e script-binding stats/display-page-2" for k, _ in pairs(pages) do - mp.add_key_binding(nil, "display-page-" .. k, - function() - curr_page = k - process_key_binding(true) - end, {repeatable=true}) + -- Single invocation key bindings for specific pages, e.g.: + -- "e script-binding stats/display-page-2" + mp.add_key_binding(nil, "display-page-" .. k, function() + curr_page = k + process_key_binding(true) + end, {repeatable=true}) + + -- Key bindings to toggle a specific page, e.g.: + -- "h script-binding stats/display-page-4-toggle". + mp.add_key_binding(nil, "display-page-" .. k .. "-toggle", function() + curr_page = k + process_key_binding(false) + end, {repeatable=true}) end -- Reprint stats immediately when VO was reconfigured, only when toggled diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000000000..d370f5cd4a1fb --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,14 @@ +[tool.ruff] +line-length = 100 + +[tool.ruff.lint] +select = [ + "F", # pyflakes + "E", "W", # pycodestyle + "I", # pycodestyle + "N", # pep8-naming + "UP", # pyupgrade + "A", # flake8-builtins + "Q", # flake8-quotes + "COM", # flake8-commas +] diff --git a/video/filter/vf_format.c b/video/filter/vf_format.c index cf08297ee34a4..f73e926d99281 100644 --- a/video/filter/vf_format.c +++ b/video/filter/vf_format.c @@ -84,6 +84,11 @@ static void set_params(struct vf_format_opts *p, struct mp_image_params *out, out->light = MP_CSP_LIGHT_AUTO; } } + if (p->colormatrix != PL_COLOR_SYSTEM_DOLBYVISION && !p->dovi) { + out->primaries_orig = out->color.primaries; + out->transfer_orig = out->color.transfer; + out->sys_orig = out->repr.sys; + } if (p->sig_peak) out->color.hdr = (struct pl_hdr_metadata){ .max_luma = p->sig_peak * MP_REF_WHITE }; if (p->light) diff --git a/video/hwdec.h b/video/hwdec.h index 8027b72e8edce..3f997eacc7fe5 100644 --- a/video/hwdec.h +++ b/video/hwdec.h @@ -8,6 +8,14 @@ struct mp_image_pool; +struct mp_conversion_filter { + // Name of the conversion filter. + const char *name; + + // Arguments for the conversion filter. + char **args; +}; + struct mp_hwdec_ctx { const char *driver_name; // NULL if unknown/not loaded @@ -24,10 +32,11 @@ struct mp_hwdec_ctx { // If NULL, all possible hwuploads are assumed to be supported. const int *supported_hwupload_formats; - // The name of this hwdec's matching conversion filter if available. + // Getter for conversion filter description, or NULL. // This will be used for hardware conversion of frame formats. - // NULL otherwise. - const char *conversion_filter_name; + // If available the talloc allocated mp_conversion_filter is returned, + // Caller is responsible to free the allocation. + struct mp_conversion_filter *(*get_conversion_filter)(int imgfmt); // The libavutil hwconfig to be used when querying constraints for the // conversion filter. Can be NULL if no special config is required. diff --git a/video/mp_image.c b/video/mp_image.c index b809dae765407..d7cc49de0b617 100644 --- a/video/mp_image.c +++ b/video/mp_image.c @@ -519,6 +519,9 @@ void mp_image_copy_attributes(struct mp_image *dst, struct mp_image *src) dst->params.chroma_location = src->params.chroma_location; dst->params.crop = src->params.crop; dst->nominal_fps = src->nominal_fps; + dst->params.primaries_orig = dst->params.color.primaries; + dst->params.transfer_orig = dst->params.color.transfer; + dst->params.sys_orig = dst->params.repr.sys; // ensure colorspace consistency enum pl_color_system dst_forced_csp = mp_image_params_get_forced_csp(&dst->params); @@ -852,6 +855,8 @@ bool mp_image_params_static_equal(const struct mp_image_params *p1, // before dovi mapping. void mp_image_params_restore_dovi_mapping(struct mp_image_params *params) { + if (params->repr.sys != PL_COLOR_SYSTEM_DOLBYVISION) + return; params->color.primaries = params->primaries_orig; params->color.transfer = params->transfer_orig; params->repr.sys = params->sys_orig; @@ -1198,13 +1203,16 @@ struct AVFrame *mp_image_to_av_frame(struct mp_image *src) if (src->fields & MP_IMGFIELD_REPEAT_FIRST) dst->repeat_pict = 1; - pl_avframe_set_repr(dst, src->params.repr); + // Image params without dovi mapped; should be passed as side data instead + struct mp_image_params params = src->params; + mp_image_params_restore_dovi_mapping(¶ms); + pl_avframe_set_repr(dst, params.repr); - dst->chroma_location = pl_chroma_to_av(src->params.chroma_location); + dst->chroma_location = pl_chroma_to_av(params.chroma_location); dst->opaque_ref = av_buffer_alloc(sizeof(struct mp_image_params)); MP_HANDLE_OOM(dst->opaque_ref); - *(struct mp_image_params *)dst->opaque_ref->data = src->params; + *(struct mp_image_params *)dst->opaque_ref->data = params; if (src->icc_profile) { AVFrameSideData *sd = @@ -1214,14 +1222,14 @@ struct AVFrame *mp_image_to_av_frame(struct mp_image *src) new_ref->icc_profile = NULL; } - pl_avframe_set_color(dst, src->params.color); + pl_avframe_set_color(dst, params.color); { AVFrameSideData *sd = av_frame_new_side_data(dst, AV_FRAME_DATA_DISPLAYMATRIX, sizeof(int32_t) * 9); MP_HANDLE_OOM(sd); - av_display_rotation_set((int32_t *)sd->data, src->params.rotate); + av_display_rotation_set((int32_t *)sd->data, params.rotate); } // Add back side data, but only for types which are not specially handled diff --git a/video/out/cocoa_cb_common.swift b/video/out/cocoa_cb_common.swift index 4b4250c565ce5..38540a2040351 100644 --- a/video/out/cocoa_cb_common.swift +++ b/video/out/cocoa_cb_common.swift @@ -92,7 +92,7 @@ class CocoaCB: Common, EventSubscriber { return } - let wr = getWindowGeometry(forScreen: targetScreen, videoOut: vo) + let (wr, _) = getWindowGeometry(forScreen: targetScreen, videoOut: vo) if !(window?.isVisible ?? false) && !(window?.isMiniaturized ?? false) && !NSApp.isHidden { window?.makeKeyAndOrderFront(nil) } diff --git a/video/out/gpu/d3d11_helpers.c b/video/out/gpu/d3d11_helpers.c index 358a7deb8b83f..f4f2e534951f5 100644 --- a/video/out/gpu/d3d11_helpers.c +++ b/video/out/gpu/d3d11_helpers.c @@ -285,8 +285,6 @@ static bool query_output_format_and_colorspace(struct mp_log *log, DXGI_FORMAT *out_fmt, DXGI_COLOR_SPACE_TYPE *out_cspace) { - IDXGIOutput *output = NULL; - IDXGIOutput6 *output6 = NULL; DXGI_OUTPUT_DESC1 desc = { 0 }; char *monitor_name = NULL; bool success = false; @@ -322,8 +320,6 @@ static bool query_output_format_and_colorspace(struct mp_log *log, done: talloc_free(monitor_name); - SAFE_RELEASE(output6); - SAFE_RELEASE(output); return success; } diff --git a/video/out/hwdec/hwdec_vaapi.c b/video/out/hwdec/hwdec_vaapi.c index 63c441464c82e..e8d4f159db28c 100644 --- a/video/out/hwdec/hwdec_vaapi.c +++ b/video/out/hwdec/hwdec_vaapi.c @@ -21,6 +21,7 @@ #include #include +#include #include #include "config.h" @@ -138,6 +139,30 @@ static const dmabuf_interop_init interop_inits[] = { NULL }; +static struct mp_conversion_filter *get_conversion_filter_desc(int target_imgfmt) +{ + const AVPixFmtDescriptor *pixfmt_desc = av_pix_fmt_desc_get(imgfmt2pixfmt(target_imgfmt)); + if (!pixfmt_desc) + return NULL; + + bool rgb = pixfmt_desc->flags & AV_PIX_FMT_FLAG_RGB; + + struct mp_conversion_filter *desc = talloc_ptrtype(NULL, desc); + desc->name = "scale_vaapi"; + desc->args = talloc_array_ptrtype(desc, desc->args, rgb ? 5 : 3); + + int i = 0; + desc->args[i++] = "format"; + desc->args[i++] = (char *)pixfmt_desc->name; + if (rgb) { + desc->args[i++] = "out_range"; + desc->args[i++] = "full"; + } + desc->args[i++] = NULL; + + return desc; +} + static int init(struct ra_hwdec *hw) { struct priv_owner *p = hw->priv; @@ -195,7 +220,7 @@ static int init(struct ra_hwdec *hw) p->ctx->hwctx.supported_formats = p->formats; p->ctx->hwctx.supported_hwupload_formats = p->hwupload_formats; p->ctx->hwctx.driver_name = hw->driver->name; - p->ctx->hwctx.conversion_filter_name = "scale_vaapi"; + p->ctx->hwctx.get_conversion_filter = get_conversion_filter_desc; p->ctx->hwctx.conversion_config = hwconfig; hwdec_devices_add(hw->devs, &p->ctx->hwctx); return 0; diff --git a/video/out/mac/common.swift b/video/out/mac/common.swift index 9fd7dbc9ea5f9..7e3af8bd46385 100644 --- a/video/out/mac/common.swift +++ b/video/out/mac/common.swift @@ -75,7 +75,7 @@ class Common: NSObject { } func initWindow(_ vo: UnsafeMutablePointer, _ previousActiveApp: NSRunningApplication?) { - let (targetScreen, wr) = getInitProperties(vo) + let (targetScreen, wr, _) = getInitProperties(vo) guard let view = self.view else { log.error("Something went wrong, no View was initialized") @@ -120,7 +120,7 @@ class Common: NSObject { } func initView(_ vo: UnsafeMutablePointer, _ layer: CALayer) { - let (_, wr) = getInitProperties(vo) + let (_, wr, _) = getInitProperties(vo) view = View(frame: wr, common: self) guard let view = self.view else { @@ -408,7 +408,7 @@ class Common: NSObject { } func getWindowGeometry(forScreen screen: NSScreen, - videoOut vo: UnsafeMutablePointer) -> NSRect { + videoOut vo: UnsafeMutablePointer) -> (NSRect, Bool) { let r = screen.convertRectToBacking(screen.frame) let targetFrame = option.mac.macos_geometry_calculation == FRAME_VISIBLE ? screen.visibleFrame : screen.frame @@ -426,7 +426,7 @@ class Common: NSObject { y1: Int32(originY + rv.size.height)) var geo: vo_win_geometry = vo_win_geometry() - vo_calc_window_geometry2(vo, &screenRC, Double(screen.backingScaleFactor), &geo) + vo_calc_window_geometry(vo, &screenRC, &screenRC, Double(screen.backingScaleFactor), false, &geo) vo_apply_window_geometry(vo, &geo) let height = CGFloat(geo.win.y1 - geo.win.y0) @@ -434,18 +434,19 @@ class Common: NSObject { // flip the y origin again let y = CGFloat(-geo.win.y1) let x = CGFloat(geo.win.x0) - return screen.convertRectFromBacking(NSRect(x: x, y: y, width: width, height: height)) + let wr = screen.convertRectFromBacking(NSRect(x: x, y: y, width: width, height: height)) + return (wr, Bool(geo.flags & Int32(VO_WIN_FORCE_POS))) } - func getInitProperties(_ vo: UnsafeMutablePointer) -> (NSScreen, NSRect) { + func getInitProperties(_ vo: UnsafeMutablePointer) -> (NSScreen, NSRect, Bool) { guard let targetScreen = getTargetScreen(forFullscreen: false) ?? NSScreen.main else { log.error("Something went wrong, no Screen was found") exit(1) } - let wr = getWindowGeometry(forScreen: targetScreen, videoOut: vo) + let (wr, forcePosition) = getWindowGeometry(forScreen: targetScreen, videoOut: vo) - return (targetScreen, wr) + return (targetScreen, wr, forcePosition) } // call before initApp, because on macOS +10.15 it changes the active App @@ -535,8 +536,8 @@ class Common: NSObject { TypeHelper.toPointer(&option.voPtr.pointee.autofit_smaller), TypeHelper.toPointer(&option.voPtr.pointee.autofit_larger): DispatchQueue.main.async { - let (_, wr) = self.getInitProperties(vo) - self.window?.updateFrame(wr) + let (_, wr, forcePosition) = self.getInitProperties(vo) + forcePosition ? self.window?.updateFrame(wr) : self.window?.updateSize(wr.size) } default: break diff --git a/video/out/mac_common.swift b/video/out/mac_common.swift index 3184e31c38422..d853c9c04a732 100644 --- a/video/out/mac_common.swift +++ b/video/out/mac_common.swift @@ -47,7 +47,7 @@ class MacCommon: Common { let previousActiveApp = getActiveApp() initApp() - let (_, wr) = getInitProperties(vo) + let (_, wr, _) = getInitProperties(vo) guard let layer = self.layer else { log.error("Something went wrong, no MetalLayer was initialized") @@ -60,7 +60,7 @@ class MacCommon: Common { initWindowState() } - if (window?.unfsContentFramePixel.size ?? NSSize.zero) != wr.size && option.vo.auto_window_resize { + if option.vo.auto_window_resize { window?.updateSize(wr.size) } diff --git a/video/out/vo_sdl.c b/video/out/vo_sdl.c index fb8cc331b66a0..cce07d3d7dd87 100644 --- a/video/out/vo_sdl.c +++ b/video/out/vo_sdl.c @@ -453,7 +453,7 @@ static int reconfig(struct vo *vo, struct mp_image_params *params) struct mp_rect screenrc; update_screeninfo(vo, &screenrc); - vo_calc_window_geometry(vo, &screenrc, &geo); + vo_calc_window_geometry(vo, &screenrc, &screenrc, 1.0, false, &geo); vo_apply_window_geometry(vo, &geo); int win_w = vo->dwidth; diff --git a/video/out/w32_common.c b/video/out/w32_common.c index 6ebd6076e86b5..691ef3d57c18f 100644 --- a/video/out/w32_common.c +++ b/video/out/w32_common.c @@ -1069,7 +1069,7 @@ static void update_fullscreen_state(struct vo_w32_state *w32) m_config_cache_write_opt(w32->opts_cache, &w32->opts->fullscreen); - if (toggle_fs) { + if (toggle_fs && (!w32->opts->window_maximized || w32->unmaximize)) { if (w32->current_fs) { // Save window rect when switching to fullscreen. w32->prev_windowrc = w32->windowrc; @@ -1104,6 +1104,8 @@ static void update_minimized_state(struct vo_w32_state *w32) } } +static void update_window_state(struct vo_w32_state *w32); + static void update_maximized_state(struct vo_w32_state *w32, bool leaving_fullscreen) { if (w32->parent) @@ -1115,6 +1117,8 @@ static void update_maximized_state(struct vo_w32_state *w32, bool leaving_fullsc if (w32->current_fs && !leaving_fullscreen) return; + bool toggle = !w32->opts->window_maximized && IsMaximized(w32->window); + WINDOWPLACEMENT wp = { .length = sizeof wp }; GetWindowPlacement(w32->window, &wp); @@ -1134,6 +1138,11 @@ static void update_maximized_state(struct vo_w32_state *w32, bool leaving_fullsc ShowWindow(w32->window, SW_SHOWNOACTIVATE); } } + + if (toggle && !w32->current_fs) { + w32->windowrc = w32->prev_windowrc; + update_window_state(w32); + } } static bool is_visible(HWND window) @@ -1168,7 +1177,7 @@ static void update_window_state(struct vo_w32_state *w32) // doesn't change the window maximized state. // ShowWindow(SW_SHOWNOACTIVATE) can't be used here because it tries to // "restore" the window to its size before it's maximized. - if (w32->unmaximize) { + if (w32->unmaximize && !w32->current_fs) { WINDOWPLACEMENT wp = { .length = sizeof wp }; GetWindowPlacement(w32->window, &wp); wp.showCmd = SW_SHOWNOACTIVATE; @@ -1920,7 +1929,7 @@ static void window_reconfig(struct vo_w32_state *w32, bool force) if (w32->dpi_scale == 0) force_update_display_info(w32); - vo_calc_window_geometry3(vo, &screen, &mon, w32->dpi_scale, &geo); + vo_calc_window_geometry(vo, &screen, &mon, w32->dpi_scale, false, &geo); vo_apply_window_geometry(vo, &geo); bool reset_size = ((w32->o_dwidth != vo->dwidth || @@ -1950,7 +1959,7 @@ static void window_reconfig(struct vo_w32_state *w32, bool force) vo->dwidth = r.right; vo->dheight = r.bottom; } else { - if (w32->current_fs) + if (w32->current_fs || w32->opts->window_maximized) rc = &w32->prev_windowrc; w32->fit_on_screen = true; } @@ -2268,7 +2277,8 @@ static int gui_thread_control(struct vo_w32_state *w32, int request, void *arg) if (!w32->window_bounds_initialized) return VO_FALSE; - RECT *rc = w32->current_fs ? &w32->prev_windowrc : &w32->windowrc; + RECT *rc = (w32->current_fs || w32->opts->window_maximized) + ? &w32->prev_windowrc : &w32->windowrc; s[0] = rect_w(*rc); s[1] = rect_h(*rc); return VO_TRUE; @@ -2282,7 +2292,7 @@ static int gui_thread_control(struct vo_w32_state *w32, int request, void *arg) RECT *rc = w32->current_fs ? &w32->prev_windowrc : &w32->windowrc; resize_and_move_rect(w32, rc, s[0], s[1]); - if (w32->opts->window_maximized) { + if (w32->opts->window_maximized && !w32->current_fs) { w32->unmaximize = true; } w32->fit_on_screen = true; diff --git a/video/out/wayland_common.c b/video/out/wayland_common.c index 4406d53d38ae3..e95730596c9c1 100644 --- a/video/out/wayland_common.c +++ b/video/out/wayland_common.c @@ -2320,7 +2320,7 @@ static void set_geometry(struct vo_wayland_state *wl, bool resize) struct vo_win_geometry geo; struct mp_rect screenrc = wl->current_output->geometry; - vo_calc_window_geometry2(vo, &screenrc, wl->scaling_factor, &geo); + vo_calc_window_geometry(vo, &screenrc, &screenrc, wl->scaling_factor, false, &geo); vo_apply_window_geometry(vo, &geo); int gcd = greatest_common_divisor(vo->dwidth, vo->dheight); diff --git a/video/out/win_state.c b/video/out/win_state.c index b4bc9fdb7b33d..435c9a090f0b6 100644 --- a/video/out/win_state.c +++ b/video/out/win_state.c @@ -69,17 +69,22 @@ static void apply_autofit(int *w, int *h, int scr_w, int scr_h, // Does not change *vo. // screen: position of the area on virtual desktop on which the video-content // should be placed (maybe after excluding decorations, taskbars, etc) +// can be the same as monitor for platforms that don't try to take into +// account decorations, taskbars, etc. // monitor: position of the monitor on virtual desktop (used for pixelaspect). // dpi_scale: the DPI multiplier to get from virtual to real coordinates // (>1 for "hidpi") +// force_center: force centering x/y in the middle of the given screen even if +// opts->force_window_position is not set. ignored if the user +// supplies valid x/y coordinates on their own. // Use vo_apply_window_geometry() to copy the result into the vo. // NOTE: currently, all windowing backends do their own handling of window // geometry additional to this code. This is to deal with initial window // placement, fullscreen handling, avoiding resize on reconfig() with no // size change, multi-monitor stuff, and possibly more. -void vo_calc_window_geometry3(struct vo *vo, const struct mp_rect *screen, - const struct mp_rect *monitor, - double dpi_scale, struct vo_win_geometry *out_geo) +void vo_calc_window_geometry(struct vo *vo, const struct mp_rect *screen, + const struct mp_rect *monitor, double dpi_scale, + bool force_center, struct vo_win_geometry *out_geo) { struct mp_vo_opts *opts = vo->opts; @@ -119,31 +124,27 @@ void vo_calc_window_geometry3(struct vo *vo, const struct mp_rect *screen, out_geo->win.x0 = (int)(scr_w - d_w) / 2; out_geo->win.y0 = (int)(scr_h - d_h) / 2; + + int old_w = d_w; + int old_h = d_h; + m_geometry_apply(&out_geo->win.x0, &out_geo->win.y0, &d_w, &d_h, scr_w, scr_h, &opts->geometry); + if ((opts->force_window_position || force_center) && !opts->geometry.xy_valid) { + out_geo->win.x0 += old_w / 2 - d_w / 2; + out_geo->win.y0 += old_h / 2 - d_h / 2; + } + out_geo->win.x0 += screen->x0; out_geo->win.y0 += screen->y0; out_geo->win.x1 = out_geo->win.x0 + d_w; out_geo->win.y1 = out_geo->win.y0 + d_h; - if (opts->geometry.xy_valid || opts->force_window_position) + if (opts->geometry.xy_valid || opts->force_window_position || force_center) out_geo->flags |= VO_WIN_FORCE_POS; } -// same as vo_calc_window_geometry3 with monitor assumed same as screen -void vo_calc_window_geometry2(struct vo *vo, const struct mp_rect *screen, - double dpi_scale, struct vo_win_geometry *out_geo) -{ - vo_calc_window_geometry3(vo, screen, screen, dpi_scale, out_geo); -} - -void vo_calc_window_geometry(struct vo *vo, const struct mp_rect *screen, - struct vo_win_geometry *out_geo) -{ - vo_calc_window_geometry2(vo, screen, 1.0, out_geo); -} - // Copy the parameters in *geo to the vo fields. // (Doesn't do anything else - windowing backends should trigger VO_EVENT_RESIZE // to ensure that the VO reinitializes rendering properly.) diff --git a/video/out/win_state.h b/video/out/win_state.h index a253efa7584d4..f19a15618bae7 100644 --- a/video/out/win_state.h +++ b/video/out/win_state.h @@ -24,12 +24,8 @@ struct vo_win_geometry { }; void vo_calc_window_geometry(struct vo *vo, const struct mp_rect *screen, - struct vo_win_geometry *out_geo); -void vo_calc_window_geometry2(struct vo *vo, const struct mp_rect *screen, - double dpi_scale, struct vo_win_geometry *out_geo); -void vo_calc_window_geometry3(struct vo *vo, const struct mp_rect *screen, - const struct mp_rect *monitor, - double dpi_scale, struct vo_win_geometry *out_geo); + const struct mp_rect *monitor, double dpi_scale, + bool force_center, struct vo_win_geometry *out_geo); void vo_apply_window_geometry(struct vo *vo, const struct vo_win_geometry *geo); #endif diff --git a/video/out/x11_common.c b/video/out/x11_common.c index ae1ea5bf465d5..6d7871fac5c33 100644 --- a/video/out/x11_common.c +++ b/video/out/x11_common.c @@ -1809,7 +1809,8 @@ void vo_x11_config_vo_window(struct vo *vo) vo_x11_update_screeninfo(vo); struct vo_win_geometry geo; - vo_calc_window_geometry2(vo, &x11->screenrc, x11->dpi_scale, &geo); + vo_calc_window_geometry(vo, &x11->screenrc, &x11->screenrc, x11->dpi_scale, + !x11->pseudo_mapped, &geo); vo_apply_window_geometry(vo, &geo); struct mp_rect rc = geo.win; @@ -1833,7 +1834,7 @@ void vo_x11_config_vo_window(struct vo *vo) x11->nofsrc = rc; vo_x11_map_window(vo, rc); } else if (reset_size) { - vo_x11_highlevel_resize(vo, rc, x11->geometry_change); + vo_x11_highlevel_resize(vo, rc, geo.flags & VO_WIN_FORCE_POS); } x11->geometry_change = false;