mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-24 06:31:20 -08:00
refactor: remove architecture documentation and improve code readability
- Deleted ARCHITECTURE.md file to streamline project documentation. - Refactored AudioEngine, EventTarget, MediaBunnyEngine, VideoEngine, VideoShim, and index.js for improved readability by adding consistent line breaks and spacing. - Enhanced code structure by ensuring single-responsibility principles and clearer flow in methods. - Updated TypeScript definitions for better clarity on options available in the artplayer-proxy-mediabunny package.
This commit is contained in:
1 parent
be44c1f8dd
commit
9d760a2f59
10 files changed
+164
-275
No files matched your search
@@ -277,15 +277,15 @@ function artplayerProxyMediabunny(option = {}) {
|
|||||||
if (typeof window !== 'undefined') window.artplayerProxyMediabunny = artplayerProxyMediabunny;
|
if (typeof window !== 'undefined') window.artplayerProxyMediabunny = artplayerProxyMediabunny;
|
||||||
|
|
||||||
},{"./VideoShim.js":"l1ksT","@parcel/transformer-js/src/esmodule-helpers.js":"8oCsH"}],"l1ksT":[function(require,module,exports,__globalThis) {
|
},{"./VideoShim.js":"l1ksT","@parcel/transformer-js/src/esmodule-helpers.js":"8oCsH"}],"l1ksT":[function(require,module,exports,__globalThis) {
|
||||||
|
var parcelHelpers = require("@parcel/transformer-js/src/esmodule-helpers.js");
|
||||||
|
parcelHelpers.defineInteropFlag(exports);
|
||||||
|
var _eventTargetJs = require("./EventTarget.js");
|
||||||
|
var _eventTargetJsDefault = parcelHelpers.interopDefault(_eventTargetJs);
|
||||||
/**
|
/**
|
||||||
* Video Element Shim
|
* Video Element Shim
|
||||||
* Simulates HTMLVideoElement interface for MediaBunny
|
* Simulates HTMLVideoElement interface for MediaBunny
|
||||||
*/ var parcelHelpers = require("@parcel/transformer-js/src/esmodule-helpers.js");
|
*/ var _mediaBunnyEngineJs = require("./MediaBunnyEngine.js");
|
||||||
parcelHelpers.defineInteropFlag(exports);
|
|
||||||
var _mediaBunnyEngineJs = require("./MediaBunnyEngine.js");
|
|
||||||
var _mediaBunnyEngineJsDefault = parcelHelpers.interopDefault(_mediaBunnyEngineJs);
|
var _mediaBunnyEngineJsDefault = parcelHelpers.interopDefault(_mediaBunnyEngineJs);
|
||||||
var _eventTargetJs = require("./EventTarget.js");
|
|
||||||
var _eventTargetJsDefault = parcelHelpers.interopDefault(_eventTargetJs);
|
|
||||||
function clamp(v, min, max) {
|
function clamp(v, min, max) {
|
||||||
return Math.max(min, Math.min(max, Number(v) || 0));
|
return Math.max(min, Math.min(max, Number(v) || 0));
|
||||||
}
|
}
|
||||||
@@ -601,6 +601,7 @@ class MediaBunnyEngine {
|
|||||||
;
|
;
|
||||||
this.events.emit('loadedmetadata');
|
this.events.emit('loadedmetadata');
|
||||||
this.events.emit('durationchange');
|
this.events.emit('durationchange');
|
||||||
|
this.events.emit('progress');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
@@ -624,6 +625,7 @@ class MediaBunnyEngine {
|
|||||||
this.events.emit('loadeddata');
|
this.events.emit('loadeddata');
|
||||||
this.events.emit('canplay');
|
this.events.emit('canplay');
|
||||||
this.events.emit('canplaythrough');
|
this.events.emit('canplaythrough');
|
||||||
|
this.events.emit('progress');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (id !== this.loadSeq) return;
|
if (id !== this.loadSeq) return;
|
||||||
this.error = {
|
this.error = {
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
{"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;;;CAGC;;6CAGuB;AAFxB;;AAEe,SAAS,yBAAyB,SAAS,CAAC,CAAC;IAC1D,OAAO,CAAC;QACN,MAAM,EAAE,WAAW,EAAE,GAAG;QACxB,MAAM,EAAE,aAAa,EAAE,GAAG,YAAY,KAAK;QAE3C,wBAAwB;QACxB,MAAM,SAAS,cAAc;QAC7B,MAAM,MAAM,OAAO,UAAU,CAAC;QAE9B,oBAAoB;QACpB,MAAM,OAAO,IAAI,CAAA,GAAA,2BAAS,AAAD,EAAE;YACzB;YACA;YACA;YACA;QACF;QAEA,+BAA+B;QAC/B,MAAM,wBAAwB,CAAC;QAC/B,IAAK,MAAM,QAAQ,OACjB,IAAI,OAAO,MAAM,CAAC,KAAK,KAAK,YAC1B,qBAAqB,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC;QAIpD,sDAAsD;QACtD,MAAM,gBAAgB,IAAI,IAAI;eACzB,OAAO,mBAAmB,CAAC;eAC3B,OAAO,mBAAmB,CAAC,OAAO,cAAc,CAAC;SACrD;QAED,gCAAgC;QAChC,KAAK,MAAM,QAAQ,cAAe;YAChC,IAAI,SAAS,eAAe;YAC5B,IAAI,CAAE,CAAA,QAAQ,MAAK,GACjB,OAAO,cAAc,CAAC,QAAQ,MAAM;gBAClC;oBACE,MAAM,QAAQ,IAAI,CAAC,KAAK;oBACxB,OAAO,OAAO,UAAU,aAAa,MAAM,IAAI,CAAC,QAAQ;gBAC1D;gBACA,KAAI,CAAC;oBACH,IAAI,CAAC,KAAK,GAAG;gBACf;gBACA,cAAc;gBACd,YAAY;YACd;QAEJ;QAEA,kCAAkC;QAClC,IAAK,MAAM,QAAQ,sBACjB,MAAM,CAAC,KAAK,GAAG,CAAC,GAAG,OAAS,qBAAqB,CAAC,KAAK,IAAI;QAG7D,gBAAgB;QAChB,SAAS;YACP,MAAM,SAAS,IAAI,QAAQ,EAAE;YAC7B,IAAI,CAAC,UAAU,IAAI,MAAM,CAAC,QAAQ,EAAE;YAEpC,OAAO,MAAM,CAAC,OAAO,KAAK,EAAE;gBAC1B,OAAO;gBACP,QAAQ;gBACR,WAAW;YACb;QACF;QAEA,IAAI,EAAE,CAAC,UAAU;QACjB,IAAI,EAAE,CAAC,wBAAwB;QAE/B,qBAAqB;QACrB,IAAI,EAAE,CAAC,WAAW;YAChB,KAAK,OAAO;QACd;QAEA,OAAO;IACT;AACF;AAEA,IAAI,OAAO,WAAW,aACpB,OAAO,wBAAwB,GAAG;;;ACrFpC;;;CAGC;;AACD;;AACA;;AAEA,SAAS,MAAM,CAAC,EAAE,GAAG,EAAE,GAAG;IACxB,OAAO,KAAK,GAAG,CAAC,KAAK,KAAK,GAAG,CAAC,KAAK,OAAO,MAAM;AAClD;AAEe,MAAM;IACnB,YAAY,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,CAAE;QACxC,IAAI,CAAC,GAAG,GAAG;QACX,IAAI,CAAC,MAAM,GAAG;QACd,IAAI,CAAC,MAAM,GAAG;QAEd,eAAe;QACf,IAAI,CAAC,MAAM,GAAG,IAAI,CAAA,GAAA,6BAAW,AAAD;QAE5B,oBAAoB;QACpB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAA,GAAA,kCAAgB,AAAD,EAAE;YAAE;YAAQ;YAAK,QAAQ,IAAI,CAAC,MAAM;YAAE;QAAO;QAE9E,iBAAiB;QACjB,IAAI,CAAC,IAAI,GAAG;QACZ,IAAI,CAAC,OAAO,GAAG,OAAO,MAAM,IAAI;QAChC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,OAAO,KAAK;QAC5B,IAAI,CAAC,aAAa,GAAG;QAErB,uBAAuB;QACvB,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,MAAM;QAE/C,8BAA8B;QAC9B,IAAI,CAAC,oBAAoB;QAEzB,mBAAmB;QACnB,IAAI,OAAO,MAAM,EACf,IAAI,CAAC,GAAG,GAAG,OAAO,MAAM;aACnB,IAAI,IAAI,MAAM,EAAE,KACrB,IAAI,CAAC,GAAG,GAAG,IAAI,MAAM,CAAC,GAAG;IAE7B;IAEA,uBAAuB;QACrB,MAAM,EAAE,QAAQ,SAAS,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,MAAM;QACzD,UAAU,OAAO,CAAC,CAAC;YACjB,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,MAAM,CAAC;gBAClC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,EAAE;YACnC;QACF;IACF;IAEA,gBAAgB;IAChB,iBAAiB,IAAI,EAAE,EAAE,EAAE;QACzB,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,MAAM;IACrC;IAEA,oBAAoB,IAAI,EAAE,EAAE,EAAE;QAC5B,IAAI,CAAC,MAAM,CAAC,mBAAmB,CAAC,MAAM;IACxC;IAEA,SAAS;IACT,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,IAAI;IAClB;IAEA,IAAI,IAAI,CAAC,EAAE;QACT,IAAI,CAAC,IAAI,GAAG;QACZ,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;IAC1B;IAEA,IAAI,aAAa;QACf,OAAO,IAAI,CAAC,IAAI;IAClB;IAEA,OAAO;IACP,IAAI,cAAc;QAChB,OAAO,IAAI,CAAC,MAAM,CAAC,WAAW;IAChC;IAEA,IAAI,YAAY,CAAC,EAAE;QACjB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,MAAM;IAChC;IAEA,IAAI,WAAW;QACb,OAAO,IAAI,CAAC,MAAM,CAAC,QAAQ;IAC7B;IAEA,2BAA2B;IAC3B,IAAI,WAAW;QACb,OAAO,IAAI,CAAC,gBAAgB,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ;IACtD;IAEA,IAAI,SAAS;QACX,OAAO,IAAI,CAAC,gBAAgB,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,WAAW;IACzD;IAEA,IAAI,WAAW;QACb,OAAO,IAAI,CAAC,gBAAgB,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ;IACtD;IAEA,iBAAiB,KAAK,EAAE,GAAG,EAAE;QAC3B,MAAM,WAAW,IAAI,CAAC,MAAM,CAAC,QAAQ;QACrC,IAAI,CAAC,YAAY,OAAO,KAAK,CAAC,aAAa,OAAO,GAChD,OAAO;YAAE,QAAQ;YAAG,OAAO,IAAM;YAAG,KAAK,IAAM;QAAE;QAEnD,OAAO;YACL,QAAQ;YACR,OAAO,IAAM;YACb,KAAK,IAAM;QACb;IACF;IAEA,iBAAiB;IACjB,IAAI,SAAS;QACX,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM;IAC3B;IAEA,IAAI,UAAU;QACZ,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK;IAClD;IAEA,IAAI,QAAQ;QACV,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK;IAC1B;IAEA,IAAI,UAAU;QACZ,OAAO,IAAI,CAAC,MAAM,CAAC,OAAO;IAC5B;IAEA,cAAc;IACd,IAAI,aAAa;QACf,OAAO,IAAI,CAAC,MAAM,CAAC,UAAU;IAC/B;IAEA,IAAI,eAAe;QACjB,OAAO,IAAI,CAAC,MAAM,CAAC,YAAY;IACjC;IAEA,IAAI,QAAQ;QACV,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK;IAC1B;IAEA,gBAAgB;IAChB,IAAI,eAAe;QACjB,OAAO,IAAI,CAAC,aAAa;IAC3B;IAEA,IAAI,aAAa,CAAC,EAAE;QAClB,MAAM,OAAO,OAAO;QACpB,IAAI,OAAO,KAAK,CAAC,SAAS,QAAQ,GAAG;QAErC,IAAI,CAAC,aAAa,GAAG;QACrB,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC;QAC5B,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;IACnB;IAEA,SAAS;IACT,IAAI,SAAS;QACX,OAAO,IAAI,CAAC,OAAO;IACrB;IAEA,IAAI,OAAO,CAAC,EAAE;QACZ,IAAI,CAAC,OAAO,GAAG,MAAM,GAAG,GAAG;QAC3B,IAAI,CAAC,MAAM,GAAG;QACd,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,MAAM;QAC/C,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;IACnB;IAEA,IAAI,QAAQ;QACV,OAAO,IAAI,CAAC,MAAM;IACpB;IAEA,IAAI,MAAM,CAAC,EAAE;QACX,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;QAChB,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,MAAM;QAC/C,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;IACnB;IAEA,mBAAmB;IACnB,OAAO;QACL,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI;IACzB;IAEA,QAAQ;QACN,IAAI,CAAC,MALine truncated
|
{"mappings":"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 truncated
|
||||||
@@ -1,167 +0,0 @@
|
|||||||
# Architecture Documentation
|
|
||||||
|
|
||||||
## 文件结构
|
|
||||||
|
|
||||||
```
|
|
||||||
src/
|
|
||||||
├── index.js # 主入口,创建canvas代理
|
|
||||||
├── VideoShim.js # HTMLVideoElement接口模拟层
|
|
||||||
├── MediaBunnyEngine.js # 主引擎,协调音视频播放
|
|
||||||
├── AudioEngine.js # 音频引擎,处理音频播放
|
|
||||||
├── VideoEngine.js # 视频引擎,处理视频帧渲染
|
|
||||||
└── EventTarget.js # 事件系统实现
|
|
||||||
```
|
|
||||||
|
|
||||||
## 架构设计
|
|
||||||
|
|
||||||
### 层次结构
|
|
||||||
|
|
||||||
```
|
|
||||||
ArtPlayer
|
|
||||||
↓
|
|
||||||
index.js (Canvas Proxy)
|
|
||||||
↓
|
|
||||||
VideoShim (HTMLVideoElement Interface)
|
|
||||||
↓
|
|
||||||
MediaBunnyEngine (Coordination Layer)
|
|
||||||
├→ AudioEngine (Web Audio API)
|
|
||||||
└→ VideoEngine (Canvas Rendering)
|
|
||||||
```
|
|
||||||
|
|
||||||
### 核心组件
|
|
||||||
|
|
||||||
#### 1. index.js
|
|
||||||
- **职责**: 插件入口点,创建canvas元素并代理到VideoShim
|
|
||||||
- **功能**:
|
|
||||||
- 创建canvas和2D上下文
|
|
||||||
- 实例化VideoShim
|
|
||||||
- 将VideoShim的属性和方法代理到canvas对象
|
|
||||||
- 处理ArtPlayer的resize事件
|
|
||||||
- 管理生命周期(destroy)
|
|
||||||
|
|
||||||
#### 2. VideoShim.js
|
|
||||||
- **职责**: 模拟HTMLVideoElement接口
|
|
||||||
- **功能**:
|
|
||||||
- 实现标准video元素的所有属性(currentTime, duration, paused等)
|
|
||||||
- 实现播放控制方法(play, pause, seek)
|
|
||||||
- 处理音量和播放速率
|
|
||||||
- 转发事件到ArtPlayer
|
|
||||||
- 管理MediaBunnyEngine实例
|
|
||||||
|
|
||||||
#### 3. MediaBunnyEngine.js
|
|
||||||
- **职责**: 协调音频和视频引擎
|
|
||||||
- **功能**:
|
|
||||||
- 管理加载流程(load timeout, metadata loading)
|
|
||||||
- 协调play/pause/seek操作
|
|
||||||
- 同步readyState和networkState
|
|
||||||
- 错误处理
|
|
||||||
- 管理播放状态(paused, ended, seeking)
|
|
||||||
|
|
||||||
#### 4. AudioEngine.js
|
|
||||||
- **职责**: 音频播放和时钟源
|
|
||||||
- **功能**:
|
|
||||||
- 使用MediaBunny加载音频轨道
|
|
||||||
- Web Audio API播放音频
|
|
||||||
- 提供准确的currentTime(作为主时钟)
|
|
||||||
- 音量控制和静音
|
|
||||||
- 播放速率调整
|
|
||||||
- 缓冲区监控(starvation detection)
|
|
||||||
|
|
||||||
#### 5. VideoEngine.js
|
|
||||||
- **职责**: 视频帧渲染和同步
|
|
||||||
- **功能**:
|
|
||||||
- 使用MediaBunny解码视频帧
|
|
||||||
- 将帧渲染到canvas
|
|
||||||
- 与音频时钟同步
|
|
||||||
- 处理late frames(可选丢帧)
|
|
||||||
- Poster图片显示
|
|
||||||
- Preflight检查(range requests支持)
|
|
||||||
|
|
||||||
#### 6. EventTarget.js
|
|
||||||
- **职责**: 简单的事件系统
|
|
||||||
- **功能**:
|
|
||||||
- addEventListener
|
|
||||||
- removeEventListener
|
|
||||||
- emit事件
|
|
||||||
|
|
||||||
## 数据流
|
|
||||||
|
|
||||||
### 加载流程
|
|
||||||
```
|
|
||||||
MediaBunnyEngine.load()
|
|
||||||
├→ AudioEngine.load()
|
|
||||||
│ ├→ 创建Input
|
|
||||||
│ ├→ 获取音频轨道
|
|
||||||
│ ├→ 创建AudioBufferSink
|
|
||||||
│ └→ 触发metadata回调
|
|
||||||
└→ VideoEngine.load()
|
|
||||||
├→ Preflight检查(可选)
|
|
||||||
├→ 创建Input
|
|
||||||
├→ 获取视频轨道
|
|
||||||
├→ 创建CanvasSink
|
|
||||||
├→ 初始化iterator
|
|
||||||
└→ 触发metadata回调
|
|
||||||
```
|
|
||||||
|
|
||||||
### 播放流程
|
|
||||||
```
|
|
||||||
MediaBunnyEngine.play()
|
|
||||||
├→ AudioEngine.play()
|
|
||||||
│ ├→ 恢复/创建AudioContext
|
|
||||||
│ └→ 启动音频buffer迭代器
|
|
||||||
└→ VideoEngine.start(audioEngine)
|
|
||||||
├→ 保存audio时钟引用
|
|
||||||
└→ 启动渲染循环(requestAnimationFrame)
|
|
||||||
```
|
|
||||||
|
|
||||||
### 渲染循环
|
|
||||||
```
|
|
||||||
VideoEngine.render()
|
|
||||||
├→ 获取当前音频时间
|
|
||||||
├→ 发送timeupdate事件
|
|
||||||
├→ 检查是否到达结束
|
|
||||||
├→ 如果nextFrame时间到了
|
|
||||||
│ ├→ 清除canvas
|
|
||||||
│ ├→ 绘制frame
|
|
||||||
│ └→ 请求下一帧
|
|
||||||
└→ requestAnimationFrame(render)
|
|
||||||
```
|
|
||||||
|
|
||||||
## 重构改进点
|
|
||||||
|
|
||||||
### 1. 代码组织
|
|
||||||
- ✅ 扁平化目录结构(移除engine/shim/utils子目录)
|
|
||||||
- ✅ 使用ES6类代替工厂函数
|
|
||||||
- ✅ 每个文件单一职责
|
|
||||||
|
|
||||||
### 2. 可读性
|
|
||||||
- ✅ 添加详细的JSDoc注释
|
|
||||||
- ✅ 有意义的变量和方法命名
|
|
||||||
- ✅ 逻辑分组和代码组织
|
|
||||||
|
|
||||||
### 3. 可维护性
|
|
||||||
- ✅ 清晰的依赖关系
|
|
||||||
- ✅ 统一的错误处理
|
|
||||||
- ✅ 一致的代码风格
|
|
||||||
|
|
||||||
### 4. 性能
|
|
||||||
- ✅ 优化的帧同步算法
|
|
||||||
- ✅ 改进的缓冲区监控
|
|
||||||
- ✅ 减少不必要的函数创建
|
|
||||||
|
|
||||||
### 5. 功能
|
|
||||||
- ✅ 移除未使用的工具函数(sleep.js)
|
|
||||||
- ✅ 简化事件系统
|
|
||||||
- ✅ 统一的配置选项
|
|
||||||
|
|
||||||
## 使用的外部库
|
|
||||||
|
|
||||||
- **mediabunny**: WebCodecs封装库,提供Input/Source/Sink抽象
|
|
||||||
- **ArtPlayer**: 视频播放器框架
|
|
||||||
|
|
||||||
## 浏览器API
|
|
||||||
|
|
||||||
- **WebCodecs API**: 视频/音频解码
|
|
||||||
- **Web Audio API**: 音频播放
|
|
||||||
- **Canvas API**: 视频渲染
|
|
||||||
- **requestAnimationFrame**: 渲染循环
|
|
||||||
@@ -14,36 +14,37 @@ import {
|
|||||||
export default class AudioEngine {
|
export default class AudioEngine {
|
||||||
constructor(events) {
|
constructor(events) {
|
||||||
this.events = events
|
this.events = events
|
||||||
|
|
||||||
// MediaBunny instances
|
// MediaBunny instances
|
||||||
this.input = null
|
this.input = null
|
||||||
this.audioSink = null
|
this.audioSink = null
|
||||||
this.audioIterator = null
|
this.audioIterator = null
|
||||||
|
|
||||||
// Web Audio API
|
// Web Audio API
|
||||||
this.audioContext = null
|
this.audioContext = null
|
||||||
this.gainNode = null
|
this.gainNode = null
|
||||||
|
|
||||||
// Playback state
|
// Playback state
|
||||||
this.audioContextStartTime = 0
|
this.audioContextStartTime = 0
|
||||||
this.playbackTimeAtStart = 0
|
this.playbackTimeAtStart = 0
|
||||||
this.latestScheduledEndTime = 0
|
this.latestScheduledEndTime = 0
|
||||||
this.duration = Number.NaN
|
this.duration = Number.NaN
|
||||||
this.paused = true
|
this.paused = true
|
||||||
|
|
||||||
// Audio settings
|
// Audio settings
|
||||||
this.volume = 0.7
|
this.volume = 0.7
|
||||||
this.muted = false
|
this.muted = false
|
||||||
this.playbackRate = 1
|
this.playbackRate = 1
|
||||||
|
|
||||||
// Async control
|
// Async control
|
||||||
this.asyncId = 0
|
this.asyncId = 0
|
||||||
this.queuedNodes = new Set()
|
this.queuedNodes = new Set()
|
||||||
}
|
}
|
||||||
|
|
||||||
get currentTime() {
|
get currentTime() {
|
||||||
if (this.paused) return this.playbackTimeAtStart
|
if (this.paused)
|
||||||
|
return this.playbackTimeAtStart
|
||||||
|
|
||||||
return (
|
return (
|
||||||
(this.audioContext.currentTime - this.audioContextStartTime) * this.playbackRate
|
(this.audioContext.currentTime - this.audioContextStartTime) * this.playbackRate
|
||||||
+ this.playbackTimeAtStart
|
+ this.playbackTimeAtStart
|
||||||
@@ -51,8 +52,10 @@ export default class AudioEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
normalizeSource(src) {
|
normalizeSource(src) {
|
||||||
if (typeof src === 'string') return new UrlSource(src)
|
if (typeof src === 'string')
|
||||||
if (src instanceof Blob) return new BlobSource(src)
|
return new UrlSource(src)
|
||||||
|
if (src instanceof Blob)
|
||||||
|
return new BlobSource(src)
|
||||||
if (typeof ReadableStream !== 'undefined' && src instanceof ReadableStream) {
|
if (typeof ReadableStream !== 'undefined' && src instanceof ReadableStream) {
|
||||||
return new ReadableStreamSource(src)
|
return new ReadableStreamSource(src)
|
||||||
}
|
}
|
||||||
@@ -60,13 +63,15 @@ export default class AudioEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ensureAudioContext(sampleRate) {
|
ensureAudioContext(sampleRate) {
|
||||||
if (this.audioContext) return
|
if (this.audioContext)
|
||||||
|
return
|
||||||
|
|
||||||
const AudioContext = window.AudioContext || window.webkitAudioContext
|
const AudioContext = window.AudioContext || window.webkitAudioContext
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.audioContext = new AudioContext({ sampleRate })
|
this.audioContext = new AudioContext({ sampleRate })
|
||||||
} catch {
|
}
|
||||||
|
catch {
|
||||||
this.audioContext = new AudioContext()
|
this.audioContext = new AudioContext()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,7 +81,8 @@ export default class AudioEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateGain() {
|
updateGain() {
|
||||||
if (!this.gainNode) return
|
if (!this.gainNode)
|
||||||
|
return
|
||||||
const v = this.muted ? 0 : this.volume
|
const v = this.muted ? 0 : this.volume
|
||||||
this.gainNode.gain.value = v * v
|
this.gainNode.gain.value = v * v
|
||||||
}
|
}
|
||||||
@@ -102,7 +108,8 @@ export default class AudioEngine {
|
|||||||
this.audioContextStartTime = 0
|
this.audioContextStartTime = 0
|
||||||
|
|
||||||
const source = this.normalizeSource(src)
|
const source = this.normalizeSource(src)
|
||||||
if (!source) return
|
if (!source)
|
||||||
|
return
|
||||||
|
|
||||||
this.input = new Input({
|
this.input = new Input({
|
||||||
source,
|
source,
|
||||||
@@ -110,7 +117,8 @@ export default class AudioEngine {
|
|||||||
})
|
})
|
||||||
|
|
||||||
this.duration = await this.input.computeDuration()
|
this.duration = await this.input.computeDuration()
|
||||||
if (id !== this.asyncId) return
|
if (id !== this.asyncId)
|
||||||
|
return
|
||||||
|
|
||||||
const audioTrack = await this.input.getPrimaryAudioTrack()
|
const audioTrack = await this.input.getPrimaryAudioTrack()
|
||||||
if (!audioTrack) {
|
if (!audioTrack) {
|
||||||
@@ -134,13 +142,15 @@ export default class AudioEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async runIterator(localId) {
|
async runIterator(localId) {
|
||||||
if (!this.audioSink) return
|
if (!this.audioSink)
|
||||||
|
return
|
||||||
|
|
||||||
await this.stopIterator()
|
await this.stopIterator()
|
||||||
this.audioIterator = this.audioSink.buffers(this.currentTime)
|
this.audioIterator = this.audioSink.buffers(this.currentTime)
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
if (localId !== this.asyncId || this.paused) return
|
if (localId !== this.asyncId || this.paused)
|
||||||
|
return
|
||||||
|
|
||||||
const nextPromise = this.audioIterator.next()
|
const nextPromise = this.audioIterator.next()
|
||||||
|
|
||||||
@@ -150,10 +160,10 @@ export default class AudioEngine {
|
|||||||
clearInterval(checkStarvation)
|
clearInterval(checkStarvation)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
this.audioContext.state === 'running' &&
|
this.audioContext.state === 'running'
|
||||||
this.audioContext.currentTime >= this.latestScheduledEndTime - 0.2
|
&& this.audioContext.currentTime >= this.latestScheduledEndTime - 0.2
|
||||||
) {
|
) {
|
||||||
this.audioContext.suspend()
|
this.audioContext.suspend()
|
||||||
this.events.emit('waiting')
|
this.events.emit('waiting')
|
||||||
@@ -163,14 +173,17 @@ export default class AudioEngine {
|
|||||||
let result
|
let result
|
||||||
try {
|
try {
|
||||||
result = await nextPromise
|
result = await nextPromise
|
||||||
} catch (e) {
|
}
|
||||||
|
catch (e) {
|
||||||
console.error('Audio iterator error:', e)
|
console.error('Audio iterator error:', e)
|
||||||
break
|
break
|
||||||
} finally {
|
}
|
||||||
|
finally {
|
||||||
clearInterval(checkStarvation)
|
clearInterval(checkStarvation)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (localId !== this.asyncId || this.paused) return
|
if (localId !== this.asyncId || this.paused)
|
||||||
|
return
|
||||||
|
|
||||||
// Resume if was suspended
|
// Resume if was suspended
|
||||||
if (this.audioContext.state === 'suspended') {
|
if (this.audioContext.state === 'suspended') {
|
||||||
@@ -179,7 +192,8 @@ export default class AudioEngine {
|
|||||||
this.events.emit('playing')
|
this.events.emit('playing')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.done) break
|
if (result.done)
|
||||||
|
break
|
||||||
|
|
||||||
const { buffer, timestamp } = result.value
|
const { buffer, timestamp } = result.value
|
||||||
|
|
||||||
@@ -189,9 +203,9 @@ export default class AudioEngine {
|
|||||||
node.connect(this.gainNode)
|
node.connect(this.gainNode)
|
||||||
node.playbackRate.value = this.playbackRate
|
node.playbackRate.value = this.playbackRate
|
||||||
|
|
||||||
const startAt =
|
const startAt
|
||||||
this.audioContextStartTime +
|
= this.audioContextStartTime
|
||||||
(timestamp - this.playbackTimeAtStart) / this.playbackRate
|
+ (timestamp - this.playbackTimeAtStart) / this.playbackRate
|
||||||
|
|
||||||
const duration = buffer.duration
|
const duration = buffer.duration
|
||||||
const endAt = startAt + duration / this.playbackRate
|
const endAt = startAt + duration / this.playbackRate
|
||||||
@@ -202,10 +216,11 @@ export default class AudioEngine {
|
|||||||
|
|
||||||
if (startAt >= this.audioContext.currentTime) {
|
if (startAt >= this.audioContext.currentTime) {
|
||||||
node.start(startAt)
|
node.start(startAt)
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
node.start(
|
node.start(
|
||||||
this.audioContext.currentTime,
|
this.audioContext.currentTime,
|
||||||
(this.audioContext.currentTime - startAt) * this.playbackRate
|
(this.audioContext.currentTime - startAt) * this.playbackRate,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -215,7 +230,8 @@ export default class AudioEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async play() {
|
async play() {
|
||||||
if (!this.paused) return
|
if (!this.paused)
|
||||||
|
return
|
||||||
|
|
||||||
if (!this.audioContext) {
|
if (!this.audioContext) {
|
||||||
this.ensureAudioContext()
|
this.ensureAudioContext()
|
||||||
@@ -234,7 +250,8 @@ export default class AudioEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pause() {
|
pause() {
|
||||||
if (this.paused) return
|
if (this.paused)
|
||||||
|
return
|
||||||
|
|
||||||
this.playbackTimeAtStart = this.currentTime
|
this.playbackTimeAtStart = this.currentTime
|
||||||
this.paused = true
|
this.paused = true
|
||||||
@@ -261,7 +278,8 @@ export default class AudioEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setPlaybackRate(rate) {
|
setPlaybackRate(rate) {
|
||||||
if (rate === this.playbackRate) return
|
if (rate === this.playbackRate)
|
||||||
|
return
|
||||||
|
|
||||||
if (!this.paused) {
|
if (!this.paused) {
|
||||||
this.playbackTimeAtStart = this.currentTime
|
this.playbackTimeAtStart = this.currentTime
|
||||||
|
|||||||
@@ -16,8 +16,9 @@ export default class EventTarget {
|
|||||||
|
|
||||||
removeEventListener(type, fn) {
|
removeEventListener(type, fn) {
|
||||||
const list = this.listeners.get(type)
|
const list = this.listeners.get(type)
|
||||||
if (!list) return
|
if (!list)
|
||||||
|
return
|
||||||
|
|
||||||
const index = list.indexOf(fn)
|
const index = list.indexOf(fn)
|
||||||
if (index >= 0) {
|
if (index >= 0) {
|
||||||
list.splice(index, 1)
|
list.splice(index, 1)
|
||||||
@@ -27,7 +28,7 @@ export default class EventTarget {
|
|||||||
emit(type, detail) {
|
emit(type, detail) {
|
||||||
const evt = new Event(type)
|
const evt = new Event(type)
|
||||||
evt.detail = detail
|
evt.detail = detail
|
||||||
|
|
||||||
const list = this.listeners.get(type)
|
const list = this.listeners.get(type)
|
||||||
if (list) {
|
if (list) {
|
||||||
list.forEach(fn => fn(evt))
|
list.forEach(fn => fn(evt))
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export default class MediaBunnyEngine {
|
|||||||
constructor({ canvas, ctx, events, option = {} }) {
|
constructor({ canvas, ctx, events, option = {} }) {
|
||||||
this.events = events
|
this.events = events
|
||||||
this.option = option
|
this.option = option
|
||||||
|
|
||||||
// Create audio and video engines
|
// Create audio and video engines
|
||||||
this.audio = new AudioEngine(events)
|
this.audio = new AudioEngine(events)
|
||||||
this.video = new VideoEngine({
|
this.video = new VideoEngine({
|
||||||
@@ -41,13 +41,13 @@ export default class MediaBunnyEngine {
|
|||||||
|
|
||||||
async load(src) {
|
async load(src) {
|
||||||
const id = ++this.loadSeq
|
const id = ++this.loadSeq
|
||||||
|
|
||||||
this.pause()
|
this.pause()
|
||||||
this.ended = false
|
this.ended = false
|
||||||
this.error = null
|
this.error = null
|
||||||
this.networkState = 2 // NETWORK_LOADING
|
this.networkState = 2 // NETWORK_LOADING
|
||||||
this.readyState = 0 // HAVE_NOTHING
|
this.readyState = 0 // HAVE_NOTHING
|
||||||
|
|
||||||
setTimeout(() => this.events.emit('waiting'), 0)
|
setTimeout(() => this.events.emit('waiting'), 0)
|
||||||
setTimeout(() => this.events.emit('loadstart'), 0)
|
setTimeout(() => this.events.emit('loadstart'), 0)
|
||||||
|
|
||||||
@@ -60,9 +60,11 @@ export default class MediaBunnyEngine {
|
|||||||
this.performLoad(src, id),
|
this.performLoad(src, id),
|
||||||
loadTimeout > 0 ? this.createTimeout(loadTimeout) : Promise.resolve(),
|
loadTimeout > 0 ? this.createTimeout(loadTimeout) : Promise.resolve(),
|
||||||
])
|
])
|
||||||
} catch (err) {
|
}
|
||||||
if (id !== this.loadSeq) return
|
catch (err) {
|
||||||
|
if (id !== this.loadSeq)
|
||||||
|
return
|
||||||
|
|
||||||
this.loadSeq++
|
this.loadSeq++
|
||||||
this.error = { code: 4, message: err.message }
|
this.error = { code: 4, message: err.message }
|
||||||
this.networkState = 3 // NETWORK_NO_SOURCE
|
this.networkState = 3 // NETWORK_NO_SOURCE
|
||||||
@@ -79,33 +81,40 @@ export default class MediaBunnyEngine {
|
|||||||
this.readyState = 1 // HAVE_METADATA
|
this.readyState = 1 // HAVE_METADATA
|
||||||
this.events.emit('loadedmetadata')
|
this.events.emit('loadedmetadata')
|
||||||
this.events.emit('durationchange')
|
this.events.emit('durationchange')
|
||||||
|
this.events.emit('progress')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
this.video.load(src, () => {
|
this.video.load(src, () => {
|
||||||
if (id !== this.loadSeq) return
|
if (id !== this.loadSeq)
|
||||||
|
return
|
||||||
videoMetadataLoaded = true
|
videoMetadataLoaded = true
|
||||||
checkMetadata()
|
checkMetadata()
|
||||||
}),
|
}),
|
||||||
this.audio.load(src, () => {
|
this.audio.load(src, () => {
|
||||||
if (id !== this.loadSeq) return
|
if (id !== this.loadSeq)
|
||||||
|
return
|
||||||
audioMetadataLoaded = true
|
audioMetadataLoaded = true
|
||||||
checkMetadata()
|
checkMetadata()
|
||||||
}),
|
}),
|
||||||
])
|
])
|
||||||
|
|
||||||
if (id !== this.loadSeq) return
|
if (id !== this.loadSeq)
|
||||||
|
return
|
||||||
|
|
||||||
this.readyState = 4 // HAVE_ENOUGH_DATA
|
this.readyState = 4 // HAVE_ENOUGH_DATA
|
||||||
this.networkState = 1 // NETWORK_IDLE
|
this.networkState = 1 // NETWORK_IDLE
|
||||||
this.events.emit('loadeddata')
|
this.events.emit('loadeddata')
|
||||||
this.events.emit('canplay')
|
this.events.emit('canplay')
|
||||||
this.events.emit('canplaythrough')
|
this.events.emit('canplaythrough')
|
||||||
} catch (err) {
|
this.events.emit('progress')
|
||||||
if (id !== this.loadSeq) return
|
}
|
||||||
|
catch (err) {
|
||||||
|
if (id !== this.loadSeq)
|
||||||
|
return
|
||||||
|
|
||||||
this.error = { code: 4, message: err.message }
|
this.error = { code: 4, message: err.message }
|
||||||
this.networkState = 3
|
this.networkState = 3
|
||||||
this.events.emit('error')
|
this.events.emit('error')
|
||||||
@@ -120,7 +129,8 @@ export default class MediaBunnyEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async play() {
|
async play() {
|
||||||
if (!this.paused) return
|
if (!this.paused)
|
||||||
|
return
|
||||||
|
|
||||||
if (this.ended) {
|
if (this.ended) {
|
||||||
this.ended = false
|
this.ended = false
|
||||||
@@ -128,44 +138,45 @@ export default class MediaBunnyEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.paused = false
|
this.paused = false
|
||||||
|
|
||||||
await this.audio.play()
|
await this.audio.play()
|
||||||
this.video.start(this.audio)
|
this.video.start(this.audio)
|
||||||
|
|
||||||
this.events.emit('play')
|
this.events.emit('play')
|
||||||
this.events.emit('playing')
|
this.events.emit('playing')
|
||||||
}
|
}
|
||||||
|
|
||||||
pause() {
|
pause() {
|
||||||
if (this.paused) return
|
if (this.paused)
|
||||||
|
return
|
||||||
|
|
||||||
this.paused = true
|
this.paused = true
|
||||||
|
|
||||||
this.audio.pause()
|
this.audio.pause()
|
||||||
this.video.stop()
|
this.video.stop()
|
||||||
|
|
||||||
this.events.emit('pause')
|
this.events.emit('pause')
|
||||||
}
|
}
|
||||||
|
|
||||||
async seek(time) {
|
async seek(time) {
|
||||||
const shouldResume = !this.paused
|
const shouldResume = !this.paused
|
||||||
|
|
||||||
this.ended = false
|
this.ended = false
|
||||||
this.seeking = true
|
this.seeking = true
|
||||||
|
|
||||||
this.events.emit('seeking')
|
this.events.emit('seeking')
|
||||||
this.events.emit('waiting')
|
this.events.emit('waiting')
|
||||||
|
|
||||||
this.pause()
|
this.pause()
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
this.audio.seek(time),
|
this.audio.seek(time),
|
||||||
this.video.seek(time),
|
this.video.seek(time),
|
||||||
])
|
])
|
||||||
|
|
||||||
this.seeking = false
|
this.seeking = false
|
||||||
this.events.emit('seeked')
|
this.events.emit('seeked')
|
||||||
|
|
||||||
if (shouldResume && !this.ended) {
|
if (shouldResume && !this.ended) {
|
||||||
await this.play()
|
await this.play()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,8 +55,10 @@ export default class VideoEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
normalizeSource(src) {
|
normalizeSource(src) {
|
||||||
if (typeof src === 'string') return new UrlSource(src)
|
if (typeof src === 'string')
|
||||||
if (src instanceof Blob) return new BlobSource(src)
|
return new UrlSource(src)
|
||||||
|
if (src instanceof Blob)
|
||||||
|
return new BlobSource(src)
|
||||||
if (typeof ReadableStream !== 'undefined' && src instanceof ReadableStream) {
|
if (typeof ReadableStream !== 'undefined' && src instanceof ReadableStream) {
|
||||||
return new ReadableStreamSource(src)
|
return new ReadableStreamSource(src)
|
||||||
}
|
}
|
||||||
@@ -64,8 +66,9 @@ export default class VideoEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async preflight(url) {
|
async preflight(url) {
|
||||||
if (!this.preflightRange || typeof url !== 'string') return true
|
if (!this.preflightRange || typeof url !== 'string')
|
||||||
|
return true
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await fetch(url, { method: 'HEAD' })
|
const res = await fetch(url, { method: 'HEAD' })
|
||||||
const acceptRanges = res.headers.get('accept-ranges')
|
const acceptRanges = res.headers.get('accept-ranges')
|
||||||
@@ -74,15 +77,17 @@ export default class VideoEngine {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
} catch (e) {
|
}
|
||||||
|
catch (e) {
|
||||||
console.warn('Preflight check failed:', e)
|
console.warn('Preflight check failed:', e)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
drawPoster() {
|
drawPoster() {
|
||||||
if (!this.poster || this.posterDrawn) return
|
if (!this.poster || this.posterDrawn)
|
||||||
|
return
|
||||||
|
|
||||||
const img = new Image()
|
const img = new Image()
|
||||||
img.onload = () => {
|
img.onload = () => {
|
||||||
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)
|
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)
|
||||||
@@ -110,7 +115,8 @@ export default class VideoEngine {
|
|||||||
this.clear()
|
this.clear()
|
||||||
this.posterDrawn = false
|
this.posterDrawn = false
|
||||||
|
|
||||||
if (!(await this.preflight(src))) return
|
if (!(await this.preflight(src)))
|
||||||
|
return
|
||||||
|
|
||||||
const source = this.normalizeSource(src)
|
const source = this.normalizeSource(src)
|
||||||
if (!source) {
|
if (!source) {
|
||||||
@@ -124,7 +130,8 @@ export default class VideoEngine {
|
|||||||
})
|
})
|
||||||
|
|
||||||
this.duration = await this.input.computeDuration()
|
this.duration = await this.input.computeDuration()
|
||||||
if (id !== this.asyncId) return
|
if (id !== this.asyncId)
|
||||||
|
return
|
||||||
|
|
||||||
const videoTrack = await this.input.getPrimaryVideoTrack()
|
const videoTrack = await this.input.getPrimaryVideoTrack()
|
||||||
if (!videoTrack) {
|
if (!videoTrack) {
|
||||||
@@ -170,7 +177,8 @@ export default class VideoEngine {
|
|||||||
async resetIterator(time) {
|
async resetIterator(time) {
|
||||||
await this.stopIterator()
|
await this.stopIterator()
|
||||||
|
|
||||||
if (!this.videoSink) return
|
if (!this.videoSink)
|
||||||
|
return
|
||||||
|
|
||||||
this.videoIterator = this.videoSink.canvases(time)
|
this.videoIterator = this.videoSink.canvases(time)
|
||||||
|
|
||||||
@@ -182,17 +190,20 @@ export default class VideoEngine {
|
|||||||
if (first) {
|
if (first) {
|
||||||
this.ctx.drawImage(first.canvas, 0, 0)
|
this.ctx.drawImage(first.canvas, 0, 0)
|
||||||
this.events.emit('loadeddata')
|
this.events.emit('loadeddata')
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
this.drawPoster()
|
this.drawPoster()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateNextFrame(localId) {
|
async updateNextFrame(localId) {
|
||||||
if (!this.videoIterator) return
|
if (!this.videoIterator)
|
||||||
|
return
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
const frame = (await this.videoIterator.next()).value ?? null
|
const frame = (await this.videoIterator.next()).value ?? null
|
||||||
if (!frame || localId !== this.asyncId) return
|
if (!frame || localId !== this.asyncId)
|
||||||
|
return
|
||||||
|
|
||||||
const t = this.audioClock.currentTime
|
const t = this.audioClock.currentTime
|
||||||
const tolerance = this.dropLateFrames
|
const tolerance = this.dropLateFrames
|
||||||
@@ -207,12 +218,13 @@ export default class VideoEngine {
|
|||||||
if (frame.timestamp <= t + tolerance) {
|
if (frame.timestamp <= t + tolerance) {
|
||||||
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)
|
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)
|
||||||
this.ctx.drawImage(frame.canvas, 0, 0)
|
this.ctx.drawImage(frame.canvas, 0, 0)
|
||||||
|
|
||||||
if (!this.dropLateFrames && frame.timestamp > t) {
|
if (!this.dropLateFrames && frame.timestamp > t) {
|
||||||
this.nextFrame = null
|
this.nextFrame = null
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
this.nextFrame = frame
|
this.nextFrame = frame
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -220,7 +232,8 @@ export default class VideoEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (!this.audioClock) return
|
if (!this.audioClock)
|
||||||
|
return
|
||||||
|
|
||||||
const t = this.audioClock.currentTime
|
const t = this.audioClock.currentTime
|
||||||
const now = Date.now()
|
const now = Date.now()
|
||||||
@@ -247,15 +260,16 @@ export default class VideoEngine {
|
|||||||
this.ctx.drawImage(this.nextFrame.canvas, 0, 0)
|
this.ctx.drawImage(this.nextFrame.canvas, 0, 0)
|
||||||
this.nextFrame = null
|
this.nextFrame = null
|
||||||
this.updateNextFrame(this.asyncId)
|
this.updateNextFrame(this.asyncId)
|
||||||
|
|
||||||
if (this.stalled) {
|
if (this.stalled) {
|
||||||
this.events.emit('canplay')
|
this.events.emit('canplay')
|
||||||
this.events.emit('playing')
|
this.events.emit('playing')
|
||||||
this.stalled = false
|
this.stalled = false
|
||||||
}
|
}
|
||||||
} else if (!this.nextFrame) {
|
}
|
||||||
|
else if (!this.nextFrame) {
|
||||||
this.updateNextFrame(this.asyncId)
|
this.updateNextFrame(this.asyncId)
|
||||||
|
|
||||||
if (!this.nextFrame && Number.isFinite(this.duration) && t < this.duration && !this.stalled) {
|
if (!this.nextFrame && Number.isFinite(this.duration) && t < this.duration && !this.stalled) {
|
||||||
this.stalled = true
|
this.stalled = true
|
||||||
this.events.emit('waiting')
|
this.events.emit('waiting')
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
|
import EventTarget from './EventTarget.js'
|
||||||
/**
|
/**
|
||||||
* Video Element Shim
|
* Video Element Shim
|
||||||
* Simulates HTMLVideoElement interface for MediaBunny
|
* Simulates HTMLVideoElement interface for MediaBunny
|
||||||
*/
|
*/
|
||||||
import MediaBunnyEngine from './MediaBunnyEngine.js'
|
import MediaBunnyEngine from './MediaBunnyEngine.js'
|
||||||
import EventTarget from './EventTarget.js'
|
|
||||||
|
|
||||||
function clamp(v, min, max) {
|
function clamp(v, min, max) {
|
||||||
return Math.max(min, Math.min(max, Number(v) || 0))
|
return Math.max(min, Math.min(max, Number(v) || 0))
|
||||||
@@ -17,7 +17,7 @@ export default class VideoShim {
|
|||||||
|
|
||||||
// Event system
|
// Event system
|
||||||
this.events = new EventTarget()
|
this.events = new EventTarget()
|
||||||
|
|
||||||
// MediaBunny engine
|
// MediaBunny engine
|
||||||
this.engine = new MediaBunnyEngine({ canvas, ctx, events: this.events, option })
|
this.engine = new MediaBunnyEngine({ canvas, ctx, events: this.events, option })
|
||||||
|
|
||||||
@@ -36,7 +36,8 @@ export default class VideoShim {
|
|||||||
// Auto-load source
|
// Auto-load source
|
||||||
if (option.source) {
|
if (option.source) {
|
||||||
this.src = option.source
|
this.src = option.source
|
||||||
} else if (art.option?.url) {
|
}
|
||||||
|
else if (art.option?.url) {
|
||||||
this.src = art.option.url
|
this.src = art.option.url
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -66,7 +67,8 @@ export default class VideoShim {
|
|||||||
|
|
||||||
set src(v) {
|
set src(v) {
|
||||||
this._src = v
|
this._src = v
|
||||||
if (v) this.engine.load(v)
|
if (v)
|
||||||
|
this.engine.load(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
get currentSrc() {
|
get currentSrc() {
|
||||||
@@ -148,8 +150,9 @@ export default class VideoShim {
|
|||||||
|
|
||||||
set playbackRate(v) {
|
set playbackRate(v) {
|
||||||
const rate = Number(v)
|
const rate = Number(v)
|
||||||
if (Number.isNaN(rate) || rate <= 0) return
|
if (Number.isNaN(rate) || rate <= 0)
|
||||||
|
return
|
||||||
|
|
||||||
this._playbackRate = rate
|
this._playbackRate = rate
|
||||||
this.engine.setPlaybackRate(rate)
|
this.engine.setPlaybackRate(rate)
|
||||||
this.events.emit('ratechange')
|
this.events.emit('ratechange')
|
||||||
@@ -187,7 +190,8 @@ export default class VideoShim {
|
|||||||
}
|
}
|
||||||
|
|
||||||
load() {
|
load() {
|
||||||
if (this._src) this.engine.load(this._src)
|
if (this._src)
|
||||||
|
this.engine.load(this._src)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Video dimensions
|
// Video dimensions
|
||||||
@@ -290,13 +294,17 @@ export default class VideoShim {
|
|||||||
setAttribute(name, value) {
|
setAttribute(name, value) {
|
||||||
if (name === 'src') {
|
if (name === 'src') {
|
||||||
this.src = value
|
this.src = value
|
||||||
} else if (name === 'autoplay') {
|
}
|
||||||
|
else if (name === 'autoplay') {
|
||||||
this.autoplay = value
|
this.autoplay = value
|
||||||
} else if (name === 'loop') {
|
}
|
||||||
|
else if (name === 'loop') {
|
||||||
this.loop = value
|
this.loop = value
|
||||||
} else if (name === 'muted') {
|
}
|
||||||
|
else if (name === 'muted') {
|
||||||
this.muted = true
|
this.muted = true
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
this.canvas.setAttribute(name, value)
|
this.canvas.setAttribute(name, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,8 @@ export default function artplayerProxyMediabunny(option = {}) {
|
|||||||
|
|
||||||
// Add shim properties to canvas
|
// Add shim properties to canvas
|
||||||
for (const prop of propertyNames) {
|
for (const prop of propertyNames) {
|
||||||
if (prop === 'constructor') continue
|
if (prop === 'constructor')
|
||||||
|
continue
|
||||||
if (!(prop in canvas)) {
|
if (!(prop in canvas)) {
|
||||||
Object.defineProperty(canvas, prop, {
|
Object.defineProperty(canvas, prop, {
|
||||||
get() {
|
get() {
|
||||||
@@ -61,7 +62,8 @@ export default function artplayerProxyMediabunny(option = {}) {
|
|||||||
// Handle resize
|
// Handle resize
|
||||||
function resize() {
|
function resize() {
|
||||||
const player = art.template?.$player
|
const player = art.template?.$player
|
||||||
if (!player || art.option.autoSize) return
|
if (!player || art.option.autoSize)
|
||||||
|
return
|
||||||
|
|
||||||
Object.assign(canvas.style, {
|
Object.assign(canvas.style, {
|
||||||
width: '100%',
|
width: '100%',
|
||||||
|
|||||||
+11
-11
@@ -6,65 +6,65 @@ interface Option {
|
|||||||
* @default 12000
|
* @default 12000
|
||||||
*/
|
*/
|
||||||
loadTimeout?: number
|
loadTimeout?: number
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interval for timeupdate events in milliseconds
|
* Interval for timeupdate events in milliseconds
|
||||||
* @default 250
|
* @default 250
|
||||||
*/
|
*/
|
||||||
timeupdateInterval?: number
|
timeupdateInterval?: number
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Audio-video synchronization tolerance in seconds
|
* Audio-video synchronization tolerance in seconds
|
||||||
* @default 0.12
|
* @default 0.12
|
||||||
*/
|
*/
|
||||||
avSyncTolerance?: number
|
avSyncTolerance?: number
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether to drop late video frames
|
* Whether to drop late video frames
|
||||||
* @default false
|
* @default false
|
||||||
*/
|
*/
|
||||||
dropLateFrames?: boolean
|
dropLateFrames?: boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Poster image URL
|
* Poster image URL
|
||||||
*/
|
*/
|
||||||
poster?: string
|
poster?: string
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Media source (URL, Blob, or ReadableStream)
|
* Media source (URL, Blob, or ReadableStream)
|
||||||
*/
|
*/
|
||||||
source?: string | Blob | ReadableStream<Uint8Array>
|
source?: string | Blob | ReadableStream<Uint8Array>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if server supports range requests before loading
|
* Check if server supports range requests before loading
|
||||||
* @default false
|
* @default false
|
||||||
*/
|
*/
|
||||||
preflightRange?: boolean
|
preflightRange?: boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initial volume (0-1)
|
* Initial volume (0-1)
|
||||||
* @default 0.7
|
* @default 0.7
|
||||||
*/
|
*/
|
||||||
volume?: number
|
volume?: number
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initial muted state
|
* Initial muted state
|
||||||
* @default false
|
* @default false
|
||||||
*/
|
*/
|
||||||
muted?: boolean
|
muted?: boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Autoplay
|
* Autoplay
|
||||||
* @default false
|
* @default false
|
||||||
*/
|
*/
|
||||||
autoplay?: boolean
|
autoplay?: boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loop playback
|
* Loop playback
|
||||||
* @default false
|
* @default false
|
||||||
*/
|
*/
|
||||||
loop?: boolean
|
loop?: boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cross-origin setting
|
* Cross-origin setting
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in new issue
Block a user