这里以我之前做的一个项目为例。项目是把ffmpeg编译成WebAssembly文件,然后在js中调用,实现纯前端代码软解码音视频数据。
在linux下编译的
一、编译ffmpeg生成静态库
build_decoder.sh
echo "Beginning Build:"
rm -r dist
mkdir -p dist
cd ffmpeg-3.3.3
make clean
emconfigure ./configure --cc="emcc" --cxx="em++" --ar="emar" --prefix=$(pwd)/../dist --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 --enable-decoder=hevc --enable-decoder=h264 \
--disable-ffplay --disable-ffprobe --disable-ffserver --disable-asm --disable-doc --disable-devices --disable-network \
--disable-hwaccels --disable-parsers --disable-bsfs --disable-debug --disable-protocols --disable-indevs --disable-outdevs \
make
make install
cd ..
./build_decoder_wasm.sh
二、编译静态库生成libffmpeg.wasm和libffmpeg.js
build_decoder_wasm.sh
rm libffmpeg.wasm libffmpeg.js
export TOTAL_MEMORY=134217728
export EXPORTED_FUNCTIONS="[ \
'_avcodec_register_all', \
'_avcodec_find_decoder', \
'_avcodec_alloc_context3', \
'_avcodec_open2', \
'_av_free', \
'_av_frame_alloc', \
'_avcodec_close', \
'_avcodec_decode_video2_js', \
'_avcodec_get_image_width_js', \
'_avcodec_get_image_height_js', \
'_avcodec_get_chroma_format_js', \
'_avcodec_get_image_plane_js', \
'_avcodec_get_image_pitch_js', \
'_avcodec_get_image_bit_depth_js', \
'_avcodec_close_AVCodecContext_js', \
'_avcodec_flush_buffers', \
'_imgScaleChange_js', \
'_drawRect_js', \
'_setPrivacyMaskRect_js', \
'_setFullRectGrids_js', \
'_setMotionRectGrids_js'
]"
echo "Running Emscripten..."
emcc dist/lib/libavcodec.a dist/lib/libavutil.a dist/lib/libswscale.a \
-O3 \
-s WASM=1 \
-s TOTAL_MEMORY=${TOTAL_MEMORY} \
-s EXPORTED_FUNCTIONS="${EXPORTED_FUNCTIONS}" \
-o libffmpeg.js
echo "Finished Build"
echo "cp libffmpeg.wasm libffmpeg.js /home/yy/nfsfile/ipc_www_both/www/libffmpeg/"
cp libffmpeg.wasm libffmpeg.js /home/yy/nfsfile/ipc_www_both/www/libffmpeg/
cp libffmpeg.wasm libffmpeg.js /home/yy/nfsfile/newWebH5player/lib/
三、在js中调用API
上述编译脚本涉及的API中函数后缀名有_js的是我自己封装的接口,其他的是ffmpeg原生接口,API一共有:
avcodec_register_all, avcodec_find_decoder, avcodec_alloc_context3,
avcodec_open2, av_free, av_frame_alloc, avcodec_close,
avcodec_decode_video2_js,
avcodec_get_image_width_js, avcodec_get_image_height_js,
avcodec_get_chroma_format_js,
avcodec_get_image_plane_js, avcodec_get_image_pitch_js, avcodec_get_image_bit_depth_js,
avcodec_close_AVCodecContext_js, avcodec_flush_buffers,
imgScaleChange_js, drawRect_js,
setPrivacyMaskRect_js, setFullRectGrids_js, setMotionRectGrids_js'
3.1 C语言中的函数声明
void avcodec_register_all(void);
AVCodec *avcodec_find_decoder(enum AVCodecID id);
AVCodecContext *avcodec_alloc_context3(const AVCodec *codec);
int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options);
void av_free(void *ptr);
AVFrame *av_frame_alloc(void);
int avcodec_close(AVCodecContext *avctx);
int avcodec_decode_video2_js(AVCodecContext *avctx, AVFrame *picture,
const uint8_t *avpkt_data,const int avpkt_size);
const int avcodec_get_image_width_js(const AVFrame *picture);
const int avcodec_get_image_height_js(const AVFrame *picture);
const int avcodec_get_chroma_format_js(const AVFrame *picture);
const uint8_t* avcodec_get_image_plane_js(const AVFrame *picture, int channel);
const int avcodec_get_image_pitch_js(const AVFrame *picture, int channel);
const int avcodec_get_image_bit_depth_js(const AVFrame *picture);
void avcodec_close_AVCodecContext_js(AVCodec *m_codec,AVCodecContext *m_context);
void avcodec_flush_buffers(AVCodecContext *avctx);
int imgScaleChange_js(AVCodecContext *pCodecCtx,AVFrame *src_pic,AVFrame *dst_pic,AVFrame *pad_pic,int nDstW ,int nDstH,int keep_scale );
int drawRect_js(int drawRectX1,int drawRectY1,int drawRectX2,int drawRectY2,unsigned long drawRectRgb);
int setPrivacyMaskRect_js(int x1,int y1,int w1,int h1,unsigned long rgb1,
int x2,int y2,int w2,int h2,unsigned long rgb2,
int x3,int y3,int w3,int h3,unsigned long rgb3,
int x4,int y4,int w4,int h4,unsigned long rgb4);
int setFullRectGrids_js(int gridsColumns,int gridsRows,int fullGridsRgb,int fullGridsEnable);
int setMotionRectGrids_js(int gridsColumns,int gridsRows,int motionGridsRgb,unsigned char *motionBlocks);
3.2 JS的调用
3.2.1 导入libffmpeg.js
(libffmpeg.js中会去加载libffmpeg.wasm的)
importScripts('libffmpeg.js');
3.2.2 接口封装
括号内的参数中,第一个number对应C函数的返回值,最后面的[]的成员对应C函数的参数。
参数数据类型number可以对应C语言中的int或是指针等,大部分都是number。
参数数据类型array是js中的数组,对应到C函数参数中也是指针,我使用的是unsigned char*
array与number不同的是,array传递到C函数中,可以通过这个指针地址取到传递过来的数组的数据。而number传递过来只是一个数字,即使转成指针地址也取不到数组内容的数据。
(function(){
var libffmpeg = {
avcodec_register_all: Module["cwrap"]('avcodec_register_all', 'number'),
avcodec_find_decoder: Module["cwrap"]('avcodec_find_decoder', 'number', ['number']),
avcodec_alloc_context3: Module["cwrap"]('avcodec_alloc_context3', 'number', ['number']),
avcodec_open2: Module["cwrap"]('avcodec_open2', 'number', ['number', 'number', 'number']),
av_free: Module["cwrap"]('av_free', 'number', ['number']),
av_frame_alloc: Module["cwrap"]('av_frame_alloc', 'number'),
avcodec_close: Module["cwrap"]('avcodec_close', 'number', ['number']),
avcodec_decode_video2_js: Module["cwrap"]('avcodec_decode_video2_js', 'number', ['number', 'number', 'array', 'number']),
avcodec_get_image_width_js: Module["cwrap"]('avcodec_get_image_width_js', 'number', ['number']),
avcodec_get_image_height_js: Module["cwrap"]('avcodec_get_image_height_js', 'number', ['number']),
avcodec_get_chroma_format_js: Module["cwrap"]('avcodec_get_chroma_format_js', 'number', ['number']),
avcodec_get_image_plane_js: Module["cwrap"]('avcodec_get_image_plane_js', 'number', ['number', 'number']),
avcodec_get_image_pitch_js: Module["cwrap"]('avcodec_get_image_pitch_js', 'number', ['number', 'number']),
avcodec_get_image_bit_depth_js: Module["cwrap"]('avcodec_get_image_bit_depth_js', 'number', ['number']),
avcodec_close_AVCodecContext_js:Module["cwrap"]('avcodec_close_AVCodecContext_js', 'number', ['number', 'number']),
avcodec_flush_buffers: Module["cwrap"]('avcodec_flush_buffers', 'number', ['number']),
imgScaleChange_js: Module["cwrap"]('imgScaleChange_js', 'number', ['number', 'number', 'number', 'number', 'number', 'number', 'number']),
drawRect_js: Module["cwrap"]('drawRect_js', 'number', ['number', 'number', 'number', 'number', 'number']),
setPrivacyMaskRect_js: Module["cwrap"]('setPrivacyMaskRect_js', 'number', ['number', 'number', 'number', 'number', 'number',
'number', 'number', 'number', 'number', 'number',
'number', 'number', 'number', 'number', 'number',
'number', 'number', 'number', 'number', 'number']),
setFullRectGrids_js: Module["cwrap"]('setFullRectGrids_js', 'number', ['number', 'number', 'number', 'number']),
setMotionRectGrids_js: Module["cwrap"]('setMotionRectGrids_js', 'number', ['number', 'number', 'number', 'array']),
AV_CODEC_ID_H264: 28,
AV_CODEC_ID_H265: 174, // 0x48323635,
_chroma_mono: 0,
_chroma_420: 1,
_chroma_422: 2,
_chroma_444: 3,
};
3.2.3 封装好的libffmpeg js接口的使用示例
libffmpeg.avcodec_register_all();
var m_codec = libffmpeg.avcodec_find_decoder(AV_CODEC_ID);
var img = libffmpeg.avcodec_decode_video2_js(this.m_context, this.m_src_pic, data, size);
var w = libffmpeg.avcodec_get_image_width_js(m_pic);
libffmpeg.setMotionRectGrids_js(gridsColumns,gridsRows,motionGridsRgb,motionBlocks);
这里就不一一列举了,封装好的js接口可直接像平常的js函数那样使用。
至此,JS如何调用WebAssembly的api就讲完了。