Files
ArtPlayer/docs/document/assets/plugin_danmuku-mask.md.DuAZXCsK.js

36 lines
20 KiB
JavaScript
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import{_ as i,o as n,c as e,ak as a,j as t}from"./chunks/framework.xxfEszSJ.js";const u=JSON.parse('{"title":"弹幕遮罩","description":"","frontmatter":{},"headers":[],"relativePath":"plugin/danmuku-mask.md","filePath":"plugin/danmuku-mask.md","lastUpdated":1789501055000}'),l={name:"plugin/danmuku-mask.md"};function p(r,s,d,h,k,o){return n(),e("div",null,s[0]||(s[0]=[a(`<h1 id="弹幕遮罩" tabindex="-1">弹幕遮罩 <a class="header-anchor" href="#弹幕遮罩" aria-label="Permalink to &quot;弹幕遮罩&quot;">​</a></h1><p><a href="./../en/plugin/danmuku-mask.html">English</a></p><p>通过人体分割结果生成 CSS 遮罩,让弹幕避开视频中的人物区域。插件处理核心的 <code>.art-danmuku</code> 层,不修改弹幕队列、条目或视频画面。本页描述未发布重构分支,不将在线示例或导航验证视为模型效果验收。</p><h2 id="安装和示例" tabindex="-1">安装和示例 <a class="header-anchor" href="#安装和示例" aria-label="Permalink to &quot;安装和示例&quot;">​</a></h2><div class="language-sh vp-adaptive-theme line-numbers-mode"><button title="Copy Code" class="copy"></button><span class="lang">sh</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">yarn</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> add</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> artplayer</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> artplayer-plugin-danmuku</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> artplayer-plugin-danmuku-mask</span></span></code></pre><div class="line-numbers-wrapper" aria-hidden="true"><span class="line-number">1</span><br></div></div><div class="language-js vp-adaptive-theme line-numbers-mode"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> Artplayer </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &#39;artplayer&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> artplayerPluginDanmuku </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &#39;artplayer-plugin-danmuku&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> artplayerPluginDanmukuMask </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &#39;artplayer-plugin-danmuku-mask&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">;</span></span></code></pre><div class="line-numbers-wrapper" aria-hidden="true"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br></div></div><p>script 用法先加载核心及两个插件的 dist 文件,全局名分别为 <code>artplayerPluginDanmuku</code>、<code>artplayerPluginDanmukuMask</code>。<a href="https://artplayer.org/?libs=./uncompiled/artplayer-plugin-danmuku/index.js%0A./uncompiled/artplayer-plugin-danmuku-mask/index.js&amp;example=danmuku.mask" target="_blank" rel="noreferrer">原在线示例</a>按此顺序注册,并从站点自己的路径加载 MediaPipe 资源:</p>`,7),t("div",{className:"run-code","data-libs":`./uncompiled/artplayer-plugin-danmuku/index.js
./uncompiled/artplayer-plugin-danmuku-mask/index.js`},"▶ Run Code",-1),a(`<div class="language-js vp-adaptive-theme line-numbers-mode"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;">// npm i artplayer-plugin-danmuku-mask</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;">// import artplayerPluginDanmukuMask from &#39;artplayer-plugin-danmuku-mask&#39;;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;">// npm i @mediapipe/selfie_segmentation</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;">// 把 node_modules/@mediapipe/selfie_segmentation 目录复制到你的项目下</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> art</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> Artplayer</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">({</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> container: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;.artplayer-app&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> url: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;/assets/sample/steve-jobs.mp4&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> autoSize: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> fullscreen: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> fullscreenWeb: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> autoOrientation: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> plugins: [</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> artplayerPluginDanmuku</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">({</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> danmuku: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;/assets/sample/danmuku.xml&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> }),</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> artplayerPluginDanmukuMask</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">({</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> solutionPath: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;/assets/@mediapipe/selfie_segmentation&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> }),</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> ],</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">})</span></span></code></pre><div class="line-numbers-wrapper" aria-hidden="true"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br></div></div><p>部署时确保 solutionPath 下有匹配的 MediaPipe 资源。复制目录本身不证明所有脚本、模型、WASM、网络策略和浏览器能力都可用。</p><h2 id="配置" tabindex="-1">配置 <a class="header-anchor" href="#配置" aria-label="Permalink to &quot;配置&quot;">​</a></h2><p>配置可省略,注册时读取并保存这些值:</p><table tabindex="0"><thead><tr><th>字段</th><th>类型 / 默认值</th><th>实际用途</th></tr></thead><tbody><tr><td><code>solutionPath</code></td><td><code>string</code></td><td>SDK 资源根路径;默认 CDN 未固定版本</td></tr><tr><td><code>modelSelection</code></td><td><code>number</code> / <code>1</code></td><td>历史转发字段,不能据此认为当前模型已切换</td></tr><tr><td><code>smoothSegmentation</code></td><td><code>boolean</code> / <code>true</code></td><td>历史转发字段,显式 false 保留</td></tr><tr><td><code>minDetectionConfidence</code></td><td><code>number</code> / <code>0.5</code></td><td>历史转发字段</td></tr><tr><td><code>minTrackingConfidence</code></td><td><code>number</code> / <code>0.5</code></td><td>历史转发字段</td></tr><tr><td><code>selfieMode</code></td><td><code>boolean</code> / <code>false</code></td><td>历史转发字段</td></tr><tr><td><code>drawContour</code></td><td><code>boolean</code> / <code>false</code></td><td>传给二值遮罩转换</td></tr><tr><td><code>foregroundThreshold</code></td><td><code>number</code> / <code>0.5</code></td><td>传给前景阈值转换</td></tr><tr><td><code>opacity</code></td><td><code>number</code> / <code>1</code></td><td>传给 SDK drawMask,不是直接设置弹幕层 opacity</td></tr><tr><td><code>maskBlurAmount</code></td><td><code>number</code> / <code>3</code></td><td>传给 SDK drawMask 的模糊量</td></tr></tbody></table><p>solutionPath 默认指向未固定版本的 <a href="https://cdn.jsdelivr.net/npm/@mediapipe/selfie_segmentation" target="_blank" rel="noreferrer">jsDelivr MediaPipe 资源根目录</a>。</p><p>除 smoothSegmentation 的 undefined 判断外,默认值沿用 <code>value || default</code>。例如 modelSelection、opacity、threshold、blur 的 0 会回退默认值,不能用于关闭功能;停止遮罩使用 stop。</p><p>当前固定选择 <code>runtime: &#39;mediapipe&#39;</code> 和 <code>modelType: &#39;general&#39;</code>。已安装的 adapter 把 general 映射为 modelSelection 0,并忽略部分额外转发字段;保留参数不是承诺这些字段都会改变推理。TensorFlow 后端先尝试 webgl,只有拒绝时才尝试 cpu;这也不能证明 MediaPipe 实际使用哪种推理后端。</p><h2 id="start、stop-与失败" tabindex="-1">start、stop 与失败 <a class="header-anchor" href="#start、stop-与失败" aria-label="Permalink to &quot;start、stop 与失败&quot;">​</a></h2><p>注册同步返回 <code>{ name: &#39;artplayerPluginDanmukuMask&#39;, start, stop }</code>,可从 <code>art.plugins.artplayerPluginDanmukuMask</code> 访问。ready 自动启动;如果在 ready 之后安装,需要自行调用 start。</p><table tabindex="0"><thead><tr><th>方法</th><th>行为</th></tr></thead><tbody><tr><td><code>start()</code></td><td>返回 <code>Promise&lt;void&gt;</code>;初始化并启动调度,不等待第一张完整遮罩</td></tr><tr><td><code>stop()</code></td><td>同步返回 undefined;取消当前调度并立刻将层的 maskImage 设为 none</td></tr></tbody></table><p>重复 start 不创建重叠推理循环。stop 可在初始化过程中调用,使公共 start 及时结算;不可取消的 SDK 工作仍被观察,迟到结果不再写入遮罩。之后 start 等待旧任务和释放过程结束再重新初始化,不以多个模型并行掩盖停止问题。</p><p>模型创建失败会记录错误且 start 可正常结算,但没有可用模型;可显式再次 start 重试。后端或必要 DOM/Canvas 初始化失败仍可能拒绝 start。自动 ready 启动会记录拒绝。推理或像素读取失败记录错误并继续下一次调度,保留上一张可用遮罩。</p><p>只在视频播放、未结束且有有效尺寸时执行分割。结果经二值遮罩、drawMask 和 Canvas 像素转换生成 PNG data URL,写入整层 maskImage。没有人物结果时保留之前的遮罩;遮罩精度、性能和跨域像素访问需要实际视频验证。</p><h2 id="生命周期和类型" tabindex="-1">生命周期和类型 <a class="header-anchor" href="#生命周期和类型" aria-label="Permalink to &quot;生命周期和类型&quot;">​</a></h2><p>播放器销毁停止运行并移除 ready/destroy 监听器。私有画布归插件所有,SDK 工作完成后重置其尺寸并释放模型。stop 不是 GPU 内存回收完成的 Promise;SDK 的私有资源实际释放完成仍需独立证据。插件不提供公开 destroy、update 或新事件,也不接管其他应用的全局 TensorFlow 生命周期。</p><p>根和 <code>/legacy</code> 保留历史声明:可选配置、同步注册、异步 start 和同步 stop;没有 <code>/runtime</code> 子路径。Option/Result 是声明内部类型,可通过工厂提取。下面仅展示 NodeNext ESM 下的类型提取,不执行 <code>.default</code> 调用:</p><div class="language-ts vp-adaptive-theme line-numbers-mode"><button title="Copy Code" class="copy"></button><span class="lang">ts</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">import</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> MaskModule </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> &#39;artplayer-plugin-danmuku-mask&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">type</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> MaskFactory</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> typeof</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> MaskModule.default;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">type</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> MaskOptions</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> =</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> Parameters</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">&lt;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">MaskFactory</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">&gt;[</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">];</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">type</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> MaskResult</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> =</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> ReturnType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">&lt;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">ReturnType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">&lt;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">MaskFactory</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">&gt;&gt;;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> options</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> MaskOptions</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> { solutionPath: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&#39;/assets/@mediapipe/selfie_segmentation&#39;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> };</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">async</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> function</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> restartMask</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;">mask</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> MaskResult</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> Promise</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">&lt;</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">void</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">&gt; {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> mask.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">stop</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">();</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> mask.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">start</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}</span></span></code></pre><div class="line-numbers-wrapper" aria-hidden="true"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br></div></div><p>保留的 NodeNext 根类型有模块命名空间行为;这不代表当前运行时工厂有 <code>.default</code> 属性。当前 CommonJS 工厂直接调用,ESM 运行时使用默认导出;不要从类型命名空间推断运行时别名。旧版导出形状、模型真实运行和真机性能各自独立验证。</p>`,19)]))}const E=i(l,[["render",p]]);export{u as __pageData,E as default};