<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[是我吖，bcw222]]></title><description><![CDATA[Blog Space]]></description><link>https://me.bcw2.top:3443</link><image><url>https://file.bcw2.top:3443/f/0zsd/bcw0613.jpg</url><title>是我吖，bcw222</title><link>https://me.bcw2.top:3443</link></image><generator>Shiro (https://github.com/mix-space-lts/Shiro)</generator><lastBuildDate>Thu, 09 Jul 2026 05:35:58 GMT</lastBuildDate><atom:link href="https://me.bcw2.top:3443/feed" rel="self" type="application/rss+xml"/><pubDate>Thu, 09 Jul 2026 05:35:58 GMT</pubDate><language><![CDATA[zh-CN]]></language><item><title><![CDATA[AI Transparency]]></title><description><![CDATA[<div><blockquote>该渲染由 Shiro API 生成，可能存在排版问题，最佳体验请前往：<a href="https://me.bcw2.top:3443/posts/blog/ai-transparency">https://me.bcw2.top:3443/posts/blog/ai-transparency</a></blockquote><div><p>Gen AI 时代，抗拒AI是愚蠢的，滥用AI是可耻的。为了规范AI使用边界，本博客中的文章可能标有如下标签：</p><ul><li><strong>AI-ASSISTED</strong>：本文间接包含AI生成内容作为人工写作的参考，如果存在错误则即使不用AI博主也会在这里犯错（说不定错的更离谱），可放心食用；</li><li><strong>AI-REFINED</strong>：本文为博主完成草稿后全篇由AI润色的产物，除博主能力所限外还可能存在LLM幻觉导致的错误和文风一股入机味，但应该无伤大雅~</li><li><strong>MAN-MADE</strong>：本文为博主纯手搓；</li><li>目前没有直接包含AI生成内容的计划——想看这种东西为什么不直接把想看的当提示词发给AI让它生成呢？</li></ul><p><strong>博主郑重承诺：我们不制造电子垃圾，任何标签均为如实声明</strong></p></div><p style="text-align:right"><a href="https://me.bcw2.top:3443/posts/blog/ai-transparency#comments">看完了？说点什么呢</a></p></div>]]></description><link>https://me.bcw2.top:3443/posts/blog/ai-transparency</link><guid isPermaLink="true">https://me.bcw2.top:3443/posts/blog/ai-transparency</guid><dc:creator><![CDATA[bcw222]]></dc:creator><pubDate>Wed, 01 Jul 2026 16:45:35 GMT</pubDate></item><item><title><![CDATA[【系统化速查表 第二弹】装命令行工具配环境变量]]></title><description><![CDATA[<div><blockquote>该渲染由 Shiro API 生成，可能存在排版问题，最佳体验请前往：<a href="https://me.bcw2.top:3443/posts/tech/environment-variable-for-command-line-interface">https://me.bcw2.top:3443/posts/tech/environment-variable-for-command-line-interface</a></blockquote><div><h2 id="">环境变量是什么</h2><p><strong>环境变量（Environment Variable）</strong>：操作系统提供给进程的键值对。进程启动时继承父进程的整个环境，可用于配置程序行为、传递路径信息等。常见如 <code>HOME</code>、<code>USER</code>、<code>SHELL</code>、<code>LANG</code>。</p><p><strong>PATH</strong>：最核心的环境变量，告诉 Shell 在哪些目录中<strong>按顺序</strong>搜索可执行文件。敲一条命令时，Shell 挨个扫 PATH 里的目录——先找到的就执行，后面的不再查。</p><p><strong>动态库搜索路径</strong>：程序运行时查找共享库（Linux <code>.so</code>、macOS <code>.dylib</code>、Windows <code>.dll</code>）的机制。逻辑与 PATH 类似但<strong>机制因平台差异极大</strong>，是最容易踩跨平台坑的领域。</p>
<h2 id="">我什么时候会用到它</h2><p>抛开应用程序主动用它传配置不谈，最常用的场景就是手动安装命令行工具。处于某些原因我们不想用包管理器或安装器安装程序时（它们通常会帮你配置好环境变量）而是直接下载一个压缩包的可执行文件使用时，我们要告诉 Shell 在哪里找到我们下载的可执行文件，即<strong>配置环境</strong>。通常情况下，只需要将可执行文件所在的目录添加到 <code>PATH</code> 环境变量里即可。那么，怎么添加？——</p><h2 id="">怎么读</h2><p>环境变量的读取范围分四层，搞不清这个是&quot;设了但不生效&quot;的第一大原因：</p><table><thead><tr><th> 层级 </th><th> 覆盖范围 </th><th> 典型用途 </th><th> 生效时机 </th></tr></thead><tbody><tr><td> <strong>系统级</strong> </td><td> 所有用户、所有进程 </td><td> 公共工具路径、系统级配置 </td><td> 需重启/新登录/广播通知 </td></tr><tr><td> <strong>用户级</strong> </td><td> 单个用户的所有进程 </td><td> 用户信息、个人工具 </td><td> 需新开 Shell 或新登录 </td></tr><tr><td> <strong>Shell级</strong> </td><td> 当前 Shell 及其子进程 </td><td> 临时测试 </td><td> 对当前 Shell 接下来的所有操作生效，关闭 Shell 即消失 </td></tr><tr><td> <strong>进程级</strong> </td><td> 仅当前运行命令 </td><td> 一次性操作 </td><td> 对当前运行命令生效，当前命令退出即消失 </td></tr></tbody></table><p>同名变量在多层设置时，层级越窄优先级越高： 进程级 &gt; Shell级 &gt; 用户级 &gt; 系统级）</p><table><thead><tr><th style="text-align:center"> 层级 </th><th style="text-align:center"> Linux/macOS bash/zsh </th><th style="text-align:center"> Windows CMD/PowerShell </th></tr></thead><tbody><tr><td style="text-align:center"> 系统 </td><td style="text-align:center"> <code>/etc/</code> 下写文件 </td><td style="text-align:center"> <code>setx /M</code> 或面板 </td></tr><tr><td style="text-align:center"> 用户 </td><td style="text-align:center"> <code>~/</code> 下写文件 </td><td style="text-align:center"> <code>setx</code> 或面板 </td></tr><tr><td style="text-align:center"> Shell </td><td style="text-align:center"> <code>export</code> </td><td style="text-align:center"> <code>set</code> / <code>$env:</code> </td></tr><tr><td style="text-align:center"> 进程 </td><td style="text-align:center"> <code>VAR=val cmd</code> </td><td style="text-align:center"> <strong>不支持</strong> </td></tr></tbody></table><p>这里的行为，与其说是平台特定，不如说是 Shell 特定：比如，如果你在 Windows 上用 Bash，那其实更应该参考 Linux 的设置。</p><p>为什么写文件可以修改环境变量？这是 Shell 的设计机制：Shell 启动时会按一定规则运行特定的初始化脚本，而作为所有命令的入口，所有的 Shell 都设置了的环境，相当于就是整个用户/系统的环境。</p><p>特例：</p><ul><li><strong>Linux</strong> <code>/etc/environment</code> 是用户登录时由 PAM 读取的，格式为 <code>KEY=VALUE</code></li><li><strong>macOS</strong> <code>/etc/paths</code> 只管 <code>$PATH</code>，每行一个路径，不是 <code>KEY=VALUE</code> 格式；<code>/etc/paths.d/*</code> 同理（每个文件一行一个路径）</li></ul><h3 id="shell--login--non-loginlinuxmacos-">Shell 的 Login / Non-login——Linux/macOS 最大的坑</h3><p>Shell 启动方式决定它读取哪些配置文件，这直接决定你的环境变量在哪个文件里写才能生效：</p><table><thead><tr><th> </th><th> Login Shell </th><th> Non-login Shell </th></tr></thead><tbody><tr><td> <strong>Interactive</strong> </td><td> SSH 登录、<code>su -</code>、<strong>macOS Terminal.app 默认</strong> </td><td> <strong>Linux GUI 终端默认</strong>、<code>su</code> </td></tr><tr><td> <strong>Non-interactive</strong> </td><td> <code>bash -l script.sh</code> </td><td> <code>bash script.sh</code>、cron 任务 </td></tr></tbody></table><h3 id="bash-">bash 读取顺序</h3><table><thead><tr><th> Shell 类型 </th><th> 读取文件 </th></tr></thead><tbody><tr><td> Login </td><td> <code>/etc/profile</code> → (<code>~/.bash_profile</code> &gt; <code>~/.bash_login</code> &gt; <code>~/.profile</code>)（只读找到的第一个） </td></tr><tr><td> Interactive non-login </td><td> <code>/etc/bash.bashrc</code> → <code>~/.bashrc</code> </td></tr><tr><td> Non-interactive </td><td> 读取 <code>$BASH_ENV</code> 指定的文件（通常不设此变量） </td></tr></tbody></table><p>bash 的 Login Shell 优先级列表 <code>~/.bash_profile</code> &gt; <code>~/.bash_login</code> &gt; <code>~/.profile</code> 诞生于 Unix 历史的“兼容期”，它是为了解决<strong>多流派 Shell 用户的迁移问题</strong>，而非让你把三个文件都用上。</p><ol start="1"><li><strong><code>~/.profile</code> 是 Bourne Shell (<code>sh</code>) 的老祖宗标准</strong>：
在当年，所有 Unix 系统的默认 Shell 都是 <code>sh</code>，大家都把环境变量写在 <code>~/.profile</code> 里。直到今天，如果你用的是 Ubuntu，它的底层系统默认 Shell 是 <code>dash</code>，<code>dash</code> 启动时依然只认 <code>~/.profile</code>。</li><li><strong><code>~/.bash_login</code> 是对 C Shell (<code>csh</code>) 的妥协</strong>：当年有一大批写 <code>~/.login</code> 的 csh 用户转投 Bash，为了照顾他们的习惯，Bash 加了这个支持。</li><li><strong><code>~/.bash_profile</code> 是 Bash 的“亲儿子”</strong>：Bash 诞生增加了很多 <code>sh</code> 不支持的高级语法（比如数组、特殊的字符串处理）。如果用户把这些高级语法写进 <code>~/.profile</code>，当系统其他组件用 <code>sh</code> 或 <code>dash</code> 读取这个文件时，就会<strong>直接报错</strong>。</li></ol><p><strong>为什么设计为互斥隔离（只读找到的第一个）？</strong>
如果 Bash 依次把这三个全读一遍，往往会引发灾难（比如环境变量 <code>PATH=...:$PATH</code> 被重复追加三次、打印了三次登录欢迎语、或者因为语法不兼容报错）。在 Bash 开发者的视角里：</p><blockquote><p>如果你写了专属的 <code>.bash_profile</code>，我就按你的来；如果你没写，我就去看你是不是习惯用 <code>.bash_login</code>；如果你连这都没写，那我就去读最普通的 <code>.profile</code> 保底。千万别读两次。</p></blockquote>
<p>但这导致了一个巨大的痛点：<strong>用户必须在 login 和 non-login 两个文件里维护两套配置</strong>。为了解决这个问题，现代的 Linux 发行版（如 Ubuntu, CentOS, Debian）在默认给用户生成家目录（从 <code>/etc/skel/</code> 拷贝）时，动了一个非常聪明的“手脚”——<strong>强制桥接</strong>。现代发行版真实的 Login Shell 读取顺序变成了一个<strong>嵌套链条</strong>：</p><ol start="1"><li><strong>bash</strong> 加载 <code>/etc/profile</code> -&gt; 遍历并 <code>source /etc/profile.d/*.sh</code> （用于配置系统级环境变量）和<strong>去读</strong> <code>/etc/bash.bashrc</code></li><li><strong>bash</strong> 去找并读取 (<code>~/.bash_profile</code> &gt; <code>~/.bash_login</code> &gt; <code>~/.profile</code>) -&gt; <strong>去读</strong> <code>~/.bashrc</code></li></ol><p>然而，想象如下情景：</p><ol start="1"><li>系统默认只有 <code>~/.profile</code>（里面包含了调用 <code>bashrc</code> 的桥接代码）。</li><li>你为了配置环境变量，上网随便搜了一篇教程，自己新建了一个 <code>~/.bash_profile</code> 文件并在里面写了 <code>export PATH=...</code>。</li><li><strong>灾难发生</strong>：由于 Bash 的优先级是 <code>~/.bash_profile</code> &gt; <code>~/.profile</code>，你的 Login Shell 发现了你新建的文件，就不会再去读系统的 <code>~/.profile</code> 了。</li><li><strong>结果</strong>：你新建的文件里没有那段“桥接代码”，导致你的 <code>~/.bashrc</code> 彻底被 Login Shell 抛弃，以前在 <code>bashrc</code> 里配的所有别名（alias）和变量在 SSH 登录时全部失效。</li></ol><p>因此，上网查教程时要特别小心，<strong>尤其是你发现它在用<code>&gt;&gt;</code>（流追加符）这种看似无害的东西修改配置文件时</strong>，因为你永远不知道它究竟是简单的追加还是默默新建了一个优先级更高的配置。</p>
<h3 id="zsh-macos-catalina-">zsh 读取顺序（macOS Catalina+ 默认）</h3><table><thead><tr><th> Shell 类型 </th><th> 读取文件 </th></tr></thead><tbody><tr><td> Login </td><td> <code>zshenv</code> → <code>zprofile</code> → <code>zshrc</code> → <code>zlogin</code>（系统+用户各一套） </td></tr><tr><td> Interactive non-login </td><td> <code>zshenv</code> → <code>zshrc</code> </td></tr><tr><td> Non-interactive </td><td> <code>zshenv</code> </td></tr></tbody></table><p>zsh 无论是否 login，<strong>都读</strong> <code>~/.zshrc</code>，所以 zsh 下环境变量只需放在 <code>~/.zshrc</code> 就够了——比 bash 简单得多。</p><hr/><h2 id="">怎么写</h2><h3 id="windows">Windows</h3><table><thead><tr><th> 层级 </th><th> 设置方法 </th><th> 写入位置 </th><th> 注意 </th></tr></thead><tbody><tr><td> <strong>系统级</strong> </td><td> <code>setx /M VAR &quot;value&quot;</code>（需管理员权限）<br/>或：系统属性 → 高级 → 环境变量 → 系统变量 </td><td> 注册表 <code>HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment</code> </td><td> 已运行的程序不受影响，需重启或新开程序 </td></tr><tr><td> <strong>用户级</strong> </td><td> <code>setx VAR &quot;value&quot;</code><br/>或：系统属性 → 高级 → 环境变量 → 用户变量 </td><td> 注册表 <code>HKCU\Environment</code> </td><td> 当前 cmd 窗口不生效，新开窗口才生效 </td></tr><tr><td> <strong>Shell级</strong> </td><td> cmd: <code>set KEY=value</code><br/>PowerShell: <code>$env:KEY = &quot;value&quot;</code> </td><td> 仅当前进程内存 </td><td> 关闭 Shell 即消失</td></tr></tbody></table><p>Windows GUI 程序改完环境变量后不会自动感知，需要重启该程序。虽然系统会广播 <code>WM_SETTINGCHANGE</code> 消息，但<strong>很多程序忽略此消息</strong>。可能可以通过重启程序或重新登录生效，但既然都是 Windows 了，最可靠的方式还是<strong>重启系统</strong>。</p><hr/><h3 id="linux">Linux</h3><table><thead><tr><th> 层级 </th><th> 文件 </th><th> 格式 </th><th> 说明 </th></tr></thead><tbody><tr><td> <strong>系统级</strong> </td><td> <code>/etc/environment</code> </td><td> <code>KEY=VALUE</code>（纯键值对） </td><td> PAM 读取，<strong>不能用 Shell 语法</strong>（不能写 <code>export</code>、不能 <code>$VAR</code> 引用），对所有用户所有 Shell 生效 </td></tr><tr><td> </td><td> <code>/etc/profile</code> </td><td> Shell 脚本 </td><td> login shell 时读取 </td></tr><tr><td> </td><td> <code>/etc/profile.d/*.sh</code> </td><td> Shell 脚本 </td><td> <code>/etc/profile</code> 自动 source 此目录下所有 <code>.sh</code>——<strong>加变量新建文件就可以，最好不要直接改 <code>/etc/profile</code></strong> </td></tr><tr><td> <strong>用户级</strong> </td><td> <code>~/.profile</code> </td><td> Shell 脚本 </td><td> login shell 时读取（被 bash/zsh 专属文件优先取代） </td></tr><tr><td> </td><td> <code>~/.bash_profile</code> </td><td> Shell 脚本 </td><td> bash login shell 优先读此文件，有它就不读 <code>~/.profile</code> </td></tr><tr><td> </td><td> <code>~/.bashrc</code> </td><td> Shell 脚本 </td><td> bash interactive non-login 时读取 </td></tr><tr><td> </td><td> <code>~/.zshrc</code> </td><td> Shell 脚本 </td><td> zsh interactive <strong>总是读取</strong>——zsh 下放变量的首选位置 </td></tr><tr><td> <strong>Shell级</strong> </td><td> 直接执行 <code>export VAR=value</code> </td><td> Shell 命令 </td><td> 即时生效，关闭即失 </td></tr></tbody></table>
<h5 id="-etcprofile--etcprofiled">为什么有 <code>/etc/profile</code> 还要 <code>/etc/profile.d/</code>？</h5><p>系统包更新可能覆盖 <code>/etc/profile</code> ，但 <code>/etc/profile.d/</code> 下的文件独立存在不受影响。<code>/etc/</code> 下很多配置其实都有 <code>xxx</code> 和 <code>xxx.d/</code> ——以便分离默认配置和用户配置。</p><h5 id="gui--bashrc">GUI 应用（从桌面环境启动的）<strong>不读 <code>~/.bashrc</code></strong>。</h5><p>它们的进程由 display manager (如 GDM) 创建，继承的是 systemd 用户会话环境。给 GUI 应用设环境变量的正确方式：</p><pre class="language-ini lang-ini"><code class="language-ini lang-ini"># ~/.config/environment.d/my-vars.conf  (systemd 用户环境)
JAVA_HOME=/usr/lib/jvm/java-11
PATH=/home/you/.local/bin:${PATH}
</code></pre><p>此目录下的 <code>.conf</code> 文件格式同 <code>/etc/environment</code>（纯 <code>KEY=VALUE</code>），由 systemd-user 启动时读取，仅对 GUI 应用生效。</p><hr/><h3 id="macos">macOS</h3><table><thead><tr><th> 层级 </th><th> 文件/机制 </th><th> 说明 </th></tr></thead><tbody><tr><td> <strong>系统级</strong> </td><td> <code>/etc/paths</code> </td><td> PATH 专用，每行一个目录（macOS 独有机制） </td></tr><tr><td> </td><td> <code>/etc/paths.d/*</code> </td><td> PATH 模块化扩展，每文件每行一个目录 </td></tr><tr><td> </td><td> <code>/etc/profile</code> → <code>/etc/profile.d/*.sh</code> </td><td> 其他环境变量（同 Linux） </td></tr><tr><td> </td><td> <code>/etc/zshrc</code>、<code>/etc/zprofile</code> </td><td> zsh 系统级配置 </td></tr><tr><td> <strong>用户级</strong> </td><td> <code>~/.zshrc</code> </td><td> zsh interactive <strong>总被读取</strong>——放环境变量的首选位置 </td></tr><tr><td> </td><td> <code>~/.zprofile</code> </td><td> zsh login shell 读取（Terminal.app 默认是 login shell） </td></tr><tr><td> <strong>Shell级</strong> </td><td> <code>export VAR=value</code> </td><td> 即时生效，关闭即失 </td></tr></tbody></table><h5 id="macos-catalina-1015--shell--bash--zsh">macOS Catalina (10.15) 起<strong>默认 Shell 从 bash 改为 zsh</strong>。</h5><p>旧版升级后，<code>~/.bash_profile</code> 里的配置在新终端中<strong>完全不生效</strong>。迁移：把 bash 配置搬到 <code>~/.zprofile</code>（login 级）+ <code>~/.zshrc</code>（interactive 级）。</p><h5 id="macos--path-">macOS 的 <strong><code>PATH</code> 机制独特</strong>。</h5><p>不在 <code>/etc/environment</code> 里（此文件不存在），而是用 <code>/etc/paths</code> + <code>/etc/paths.d/</code>。加系统级 PATH 目录的正确做法：</p><pre class="language-bash lang-bash"><code class="language-bash lang-bash"># 加一个文件到 /etc/paths.d/
sudo tee /etc/paths.d/homebrew &lt;&lt;EOF
/opt/homebrew/bin
EOF
</code></pre><p>注意这里用 <code>tee</code> 而非流重定向符，是因为 <code>sudo</code> 只管命令运行权限，而流是由 Shell 处理的——用 <code>sudo</code> 不就是因为 Shell 权限不够嘛。你要用 <code>sudo sh -c &#x27;echo /opt/homebrew/bin &gt;&gt; /etc/paths.d/homebrew&#x27;</code> 也是一样的。</p><h5 id="gui--docklaunchpad--shell-">GUI 应用（从 Dock/Launchpad 启动）<strong>不继承 Shell 环境变量</strong>。</h5><p>macOS 的 GUI 应用由 <code>launchd</code> 启动，<code>launchd</code> 的环境来自系统级配置，不读 <code>~/.zshrc</code>。给 GUI 应用设环境变量的正确方式：</p><pre class="language-zsh lang-zsh"><code class="language-zsh lang-zsh">launchctl setenv KEY value
launchctl unsetenv KEY
</code></pre>
<hr/><h2 id="path-">PATH 设置最佳实践</h2><table><thead><tr><th> 原则 </th><th> 为什么 </th></tr></thead><tbody><tr><td> <strong>层级越窄越好</strong> </td><td> 个人工具放用户级 PATH，别放系统级影响所有人 </td></tr><tr><td> <strong>追加而非覆盖</strong> </td><td> 设 PATH 时务必保留原有：<code>$PATH:newdir</code>（Linux/macOS）或 <code>%PATH%;newdir</code>（Windows cmd） </td></tr><tr><td> <strong>顺序就是优先级</strong> </td><td> PATH 中靠前的目录先匹配。<code>~/bin</code> 里有个同名程序放在 PATH 前面就会覆盖系统的——可能是你想要的，也可能是灾难 </td></tr><tr><td> <strong>永远不放 <code>.</code>（当前目录）</strong> </td><td> 安全风险：某恶意目录下放个 <code>ls</code>，你 cd 进去敲 <code>ls</code> 就执行了恶意程序。Windows 早期默认把 <code>.</code> 放 PATH 最前面，这是历史安全漏洞 </td></tr><tr><td> <strong>Linux/macOS 个人工具放 <code>~/.local/bin</code></strong> </td><td> XDG 标准目录，现代发行版已默认将其加入 PATH。旧惯例 <code>~/bin</code> 也能用但不是标准 </td></tr></tbody></table><h3 id="-path-">各平台追加 PATH 的安全写法</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash"># Linux/macOS — 在 ~/.bashrc 或 ~/.zshrc 中
export PATH=&quot;$HOME/.local/bin:$PATH&quot;    # 前置（优先使用个人版本）
export PATH=&quot;$PATH:/opt/some-tool/bin&quot;  # 后置（系统版本优先，仅补充）
</code></pre><pre class="language-powershell lang-powershell"><code class="language-powershell lang-powershell"># Windows PowerShell — 用户级永久追加
[Environment]::SetEnvironmentVariable(
    &quot;PATH&quot;,
    [Environment]::GetEnvironmentVariable(&quot;PATH&quot;, &quot;User&quot;) + &quot;;C:\Tools\Bin&quot;,
    &quot;User&quot;
)
</code></pre>
<hr/><h2 id="">动态库搜索——跨平台差异最大的领域</h2><h3 id="">三平台机制速览</h3><table><thead><tr><th> </th><th> Windows </th><th> Linux </th><th> macOS </th></tr></thead><tbody><tr><td> <strong>库文件格式</strong> </td><td> <code>.dll</code> </td><td> <code>.so</code> </td><td> <code>.dylib</code> / <code>.framework</code> </td></tr><tr><td> <strong>环境变量</strong> </td><td> <code>PATH</code>（<strong>兼管 exe 和 DLL</strong>） </td><td> <code>LD_LIBRARY_PATH</code> </td><td> <code>DYLD_LIBRARY_PATH</code> / <code>DYLD_FRAMEWORK_PATH</code> </td></tr><tr><td> <strong>系统级配置</strong> </td><td> PATH 系统变量 </td><td> <code>/etc/ld.so.conf</code> + <code>ldconfig</code> </td><td> 无直接等效（用编译时嵌入路径） </td></tr><tr><td> <strong>编译时嵌入</strong> </td><td> 无标准机制 </td><td> <code>-Wl,-rpath,/dir</code> </td><td> <code>@rpath</code> / <code>@loader_path</code> / <code>@executable_path</code> </td></tr><tr><td> <strong>设计哲学</strong> </td><td> 搜索顺序固定，应用目录优先 </td><td> ldconfig 缓存为主，环境变量为辅 </td><td> <strong>路径编进库本身</strong>，不依赖环境变量 </td></tr></tbody></table><hr/><h3 id="windows-dll-">Windows DLL 搜索顺序</h3><h5 id="windows--path--dll-">Windows 的 PATH <strong>同时管可执行文件搜索和 DLL 搜索</strong>.</h5><p>这是独一无二的：<code>PATH</code> 里加个目录，不光影响命令查找，还影响 DLL 加载。<code>PATH</code> 越宽，DLL 搜索范围也越宽，可能加载到错误版本 DLL（DLL Hell 的根源）。这也是为什么 Windows 下按个 <code>Tab</code> 会冒出来一堆不能直接运行的<code>.dll</code>.</p><h5 id="safedllsearchmode-windows-vista-"><strong>SafeDllSearchMode 开启时</strong>（Windows Vista+ 默认）：</h5><table><thead><tr><th> 顺序 </th><th> 搜索位置 </th></tr></thead><tbody><tr><td> 1 </td><td> <strong>应用程序所在目录</strong>（最高优先——Windows 避免 DLL Hell 的主要机制） </td></tr><tr><td> 2 </td><td> 系统目录 <code>C:\Windows\System32</code> </td></tr><tr><td> 3 </td><td> 16位系统目录（历史遗留） </td></tr><tr><td> 4 </td><td> Windows 目录 <code>C:\Windows</code> </td></tr><tr><td> 5 </td><td> PATH 中的目录 </td></tr><tr><td> 6 </td><td> 当前工作目录 </td></tr></tbody></table><h5 id="safedllsearchmode--path-">SafeDllSearchMode 关闭时（仅旧系统），当前目录排在 PATH 前面——这是安全隐患。</h5><p><strong>最佳实践</strong>：DLL 放在应用程序自己的目录下——这是搜索顺序第一位，最可靠且不影响其他程序。<strong>尽量不要依赖 PATH 来找 DLL</strong>。
这也就是为什么 Windows 下 FFmpeg 那么容易出问题：FFmpeg 的协议使其经常没法再分发，只能手动补齐。</p><hr/><h3 id="linux-">Linux 动态库搜索</h3><p><strong>搜索顺序：</strong></p><table><thead><tr><th> 顺序 </th><th> 来源 </th><th> 说明 </th></tr></thead><tbody><tr><td> 1 </td><td> <code>DT_RPATH</code>（旧） </td><td> 在 <code>LD_LIBRARY_PATH</code> <strong>之前</strong>搜索，可覆盖——<strong>已被 RUNPATH 取代</strong> </td></tr><tr><td> 2 </td><td> <code>LD_LIBRARY_PATH</code> </td><td> 环境变量 </td></tr><tr><td> 3 </td><td> <code>DT_RUNPATH</code>（新） </td><td> 在 <code>LD_LIBRARY_PATH</code> <strong>之后</strong>搜索，不可覆盖——现代工具链默认 </td></tr><tr><td> 4 </td><td> <code>ldconfig</code> 缓存 </td><td> <code>/etc/ld.so.cache</code>，由 <code>ldconfig</code> 命令从 <code>/etc/ld.so.conf</code> 构建 </td></tr><tr><td> 5 </td><td> 默认路径 </td><td> <code>/lib</code>、<code>/usr/lib</code>、<code>/lib64</code>、<code>/usr/lib64</code> 等 </td></tr></tbody></table><p><strong>设置方法：</strong></p><table><thead><tr><th> 层级 </th><th> 方法 </th><th> 说明 </th></tr></thead><tbody><tr><td> <strong>系统级</strong> </td><td> 在 <code>/etc/ld.so.conf.d/</code> 加 <code>.conf</code> 文件，然后 <code>sudo ldconfig</code> </td><td> 模块化管理——<strong>不要直接改 <code>/etc/ld.so.conf</code></strong> </td></tr><tr><td> <strong>用户级</strong> </td><td> 无标准机制 </td><td> 可在 <code>~/.bashrc</code> 里设 <code>LD_LIBRARY_PATH</code>，但<strong>不推荐永久使用</strong> </td></tr><tr><td> <strong>Shell级</strong> </td><td> <code>export LD_LIBRARY_PATH=/new/dir:$LD_LIBRARY_PATH</code> </td><td> 临时使用 </td></tr><tr><td> <strong>编译时</strong> </td><td> <code>gcc -Wl,-rpath,/dir</code> 或 <code>-Wl,-rpath,$ORIGIN/lib</code> </td><td> 写进可执行文件，最可靠 </td></tr></tbody></table><h5 id="ldlibrarypath-"><code>LD_LIBRARY_PATH</code> 应当只临时使用。</h5><p><code>LD_LIBRARY_PATH</code> 不应永久写入 Shell 配置。它会覆盖 ldconfig 缓存和默认路径，可能导致系统工具加载到错误版本的库（比如你的 <code>LD_LIBRARY_PATH</code> 里有个旧版 <code>libssl</code>，系统 <code>curl</code> 就可能加载它然后崩掉）。</p><h5 id="rpath-vs-runpath-"><code>RPATH</code> vs <code>RUNPATH</code> 行为不同。</h5><p><code>RPATH</code> 在 <code>LD_LIBRARY_PATH</code> 之前搜索（可被 RPATH 覆盖），<code>RUNPATH</code> 在之后搜索（LD<em>LIBRARY</em>PATH 可覆盖 RUNPATH）。现代 binutils 默认用 <code>RUNPATH</code>。如果你需要编译时路径覆盖环境变量，要显式用 <code>-Wl,--disable-new-dtags</code>（强制 RPATH）——但这通常不是你想要的。</p><h5 id="origin--rpath-"><code>$ORIGIN</code> 是一个特殊的 rpath 值，展开为可执行文件所在目录。</h5><p>用于可移植部署（可执行文件和库放同一目录树），但 <strong><code>$ORIGIN</code> 必须写进 rpath 字面量</strong>，不能在 Shell 里展开——gcc 命令中要写成：</p><pre class="language-bash lang-bash"><code class="language-bash lang-bash">gcc -Wl,-rpath,&#x27;$ORIGIN/lib&#x27;  main.c  # 单引号保护 $ORIGIN 不被 Shell 展开
</code></pre>
<hr/><h3 id="macos-">macOS 动态库搜索</h3><p><strong>搜索顺序：</strong></p><table><thead><tr><th> 顺序 </th><th> 来源 </th><th> 说明 </th></tr></thead><tbody><tr><td> 1 </td><td> <code>DYLD_LIBRARY_PATH</code> / <code>DYLD_FRAMEWORK_PATH</code> </td><td> 环境变量覆盖（由于SIP， 对系统进程无效） </td></tr><tr><td> 2 </td><td> <code>@rpath</code> 展开 </td><td> 可执行文件中 <code>LC_RPATH</code> 指定的目录列表 </td></tr><tr><td> 3 </td><td> <code>@loader_path</code> / <code>@executable_path</code> </td><td> 相对于加载者/主程序的目录 </td></tr><tr><td> 4 </td><td> install name 绝对路径 </td><td> 库自身声明的路径 </td></tr><tr><td> 5 </td><td> <code>DYLD_FALLBACK_LIBRARY_PATH</code> </td><td> 默认 <code>/usr/local/lib:/usr/lib</code> </td></tr></tbody></table><p><strong>macOS 的 <code>@</code> 路径魔术——这是理解 macOS 动态库的关键：</strong></p><table><thead><tr><th> 语法 </th><th> 展开为 </th><th> 用途 </th></tr></thead><tbody><tr><td> <code>@executable_path</code> </td><td> 主程序所在目录 </td><td> 主程序找自己的库 </td></tr><tr><td> <code>@loader_path</code> </td><td> 加载此库的那个文件所在目录 </td><td> 库找自己依赖的库（递归场景） </td></tr><tr><td> <code>@rpath</code> </td><td> 可执行文件中 <code>LC_RPATH</code> 列表中的目录 </td><td> 最灵活——主程序定义搜索列表，库只需声明&quot;在 rpath 里找我&quot; </td></tr></tbody></table><p>macOS 的设计哲学：<strong>库的搜索路径编译进库本身</strong>（install name），而不是依赖环境变量。库的 install name 通常形如 <code>@rpath/libfoo.dylib</code>，加载时 dyld 用主程序的 rpath 列表展开 <code>@rpath</code> 为具体目录。</p><p><strong>设置方法：</strong></p><table><thead><tr><th> 层级 </th><th> 方法 </th><th> 说明 </th></tr></thead><tbody><tr><td> <strong>系统级</strong> </td><td> 无环境变量机制 </td><td> macOS 设计上不支持——靠编译时嵌入 </td></tr><tr><td> <strong>Shell级</strong> </td><td> <code>export DYLD_LIBRARY_PATH=/dir</code> </td><td> 临时用，<strong>SIP会对系统进程剥离此变量</strong> </td></tr><tr><td> <strong>编译时</strong> </td><td> <code>gcc -Wl,-rpath,/dir</code> 或 <code>install_name_tool</code> </td><td> 最可靠的方式 </td></tr><tr><td> <strong>修改已编译库</strong> </td><td> <code>install_name_tool -change old_path new_path libfoo.dylib</code> </td><td> macOS 独有，改库的 install name 不需要重新编译 </td></tr></tbody></table>
<p>Homebrew 安装的库使用绝对路径 install name（<code>/opt/homebrew/lib/libfoo.dylib</code> 或 <code>/usr/local/lib/libfoo.dylib</code>），Homebrew 在编译时通过 <code>-rpath</code> 让可执行文件能找到它们。如果你<strong>手动编译程序链接 Homebrew 库</strong>，必须传：</p><pre class="language-bash lang-bash"><code class="language-bash lang-bash">gcc -L/opt/homebrew/lib -Wl,-rpath,/opt/homebrew/lib main.c -lfoo
</code></pre>
<p>漏了 <code>-rpath</code> 就能编译但运行时找不到库——这是 macOS 上 <code>dyld: Library not loaded</code> 报错的第一大原因。</p><h5 id="installnametool--macos-"><code>install_name_tool</code> 是 macOS 独有的神器：</h5><p>可以修改已编译 <code>.dylib</code> 的 install name 和已编译可执行文件的 rpath，<strong>不需要重新编译</strong>：</p><pre class="language-zsh lang-zsh"><code class="language-zsh lang-zsh"># 修改库声明自己的路径
install_name_tool -id @rpath/libfoo.dylib libfoo.dylib

# 修改可执行文件中对某库的引用路径
install_name_tool -change /old/path/libfoo.dylib @rpath/libfoo.dylib myapp

# 给可执行文件追加 rpath
install_name_tool -add_rpath /opt/homebrew/lib myapp
</code></pre>
<hr/><p><strong>经验交流</strong>：每个平台的环境和动态库问题都有大量隐坑，尤其是 Windows 的 DLL 搜索顺序和 macOS 的 install name 体系——如果你有踩坑经验或补充，欢迎评论区分享。</p></div><p style="text-align:right"><a href="https://me.bcw2.top:3443/posts/tech/environment-variable-for-command-line-interface#comments">看完了？说点什么呢</a></p></div>]]></description><link>https://me.bcw2.top:3443/posts/tech/environment-variable-for-command-line-interface</link><guid isPermaLink="true">https://me.bcw2.top:3443/posts/tech/environment-variable-for-command-line-interface</guid><dc:creator><![CDATA[bcw222]]></dc:creator><pubDate>Sun, 19 Apr 2026 05:35:32 GMT</pubDate></item><item><title><![CDATA[【系统化速查表 第〇弹】序言]]></title><description><![CDATA[<div><blockquote>该渲染由 Shiro API 生成，可能存在排版问题，最佳体验请前往：<a href="https://me.bcw2.top:3443/posts/tech/systematic-cheat-sheet-prelogue">https://me.bcw2.top:3443/posts/tech/systematic-cheat-sheet-prelogue</a></blockquote><div><h2 id="">是什么</h2><p>本系列博文旨在解决就事论事解决问题不全面系统，翻书查教程冗长且可能过时的痛点，将凝练的知识系统性地组织起来，供入门用户建立认知框架，高级用户预知经验坑点。</p><h2 id="">为什么</h2><p>博主深受其害：</p><ul><li>碎片化搜答案：一个自以为是特例的报错，翻了半天社区讨论终于修好了，长舒一口气顺手清了浏览历史，结果没过几天同方向的问题又出现了，本可以复用的基础搜索又得全部重来；</li><li>硬啃完整教程：想用个项目，结果单开一个网页的 Wiki / Documention 翻半天找不到要用的点，回 GitHub 文档仓库一搜发现压根没写，定睛一看原来维护者在 issue 里说 not-planned</li></ul><p>对于低频场景，稍微花点时间跳读一下可能无伤大雅；但当连自己都注意到了经常在做无用功，那不妨跟我一起总结经验教训。</p><h2 id="">怎么办</h2><ul><li>聚焦坑点：不再把坑点当补充，而是作为正文主要部分</li><li>标记时间：博文自带最后修改时间标记，可能过时的提醒你谨慎阅读</li></ul><h2 id="">适合谁</h2><p><strong>适合：</strong></p><ul><li><strong>速查的高级用户</strong>：直接翻目录或 Ctrl+F，找你要的那节，参数表格和命令示例都在</li><li><strong>想系统了解的小白</strong>：从头读，每个参数都有解释</li></ul><p><strong>不适合：</strong></p><ul><li><p><strong>想看保姆式教程的人</strong>：场景千变万化，要全列出来这就不是速查表了；但根因殊途同归，本系列博文尽管没法告诉你怎么做但至少尽可能教会你为什么；</p></li><li><p><strong>只想要不二法门的人</strong>：授人以鱼不如授人以渔，适合的才是最好的，天天看杠精叫嚣“诶你这个通用解决方案不如我在我自己特定场景下调整过的在我自己的特定场景下的表现更好”有意思吗？与其给你个大多数时候够用的通用解决方案，不如教会你怎么针对自己的特定场景优化。</p></li></ul><h2 id="">说在最后</h2><p>作为经验交流，博主个人的经验总是有限的，在这里也鼓励大家在评论区讨论分享自己的经验之谈。</p></div><p style="text-align:right"><a href="https://me.bcw2.top:3443/posts/tech/systematic-cheat-sheet-prelogue#comments">看完了？说点什么呢</a></p></div>]]></description><link>https://me.bcw2.top:3443/posts/tech/systematic-cheat-sheet-prelogue</link><guid isPermaLink="true">https://me.bcw2.top:3443/posts/tech/systematic-cheat-sheet-prelogue</guid><dc:creator><![CDATA[bcw222]]></dc:creator><pubDate>Sun, 19 Apr 2026 02:50:00 GMT</pubDate></item><item><title><![CDATA[【系统化速查表 第一弹】FFmpeg 视频二压实战指南]]></title><description><![CDATA[<div><blockquote>该渲染由 Shiro API 生成，可能存在排版问题，最佳体验请前往：<a href="https://me.bcw2.top:3443/posts/tech/ffmpeg-video-compress-practical-guide">https://me.bcw2.top:3443/posts/tech/ffmpeg-video-compress-practical-guide</a></blockquote><div><h2 id="">谁应该看这篇文章？</h2><p><strong>适合：</strong></p><ul><li><strong>速查的高级用户</strong>：直接翻目录或 Ctrl+F，找你要的那节，参数表格和命令示例都在</li><li><strong>想系统了解的小白</strong>：从头读，每个参数都有解释，假定你不熟悉命令行</li></ul><p><strong>不适合：</strong></p><ul><li><p><strong>想抬杠的</strong>：关于哪种编码器在哪种内容下 VMAF 更高，这个问题没有金标准。根据信息论的基本原理，不可能存在一种编码既无损画质又极低码率——一切都是 trade-off，只能对特定视频源做 VMAF 实测才能得出有效结论。如果你热衷这类深度分析，出门左转这篇文章更适合你：<a href="https://zhuanlan.zhihu.com/p/1913258114746122747">视频压缩深度测试（知乎）</a></p></li><li><p><strong>只盯着存储成本的人</strong>：除了存储成本，还有——<strong>编码时间成本</strong>（压个视频让电脑满载几百个小时值不值）、<strong>解码平台限制成本</strong>（目标设备不支持AV1还得转回来再看）、<strong>编解码电费成本</strong>（显卡满载的功耗感人）、<strong>调参时间成本</strong>（每种不同的视频源都得反复测 VMAF 和调参）。存储是成本，这些也是成本，甚至还有<strong>你看这篇文章本身的时间成本</strong>，都应该考虑在内。</p></li></ul><hr/><h2 id="-ffmpeg-cli-gui-">为什么用 FFmpeg CLI，而不是 GUI 工具？</h2><p>市面上有很多 GUI 封装工具——小丸、格式工厂、ShanaEncoder、HandBrake、FFmpegFreeUI 等等，它们的本质大多是给 FFmpeg 套了个图形界面，把你在界面里选的参数翻译成 FFmpeg 命令在后台运行。尽管并非所有 GUI 工具都基于 FFmpeg（例如 HandBrake 部分功能直接调用显卡厂商 SDK），但功能上大差不差。</p><p>GUI 工具的问题：</p><ul><li><strong>参数覆盖不全</strong>：GUI 不可能把 FFmpeg 所有参数都列出来，总有你需要但界面没有的选项</li><li><strong>停更风险与沉没成本</strong>：很多小工具停更了你就傻了，学会的操作逻辑也迁移不走；FFmpeg 本身几乎不可能停更，它已经是音视频处理领域的基础设施级的存在了，掌握了就是你的</li><li><strong>自由度受限</strong>：脚本化、批量处理、条件判断，CLI 天然支持，GUI 永远差一口气</li></ul><p>并不是说 GUI 工具不好，它们的确很方便（我自己也经常用 LosslessCut）；但掌握 FFmpeg CLI 之后，GUI 工具对你来说就指是&quot;便利品&quot;，而不是&quot;必需品&quot;，你永远有托底的杀手锏。</p><hr/><h2 id="-ffmpeg">安装 FFmpeg</h2><p>请直接参考官方指南 <a href="https://ffmpeg.org/download.html">Download FFmpeg</a></p><p>如果用包管理器装，环境变量应该会自动配好，直接运行 <code>ffmpeg</code> 就可以；如果直接下载静态构建手动安装，记得手动添加<a href="https://me.bcw2.top:3443/posts/tech/environment-variable-for-command-line-interface">环境变量</a>或把命令中的 <code>ffmpeg</code> 改成相应路径。</p><p><strong>验证安装：</strong> 打开终端或命令提示符，输入 <code>ffmpeg -version</code>，能看到版本信息就成功了。</p><hr/><h2 id="">一、基础概念</h2><p>在输参数之前，有几个概念必须搞清楚。</p><h3 id="11--vs-">1.1 容器格式 vs 编码格式</h3><p>我们平时说的&quot;MP4 文件&quot;，严格来说由两层组成：</p><ul><li><strong>容器格式（封装格式）</strong>：文件的&quot;外壳&quot;，即文件扩展名所代表的格式。常见的有 <code>.mp4</code>、<code>.mkv</code>、<code>.mov</code>。容器负责把视频流、音频流、字幕流&quot;装在一起&quot;</li><li><strong>编码格式</strong>：视频流本身的压缩标准。同样是 <code>.mp4</code> 容器，里面的视频流可能是 H.264，也可能是 H.265，或者 AV1</li></ul><p>&quot;这是个 MP4&quot;只说明了容器，没说明编码。用 <code>ffprobe</code>（FFmpeg 三件套之一，和 <code>ffplay</code> 一样应该会一起装好）可以直接查看文件的元信息。</p><h3 id="12-">1.2 编码格式与编码器</h3><p><strong>编码格式</strong>只是标准，<strong>编码器</strong>才是执行者（按照标准把视频数据实际编码出来）。</p><p>常用对照表：</p><table><thead><tr><th> 编码格式 </th><th> CPU 软件编码器 </th><th> NVIDIA GPU </th><th> Intel GPU </th><th> AMD GPU </th></tr></thead><tbody><tr><td> H.264 </td><td> <code>libx264</code> </td><td> <code>h264_nvenc</code> </td><td> <code>h264_qsv</code> </td><td> <code>h264_amf</code> </td></tr><tr><td> H.265 (HEVC) </td><td> <code>libx265</code> </td><td> <code>hevc_nvenc</code> </td><td> <code>hevc_qsv</code> </td><td> <code>hevc_amf</code> </td></tr><tr><td> AV1 </td><td> <code>libsvtav1</code> </td><td> <code>av1_nvenc</code>（40系+） </td><td> <code>av1_qsv</code>（Arc/14代核显+） </td><td> <code>av1_amf</code>（RX7000+） </td></tr></tbody></table><p>带 <code>lib</code> 前缀的是纯软件编码器（用 CPU 计算），其余为硬件编码器（调用显卡内专用编解码单元）。</p><p><strong>关于显卡编码速度的常见误解</strong>：显卡的&quot;3D 游戏性能&quot;与视频编码速度几乎无关。NVIDIA 的 NVENC 编码单元是独立的专用硬件——4060 和 4090 在单个编码任务上速度基本一致，因为两者都只有一两个 NVENC 单元，是 NVENC 单元数量和代际决定速度，而不是 CUDA 核心数量。</p><h3 id="13-">1.3 码率（比特率）</h3><p>码率 = 视频每秒传输的数据量，单位是 kbps（千比特/秒）或 Mbps（兆比特/秒）。</p><ul><li>1080p 普通内容（教程、生活录像）：1-3 Mbps 通常够用</li><li>1080p 高动态游戏录屏：3-6 Mbps</li><li>4K 内容：8-20 Mbps</li></ul><h3 id="14-">1.4 像素格式</h3><ul><li><code>yuv420p</code>：8bit，兼容性最广，几乎所有设备都支持</li><li><code>yuv420p10le</code>：10bit，色彩过渡更细腻，低码率下反而质量可能更好（不平滑的色彩过渡本身就是高频噪声会浪费码率），用 HEVC/AV1 且设备兼容时推荐</li></ul><hr/><h2 id="">二、命令行基础语法</h2><h3 id="21-">2.1 命令的基本结构</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 输入文件.mp4 [各种参数] 输出文件.mp4
</code></pre>
<ul><li><code>ffmpeg</code>：调用 FFmpeg 程序</li><li><code>-i</code>：<strong>input</strong> 的缩写，指定输入文件，后面跟文件路径</li><li>各种参数：每个选项都是 <code>-参数名 参数值</code> 的格式</li><li>最后一个路径（没有 <code>-</code> 开头的参数在前）通常是输出文件</li></ul><h3 id="22-bash--powershell">2.2 反斜杠换行（bash / PowerShell）</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 输入.mp4 \
  -c:v hevc_nvenc \
  -c:a copy     \
  输出.mp4
</code></pre>
<p>行末的 <code>\</code> 表示&quot;这行还没写完，下一行是接续&quot;。这只是为了让很长的命令看起来更清晰，效果完全等同于写成一行。</p><p>Windows CMD 中不能用 <code>` 换行，要用 </code>^<code>；Powershell 要用</code><code> </code> ``，且前面要由空格；本文示例统一用 bash 格式，Windows 用户请自行替换，或者不换行直接写成一行。</p><h3 id="23-">2.3 环境变量</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash"># bash 写法（Linux / macOS / Git Bash）
export VIDEO=&quot;https://example.com/video.mp4?sign=abc123xyz&quot;
ffmpeg -i &quot;$VIDEO&quot; 输出.mp4

# PowerShell 写法
$VIDEO = &quot;https://example.com/video.mp4?sign=abc123xyz&quot;
ffmpeg -i &quot;$VIDEO&quot; 输出.mp4
</code></pre>
<p><code>export VIDEO=&quot;...&quot;</code> 是 bash 中<strong>设置变量</strong>的写法。之后用 <code>$VIDEO</code>，shell 会自动把它替换成那段 URL，避免重复输入。这个变量只在当前终端窗口的会话中有效，关闭窗口就消失了。</p><h3 id="24--gpu">2.4 指定使用哪块 GPU</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">CUDA_VISIBLE_DEVICES=1 ffmpeg -i 输入.mp4 -c:v hevc_nvenc 输出.mp4
</code></pre>
<p><code>CUDA_VISIBLE_DEVICES=1</code> 是一个<strong>临时环境变量</strong>，写在命令前，只对这条命令生效。它告诉 CUDA 只把编号为 <code>1</code> 的 GPU（编号从 0 开始）暴露给这个进程，FFmpeg 就只能用那块显卡。多卡机器分配任务时使用。</p><hr/><h2 id="">三、最基础的输入输出</h2><h3 id="31-">3.1 查看文件信息</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffprobe 文件.mp4
</code></pre>
<p>FFProbe 会打印输入文件的元信息（分辨率、帧率、编码格式、时长、码率等），然后退出。这是快速查看文件信息的常用方式。</p><h3 id="32-">3.2 直接转封装（不重新编码）</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 输入.mkv -c copy 输出.mp4
</code></pre>
<ul><li><code>-c</code>：<strong>codec</strong>，指定编码器</li><li><code>copy</code>：对该流直接复制（不重编码）</li><li>速度极快，因为只是换了个容器&quot;壳&quot;，但仅适用于目标容器兼容源编码的情形，否则会报错。</li></ul><h3 id="33-">3.3 覆盖已有文件</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 输入.mp4 -c:v hevc_nvenc -c:a copy -y 输出.mp4
</code></pre>
<ul><li><code>-y</code>：如果输出文件已存在，<strong>直接覆盖</strong>，不交互询问。不加这个参数时，FFmpeg 会停下来问你要不要覆盖</li></ul><h3 id="34-">3.4 指定编码器</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 输入.mp4 -c:v hevc_nvenc -c:a copy 输出.mp4
</code></pre>
<ul><li><code>-c</code>：<strong>codec</strong>，指定编码器</li><li><code>:v</code>：<strong>video</strong>，对视频</li><li><code>:a</code>：<strong>audio</strong>，对音频</li><li><code>copy</code>：对该流直接复制（不重编码）</li></ul><h3 id="35-">3.5 指定码率</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 输入.mp4 -c:v hevc_nvenc -b:v 2M -c:a copy 输出.mp4
</code></pre>
<ul><li><code>-b</code>：<strong>bitrate</strong>，指定码率</li><li><code>2M</code>: 2Mbps，<code>M</code> 大写</li></ul><hr/><h2 id="">四、时间裁剪与剪切</h2><p>这是高频操作，和编码参数无关，单独讲清楚。</p><h3 id="41-">4.1 时间参数说明</h3><table><thead><tr><th> 参数 </th><th> 含义 </th></tr></thead><tbody><tr><td> <code>-ss 30</code> </td><td> 从第 30 秒<strong>开始</strong>处理（也支持 <code>00:00:30</code> 格式） </td></tr><tr><td> <code>-to 120</code> </td><td> 输出<strong>截止到</strong>第 120 秒（相对于输入文件起点） </td></tr><tr><td> <code>-t 90</code> </td><td> 输出<strong>持续 90 秒</strong> </td></tr></tbody></table><p><code>-to</code> 和 <code>-t</code> 的区别：<code>-ss 30 -to 120</code> 等于 <code>-ss 30 -t 90</code>，结果一样（从 30s 到 120s，共 90s），但逻辑语义不同，按需选用。</p><h3 id="42-">4.2 关键帧剪切（快，不精确）</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -ss 30 -i 输入.mp4 -to 90 -c copy 输出.mp4
</code></pre>
<p><strong><code>-ss</code> 放在 <code>-i</code> 前面</strong>：FFmpeg 先快速 seek 到靠近 30s 的关键帧，然后再开始解码。由于关键帧不一定恰好在 30s，实际起点可能略早几秒。</p><p><code>-c copy</code> 直接复制流，<strong>不重新编码</strong>，速度接近瞬间完成，适合快速提取片段。</p><h3 id="43-">4.3 重编码剪切（精确，较慢）</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 输入.mp4 -ss 30 -to 120 -c:v hevc_nvenc -c:a aac 输出.mp4
</code></pre>
<p><strong><code>-ss</code> 放在 <code>-i</code> 后面</strong>：FFmpeg 从头解码，精确丢弃前 30s，起点<strong>精确到帧</strong>。代价是需要重新编码，速度相应变慢。</p><p>适合场景：本身就要重压缩的任务（剪切和二压合并成一步，不需要先切再压）。</p><blockquote><p><strong>建议</strong>：如果本来就要二压，直接用 <code>-ss</code>（放 <code>-i</code> 后）同时做剪切和压缩，一步到位。如果只是想快速切片段留着，用 <code>-c copy</code>，秒出结果，精度够用就行。</p></blockquote>
<hr/><h2 id="">五、视频参数</h2><h3 id="51-">5.1 分辨率缩放</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 输入.mp4 -vf &quot;scale=-2:1080&quot; 输出.mp4
</code></pre>
<ul><li><code>-vf</code>：<strong>video filter</strong>，视频滤镜，后面跟引号包裹的滤镜表达式</li><li><code>scale=-2:1080</code>：缩放滤镜
<ul><li>第一个值 <code>-2</code>：宽度<strong>自动计算</strong>，保持原始宽高比，且结果能被 2 整除（编码器要求）</li><li>第二个值 <code>1080</code>：目标高度 1080 像素</li><li>写 <code>scale=1920:1080</code> 则强制指定宽和高（不保证宽高比）</li><li>写 <code>scale=1280:-2</code> 则固定宽，高度自动算</li></ul></li></ul><h3 id="52-">5.2 帧率</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 输入.mp4 -r 30 输出.mp4
</code></pre>
<ul><li><code>-r</code>：设置输出帧率（Frames Per Second）。不加 <code>-r</code> 则保持输入帧率</li></ul><h3 id="53-">5.3 像素格式</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 输入.mp4 -pix_fmt yuv420p10le 输出.mp4
</code></pre>
<ul><li><code>-pix_fmt</code>：<strong>pixel format</strong>
<ul><li><code>yuv420p</code>：8bit，兼容性最好，保守推荐</li><li><code>yuv420p10le</code>：10bit，HEVC/AV1 推荐</li></ul></li></ul><hr/><h2 id="">六、音频参数</h2><h3 id="61-">6.1 直接复制音频（最推荐）</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">-c:a copy
</code></pre>
<p>源文件音频质量没问题的情况下，优先复制，无质量损失，而且快。</p><h3 id="62-">6.2 重新编码音频</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 输入.mp4 -c:a aac -b:a 128k 输出.mp4
</code></pre>
<ul><li><code>-b:a 128k</code>：音频目标<strong>比特率</strong> 128 kbps</li></ul><p>常用音频编码器：</p><table><thead><tr><th> 编码器 </th><th> 特点 </th></tr></thead><tbody><tr><td> <code>aac</code> </td><td> 最通用，MP4 容器标配，128k-320k </td></tr><tr><td> <code>libopus</code> </td><td> 同质量体积最小，推荐用于 MKV </td></tr><tr><td> <code>flac</code> </td><td> 无损 </td></tr><tr><td> <code>libmp3lame</code> </td><td> MP3，兼容性好，用 <code>-q:a 0</code>（最佳质量） </td></tr><tr><td> <code>copy</code> </td><td> 直接复制，优先选这个 </td></tr></tbody></table><h3 id="63-">6.3 选择特定音轨</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 输入.mkv -map 0:v:0 -map 0:a:1 -c copy 输出.mp4
</code></pre>
<ul><li><code>-map</code>：手动指定要输出哪条流
<ul><li><code>0</code>：第 1 个输入文件（从 0 计数）</li><li><code>v:0</code>：该文件的第 1 条视频流</li><li><code>a:1</code>：该文件的第 2 条音频流（比如日语音轨）</li></ul></li><li>不加 <code>-map</code> 时，FFmpeg 自动选每种类型中&quot;最佳&quot;的一条</li></ul><hr/><h2 id="">七、码率控制模式</h2><p>这是视频编码最核心的部分，直接决定画质和文件大小。</p><h3 id="71-crf-cpu-">7.1 CRF 模式（CPU 编码器专用）</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 输入.mp4 -c:v libx265 -crf 24 -c:a copy 输出.mp4
</code></pre>
<ul><li><code>-crf</code>：<strong>Constant Rate Factor</strong>，恒定质量因子</li><li>数值越小画质越好文件越大，越大越糊越小</li><li>推荐范围：</li></ul><table><thead><tr><th> 编码器 </th><th> 推荐 CRF 范围 </th><th> 通用中点 </th></tr></thead><tbody><tr><td> <code>libx264</code> </td><td> 18-28 </td><td> 23-24 </td></tr><tr><td> <code>libx265</code> </td><td> 20-28 </td><td> 24-26 </td></tr><tr><td> <code>libsvtav1</code> </td><td> 28-38 </td><td> 32-34 </td></tr></tbody></table><blockquote><p><strong>重要</strong>：CRF 是 CPU 软件编码器专用，GPU 硬件编码器<strong>不支持 <code>-crf</code></strong>。</p></blockquote>
<h3 id="72-cq-nvenc-">7.2 CQ 模式（NVENC 的恒定质量）</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 输入.mp4 -c:v hevc_nvenc -rc:v vbr -cq:v 28 -b:v 0 -c:a copy 输出.mp4
</code></pre>
<ul><li><code>-rc:v vbr</code>：<strong>rate control</strong>，码率控制模式，<code>vbr</code> 是可变码率</li><li><code>-cq:v 28</code>：NVENC 的恒定质量值（类似 CRF，但数值对应关系不同）</li><li><code>-b:v 0</code>：目标码率设为 0，配合 CQ 使用，表示不额外限制码率</li></ul><p><strong>CQ 与 CRF 的对应关系（近似）</strong>：</p><pre class=""><code class="">libx264 -crf 24  ≈  h264_nvenc   -cq 28~30
libx265 -crf 24  ≈  hevc_nvenc   -cq 28~30
libsvtav1 -crf 34 ≈ av1_nvenc    -cq 34~36
</code></pre><blockquote>
<p>⚠️ <strong>最常见的误区</strong>：根据 CPU 编码 <code>-crf 24</code> 的经验，在用 GPU 编码时也选 <code>-cq 24</code>。这相当于 CPU 的 <code>-crf 18</code>（画质极好但文件极大），被误以为&quot;GPU 编码出来的文件比 CPU 还大&quot;——其实只是 CQ 值选错了。</p></blockquote>
<h3 id="73-global-qualityintel-qsv">7.3 Global Quality（Intel QSV）</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 输入.mp4 -c:v hevc_qsv -global_quality 24 -c:a copy 输出.mp4
</code></pre>
<ul><li>Intel QSV 的恒定质量参数，取值逻辑与 CPU CRF 更接近（24 对 24）</li></ul><h3 id="74-qp-amd-amf">7.4 QP 模式（AMD AMF）</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 输入.mp4 -c:v hevc_amf -qp_i 28 -qp_p 28 -c:a copy 输出.mp4
</code></pre>
<ul><li><code>-qp_i</code>：I 帧（关键帧）的量化参数</li><li><code>-qp_p</code>：P 帧的量化参数，通常与 <code>-qp_i</code> 设相同值</li></ul><h3 id="75-vbr-">7.5 VBR 码率模式（有明确码率上限时）</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 输入.mp4 -c:v hevc_nvenc \
  -rc:v vbr -b:v 2M -maxrate 3M -bufsize 5M \
  -c:a copy 输出.mp4
</code></pre>
<ul><li><code>-b:v 2M</code>：视频目标码率 <strong>2 Mbps</strong></li><li><code>-maxrate 3M</code>：<strong>最大码率</strong>上限，峰值不超过 3 Mbps</li><li><code>-bufsize 5M</code>：<strong>缓冲区大小</strong>，控制码率波动幅度（建议设为 maxrate 的 1.5-2 倍）</li><li>注意：<code>M</code> 必须大写，小写 <code>m</code> 在某些情况下不生效</li></ul><hr/><h2 id="nvenc-">八、NVENC 高级参数详解</h2><p>以下参数针对 NVIDIA 显卡编码（<code>hevc_nvenc</code> / <code>av1_nvenc</code>）。</p><h3 id="81-">8.1 编码预设与调优</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">-preset p7 -tune:v hq
</code></pre>
<ul><li><code>-preset</code>：<strong>编码速度预设</strong>，<code>p1</code>（最快）到 <code>p7</code>（最慢/最优质）。越慢压缩效率越高，同等画质文件更小。日常推荐 <code>p4</code>（平衡）到 <code>p7</code>（质量优先）</li><li><code>-tune:v hq</code>：调优目标为<strong>高质量</strong>（High Quality）；<code>ull</code> 是超低延迟（直播场景）；<code>lossless</code> 是无损</li></ul><h3 id="82-">8.2 多遍编码</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">-multipass 2
</code></pre>
<ul><li><code>-multipass 2</code>：两遍编码，第一遍分析，第二遍编码，压缩效率更高。编码时间增加约 30-50%，但文件更小或画质更好</li></ul><h3 id="83-">8.3 前瞻帧数</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">-rc-lookahead 32
</code></pre>
<ul><li><code>-rc-lookahead</code>：向前预看多少帧来辅助码率分配决策。范围 0-32，值越大压缩越好，速度越慢，推荐设 <code>32</code></li></ul><h3 id="84-aq">8.4 自适应量化（AQ）</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">-spatial-aq 1 -temporal-aq 1 -aq-strength 10
</code></pre>
<ul><li><code>-spatial-aq 1</code>：<strong>空间自适应量化</strong>，开启（<code>1</code>）后，根据画面区域复杂度分配码率——纹理丰富的地方多给，大面积纯色的地方少给</li><li><code>-temporal-aq 1</code>：<strong>时间自适应量化</strong>，根据帧间运动量分配码率——运动剧烈的帧多给</li><li><code>-aq-strength</code>：AQ 强度，范围 1-15，推荐 8-12；值过高会导致码率分配失衡</li></ul><h3 id="85-gop-">8.5 GOP 结构</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">-g 300 -bf 4 -b_ref_mode 2
</code></pre>
<ul><li><code>-g 300</code>：<strong>GOP 长度</strong>（关键帧间隔），每隔 300 帧放一个关键帧。GOP 越短，跳转越精确但压缩效率略降；推荐设为帧率 × 10（30fps 就设 300，60fps 就设 600）</li><li><code>-bf 4</code>：<strong>B 帧数量</strong>，范围 0-4。B 帧通过参考前后帧提升压缩效率，设 4 是上限</li><li><code>-b_ref_mode 2</code>：B 帧参考模式，<code>2</code> 是金字塔模式，压缩效率最高，推荐</li></ul><h3 id="86-">8.6 分块多单元编码</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">-split_encode_mode 2
</code></pre>
<ul><li><code>-split_encode_mode 2</code>：尝试调用多个 NVENC 编码单元（如 4090 有两个单元）。但实际调用几个取决于 FFmpeg 调度，不保证双倍速度</li></ul><h3 id="87-hevc-10bit-profile">8.7 HEVC 10bit Profile</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">-c:v hevc_nvenc -profile:v main10 -pix_fmt yuv420p10le
</code></pre>
<ul><li><code>-profile:v main10</code>：使用 HEVC Main10 Profile，支持 10bit 输出，必须配合 <code>-pix_fmt yuv420p10le</code> 使用</li></ul><hr/><h2 id="">九、硬件加速平台</h2><h3 id="nvidia-nvenc">NVIDIA NVENC</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash"># HEVC 10bit，质量优先
ffmpeg -i 输入.mp4 -c:v hevc_nvenc -pix_fmt yuv420p10le -profile:v main10 \
  -preset p7 -tune:v hq -rc:v vbr -cq:v 28 -b:v 0 \
  -multipass 2 -rc-lookahead 32 \
  -spatial-aq 1 -temporal-aq 1 -aq-strength 10 \
  -g 300 -bf 4 -b_ref_mode 2 \
  -c:a copy 输出.mp4

# AV1（需要 40 系及以上显卡）
ffmpeg -i 输入.mp4 -c:v av1_nvenc -pix_fmt yuv420p10le \
  -preset p7 -tune:v hq -rc:v vbr -cq:v 34 -b:v 0 \
  -multipass 2 -rc-lookahead 32 \
  -spatial-aq 1 -temporal-aq 1 -aq-strength 10 \
  -g 300 -bf 4 -b_ref_mode 2 \
  -c:a copy 输出.mp4
</code></pre>
<p>代际说明：16/20/30 系是第七代 NVENC；40 系是第八代，新增 AV1 硬编；50 系是第九代，速度略有提升。<strong>AV1 硬编是 40 系的重要分水岭。</strong></p><h3 id="intel-quick-sync-qsv">Intel Quick Sync (QSV)</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 输入.mp4 -c:v hevc_qsv -preset medium -global_quality 24 -c:a copy 输出.mp4
ffmpeg -i 输入.mp4 -c:v av1_qsv  -preset medium -global_quality 28 -c:a copy 输出.mp4
</code></pre>
<p>AV1 硬编需要 Intel Arc 独显或 Ultra 100 系列（14 代之后）核显。</p><h3 id="amd-amf">AMD AMF</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 输入.mp4 -c:v hevc_amf -preset balanced -qp_i 28 -qp_p 28 -c:a copy 输出.mp4
ffmpeg -i 输入.mp4 -c:v av1_amf  -preset balanced -qp_i 32 -qp_p 32 -c:a copy 输出.mp4
</code></pre>
<p>AV1 硬编需要 RX 7000 系列独显或 Ryzen 7000 系列的 700M 核显（如 7840H 的 780M）。</p><h3 id="apple-videotoolboxmacos">Apple VideoToolbox（macOS）</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 输入.mp4 -c:v h264_videotoolbox 输出.mp4
ffmpeg -i 输入.mp4 -c:v hevc_videotoolbox -profile:v main10 输出.mp4
</code></pre>
<p>M 系列芯片支持 H.264 和 HEVC 硬编。AV1 编码支持情况视 FFmpeg 版本而定，请查阅对应版本文档。</p><hr/><h2 id="hev1--hvc1">十、hev1 与 hvc1：播放兼容性的那个坑</h2><p>HEVC 视频在 MP4/MOV 容器中有两种打包标签，影响播放兼容性：</p><ul><li><strong><code>hvc1</code></strong>：参数集（SPS/PPS）内联在容器 Sample Entry 中，视频开头一次性写入</li><li><strong><code>hev1</code></strong>：参数集可散布在码流中，每隔一段重复</li></ul><p><strong>FFmpeg 默认生成 <code>hev1</code>。</strong> 如需改为兼容性更好的 <code>hvc1</code>：</p><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 输入.mp4 -c:v hevc_nvenc -tag:v hvc1 -c:a copy 输出.mp4
</code></pre>
<ul><li><code>-tag:v hvc1</code>：强制将视频流的 codec tag 设为 <code>hvc1</code></li></ul><p><strong>各平台兼容情况：</strong></p><table><thead><tr><th> 平台 / 场景 </th><th> <code>hvc1</code> </th><th> <code>hev1</code> </th></tr></thead><tbody><tr><td> Windows Media Foundation </td><td> ✅ </td><td> ✅ </td></tr><tr><td> iOS / Safari（部分版本） </td><td> ✅ </td><td> ⚠️ 可能失败 </td></tr><tr><td> 大多数 Android </td><td> ✅ </td><td> ✅ </td></tr><tr><td> Web 播放器 </td><td> ✅ 推荐 </td><td> ⚠️ 兼容性略差 </td></tr><tr><td> VLC / PotPlayer </td><td> ✅ </td><td> ✅ </td></tr></tbody></table><p><strong>来自实际踩坑的建议</strong>：视频要在 iOS/Safari 播放，或者上传到 Web 播放器，加 <code>-tag:v hvc1</code>。用 <code>hevc_nvenc</code> 等硬件编码器有时会自动用 <code>hvc1</code>，用 <code>libx265</code> 软编默认是 <code>hev1</code>，最稳是<strong>显式指定</strong>。</p><hr/><h2 id="url--url-">十一、URL 作为输入，以及为什么不建议 URL 输出</h2><h3 id="111-url-">11.1 URL 输入</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i &quot;https://example.com/video.mp4?sign=abc123&amp;token=xyz&quot; 输出.mp4
</code></pre>
<p><strong>为什么 URL 必须加引号？</strong> URL 中常含 <code>?</code>、<code>&amp;</code>、<code>=</code> 等字符，在 shell 里有特殊含义（<code>&amp;</code> 表示后台运行命令）。不加引号，shell 会在 FFmpeg 看到这个 URL 之前就把它&quot;拆解&quot;，导致参数传错或命令直接出错。</p><p>规则：<strong>URL 永远加双引号</strong>。</p><p>使用环境变量存储 URL（避免反复复制粘贴超长签名链接）：</p><pre class="language-bash lang-bash"><code class="language-bash lang-bash">export VIDEO=&quot;https://your-server.com/path/to/video.mp4?sign=very_long_token_here&quot;
ffmpeg -i &quot;$VIDEO&quot; -to 120 -c:v hevc_nvenc -c:a copy 输出.mp4
</code></pre>
<p>注意 <code>&quot;$VIDEO&quot;</code> 也要有引号，防止变量本身展开后的内容被 shell 解析。</p><h3 id="112--url-">11.2 为什么不建议 URL 输出</h3><p>FFmpeg 写入 MP4 文件时，必须在编码完成后<strong>回到文件开头写入 moov box</strong>（元数据，包含帧索引等信息），这个操作需要输出目标支持随机写入（seek）。</p><p>WebDAV 等网络存储协议通常<strong>不支持 seek 写入</strong>，会导致写入失败，或者输出的文件能写进去但无法正常播放。</p><p><strong>结论</strong>：</p><ul><li><strong>输入</strong>：URL 可以用，注意加引号</li><li><strong>输出</strong>：强烈建议输出到本地文件，完成后再上传。如果一定要直出，可以改用 MKV 格式（MKV 对 seek 要求更低），但兼容性不如 MP4</li></ul><hr/><h2 id="">十二、视频变速</h2><h3 id="121---">12.1 视频加速 / 减速</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 输入.mp4 -vf &quot;setpts=0.5*PTS&quot; -c:a copy 输出.mp4
</code></pre>
<ul><li><code>setpts</code>：修改视频帧的<strong>显示时间戳</strong>（Presentation TimeStamp）</li><li><code>setpts=0.5*PTS</code>：时间戳乘以 0.5 → 视频 <strong>2 倍速</strong></li><li><code>setpts=2.0*PTS</code>：时间戳乘以 2 → 视频 <strong>0.5 倍速</strong>（慢动作）</li><li>规律：<code>setpts = (1 ÷ 目标倍速) × PTS</code></li></ul><h3 id="122---">12.2 音频加速 / 减速</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 输入.mp4 -c:v copy -af &quot;atempo=2.0&quot; 输出.mp4
</code></pre>
<ul><li><code>atempo=2.0</code>：音频 2 倍速</li><li><code>atempo=0.5</code>：音频 0.5 倍速</li><li>⚠️ <code>atempo</code> 取值范围是 <strong>0.5-2.0</strong>，超出范围需要串联多个：</li></ul><pre class="language-bash lang-bash"><code class="language-bash lang-bash"># 4 倍速音频（超出范围，串联两个 atempo）
-af &quot;atempo=2.0,atempo=2.0&quot;
</code></pre>
<h3 id="123-">12.3 音视频同步变速</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 输入.mp4 \
  -vf &quot;setpts=0.5*PTS&quot; \
  -af &quot;atempo=2.0&quot; \
  -c:v libx265 -crf 24 \
  输出.mp4
</code></pre>
<p>同时调整音视频速度，保持同步。注意变速后视频流发生了改变，不能用 <code>-c:v copy</code>。</p><hr/><h2 id="">十三、视频拼接与合并</h2><h3 id="131-">13.1 文件列表拼接法（推荐）</h3><p>新建文本文件 <code>list.txt</code>：</p><pre class=""><code class="">file &#x27;片段1.mp4&#x27;
file &#x27;片段2.mp4&#x27;
file &#x27;片段3.mp4&#x27;
</code></pre>
<pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -f concat -safe 0 -i list.txt -c copy 输出.mp4
</code></pre>
<ul><li><code>-f concat</code>：使用 concat <strong>解复用器</strong>，按列表顺序读取文件</li><li><code>-safe 0</code>：允许列表中使用相对路径（默认出于安全只允许绝对路径）</li><li><code>-c copy</code>：直接复制流，不重编码，速度极快</li></ul><p><strong>前提</strong>：所有片段的编码格式、分辨率、帧率必须完全一致，否则拼接结果会花屏或音视频不同步。格式不一致时，去掉 <code>-c copy</code> 改为指定编码器重新编码，代价是时间更长。</p><h3 id="132-filtercomplex-concat-">13.2 filter_complex concat 滤镜（格式不一致或需要重编码时）</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 片段1.mp4 -i 片段2.mp4 -i 片段3.mp4 \
  -filter_complex &quot;[0:v][0:a][1:v][1:a][2:v][2:a]concat=n=3:v=1:a=1[outv][outa]&quot; \
  -map &quot;[outv]&quot; -map &quot;[outa]&quot; \
  -c:v hevc_nvenc -cq:v 28 -rc:v vbr -b:v 0 -c:a aac \
  输出.mp4
</code></pre>
<ul><li><code>-filter_complex</code>：<strong>复杂滤镜图</strong>，支持多输入处理</li><li><code>[0:v][0:a]</code>：第 1 个输入文件的视频流和音频流（方括号是标签，用于在滤镜图中引用）</li><li><code>[1:v][1:a]</code>：第 2 个输入文件……以此类推</li><li><code>concat=n=3:v=1:a=1</code>：拼接 <code>n=3</code> 段，输出 <code>v=1</code> 条视频流和 <code>a=1</code> 条音频流</li><li><code>[outv][outa]</code>：给滤镜输出命名，供后续 <code>-map</code> 引用</li><li><code>-map &quot;[outv]&quot;</code> 和 <code>-map &quot;[outa]&quot;</code>：选这两条流作为最终输出</li></ul><h3 id="133-">13.3 时长不同时的对齐处理</h3><p><strong>场景：音频比视频短，补静音至视频时长</strong></p><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 视频.mp4 -i 音频.mp3 \
  -filter_complex &quot;[1:a]apad[a]&quot; \
  -map 0:v -map &quot;[a]&quot; \
  -shortest \
  输出.mp4
</code></pre>
<ul><li><code>apad</code>：对音频末尾<strong>填充静音</strong></li><li><code>-shortest</code>：以最短流为准截断输出，防止无限延伸</li></ul><p><strong>场景：填充到指定时长</strong></p><pre class="language-bash lang-bash"><code class="language-bash lang-bash">-filter_complex &quot;[1:a]apad=whole_dur=300[a]&quot;
</code></pre>
<ul><li><code>whole_dur=300</code>：音频填充/截断到恰好 300 秒</li></ul><p><strong>场景：视频比音频短，补黑帧</strong></p><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 视频.mp4 \
  -vf &quot;tpad=stop=-1:stop_mode=black&quot; \
  -af &quot;apad&quot; \
  -c:v libx265 -crf 24 \
  输出.mp4
</code></pre>
<ul><li><code>tpad=stop=-1:stop_mode=black</code>：在视频末尾补充黑帧（<code>-1</code> 表示无限补，配合 <code>-shortest</code> 或音频时长决定何时停止）</li><li><code>stop_mode=clone</code>：改为复制最后一帧（冻结画面）</li></ul><hr/><h2 id="vmaf-">十四、VMAF 质量评估</h2><p>VMAF（Video Multimethod Assessment Fusion）是 Netflix 开发的视频质量客观评估标准，模拟人眼感知，分数 0-100，<strong>95 分以上</strong>通常认为肉眼几乎无法区分与原片的差别。已内置在 FFmpeg 中，无需额外安装。</p><h3 id="141-">14.1 基本用法</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 压缩后.mp4 -i 原始视频.mp4 \
  -filter_complex &quot;[0:v][1:v]libvmaf=model=version=vmaf_v0.6.1:log_path=&#x27;vmaf_log.txt&#x27;:n_threads=20&quot; \
  -f null -
</code></pre>
<ul><li><code>[0:v]</code>：<strong>被测</strong>视频（压缩后）</li><li><code>[1:v]</code>：<strong>参考</strong>视频（原始），顺序不能搞反</li><li><code>model=version=vmaf_v0.6.1</code>：使用内置的 1080p 模型</li><li><code>log_path=&#x27;vmaf_log.txt&#x27;</code>：结果写入日志文件</li><li><code>n_threads=20</code>：用 20 线程并行计算（设成你的 CPU 核心数即可）</li><li><code>-f null -</code>：不生成任何输出文件，只运行滤镜计算</li></ul><h3 id="142-4k--4k-">14.2 4K 内容使用 4K 模型</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">-filter_complex &quot;[0:v][1:v]libvmaf=model=version=vmaf_4k_v0.6.1:log_path=&#x27;vmaf_log.txt&#x27;:n_threads=20&quot;
</code></pre>
<h3 id="143-">14.3 帧对齐问题</h3><p>如果测出异常低的分数（比如平时都是 95 分，突然跳到 70 分），多半是帧没有对齐。在两个 <code>-i</code> 前各加 <code>-r 帧率</code> 强制同步：</p><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -r 30 -i 压缩后.mp4 -r 30 -i 原始视频.mp4 \
  -filter_complex &quot;[0:v][1:v]libvmaf=model=version=vmaf_v0.6.1:log_path=&#x27;vmaf_log.txt&#x27;:n_threads=20&quot; \
  -f null -
</code></pre>
<p>另外，两个视频必须<strong>帧数和时长相同</strong>，建议测试前用 <code>-to</code> 裁剪到相同长度。</p><hr/><h2 id="">十五、完整命令参考</h2><h3 id="151-1080p-hevc-nvenc">15.1 1080p HEVC 二压（NVENC，质量优先，兼容性标签）</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 输入.mp4 \
  -vf &quot;scale=-2:1080&quot; \
  -c:v hevc_nvenc -pix_fmt yuv420p10le -profile:v main10 \
  -preset p7 -tune:v hq \
  -rc:v vbr -cq:v 28 -b:v 0 \
  -multipass 2 -rc-lookahead 32 \
  -spatial-aq 1 -temporal-aq 1 -aq-strength 10 \
  -g 300 -bf 4 -b_ref_mode 2 \
  -split_encode_mode 2 \
  -tag:v hvc1 \
  -c:a copy \
  -y 输出.mp4
</code></pre>
<h3 id="152-1080p-av1-nvenc-40-">15.2 1080p AV1 二压（NVENC 40 系，质量优先）</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 输入.mp4 \
  -vf &quot;scale=-2:1080&quot; \
  -c:v av1_nvenc -pix_fmt yuv420p10le \
  -preset p7 -tune:v hq \
  -rc:v vbr -cq:v 34 -b:v 0 \
  -multipass 2 -rc-lookahead 32 \
  -spatial-aq 1 -temporal-aq 1 -aq-strength 10 \
  -g 300 -bf 4 -b_ref_mode 2 \
  -split_encode_mode 2 \
  -c:a copy \
  -y 输出.mp4
</code></pre>
<h3 id="153-hevc-">15.3 HEVC 码率上限模式（适合有明确码率要求的场景）</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 输入.mp4 \
  -c:v hevc_nvenc -pix_fmt yuv420p10le \
  -preset p7 -tune:v hq \
  -rc:v vbr -b:v 2M -maxrate 3M -bufsize 5M \
  -multipass 2 -rc-lookahead 32 \
  -spatial-aq 1 -temporal-aq 1 -aq-strength 10 \
  -c:a copy \
  -y 输出.mp4
</code></pre>
<h3 id="154-url--2--720p">15.4 URL 输入，剪前 2 分钟，降至 720p</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">export VIDEO=&quot;https://your-server.com/path/video.mp4?sign=token_here&quot;

ffmpeg -i &quot;$VIDEO&quot; \
  -to 120 \
  -vf &quot;scale=-2:720&quot; \
  -c:v hevc_nvenc -pix_fmt yuv420p10le \
  -preset p4 -tune:v hq \
  -rc:v vbr -cq:v 28 -b:v 0 \
  -c:a copy \
  -y 输出_720p.mp4
</code></pre>
<h3 id="155--ss---i-">15.5 精确剪切并二压（起点帧精确，-ss 在 -i 后）</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 输入.mp4 \
  -ss 00:01:30 -to 00:05:00 \
  -c:v hevc_nvenc -pix_fmt yuv420p10le \
  -preset p7 -tune:v hq \
  -rc:v vbr -cq:v 28 -b:v 0 \
  -c:a copy \
  -y 片段.mp4
</code></pre>
<h3 id="156-">15.6 关键帧快速剪切（秒出，不重编码）</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -ss 00:01:30 -i 输入.mp4 -to 00:03:30 -c copy -y 片段.mp4
</code></pre>
<h3 id="157--gpu-">15.7 双 GPU 同时跑两个任务</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash"># 终端 1（使用 GPU 0）
CUDA_VISIBLE_DEVICES=0 ffmpeg -i 视频A.mp4 \
  -c:v av1_nvenc -rc:v vbr -cq:v 34 -b:v 0 -preset p7 \
  -c:a copy -y 输出A.mp4

# 终端 2（使用 GPU 1）
CUDA_VISIBLE_DEVICES=1 ffmpeg -i 视频B.mp4 \
  -c:v av1_nvenc -rc:v vbr -cq:v 34 -b:v 0 -preset p7 \
  -c:a copy -y 输出B.mp4
</code></pre>
<h3 id="158-vmaf-">15.8 VMAF 对比参考（草稿示例中的参数）</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 参考视频.mp4 -i 被测视频.mp4 \
  -filter_complex &quot;[0:v][1:v]libvmaf=model=version=vmaf_v0.6.1:log_path=&#x27;vmaf_log.txt&#x27;:n_threads=20&quot; \
  -f null -
</code></pre>
<hr/><h2 id="">十六、常见问题与坑点</h2><h3 id="-impossible-to-convert-between-the-formats">❌ 报错：Impossible to convert between the formats</h3><p>完整错误：<code>Impossible to convert between the formats supported by the filter &#x27;Parsed_null_0&#x27; and the filter &#x27;auto_scale_0&#x27;</code></p><p><strong>原因</strong>：<code>-pix_fmt p010le</code> 与 <code>scale</code> 等滤镜不兼容</p><p><strong>解决</strong>：把 <code>-pix_fmt p010le</code> 改为 <code>-pix_fmt yuv420p10le</code></p><hr/><h3 id="--webdav---">❌ 输出到 WebDAV 失败 / 输出文件损坏无法播放</h3><p><strong>原因</strong>：MP4 写入完成后需要 seek 回文件头写 moov box，WebDAV 不支持 seek 写入</p><p><strong>解决</strong>：</p><ol start="1"><li>输出到本地，完成后手动上传（最稳）</li><li>改用 MKV 格式输出（对 seek 要求更低）</li><li>使用 rclone mount 并确认挂载方式支持 seek</li></ol><hr/><h3 id="-hevc--safari--ios-">❌ HEVC 在 Safari / iOS 不播放</h3><p>把 <code>-tag:v hev1</code> 改为 <code>-tag:v hvc1</code>（或显式加上 <code>-tag:v hvc1</code>）</p><hr/><h3 id="-">❌ 拼接后音视频不同步</h3><ul><li>检查各片段帧率是否一致，不一致先用 <code>-r</code> 统一</li><li><code>-c copy</code> 拼接要求时间戳连续，如果各片段来源复杂，改为重新编码</li><li>最稳方案：拼接时不用 <code>-c copy</code>，同时编码，代价是时间更长</li></ul><hr/><h3 id="-cq-">❌ CQ 模式下文件比预期大很多</h3><ul><li>检查 CQ 值是否选错（参考上方 CRF/CQ 对应表）</li><li>加 <code>-maxrate</code> 和 <code>-bufsize</code> 限制峰值码率</li><li>检查 <code>-b:v 0</code> 是否有加（不加的话 NVENC 可能以默认码率为目标）</li></ul><hr/><h2 id="">十七、参数速查表</h2><table><thead><tr><th> 参数 </th><th> 含义 </th><th> 常用值/说明 </th></tr></thead><tbody><tr><td> <code>-i 文件</code> </td><td> 输入文件 </td><td> 本地路径或 URL（URL 加引号） </td></tr><tr><td> <code>-c:v</code> </td><td> 视频编码器 </td><td> <code>hevc_nvenc</code> <code>av1_nvenc</code> <code>libx265</code> <code>copy</code> </td></tr><tr><td> <code>-c:a</code> </td><td> 音频编码器 </td><td> <code>aac</code> <code>copy</code> <code>libopus</code> </td></tr><tr><td> <code>-vf &quot;...&quot;</code> </td><td> 视频滤镜 </td><td> <code>scale=-2:1080</code> <code>setpts=0.5*PTS</code> </td></tr><tr><td> <code>-af &quot;...&quot;</code> </td><td> 音频滤镜 </td><td> <code>atempo=2.0</code> <code>apad</code> </td></tr><tr><td> <code>-pix_fmt</code> </td><td> 像素格式 </td><td> <code>yuv420p10le</code>（10bit推荐）<code>yuv420p</code>（8bit兼容） </td></tr><tr><td> <code>-ss 时间</code> </td><td> 起始时间 </td><td> 放 <code>-i</code> 前：快速seek；放 <code>-i</code> 后：精确seek </td></tr><tr><td> <code>-to 时间</code> </td><td> 截止时间（绝对） </td><td> <code>120</code> <code>00:02:00</code> </td></tr><tr><td> <code>-t 时长</code> </td><td> 持续时长（相对） </td><td> <code>90</code> </td></tr><tr><td> <code>-r 帧率</code> </td><td> 输出帧率 </td><td> <code>24</code> <code>30</code> <code>60</code> </td></tr><tr><td> <code>-y</code> </td><td> 覆盖已有输出 </td><td> 不加则交互询问 </td></tr><tr><td> <code>-map</code> </td><td> 选择特定流 </td><td> <code>0:v:0</code> <code>0:a:1</code> </td></tr><tr><td> <code>-preset</code> </td><td> 编码速度预设 </td><td> NVENC: <code>p1</code>-<code>p7</code>；CPU: <code>fast/medium/slow</code> </td></tr><tr><td> <code>-tune:v</code> </td><td> 调优目标 </td><td> <code>hq</code>（高质量）<code>ull</code>（低延迟） </td></tr><tr><td> <code>-rc:v vbr</code> </td><td> 码率控制：可变码率 </td><td> 配合 <code>-cq:v</code> 或 <code>-b:v</code> 使用 </td></tr><tr><td> <code>-cq:v</code> </td><td> NVENC 恒定质量 </td><td> HEVC: <code>24-32</code>；AV1: <code>30-40</code> </td></tr><tr><td> <code>-crf</code> </td><td> CPU 恒定质量 </td><td> x264/x265: <code>18-28</code>；svtav1: <code>28-38</code> </td></tr><tr><td> <code>-global_quality</code> </td><td> QSV 恒定质量 </td><td> <code>20-28</code>，接近 CRF 逻辑 </td></tr><tr><td> <code>-qp_i</code> <code>-qp_p</code> </td><td> AMF 量化参数 </td><td> <code>24-32</code>，两者设相同值 </td></tr><tr><td> <code>-b:v</code> </td><td> 目标码率 </td><td> <code>2M</code> <code>500k</code>（M 要大写） </td></tr><tr><td> <code>-maxrate</code> </td><td> 最大码率 </td><td> <code>3M</code> </td></tr><tr><td> <code>-bufsize</code> </td><td> 缓冲区大小 </td><td> <code>5M</code>（建议为 maxrate 的 1.5-2 倍） </td></tr><tr><td> <code>-profile:v main10</code> </td><td> HEVC 10bit Profile </td><td> 配合 <code>-pix_fmt yuv420p10le</code> </td></tr><tr><td> <code>-tag:v hvc1</code> </td><td> 强制 HEVC 兼容标签 </td><td> Web/iOS 分发推荐 </td></tr><tr><td> <code>-g</code> </td><td> GOP 长度 </td><td> <code>300</code>（帧率×10） </td></tr><tr><td> <code>-bf</code> </td><td> B 帧数量 </td><td> <code>4</code>（上限） </td></tr><tr><td> <code>-b_ref_mode 2</code> </td><td> B 帧金字塔参考 </td><td> 推荐 </td></tr><tr><td> <code>-multipass 2</code> </td><td> 两遍编码 </td><td> 提升压缩效率 </td></tr><tr><td> <code>-rc-lookahead 32</code> </td><td> 前瞻帧数 </td><td> <code>32</code>（最大值） </td></tr><tr><td> <code>-spatial-aq 1</code> </td><td> 空间自适应量化 </td><td> <code>1</code> 开启 </td></tr><tr><td> <code>-temporal-aq 1</code> </td><td> 时间自适应量化 </td><td> <code>1</code> 开启 </td></tr><tr><td> <code>-aq-strength</code> </td><td> AQ 强度 </td><td> <code>8-12</code> </td></tr><tr><td> <code>-split_encode_mode 2</code> </td><td> 多 NVENC 单元分块 </td><td> 不保证效果 </td></tr><tr><td> <code>-f null -</code> </td><td> 不输出文件 </td><td> 仅用于 VMAF 等测试计算 </td></tr><tr><td> <code>CUDA_VISIBLE_DEVICES=N</code> </td><td> 指定第 N+1 块 GPU </td><td> 写在命令前，临时生效 </td></tr></tbody></table><hr/><p><em>持续更新。后续可补充：字幕烧录、色彩空间转换（HDR→SDR）、批处理脚本、更多滤镜参数等。</em></p></div><p style="text-align:right"><a href="https://me.bcw2.top:3443/posts/tech/ffmpeg-video-compress-practical-guide#comments">看完了？说点什么呢</a></p></div>]]></description><link>https://me.bcw2.top:3443/posts/tech/ffmpeg-video-compress-practical-guide</link><guid isPermaLink="true">https://me.bcw2.top:3443/posts/tech/ffmpeg-video-compress-practical-guide</guid><dc:creator><![CDATA[bcw222]]></dc:creator><pubDate>Mon, 06 Apr 2026 15:32:15 GMT</pubDate></item><item><title><![CDATA[DEATH STRANDING - Don't Be So Serious]]></title><description><![CDATA[<div><blockquote>该渲染由 Shiro API 生成，可能存在排版问题，最佳体验请前往：<a href="https://me.bcw2.top:3443/posts/game/death-stranding-dont-be-so-serious">https://me.bcw2.top:3443/posts/game/death-stranding-dont-be-so-serious</a></blockquote><div><style>  .paragraph {
    text-indent: 0 !important;
  }
</style><audio controls="" autoPlay="" loop=""><source src="https://file.team.silveridge.cn:3443/f/EVvH9/Don%27t%20Be%20So%20Serious.mp3" type="audio/mpeg"/><p>Don&#x27;t Be So Serious - Low Roar</p></audio><p>Composed by：Ryan Joseph Karazija/Andrew Scheps/Mike Lindsay</p><p>Wake<br/>Heavy pulse<br/>Bloody eyes<br/>Sweaty clothes</p><p>New routine<br/>That I&#x27;ve learned<br/>To understand and know</p><p>Play my tricks<br/>Fragile mind<br/>Rest your head<br/>On me</p><p>Shut my eyes<br/>I&#x27;m not here<br/>There must be some mistake</p><p>Don&#x27;t be so serious<br/>(×4)</p><p>Watch your words<br/>Turn to dust<br/>As we forget<br/>As we move on</p><p>Still<br/>I refuse<br/>To let you slip away</p><p>Don&#x27;t be so serious<br/>(×4)</p><p>Magnets<br/>(×n)</p></div><p style="text-align:right"><a href="https://me.bcw2.top:3443/posts/game/death-stranding-dont-be-so-serious#comments">看完了？说点什么呢</a></p></div>]]></description><link>https://me.bcw2.top:3443/posts/game/death-stranding-dont-be-so-serious</link><guid isPermaLink="true">https://me.bcw2.top:3443/posts/game/death-stranding-dont-be-so-serious</guid><dc:creator><![CDATA[bcw222]]></dc:creator><pubDate>Wed, 01 Apr 2026 06:39:03 GMT</pubDate></item><item><title><![CDATA[Windows 平台 类 vLLM 推理加速 配置小记]]></title><description><![CDATA[<div><blockquote>该渲染由 Shiro API 生成，可能存在排版问题，最佳体验请前往：<a href="https://me.bcw2.top:3443/posts/tech/windows-platform-vllm-like-inference-accelerate-config-note">https://me.bcw2.top:3443/posts/tech/windows-platform-vllm-like-inference-accelerate-config-note</a></blockquote><div><p>呜… 大过年的终于是把整个假期消磨在晚六朝三（六点睡十五点起；12 小时制）的离谱作息里了，过年前说好的写博客记录配置的豪言壮语也是不知道抛到哪里去了；趁着假期还没完全结束（我还没忘干净），就把我的一点心得记录一下吧。</p><h2 id="">背景</h2><p>事情的起因是这样的：音色克隆是个危险的事情，把私有的数据交给云端，哪怕只有一句话，都有可能被日渐先进的技术去噪，扩增到几个小时，训练变声模型，巴拉巴拉拿来干什么就不知道了（没错我就是这么干的）。正好手上有 gpu 运算资源，VoxCPM 这种 Zero-Shot 的 TTS 效果也已经不错了，于是便打算本地部署一个推理服务。</p><h2 id="">推理</h2><p>推理好啊，写成 OpenAI 兼容的 API 端点就可以无缝兼容各种成熟的应用了。正好，VoxCPM 的 README 里就有社区项目：voxcpm-fastapi，将原生调用包装成了 OpenAI 兼容的端点。</p><p>一顿操作猛如虎，一看推理速度5。在我的 RTX 4060 上，它的推理速度大概只有 5~6 it/s。尽管 RTF 勉强小于 1 吧，但现在的应用很少有实现流式 TTS 的 —— 其实技术复杂度挺高的，你需要类似 WebRTC 的实现，得到的就只是不需要像视频那样鼠标跟手的音频（又不是变声器）。而非流式就意味着热启动在几秒内不能生成一段不短的音频的话，直观感受就是挺慢的。</p><p>我也换到 RTX 4070 Ti Super 上试了下（是的，我的家里云有两张不同型号的N卡），上下文短的时候有 30 it/s 左右（其实这两张卡算力差距大概只是翻倍，这个推理速度差距大概率意味着 CUDA OOM 被 Windows 交换到内存了，这是推理服务这种计算密集型任务要极力避免的），但整个 pipeline 走完还是挺慢的。</p><p>关键是，原生 Pytorch 推理，不支持并发！FastAPI 的并发请求，就只能积压等待串行处理。作为一个用户会靠切分长请求解决高延迟和长上下文问题的服务，这显然是不可接受的。</p><p>咋办？回去翻 VoxCPM 的 README。还有一个社区项目，nanovllm-voxcpm，将 VoxCPM 里的 LLM 部分用 nanovLLM（一个使用 vLLM 优化思路达到相近的性能但轻量得多的 LLM 推理框架）实现了。</p><p>众所周知，vLLM 作为生产级的推理框架，显存优化和并发推理不是手拿把掐——于是让 LLM 帮我用它替换原生 Pytorch 推理，达到了 4060 上 ~30 step/s，4070 Ti Super 上 ~60 step/s，RTF 逼近不足 0.1 的性能。这对于我的应用场景已经绰绰有余（其实应该已经远超 VoxCPM 团队的预期了，但我没做详细测试，团队是在 4090 上测试的，也没啥比较的意义）。接下来，就是部署了。</p><h2 id="">部署</h2><p>家里云嘛，能装 Windows 还装 Linux 多浪费；wsl 嘛，交互式用用还行，拿来跑服务就不太优雅了；至于 Docker，这么重的环境，也不够优雅；何况虚拟化必然有性能开销，尤其是对于计算密集型任务。然而众所周知，机器学习的推理对 Windows 平台并不友好。</p><p>首先，在原生 Pytorch 推理的时候<code>torch.compile</code> 需要的 <code>triton</code> 库就没有。这倒是好办，装<strong>对应版本</strong>（务必看清楚版本对应矩阵）的<code>windows-triton</code>就好了。（附：不 compile 的话大概是 4060 ~3 it/s, 4070 Ti Super ~17 it/s；compile的话加载模型会慢不少，是正常的）</p><p>然而，当换到 nanovLLM，依赖地狱才刚刚开始。vLLM 类框架需要的不只是 <code>triton</code>，还有 Flash Attention （<code>flash-attn</code>，一个更快的，显存更高效的注意力实现）。而这个东西，在 Windows 平台，没！有！预！构！建！编译这玩意儿大概在十几核的现代小型工作站（或者说，GitHub 的 CI Runner）上都要至少3个小时。幸好，GitHub 上有人 fork 了上传了预构建。只要下载安装<strong>对应版本</strong>（务必看清楚<code>torch</code>（包括<code>cuda</code>版本）和<code>cpython</code>的版本对应关系，以及预装相应版本的 CUDA Toolkit 并确保<code>CUDA_PATH</code>环境变量配置正确）即可正常运行。。。吗？</p><p>并不能。你大概率会收到报错：<code>nccl</code> 在 Windows 上不被支持。这是什么？这是分布式计算的后端。是，我知道我们没有用张量并行，理论上用不着分布式，但生产级的推理大概率不会是单卡的，为单卡额外维护一套逻辑没必要，典型的处理方法是起一个只有一张卡的“伪”分布式环境。然而，nanovLLM的分布式计算后端是硬编码的<code>nccl</code> 。那么，只要改成 Windows 可用的<code>gloo</code> ，就可以了。。。吗？</p><p>还是不能。因为：Pytorch 2.8~2.9 的内置<code>gloo</code>，是坏的！理论上这个问题在 2.10 应该已经修复（没试过，但 nightly 肯定是好的），但预构建的<code>flash-attn</code> 未必跟得上。所以，我直接回退到了 2.7 版本，重装了相关依赖（顺便提一句，如果是<code>choco</code>装的<code>ffmpeg</code> ,<code>torchcodec</code>会找不到它，但我已经提PR了），然后——</p><p>终于能正常运行了！看着龟速爬行的 tqdm 进度条被不断闪过的 200 响应日志取代，我只想说一句话：</p><p><strong>我作业忘写了啊啊啊！！！  </strong></p><p>（PS：如果评论区有人感兴趣的话，我就抽空把我的方案精简一下以更“可部署”的方式分享出来）</p></div><p style="text-align:right"><a href="https://me.bcw2.top:3443/posts/tech/windows-platform-vllm-like-inference-accelerate-config-note#comments">看完了？说点什么呢</a></p></div>]]></description><link>https://me.bcw2.top:3443/posts/tech/windows-platform-vllm-like-inference-accelerate-config-note</link><guid isPermaLink="true">https://me.bcw2.top:3443/posts/tech/windows-platform-vllm-like-inference-accelerate-config-note</guid><dc:creator><![CDATA[bcw222]]></dc:creator><pubDate>Thu, 19 Feb 2026 12:24:29 GMT</pubDate></item><item><title><![CDATA[浅谈2025高考英语全国一卷35题]]></title><description><![CDATA[<div><blockquote>该渲染由 Shiro API 生成，可能存在排版问题，最佳体验请前往：<a href="https://me.bcw2.top:3443/posts/learn/2025-gaokao-english-35">https://me.bcw2.top:3443/posts/learn/2025-gaokao-english-35</a></blockquote><div><p>2025高考已经结束了，全国一卷的英语第35题也是引起了不小的争议。下面，我将以普通在读高中生的视角，为大家带来我的解读。本文意在分享心得，因此请勿将其作为严谨的学术交流内容看待。</p><p><strong>利益相关声明，我在撰写本文时是一位参与了2025年在英语科目使用全国一卷的地方的高考的高二年级学生，在真实的高考环境中完成了下面的题目，但同时这场高考对我来说无关紧要，因此本文仅以学习交流的视角谈谈我的看法。</strong>话不多说，我们先看看原题：</p><hr/><h3 id="d">D篇</h3><p>Microplastics have become a common source of pollution across the Earth—they have settled in the deep sea and on the Himalayas, stuck inside volcanic rocks, filled the stomachs of seabirds and even fallen in fresh Antarctic snow. They are even appearing inside humans.<br/>Now, new research suggests that a simple, cheap measure may significantly reduce the level of microplastics in water from your tap (水龙头): boiling and filtering (过滤) it. In a study published Wednesday in Environmental Science &amp; Technology Letters, researchers from China found that boiling tap water for just five minutes—then filtering it after it cools—could remove at least 80 percent of its microplastics.<br/>Crucially, this process relies on the water containing enough calcium carbonate (碳酸钙) to trap the plastics. In the study, boiling hard water containing 300 milligrams of calcium carbonate led to an almost 90 percent drop in plastics. But in samples with less than 60 milligrams of calcium carbonate, boiling reduced the level of plastics by just 25 percent. Additionally, the research didn’t include all types of plastics. The team focused only on three common types—polystyrene, polyethylene and polypropylene—and they didn’t study other chemicals previously found in water such as vinyl chloride.<br/>Still, the findings show a potential path forward for reducing microplastic exposure—a task that’s becoming increasingly difficult. Even bottled water, scientists found earlier this year, contains 10 to 1,000 times more microplastics than originally thought.<br/>Scientists are still trying to determine how harmful microplastics are—but what they do know has raised concerns. The new study suggests boiling tap water could be a tool to limit intake. “The way they demonstrated how microplastics were trapped through the boiling process was nice,” Caroline Gauchotte-Lindsay, an environmental engineer at the University of Glasgow in Scotland who was not involved in the research, tells New Scientist. “We should be looking into upgrading drinking water treatment plants so they remove microplastics.”</p><ol start="32"><li>How does the author present the issue in the first paragraph?<br/>A. By quoting an expert.<br/>B. By defining a concept.<br/><strong>C. By giving examples.</strong><br/>D. By providing  statistics.</li><li>What determines the effectiveness of trapping microplastics in water?<br/><strong>A. The hardness of water.</strong><br/>B. The length of cooling time.<br/>C. The frequency of filtering.<br/>D. The type of plastic in water.</li><li>What does the author try to illustrate by mentioning bottled water in paragraph 4?<br/>A. The importance of plastic recycling.<br/><strong>B. The severity of the microplastic problem.</strong><br/>C. The danger in overusing pure water.<br/>D. The difficulty in treating polluted water.</li><li>What is Gauchotte-Lindsay&#x27;s suggestion about?<br/>A. Choice of new research methods.<br/>B. Possible direction for further study.<br/>C. Need to involve more researchers.<br/><strong>D. Potential application of the findings.</strong></li></ol><h3 id="">初次做题</h3><p>首先，我们看看我在考场上的做题思路。第35题的关键线索是下面这句话：</p><blockquote><p>We should be looking into upgrading drinking water treatment plants so they remove microplastics.</p></blockquote>
<p>作为全文的最后一句话，它的位置符合题意“suggestion”；根据题意，答案与“research methods”或“more researchers”明显无关，A、C选项排除；文义“upgrading drinking water treatment plants”（升级饮用水处理设施）义即将上文发现的去除微塑料的方法应用于饮用水处理设施，从而从源头解决饮用水中含微塑料的问题，而非进行进一步研究，因此我选 D。</p><h3 id="">换位思考</h3><p>下面，我们从出题者的角度，尝试剖析题目考察点。</p><h6 id="1upgrade-upgrading">关键词1：upgrade (<code>upgrading</code>)</h6><p>根据生活中常用的语境：</p><blockquote><p>Usage: apt [options] command
Most used commands:
  upgrade - upgrade the system by installing/upgrading packages
  full-upgrade - upgrade the system by removing/installing/upgrading packages</p></blockquote><blockquote>
<p>RAID degraded</p></blockquote>
<p>不难明白，upgrade在此处应指一种较为深度的，向好方向的更新，即使用某种方法减少微塑料；</p><h6 id="2plant-plants">关键词2：plant (<code>plants</code>)</h6><p>根据《辐射：避难所》里电力设施被称为<code>Power Plant</code>，不难理解 plant 可泛指集中的基础设施，通常用于处理与供应基础资源如水、电、煤）。之于文中，即指水处理厂。以普通的英语学习者，该义项通常被视为一词多义，且错误理解为植物会较大程度上影响对文义的理解。</p><p>不难推测，文义为希望在上述研究的基础上，在饮用水供应的源头缓解微塑料问题，因此题目中即将其替换为将研究结果应用到水处理厂。</p><h3 id="">主要争议</h3><p>然而，题目放出后，许多人认为本题选 B。主要理由如下：</p><h6 id="1-d-">1. D 选项工程上不可行</h6><p>许多人认为，将大量饮用水“煮沸”并冷却过滤消耗大量能源，在工程上不可行。对此，我表示怀疑。人们的第一印象告诉自己，烧水需要较长时间，消耗大量能源，并在冷却过程中向环境大量散热。这是不对的。工程上对于这种需要对工质短暂调整温度，但产物和原料温度相差不大的处理，我们可以使用逆流换热来几乎完全回收热量。沿海干旱地区的海水淡化设施的推广，今麦郎等品牌的凉白开也证明“煮沸”饮用水并冷却过滤并非完全不可行。</p><h6 id="2-">2. 原文用词不同</h6><blockquote><p>“We should be looking into modifying drinking water treatment plants so they remove microplastics.”</p></blockquote>
<p>根据英语<a href="https://www.smithsonianmag.com/smart-news/boiling-tap-water-could-help-remove-80-percent-of-its-microplastics-study-suggests-180983874/">原文</a>，G.-L. 的本意并不直击大刀阔斧地升级水处理厂，自然希望进行进一步研究而非直接应用。但既然题目进行了修改，我们自然应当依照改文作答。</p><h6 id="3-g-l-">3. G.-L. 自述</h6><blockquote><p>I certainly wouldn&#x27;t recommend boiling water at Drinking Water plants so certainly not option D.</p></blockquote>
<p>网传一份网友询问 G.-L. 本人的邮件回复，其中教授表示题中四个选项均“非其本意”，但是B选项应当是最接近正确的。然而，阅读题中需要分析的是“作者对受访者话语的转达方式”，受访者的本意究竟如何并不该是决定答案的关键因素。</p><h6 id="4-">4. 常识判断</h6><blockquote><p>在饮用水处理设施中将水煮沸，并不是一个工程上可行，只是在此项研究见刊之前尚不清楚其净水作用的工业方案。在部分考生眼中——哪怕不是经过了专业的学习而是凭借一定的生活直觉——饮用水设施应用煮沸法就不是一个亟待讨论的新课题，而是一个已经可以宣判死刑的荒谬方案（至少在绝大多数正常生态/工业环境的社会中）。<a href="https://www.zhihu.com/question/1916068422468477412/answer/1918602602205132035">知乎</a></p></blockquote>
<p>这种观点认为，文末 G.-L. 的评论与前文的煮沸法无关，而只是一个完全不相干的隔壁领域。作为一场全国性的英语语言考试，考生不该因为这种常识性的分歧而被扣去分数。</p><p>但是，假如说考生不希望常识性的分歧而被扣去分数，就应该在做题时完全摒弃常识，而忠于原文，寻找题目可能的暗示。这点，正是上面用词不同体现的出题人的暗示。</p><h3 id="">整理思路</h3><p>在看了那么多争议后，我决定重新整理思路。</p><h6 id="1-">1. 重新理解文义</h6><p>文中的发现，与其说是煮沸法除微塑料，不如说是硬水煮沸后微塑料会减少。这也解决了一个“中国人的问题”：我们千年以来都使用的“喝开水”，怎么就变成前沿学术成果了？文义并不单讲煮沸，而是在煮沸后进行了水的理化性质分析。</p><h6 id="2-">2. 重新理解选项</h6><p>选项 D 说 <code>potential</code> 应用，而任何一项研究的应用都不是直接套用实验室做法。首先，水处理厂不可能为了去除微塑料而自降水质，直接把处理好的水变成硬水再煮沸过滤。从这个角度上来说，选项 D 是包含了选项 B 的 <code>further research</code> 的。既然我们认为 D 去除微塑料 <code>was nice</code>，而 D 又包含了 B，选更全面概括了 G.-L. 的评论的选项应当更合理。</p><hr/><p>因此，本题我选 <strong>D. Potential application of the findings.</strong></p><hr/><blockquote><p>NOT by AI</p></blockquote></div><p style="text-align:right"><a href="https://me.bcw2.top:3443/posts/learn/2025-gaokao-english-35#comments">看完了？说点什么呢</a></p></div>]]></description><link>https://me.bcw2.top:3443/posts/learn/2025-gaokao-english-35</link><guid isPermaLink="true">https://me.bcw2.top:3443/posts/learn/2025-gaokao-english-35</guid><dc:creator><![CDATA[bcw222]]></dc:creator><pubDate>Thu, 19 Jun 2025 12:09:57 GMT</pubDate></item><item><title><![CDATA[Moon Halo 分角色歌词]]></title><description><![CDATA[<div><blockquote>该渲染由 Shiro API 生成，可能存在排版问题，最佳体验请前往：<a href="https://me.bcw2.top:3443/posts/game/moon-halo">https://me.bcw2.top:3443/posts/game/moon-halo</a></blockquote><div><p>网页可能有渲染错误，建议移步 <a href="https://file.bcw2.top:3443/s/xXoSM">PDF 版</a></p><audio controls="" autoPlay="" loop=""><source src="https://file.bcw2.top:3443/f/NgLfq/%E8%8C%B6%E7%90%86%E7%90%86%E7%90%86%E5%AD%90%20_%20TetraCalyx%20_%20hanser%20_%20HOYO-MiX%20-%20Moon%20Halo.flac" type="audio/flac"/><source src="https://file.bcw2.top:3443/f/KaGT5/%E8%8C%B6%E7%90%86%E7%90%86%E7%90%86%E5%AD%90%20_%20TetraCalyx%20_%20hanser%20_%20HOYO-MiX%20-%20Moon%20Halo.mp3" type="audio/mpeg"/><source src="https://file.bcw2.top:3443/f/6qXHw/%E8%8C%B6%E7%90%86%E7%90%86%E7%90%86%E5%AD%90%20_%20TetraCalyx%20_%20hanser%20_%20HOYO-MiX%20-%20Moon%20Halo.ogg" type="audio/ogg"/><p>Moon Halo - 茶理理理子, TetraCalyx, hanser, HOYO-MiX</p></audio><style><!--
 /* Font Definitions */
@font-face
    {font-family:得意黑;
    src: url('https://file.bcw2.top:3443/f/5qpUw/SmileySans-Oblique.ttf.woff2') format('woff2');
    panose-1:0 0 0 0 0 0 0 0 0 0;}
 /* Style Definitions */
 p.MsoNormal, li.MsoNormal, div.MsoNormal
    {margin-top:0cm;
    margin-right:0cm;
    margin-bottom:8.0pt;
    margin-left:0cm;
    line-height:115%;
    font-size:11.0pt;
    font-family:得意黑;}
.MsoChpDefault
    {font-size:11.0pt;
    font-family:得意黑;}
.MsoPapDefault
    {margin-bottom:8.0pt;
    line-height:115%;}
 /* Page Definitions */
 @page WordSection1
    {size:595.3pt 841.9pt;
    margin:72.0pt 90.0pt 72.0pt 90.0pt;
    layout-grid:15.6pt;}
div.WordSection1
    {page:WordSection1;}
-->
</style><div class="true" style="layout-grid:15.6pt">
<p class="true"><span style="font-size:16.0pt;font-family:得意黑;color:#E6A26C">Some deserts on this
planet were oceans once</span></p><p class="true"><span style="font-size:16.0pt;font-family:得意黑;color:#E6A26C">Somewhere shrouded by the
night the sun will shine</span></p><p class="true"><span style="font-size:16.0pt;font-family:得意黑;color:#A3D8A3">Sometimes I see a dying
bird fall to the ground</span></p><p class="true"><span style="font-size:16.0pt;font-family:得意黑;color:#A7E3E8">But it used to fly so
high</span></p><p class="true"><span style="font-size:16.0pt;font-family:得意黑;color:#A3D8A3">I thought I were no more
than a bystander till I felt a touch so real</span></p><p class="true"><span style="font-size:16.0pt;font-family:得意黑;color:#A7E3E8">I will no longer be a
transient when I see smiles with tears</span></p><p class="true"><span style="font-size:16.0pt;font-family:得意黑;color:#E6A26C">If I have never known the
sore of farewell and pain of sacrifices</span></p><p class="true"><span style="font-size:16.0pt;font-family:得意黑;color:#E6A26C">What else should I
engrave on my mind</span></p><p class="true"><span style="font-size:16.0pt;font-family:得意黑;color:#A7E3E8">Frozen into icy rocks
that&#x27;s how it starts </span><span style="font-size:16.0pt;font-family:得意黑;color:#E6A26C">that&#x27;s how it starts</span></p><p class="true"><span style="font-size:16.0pt;font-family:得意黑;color:#A3D8A3">Crumbled like the sands
of time that&#x27;s how it ends </span><span style="font-size:16.0pt;font-family:得意黑;color:#A7E3E8">that&#x27;s how it ends</span></p><p class="true"><span style="font-size:16.0pt;font-family:得意黑;color:#E6A26C">Every page of tragedy is
thrown away            burned
out in the flame</span></p><p class="true" style="margin-left:8.0pt;text-indent:158.1pt"><span style="font-size:16.0pt;font-family:得意黑;color:#A7E3E8">is thrown away               in
the flame</span></p><p class="true"><span style="font-size:16.0pt;font-family:得意黑;color:#E6A26C">A shoulder for the past</span></p><p class="true"><span style="font-size:16.0pt;font-family:得意黑;color:#E6A26C">Let out the cries </span><span style="font-size:16.0pt;font-family:得意黑;background-clip:text;-webkit-background-clip:text;color:transparent">imprisoned
for so long</span></p><p class="true"><span style="font-size:16.0pt;font-family:得意黑;color:#E6A26C">A pair of wings for me at
this moment</span></p><p class="true"><span style="font-size:16.0pt;font-family:得意黑;color:#E6A26C">To soar above this world</span></p><p class="true" style="text-indent:19.05pt"><span style="font-size:16.0pt;font-family:得意黑;color:#A3D8A3"> soar
above this world</span></p><p class="true" style="text-indent:12.8pt"><span style="font-size:16.0pt;font-family:得意黑;color:#A3D8A3">               
</span><span style="font-size:16.0pt;font-family:得意黑;color:#A7E3E8">Oh~ Ho~</span></p><p class="true"><span style="font-size:16.0pt;font-family:得意黑;color:#E6A26C">Turn into a shooting star
that briefly shines</span></p><p class="true"><span style="font-size:16.0pt;font-family:得意黑;color:#A7E3E8"><del>~</del><del>~</del><del>~</del>~~~that briefly
shines but warms up every heart</span></p><p class="true"><span style="font-size:16.0pt;font-family:得意黑;color:#E6A26C">May all </span><span style="font-size:16.0pt;font-family:得意黑;color:#A3D8A3">May
all</span><span style="font-size:16.0pt;font-family:得意黑;color:#E6A26C"></span><span style="font-size:16.0pt;font-family:得意黑;background-clip:text;-webkit-background-clip:text;color:transparent">the beauty be blessed</span></p><p class="true"><span style="font-size:16.0pt;font-family:得意黑;background-clip:text;-webkit-background-clip:text;color:transparent">May all the beauty be blessed</span></p><p class="true"><span style="font-size:16.0pt;font-family:得意黑;color:#E6A26C">I will never go</span></p><p class="true"><span style="font-size:16.0pt;font-family:得意黑;color:#E6A26C">There&#x27;s a way back home</span></p><p class="true"><span style="font-size:16.0pt;font-family:得意黑;color:#E6A26C">Brighter than tomorrow
and yesterday</span></p><p class="true" style="text-indent:179.2pt"><span style="font-size:16.0pt;font-family:得意黑;color:#A3D8A3">May
all the beauty be blessed</span></p><p class="true"><span style="font-size:16.0pt;font-family:得意黑;background-clip:text;-webkit-background-clip:text;color:transparent">Wave good-bye</span><span style="font-size:16.0pt;font-family:得意黑"></span><span style="font-size:16.0pt;font-family:得意黑;background-clip:text;-webkit-background-clip:text;color:transparent">to the past
when hope and faith</span></p><p class="true"><span style="font-size:16.0pt;font-family:得意黑;background-clip:text;-webkit-background-clip:text;color:transparent">have grown so strong and sound</span></p><p class="true"><span style="font-size:16.0pt;font-family:得意黑;color:#A3D8A3">Oh<del>~</del><del>~</del><del>~</del><del>~</del>~~grown
so strong and sound</span></p><p class="true"><span style="font-size:16.0pt;font-family:得意黑;color:#BEE3F8">Unfold this pair of wings</span><span style="font-size:16.0pt;font-family:得意黑;background-clip:text;-webkit-background-clip:text;color:transparent">for me
again</span></p><p class="true"><span style="font-size:16.0pt;font-family:得意黑;color:#E6A26C">To soar above this world </span><span style="font-size:16.0pt;font-family:得意黑;color:#BEE3F8">soar
above this world</span></p><p class="true"><span style="font-size:16.0pt;font-family:得意黑;color:#E6A26C">Turned into a moon that
always tells the warmth</span></p><p class="true"><span style="font-size:16.0pt;font-family:得意黑;color:#E6A26C">and</span><span style="font-size:16.0pt;font-family:得意黑;background-clip:text;-webkit-background-clip:text;color:transparent">brightness
of the sun<span style="color:#A3D8A3">and brightness of the sun</span></span></p><p class="true"><span style="font-size:16.0pt;font-family:得意黑;color:#E6A26C">May all</span><span style="font-size:16.0pt;font-family:得意黑;color:#BEE3F8">May all </span><span style="font-size:16.0pt;font-family:得意黑;background-clip:text;-webkit-background-clip:text;color:transparent">the beauty be </span><span style="font-size:16.0pt;font-family:得意黑;background-clip:text;-webkit-background-clip:text;color:transparent">blessed</span></p><p class="true"><span style="font-size:16.0pt;font-family:得意黑;color:#E6A26C">May all the beauty be</span><span style="font-size:16.0pt;font-family:得意黑;background-clip:text;-webkit-background-clip:text;color:transparent">blessed</span></p></div></div><p style="text-align:right"><a href="https://me.bcw2.top:3443/posts/game/moon-halo#comments">看完了？说点什么呢</a></p></div>]]></description><link>https://me.bcw2.top:3443/posts/game/moon-halo</link><guid isPermaLink="true">https://me.bcw2.top:3443/posts/game/moon-halo</guid><dc:creator><![CDATA[bcw222]]></dc:creator><pubDate>Sat, 24 May 2025 16:01:11 GMT</pubDate></item><item><title><![CDATA[ESP32C3TFT 开发板图片上传与固件管理]]></title><description><![CDATA[<div><blockquote>该渲染由 Shiro API 生成，可能存在排版问题，最佳体验请前往：<a href="https://me.bcw2.top:3443/posts/tech/ESP32C3TFT-tutorial">https://me.bcw2.top:3443/posts/tech/ESP32C3TFT-tutorial</a></blockquote><div><p>欢迎来到 ESP32C3TFT 开发板图片上传与固件管理指南！本指南将引导您完成安装图片与解决常见问题的全部流程。</p><h2 id="1--thonny-ide">1. 安装 Thonny IDE</h2><p>Thonny 是一个简单易用的 Python 集成开发环境（IDE），它提供了良好的 MicroPython 支持。在本教程中，我们将使用 Thonny 来连接和管理 ESP32C3TFT 开发板。</p><ol start="1"><li>请访问 Thonny 官方网站：<a href="https://thonny.org/">thonny.org</a> <a href="https://file.bcw2.top:3443/s/gdEHa?password=ESP32C3TFT">镜像地址</a></li><li>下载适合您操作系统的版本（Windows、macOS 或 Linux）。</li><li>按照提示安装 Thonny（通常只需点击“下一步”即可完成安装）。</li></ol><h2 id="2--240x240-">2. 转换图片为 240x240 格式</h2><p>由于本设备的显示屏分辨率为 240x240，为了确保较好的显示效果并节省存储空间，我们需要将图片转换为 240x240 的格式。</p><ol start="1"><li>准备您想要显示的图片。</li><li>使用图像编辑软件（例如 Windows 系统自带的“照片”应用）打开该图片。</li><li>将图片的大小调整为 240x240 像素。</li><li>将调整好的图片保存为 PNG 格式。</li><li>将图片文件重命名为您喜欢的名称。</li></ol><h2 id="3-">3. 复制图片到开发板</h2><ol start="1"><li>在 Thonny 中点击工具栏的“视图”选项，打开“文件”和“终端”面板。</li><li>确保您的开发板已经连接至电脑。此时，在文件视图中您应该能够同时看到电脑和设备上的文件；在终端中也没有出现错误信息。</li><li>在终端中键入 <code>Control + C</code>，直到显示以下内容：
<code>python
MicroPython (固件版本) on (固件编译日期); ESP32C3 module with ESP32C3
Type &quot;help()&quot; for more information.
&gt;&gt;&gt;
</code>
如果连接出现异常，请点击页面上方的“Stop/Restart”按钮，然后重新连接设备。</li><li>在电脑的文件系统中，导航至您转换好的图片所在的文件夹。</li><li>在设备文件系统中，双击打开 <code>imgs</code> 文件夹。</li><li>在电脑文件系统中，选中转换好的图片，右键点击并选择“上传到 imgs”。</li></ol><h2 id="">常见问题</h2><h3 id="">无法连接开发板</h3><p>如果您将 ESP32C3TFT 开发板连接到电脑后，Thonny 无法检测到设备，请按照以下步骤检查连接是否正常：</p><ol start="1"><li>打开设备管理器（Windows）、系统报告（macOS）或使用 lsusb 命令（Linux）。</li><li>查看是否有新的串口设备出现。如果没有，请尝试更换 USB 端口或 USB 线缆（确保您的 USB 线缆至少是 USB 2.0 的数据线）。</li><li>如果有串口设备出现，但 Thonny 仍然无法连接，请尝试重新烧写固件。</li></ol><h3 id="">如何烧写固件</h3><p>这一步通常在设备出厂前已经完成。如果您的设备无法正常连接或需要更新固件，请按照以下步骤操作：</p><ol start="1"><li>确保开发板已经正确连接，并尝试在按下 BOOT 按钮的同时为设备上电。</li><li>打开 Thonny IDE。</li><li>在“解释器”选项卡中，点击“配置解释器”。</li><li>点击“安装或更新 MicroPython（esptool）”。</li><li>点击界面右下角的按钮 ☰。</li><li>选择相应的串口设备作为目标设备。</li><li>点击“选择本地 Micropython 镜像”（Select local Micropython image...）。</li><li>选择包含 ST7789 驱动的适合的 MicroPython 固件文件（可以从<a href="https://github.com/bcw222/st7789_mpy/actions">这个仓库下载最新版</a>或使用<a href="https://file.bcw2.top:3443/s/b0XuY?password=ESP32C3TFT">镜像地址，密码为 ESP32C3TFT</a>）。</li></ol><h3 id="">如何更新程序</h3><p>如果您需要更新设备上的程序，请按照以下步骤操作：</p><ol start="1"><li>在<a href="https://gogs.bcw2.top:3443/bcw0613/ESP32C3TFT/releases">这里</a>下载所需的 dist.zip 文件。</li><li>解压 dist.zip 文件。</li><li>在 Thonny 中点击工具栏的“视图”选项，打开“文件”和“终端”面板。</li><li>确保您的开发板已经连接至电脑。此时，在文件视图中您应该能够同时看到电脑和设备上的文件；在终端中也没有出现错误信息。</li><li>在终端中键入 <code>Control + C</code>，直到显示以下内容：
<code>python
MicroPython (固件版本) on (固件编译日期); ESP32C3 module with ESP32C3
Type &quot;help()&quot; for more information.
&gt;&gt;&gt;
</code>
如果连接出现异常，请点击页面上方的“Stop/Restart”按钮，然后重新连接设备。</li><li>在电脑文件中，导航到解压后的 dist.zip 文件夹</li><li>在设备文件中，右键删除所有文件（imgs中的文件可保留）</li><li>在电脑文件中，选中 dist.zip 文件夹中的所有文件，右键点击并选择 “上传到 /”</li></ol><p>希望本指南对您有所帮助！祝您开发愉快！</p></div><p style="text-align:right"><a href="https://me.bcw2.top:3443/posts/tech/ESP32C3TFT-tutorial#comments">看完了？说点什么呢</a></p></div>]]></description><link>https://me.bcw2.top:3443/posts/tech/ESP32C3TFT-tutorial</link><guid isPermaLink="true">https://me.bcw2.top:3443/posts/tech/ESP32C3TFT-tutorial</guid><dc:creator><![CDATA[bcw222]]></dc:creator><pubDate>Fri, 23 May 2025 09:10:07 GMT</pubDate></item><item><title><![CDATA[从踩坑到封神：一个ESP32C3开发板的硬核重生录]]></title><description><![CDATA[<div><blockquote>该渲染由 Shiro API 生成，可能存在排版问题，最佳体验请前往：<a href="https://me.bcw2.top:3443/posts/tech/ESP32C3TFT">https://me.bcw2.top:3443/posts/tech/ESP32C3TFT</a></blockquote><div><h4 id="">一、命名の暴力美学</h4><p>「ESP32C3TFT」——这名字就像把泡面调料包全倒进去一样简单粗暴。主控 ESP32C3 +插拔式 ST7789 屏 + ADXL345 加速度计，齐活！当同龄人沉迷亚克力挂件时，我选择在书包上挂电路板——毕竟用 MicroPython 写个会动的电子挂件，可比塑料小人硬核多了。</p><hr/><h4 id="">二、现成方案の暴击</h4><p>斥资购入某「ESP32C3TFT128」开发板后遭遇灵魂暴击：</p><ul><li><strong>防水玄学</strong>：前后壳像三明治包装，侧面裸奔接雨水</li><li><strong>续航刺客</strong>：亮屏3小时自动关机，充电时温度直逼暖手宝</li><li><strong>焊死美学</strong>：128x128 TN 屏自带斜视黑屏特效，焊死设计=换屏请买新板</li><li><strong>休眠欺诈</strong>：DeepSleep 功耗和醒着时一样，仿佛在说&quot;我睡了，但没完全睡&quot;</li><li><strong>插头寿命论</strong>：每天拔插 MX1.2 5接口，设计寿命几百次还废手指</li></ul><hr/><h4 id="">三、硬件改造の奥义</h4><h5 id="">电源系统の革命</h5><ul><li><strong>LDO退散</strong>：原板用 LDO 分压，USB 5V→3.3V 时 1.7V压降×电流=活体暖宝宝。换上 TLV62568 DCDC 方案效率起飞（德仪虽贵但香，自用必须堆料！）</li><li><strong>充电芯片起义</strong>：杂牌IC换成 TP4056，实测可用手摸（原厂充电IC烫到能烤肉末）</li><li><strong>二极管の背叛</strong>：试图用二极管做电源或门，结果锂电池最低 3.7V 经 0.7V 压降只剩 3V，连屏幕都点不亮</li><li><strong>MOS管救世</strong>：改用 SI2301 管理，USB 供电时自动切换电源路径，电池电压原厂直供不缩水</li></ul><h5 id="">外设の尊严</h5><ul><li><strong>屏幕自由宣言</strong>：换装国产 240x240 IPS 插接屏（8.4元/片，弄坏就换）</li><li><strong>传感器の逆袭</strong>：ADXL345 共享 SPI 总线，中断引脚直连 GPIO3 实现免占用 SPI 触发休眠</li><li><strong>按键自由</strong>：剩余 GPIO 全接按键开关，Micropython 自定义控制逻辑</li></ul><h5 id="">玄学调试实录</h5><ul><li><strong>DeepSleep之谜</strong>：补上 32.768kHz 晶振后深度睡眠功耗直降（原厂竟省了这玩意！RTC时钟缺失导致休眠了个寂寞）</li><li><strong>天线の诅咒</strong>：第一次外包画板显示屏线序接反，第二版换便宜PCBA连USB都认不出。最终天线周边铺满地——信号？能连WiFi就行！</li><li><strong>叠层の美学</strong>：选 QFN 封装 ESP32C3 + 叠层 Flash，1.6mm 板厚控制完爆 3mm 厚的模块方案</li></ul><p><a href="https://my.bcw2.top:3443/api/v2/objects/file/omk5gidofzlg1quij8.pdf">原理图点这里</a></p><hr/><h4 id="">四、代码の炼狱</h4><h5 id="">驱动の黑暗森林</h5><ul><li><strong>ST7789历险</strong>：致敬开源驱动！预编译固件旧版 MicroPython 计时器 bug（硬件计时器0和2能用，1号神秘消失），自行构建MicroPython v1.25.0解决</li><li><strong>ADXL345の试炼</strong>：全网唯一SPI驱动没有中断？啃完62页 datasheet 手搓代码（注释比代码长3倍），寄存器配置精确到bit级</li></ul><h5 id="">内存の背叛</h5><ul><li><strong>OOM惊魂夜</strong>：<code>gc.free_mem()</code> 显示内存充足，但 <code>bytearray()</code> 死活分配不出来——用 mpy-cross 预编译后原地复活（文档警告：未经编译的.py文件注释过长会撑爆内存！）</li><li><strong>SPI总线の修罗场</strong>：忘记置高显示屏 CS 引脚，导致 ADXL345 读数全乱码（原来显示屏驱动不会自动释放总线！）</li></ul><h5 id="">续航の黑魔法</h5><ul><li><strong>摆烂检测器</strong>：ADXL345 中断触发静止超时，自动 DeepSleep（静止 uint8 max=255秒休眠）</li><li><strong>NVS存档术</strong>：用 ESP32 的 NVS 保存开机图片索引，避免每次改图片都要连电脑重命名文件</li><li><strong>终极省电</strong>：关屏时切断屏幕背光供电+deepsleep（关屏待机≈电子尸体）</li></ul><hr/><h4 id="">五、外壳の战争</h4><ul><li><strong>初版亚克力壳</strong>：公差堪比山寨散装乐高，硬塞半小时后得分0.8（厂家：±0.5mm误差正常啦）</li><li><strong>代工の真香</strong>：把板+壳打包寄回厂家代工组装（社恐の胜利：加钱能解决的事绝不自己动手）</li><li><strong>透明の美学</strong>：2.7mm 亚克力拼接，成品厚度＜1cm，挂链加持后荣升书包最硬核挂件</li></ul><hr/><h4 id="">六、成果发布会</h4><ul><li><strong>续航奇迹</strong>：标称10小时，实际亮屏20小时+</li><li><strong>温度の尊严</strong>：连续工作＜40℃，被窝使用安全认证√</li><li><strong>防水の玄学</strong>：至少没被小雨当场送走（设计理念：只要水没淹到芯片引脚就算胜利）</li><li><strong>社死名场面</strong>：在学校被问&quot;这玩意能玩原神吗？&quot;</li></ul><hr/><h3 id="">后记：硬核浪漫</h3><p>当同学们炫耀新挂件时，我默默掏出烙铁：「要给你焊个会动的吗？」——所谓极客的浪漫，就是把开发板变成随身携带的赛博护身符。</p><p>（项目成本核算：五个打板费79.84+元器件300.37+贴片费919.19+外壳定制250=1,444.95元。性价比？硬核玩家的事，能叫贵吗？）</p><blockquote><p>技术宅的快乐：用20小时续航打脸商家参数，用透明外壳嘲讽工业设计，用自研驱动证明——有些坑，跳进去是为了爬出来时更闪耀。</p></blockquote></div><p style="text-align:right"><a href="https://me.bcw2.top:3443/posts/tech/ESP32C3TFT#comments">看完了？说点什么呢</a></p></div>]]></description><link>https://me.bcw2.top:3443/posts/tech/ESP32C3TFT</link><guid isPermaLink="true">https://me.bcw2.top:3443/posts/tech/ESP32C3TFT</guid><dc:creator><![CDATA[bcw222]]></dc:creator><pubDate>Sat, 17 May 2025 03:26:01 GMT</pubDate></item></channel></rss>