v20260405
- 架构总览
- 菜单系统 1:
mp.input.select— 列表选择器 - 菜单系统 2:
context_menu.lua— OSD 右键菜单 - 菜单系统 3:Windows 原生右键菜单
- 菜单系统 4:macOS 原生菜单栏
- 四种菜单系统对比
- 完全禁用内置菜单
- 附录:mpv-menu-plugin 与内置菜单的关系
- 附录:uosc菜单
mpv 的菜单系统分为 数据层 和 渲染层 两个层级,共包含 4 种菜单/选择界面:
┌─────────────────────────────────────────────────────────────────┐
│ 数据层 │
│ │
│ menu.conf ──→ select.lua ──→ menu-data 属性 │
│ │ │
│ ├──→ mp.input.select() ──→ console.lua (OSD) │
│ │ (列表选择器) │
│ │ │
│ └──→ context-menu 命令 │
│ │ │
│ ┌───────────────────────────┼────────────────────────┐ │
│ │ 渲染层 (平台分发) │ │
│ │ │ │ │
│ │ ┌──────────────────────┼─────────────────┐ │ │
│ │ │ Windows │ macOS │其他 │ │
│ │ │ HMENU 原生菜单 │ NSMenu 菜单栏 │ │ │
│ │ │ (win32/menu.c) │ (menu_bar.swift)│ │ │
│ │ └──────────────────────┘─────────────────┘ │ │
│ │ │ │ │
│ │ context_menu.lua │ │
│ │ (ASS OSD 菜单) │ │
│ └────────────────────────────────────────────────────┘ │
└───────────────────────────────────────────────────────────────────┘
- 类型:OSD 列表选择界面(基于 console.lua 渲染)
- 源文件:
player/lua/input.lua+player/lua/console.lua - 外观:带搜索输入框的可滚动列表,支持模糊搜索、键盘/鼠标导航
- 调用者:
select.lua中的g-*系列快捷键(如g-p选播放列表、g-t选轨道) - 禁用选项:
--load-console=no(注意:这会同时禁用整个控制台)
| 类型 | 路径 / 链接 |
|---|---|
| Lua API 文档 | DOCS/man/lua.rst → "mp.input functions" 章节 |
| select 脚本文档 | DOCS/man/select.rst |
| console 脚本文档 | DOCS/man/console.rst |
| 选项文档 | DOCS/man/options.rst → --load-select / --load-console |
| API 实现 | player/lua/input.lua |
| 渲染实现 | player/lua/console.lua |
| 选择器客户端 | player/lua/select.lua |
| 脚本加载入口 | player/scripting.c → mp_load_builtin_scripts() |
| 在线文档 | https://mpv.io/manual/master/#select |
| 在线文档 | https://mpv.io/manual/master/#console |
| 在线文档 | https://mpv.io/manual/master/#mp-input-functions |
快捷键:
| 快捷键 | 功能 | 激活指令 |
|---|---|---|
g-p |
选择播放列表条目 | script-binding select/select-playlist |
g-s |
选择字幕轨道 | script-binding select/select-sid |
g-S |
选择副字幕轨道 | script-binding select/select-secondary-sid |
g-a |
选择音频轨道 | script-binding select/select-aid |
g-v |
选择视频轨道 | script-binding select/select-vid |
g-t |
选择所有轨道 | script-binding select/select-track |
g-c |
选择章节 | script-binding select/select-chapter |
g-e |
选择版本 | script-binding select/select-edition |
g-l |
选择字幕行 | script-binding select/select-subtitle-line |
g-d |
选择音频设备 | script-binding select/select-audio-device |
g-h |
浏览观看历史 | script-binding select/select-watch-history |
g-w |
浏览稍后观看 | script-binding select/select-watch-later |
g-b |
选择按键绑定 | script-binding select/select-binding |
g-r |
查看属性列表 | script-binding select/show-properties |
g-m / Ctrl+p |
打开综合菜单 | script-binding select/menu |
命令行激活示例:
# 在 input.conf 中绑定快捷键
g-p script-binding select/select-playlist
Ctrl+p script-binding select/menu
# 通过 mpv 命令行测试
# mpv --input-test --idle --force-window
# 然后在控制台输入:script-binding select/select-playlist脚本 API 调用:
local input = require "mp.input"
input.select({ items = {"A", "B"}, submit = function(i) end })mp.input.select() 是一个 Lua API,任何脚本都可以调用它来展示一个可过滤的选择列表。它向 console.lua 发送 get-input 消息,console.lua 在视频窗口上用 ASS overlay 渲染带搜索框的列表界面。
-- ~/.config/mpv/scripts/my-selector.lua
local input = require "mp.input"
mp.add_key_binding("Ctrl+e", "my-selector", function()
input.select({
prompt = "选择一个操作:",
items = {
"50% 速度",
"100% 速度",
"200% 速度",
"截图",
},
default_item = 2,
submit = function(index)
if index == 1 then mp.set_property_number("speed", 0.5)
elseif index == 2 then mp.set_property_number("speed", 1.0)
elseif index == 3 then mp.set_property_number("speed", 2.0)
elseif index == 4 then mp.commandv("screenshot")
end
end,
})
end)运行效果:按 Ctrl+e 弹出带模糊搜索的列表,可键入文字过滤,回车确认。
- 类型:ASS overlay 绘制的多级上下文菜单
- 源文件:
player/lua/context_menu.lua - 外观:传统右键菜单样式,支持子菜单悬停展开、圆角、阴影
- 触发方式:右键点击(通过
select.lua中的context-menu绑定分发) - 禁用选项:
--load-context-menu=no - 默认状态:在 非 Windows/macOS 平台默认启用;Windows/macOS 平台默认禁用(因为走原生菜单通道)
| 类型 | 路径 / 链接 |
|---|---|
| 专项文档 | DOCS/man/context_menu.rst |
| 选项文档 | DOCS/man/options.rst → --load-context-menu |
context-menu 命令 |
DOCS/man/input.rst → "context-menu" |
menu-data 属性 |
DOCS/man/input.rst → "menu-data" |
| 菜单渲染实现 | player/lua/context_menu.lua |
| 菜单数据解析 | player/lua/select.lua → parse_menu_conf() / on_idle() |
| 默认菜单定义 | etc/menu.conf |
| 内置样式覆盖 | etc/builtin.conf → context_menu-* |
context-menu 命令实现 |
player/command.c → cmd_context_menu() |
| 在线文档 | https://mpv.io/manual/master/#context-menu |
快捷键:
| 快捷键 | 激活指令 |
|---|---|
MENU(键盘菜单键) |
script-binding select/context-menu |
Shift+F10 |
script-binding select/context-menu |
命令行 / 脚本激活:
# 在 input.conf 中绑定右键弹出 OSD 菜单(非原生平台)
MBTN_RIGHT script-binding select/context-menu
MENU script-binding select/context-menu
# 或者直接绑定底层命令(跳过 select.lua 分发,强制走 OSD 菜单)
MBTN_RIGHT script-message-to context_menu open-- 脚本中直接激活
mp.commandv("script-message-to", "context_menu", "open")
-- 或通过 select.lua 分发(会根据平台自动选择原生/OSD)
mp.command("script-binding select/context-menu")- 用户右键点击 → 触发
context-menu按键绑定 select.lua检查load-context-menu选项- 如果为
yes:发送script-message-to context_menu open context_menu.lua读取menu-data属性,用 ASS 绘制多级菜单
menu.conf 是所有菜单(此菜单 + 原生菜单)的共享数据源,由 select.lua 解析后写入 menu-data 属性。
# 字段以 Tab 分隔
# 第一列:显示文本(& 标记下划线字母)
# 第二列:mpv 命令 或 特殊 token
# 第三列起:条件状态
Pla&y cycle pause hidden=not pause and not idle_active
Pa&use cycle pause hidden=idle_active or pause
&Playlist $playlist
Sp&eed
25% set speed 0.25 checked=speed == 0.25
50% set speed 0.50 checked=speed == 0.50
100% set speed 1 checked=speed == 1
200% set speed 2 checked=speed == 2
特殊 token(自动生成子菜单):$playlist、$tracks、$video-tracks、$audio-tracks、$sub-tracks、$secondary-sub-tracks、$chapters、$editions、$audio-devices、$profiles
在 script-opts/context_menu.conf 中:
font_size=14
gap=0.2
padding_x=8
padding_y=4
corner_radius=5
focused_color=#222222
focused_back_color=#FFFFFF
disabled_color=#555555
menu_outline_size=0
menu_outline_color=#FFFFFF
seconds_to_open_submenus=0.2
seconds_to_close_submenus=0.2select.lua 将 menu.conf 解析后写入 menu-data,格式为 mpv_node:
MPV_FORMAT_NODE_ARRAY -- 菜单项数组
└─ MPV_FORMAT_NODE_MAP -- 单个菜单项
├─ "type" : string -- "separator" | "submenu" | (省略=普通项)
├─ "title" : string -- 显示文本
├─ "cmd" : string -- 要执行的 mpv 命令
├─ "shortcut" : string -- 快捷键提示文本
├─ "state" : string[] -- ["checked", "disabled", "hidden"]
└─ "submenu" : array -- 子菜单(同结构递归)
通过脚本动态设置 menu-data 属性,即可自定义菜单内容:
-- ~/.config/mpv/scripts/my-context-menu.lua
-- 这个例子展示脚本如何直接操纵 menu-data 来填充右键菜单
local function set_my_menu()
local menu = {
{
title = "我的播放控制",
type = "submenu",
submenu = {
{ title = "播放/暂停", cmd = "cycle pause" },
{ title = "下一个文件", cmd = "playlist-next" },
{ type = "separator" },
{ title = "半速", cmd = "set speed 0.5" },
{ title = "原速", cmd = "set speed 1" },
{ title = "倍速", cmd = "set speed 2" },
},
},
{ type = "separator" },
{ title = "截图", cmd = "screenshot" },
{ title = "退出", cmd = "quit" },
}
mp.set_property_native("menu-data", menu)
end
mp.observe_property("vo-configured", "native", function(_, value)
if value then set_my_menu() end
end)- 类型:Win32 原生
HMENU弹出菜单 - 源文件:
video/out/win32/menu.c+video/out/w32_common.c - 外观:Windows 系统原生右键菜单,跟随系统主题
- 触发方式:
context-menu命令 →VOCTRL_SHOW_MENU→TrackPopupMenuEx() - 禁用方式:
--load-select=no(不填充 menu-data,菜单为空不会显示)
| 类型 | 路径 / 链接 |
|---|---|
| HMENU 构建与显示 | video/out/win32/menu.c |
| HMENU 头文件 | video/out/win32/menu.h |
| 窗口集成(事件处理) | video/out/w32_common.c → VOCTRL_SHOW_MENU / menu-data 监听 |
context-menu 命令 |
player/command.c → cmd_context_menu() → vo_control(VOCTRL_SHOW_MENU) |
menu-data 属性定义 |
player/command.c → mp_property_mdata |
| 数据源脚本 | player/lua/select.lua → mp.set_property_native("menu-data", ...) |
| 在线文档 | https://mpv.io/manual/master/#context-menu |
Windows 原生菜单由 select.lua 的 context-menu 绑定自动分发(在 Windows 平台上 load-context-menu 默认为 no,因此走原生通道):
| 快捷键 | 激活指令 |
|---|---|
MENU |
script-binding select/context-menu |
Shift+F10 |
script-binding select/context-menu |
命令行 / 脚本激活:
# 在 input.conf 中绑定右键弹出原生菜单
MBTN_RIGHT script-binding select/context-menu
# 或直接调用底层命令(跳过 select.lua 分发)
MBTN_RIGHT context-menu-- 脚本中直接触发原生菜单(通过 VOCTRL_SHOW_MENU)
mp.command("context-menu")
-- 或通过 select.lua 分发(推荐,会根据平台自动选择)
mp.command("script-binding select/context-menu")select.lua解析menu.conf→ 写入menu-data属性w32_common.c监听menu-data属性变化 → 调用mp_win32_menu_update()重建 HMENU- 用户右键 →
context-menu命令 →VOCTRL_SHOW_MENU→mp_win32_menu_show() - Windows API
TrackPopupMenuEx()显示原生弹出菜单 - 用户选择 →
WM_COMMAND消息 →mp_win32_menu_get_cmd()取回命令 → 执行
// 初始化:创建 HMENU,挂载到窗口系统菜单的 "mpv" 子项下
struct menu_ctx *mp_win32_menu_init(HWND hwnd);
// 更新:根据 menu-data (mpv_node) 重建整个 HMENU
void mp_win32_menu_update(struct menu_ctx *ctx, struct mpv_node *data);
// 显示:在鼠标位置弹出菜单
void mp_win32_menu_show(struct menu_ctx *ctx, HWND hwnd);
// 查询:根据菜单项 ID 取回关联的 mpv 命令字符串
const char* mp_win32_menu_get_cmd(struct menu_ctx *ctx, UINT id);
// 销毁
void mp_win32_menu_uninit(struct menu_ctx *ctx);普通用户不需要写 C 代码——只需编辑 menu.conf 或在 Lua 脚本中设置 menu-data 属性(见菜单系统 2 的示例),Windows 原生菜单会自动从同一属性读取数据。
如果你要开发像 mpv-menu-plugin 那样的 C 插件来完全替代原生菜单:
// 最简 cplugin 骨架:监听自定义属性并弹出 Win32 原生菜单
#include <windows.h>
#include <mpv/client.h>
#define PROP_NAME "user-data/my-menu/items"
int mpv_open_cplugin(mpv_handle *handle) {
// 获取 mpv 窗口句柄
mpv_observe_property(handle, 1, "window-id", MPV_FORMAT_INT64);
// 监听菜单数据变化
mpv_observe_property(handle, 2, PROP_NAME, MPV_FORMAT_NODE);
HWND hwnd = NULL;
while (1) {
mpv_event *event = mpv_wait_event(handle, -1);
if (event->event_id == MPV_EVENT_SHUTDOWN)
break;
if (event->event_id == MPV_EVENT_PROPERTY_CHANGE) {
mpv_event_property *prop = event->data;
if (prop->reply_userdata == 1 && prop->format == MPV_FORMAT_INT64) {
hwnd = (HWND)(intptr_t)(*(int64_t *)prop->data);
}
if (prop->reply_userdata == 2 && prop->format == MPV_FORMAT_NODE) {
// 从 mpv_node 构建 HMENU 并显示
// (实际实现需要递归遍历 node,与 win32/menu.c 类似)
}
}
}
return 0;
}- 类型:Cocoa
NSMenu系统菜单栏 - 源文件:
osdep/mac/menu_bar.swift+osdep/mac/app_hub.swift - 外观:标准 macOS 应用菜单栏(屏幕顶部)
- 禁用方式:无直接开关(硬编码在 macOS 平台代码中)
| 类型 | 路径 / 链接 |
|---|---|
| 菜单栏定义与构建 | osdep/mac/menu_bar.swift |
| 应用入口集成 | osdep/mac/app_hub.swift |
| macOS 快捷键选项 | options/options.c → macos_menu_shortcuts |
| 在线文档 | https://mpv.io/manual/master/#options (搜索 macos 相关选项) |
macOS 菜单栏是 自动激活 的,无需任何指令。mpv 启动后即在屏幕顶部显示 macOS 原生菜单栏,包含 File、Edit、View、Video、Audio 等标准菜单。
- 无用户侧激活命令,无快捷键触发
- 菜单中的快捷键由
macos_menu_shortcuts选项控制是否启用 - 可通过
--macos-menu-shortcuts=no禁用菜单中的快捷键绑定(但菜单栏本身无法禁用)
与其他菜单不同,macOS 菜单栏 不读取 menu-data 属性,而是在 Swift 代码中硬编码了完整的菜单结构:
- Apple 菜单:关于、设置、服务、退出
- File:打开文件/URL、截图
- Edit:撤销、剪切、复制、粘贴
- View:全屏、置顶、工作区
- Video:缩放、宽高比、旋转
- Audio:轨道选择、静音、音量延迟
- Subtitle:轨道选择、强制样式、延迟
- Playback:播放/暂停、速度、循环、章节
- Window:最小化、缩放
- Help:在线帮助
每个菜单项通过 Config 结构体定义 mpv 命令:
struct Config {
let name: String // 显示名称
let key: String // 快捷键
let modifiers: NSEvent.ModifierFlags // 修饰键
let type: Type // menu | separator | item
let command: String // mpv 命令
var configs: [Config] // 子菜单
}macOS 菜单栏无法通过脚本操作。如需自定义,需修改 menu_bar.swift 源码并重新编译 mpv。以下是添加自定义菜单项的方式:
// 在 menu_bar.swift 的 menuConfigs 数组中添加
Config(name: "My Tools", configs: [
Config(name: "Reset Speed", key: "r", command: "set speed 1"),
Config(name: "Toggle Sub", key: "s", command: "cycle sub-visibility"),
Config(type: .separator),
Config(name: "Screenshot", key: "p", command: "screenshot"),
])| 特性 | mp.input.select |
context_menu.lua |
Windows 原生 | macOS 原生 |
|---|---|---|---|---|
| 渲染方式 | ASS OSD (console) | ASS OSD (overlay) | Win32 HMENU | Cocoa NSMenu |
| 交互方式 | 模糊搜索 + 列表选择 | 多级子菜单展开 | 系统原生右键菜单 | 系统菜单栏 |
| 数据源 | 脚本直接调用 API | menu-data 属性 |
menu-data 属性 |
硬编码 Swift |
| 配置文件 | 无(代码驱动) | menu.conf → menu-data |
menu.conf → menu-data |
源码 |
| 跨平台 | 全平台 | 全平台 | 仅 Windows | 仅 macOS |
| 默认启用 | 是(随 console) | 非 Win/Mac 平台 | Windows 自动 | macOS 自动 |
| 禁用选项 | --load-console=no |
--load-context-menu=no |
--load-select=no |
无 |
在 mpv.conf 中添加:
# 禁用 select.lua(停止解析 menu.conf,停止填充 menu-data)
load-select=no
# 禁用 context_menu.lua(停止 OSD 右键菜单渲染)
load-context-menu=no注意:
load-select=no会同时禁用g-*系列列表选择器(选轨道、选章节等),因为它们都由select.lua提供。如果你只想禁用右键菜单但保留列表选择器,只需load-context-menu=no。
mpv-menu-plugin 是一个完全独立的第三方 Windows C 插件:
| 内置菜单系统 | mpv-menu-plugin | |
|---|---|---|
| 菜单配置 | menu.conf |
input.conf 中的 #menu: 注释 |
| 数据属性 | menu-data |
user-data/menu/items(自定义) |
| 渲染 | OSD / Win32 HMENU / NSMenu | Win32 TrackPopupMenuEx() |
| 依赖 | mpv 核心 | 仅 mpv/client.h 公共 API |
两者使用不同的属性、不同的配置文件、不同的渲染路径,
select.lua 的 populate_menu_data 选项会影响 mpv-menu-plugin
或者彻底设置 load-select=no + load-context-menu=no 后可安全地只使用 mpv-menu-plugin。
另一个热门的社区菜单API,同样独立,具体见对应仓库。