https://www.w3.org/2020/08/29-chinese-web/wasm-intro.pdf
https://zhuanlan.zhihu.com/p/68048524
一个可移植、体积小、加载快并且兼容 Web 的全新二进制格式(以前只有js)
能从C/C++等语言将代码编译到WebAssembly格式.并在浏览器中运行它们,也就是可以在Web端跑C/C++代码,后缀名为.wasm
本质是类汇编的一种新的抽象栈式虚拟机
更为接近机器码,已经高度优化过,无需浏览器进行更多的优化和编译
拥有静态类型校验,无需再次优化或者去优化操作
没有垃圾回收机制
WebAssembly执行完全依赖CPU计算,不能借助GPU硬件加速,所以需要尽量挖掘CPU执行提升程序效率的手段。目前两个主要优化手段为多线程和SIMD
WAT(WebAssembly Text Format)
wat2wasm sum.wat -o demo.wasm
AssemblyScript(直接将TypeScript编译成WebAssembly)
Golang
GOARCH=wasm GOOS=js go build -o main.wasm main.go
Emscripten (核心工具、转换c\cpp为wasm)
emcc hello.c -o demo.wasm
onst fetchAndInstantiate = async (url,importObject = {}) => {
const response = await fetch(url)
const bytes = await response.arrayBuffer()
const results = await WebAssembly. instantiate(bytes, importobject)
return results.instance. exports
}
(async () => {
const { addTwo } = await fetchAndInstantiate('sum.wasm')
console. log(addTwo(1, 2))
})()
建议clone 官网地址,然后按需求下载指定版本sdk,可灵活切换
add.c
int add(int a,int b){
return a+b;
}
emcc hello.c -s WASM=1 -o index.html #时间有点久1分钟差不多
emrun --no_browser --port 8080 .
emcc add.c -Os -s WASM=1 -s -o add.wasm
#为了快速编译,添加 -s SIDE_MODULE=1 表示就只要这一个模块
在浏览器里播放H.265需要借用Webassembly+FFmpeg,因为FFmpeg 是用 C\汇编 语言开发的所以Emscripten会很方便的配置FFmpeg
Emscripten版的ffmpeg编译:https://cloud.tencent.com/developer/article/1467813
由于我们此次主要针对 H.265 的解码进行实践,所以可以在编译时通过参数来定制 FFmpeg 只支持必要的解封装和解码器。不同于常规编译 FFmpeg 时使用的./configure,在编译给 Wasm 调用的 FFmpeg 时需要使用 Emscripten 提供的 emconfigure ./configure:
完整项目:https://github.com/goldvideo/decoder_wasm
上面的项目好像编译有点问题,我写了一个完整脚本给可以一键搭建完成环境
#!/bin/bash
ff_version="ffmpeg-4.1.10"
work_path="decoder_wasm"
ff_build_dir="ffmpeg"
main(){
initializaiton_emsdk
get_ffmpeg
pull_decoder_wasm
emscripten_build_ffmpeg
update_video_js
emscripten_build_c
}
pull_decoder_wasm(){
if [ ! -d decoder_wasm ];then
git clone https://github.com/goldvideo/decoder_wasm decoder_wasm
fi
}
intstall_emsdk(){
pushd emsdk
./emsdk install 1.38.45
./emsdk activate 1.38.45
source ./emsdk_env.sh
popd
}
initializaiton_emsdk(){
obj=emcc
if [ ! -x "$(command -v $obj)" ];then
echo "$obj installing ....."
if [ ! -d emsdk ];then
git clone https://github.com/emscripten-core/emsdk.git emsdk
intstall_emsdk
fi
fi
local version=$(emcc -v 2>&1 | grep "1\...\..." | awk '{printf $10}')
echo -e "\033[31m$version\033[0m"
if [ "$version" != "1.38.45" ];then
intstall_emsdk
fi
local version=$(emcc -v 2>&1 | grep "1\...\..." | awk '{printf $10}')
echo $version
if [ "$version" != "1.38.45" ];then
echo -e "\033[31mexit env error!!!\033[0m";exit
fi
}
get_ffmpeg(){
if [ ! -d "$ff_version" ];then
if [ ! -f "$ff_version.tar.bz2" ];then
wget https://ffmpeg.org/releases/${ff_version}.tar.bz2
tar -xjvf ${ff_version}.tar.bz2
fi
fi
}
emscripten_build_ffmpeg(){
pushd $ff_version
ok="y"
local out=$(find . -name "*.o")
if [ -n "$out" ];then
echo -e "\033[31mThis is very long time operation!!!!!\033[0m"
echo -e "\033[31mRecompile FFmpeg???? [y/N]\033[0m"
read ok
fi
if [ "$ok" != "y" ];then popd;return;fi
if [ -d "$ff_build_dir" ];then
rm -r $ff_build_dir
fi
make clean
emconfigure ./configure --cc="emcc" --cxx="em++" --ar="emar" --prefix=$(pwd)/../${work_path}/${ff_build_dir} --enable-cross-compile --target-os=none --arch=x86_32 --cpu=generic \
--enable-gpl --enable-version3 --disable-avdevice --disable-avformat --disable-swresample --disable-postproc --disable-avfilter \
--disable-programs --disable-logging --disable-everything \
--disable-ffplay --disable-ffprobe --disable-asm --disable-doc --disable-devices --disable-network \
--disable-hwaccels --disable-parsers --disable-bsfs --disable-debug --disable-protocols --disable-indevs --disable-outdevs --disable-pthreads --disable-w32threads --ranlib=emranlib \
--enable-decoder=hevc --enable-parser=hevc \
--enable-decoder=h264 --enable-parser=h264
make -j 16
make install
popd
}
emscripten_build_c(){
types="264_265"
pushd $work_path
if [ -d dist ];then rm -rf dist;fi
mkdir dist
export TOTAL_MEMORY=67108864
export EXPORTED_FUNCTIONS="[ \
'_openDecoder', \
'_flushDecoder', \
'_closeDecoder', \
'_decodeData', \
'_main'
]"
echo "Running Emscripten..."
emcc decode_video.c ${ff_build_dir}/lib/libavcodec.a ${ff_build_dir}/lib/libavutil.a ${ff_build_dir}/lib/libswscale.a \
-O2 \
-I "${ff_build_dir}/include" \
-s WASM=1 \
-s TOTAL_MEMORY=${TOTAL_MEMORY} \
-s EXPORTED_FUNCTIONS="${EXPORTED_FUNCTIONS}" \
-s EXTRA_EXPORTED_RUNTIME_METHODS="['addFunction']" \
-s RESERVED_FUNCTION_POINTERS=20 \
-s FORCE_FILESYSTEM=1 \
-w -o dist/libffmpeg_${types}.js
echo "Finished Build . output file in dist: `ls dist`"
echo -e "\033[31mAre you need into $work_path execution 'npm install && npm start' ?\033[0m"
}
update_video_js(){
pushd $work_path
git restore test/video.js
local flag="displayVideoFrame(obj);"
local file="test/video.js"
line=$(cat $file | grep -n "${flag}" | awk -F ":" '{print $1}')
((line++))
echo $line
sed -i "" "${line}d" $file
sed -i "" "/${flag}/a\
},'viiiiiiiii');
" $file
popd
}
main
在shell里面bash init.sh
即可,过程大概需要5分钟
但是项目整体还是有很大的问题,播放的h265画质会错乱。可以接着优化
WebAssembly执行完全依赖CPU计算,不能借助GPU硬件加速!!!
需要尽量挖掘CPU执行提升程序效率的手段。目前主要优化手段:
WASM标准目前定义了对128bit SIMD指令集的支持规范。emscripten编译工具也支持对使用了simd能力的源代码编译成wasm(只支持通过 simd内置函数方式写的源代码)