本菜雞最近在搞FFmpeg嵌入到前端中做解碼,初步的想法是通過(guò)web worker 建一個(gè)解碼的線程專門用于解碼H264數(shù)據(jù),然后主線程負(fù)責(zé)渲染解碼后的數(shù)據(jù)。
首先要在前端中運(yùn)行C代碼,那就要使用webassembly技術(shù),webassembly技術(shù)可以將C/C++、rust代碼編譯為.wasm文件,然后導(dǎo)入到前端中實(shí)現(xiàn)前端運(yùn)行C/C++、rust代碼的效果。而編譯生成.wasm文件需要用到emscripten工具。
本文中的emscripten版本如下:

emscripten版本
來(lái)個(gè)簡(jiǎn)單的C代碼:
#include <stdio.h>
int main(int argc, char ** argv) {
printf("Hello, world!\n");
}
int mytest() {
printf("this is wasm test!\n");
return 999;
}
編譯上面的C代碼為wasm文件(最后生成的文件為JS文件,導(dǎo)入這個(gè).js代碼就可以導(dǎo)入.wasm文件):
emcc hello.c -o hello.js -s MODULARIZE=1 -s EXPORT_NAME=Test -s EXTRA_EXPORTED_RUNTIME_METHODS='["ccall"]' -s EXPORTED_FUNCTIONS='["_mytest","_main"]'
新建一個(gè)html文件作為演示:
<!DOCTYPE html>
<html>
<head>
<title>wasm</title>
</head>
<body>
<h1>test !</h1>
<button onclick="test()">send a test</button>
<button onclick="closeWebworker()">close web worker</button>
</body>
<script>
var worker = null
window.onload = function () {
// 新建一個(gè)worker
worker = new Worker("test.js")
// 向子線程傳遞數(shù)據(jù)
worker.postMessage('init')
worker.onmessage = function (evt) {
console.log('master reveived msg: ',evt.data)
}
var ab = new ArrayBuffer(1);
// 使用transportObject
worker.postMessage(ab, [ab]);
}
function closeWebworker() {
worker.terminate()
worker = null
console.log('close web worker !!')
}
function test() {
if (worker) {
worker.postMessage('test')
} else {
console.log('web worker has closed !')
}
}
</script>
</html>
然后是頁(yè)面中用到的test.js
importScripts('hello.js')
var self = this
function Test2() {
// 初始化worker
this.initWorker()
}
Test2.prototype.initWorker = function () {
console.log('初始化worker !')
}
Test2.prototype.init = async function () {
self.Module = await initModule()
postMessage('初始化')
var testBuffer = new ArrayBuffer(32)
postMessage(testBuffer,[testBuffer])
}
Test2.prototype.mytest = function () {
console.log(self.Module._mytest())
}
self.test = new Test2
self.onmessage = function (evt) {
if (self.test === undefined) {
console.log('init error !', self.test)
return ;
}
console.log(`子線程Reveived msg: ${evt.data}`)
switch (evt.data) {
case 'init':
self.test.init();
break;
case 'test':
self.test.mytest();
break;
default:
break;
}
}
function onWasmLoaded() {
if (test) {
test.mytest();
} else {
console.log("[ER] No decoder!");
}
}
function initModule() {
return new Promise((r,j) => {
r(
Test({
onRuntimeInitialized: () => {
console.log('初始化wasm!')
},
})
)
})
}
最后整體的項(xiàng)目結(jié)構(gòu):

項(xiàng)目結(jié)構(gòu)
運(yùn)行命令:
emrun --no_browser --port 8087 .打開(kāi)localhost:8087查看效果。