Git Product home page Git Product logo

decoder_wasm's Introduction

1 简介

借助于WebAssembly(简称Wasm)技术,实现在浏览器端调用ffmpeg接口完成H.265码流到YUV数据的解码。
总体流程如下: Decode With FFmpeg and WASM

2 依赖

按照官网的定义,WebAssembly (wasm) 是一个可移植、体积小、加载快并且兼容 Web 的全新格式。通过wasm,可以在浏览器里执行原生代码(例如C、C++)。
目前,wasm技术已经得到主流浏览器的广泛支持(数据来源Can I Use)。 Browser Suport For WASM

2.2 FFmpeg

FFmpeg是一套可以用来记录、转换数字音频、视频,并能将其转化为流的开源计算机程序。采用LGPL或GPL许可证。它提供了录制、转换以及流化音视频的完整解决方案。
我们代码里主要使用FFmpeg来做解码(decode)。为了减小体积,最终编译生成的wasm里包含的是裁剪过的FFmpeg,主要包含以下几个库:

  • libavcodec: 编解码(最重要的库)
  • libavutil: 工具库(大部分库都需要这个库的支持)
  • libswscale: 视频像素数据格式转换

3 具体实现

3.1 接口

编译生成的wasm文件对外提供四个接口:

  • openDecoder:初始化解码器;
  • decodeData:解码传入的H.265码流数据;
  • flushDecoder:清空缓存数据;
  • closeDecoder:关闭解码器;

3.2 实现细节

解码过程中使用到的FFmpeg API及解码流程如下图所示: decoder

3.3 如何使用

最终的编译结果是两个文件,一个是包含ffmpeg库的wasm文件,另一个是胶水代码(js文件)。页面里引用js文件时,胶水代码会加载wasm。
Javascript与WASM的数据交互:

// 发送:
var cacheBuffer = Module._malloc(data.length);
Module.HEAPU8.set(data, cacheBuffer);
var ret = Module._decodeData(cacheBuffer, data.length, pts);

// 接收:
var videoSize = 0;
var videoCallback = Module.addFunction(function (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", ++videoSize, width, height, pts)
    let out_y = HEAPU8.subarray(addr_y, addr_y + stride_y * height)
    let out_u = HEAPU8.subarray(addr_u, addr_u + (stride_u * height) / 2)
    let out_v = HEAPU8.subarray(addr_v, addr_v + (stride_v * height) / 2)
    let buf_y = new Uint8Array(out_y)
    let buf_u = new Uint8Array(out_u)
    let buf_v = new Uint8Array(out_v)
    let data = new Uint8Array(buf_y.length + buf_u.length + buf_v.length)
    data.set(buf_y, 0)
    data.set(buf_u, buf_y.length)
    data.set(buf_v, buf_y.length + buf_u.length)
    var obj = {
        data: data,
        width,
        height
    }
    displayVideoFrame(obj);
});
var codecType = 1; // 0 - H.264, 1 - H.265
var ret = Module._openDecoder(codecType, videoCallback, LOG_LEVEL_WASM)
// 需要把回调通过openDecoder方法传入C层,在C层调用。

4 编译

4.1 安装Wasm工具链Emscripten

安装步骤可参考其官方文档,目前支持 Windows, MacOS, Linux。

建议版本:1.38.45, 编译运行都没问题。

4.2 下载FFmpeg

mkdir goldvideo
cd goldvideo
git clone https://git.ffmpeg.org/ffmpeg.git
cd ffmpeg
git checkout -b 4.1 origin/release/4.1

这里切到了4.1分支。

4.3 下载本文的代码

保证FFmpeg目录和代码目录平级。

git clone http://github.com/goldvideo/decoder_wasm.git
cd decoder_wasm

目录结构:

├─goldvideo
│  ├─ffmpeg
│  ├─decoder_wasm

4.4 编译

进入代码目录,根据需要,以下命令三选一执行:

./build_decoder_264.sh      //支持解码 H.264
./build_decoder_265.sh      //支持解码 H.265
./build_decoder_264_265.sh  //支持解码 H.264 和 H.265

5 测试

5.1 WebGL

H5使用Canvas来绘图,但是默认的2d模式只能绘制RGB格式,使用FFmpeg解码出来的视频数据是YUV格式,想要渲染出来需要进行颜色空间转换,可以使用FFmpeg的libswscale模块进行转换。 为了提升性能,这里使用了WebGL来硬件加速,主要参考了这个项目,做了一些修改: https://github.com/p4prasoon/YUV-Webgl-Video-Player

5.2 启动:

npm install
npm start

5.3 测试页面:

http://localhost:3000/test/main.html

6 参考项目

WasmVideoPlayer.

decoder_wasm's People

Contributors

snxly avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

decoder_wasm's Issues

有人重新编译成功了的嘛?

我直接用dist文件夹下的libffmpeg_264_265.js和libffmpeg_264_265.wasm是可以的,但是执行里面的相关sh脚本重新编译就失败了,请问这里所需的emscripten和FFmpeg版本分别是什么?

yuv420p数据使用webgl渲染失败,可以解答下吗谢谢

博主你好,参考你的webgl.js方法将yuv420p数据渲染在网页上失败:
image

我这边思路:
1)在websock服务器推送h.265/h.264裸流到浏览器中
2)在js中收流,调用ffmpeg解码接口(加载ffmpeg.js 和ffmpeg.wasm)将裸流数据转成yuv420p数据
3)yuv420p数据使用webgl渲染在网页上
裸流->yuv数据接口函数在Linux里跑过程序,写下yuv数据在yuv播放器可以正常播放

使用emsdk工具编译ffmpeg源代码错误

博主你好,使用emsdk工具编译ffmpeg-4.1.5源代码编译不过,请问怎么解决
image
1)报错地方如下,找不到/opt/tar-download/emsdk/upstream/bin/wasm-opt: /usr/lib64/libc.so.6: version `GLIBC_2.18' not found (required by /opt/tar-download/emsdk/upstream/bin/wasm-opt)
image
2)emsdk工具版本 1.39.15
image

手动使用两个停止函数后会持续报错

在原案例上, 我手动添加了一个停止按钮,只使用_flushDecoder和_closeDecoder两个函数。会出现下面的错误信息,
Uncaught RuntimeError: memory access out of bounds
请问是什么原因导致的呢?

解码出来的yuv数据只有最后一帧能渲染到canvas上

你好,我实验的视频流是类似rtp结构的,我解码之后,先在本地查看生成的yuv图片,都是正常的,但是在使用你的播放器播放时,直到整个流解析完了之后,最后一帧才会被渲染到canvas上,开始还以为是pts设置的原因,但是直接用了你的这种方法之后发现还是不行,想请问一下是什么原因呢

作者您好,一个编译问题

您好,请教一个编译问题。我这边按照示例说明,编译了ffmpeg4.1.6版本,但总是提示。编译其他版本也是一样的,编译环境采用的是Ubuntu18.04

当载入H264或者H265后,总是出现报错,如下
libffmpeg_265.js:1 Uncaught TypeError: Cannot read property 'slice' of undefined
at convertJsFunctionToWasm (libffmpeg_265.js:1)
at addFunctionWasm (libffmpeg_265.js:1)
at Object.addFunction (libffmpeg_265.js:1)
at decode_seq (video.js:30)
at handleVideoFiles (video.js:14)
at HTMLInputElement.onchange (main.html:11)

音频播放无问题

但使用从源程序中下载的libffmpeg_265.js 和 libffmpeg_265.wasm 却不会报错,是编译上哪里出问题了么?还是需要代码进行调整?

x265 wasm编译不过

[ 98%] Building CXX object CMakeFiles/cli.dir/abrEncApp.cpp.o
[100%] Linking CXX executable x265.js
wasm-ld: warning: unknown -z value: noexecstack
wasm-ld: warning: -Bsymbolic is only meaningful when combined with -shared
wasm-ld: warning: function signature mismatch: x265_cpu_xgetbv

defined as (i32) -> i64 in libx265.a(cpu.cpp.o)
defined as (i32, i32, i32) -> void in libx265.a(primitives.cpp.o)

wasm-ld: error: libx265.a(ringmem.cpp.o): undefined symbol: sem_close
wasm-ld: error: libx265.a(ringmem.cpp.o): undefined symbol: sem_unlink
wasm-ld: error: libx265.a(ringmem.cpp.o): undefined symbol: sem_close
wasm-ld: error: libx265.a(ringmem.cpp.o): undefined symbol: sem_unlink
em++: error: 'xxx/emsdk/upstream/bin/wasm-ld -o x265.wasm -Bsymbolic -znoexecstack CMakeFiles/cli.dir/input/input.cpp.o CMakeFiles/cli.dir/input/yuv.cpp.o CMakeFiles/cli.dir/input/y4m.cpp.o CMakeFiles/cli.dir/output/output.cpp.o CMakeFiles/cli.dir/output/reconplay.cpp.o CMakeFiles/cli.dir/output/yuv.cpp.o CMakeFiles/cli.dir/output/y4m.cpp.o CMakeFiles/cli.dir/output/raw.cpp.o CMakeFiles/cli.dir/x265.cpp.o CMakeFiles/cli.dir/x265cli.cpp.o CMakeFiles/cli.dir/abrEncApp.cpp.o libx265.a -L/xxx/emsdk/upstream/emscripten/cache/sysroot/lib/wasm32-emscripten -lGL -lal -lhtml5 -lstubs -lnoexit -lc -ldlmalloc -lcompiler_rt -lc++-noexcept -lc++abi-noexcept -lsockets -mllvm -combiner-global-alias-analysis=false -mllvm -enable-emscripten-sjlj -mllvm -disable-lsr /tmp/tmpb9m_3v81libemscripten_js_symbols.so --strip-debug --export-if-defined=main --export-if-defined=__start_em_asm --export-if-defined=__stop_em_asm --export-if-defined=__start_em_lib_deps --export-if-defined=__stop_em_lib_deps --export-if-defined=__start_em_js --export-if-defined=__stop_em_js --export-if-defined=__main_argc_argv --export=stackSave --export=stackRestore --export=stackAlloc --export=__errno_location --export=__get_temp_ret --export=__set_temp_ret --export=__wasm_call_ctors --export-table -z stack-size=65536 --initial-memory=16777216 --no-entry --max-memory=16777216 --global-base=1024' failed (returned 1)

Decode 4k h265 videos

Thanks for the great work, it actually works very well with h264 and h265 video files up to HD resolution.
Decoding h264 4k video files works at about 10 fps and Decoding h265 4k video files works at about 3 fps.
I did some logs in the video_decoder.c file to check out what is going on. FFMPEG method: av_send_packet actually takes the longest execution time (about 350 ms per frame for h265 4k files), other methods are working very well.

Any ideas how to improve the performance of this method? I knew that this might be a limitation from web assembly technology itself, but can we somehow enable multi-threading or hardware acceleration to get better performance?

编译错误

按照作者环境一步步走下来,在执行emconfigure后各种头文件定义错误。死活make不过

将emsdk升级到最高版本:我目前的版本为emcc(version:3.1.29) ,然后即可make编译通过(可以修改脚本里面的make为 make -j 16)

最后执行./build_decoder_wasm.sh又报错:
需要把 “int initBuffer(width, height)” 改为 “int initBuffer(int width, int height)” 最后即可编译生成 .wasm .js

但是上面的方法最终运行不了,无奈~没学过js,只好回头来解决编译问题

最终我将编译步骤写成了一个完整的脚本 ,它可以完美的帮你一键编译完成,脚本如下:

`
#!/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
`

脚本除了首次执行外,之后的执行过程中会有一次交互操作,"Recompile FFmpeg???? [y/N]" 如果需要再次编译则手动输入 ”y“来确认编译ffmpeg,因为这个一项耗时操作,为了防止误操作所以我将它单独分出来执行。 最后执行npm install && npm start就可以完成部署了。。。。已经在Mac OS (Intel) 、 Ubantu 18上面完美一键搭建完成。

ffmpeg解码会产生延时吗

你好,想问个问题,谢谢
使用ffmpegAPI软解h.265/h.264裸流 为yuv420p数据会产生延时吗,推送端,推送实时流足够快,裸流到了ffmpeg解码这里慢了,然后后面导致推流端堆积了裸流。产生延时固定在3-6s

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.