Skip to content

Commit

Permalink
支持设置videoDecoderHardwareAcceleration选项指示视频解码器硬件加速方法
Browse files Browse the repository at this point in the history
  • Loading branch information
Vinlic committed Dec 24, 2023
1 parent e81a64b commit bbd0b63
Show file tree
Hide file tree
Showing 8 changed files with 39 additions and 11 deletions.
8 changes: 6 additions & 2 deletions api/ChunkVideo.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ export default class ChunkVideo extends VideoChunk {
* @param {boolean} [options.autostartRender=true] - 是否自动启动渲染,如果为false请务必在页面中执行 captureCtx.start()
* @param {boolean} [options.consoleLog=false] - 是否开启控制台日志输出
* @param {boolean} [options.videoPreprocessLog=false] - 是否开启视频预处理日志输出
* @param {string} [options.videoDecoderHardwareAcceleration] - VideoDecoder硬件加速指示
* @param {WaitForOptions} [options.pageWaitForOptions] - 页面等待选项
* @param {Viewport} [options.pageViewport] - 页面视窗参数
* @param {Function} [options.pagePrepareFn] - 页面预处理函数
Expand All @@ -83,7 +84,7 @@ export default class ChunkVideo extends VideoChunk {
constructor(options = {}) {
super(options);
assert(_.isObject(options), "options must be Object");
const { url, content, startTime, autostartRender, consoleLog, videoPreprocessLog, pageWaitForOptions, pageViewport, pagePrepareFn, timeActions } = options;
const { url, content, startTime, autostartRender, consoleLog, videoPreprocessLog, pageWaitForOptions, pageViewport, pagePrepareFn, videoDecoderHardwareAcceleration, timeActions } = options;
assert(_.isUndefined(url) || util.isURL(url), `url ${url} is not valid URL`);
assert(_.isUndefined(content) || _.isString(content), "page content must be string");
assert(!_.isUndefined(url) || !_.isUndefined(content), "page url or content must be provide");
Expand All @@ -93,6 +94,7 @@ export default class ChunkVideo extends VideoChunk {
assert(_.isUndefined(pageWaitForOptions) || _.isObject(pageWaitForOptions), "pageWaitForOptions must be Object");
assert(_.isUndefined(pageViewport) || _.isObject(pageViewport), "pageViewport must be Object");
assert(_.isUndefined(pagePrepareFn) || _.isFunction(pagePrepareFn), "pagePrepareFn must be Function");
assert(_.isUndefined(videoDecoderHardwareAcceleration) || _.isString(videoDecoderHardwareAcceleration), "videoDecoderHardwareAcceleration must be string");
assert(_.isUndefined(timeActions) || _.isObject(timeActions), "timeActions must be Object");
timeActions && Object.keys(timeActions).forEach(key => {
key = Number(key)
Expand All @@ -108,6 +110,7 @@ export default class ChunkVideo extends VideoChunk {
this.pageWaitForOptions = pageWaitForOptions;
this.pageViewport = pageViewport;
this.pagePrepareFn = pagePrepareFn;
this.videoDecoderHardwareAcceleration = videoDecoderHardwareAcceleration;
this.timeActions = timeActions;
}

Expand Down Expand Up @@ -154,7 +157,7 @@ export default class ChunkVideo extends VideoChunk {
async #synthesize() {
const page = await this.#acquirePage();
try {
const { url, content, width, height, fps, startTime, duration, pageWaitForOptions, pageViewport = {} } = this;
const { url, content, width, height, fps, startTime, duration, pageWaitForOptions, pageViewport = {}, videoDecoderHardwareAcceleration } = this;
// 监听页面实例发生的某些内部错误
page.on("error", err => this._emitError("Page error:\n" + err.stack));
// 监听页面是否崩溃,当内存不足或过载时可能会崩溃
Expand Down Expand Up @@ -210,6 +213,7 @@ export default class ChunkVideo extends VideoChunk {
fps,
startTime,
duration,
videoDecoderHardwareAcceleration,
autostart: this.autostartRender
});
// 监听并等待录制完成
Expand Down
8 changes: 6 additions & 2 deletions api/SingleVideo.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ export default class SingleVideo extends Synthesizer {
* @param {number} [options.parallelWriteFrames=10] - 并行写入帧数
* @param {Viewport} [options.pageViewport] - 页面视窗参数
* @param {Function} [options.pagePrepareFn] - 页面预处理函数
* @param {string} [options.videoDecoderHardwareAcceleration] - VideoDecoder硬件加速指示
* @param {{[key: number]: Function}} [options.timeActions] - 动作序列
* @param {WaitForOptions} [options.pageWaitForOptions] - 页面等待选项
* @param {boolean} [options.showProgress=false] - 是否在命令行展示进度
Expand All @@ -80,7 +81,7 @@ export default class SingleVideo extends Synthesizer {
*/
constructor(options = {}) {
super(options);
const { url, content, startTime, autostartRender, consoleLog, videoPreprocessLog, pageWaitForOptions, pageViewport, pagePrepareFn, timeActions } = options;
const { url, content, startTime, autostartRender, consoleLog, videoPreprocessLog, pageWaitForOptions, pageViewport, pagePrepareFn, videoDecoderHardwareAcceleration, timeActions } = options;
assert(_.isUndefined(url) || util.isURL(url), `url ${url} is not valid URL`);
assert(_.isUndefined(content) || _.isString(content), "page content must be string");
assert(!_.isUndefined(url) || !_.isUndefined(content), "page url or content must be provide");
Expand All @@ -90,6 +91,7 @@ export default class SingleVideo extends Synthesizer {
assert(_.isUndefined(pageWaitForOptions) || _.isObject(pageWaitForOptions), "pageWaitForOptions must be Object");
assert(_.isUndefined(pageViewport) || _.isObject(pageViewport), "pageViewport must be Object");
assert(_.isUndefined(pagePrepareFn) || _.isFunction(pagePrepareFn), "pagePrepareFn must be Function");
assert(_.isUndefined(videoDecoderHardwareAcceleration) || _.isString(videoDecoderHardwareAcceleration), "videoDecoderHardwareAcceleration must be string");
assert(_.isUndefined(timeActions) || _.isObject(timeActions), "timeActions must be Object");
timeActions && Object.keys(timeActions).forEach(key => {
key = Number(key)
Expand All @@ -105,6 +107,7 @@ export default class SingleVideo extends Synthesizer {
this.pageViewport = pageViewport;
this.pageWaitForOptions = pageWaitForOptions;
this.pagePrepareFn = pagePrepareFn;
this.videoDecoderHardwareAcceleration = videoDecoderHardwareAcceleration;
this.timeActions = timeActions;
}

Expand Down Expand Up @@ -151,7 +154,7 @@ export default class SingleVideo extends Synthesizer {
async #synthesize() {
const page = await this.#acquirePage();
try {
const { url, content, width, height, fps, startTime, duration, pageWaitForOptions, pageViewport = {} } = this;
const { url, content, width, height, fps, startTime, duration, pageWaitForOptions, pageViewport = {}, videoDecoderHardwareAcceleration } = this;
// 监听页面实例发生的某些内部错误
page.on("error", err => this._emitError("Page error:\n" + err.stack));
// 监听页面是否崩溃,当内存不足或过载时可能会崩溃
Expand Down Expand Up @@ -202,6 +205,7 @@ export default class SingleVideo extends Synthesizer {
fps,
startTime,
duration,
videoDecoderHardwareAcceleration,
autostart: this.autostartRender
});
// 监听并等待录制完成
Expand Down
2 changes: 2 additions & 0 deletions api/WebVideoCreator.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ export default class WebVideoCreator {
* @param {boolean} [options.autostartRender=true] - 是否自动启动渲染,如果为false请务必在页面中执行 captureCtx.start()
* @param {boolean} [options.consoleLog=false] - 是否开启控制台日志输出
* @param {boolean} [options.videoPreprocessLog=false] - 是否开启视频预处理日志输出
* @param {string} [options.videoDecoderHardwareAcceleration] - VideoDecoder硬件加速指示
* @param {WaitForOptions} [options.pageWaitForOptions] - 页面等待选项
* @param {Viewport} [options.pageViewport] - 页面视窗参数
* @param {Function} [options.pagePrepareFn] - 页面预处理函数
Expand Down Expand Up @@ -158,6 +159,7 @@ export default class WebVideoCreator {
* @param {boolean} [options.autostartRender=true] - 是否自动启动渲染,如果为false请务必在页面中执行 captureCtx.start()
* @param {boolean} [options.consoleLog=false] - 是否开启控制台日志输出
* @param {boolean} [options.videoPreprocessLog=false] - 是否开启视频预处理日志输出
* @param {string} [options.videoDecoderHardwareAcceleration] - VideoDecoder硬件加速指示
* @param {WaitForOptions} [options.pageWaitForOptions] - 页面等待选项
* @param {Viewport} [options.pageViewport] - 页面视窗参数
* @param {Function} [options.pagePrepareFn] - 页面预处理函数
Expand Down
2 changes: 2 additions & 0 deletions core/CaptureContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -657,6 +657,8 @@ export default class CaptureContext {
volume: (e.getNumberAttribute("volume") || e.volume || 1) * 100,
// 视频是否自动播放
autoplay: e.getBooleanAttribute("autoplay"),
// 解码器硬件加速方法提示
hardwareAcceleration: this.config.videoDecoderHardwareAcceleration,
// 视频是否静音
muted: e.getBooleanAttribute("muted"),
// 拉取失败时重试拉取次数
Expand Down
5 changes: 3 additions & 2 deletions core/Page.js
Original file line number Diff line number Diff line change
Expand Up @@ -338,12 +338,13 @@ export default class Page extends EventEmitter {
*/
async startScreencast(options = {}) {
await this.#asyncLock.acquire("startScreencast", async () => {
let { fps, startTime = 0, duration, frameCount, autostart = true } = options;
let { fps, startTime = 0, duration, frameCount, autostart = true, videoDecoderHardwareAcceleration } = options;
assert(this.isReady(), "Page state must be ready");
assert(_.isUndefined(fps) || _.isFinite(fps), "fps must be number");
assert(_.isFinite(startTime), "startTime must be number");
assert(_.isUndefined(duration) || _.isFinite(duration), "duration must be number");
assert(_.isUndefined(frameCount) || _.isFinite(frameCount), "frameCount must be number");
assert(_.isUndefined(videoDecoderHardwareAcceleration) || _.isString(videoDecoderHardwareAcceleration), "videoDecoderHardwareAcceleration must be string");
// 指定时长时将计算总帧数
if (_.isFinite(duration))
frameCount = util.durationToFrameCount(duration, fps);
Expand All @@ -365,7 +366,7 @@ export default class Page extends EventEmitter {
Object.assign(captureCtx.config, config);
// 如果准备后还未启动且自动启动选项开启时渲染则开始
!captureCtx.ready() && captureCtx.config.autostart && captureCtx.start();
}, _.pickBy({ fps, startTime, duration, frameCount, autostart }, v => !_.isUndefined(v)));
}, _.pickBy({ fps, startTime, duration, frameCount, autostart, videoDecoderHardwareAcceleration }, v => !_.isUndefined(v)));
});
}

Expand Down
5 changes: 5 additions & 0 deletions docs/api-reference-high-level.md
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,11 @@
<td>boolean</td>
<td>是否开启视频预处理日志输出</td>
</tr>
<tr>
<td>videoDecoderHardwareAcceleration</td>
<td>string</td>
<td><a href="https://developer.mozilla.org/en-US/docs/Web/API/VideoDecoder/configure">VideoDecoder</a>硬件加速指示,默认值 prefer-hardware</td>
</tr>
<tr>
<td>parallelWriteFrames</td>
<td>number</td>
Expand Down
5 changes: 5 additions & 0 deletions docs/api-reference-low-level.md
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,11 @@ const { ... } = core;
<td>boolean</td>
<td>是否自动启动渲染,默认true</td>
</tr>
<tr>
<td>videoDecoderHardwareAcceleration</td>
<td>string</td>
<td><a href="https://developer.mozilla.org/en-US/docs/Web/API/VideoDecoder/configure">VideoDecoder</a>硬件加速指示,默认值 prefer-hardware</td>
</tr>
</tbody>
</table>

Expand Down
15 changes: 10 additions & 5 deletions media/VideoCanvas.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ export default class VideoCanvas {
volume;
/** @type {boolean} - 是否自动播放 */
autoplay;
/** @type {string} - 解码器硬件加速方法提示 */
hardwareAcceleration;
/** @type {boolean} - 是否静音 */
muted;
/** @type {number} - 重试下载次数 */
Expand Down Expand Up @@ -104,13 +106,14 @@ export default class VideoCanvas {
* @param {number} [options.volume=100] - 视频音频音量(0-100)
* @param {boolean} [options.loop=false] - 是否循环播放
* @param {boolean} [options.muted=false] - 是否静音
* @param {string} [options.hardwareAcceleration="prefer-hardware"] - 解码器硬件加速方法提示
* @param {boolean} [options.retryFetchs=2] - 重试下载次数
* @param {boolean} [options.ignoreCache=false] - 是否忽略本地缓存
*/
constructor(options) {
const u = ____util;
u.assert(u.isObject(options), "VideoCanvas options must be Object");
const { url, maskUrl, startTime, endTime, audioId, format, seekStart, seekEnd, fadeInDuration, fadeOutDuration, autoplay, volume, loop, muted, retryFetchs, ignoreCache } = options;
const { url, maskUrl, startTime, endTime, audioId, format, seekStart, seekEnd, fadeInDuration, fadeOutDuration, autoplay, volume, loop, muted, hardwareAcceleration, retryFetchs, ignoreCache } = options;
u.assert(u.isString(url), "url must be string");
u.assert(u.isNumber(startTime), "startTime must be number");
u.assert(u.isNumber(endTime), "endTime must be number");
Expand All @@ -125,6 +128,7 @@ export default class VideoCanvas {
u.assert(u.isUndefined(volume) || u.isNumber(volume), "volume must be number");
u.assert(u.isUndefined(loop) || u.isBoolean(loop), "loop must be boolean");
u.assert(u.isUndefined(muted) || u.isBoolean(muted), "muted must be boolean");
u.assert(u.isUndefined(hardwareAcceleration) || u.isString(hardwareAcceleration), "hardwareAcceleration must be string");
u.assert(u.isUndefined(retryFetchs) || u.isNumber(retryFetchs), "retryFetchs must be number");
u.assert(u.isUndefined(ignoreCache) || u.isBoolean(ignoreCache), "ignoreCache must be boolean");
this.url = url;
Expand All @@ -141,6 +145,7 @@ export default class VideoCanvas {
this.volume = u.defaultTo(volume, 100);
this.loop = u.defaultTo(loop, false);
this.muted = u.defaultTo(muted, false);
this.hardwareAcceleration = u.defaultTo(hardwareAcceleration, "prefer-hardware");
this.retryFetchs = u.defaultTo(retryFetchs, 2);
this.ignoreCache = u.defaultTo(ignoreCache, false);
}
Expand Down Expand Up @@ -227,7 +232,7 @@ export default class VideoCanvas {
return true;
}
catch (err) {
console.log(err);
console.error(err);
this.destory();
return false;
}
Expand Down Expand Up @@ -513,7 +518,7 @@ export default class VideoCanvas {
u.assert(u.isFunction(onError), "onError must be Function");
const decoder = (isMask ? this.maskDecoder : this.decoder) || new VideoDecoder({
output: onFrame.bind(this),
error: onError.bind(this)
error: err => onError.bind(this)(new Error(err))
});
const demuxer = new ____MP4Demuxer();
let timer;
Expand All @@ -523,8 +528,8 @@ export default class VideoCanvas {
decoder.configure({
// 视频信息配置
...config,
// 指示优先使用硬件加速解码
hardwareAcceleration: "prefer-hardware",
// 解码器硬件加速指示
hardwareAcceleration: this.hardwareAcceleration,
// 关闭延迟优化,让解码器批量处理解码,降低负载
optimizeForLatency: true
});
Expand Down

0 comments on commit bbd0b63

Please sign in to comment.