作者:Jerome Wu
原文链接:Build FFmpeg WebAssembly version (= ffmpeg.wasm): Part.4 ffmpeg.wasm v0.2 — Add Libx264
译者:Yodoxu
上一篇文章:编译WebAssembly版本的FFmpeg(ffmpeg.wasm):(3)ffmpeg.wasm v0.1 - 将avi转为mp4的编码
在这一部分中,你将学习:
- 将Libx264添加到ffmpeg-core.js中
- 在浏览器中的ffmpeg.wasm demo
添加Libx264到ffmpeg-core.js中
下一步,我们想对avi视频进行转码,并在我们的网络浏览器中播放它。默认情况下,ffmpeg-core.js可以将avi转码为mp4,但是mp4文件不能在web浏览器中播放,因为它的编码不被支持。所以我们需要先将libx264添加到我们的ffmpeg-core.js中。
下面是我们要添加的x264
库的链接。
https://code.videolan.org/videolan/x264
与ffmpeg相比,x264
的构建要容易得多,下面是你需要传递的关键参数
#!/bin/bash -x
ARGS=(
--host=i686-gnu # use i686 gnu
--enable-static # enable building static library
--disable-cli # disable cli tools
--disable-asm # disable asm optimization
--extra-cflags="-s USE_PTHREADS=1" # pass this flags for using pthreads
)
emconfigure ./configure "${ARGS[@]}"
请查看版本库中的build-x264.sh的完整版本。
在配置ffmpeg时,必须添加--enable-gpl
和--enable-libx264
的标志。
#!/bin/bash -x
# ...
CONFIG_ARGS=(
--target-os=none # use none to prevent any os specific configurations
--arch=x86_32 # use x86_32 to achieve minimal architectural optimization
--enable-cross-compile # enable cross compile
--disable-x86asm # disable x86 asm
--disable-inline-asm # disable inline asm
--disable-stripping # disable stripping
--disable-programs # disable programs build (incl. ffplay, ffprobe & ffmpeg)
--disable-doc # disable doc
--enable-gpl ## required by x264
--enable-libx264 ## enable x264
--extra-cflags="$CFLAGS"
--extra-cxxflags="$CFLAGS"
--extra-ldflags="$LDFLAGS"
--nm="llvm-nm -g"
--ar=emar
--as=llvm-as
--ranlib=llvm-ranlib
--cc=emcc
--cxx=em
--objcc=emcc
--dep-cc=emcc
)
emconfigure ./configure "${CONFIG_ARGS[@]}"
# ...
ARGS=(
-I. -I./fftools -I$BUILD_DIR/include
-Llibavcodec -Llibavdevice -Llibavfilter -Llibavformat -Llibavresample -Llibavutil -Llibpostproc -Llibswscale -Llibswresample -L$BUILD_DIR/lib
-Qunused-arguments
-o wasm/dist/ffmpeg-core.js fftools/ffmpeg_opt.c fftools/ffmpeg_filter.c fftools/ffmpeg_hw.c fftools/cmdutils.c fftools/ffmpeg.c
# Add -lpostproc and -lx264 below
-lavdevice -lavfilter -lavformat -lavcodec -lswresample -lswscale -lavutil -lpostproc -lm -lx264 -pthread
-O3 # Optimize code with performance first
-s USE_SDL=2 # use SDL2
-s USE_PTHREADS=1 # enable pthreads support
-s PROXY_TO_PTHREAD=1 # detach main() from browser/UI main thread
-s INVOKE_RUN=0 # not to run the main() in the beginning
-s EXPORTED_FUNCTIONS="[_main, _proxy_main]" # export main and proxy_main funcs
-s EXTRA_EXPORTED_RUNTIME_METHODS="[FS, cwrap, setValue, writeAsciiToMemory]" # export preamble funcs
-s INITIAL_MEMORY=268435456 ## increase to 268435456 bytes = 256 MB
)
emcc "${ARGS[@]}"
查看仓库中的configure.sh和build-ffmpeg.sh的完整版本。
有了所有的脚本,现在你可以用x264构建ffmpeg.wasm(也可能是所有其他的库。)
在浏览器中的ffmpeg.wasm demo
这篇文章的最后一部分是ffmpeg.wasm v0.2的演示,场景是创建一个网页,使用户能够上传一个视频文件(例如avi)并在网页浏览器中播放。由于不可能直接播放avi文件,我们将使用ffmpeg.wasm先对视频进行转码。
以下是完整的HTML代码(按这里下载样本视频)。
代码语言:shell复制<html>
<head>
<style>
html, body {
margin: 0;
width: 100%;
height: 100%
}
body {
display: flex;
flex-direction: column;
align-items: center;
}
</style>
</head>
<body>
<h3>Upload a video to transcode to mp4 (x264) and play!</h3>
<video id="output-video" controls></video><br/>
<input type="file" id="uploader">
<p id="message">Remeber to wait for 5 seconds for ffmpeg.wasm to load</p>
<script type="text/javascript">
const readFromBlobOrFile = (blob) => (
new Promise((resolve, reject) => {
const fileReader = new FileReader();
fileReader.onload = () => {
resolve(fileReader.result);
};
fileReader.onerror = ({ target: { error: { code } } }) => {
reject(Error(`File could not be read! Code=${code}`));
};
fileReader.readAsArrayBuffer(blob);
})
);
const message = document.getElementById('message');
const transcode = async ({ target: { files } }) => {
const { name } = files[0];
message.innerHTML = 'Writing file to MEMFS';
const data = await readFromBlobOrFile(files[0]);
Module.FS.writeFile(name, new Uint8Array(data));
const ffmpeg = Module.cwrap('proxy_main', 'number', ['number', 'number']);
const args = ['ffmpeg', '-hide_banner', '-nostdin', '-report', '-i', name, 'out.mp4'];
const argsPtr = Module._malloc(args.length * Uint32Array.BYTES_PER_ELEMENT);
args.forEach((s, idx) => {
const buf = Module._malloc(s.length 1);
Module.writeAsciiToMemory(s, buf);
Module.setValue(argsPtr (Uint32Array.BYTES_PER_ELEMENT * idx), buf, 'i32');
});
message.innerHTML = 'Start to transcode';
ffmpeg(args.length, argsPtr);
/*
* The execution of ffmpeg is not synchronized,
* so we need to parse the log file to check if completed.
*/
const timer = setInterval(() => {
const logFileName = Module.FS.readdir('.').find(name => name.endsWith('.log'));
if (typeof logFileName !== 'undefined') {
const log = String.fromCharCode.apply(null, Module.FS.readFile(logFileName));
if (log.includes("frames successfully decoded")) {
clearInterval(timer);
message.innerHTML = 'Finish transcoding';
const out = Module.FS.readFile('out.mp4');
const video = document.getElementById('output-video');
video.src = URL.createObjectURL(new Blob([out.buffer], { type: 'video/mp4' }));
}
}
}, 500);
};
document.getElementById('uploader').addEventListener('change', transcode);
</script>
<script type="text/javascript" src="./dist/ffmpeg-core.js"></script>
</body>
</html>
它可能需要很长时间才能完成,你可以打开DevTools查看日志。检查transcode.html,看看它是如何工作的。
你可以在这里访问Github库,看看它的工作细节:https://github.com/ffmpegwasm/FFmpeg/tree/n4.3.1-p4
也可以在这里自由下载构建工件:https://github.com/ffmpegwasm/FFmpeg/releases/tag/n4.3.1-p4
这一系列的故事到此为止,希望你能学会如何从头开始构建你自己的ffmpeg.wasm,并有可能应用于其他库。下一次见!