Список переведённых частей серии:
Начиная с этой части, материал будет посложнее, так что не стесняйтесь гуглить по ходу чтения, если не понимаете, что происходит.
К тому же я постараюсь задокументировать решение возможных проблем, чтобы вы смогли скомпилировать бибилиотеку со своими настройками.
В этой части мы разберём:
- Как настроить окружение для Emscripten в Docker
- Использование emconfigure и emmake
- Как решать проблемы, возникающие при компиляции FFmpeg с Emscripten
Как настроить окружение для Emscripten в Docker
В первой части мы собрали FFmpeg с gcc и можем перейти к использованию образа Докера с emscripten.
Я буду использовать trzeci/emscripten версии 1.38.45:
$ docker pull trzeci/emscripten:1.38.45
Так как образ занимает около 1 Гб, процесс может занять некоторое время.
Теперь найдём правильную конфигурацию для компиляции FFmpeg в emscripten методом проб и ошибок, что потребует усидчивости и чтения больших объёмов документации. Запустим контейнер с emscripten и монтируем исходники FFmpeg в каталог /src.
# Убедитесь, что вы в корне репозитория FFmpeg
$ docker run -it
-v $PWD:/src
trzeci/emscripten:1.38.45
/bin/bash
Внутри контейнера выполните ls --color, чтобы увидеть что-то подобное:
Использование emconfigure и emmake. Как решать проблемы, возникающие при компиляции
Начнём с конфигурации. В первой части мы выполняли ./configure --disable-x86asm, в emscripten это достигается командой emconfigure ./configure --disable-x86asm. (Детали использования emconfigure смотрите здесь)
$ emconfigure ./configure --disable-x86asm
И так как ошибок мы не увидели, осталось лишь выполнить emmake make -j4 и получить заветный FFmpeg.js? К сожалению, нет. Одной из наиболее важных задач для emconfigure является замена компилятора gcc на emcc (или g++ на em++), но результат выполнения ./configure всё ещё выдаёт gcc.
root@57ab95def750:/src# emconfigure ./configure --disable-x86asm
emscripten sdl2-config called with /emsdk_portable/emscripten/tag-1.38.45/system/bin/sdl2-config --cflags
emscripten sdl2-config called with /emsdk_portable/emscripten/tag-1.38.45/system/bin/sdl2-config --libs
install prefix /usr/local
source path .
C compiler gcc # А должно быть emcc
C library glibc
ARCH x86 (generic)
big-endian no
runtime cpu detection yes
standalone assembly no
x86 assembler nasm
У любой автоматизации есть свои пределы и в данном случае, к сожалению, нам придётся делать всё вручную. Давайте посмотрим, есть ли какие-нибудь аргументы нам в помощь:
$ ./configure --help
Под разделом Toolchain options мы видим аргументы для указания типа компилятора.
root@57ab95def750:/src# ./configure --help
Usage: configure [options]
Options: [defaults in brackets after descriptions]Help options:
...
Toolchain options:
...
--nm=NM use nm tool NM [nm -g]
--ar=AR use archive tool AR [ar]
--as=AS use assembler AS []
--ln_s=LN_S use symbolic link tool LN_S [ln -s -f]
--strip=STRIP use strip tool STRIP [strip]
--windres=WINDRES use windows resource compiler WINDRES [windres]
--x86asmexe=EXE use nasm-compatible assembler EXE [nasm]
--cc=CC use C compiler CC [gcc]
--cxx=CXX use C compiler CXX [g++]
--objcc=OCC use ObjC compiler OCC [gcc]
--dep-cc=DEPCC use dependency generator DEPCC [gcc]
--nvcc=NVCC use Nvidia CUDA compiler NVCC [nvcc]
--ld=LD use linker LD []
...
Давайте используем их в emscripten
$ emconfigure ./configure
--disable-x86asm
--nm="llvm-nm -g"
--ar=emar
--cc=emcc
--cxx=em++
--objcc=emcc
--dep-cc=emcc
Теперь выполнение ./configure займёт больше времени, но в результате мы получим emcc.
root@57ab95def750:/src# emconfigure ...
emscripten sdl2-config called with /emsdk_portable/emscripten/tag-1.38.45/system/bin/sdl2-config --cflags
emscripten sdl2-config called with /emsdk_portable/emscripten/tag-1.38.45/system/bin/sdl2-config --libs
install prefix /usr/local
source path .
C compiler emcc # emcc как и необходимо
C library
ARCH x86 (generic)
big-endian no
runtime cpu detection yes
standalone assembly no
Посмотрим, как пройдёт компиляция.
$ emmake make -j4
И сразу ошибка…
root@57ab95def750:/src# emmake make -j4
...
./libavutil/x86/timer.h:39:24: error: invalid output constraint '=a' in asm
: "=a" (a), "=d" (d));
^
Из сообщения видно, что ошибка как-то связана с asm. Откроем ./libavutil/x86/timer.h чтобы увидеть, что проблема в инлайновом ассемблере x86, который несовместим с WebAssembly, так что отключим его.
$ emconfigure ./configure
--disable-x86asm
--disable-inline-asm # Отключаем инлайн asm
--nm="llvm-nm -g"
--ar=emar
--cc=emcc
--cxx=em++
--objcc=emcc
--dep-cc=emcc
Попробуем скомпилировать вновь.
$ emmake make -j4
Компиляция продолжается до следующей ошибки
root@57ab95def750:/src# emmake make -j4
...
AR libavdevice/libavdevice.a
AR libavfilter/libavfilter.a
AR libavformat/libavformat.a
AR libavcodec/libavcodec.a
AR libswresample/libswresample.a
AR libswscale/libswscale.a
AR libavutil/libavutil.a
HOSTLD doc/print_options
GENTEXI doc/avoptions_format.texi
/bin/sh: 1: doc/print_options: Exec format error
doc/Makefile:59: recipe for target 'doc/avoptions_format.texi' failed
make: *** [doc/avoptions_format.texi] Error 2
make: *** Waiting for unfinished jobs....
Что-то связанное с генерацией документации, которая нам совершенно не нужна, так что просто отключим её.
$ emconfigure ./configure
--disable-x86asm
--disable-inline-asm
--disable-doc # Отключаем генерацию документации
--nm="llvm-nm -g"
--ar=emar
--cc=emcc
--cxx=em++
--objcc=emcc
--dep-cc=emcc
Вновь выполняем.
$ emmake make -j4
Теперь ошибка возникла на этапе strip.
root@57ab95def750:/src# emmake make -j4
...
STRIP ffmpeg
STRIP ffprobe
strip:ffmpeg_g: File format not recognized
strip:ffprobe_g: File format not recognized
Makefile:101: recipe for target 'ffmpeg' failed
make: *** [ffmpeg] Error 1
make: *** Waiting for unfinished jobs....
Makefile:101: recipe for target 'ffprobe' failed
make: *** [ffprobe] Error 1
Раз нативная обрезка несовместима с нашей версией WebAssembly, отключим и её.
$ emconfigure ./configure
--disable-x86asm
--disable-inline-asm
--disable-doc
--disable-stripping # Отключаем strip
--nm="llvm-nm -g"
--ar=emar
--cc=emcc
--cxx=em++
--objcc=emcc
--dep-cc=emcc
Четвёртая попытка.
$ emmake make -j4
Наконец-то процесс завершился без ошибок. Вот только на выходе мы получили файл ffmpeg, который не запускается, да и не является js файлом (или wasm файлом). Чтобы получить js файл, нам нужно добавить -o ffmpeg.js в комманду emcc, что можно сделать двумя способами:
- Изменить Makefile самого FFmpeg
- Добавить дополнительную компиляцию/линковку
Мы выберем второй путь, так как я не хочу трогать исходники FFmpeg из-за возможных побочных эффектов. Так что найдём как генерируется ffmpeg с помощью make. Здесь пригодится возможность make запустить сухой прогон (dry-run).
$ emmake make -n
Видим команду генерации.
root@57ab95def750:/src# emmake make -n
...
printf "LDt%sn" ffmpeg_g; emcc -Llibavcodec -Llibavdevice -Llibavfilter -Llibavformat -Llibavresample -Llibavutil -Llibpostproc -Llibswscale -Llibswresample -Wl,--as-needed -Wl,-z,noexecstack -Wl,--warn-common -Wl,-rpath-link=libpostproc:libswresample:libswscale:libavfilter:libavdevice:libavformat:libavcodec:libavutil:libavresample -Qunused-arguments -o ffmpeg_g fftools/ffmpeg_opt.o fftools/ffmpeg_filter.o fftools/ffmpeg_hw.o fftools/cmdutils.o fftools/ffmpeg.o -lavdevice -lavfilter -lavformat -lavcodec -lswresample -lswscale -lavutil -lm -pthread -lm -lm -pthread -lm -lm -lm -pthread -lm
printf "CPt%sn" ffmpeg; cp -p ffmpeg_g ffmpeg
...
Много всего ненужного, так что давайте уберём неиспользуемые аргументы (которые вы увидите в конце компиляции), немного приберёмся и переименуем ffmpeg_g в ffmpeg.js.
$ emcc
-Llibavcodec -Llibavdevice -Llibavfilter -Llibavformat -Llibavresample -Llibavutil -Llibpostproc -Llibswscale -Llibswresample
-Qunused-arguments
-o ffmpeg.js fftools/ffmpeg_opt.o fftools/ffmpeg_filter.o fftools/ffmpeg_hw.o fftools/cmdutils.o fftools/ffmpeg.o
-lavdevice -lavfilter -lavformat -lavcodec -lswresample -lswscale -lavutil -lm -pthread
Должно было сработать, но мы столкнёмся с проблемой отсутствия памяти.
root@57ab95def750:/src# emcc ...
shared:ERROR: Memory is not large enough for static data (11794000) plus the stack (5242880), please increase TOTAL_MEMORY (16777216) to at least 17037904
Добавим аргумент TOTAL_MEMORY для увеличения размера памяти (33554432 Bytes := 32 MB).
$ emcc
-Llibavcodec -Llibavdevice -Llibavfilter -Llibavformat -Llibavresample -Llibavutil -Llibpostproc -Llibswscale -Llibswresample
-Qunused-arguments
-o ffmpeg.js fftools/ffmpeg_opt.o fftools/ffmpeg_filter.o fftools/ffmpeg_hw.o fftools/cmdutils.o fftools/ffmpeg.o
-lavdevice -lavfilter -lavformat -lavcodec -lswresample -lswscale -lavutil -lm -pthread
-s TOTAL_MEMORY=33554432
Наконец-то мы получили наши js и wasm файлы
root@57ab95def750:/src# ls ffmpeg*
ffmpeg ffmpeg.js ffmpeg.js.mem ffmpeg.wasm ffmpeg.worker.js ffmpeg_g
Создадим test.html для тестирования FFmpeg.js
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<script src="./ffmpeg.js"></script>
</head>
<body>
</body>
</html>
Запустим лёгенький сервер (выполнив python2 -m SimpleHTTPServer) и откроем получившуюся страницу (http://localhost:8000/test.html), после чего откроем Chrome DevTools.
Как видно по сообщениям, FFmpeg с грехом пополам работает, так что теперь можно приступать к полировке ffmpeg.js.
Полностью скрипт для сборки можно найти в этом репозитории (build-with-docker.sh и build-js.sh)
.
Автор: germn