2026-06-24 10:21:00
| 项目 | 规格 |
|---|---|
| 型号 | MECHREVO Taitan Series GM7TG0M |
| CPU | 11th Gen Intel Core i7-11800H × 16 (8C/16T, Tiger Lake-H) |
| 独显 | NVIDIA GeForce RTX 3060 Laptop GPU (6GB GDDR6) |
| 核显 | Intel UHD Graphics (Tiger Lake-H GT1, 集成于 i7-11800H) |
| 内存 | 64 GiB DDR4 |
| 磁盘 1 | CT240BX500SSD1 240GB SATA SSD |
| 磁盘 2 | Fanxiang S500PRO 1TB NVMe SSD |
| 磁盘 3 | IM2P33F8-512GD 512GB NVMe SSD |
| 固件 | N.1.07MRO06 |
| BIOS 显卡模式 | dGPU Only (独显直连) / MSHybrid (双显卡混合输出) |
| 项目 | 版本 |
|---|---|
| 操作系统 | Debian GNU/Linux 13 (trixie) |
| 架构 | x86_64 |
| 桌面环境 | GNOME 48.7 |
| 窗口系统 | Wayland |
| 内核 | Linux 6.12.90+deb13.1-amd64 |
| NVIDIA 驱动 | 595.80 (私有驱动) |
| NVIDIA VA-API | nvidia-vaapi-driver 0.0.13 (NVDEC 后端) |
| PipeWire | 1.4.2 |
| 腾讯会议 | v3.26.10.401 (deb 包安装于 /opt/wemeet) |
BIOS 中设为双显卡混合输出时,系统主 GPU 是 Intel 核显,桌面合成器(Mutter)由核显驱动。但笔者的两台外接显示器(一台 4K 27 寸、一台 1080P 24 寸)通过 HDMI 和 USB-C 直连到了 RTX 3060 独显,这意味着:外接屏幕的最终画面输出必须经过 NVIDIA 独显。每一帧渲染流程为 Intel 核显合成→经 PCIe 总线拷贝到 NVIDIA 显存→NVIDIA 输出到屏幕,这个跨 GPU 的 PRIME 缓冲区同步过程引入了显著的延迟和掉帧;同时 Mutter 同时管理两个 GPU 的 framebuffer,帧时序难以对齐,进一步加剧了卡顿感。
为什么 Windows 没这个问题? Windows 下 NVIDIA Optimus 的混合输出走的是一个更高效的路径:NVIDIA 驱动内置硬件拷贝引擎(cross-adapter copy engine),独显渲染的画面通过 PCIe 直接写进核显的 framebuffer 然后输出到屏幕,不需要等待双向同步和帧对齐。而 Linux 的 PRIME 方案目前依赖通用 DMA-BUF 机制进行跨 GPU 缓冲区共享,多了一层软件调度开销,在 4K 高分辨率下带宽压力更大,卡顿感就非常明显。本质上不是硬件不行,是 Linux 图形栈在双 GPU 调度上还没做到 Windows 的成熟度。NVIDIA 在 Windows 下的图形驱动已经打磨了超过 25 年(自 1999 年 GeForce 256 发布算起),Optimus 双显卡切换技术也已在 Windows 上迭代了 15 年以上;而 Linux 下的 PRIME 多 GPU 同步方案直到 2016 年前后才逐渐可用,Wayland 下的双 GPU 混合渲染就更加年轻。十几年的工程积累差距,不是短期内能抹平的。
补充:混合模式下若改用 X11 显示协议,流畅度问题同样可以得到缓解——X11 的 GPU 调度路径更成熟,PRIME 同步开销较低。但笔者不选择此方案,因为 Wayland 是 Linux 桌面显示的明确未来方向,且大部分日常软件(包括通过 XWayland 兼容层运行的)在 Wayland 下已足够稳定。放弃 Wayland 换 X11 是走回头路。
最理想的情况似乎是 BIOS 设为 iGPU Only,只用 Intel 核显驱动内置屏幕,简单省电。但这是不可能的,原因在于本游戏本的硬件设计:
HDMI 和 USB-C(DP Alt Mode)视频输出接口在物理上直连到了 RTX 3060 独显,而非 Intel 核显。
这意味着:
因此实际可选的模式只有两种:MSHybrid(双显卡混合输出) 和 dGPU Only(独显直连)。
BIOS 切换为独显直连后,所有渲染由 RTX 3060 承担,桌面流畅度大幅提升。但带来三个问题:
Waydroid 维护者 electrikjesus 于 2025 年 12 月明确表态:
Android uses only Mesa drivers. No way to get around it.
Android 图形栈的 GPU 加速只能通过 Mesa 体系内的驱动实现。NVIDIA 私有驱动(包括 nvidia-open 内核模块)不属于 Mesa 生态,waydroid 无法调用它为 Android 容器渲染界面。
NVIDIA 支持的唯一例外是更换为
nouveau开源驱动,但nouveau在 RTX 3060 上的性能极差,没有实用价值。
既然 Waydroid 使用频率不高,而桌面流畅度与日常体验息息相关,最终决定:
BIOS 设为 dGPU Only(独显直连),放弃 Waydroid。
| 软件 | 状态 | 说明 |
|---|---|---|
| Waydroid | 不可用 | 硬伤,无解 |
| 腾讯会议 webcam | 已修复 | 见第四章 |
| 腾讯会议屏幕共享 | 已修复 | 见第四章 |
| kooha | 已卸载 | VA-API 编码依赖,已用 GNOME Screenshot + ffmpeg NVENC 替代 |
| 音频输入输出 | 正常 | 之前测试时为空是因为 systemctl --user stop pipewire.socket 关掉了 PipeWire,与独显直连无关 |
| GNOME 桌面 | 流畅 | RTX 3060 渲染 + nvidia-clock-unlock 锁频,体验显著改善 |
| 其他软件 | 正常 | Chrome、VS Code、飞书、LibreOffice、GIMP、Krita、VLC、Docker、Flatpak 等均不依赖 Intel GPU |
独显直连能改善流畅度,但 NVIDIA 私有驱动在默认情况下会根据负载动态调整 GPU 频率。桌面合成器(Mutter)的渲染负载波动较大,GPU 在低频↔高频之间频繁切换,导致 GNOME 动画仍有偶尔的掉帧。
为此创建了 nvidia-clock-unlock.service 系统服务,在启动时:
nvidia-smi -pm 1),防止 GPU 在空闲时进入深度休眠nvidia-smi -lgc 1200,2100),确保合成器始终获得足够的渲染算力# /etc/systemd/system/nvidia-clock-unlock.service
[Unit]
Description=Unlock NVIDIA GPU clock floor for smooth Wayland compositing
After=multi-user.target
[Service]
Type=oneshot
ExecStart=/usr/bin/nvidia-smi -pm 1
ExecStart=/usr/bin/nvidia-smi -lgc 1200,2100
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
启用方式:
sudo systemctl daemon-reload
sudo systemctl enable --now nvidia-clock-unlock.service
代价:GPU 无法完全进入最低功耗状态,闲置功耗略高(对本游戏本影响不大,日常插电使用)。
i965-va-driver # Intel 老 GPU VA-API 驱动
intel-media-va-driver # Intel 新 GPU VA-API 驱动
firmware-intel-graphics # Intel GPU 固件
libdrm-intel1 # Intel DRM 用户态库
libigdgmm12 # Intel 显存管理库
以上包在不加载 i915 模块时不生效,占空间极小,保留以免日后切回混合模式时需重装。
# /etc/default/grub 中以下 i915 参数可删除(独显直连下 i915 模块不加载):
i915.enable_psr=0 i915.enable_fbc=1 i915.enable_guc=3
现象:启动腾讯会议后,webcam 预览画面黑屏,无法显示视频。
根因:xcast 日志中 eglCreateWindowSurface 返回 error:3005(EGL_BAD_CONFIG)。wemeet 以 X11(XCB) 后端运行在 XWayland 下,NVIDIA EGL 无法将 XWayland 的 X11 窗口句柄转换为 EGL Window Surface。
关键线索:AUR wemeet-bin 评论区 pinned 评论(by anlorsp, 2025-03-08):
对于 N 卡视频黑屏的问题,可以尝试为腾讯会议单独设置
__EGL_VENDOR_LIBRARY_FILENAMES=/usr/share/glvnd/egl_vendor.d/50_mesa.json环境变量(不要全局设置该变量!)。在双显卡环境下可以修复黑屏问题,在单 NVIDIA 显卡环境下未测试。
本机验证:dGPU Only 模式(即单 NVIDIA)下,设置此变量后 webcam 加载成功。原理:强制 wemeet 走 Mesa 的 libEGL_mesa.so.0,走 llvmpipe 软件渲染路径,虽然性能不如硬件加速,但可以正常创建 EGL Window Surface 并显示摄像头画面。
修复步骤:编辑 /opt/wemeet/wemeetapp.sh,在第 53 行添加:
export __EGL_VENDOR_LIBRARY_FILENAMES=/usr/share/glvnd/egl_vendor.d/50_mesa.json
注意:此处必须有
export,仅写变量赋值不导出的话子进程wemeetapp继承不到,变量无效。
当前完整脚本内容见文末 附录 B。
现象:点击"共享屏幕"无反应,或能发起共享但别人看到白屏。
根因(来自 wemeet-screenshare-patch 技术细节):
libscreen_share_module.so 中 DBus 异步调用顺序错误 — Start 调用放在了 SelectSources 之后而未等待其完成,依赖竞态条件。xdg-desktop-portal-gnome 后端将 SelectSources 立即返回、Start 弹窗等待用户选择显示器,掩盖了此 bug;但在 xdg-desktop-portal-wlr 后端则会必然失败。BGRx 格式,部分 portal 后端不支持,导致对方看到白屏。补丁中的 libhook.so 劫持缓冲区路径做了 BGRx↔RGBx 转换。本机修复过程:
python3 patch.py) 和 Step 2 (添加 LD_PRELOAD),能发起共享但别人看到白屏(颜色格式问题)。reverse_colors.py 撤销 Hunk 1a/1b 的颜色格式劫持,同时删除 LD_PRELOAD 行。reverse_colors.py 代码(保存为 .py 文件后用 sudo python3 执行):
#!/usr/bin/env python3
"""Reverse Hunk 1a/1b (color format patches) while keeping Hunk 0 (DBus fix)."""
import mmap
import os
import sys
REVERSE_PATCHES = {
"/opt/wemeet/bin/modules/screen_share/libscreen_share_module.so": [
(0x450fb6, bytes.fromhex("08")),
(0x4566e4, bytes.fromhex("8b45c08b4808")),
(0x6b1951, bytes.fromhex("4889e54883ec1048897df8488b7df8e88b01")),
(0x6b1964, bytes.fromhex("004889c7e8b30100004883c4105dc3662e0f1f84")),
],
"/opt/wemeet/lib/libxcast.so": [
(0xfedbfa, bytes.fromhex("660f1f44")),
(0xfedc3c, bytes.fromhex("23")),
(0xfedc4b, bytes.fromhex("4883")),
(0xfedc4f, bytes.fromhex("5b5d415c415d415e415fc3660f1f440000")),
],
}
def apply(path, patches):
print(f"Patching: {path}")
with open(path, "r+b") as fh:
with mmap.mmap(fh.fileno(), 0, access=mmap.ACCESS_WRITE) as mm:
for offset, new_bytes in patches:
current = mm[offset:offset+len(new_bytes)]
print(f" 0x{offset:08x}: {current.hex()} => {new_bytes.hex()}")
mm.seek(offset)
mm.write(new_bytes)
mm.flush()
print(" OK\n")
for f, patches in REVERSE_PATCHES.items():
apply(f, patches)
print("Done. Hunks 1a/1b reversed. Hunk 0 (DBus fix) retained.")
执行后删除启动脚本中的 LD_PRELOAD 行:
sudo sed -i '/^export LD_PRELOAD.*libhook\.so/d' /opt/wemeet/wemeetapp.sh
| 功能 | 状态 | 备注 |
|---|---|---|
| Webcam 预览 | 正常 |
50_mesa.json → llvmpipe 软件渲染 |
| 观看别人共享屏幕 | 正常 | 软解 + 软渲染,稍慢但可用 |
| 创建屏幕共享给别人看 | 正常 | Hunk 0 DBus 修复 |
| 共享画面颜色 | 正常 | 已撤销 Hunk 1a/1b |
| 音频输入/输出 | 正常 | PipeWire 正常启动即可 |
| 视频硬件编解码 | 关闭 (hwdec:0, hwenc:0) | 走 Mesa llvmpipe 软件路径 |
| 维度 | 混合输出 (MSHybrid) | 独显直连 (dGPU Only) |
|---|---|---|
| 桌面流畅度 | 差 | 好 |
| Waydroid | 可用 | 不可用 |
| 腾讯会议 webcam | 正常 | 已修复(Mesa llvmpipe 兜底) |
| 腾讯会议屏幕共享 | 打 patch 后正常 | 打 patch 后正常 |
| NVIDIA VA-API 硬解 | NVDEC + Intel 都可用 | 仅 NVDEC |
| NVIDIA 硬编 | NVENC 正常 | NVENC 正常 |
核心妥协:放弃 Waydroid,换取桌面流畅度。腾讯会议通过 __EGL_VENDOR_LIBRARY_FILENAMES + wemeet-screenshare-patch(仅 Hunk 0)组合方案完全可用。
/opt/wemeet/wemeetapp.sh 当前内容#!/bin/bash
os_release="/etc/os-release"
if [[ -e ${os_release} ]];then
source /etc/os-release
main=`echo ${VERSION_ID} | awk -F . '{print $1}'`
case $ID in
ubuntu)
if [[ ${main} -le "16" ]];then
zenity --info --title="腾讯会议" --text="腾讯会议检测到您操作系统版本过低,请升级系统到ubuntu18.04或以上版本!" --width=350 --height=100
exit 1
fi
if [[ ${main} -le "18" ]];then
if [ $XMODIFIERS ];then
im_module=$XMODIFIERS
echo 'use XMODIFIERS'
export QT_IM_MODULE=${im_module#*=}
elif [ $GTK_IM_MODULE ];then
echo 'use GTK_IM_MODULE'
export QT_IM_MODULE=${GTK_IM_MODULE}
fi
echo ${QT_IM_MODULE}
fi
;;
*)
;;
esac
fi
if [ "$XDG_SESSION_TYPE" = "wayland" ];then
if [ -f "/opt/x11-wayland/x11-ext.sh" ];then
source /opt/x11-wayland/x11-ext.sh
else
export QT_QPA_PLATFORM=xcb
export XDG_SESSION_TYPE=x11
unset WAYLAND_DISPLAY
export WEMEET_XWAYLAND=1
fi
fi
SELF=$(readlink -f "$0")
HERE=${SELF%/*}
export LC_ALL=zh_CN.UTF-8
export PATH="${HERE}/bin${PATH:+:$PATH}"
export LD_LIBRARY_PATH="${HERE}/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
export QT_PLUGIN_PATH="${HERE}/plugins"
export TZ=Asia/Shanghai
export __EGL_VENDOR_LIBRARY_FILENAMES=/usr/share/glvnd/egl_vendor.d/50_mesa.json
exec wemeetapp $*
2026-06-18 16:46:00
为了解决我的机械革命无界 14 pro 笔记本在 linux debian testing(forky) 下的自带麦克风、自带扬声器、插入 3.5mm 耳机这三个场景下,对应硬件都能正常工作的问题,遂有了此文。
前景提要:机械革命无界14Pro笔记本debian forky成功驱动内置扬声器和麦克风
| 项目 | 值 |
|---|---|
| 机型 | MECHREVO WUJIE14 PRO |
| CPU | 12th Gen Intel Core i7-12650H |
| 声卡 | Intel Alder Lake PCH-P HD Audio (8086:51c8) |
| Codec | Realtek ALC256 (10ec:0256) |
| 子系统 ID | 1d05:170b |
| 固件 | SOF (Sound Open Firmware) 签名固件 2025.05.1 |
| OS | Debian forky/sid, GNOME 50, Wayland |
| 内核 | 7.0.10+deb14-amd64 |
| PipeWire | 1.6.6 + WirePlumber 1.6.6 |
| ALSA UCM2 | alsa-ucm-conf 1.2.15.3-1 |
确保以下三个场景全部正常工作:
| 场景 | 状态 |
|---|---|
| 内置数字麦克风(DMIC) | 始终正常 |
| 内置扬声器 | 始终正常 |
| 3.5mm 耳机插入后 | 修复前无声,修复后正常 |
插入 3.5mm 耳机后声音仍然从笔记本内置扬声器输出,耳机无声音。系统设置输入设备显示正常(内置数字麦克风可用)。
┌─────────────────────────────────────────────────────────┐
│ 问题 1:硬件插孔检测不工作 │
│ ALSA 控制 "Headphone Jack" 始终 = off │
│ 即使耳机物理插入,Codec (Node 0x21) 的 pin sense 未触发 │
│ 可能原因:SOF 驱动与 ALC256 pin sense 事件处理有兼容问题 │
├─────────────────────────────────────────────────────────┤
│ 问题 2:UCM2 启动时禁用 Auto-Mute │
│ /usr/share/alsa/ucm2/HDA/init.conf │
│ → cset "name='Auto-Mute Mode' off" │
│ 设计意图:让 PipeWire 用户态管理切换,但 UCM2 配置有缺陷 │
├─────────────────────────────────────────────────────────┤
│ 问题 3:Speaker 和 Headphones 在不同 HiFi Profile 中 │
│ 两者共用 PlaybackPCM "hw:0",ACP 拆分成了两个互斥 Profile │
│ Headphones 依赖 JackControl,端口永远 "not available" │
│ 导致 PipeWire 无法激活耳机 DSP 通路 │
└─────────────────────────────────────────────────────────┘
model= 参数无效snd_soc_skl_hda_dsp),声卡名 sof-hda-dsp
snd-hda-intel 模块虽然被加载,但依赖计数为 0,不参与实际驱动/etc/modprobe.d/alsa-alc256.conf 中配置的 options snd-hda-intel model=xxx 在 SOF 下不生效snd-intel-dspcfg.dsp_driver=1)会丢失内置 DMIC ┌──────────┐
hw:0 PCM ──────┤ DAC 0x02 │──┬── Pin 0x14 (Speaker) ── 扬声器
│ (Speaker)│ │
└──────────┘ │
├── Pin 0x1b (unused)
└── Pin 0x21 (HP β) ── 耳机(辅)
┌──────────┐
hw:0 PCM ──────┤ DAC 0x03 │───── Pin 0x21* (HP α) ── 耳机(主)
│(Headphone)│
└──────────┘
* 表示活动连接
关键:两个 DAC 接收同一路 PCM 音频流,SOF DSP 固件根据激活的 Profile 控制哪个 DAC 通路工作。
| 尝试 | 方法 | 结果 | 原因 |
|---|---|---|---|
| 1 |
model=laptop-amic → headset-mic
|
无效 | SOF 驱动忽略 model= 参数 |
| 2 | 方案A:加 dsp_driver=1 切传统 HDA + headset-mic
|
耳机无效、麦消失 | 传统 HDA 不支持 DMIC;headset-mic 合并插孔类型不匹配 |
| 3 | SOF 下手动切 Headphones Profile | 端口 not available 无法路由 | JackControl 依赖失败检测 |
| 4 | Speaker Profile 下手动 amixer 开耳机 | 无声音 | DSP 未激活耳机通路,DAC 0x03 无 stream |
| 5 | amixer toggle 脚本(操作 Playback Switch) | 调音量时扬声器被激活 | WirePlumber EnableSequence 每回路由激活都重开 Speaker |
绕过硬件插孔检测,通过切换 Profile 来切换耳机/扬声器,让 WirePlumber 独立管理每个 Profile 的音量。
1. 修改 UCM2 配置,移除耳机 JackControl
# 备份
sudo cp /usr/share/alsa/ucm2/HDA/HiFi-analog.conf \
/usr/share/alsa/ucm2/HDA/HiFi-analog.conf.bak
# 注释 JackControl 行(保留缩进)
sudo sed -i 's/^\([[:space:]]*\)JackControl "${var:hpjack}"/\1#JackControl "${var:hpjack}"/' \
/usr/share/alsa/ucm2/HDA/HiFi-analog.conf
修改位置:/usr/share/alsa/ucm2/HDA/HiFi-analog.conf 第 156 行
PlaybackSwitch "${var:hpvol} Playback Switch"
- JackControl "${var:hpjack}"
+ #JackControl "${var:hpjack}"
效果:Headphones 端口不再依赖 Headphone Jack ALSA 控制,始终显示为可用(如同 Speaker 的 availability unknown)。
2. 创建快捷键切换脚本
~/.local/bin/toggle-audio.sh:
#!/bin/bash
# 在扬声器和耳机之间切换
CARD="alsa_card.pci-0000_00_1f.3-platform-skl_hda_dsp_generic"
SPK_PROFILE="HiFi (HDMI1, HDMI2, HDMI3, Mic1, Mic2, Speaker)"
HP_PROFILE="HiFi (HDMI1, HDMI2, HDMI3, Headphones, Mic1, Mic2)"
SPK_SINK="alsa_output.pci-0000_00_1f.3-platform-skl_hda_dsp_generic.HiFi__Speaker__sink"
HP_SINK="alsa_output.pci-0000_00_1f.3-platform-skl_hda_dsp_generic.HiFi__Headphones__sink"
current=$(pactl list cards 2>/dev/null | grep "Active Profile" | grep -o "Speaker")
if [ -n "$current" ]; then
pactl set-card-profile "$CARD" "$HP_PROFILE"
sleep 0.3
pactl set-default-sink "$HP_SINK"
for i in $(pactl list sink-inputs short 2>/dev/null | awk '{print $1}'); do
pactl move-sink-input "$i" "$HP_SINK" 2>/dev/null
done
notify-send -a "音频切换" "已切换到耳机" -i audio-headphones
else
pactl set-card-profile "$CARD" "$SPK_PROFILE"
sleep 0.3
pactl set-default-sink "$SPK_SINK"
for i in $(pactl list sink-inputs short 2>/dev/null | awk '{print $1}'); do
pactl move-sink-input "$i" "$SPK_SINK" 2>/dev/null
done
notify-send -a "音频切换" "已切换到扬声器" -i audio-speakers
fi
chmod +x ~/.local/bin/toggle-audio.sh
3. 绑定快捷键
GNOME 设置 → 键盘 → 查看及自定义快捷键 → 自定义快捷键 → 添加:
/home/dk/.local/bin/toggle-audio.sh
Super+Alt+S
4. 重启音频服务
systemctl --user restart wireplumber pipewire pipewire-pulse
alsa-ucm-conf 包更新后会覆盖修改的配置文件,需重新执行 sed 命令/usr/share/alsa/ucm2/HDA/HiFi-analog.conf.bak
sudo cp /usr/share/alsa/ucm2/HDA/HiFi-analog.conf.bak /usr/share/alsa/ucm2/HDA/HiFi-analog.conf
| 层级 | 组件 | 说明 |
|---|---|---|
| 用户态 | PipeWire / WirePlumber | 音频路由、流管理、音量控制 |
| 用户态 | ACP (ALSA Card Profile) | 解析 UCM2 配置,生成 Profile/Port |
| 用户态 | ALSA UCM2 | 声卡用例配置(Volume、Switch、Jack、PCM) |
| 内核态 | SOF 固件 | DSP 音频处理管线 |
| 内核态 | snd_soc_skl_hda_dsp |
ASoC machine driver |
| 内核态 | snd_hda_codec_alc269 |
ALC256 Codec 驱动 |
| 硬件 | ALC256 Codec | DAC/ADC/Pin Complex 寄存器 |
| 文件 | 作用 |
|---|---|
/usr/share/alsa/ucm2/HDA/init.conf |
初始化序列(含 Auto-Mute off) |
/usr/share/alsa/ucm2/HDA/HiFi-analog.conf |
模拟设备定义(Speaker/Headphones/Mic) |
/usr/share/alsa/ucm2/Intel/sof-hda-dsp/HiFi.conf |
SOF HiFi 顶层用例 |
/usr/share/alsa/ucm2/Intel/sof-hda-dsp/HiFi-sof.conf |
SOF 特定覆盖(DRC/EQ/固件) |
# 查看声卡
cat /proc/asound/cards
aplay -l
# 查看 Codec 寄存器(Pin Sense、DAC 音量、静音状态)
cat /proc/asound/card0/codec#0
# 查看 ALSA 控制和插孔状态
amixer -c0 contents | grep -E "Jack|Auto-Mute|name="
# 查看 PipeWire 状态
pactl info | grep "Default Sink"
pactl list cards | grep -A2 "Active Profile"
pactl list sinks short
# 查看 snd_hda_intel 是否被使用
ls /sys/module/snd_hda_intel/holders/
cat /sys/module/snd_hda_intel/refcnt
# 查看内核参数
cat /proc/cmdline
# 手动控制 Codec
amixer -c0 cset name='Headphone Playback Switch' on,on
amixer -c0 cset name='Auto-Mute Mode' Enabled
2026-06-14 10:05:00
采用 python 脚本实现(python3)。支持图片(JPG/ARW/HEIF 等)和视频(MP4 + 配套 XML 元数据)的筛选删除。
相关说明:
--dry-run
.jpg .jpeg .png .arw .heic .heif .hif .dng .mp4 .xml
C0054M01.XML 跟随 C0054.MP4 同进退),无需在 keep.txt 中单独列出索尼 JPEG 照片扩展名是 JPG,RAW 拍摄文件扩展名是 ARW,HEIF 拍摄扩展名是 HIF (Apple 的 HEIF 照片扩展名是 HEIC)
cd /run/media/dk/708D-3AF3/DCIM/15660613/
ls
HDK09429.JPG HDK09432.JPG HDK09435.JPG HDK09438.JPG HDK09441.JPG HDK09444.JPG HDK09447.JPG
HDK09430.JPG HDK09433.JPG HDK09436.JPG HDK09439.JPG HDK09442.JPG HDK09445.JPG HDK09448.JPG
HDK09431.JPG HDK09434.JPG HDK09437.JPG HDK09440.JPG HDK09443.JPG HDK09446.JPG HDK09449.JPG
$python3 ~/Downloads/myapps/delete_unwanted_media.py ./keep.txt --dry-run
========== 执行模式 ==========
DRY RUN
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09429.JPG
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09430.JPG
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09431.JPG
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09433.JPG
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09434.JPG
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09435.JPG
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09436.JPG
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09437.JPG
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09439.JPG
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09443.JPG
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09444.JPG
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09445.JPG
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09446.JPG
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09447.JPG
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09448.JPG
========== 保留文件 ==========
保留: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09432.JPG
保留: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09438.JPG
保留: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09440.JPG
保留: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09441.JPG
保留: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09442.JPG
保留: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09449.JPG
========== 统计 ==========
模式: DRY-RUN
计划删除文件数: 15
保留文件数: 6
========== 删除列表 ==========
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09429.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09430.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09431.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09433.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09434.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09435.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09436.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09437.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09439.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09443.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09444.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09445.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09446.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09447.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09448.JPG
========== 保留列表 ==========
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09432.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09438.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09440.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09441.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09442.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09449.JPG
$python3 ~/Downloads/myapps/delete_unwanted_media.py ./keep.txt
========== 执行模式 ==========
REAL DELETE
已删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09429.JPG
已删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09430.JPG
已删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09431.JPG
已删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09433.JPG
已删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09434.JPG
已删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09435.JPG
已删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09436.JPG
已删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09437.JPG
已删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09439.JPG
已删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09443.JPG
已删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09444.JPG
已删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09445.JPG
已删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09446.JPG
已删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09447.JPG
已删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09448.JPG
========== 保留文件 ==========
保留: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09432.JPG
保留: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09438.JPG
保留: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09440.JPG
保留: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09441.JPG
保留: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09442.JPG
保留: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09449.JPG
========== 统计 ==========
模式: DELETE
计划删除文件数: 15
保留文件数: 6
实际删除成功数: 15
========== 删除列表 ==========
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09429.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09430.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09431.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09433.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09434.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09435.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09436.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09437.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09439.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09443.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09444.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09445.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09446.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09447.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09448.JPG
========== 保留列表 ==========
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09432.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09438.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09440.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09441.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09442.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09449.JPG
ls
HDK09432.JPG HDK09438.JPG HDK09440.JPG HDK09441.JPG HDK09442.JPG HDK09449.JPG keep.txt
索尼 A7C2 录制视频后,存储卡上的目录结构为 PRIVATE/M4ROOT/CLIP/。与照片不同,视频不按日期分子目录,所有日期的视频文件都平铺在同一个 CLIP/ 目录下,因此 keep.txt 会较长。不过首次维护好之后,每次拍摄后只需要在末尾追加新增的 clip 号即可。
每个视频片段会生成:
C0054.MP4 — 视频主文件C0054M01.XML — 配套元数据文件(自动跟随 MP4 同进退)cd /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/
ls
C0054.MP4 C0054M01.XML C0055.MP4 C0055M01.XML C0056.MP4 C0056M01.XML C0057.MP4 C0057M01.XML
C0061.MP4 C0061M01.XML C0062.MP4 C0062M01.XML C0063.MP4 C0063M01.XML C0068.MP4 C0068M01.XML
C0070.MP4 C0070M01.XML C0071.MP4 C0071M01.XML C0074.MP4 C0074M01.XML C0076.MP4 C0076M01.XML
C0077.MP4 C0077M01.XML C0079.MP4 C0079M01.XML C0080.MP4 C0080M01.XML C0083.MP4 C0083M01.XML
C0084.MP4 C0084M01.XML C0085.MP4 C0085M01.XML C0086.MP4 C0086M01.XML C0087.MP4 C0087M01.XML
C0088.MP4 C0088M01.XML C0089.MP4 C0089M01.XML C0090.MP4 C0090M01.XML C0091.MP4 C0091M01.XML
C0092.MP4 C0092M01.XML C0093.MP4 C0093M01.XML C0094.MP4 C0094M01.XML C0095.MP4 C0095M01.XML
C0096.MP4 C0096M01.XML
keep.txt 内容(只写 stem,不包含扩展名):
$cat keep.txt
# start to record keep.txt list
C0054
C0055
C0056
C0057
C0061
C0062
C0063
C0068
C0070
C0071
C0074
C0076
C0077
C0080
C0083
C0086
C0092
C0094
C0095
C0096
# 2026-07-04
先模拟运行确认预期:
$python3 ~/Downloads/myapps/delete_unwanted_media.py ./keep.txt --dry-run
========== 执行模式 ==========
DRY RUN
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0079M01.XML
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0079.MP4
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0084M01.XML
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0084.MP4
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0085M01.XML
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0085.MP4
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0087M01.XML
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0087.MP4
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0088M01.XML
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0088.MP4
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0089M01.XML
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0089.MP4
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0090M01.XML
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0090.MP4
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0091M01.XML
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0091.MP4
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0093M01.XML
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0093.MP4
========== 保留文件 ==========
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0054M01.XML
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0054.MP4
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0055M01.XML
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0055.MP4
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0056M01.XML
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0056.MP4
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0057M01.XML
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0057.MP4
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0061M01.XML
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0061.MP4
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0062M01.XML
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0062.MP4
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0063M01.XML
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0063.MP4
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0068M01.XML
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0068.MP4
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0071M01.XML
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0071.MP4
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0070M01.XML
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0070.MP4
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0074M01.XML
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0074.MP4
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0077M01.XML
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0077.MP4
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0076M01.XML
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0076.MP4
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0080M01.XML
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0080.MP4
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0083M01.XML
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0083.MP4
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0086M01.XML
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0086.MP4
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0092M01.XML
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0092.MP4
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0094M01.XML
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0094.MP4
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0095M01.XML
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0095.MP4
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0096M01.XML
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0096.MP4
========== 统计 ==========
模式: DRY-RUN
计划删除文件数: 18
保留文件数: 40
========== 删除列表 ==========
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0079M01.XML
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0079.MP4
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0084M01.XML
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0084.MP4
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0085M01.XML
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0085.MP4
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0087M01.XML
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0087.MP4
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0088M01.XML
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0088.MP4
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0089M01.XML
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0089.MP4
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0090M01.XML
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0090.MP4
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0091M01.XML
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0091.MP4
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0093M01.XML
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0093.MP4
========== 保留列表 ==========
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0054M01.XML
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0054.MP4
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0055M01.XML
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0055.MP4
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0056M01.XML
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0056.MP4
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0057M01.XML
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0057.MP4
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0061M01.XML
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0061.MP4
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0062M01.XML
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0062.MP4
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0063M01.XML
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0063.MP4
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0068M01.XML
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0068.MP4
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0071M01.XML
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0071.MP4
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0070M01.XML
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0070.MP4
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0074M01.XML
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0074.MP4
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0077M01.XML
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0077.MP4
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0076M01.XML
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0076.MP4
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0080M01.XML
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0080.MP4
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0083M01.XML
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0083.MP4
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0086M01.XML
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0086.MP4
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0092M01.XML
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0092.MP4
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0094M01.XML
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0094.MP4
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0095M01.XML
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0095.MP4
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0096M01.XML
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0096.MP4
确认无误后去掉 --dry-run 执行实际删除。
$cat ~/Downloads/myapps/delete_unwanted_media.py
#!/usr/bin/env python3
import os
import sys
TARGET_EXTS = {".jpg", ".jpeg", ".png", ".arw", ".heic", ".heif", ".hif", ".dng", ".mp4", ".xml"}
def load_keep_list(path):
exact_keep = set()
stem_keep = set()
with open(path, "r", encoding="utf-8") as f:
for line in f:
raw = line.strip()
if not raw or raw.startswith("#"):
continue
stem, ext = os.path.splitext(raw)
if ext: # 有扩展名 → 精确匹配
exact_keep.add(raw)
else: # 无扩展名 → stem 匹配
stem_keep.add(stem)
return exact_keep, stem_keep
def get_effective_stem(filename, mp4_stems):
stem, ext = os.path.splitext(filename)
if ext.lower() == ".xml" and mp4_stems:
match = ""
for mp4_stem in mp4_stems:
if len(mp4_stem) > len(match) and stem.startswith(mp4_stem):
match = mp4_stem
if match:
return match
return stem
def is_target_file(filename):
ext = os.path.splitext(filename)[1].lower()
return ext in TARGET_EXTS
def main():
if len(sys.argv) < 2:
print("Usage: python3 delete_unwanted_media.py ./keep.txt [--dry-run]")
sys.exit(1)
keep_path = sys.argv[1]
dry_run = "--dry-run" in sys.argv[2:]
root_dir = os.getcwd()
if not os.path.exists(keep_path):
print(f"[ERROR] keep.txt not found: {keep_path}")
sys.exit(1)
exact_keep, stem_keep = load_keep_list(keep_path)
to_delete = []
to_keep = []
# 扫描文件
for root, dirs, files in os.walk(root_dir):
mp4_stems = [os.path.splitext(f)[0] for f in files if f.lower().endswith(".mp4")]
for file in files:
if not is_target_file(file):
continue
full_path = os.path.join(root, file)
stem = get_effective_stem(file, mp4_stems)
if (file in exact_keep) or (stem in stem_keep):
to_keep.append(full_path)
else:
to_delete.append(full_path)
deleted_count = 0
print("\n========== 执行模式 ==========")
print("DRY RUN" if dry_run else "REAL DELETE")
# 删除逻辑
for path in to_delete:
if dry_run:
print(f"[DRY-RUN] 将删除: {path}")
else:
try:
os.remove(path)
print(f"已删除: {path}")
deleted_count += 1
except Exception as e:
print(f"[ERROR] 删除失败 {path}: {e}")
# 保留输出
print("\n========== 保留文件 ==========")
for path in to_keep:
print(f"保留: {path}")
# 统计
print("\n========== 统计 ==========")
print(f"模式: {'DRY-RUN' if dry_run else 'DELETE'}")
print(f"计划删除文件数: {len(to_delete)}")
print(f"保留文件数: {len(to_keep)}")
if not dry_run:
print(f"实际删除成功数: {deleted_count}")
# 列表输出
print("\n========== 删除列表 ==========")
for path in to_delete:
print(path)
print("\n========== 保留列表 ==========")
for path in to_keep:
print(path)
if __name__ == "__main__":
main()
keep.txt 内容示例(照片):
$cat keep.txt
HDK09432
HDK09438
HDK09440
HDK09441
HDK09442
HDK09449
keep.txt 内容示例(视频,支持 # 注释行):
$cat keep.txt
# start to record keep.txt list
C0054
C0055
C0056
C0057
C0061
C0062
C0063
C0068
C0070
C0071
C0074
C0076
C0077
C0080
C0083
C0086
C0092
C0094
C0095
C0096
# 2026-07-04
2026-06-12 16:09:00
gnome 桌面自带这样的功能,右键就能“使用独立显卡启动”,看下图

NVIDIA 官方提供了 PRIME Render Offload 机制,通过环境变量让指定程序走独显渲染:
__NV_PRIME_RENDER_OFFLOAD=1 — 告诉 NVIDIA 驱动将 GL/Vulkan 请求路由到独显__GLX_VENDOR_LIBRARY_NAME=nvidia — 强制 GLX 使用 NVIDIA 的 vendor 库将系统目录下的 .desktop 文件拷贝到用户目录(避免被系统更新覆盖):
cp /usr/share/applications/google-earth-pro.desktop ~/.local/share/applications/
cp /usr/share/applications/wechat.desktop ~/.local/share/applications/
修改 Exec 行,在程序路径前加上环境变量:
sed -i 's|^Exec=/opt/google/earth/pro/google-earth-pro %f|Exec=env __NV_PRIME_RENDER_OFFLOAD=1 __GLX_VENDOR_LIBRARY_NAME=nvidia /opt/google/earth/pro/google-earth-pro %f|' \
~/.local/share/applications/google-earth-pro.desktop
sed -i 's|^Exec=/usr/bin/wechat %U|Exec=env __NV_PRIME_RENDER_OFFLOAD=1 __GLX_VENDOR_LIBRARY_NAME=nvidia /usr/bin/wechat %U|' \
~/.local/share/applications/wechat.desktop
让 GNOME Shell 重新加载 .desktop 文件缓存。
nvidia-smi
启动 Google Earth Pro 和 WeChat 后,应看到类似输出:
+-----------------------------------------------------------------------------------------+
| Processes: |
| GPU GI CI PID Type Process name GPU Memory |
| ID ID Usage |
|=========================================================================================|
| 0 N/A N/A 2327 G /usr/bin/gnome-shell 130MiB |
| 0 N/A N/A 25233 G ...gle/earth/pro/googleearth-bin 112MiB |
| 0 N/A N/A 25399 G ...ns-seed-version --log-level=2 72MiB |
+-----------------------------------------------------------------------------------------+
googleearth-bin 为 Google Earth Pro,...ns-seed-version 为 WeChat 的 GPU 子进程。
--use-gl=egl 参数。~/.local/share/applications/ 下对应的 .desktop 文件即可回到系统默认配置。2026-06-01 10:25:05
GitHub 地址:https://github.com/hellodk34/linux-quick-translate
在 Linux 桌面环境快速查词/翻译句子,绑定系统全局快捷键。支持各种 Linux 发行版和不同的 DE(桌面环境)。详细食用方法请看 GitHub
选中文本 → 复制( Ctrl+C )→ 按 Super+C 弹出结果。
这样在浏览器或者系统中的任何地方阅读英文文献/文章都能快速翻译,复制文章的全部文本也能翻译整篇文章。
截图展示:
欢迎使用!
2026-06-01 10:06:31
使用截图


介绍请看 GitHub,这里懒得再写一遍了。地址 https://github.com/hellodk34/checkitout_weather_android