Skip to content

Instantly share code, notes, and snippets.

@hooke007
Last active April 5, 2026 07:45
Show Gist options
  • Select an option

  • Save hooke007/d80eb8474cb0c2616668e3a2a4bb7f0d to your computer and use it in GitHub Desktop.

Select an option

Save hooke007/d80eb8474cb0c2616668e3a2a4bb7f0d to your computer and use it in GitHub Desktop.
mpv的菜单开发抉择方向

mpv 内置的多种菜单系统解析

v20260405

架构总览

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 菜单)            │            │
│  └────────────────────────────────────────────────────┘           │
└───────────────────────────────────────────────────────────────────┘

菜单系统 1:mp.input.select — 列表选择器

概述

  • 类型: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.cmp_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 弹出带模糊搜索的列表,可键入文字过滤,回车确认。


菜单系统 2:context_menu.lua — OSD 右键菜单

概述

  • 类型: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.luaparse_menu_conf() / on_idle()
默认菜单定义 etc/menu.conf
内置样式覆盖 etc/builtin.confcontext_menu-*
context-menu 命令实现 player/command.ccmd_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")

工作原理

  1. 用户右键点击 → 触发 context-menu 按键绑定
  2. select.lua 检查 load-context-menu 选项
  3. 如果为 yes:发送 script-message-to context_menu open
  4. context_menu.lua 读取 menu-data 属性,用 ASS 绘制多级菜单

数据源:menu.conf 格式

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.2

menu-data 属性的数据结构

select.luamenu.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)

菜单系统 3:Windows 原生右键菜单

概述

  • 类型:Win32 原生 HMENU 弹出菜单
  • 源文件video/out/win32/menu.c + video/out/w32_common.c
  • 外观:Windows 系统原生右键菜单,跟随系统主题
  • 触发方式context-menu 命令 → VOCTRL_SHOW_MENUTrackPopupMenuEx()
  • 禁用方式--load-select=no(不填充 menu-data,菜单为空不会显示)

参考文档与源码

类型 路径 / 链接
HMENU 构建与显示 video/out/win32/menu.c
HMENU 头文件 video/out/win32/menu.h
窗口集成(事件处理) video/out/w32_common.cVOCTRL_SHOW_MENU / menu-data 监听
context-menu 命令 player/command.ccmd_context_menu()vo_control(VOCTRL_SHOW_MENU)
menu-data 属性定义 player/command.cmp_property_mdata
数据源脚本 player/lua/select.luamp.set_property_native("menu-data", ...)
在线文档 https://mpv.io/manual/master/#context-menu

激活指令

Windows 原生菜单由 select.luacontext-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")

工作原理

  1. select.lua 解析 menu.conf → 写入 menu-data 属性
  2. w32_common.c 监听 menu-data 属性变化 → 调用 mp_win32_menu_update() 重建 HMENU
  3. 用户右键 → context-menu 命令 → VOCTRL_SHOW_MENUmp_win32_menu_show()
  4. Windows API TrackPopupMenuEx() 显示原生弹出菜单
  5. 用户选择 → WM_COMMAND 消息 → mp_win32_menu_get_cmd() 取回命令 → 执行

关键 C API

// 初始化:创建 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 插件层面)

普通用户不需要写 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;
}

菜单系统 4:macOS 原生菜单栏

概述

  • 类型: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.cmacos_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.confmenu-data menu.confmenu-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 与内置菜单的关系

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.luapopulate_menu_data 选项会影响 mpv-menu-plugin
或者彻底设置 load-select=no + load-context-menu=no 后可安全地只使用 mpv-menu-plugin。

附录:uosc菜单

另一个热门的社区菜单API,同样独立,具体见对应仓库。

https://github.com/tomasklaen/uosc/wiki#menu-api

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment