mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-27 17:51:20 -08:00
docs(site): [SITE-04] document PiP and ASR contracts in both languages
This commit is contained in:
1 parent
3138812610
commit
94adb77fd6
97 files changed
+4318
-1817
No files matched your search
@@ -0,0 +1,151 @@
|
||||
# 音频采集与识别字幕
|
||||
|
||||
[English](../en/plugin/asr.md)
|
||||
|
||||
从播放器视频采集音频,将 PCM/WAV 片段交给应用提供的识别回调,并显示回调返回的字幕。插件不内置识别模型或网络服务,也不读取麦克风。本页描述当前未发布的重构分支;新增类型入口和采集选项不等于线上版本已具备这些能力。
|
||||
|
||||
## 安装和本地示例
|
||||
|
||||
```sh
|
||||
yarn add artplayer artplayer-plugin-asr
|
||||
```
|
||||
|
||||
```js
|
||||
import Artplayer from 'artplayer';
|
||||
import artplayerPluginAsr from 'artplayer-plugin-asr';
|
||||
```
|
||||
|
||||
使用 script 时先加载 ArtPlayer,再加载 `dist/artplayer-plugin-asr.js`,全局名为 `artplayerPluginAsr`。以下保留 `asr.local` 原始示例:只采集本地站点的样本视频并显示统计,字幕是模拟文字,不上传音频,也不代表识别准确率。识别服务由应用在回调内自行接入。
|
||||
|
||||
<div className="run-code" data-libs="./uncompiled/artplayer-plugin-asr/index.js">▶ Run Code</div>
|
||||
|
||||
```js
|
||||
/* global Artplayer, artplayerPluginAsr */
|
||||
// Local audio capture demo. The subtitles below are simulated, not recognized speech.
|
||||
// No audio is uploaded; only the sample media is loaded from this local site.
|
||||
const statistics = document.createElement('div')
|
||||
statistics.textContent = 'Local ASR demo: press play. No recognition service is used.'
|
||||
let chunks = 0
|
||||
let pcmBytes = 0
|
||||
let wavBytes = 0
|
||||
|
||||
const art = new Artplayer({
|
||||
container: '.artplayer-app',
|
||||
url: '/assets/sample/steve-jobs.mp4',
|
||||
autoSize: true,
|
||||
fullscreen: true,
|
||||
fullscreenWeb: true,
|
||||
layers: [{
|
||||
name: 'asr-local-statistics',
|
||||
html: statistics,
|
||||
style: {
|
||||
position: 'absolute',
|
||||
top: '12px',
|
||||
left: '12px',
|
||||
right: '12px',
|
||||
padding: '8px 12px',
|
||||
background: 'rgba(0, 0, 0, 0.65)',
|
||||
color: '#fff',
|
||||
fontSize: '12px',
|
||||
whiteSpace: 'pre-line',
|
||||
pointerEvents: 'none',
|
||||
},
|
||||
}],
|
||||
controls: [{
|
||||
name: 'asr-local-stop',
|
||||
position: 'right',
|
||||
html: 'Stop ASR',
|
||||
tooltip: 'Stop capture; pause and play to restart',
|
||||
async click() {
|
||||
await art.plugins.artplayerPluginAsr.stop()
|
||||
if (!art.isDestroy)
|
||||
statistics.textContent = 'Local capture stopped. Pause and play to restart. Nothing was uploaded.'
|
||||
},
|
||||
}],
|
||||
plugins: [artplayerPluginAsr({
|
||||
length: 2,
|
||||
interval: 250,
|
||||
sampleRate: 16000,
|
||||
autoHideTimeout: 5000,
|
||||
onAudioChunk({ pcm, wav }) {
|
||||
if (art.isDestroy)
|
||||
return
|
||||
chunks++
|
||||
pcmBytes += pcm.byteLength
|
||||
wavBytes += wav.byteLength
|
||||
const sampleRate = new DataView(wav).getUint32(24, true)
|
||||
const samples = new DataView(pcm)
|
||||
let peak = 0
|
||||
for (let offset = 0; offset < pcm.byteLength; offset += 2)
|
||||
peak = Math.max(peak, Math.abs(samples.getInt16(offset, true)))
|
||||
const duration = (pcmBytes / 2 / sampleRate).toFixed(2)
|
||||
statistics.textContent = [
|
||||
'Local capture only - simulated subtitles, no speech recognition',
|
||||
`Chunks: ${chunks} | ${sampleRate} Hz mono PCM16 | ${duration} seconds captured`,
|
||||
`PCM: ${pcmBytes} bytes | WAV: ${wavBytes} bytes | Current peak: ${peak}`,
|
||||
].join('\n')
|
||||
return `Simulated local subtitle: audio chunk ${chunks} received.`
|
||||
},
|
||||
})],
|
||||
})
|
||||
```
|
||||
|
||||
## 配置和音频片段
|
||||
|
||||
| 字段 | 默认值 | 含义 |
|
||||
| --- | --- | --- |
|
||||
| `length` | `3` | 从本次字幕按标点拆分的非空片段中保留最后多少段,不是音频秒数 |
|
||||
| `interval` | `100` | 采集消费定时器间隔和每片音频的目标时长,单位毫秒 |
|
||||
| `sampleRate` | `16000` | 请求的采样率,单位 Hz;需要环境支持 |
|
||||
| `autoHideTimeout` | `10000` | 接受字幕后自动隐藏的等待时间,单位毫秒 |
|
||||
| `onAudioChunk` | 返回 `null` 的函数 | 接收 `{ pcm, wav }`;返回字符串时显示字幕,可返回 Promise |
|
||||
| `audioInput` | 未设置 | 默认直接 Web Audio 路径;`{ type: 'capture' }` 显式选择捕获流 |
|
||||
|
||||
`pcm` 和 `wav` 都是 `ArrayBuffer`。音频来自第一声道,PCM 为有符号 16 位小端,WAV 包含 44 字节单声道头。每片样本数为 `Math.floor(sampleRate * interval / 1000)`,需要至少一个样本和正的有限 interval。实际回调频率受音频就绪、浏览器调度和识别耗时影响,不承诺精确间隔。
|
||||
|
||||
当前采集代内一次最多等待一个识别回调;未满一片的样本继续排队。回调拒绝会记录错误并允许后续处理。积压超过约一分钟音频或两片中较大的上限时,采集暂停并记录错误,避免无限占用内存。暂停、切源、停止或销毁使旧回调结果失效;插件不会取消应用已经发送的网络请求,应用应自行管理请求资源。
|
||||
|
||||
## 字幕与生命周期
|
||||
|
||||
插件结果在 `art.plugins.artplayerPluginAsr`,`name` 固定为 `artplayerPluginAsr`:
|
||||
|
||||
| 方法 | 行为 |
|
||||
| --- | --- |
|
||||
| `append(text)` | 同步返回 `undefined`;显示本次文本的最后 `length` 段并重置隐藏定时器,替换已有字幕,不累积历史 |
|
||||
| `hide()` | 同步返回 `undefined`;隐藏字幕,不停止采集或清空文字 |
|
||||
| `stop()` | 实际返回 `Promise<void>`;停止本次采集并丢弃旧结果,之后的播放事件可以重新开始 |
|
||||
|
||||
`append` 和回调返回的字符串保留历史 HTML 渲染行为,不会自动转义。只传入可信字幕;识别服务返回的外部文字需要由应用转义或清理。`null`、`undefined` 或其他非字符串回调结果不会更新字幕。`stop()` 不立即隐藏已有字幕,原自动隐藏时间仍然有效。
|
||||
|
||||
播放事件启动采集,暂停停止记录并丢弃待处理结果,切源重新建立适用的采集状态,媒体错误执行非终止的停止。默认直接连接会保留视频的音频输出路线直到 `art.destroy()`,因此 stop 后仍可正常播放;这不是资源未清理。销毁释放插件监听器、定时器、录音资源和音频上下文。插件没有单独的公开 `start()` 或 `destroy()`。
|
||||
|
||||
## 音频路径与跨域
|
||||
|
||||
默认路径读取 `art.video`,不混合独立 Audio Track 插件的音轨;视频自身的音量和静音会影响所采音频。跨域媒体需要加载前设置 `moreVideoAttr: { crossOrigin: 'anonymous' }`,同时服务端提供允许的 CORS 响应。仅视频可播放不代表 Web Audio 可以读取:无访问权限时可能仍走时间但音频输出静音、产生全零数据或收不到片段,重定向后的跨域同样受限。
|
||||
|
||||
如果应用已有 Web Audio 图,请显式选择 `audioInput: { type: 'capture' }`。此模式通过 `captureStream/mozCaptureStream` 采集,不接管已有播放输出,也不会在失败时退回直接连接。它只释放自己创建的上下文和捕获轨道;暂停保留采集图,stop 或切源释放,后续播放重新获取。缺少能力或初始化失败会记录错误并清理资源。
|
||||
|
||||
捕获流数据可能不受视频音量或静音影响,不包含外部音效图或独立音轨的最终混音。CORS 仍适用:可能拒绝捕获,也可能有轨道却没有可读音频。不要把某个桌面浏览器通过解释为 Safari、手机、所有代理或物理扬声器均已验证。
|
||||
|
||||
## TypeScript 入口
|
||||
|
||||
根入口保留历史 `AsrPluginOption`、`AsrPluginInstance` 和 `AudioChunk`:回调为 `void | Promise<void>`,stop 为 void。要在类型中返回识别字符串、等待 stop 或设置捕获流,使用同一运行时实现的 `/runtime`:
|
||||
|
||||
```ts
|
||||
import asr from 'artplayer-plugin-asr/runtime';
|
||||
import type { RuntimeResult } from 'artplayer-plugin-asr/runtime';
|
||||
|
||||
const installAsr = asr({
|
||||
audioInput: { type: 'capture' },
|
||||
onAudioChunk({ pcm, wav }) {
|
||||
console.log(pcm.byteLength, wav.byteLength);
|
||||
return null; // Replace with your recognizer; null displays no subtitle.
|
||||
},
|
||||
});
|
||||
|
||||
async function stopAsr(plugin: RuntimeResult): Promise<void> {
|
||||
await plugin.stop();
|
||||
}
|
||||
```
|
||||
|
||||
精确类型为 `RuntimeOption`、`RuntimeResult`、`RuntimeFactory`,并导出 `AudioChunk`。旧根入口在部分 NodeNext ESM 消费中保留模块命名空间形状;需要可调用默认导入时选择 `/runtime`。CommonJS 运行时同时支持函数本身和 `.default` 自引用,`/runtime` 也支持 TypeScript 的 `import = require`。
|
||||
@@ -0,0 +1,95 @@
|
||||
# 文档画中画
|
||||
|
||||
[English](../en/plugin/document-pip.md)
|
||||
|
||||
将整个播放器移到浏览器的 Document Picture-in-Picture 窗口,保留播放器控件。关闭窗口时将同一个播放器节点移回原位置。本页描述当前未发布的重构分支;在线示例和未固定版本的 npm/CDN 包不等于当前候选。
|
||||
|
||||
## 安装和示例
|
||||
|
||||
```sh
|
||||
yarn add artplayer artplayer-plugin-document-pip
|
||||
```
|
||||
|
||||
```js
|
||||
import Artplayer from 'artplayer';
|
||||
import artplayerPluginDocumentPip from 'artplayer-plugin-document-pip';
|
||||
```
|
||||
|
||||
使用 script 时先加载 ArtPlayer,再加载 `dist/artplayer-plugin-document-pip.js`,全局名为 `artplayerPluginDocumentPip`。下面保留[在线示例](https://artplayer.org/?libs=./uncompiled/artplayer-plugin-document-pip/index.js&example=document.pip)的原始代码。
|
||||
|
||||
<div className="run-code" data-libs="./uncompiled/artplayer-plugin-document-pip/index.js">▶ Run Code</div>
|
||||
|
||||
```js
|
||||
// npm i artplayer-plugin-document-pip
|
||||
// import artplayerPluginDocumentPip from 'artplayer-plugin-document-pip';
|
||||
|
||||
const art = new Artplayer({
|
||||
container: '.artplayer-app',
|
||||
url: '/assets/sample/video.mp4',
|
||||
plugins: [
|
||||
artplayerPluginDocumentPip({
|
||||
width: 480,
|
||||
height: 270,
|
||||
fallbackToVideoPiP: true,
|
||||
placeholder: `Playing in Document Picture-in-Picture`,
|
||||
}),
|
||||
],
|
||||
})
|
||||
|
||||
art.on('document-pip', (state) => {
|
||||
console.log('Document Picture-in-Picture', state)
|
||||
})
|
||||
```
|
||||
|
||||
## 配置
|
||||
|
||||
| 字段 | 类型 | 默认值 | 含义 |
|
||||
| --- | --- | --- | --- |
|
||||
| `width` | `number` | `480` | 请求的窗口宽度,由浏览器决定实际尺寸 |
|
||||
| `height` | `number` | `270` | 请求的窗口高度 |
|
||||
| `placeholder` | `string` | `'Playing in Document Picture-in-Picture'` | 原播放器位置的占位文字 |
|
||||
| `fallbackToVideoPiP` | `boolean` | `true` | 未检测到 Document PiP API 时尝试设置 `art.pip = true` |
|
||||
|
||||
插件安装名为 `artplayerPluginDocumentPip`,并添加画中画控制按钮。Document PiP 需要浏览器提供 `documentPictureInPicture.requestWindow`;能力检测为真仍可能因权限、调用环境或用户操作条件而打开失败。请在用户点击处理函数内直接调用 `open()` 或 `toggle()`,避免先等待网络等异步工作而丢失用户激活。
|
||||
|
||||
视频 PiP 降级也受浏览器及当前媒体能力限制。它不会移走整个播放器,不会将插件的 `isActive` 设为真,也不会发出 Document PiP 的激活事件。`close()` 管理 Document PiP 窗口;需要退出视频 PiP 时使用 `art.pip = false`。关闭降级选项不代表浏览器自动获得 Document PiP 能力。
|
||||
|
||||
## 状态、方法和事件
|
||||
|
||||
从 `art.plugins.artplayerPluginDocumentPip` 取得结果:
|
||||
|
||||
| 成员 | 实际行为 |
|
||||
| --- | --- |
|
||||
| `name` | 固定为 `artplayerPluginDocumentPip` |
|
||||
| `isSupported` | 只读 getter,插件创建时的 Document PiP API 能力快照 |
|
||||
| `isActive` | 只读 getter,当前是否持有 Document PiP 会话;不是视频 PiP 状态 |
|
||||
| `open()` | 实际返回 `Promise<void>`;请求并移入播放器,已打开时不重复创建,请求中重复调用共享该次请求 |
|
||||
| `close()` | 实际返回 `Promise<void>`;取消待处理请求或恢复节点并关闭窗口 |
|
||||
| `toggle()` | 同步返回 `undefined`;打开,或关闭当前/正在申请的窗口 |
|
||||
|
||||
成功激活及正常关闭时,播放器发出 `document-pip` 事件,参数分别为 `true` 和 `false`。插件同步更新原有 `artplayer-document-pip` 类并重新绑定跨文档事件,随后安排 resize;不要把此事件理解为一次媒体加载或播放成功。
|
||||
|
||||
正常窗口请求/还原失败会显示 notice 并写入控制台警告,`open()` 不是“返回即保证窗口打开”的成功标记。视频降级设置器抛出的错误仍可使 Promise 拒绝。关闭待处理请求后,迟到的窗口会被关闭,不再接管播放器。销毁播放器会释放窗口、控件、订阅和定时器,不再发出新的插件状态事件。插件没有单独的公开 `destroy()`。
|
||||
|
||||
插件尽力复制播放器所在文档的样式;无法读取的跨域样式及应用自行管理的外部 DOM 仍需在目标环境验证。Canvas、其他代理、键盘焦点和持续播放的支持也应逐一验证,不能仅凭能力字段推断。
|
||||
|
||||
## TypeScript 兼容类型
|
||||
|
||||
根入口和 `/legacy` 保留旧声明:工厂参数对象必填、`Result` 的状态字段可写、方法返回 `void`。JavaScript 实际允许省略配置,状态 getter 不可赋值,`open/close` 返回 Promise。旧类型保留是为了兼容已有消费者和替换函数,不表示运行时字段变成可写。
|
||||
|
||||
本包没有 `/runtime` 子路径。需要精确类型时,将真实工厂显式看作 `RuntimeFactory`:
|
||||
|
||||
```ts
|
||||
import documentPip from 'artplayer-plugin-document-pip';
|
||||
import type { AsyncResult, RuntimeFactory } from 'artplayer-plugin-document-pip';
|
||||
|
||||
const runtimeFactory = documentPip as RuntimeFactory;
|
||||
const installPip = runtimeFactory();
|
||||
const installDefaultPip = runtimeFactory.default({ width: 480 });
|
||||
|
||||
async function closePip(pip: AsyncResult): Promise<void> {
|
||||
await pip.close();
|
||||
}
|
||||
```
|
||||
|
||||
命名类型为 `Option`、`Result`、`AsyncResult`、`Factory`、`RuntimeFactory`。精确视图仅用于未经替换的真实实现,不能将返回 void 的 mock 或被替换方法强转为异步实现。CommonJS 运行时支持 `require(package)(options)` 和 `.default(options)`;旧 `import = require` 类型视图使用 `.default`,需要直接调用时选择精确工厂视图。
|
||||
Reference in new issue
Block a user