Обновление 2020/9: измените историю, чтобы она работала в MacOS.
Обновление 2020/2/11: судя по ответам в этой истории, в настоящее время следующие инструкции не работают в среде Mac. Я не могу решить эту проблему, так как у меня ее нет. Самый простой способ сделать это на Mac - установить дистрибутив Linux, например Ubuntu, на виртуальную машину.
Предыдущая статья: Сборка версии FFmpeg WebAssembly (= ffmpeg.wasm): Часть 1 Подготовка
С этого момента все будет сложнее и труднее для понимания. Возможно, вам потребуются дополнительные знания в Google, если вы не знаете, что произошло (или вы можете оставить ответы, чтобы спросить меня).
Кроме того, чтобы сделать это руководство более удобным, я стараюсь подробно описать, как я решил каждую проблему, надеюсь, это поможет вам в создании библиотеки по вашему выбору.
В этой части вы узнаете:
- Как настроить среду Emscripten с помощью Docker
- Использование
emconfigure
иemmake
- Как исправить проблемы при компиляции FFmpeg с Emscripten
Как настроить среду Emscripten с помощью Docker
В Build FFmpeg WebAssembly version (= ffmpeg.wasm): Part.1 Preparation мы построили исходную версию FFmpeg с GCC, и теперь мы переходим к использованию Emscripten.
Версия Emscripten, которую мы собираемся использовать, - 1.39.18 (trzeci / emscripten: 1.39.18-upstream), вы можете установить Emscripten с помощью официального руководства (в этом руководстве мы setup-emsdk Github Actions в MacOS) или вытащите образ Emscripten из концентратора докеров.
$ docker pull trzeci/emscripten:1.39.18-upstream
Это может занять несколько минут, так как размер изображения составляет около 1 ГБ.
Затем нам нужно обновить build-with-docker.sh
, как показано ниже:
Строка 8 не требуется, но она может помочь вам ускорить последующую сборку.
Следующее, что мы собираемся сделать, это найти конфигурацию для создания FFmpeg с emscripten, это процесс проб и ошибок, требующий копания документов и терпения.
Использование emconfigure
и emmake &
Как исправить проблемы при компиляции FFmpeg с Emscripten
Давайте начнем наш путь к поиску правильной конфигурации. В Части 1 он начинается с ./configure --disable-x86asm
, чтобы сделать это с помощью emscripten, вам нужно изменить его на emconfigure ./configure --disable-x86asm
. (Подробнее о emconfigure см. ЗДЕСЬ). Поскольку мы выполняем кросс-компиляцию, нам нужно добавить флаги кросс-компиляции, чтобы явно указать FFmpeg.
Давайте обновим build.sh
, как показано ниже:
И волшебным образом нет никакой ошибки или чего-то неправильного, так что нам просто нужно набрать emmake make -j
, и мы получим FFmpeg.wasm? К сожалению, нет. Одна из наиболее важных задач для emconfigure
- заменить компилятор с gcc на emcc (или с g ++ на em ++), но в выводе ./configure
мы по-прежнему получаем gcc в качестве нашего компилятора.
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 # Should be 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 в build.sh
:
Для собственной сборки убедитесь, что
llvm-ranlib
,llvm-as
иllvm-nm
существуют. Если нет, вы можете найти их в$EMSDK_ROOT/upstream/bin
.
С этими аргументами для запуска ./configure
потребуется больше времени, но в конце концов вы получите желаемый результат.
emscripten sdl2-config called with /emsdk_portable/emscripten/tag-1.39.18/system/bin/sdl2-config --cflags emscripten sdl2-config called with /emsdk_portable/emscripten/tag-1.39.18/system/bin/sdl2-config --libs install prefix /usr/local source path . C compiler emcc # emcc as expected C library ARCH x86 (generic) big-endian no runtime cpu detection yes standalone assembly no
Добавьте emmake make -j4
(вы можете повысить параллелизм до -j8
или просто использовать -j
для использования всех ядер) в конце build.sh
:
И сразу после выполнения не работает:
... ./libavutil/x86/timer.h:39:24: error: invalid output constraint '=a' in asm : "=a" (a), "=d" (d)); ^
Из выходного сообщения мы можем определить, что ошибка связана с asm. Откройте ./libavutil/x86/timer.h
, и мы можем подтвердить, что проблема вызвана встроенной сборкой x86, которая несовместима с WebAssembly, поэтому решение состоит в том, чтобы отключить ее в build.sh
:
Он работает и продолжает компилироваться, пока мы не встретим еще одну ошибку:
... CC libavfilter/dnn/dnn_backend_native_layers.o In file included from libavfilter/aeval.c:26: In file included from ./libavutil/avassert.h:31: In file included from ./libavutil/avutil.h:296: In file included from ./libavutil/common.h:533: In file included from ./libavutil/internal.h:176: ./libavutil/libm.h:54:32: error: static declaration of 'cbrt' follows non-static declaration static av_always_inline double cbrt(double x) ^ /emsdk_portable/upstream/emscripten/system/include/libc/math.h:151:13: note: previous declaration is here double cbrt(double); ^ In file included from libavfilter/aeval.c:26:
На этот раз основная причина не так очевидна, поэтому нам нужно глубже копнуть в том, что пошло не так во время ./configure
. Очень полезно проверить файл ffbuild/config.log
, он содержит журналы за ./configure
, и в большинстве случаев вы можете найти там основную причину.
Выполнив поиск cbrt
внутри config.log
, мы находим сообщение об ошибке ниже:
... check_mathfunc cbrt 1 test_ld cc test_cc BEGIN /tmp/ffconf.syfN4Irw/test.c 1 #include <math.h> 2 float foo(float f, float g) { return cbrt(f); } 3 int main(void){ return (int) foo; } END /tmp/ffconf.syfN4Irw/test.c emcc -D_ISOC99_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_POSIX_C_SOURCE=200112 -D_XOPEN_SOURCE=600 -std=c11 -fomit-frame-pointer -pthread -c -o /tmp/ffconf.syfN4Irw/test.o /tmp/ffconf.syfN4Irw/test.c emcc -Wl,-z,noexecstack -o /tmp/ffconf.syfN4Irw/test /tmp/ffconf.syfN4Irw/test.o wasm-ld: error: 'atomics' feature is used by /tmp/ffconf.syfN4Irw/test.o, so --shared-memory must be used ...
Этот тест пытался проверить, работает ли cbrt в среде, но не удалось из-за ошибки atomics
функции. atomics
спрашивается, когда вы используете pthread
, поэтому давайте добавим pthread
флаги. (Проверьте ЗДЕСЬ, чтобы узнать больше о флагах pthread)
Обновить build.sh
:
Он работает и продолжает компилироваться, пока мы не встретим еще одну ошибку:
... LD ffplay_g emcc: warning: ignoring unsupported linker flag: `-rpath-link=:libpostproc:libswresample:libswscale:libavfilter:libavdevice:libavformat:libavcodec:libavutil:libavresample` [-Wlinkflags] 7 warnings generated. wasm-ld: error: initial memory too small, 19491744 bytes needed ... make: *** [Makefile:114: ffplay_g] Error 1 make: *** Waiting for unfinished jobs.... emcc: warning: ignoring unsupported linker flag: `-rpath-link=:libpostproc:libswresample:libswscale:libavfilter:libavdevice:libavformat:libavcodec :libavutil:libavresample` [-Wlinkflags] ...
На этот раз проблема вызвана тем, что исходная память слишком мала (по умолчанию только 16 МБ в Emscripten, а здесь минимальное значение составляет 19+ МБ), поэтому нам нужно увеличить начальную память до более высокого значения, передав -s INITIAL_MEMORY=33554432
(32 МБ) .
После этого исправления все еще возникает ошибка:
LD ffplay_g emcc: warning: ignoring unsupported linker flag: `-rpath-link=:libpostproc:libswresample:libswscale:libavfilter:libavdevice:libavformat:libavcodec:libavutil:libavresample` [-Wlinkflags] 6 warnings generated. LD ffmpeg_g emcc: warning: ignoring unsupported linker flag: `-rpath-link=:libpostproc:libswresample:libswscale:libavfilter:libavdevice:libavformat:libavcodec:libavutil:libavresample` [-Wlinkflags] 9 warnings generated. LD ffprobe_g emcc: warning: ignoring unsupported linker flag: `-rpath-link=:libpostproc:libswresample:libswscale:libavfilter:libavdevice:libavformat:libavcodec:libavutil:libavresample` [-Wlinkflags] STRIP ffmpeg strip:ffmpeg_g: file format not recognized make: *** [Makefile:107: ffmpeg] Error 1 make: *** Waiting for unfinished jobs....
Поскольку мы не можем разделить (это имеет смысл, поскольку это недопустимый двоичный формат), давайте просто отключим разделение с помощью --disable-stripping
и снова сделаем:
Наконец, нам удалось успешно выполнить часть emmake make -j
, и вы можете увидеть ffplay / ffplay_g, ffprobe / ffprobe_g и ffmpeg / ffmpeg_g, созданные в корневой папке. Выглядит идеально, но есть странный суффикс _g
, который делает выходные файлы такими:
- ffmpeg
- ffmpeg_g
- ffmpeg_g.wasm
- ffmpeg_g.worker.js
И ffmpeg, и ffmpeg_g здесь являются актуальными js-файлами, идеальное именование показано ниже:
- ffmpeg / ffmpeg_g = ›ffmpeg.js
- ffmpeg_g.wasm = ›ffmpeg.wasm
- ffmpeg_g.worker.js = ›ffmpeg.worker.js
Чтобы решить эту проблему, нам нужно создать ее самостоятельно. Команду для создания ffmpeg можно извлечь, запустив emmake make -n
:
... printf "LD\t%s\n" 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 "CP\t%s\n" ffmpeg; cp -p ffmpeg_g ffmpeg ...
С небольшой очисткой:
emcc \ -I. -I./fftools \ -Llibavcodec -Llibavdevice -Llibavfilter -Llibavformat -Llibavresample -Llibavutil -Llibpostproc -Llibswscale -Llibswresample \ -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
Поскольку мы создаем нашу собственную версию, давайте добавим --disable-programs
и --disable-doc
на этапе ./configure
, чтобы ускорить сборку, а также добавим некоторые важные флаги при сборке ffmpeg.
Давайте создадим basic.html, чтобы проверить, работает ли ffmpeg.wasm:
Запустите легкий веб-сервер (например, python3 -m http.server 3000
), посетите веб-страницу (например, http://localhost:3000/basic.html
) и откройте Chrome DevTools.
Это вроде как работает, так как вы можете видеть результат, аналогичный исходному FFmpeg, это дает нам хорошую отправную точку для полировки нашей библиотеки ffmpeg.wasm.
Вы можете посетить репозиторий здесь, чтобы подробнее узнать, как это работает: https://github.com/ffmpegwasm/FFmpeg/tree/n4.3.1-p2
И не стесняйтесь загружать артефакты сборки здесь: https://github.com/ffmpegwasm/FFmpeg/releases/tag/n4.3.1-p2
О том, как усовершенствовать и создать настоящую библиотеку ffmpeg.wasm, см. Сборка версии FFmpeg WebAssembly (= ffmpeg.wasm): Часть 3 ffmpeg.wasm v0.1 - Транскодирование avi в mp4 этой серии рассказов. 😃