编译WebAssembly版本的FFmpeg(ffmpeg.wasm):(4)ffmpeg.wasm v0.2 - 添加Libx264

2022-04-14 20:24:12 浏览数 (1)

作者: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的编码

在这一部分中,你将学习:

  1. 将Libx264添加到ffmpeg-core.js中
  2. 在浏览器中的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的构建要容易得多,下面是你需要传递的关键参数

代码语言:shell复制
#!/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的标志。

代码语言:shell复制
#!/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,并有可能应用于其他库。下一次见!

0 人点赞