基于webassembly的H265视频播放器前端Angular学习笔记4:使用wasm解码H265数据
本文基于开源项目decoder_wasm使用Angular开发的前端页面项目,对项目整理结构进行的改动,并使用Typescript重写大部分js源码,便于维护。
着重学习Angular框架下Worker,Wasm的使用,本人非前端开发从业人员,页面简陋请见谅。
使用wasm解码H265
创建组件与路由
创建组件
$ ng generate component DecoderTest --skipTests=true
CREATE src/app/decoder-test/decoder-test.component.scss (0 bytes)
CREATE src/app/decoder-test/decoder-test.component.html (27 bytes)
CREATE src/app/decoder-test/decoder-test.component.ts (299 bytes)
UPDATE src/app/app.module.ts (790 bytes)
设置路由
{ path: 'decoder', component: DecoderTestComponent },
获取H265数据
详见的之前篇笔记
基于webassembly的H265视频播放器前端Angular学习笔记2:创建Angular组件获取H265文件数据
拷贝代码相关逻辑到新的测试组件中
加载Wasm文件
详见的之前篇笔记
基于webassembly的H265视频播放器前端Angular学习笔记3:创建Angular组件中加载wasm文件
拷贝代码相关逻辑到新的测试组件中
设置wasm的加载回调函数
常量
const CHUNK_SIZE = 4096;
const DECODER_H264 = 0;
const DECODER_H265 = 1;
const LOG_LEVEL_JS = 0;
const LOG_LEVEL_WASM = 1;
const LOG_LEVEL_FFMPEG = 2;
添加成员变量
private isWasmLoaded = false;
private wasm = (window as any).Module as any;
private decoderType = DECODER_H265;
private decoderLogLevel = LOG_LEVEL_WASM;
构造函数中设置加载回调
constructor() {
this.wasm = typeof this.wasm !== 'undefined' ? this.wasm : {};
this.wasm.onRuntimeInitialized = () => {
this.isWasmLoaded = true;
this.onWasmLoaded();
};
}
onRuntimeInitialized这个回调是emscripten编译后自动生成的,只需要赋值给window.Module.onRuntimeInitialized赋值一个回调函数,在wasm文件加载完成后就会调用。
设置解码器数据回调函数
添加两个成员变量
private videoSize = 0; // 计数
private onWasmLoaded(): void {
const callback = this.wasm.addFunction((addr_y, addr_u, addr_v, stride_y, stride_u, stride_v, width, height, pts) => {
console.log('[%d]In video callback, size = %d * %d, pts = %d', ++this.videoSize, width, height, pts);
});
this.wasm._openDecoder(this.decoderType, callback, this.decoderLogLevel);
}
_openDecoder是开启解码器的API接口
向解码器喂数据
修改笔记2中的reader.onload回调函数
reader.onload = (ev: ProgressEvent<FileReader>) => {
const typedArray: Uint8Array = new Uint8Array(ev.target.result as ArrayBuffer);
const size = typedArray.byteLength;
let cacheBuffer = this.wasm._malloc(size);
this.wasm.HEAPU8.set(typedArray, cacheBuffer);
this.wasm._decodeData(cacheBuffer, size, this.pts++);
if (cacheBuffer != null) {
this.wasm._free(cacheBuffer);
cacheBuffer = null;
}
if (size < CHUNK_SIZE) {
console.log('Flush frame data');
this.wasm._flushDecoder();
this.wasm._closeDecoder();
}
};
_malloc与_free时emscripten自动生成的接口,用于开辟和释放内存。
_decodeData是添加要解码的数据的API接口
_flushDecoder是清空解码器的API接口
_closeDecoder是关闭解码器的API接口
测试结果
decoder_wasm中下载测试H265文件到你的电脑;
打开http://localhost:4200/decoder进行测试。
decoder-test.component.ts:33[1]In video callback, size = 1000 * 504, pts = 0
decoder-test.component.ts:33 [2]In video callback, size = 1000 * 504, pts = 1
decoder-test.component.ts:33 [3]In video callback, size = 1000 * 504, pts = 2
decoder-test.component.ts:33 [4]In video callback, size = 1000 * 504, pts = 3
decoder-test.component.ts:33 [5]In video callback, size = 1000 * 504, pts = 4
decoder-test.component.ts:33 [6]In video callback, size = 1000 * 504, pts = 5
decoder-test.component.ts:33 [7]In video callback, size = 1000 * 504, pts = 6
decoder-test.component.ts:33 [8]In video callback, size = 10wasm-worker-test00 * 504, pts = 7
decoder-test.component.ts:33 [9]In video callback, size = 1000 * 504, pts = 8
decoder-test.component.ts:33 [10]In video callback, size = 1000 * 504, pts = 9
可以看到控制台解码后的数据已经在回调中输出出来了。
emscripten编译
关于emscripten编译使用ffmpeg库C/C++代码,我计划另外抽时间写专题,此专题我只是记录我学习angular的笔记,暂时不介绍。
如果想自己用emscripten编译,遇到问题的话可以联系我,我们一起探讨。
decoder_wasm并没有工程化C代码,是单个C文件编译的。
其实可以使用cmake或makefile等工具把整个C工程编译成一个wasm文件。
微信号:yjkhtddx