diff --git a/.github/actions/build/action.yml b/.github/actions/build/action.yml
index a9f167c5..99ca8e90 100644
--- a/.github/actions/build/action.yml
+++ b/.github/actions/build/action.yml
@@ -34,6 +34,17 @@ runs:
key: webrtc-${{ env.WEBRTC_CACHE_BRANCH }}-${{ inputs.platform-name }}-${{ hashFiles('webrtc-jni/src/main/cpp/dependencies/webrtc/CMakeLists.txt') }}-${{ hashFiles('webrtc-jni/pom.xml') }}
restore-keys: webrtc-${{ env.WEBRTC_CACHE_BRANCH }}-${{ inputs.platform-name }}-${{ hashFiles('webrtc-jni/src/main/cpp/dependencies/webrtc/CMakeLists.txt') }}-
+ - name: Derive the FFmpeg version from the pom
+ run: echo "FFMPEG_VERSION=$(sed -n 's|.*\([^<]*\).*|\1|p' webrtc-java-media/pom.xml)" >> "$GITHUB_ENV"
+ shell: bash
+
+ - name: Set up FFmpeg cache
+ uses: actions/cache@v4
+ with:
+ path: ~/ffmpeg
+ key: ffmpeg-${{ env.FFMPEG_VERSION }}-${{ inputs.platform-name }}-${{ hashFiles('webrtc-java-media/src/main/cpp/dependencies/ffmpeg/CMakeLists.txt') }}-${{ hashFiles('webrtc-java-media/pom.xml') }}
+ restore-keys: ffmpeg-${{ env.FFMPEG_VERSION }}-${{ inputs.platform-name }}-${{ hashFiles('webrtc-java-media/src/main/cpp/dependencies/ffmpeg/CMakeLists.txt') }}-
+
- name: Set up Maven cache
uses: actions/cache@v4
with:
diff --git a/.github/actions/prepare-linux/action.yml b/.github/actions/prepare-linux/action.yml
index 6fbf9f30..23f07c40 100644
--- a/.github/actions/prepare-linux/action.yml
+++ b/.github/actions/prepare-linux/action.yml
@@ -31,7 +31,8 @@ runs:
- name: Install required packages
run: |
sudo apt update
- sudo apt install -y binutils cmake git locales lsb-release ninja-build pipewire pipewire-pulse pkg-config python3 python3-setuptools rsync unzip wget xz-utils
+ # nasm is what FFmpeg assembles its x86 code with.
+ sudo apt install -y binutils cmake git locales lsb-release nasm ninja-build pipewire pipewire-pulse pkg-config python3 python3-setuptools rsync unzip wget xz-utils
# Chromium Clang to be used with the clang toolchain file
#curl -s https://raw.githubusercontent.com/chromium/chromium/main/tools/clang/scripts/update.py | python3 - --output-dir=/opt/clang
diff --git a/.github/actions/prepare-macos/action.yml b/.github/actions/prepare-macos/action.yml
index 818e04ea..5ef69300 100644
--- a/.github/actions/prepare-macos/action.yml
+++ b/.github/actions/prepare-macos/action.yml
@@ -9,6 +9,9 @@ runs:
run: |
# Required on macos-14
brew install python-setuptools
+ # nasm is what FFmpeg assembles its x86 code with, which the Intel
+ # build needs even though the runner itself is Apple Silicon.
+ brew install nasm
shell: bash
- name: Select Xcode version
diff --git a/.github/actions/prepare-windows/action.yml b/.github/actions/prepare-windows/action.yml
index ff9b023a..dd0e96a5 100644
--- a/.github/actions/prepare-windows/action.yml
+++ b/.github/actions/prepare-windows/action.yml
@@ -38,3 +38,16 @@ runs:
run: |
choco install ninja
shell: powershell
+
+ - name: Install MSYS2 and the FFmpeg build tools
+ run: |
+ # FFmpeg's configure and makefiles are shell scripts, so building it
+ # needs a POSIX shell even with MSVC, and that shell has to carry make
+ # and nasm. Git for Windows' bash carries neither.
+ if (-not (Test-Path "C:\msys64\usr\bin\bash.exe")) {
+ choco install msys2 --no-progress -y
+ }
+ C:\msys64\usr\bin\bash.exe -lc "pacman -Sy --noconfirm && pacman -S --needed --noconfirm make nasm diffutils pkgconf"
+ # Where the media module's CMake looks for it.
+ "MSYS2_ROOT=C:\msys64" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
+ shell: powershell
diff --git a/.github/actions/release/action.yml b/.github/actions/release/action.yml
index fa20ed67..0c6fe4af 100644
--- a/.github/actions/release/action.yml
+++ b/.github/actions/release/action.yml
@@ -25,6 +25,17 @@ runs:
key: webrtc-${{ env.WEBRTC_CACHE_BRANCH }}-${{ inputs.platform-name }}-${{ hashFiles('webrtc-jni/src/main/cpp/dependencies/webrtc/CMakeLists.txt') }}-${{ hashFiles('webrtc-jni/pom.xml') }}
restore-keys: webrtc-${{ env.WEBRTC_CACHE_BRANCH }}-${{ inputs.platform-name }}-${{ hashFiles('webrtc-jni/src/main/cpp/dependencies/webrtc/CMakeLists.txt') }}-
+ - name: Derive the FFmpeg version from the pom
+ run: echo "FFMPEG_VERSION=$(sed -n 's|.*\([^<]*\).*|\1|p' webrtc-java-media/pom.xml)" >> "$GITHUB_ENV"
+ shell: bash
+
+ - name: Set up FFmpeg cache
+ uses: actions/cache@v4
+ with:
+ path: ~/ffmpeg
+ key: ffmpeg-${{ env.FFMPEG_VERSION }}-${{ inputs.platform-name }}-${{ hashFiles('webrtc-java-media/src/main/cpp/dependencies/ffmpeg/CMakeLists.txt') }}-${{ hashFiles('webrtc-java-media/pom.xml') }}
+ restore-keys: ffmpeg-${{ env.FFMPEG_VERSION }}-${{ inputs.platform-name }}-${{ hashFiles('webrtc-java-media/src/main/cpp/dependencies/ffmpeg/CMakeLists.txt') }}-
+
- name: Set up Maven cache
uses: actions/cache@v4
with:
diff --git a/.github/actions/test-natives/action.yml b/.github/actions/test-natives/action.yml
index 3866de00..02459615 100644
--- a/.github/actions/test-natives/action.yml
+++ b/.github/actions/test-natives/action.yml
@@ -76,13 +76,50 @@ runs:
- name: Install the natives into the local repository
run: |
VERSION=$(sed -n '0,//s|.*\([^<]*\).*|\1|p' pom.xml)
- JAR=$(ls natives/webrtc-java-*.jar | head -n 1)
+ JAR=$(find natives -path '*webrtc-jni*' -name 'webrtc-java-*.jar' | head -n 1)
echo "Installing $JAR as webrtc-java:$VERSION:${{ inputs.classifier }}"
mvn -B -q install:install-file -Dfile="$JAR" -DgroupId=dev.onvoid.webrtc \
-DartifactId=webrtc-java -Dversion="$VERSION" -Dpackaging=jar \
-Dclassifier=${{ inputs.classifier }}
shell: bash
+ # Both native libraries go into the directory the media module's tests
+ # already read from, which is where its own build collects them. That is
+ # what puts them on the class path, and it is also what makes the reactor
+ # below work: webrtc-java's natives are a classifier artifact of a module
+ # that is in that reactor, and Maven answers a dependency on such a thing
+ # with the module's output directory, which holds no native library at
+ # all. Coming from a directory instead, they are simply found.
+ - name: Unpack the natives where the tests look for them
+ run: |
+ MEDIA_JAR=$(find natives -name 'webrtc-java-media-*-${{ inputs.classifier }}.jar' | head -n 1)
+ WEBRTC_JAR=$(find natives -path '*webrtc-jni*' -name 'webrtc-java-*.jar' | head -n 1)
+
+ if [ -z "$MEDIA_JAR" ]; then
+ echo "No webrtc-java-media natives for ${{ inputs.classifier }} in the artifact" >&2
+ exit 1
+ fi
+
+ MEDIA_JAR="$PWD/$MEDIA_JAR"
+ WEBRTC_JAR="$PWD/$WEBRTC_JAR"
+
+ mkdir -p webrtc-java-media/target/natives
+ cd webrtc-java-media/target/natives
+ # jar rather than unzip: the JDK is set up above, and unzip is not on
+ # every runner image.
+ "$JAVA_HOME/bin/jar" xf "$MEDIA_JAR"
+ "$JAVA_HOME/bin/jar" xf "$WEBRTC_JAR"
+ rm -rf META-INF
+ ls -l
+ shell: bash
+
+ # One reactor for both, so that webrtc-java-media resolves webrtc-java
+ # from it. Testing rather than installing: webrtc attaches the host
+ # natives from webrtc-jni/target during its package phase, and on a runner
+ # that only downloaded them there is no such directory.
+ #
+ # The native build is off: these natives were cross compiled by another
+ # job, and this runner is here to run them, not to build them again.
- name: Test
- run: mvn -B -pl webrtc test -P${{ inputs.profile }}
+ run: mvn -B -pl webrtc,webrtc-java-media test -DskipNativeBuild -P${{ inputs.profile }}
shell: bash
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 5c63b2fe..4de02cd7 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -34,6 +34,9 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v6
+ with:
+ # The media module builds FFmpeg from its third-party submodule.
+ submodules: true
- id: prepare
name: Prepare build
@@ -53,7 +56,9 @@ jobs:
uses: actions/upload-artifact@v4
with:
name: natives-${{ matrix.platform.name }}
- path: webrtc-jni/target/webrtc-java-*.jar
+ path: |
+ webrtc-jni/target/webrtc-java-*.jar
+ webrtc-java-media/target/webrtc-java-media-*.jar
if-no-files-found: error
build-linux:
@@ -72,6 +77,9 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v6
+ with:
+ # The media module builds FFmpeg from its third-party submodule.
+ submodules: true
- id: prepare
name: Prepare build
@@ -91,7 +99,9 @@ jobs:
uses: actions/upload-artifact@v4
with:
name: natives-${{ matrix.platform.name }}
- path: webrtc-jni/target/webrtc-java-*.jar
+ path: |
+ webrtc-jni/target/webrtc-java-*.jar
+ webrtc-java-media/target/webrtc-java-media-*.jar
if-no-files-found: error
build-macos:
@@ -108,6 +118,9 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v6
+ with:
+ # The media module builds FFmpeg from its third-party submodule.
+ submodules: true
- id: prepare-build
name: Prepare build
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 0684e52f..33085969 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -148,6 +148,8 @@ jobs:
uses: actions/checkout@v6
with:
fetch-tags: true
+ # The media module builds FFmpeg from its third-party submodule.
+ submodules: true
- name: Get tag from current branch
id: tag-selector
@@ -189,6 +191,8 @@ jobs:
uses: actions/checkout@v6
with:
fetch-tags: true
+ # The media module builds FFmpeg from its third-party submodule.
+ submodules: true
- name: Get tag from current branch
id: tag-selector
@@ -227,6 +231,8 @@ jobs:
uses: actions/checkout@v6
with:
fetch-tags: true
+ # The media module builds FFmpeg from its third-party submodule.
+ submodules: true
- name: Get tag from current branch
id: tag-selector
diff --git a/docs/guide/examples.md b/docs/guide/examples.md
index ae9191cb..a39d646f 100644
--- a/docs/guide/examples.md
+++ b/docs/guide/examples.md
@@ -65,11 +65,11 @@ The [`MediaFileExample`](https://github.com/devopvoid/webrtc-java/blob/master/we
This example is useful for applications that stream pre-recorded media, or that need a dependable stand-in for a camera in testing.
-::: warning
-This example needs the `webrtc-java-media` module, which is opt-in while it is being brought up on every platform, so it is built and run with the `with-media-extension` profile:
+::: info
+This example needs the `webrtc-java-media` module, which builds FFmpeg from the `third-party/ffmpeg` submodule. Make sure the submodule is checked out before building, as the [Media Files](/guide/media/media-files) guide describes.
```bash
-mvn -Pwith-media-extension exec:java -D"exec.mainClass=dev.onvoid.webrtc.examples.MediaFileExample" -D"exec.args=movie.mp4"
+mvn exec:java -D"exec.mainClass=dev.onvoid.webrtc.examples.MediaFileExample" -D"exec.args=movie.mp4"
```
:::
diff --git a/docs/guide/media/media-files.md b/docs/guide/media/media-files.md
index 56874a1e..e727cde1 100644
--- a/docs/guide/media/media-files.md
+++ b/docs/guide/media/media-files.md
@@ -12,17 +12,13 @@ Sending a file is a common need: a test pattern instead of a webcam, a pre-recor
Decoding happens entirely in native code. Frames never travel through Java: the module hands decoded pictures straight to the native side of a `CustomVideoSource`, and 10 ms chunks to a `CustomAudioSource`. They are paced in real time and carry the presentation times of the file, so what a receiver gets keeps the timing of the media rather than the timing of a Java thread.
-::: warning Opt-in while it is being brought up
-The media module is not part of the default build yet, and its native library has so far been built for `windows-x86_64` only. Build it with the `with-media-extension` profile, as described below.
-:::
-
## Adding the Module
-The module builds FFmpeg from a submodule pinned to a release tag, so the submodule has to be present:
+The module is part of the normal build, and it builds FFmpeg from a submodule pinned to a release tag, so the submodule has to be present:
```shell
git submodule update --init --depth 1 webrtc-java-media/third-party/ffmpeg
-mvn install -Pwith-media-extension
+mvn install
```
Building FFmpeg needs `make` and `nasm`. On Windows they come from MSYS2:
@@ -34,7 +30,7 @@ C:\msys64\usr\bin\bash -lc "pacman -S --needed make nasm diffutils pkgconf"
Maven still runs from an ordinary shell; the build enters MSYS2 and the Visual Studio environment on its own. The first build compiles FFmpeg, which takes a while; later builds reuse the install directory.
-Once installed, depend on it alongside `webrtc-java`:
+Once installed, depend on it alongside `webrtc-java`. It takes two entries: one for the Java API, and one for the natives of the platform you are running on.
```xml
@@ -42,9 +38,19 @@ Once installed, depend on it alongside `webrtc-java`:
webrtc-java-media0.19.0-SNAPSHOT
+
+ dev.onvoid.webrtc
+ webrtc-java-media
+ 0.19.0-SNAPSHOT
+ windows-x86_64
+
```
-The classifier jar carries the module's native library together with the FFmpeg shared libraries it uses. Applications that do not use this module never download FFmpeg.
+The classifier jar carries the module's native library together with the FFmpeg shared libraries it uses, so applications that do not use this module never download FFmpeg. Replace the classifier with the platform you are building for: `windows-x86_64`, `windows-aarch64`, `linux-x86_64`, `linux-aarch64`, `linux-aarch32`, `macos-x86_64` or `macos-aarch64`.
+
+::: info
+Unlike `webrtc-java`, which brings its natives along by itself, this module cannot: the natives are built by the module rather than by a separate one, so a dependency on them would have nothing to resolve against on a first build. Asking for them explicitly is the price of that.
+:::
## Sending a File
@@ -228,5 +234,5 @@ The module uses FFmpeg under the LGPL version 2.1 or later. It is configured wit
See `MediaFileExample` in the `webrtc-examples` module, which opens a file, reports what it contains, creates tracks, adds them to a peer connection and follows playback to the end.
```shell
-mvn -Pwith-media-extension -pl webrtc-examples compile
+mvn -pl webrtc-examples compile
```
diff --git a/pom.xml b/pom.xml
index b8e6770a..90d3a69d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -52,6 +52,7 @@
webrtc-jniwebrtc
+ webrtc-java-mediawebrtc-examples
@@ -354,19 +355,6 @@
webrtc.macos.aarch64
-
-
- with-media-extension
-
- webrtc-java-media
-
-
diff --git a/webrtc-examples/pom.xml b/webrtc-examples/pom.xml
index cf9437d3..5f0118f9 100644
--- a/webrtc-examples/pom.xml
+++ b/webrtc-examples/pom.xml
@@ -21,6 +21,27 @@
+
+
+ org.codehaus.mojo
+ exec-maven-plugin
+ 3.5.0
+
+
+ ${project.basedir}/../webrtc-java-media/target/natives
+
+
+
+
org.apache.maven.pluginsmaven-compiler-plugin
@@ -65,6 +86,12 @@
${project.version}
+
+ ${project.groupId}
+ webrtc-java-media
+ ${project.version}
+
+
org.eclipse.jettyjetty-server
@@ -95,74 +122,4 @@
-
-
-
- with-media-extension
-
-
-
-
- org.apache.maven.plugins
- maven-compiler-plugin
-
-
- compile-media-example
- compile
-
- compile
-
-
- 17
-
- ${project.basedir}/src/media/java
-
- ${project.build.directory}/media-classes
-
-
-
-
-
-
-
- org.codehaus.mojo
- exec-maven-plugin
- 3.5.0
-
-
- ${project.build.directory}/media-classes
-
-
-
-
-
-
-
-
- ${project.groupId}
- webrtc-java-media
- ${project.version}
-
-
-
-
\ No newline at end of file
diff --git a/webrtc-examples/src/media/java/dev/onvoid/webrtc/examples/MediaFileExample.java b/webrtc-examples/src/main/java/dev/onvoid/webrtc/examples/MediaFileExample.java
similarity index 100%
rename from webrtc-examples/src/media/java/dev/onvoid/webrtc/examples/MediaFileExample.java
rename to webrtc-examples/src/main/java/dev/onvoid/webrtc/examples/MediaFileExample.java
diff --git a/webrtc-examples/src/main/java/module-info.java b/webrtc-examples/src/main/java/module-info.java
index b330984a..28989e1f 100644
--- a/webrtc-examples/src/main/java/module-info.java
+++ b/webrtc-examples/src/main/java/module-info.java
@@ -6,6 +6,7 @@
requires org.eclipse.jetty.server;
requires org.eclipse.jetty.websocket.server;
requires webrtc.java;
+ requires webrtc.java.media;
exports dev.onvoid.webrtc.examples.web.client;
exports dev.onvoid.webrtc.examples.web.server;
diff --git a/webrtc-java-media/README.md b/webrtc-java-media/README.md
index ef048c1c..47bbaff5 100644
--- a/webrtc-java-media/README.md
+++ b/webrtc-java-media/README.md
@@ -39,11 +39,12 @@ licensed under the Apache License 2.0 like the rest of webrtc-java.
## Building
-The submodule has to be present:
+This module is part of the normal build, and it builds FFmpeg from a submodule, so the submodule
+has to be present:
```shell
git submodule update --init --depth 1 webrtc-java-media/third-party/ffmpeg
-mvn install -Pwith-media-extension
+mvn install
```
The first build compiles FFmpeg, which takes a while; later builds reuse the install directory
diff --git a/webrtc-java-media/pom.xml b/webrtc-java-media/pom.xml
index 45c08edf..e348cad2 100644
--- a/webrtc-java-media/pom.xml
+++ b/webrtc-java-media/pom.xml
@@ -24,11 +24,31 @@
the WebRTC checkout is reused. -->
${user.home}/ffmpeg/${platform.classifier}Release
+
+ process-sources
+ compile
+
+
+ ${project.basedir}/../webrtc-jni/src/main/cpp/toolchain
+
+ ${user.home}/webrtc/build
@@ -63,6 +83,7 @@
cmake-generate
+ ${cmake.generate.phase}generate
@@ -73,6 +94,12 @@
+
+
@@ -90,6 +117,7 @@
cmake-compile
+ ${cmake.compile.phase}compile
@@ -130,25 +158,98 @@
+
dev.onvoid.webrtcwebrtc-java${project.version}
-
-
-
- ${project.groupId}
- ${project.artifactId}
- ${project.version}
- ${platform.classifier}
-
+
+
+ skip-native-build
+
+
+ skipNativeBuild
+
+
+
+ none
+ none
+
+
+
+
+
+ linux-x86_64
+
+
+ linux
+ amd64
+
+
+
+ ${jni.toolchain.dir}/x86_64-linux-clang.cmake
+
+
+
+ linux-aarch32
+
+
+ linux
+ aarch32
+
+
+
+ ${jni.toolchain.dir}/aarch32-linux-clang.cmake
+
+
+
+ linux-aarch64
+
+
+ linux
+ aarch64
+
+
+
+ ${jni.toolchain.dir}/aarch64-linux-clang.cmake
+
+
+
+ macos-aarch64
+
+
+ mac
+ aarch64
+
+
+
+
+
+ macos-cross-x86_64
+
+
@@ -190,6 +291,7 @@
-AARM64${cmake.build.type}
+ ${jni.toolchain.dir}/aarch64-windows-clang.cmake
diff --git a/webrtc-java-media/src/main/cpp/CMakeLists.txt b/webrtc-java-media/src/main/cpp/CMakeLists.txt
index c9a8c294..f338320c 100644
--- a/webrtc-java-media/src/main/cpp/CMakeLists.txt
+++ b/webrtc-java-media/src/main/cpp/CMakeLists.txt
@@ -1,6 +1,27 @@
cmake_minimum_required(VERSION 3.13)
+
+#
+# A cross build is driven by a toolchain file, the same ones webrtc-jni uses:
+# they are where this project says which compiler and which sysroot a target is
+# built with, and having two answers to that would only let them drift apart.
+# It has to be set before project(), which is why it arrives under a name of
+# its own rather than as CMAKE_TOOLCHAIN_FILE.
+#
+if(DEFINED MEDIA_TOOLCHAIN_FILE AND NOT MEDIA_TOOLCHAIN_FILE STREQUAL "")
+ message(STATUS "Toolchain file: ${MEDIA_TOOLCHAIN_FILE}")
+ set(CMAKE_TOOLCHAIN_FILE "${MEDIA_TOOLCHAIN_FILE}")
+endif()
+
project(webrtc-java-media CXX)
+# The toolchain files find a sysroot but leave it to be applied here, after
+# project(), because setting it earlier breaks CMake's own compiler checks.
+if(DEFINED DEFERRED_SYSROOT AND DEFERRED_SYSROOT)
+ message(STATUS "Sysroot: ${DEFERRED_SYSROOT}")
+ set(CMAKE_SYSROOT ${DEFERRED_SYSROOT})
+ set(CMAKE_FIND_ROOT_PATH ${DEFERRED_SYSROOT})
+endif()
+
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
@@ -38,6 +59,56 @@ target_include_directories(${PROJECT_NAME}
target_link_libraries(${PROJECT_NAME} PRIVATE ffmpeg)
if(LINUX)
+ #
+ # The toolchain file builds against the Chromium sysroot, so that this
+ # library needs no newer glibc than webrtc-java's does: a machine that can
+ # run one can run the other. It also compiles with -nostdinc++, because
+ # everything Linux in this project uses the libc++ that WebRTC bundles
+ # rather than whatever the build machine happens to have.
+ #
+ # That libc++ is installed beside WebRTC itself, so webrtc-jni has to have
+ # been built before this module on Linux. It is the one thing this module
+ # takes from that build, and it is a C++ standard library, not any part of
+ # WebRTC: no WebRTC symbol is linked here, and the two native libraries
+ # still meet only through the C function table.
+ #
+ set(LIBCPP_CONFIG_DIR "${WEBRTC_INSTALL_DIR}/include/third_party/libc++")
+ set(LIBCPP_INCLUDE_DIR "${LIBCPP_CONFIG_DIR}/include")
+ set(LIBCPP_ABI_INCLUDE_DIR "${WEBRTC_INSTALL_DIR}/include/third_party/libc++abi/include")
+
+ if(NOT EXISTS "${LIBCPP_INCLUDE_DIR}")
+ message(FATAL_ERROR
+ "No libc++ in ${LIBCPP_INCLUDE_DIR}. This module builds against the one WebRTC "
+ "bundles, so webrtc-jni has to be built first, or WEBRTC_INSTALL_DIR has to point "
+ "at where it installed WebRTC.")
+ endif()
+
+ # The first carries __config_site, which the headers in the second need.
+ target_include_directories(${PROJECT_NAME} PRIVATE "${LIBCPP_CONFIG_DIR}")
+ target_include_directories(${PROJECT_NAME} SYSTEM PRIVATE
+ "${LIBCPP_INCLUDE_DIR}" "${LIBCPP_ABI_INCLUDE_DIR}")
+
+ # These two belong with those include directories and have to be set here
+ # rather than left to the toolchain file, which only the x86_64 one does.
+ # webrtc-jni does not notice, because it links the webrtc target and picks
+ # them up from its PUBLIC flags; nothing carries them to this module.
+ #
+ # The bundled libc++ has wanted the hardening mode chosen at configuration
+ # time since branch-heads/7977, and without it every translation unit
+ # fails in __config before it reaches a line of ours.
+ target_compile_definitions(${PROJECT_NAME}
+ PRIVATE _LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_EXTENSIVE)
+
+ # Clang finds the GCC installation inside the cross sysroots and puts its
+ # libstdc++ headers on the implicit search path, where they collide with
+ # the bundled libc++. Excluding the implicit C++ directories leaves the
+ # bundled headers serving C++ on their own.
+ target_compile_options(${PROJECT_NAME} PRIVATE -nostdinc++)
+
+ target_link_directories(${PROJECT_NAME} PRIVATE "${WEBRTC_INSTALL_DIR}/lib")
+ target_link_libraries(${PROJECT_NAME} PRIVATE
+ -static-libgcc -stdlib=libc++ -lc++ -lc++abi)
+
target_link_libraries(${PROJECT_NAME} PRIVATE dl pthread)
endif()
diff --git a/webrtc-java-media/src/main/cpp/dependencies/ffmpeg/CMakeLists.txt b/webrtc-java-media/src/main/cpp/dependencies/ffmpeg/CMakeLists.txt
index 30fe26a8..2f4cf5e1 100644
--- a/webrtc-java-media/src/main/cpp/dependencies/ffmpeg/CMakeLists.txt
+++ b/webrtc-java-media/src/main/cpp/dependencies/ffmpeg/CMakeLists.txt
@@ -25,6 +25,11 @@ if(NOT DEFINED FFMPEG_INSTALL_DIR OR FFMPEG_INSTALL_DIR STREQUAL "")
message(FATAL_ERROR "FFMPEG_INSTALL_DIR is not set")
endif()
+# It arrives from Maven, where the user's home on Windows is a backslash
+# path. CMake reads a backslash as the start of an escape, so C:\Users\...
+# is not only the wrong separator, it is a parse error waiting to happen.
+file(TO_CMAKE_PATH "${FFMPEG_INSTALL_DIR}" FFMPEG_INSTALL_DIR)
+
file(MAKE_DIRECTORY "${FFMPEG_INSTALL_DIR}")
# The libraries this module needs, in dependency order. The order matters
@@ -158,13 +163,18 @@ else()
# configure and make are run by a POSIX shell, which on Windows means MSYS2
# and its /c/... form of a path rather than C:/...
- macro(to_shell_path PATH_IN RESULT)
- set(${RESULT} "${PATH_IN}")
+ # A function rather than a macro on purpose: a macro substitutes its
+ # arguments into the body as text, so a path still holding a backslash
+ # would be parsed as an escape sequence instead of passed as a value.
+ function(to_shell_path PATH_IN RESULT)
+ set(CONVERTED "${PATH_IN}")
if(WIN32)
- string(REGEX REPLACE "^([A-Za-z]):/" "/\\1/" ${RESULT} "${${RESULT}}")
+ string(REGEX REPLACE "^([A-Za-z]):/" "/\\1/" CONVERTED "${CONVERTED}")
endif()
- endmacro()
+
+ set(${RESULT} "${CONVERTED}" PARENT_SCOPE)
+ endfunction()
to_shell_path("${FFMPEG_INSTALL_DIR}" FFMPEG_INSTALL_DIR_SH)
to_shell_path("${FFMPEG_SOURCE_DIR}" FFMPEG_SOURCE_DIR_SH)
@@ -229,23 +239,134 @@ else()
--enable-protocol=pipe
)
+ #
+ # What configure has to be told about the target. It is all worked out from
+ # what the toolchain file already set, so a target CMake can build for
+ # needs no new case here.
+ #
+
+ # The architecture under the name FFmpeg knows it by.
+ function(ffmpeg_arch_name PROCESSOR RESULT)
+ set(NAME "")
+
+ if("${PROCESSOR}" MATCHES "^(x86_64|AMD64|amd64)$")
+ set(NAME x86_64)
+ elseif("${PROCESSOR}" MATCHES "^(aarch64|arm64|ARM64)$")
+ if(APPLE)
+ # Apple's clang knows this architecture as arm64 and rejects
+ # -arch aarch64 outright, which configure reports only as
+ # being unable to create an executable.
+ set(NAME arm64)
+ else()
+ set(NAME aarch64)
+ endif()
+ elseif("${PROCESSOR}" MATCHES "^(arm|armv7.*|armhf)$")
+ set(NAME arm)
+ endif()
+
+ set(${RESULT} "${NAME}" PARENT_SCOPE)
+ endfunction()
+
+ ffmpeg_arch_name("${CMAKE_SYSTEM_PROCESSOR}" FFMPEG_ARCH)
+ ffmpeg_arch_name("${CMAKE_HOST_SYSTEM_PROCESSOR}" FFMPEG_HOST_ARCH)
+
if(WIN32)
- # FFmpeg builds against the MSVC runtime through its own msvc
- # toolchain support, which keeps the DLLs free of an MSYS2 runtime
- # dependency. configure needs Windows-style output, so cl and link
- # must be on the PATH of the shell that runs it.
+ set(FFMPEG_TARGET_OS win64)
+
+ # FFmpeg builds against the MSVC runtime through its own msvc toolchain
+ # support, which keeps the DLLs free of an MSYS2 runtime dependency.
+ # configure needs Windows-style output, so cl and link must be on the
+ # PATH of the shell that runs it.
list(APPEND FFMPEG_CONFIGURE_ARGS --toolchain=msvc)
+ elseif(APPLE)
+ set(FFMPEG_TARGET_OS darwin)
+ else()
+ set(FFMPEG_TARGET_OS linux)
endif()
- if(CMAKE_SYSTEM_PROCESSOR MATCHES "^(aarch64|arm64|ARM64)$" AND WIN32)
- # No assembler for this target yet; correctness first, speed later.
- list(APPEND FFMPEG_CONFIGURE_ARGS --disable-asm --arch=arm64)
+ if(FFMPEG_ARCH)
+ list(APPEND FFMPEG_CONFIGURE_ARGS --arch=${FFMPEG_ARCH})
endif()
+ set(FFMPEG_EXTRA_CFLAGS "")
+ set(FFMPEG_EXTRA_LDFLAGS "")
+
if(APPLE AND CMAKE_OSX_ARCHITECTURES)
+ # On Apple the architecture is chosen by a compiler flag rather than by
+ # a different compiler, and the deployment target has to match what the
+ # rest of the build asks for or the linker complains about every object.
+ set(FFMPEG_EXTRA_CFLAGS "-arch ${CMAKE_OSX_ARCHITECTURES}")
+ set(FFMPEG_EXTRA_LDFLAGS "-arch ${CMAKE_OSX_ARCHITECTURES}")
+
+ if(CMAKE_OSX_DEPLOYMENT_TARGET)
+ set(FFMPEG_EXTRA_CFLAGS
+ "${FFMPEG_EXTRA_CFLAGS} -mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET}")
+ set(FFMPEG_EXTRA_LDFLAGS
+ "${FFMPEG_EXTRA_LDFLAGS} -mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET}")
+ endif()
+ elseif(NOT WIN32)
+ # Elsewhere the target is a compiler flag the toolchain file put into
+ # CMAKE_C_FLAGS, which is exactly what FFmpeg needs to be handed too.
+ set(FFMPEG_EXTRA_CFLAGS "${CMAKE_C_FLAGS}")
+ set(FFMPEG_EXTRA_LDFLAGS "${CMAKE_C_FLAGS}")
+ endif()
+
+ if(CMAKE_SYSROOT)
+ list(APPEND FFMPEG_CONFIGURE_ARGS --sysroot=${CMAKE_SYSROOT})
+ endif()
+
+ # CMake reports a cross build whenever a toolchain file names a system,
+ # even one that matches the host, and every toolchain file here does. What
+ # decides this for FFmpeg is narrower: whether it can run what it builds,
+ # which is a question about the architecture and the operating system.
+ set(FFMPEG_CROSS_COMPILING FALSE)
+
+ if(CMAKE_CROSSCOMPILING)
+ if(NOT CMAKE_SYSTEM_NAME STREQUAL CMAKE_HOST_SYSTEM_NAME
+ OR NOT FFMPEG_ARCH STREQUAL FFMPEG_HOST_ARCH)
+ set(FFMPEG_CROSS_COMPILING TRUE)
+ endif()
+ endif()
+
+ if(FFMPEG_CROSS_COMPILING)
list(APPEND FFMPEG_CONFIGURE_ARGS
- --extra-cflags=-arch\ ${CMAKE_OSX_ARCHITECTURES}
- --extra-ldflags=-arch\ ${CMAKE_OSX_ARCHITECTURES})
+ --enable-cross-compile
+ --target-os=${FFMPEG_TARGET_OS})
+
+ # make install strips what it installs, with whichever strip is on
+ # the PATH, and the host's cannot read a binary built for another
+ # architecture: "unable to recognise the format of the input file".
+ # Shipping unstripped costs a little size and nothing else.
+ list(APPEND FFMPEG_CONFIGURE_ARGS --disable-stripping)
+
+ # An assembler for a foreign architecture is a build prerequisite of
+ # its own, and this module is not fast enough to need one yet.
+ # Correctness on every platform first, speed where it is measured.
+ if(FFMPEG_ARCH STREQUAL "aarch64" OR FFMPEG_ARCH STREQUAL "arm")
+ list(APPEND FFMPEG_CONFIGURE_ARGS --disable-asm)
+ endif()
+ endif()
+
+ # The compiler CMake settled on, so that FFmpeg is built by the same one as
+ # the rest of this module rather than by whatever configure finds first.
+ #
+ # Not on Apple. There CMake names the compiler inside the toolchain, and
+ # calling that directly skips the /usr/bin/cc shim that runs xcrun to
+ # point SDKROOT at an SDK. Without one there is no libSystem to link
+ # against, which configure reports only as being unable to create an
+ # executable. Its own default, plain "cc", goes through the shim.
+ if(NOT WIN32 AND NOT APPLE AND CMAKE_C_COMPILER)
+ list(APPEND FFMPEG_CONFIGURE_ARGS --cc=${CMAKE_C_COMPILER})
+ endif()
+
+ # These carry spaces, so they are quoted here rather than in the script.
+ if(NOT FFMPEG_EXTRA_CFLAGS STREQUAL "")
+ string(STRIP "${FFMPEG_EXTRA_CFLAGS}" FFMPEG_EXTRA_CFLAGS)
+ list(APPEND FFMPEG_CONFIGURE_ARGS "--extra-cflags=\"${FFMPEG_EXTRA_CFLAGS}\"")
+ endif()
+ if(NOT FFMPEG_EXTRA_LDFLAGS STREQUAL "")
+ string(STRIP "${FFMPEG_EXTRA_LDFLAGS}" FFMPEG_EXTRA_LDFLAGS)
+ list(APPEND FFMPEG_CONFIGURE_ARGS "--extra-ldflags=\"${FFMPEG_EXTRA_LDFLAGS}\"")
endif()
# Build out of tree so the submodule working tree stays clean.
@@ -329,10 +450,29 @@ set MSYS2_PATH_TYPE=inherit
)
if(NOT BUILD_RESULT EQUAL 0)
+ # configure says almost nothing on the console and writes the actual
+ # reason into config.log, which on a build machine nobody can reach is
+ # the same as writing it nowhere. The tail of it goes to the console.
+ set(FFMPEG_CONFIG_LOG "${FFMPEG_BUILD_DIR}/ffbuild/config.log")
+
+ if(EXISTS "${FFMPEG_CONFIG_LOG}")
+ file(READ "${FFMPEG_CONFIG_LOG}" FFMPEG_CONFIG_LOG_TEXT)
+ string(LENGTH "${FFMPEG_CONFIG_LOG_TEXT}" FFMPEG_CONFIG_LOG_LENGTH)
+
+ if(FFMPEG_CONFIG_LOG_LENGTH GREATER 6000)
+ math(EXPR FFMPEG_CONFIG_LOG_OFFSET "${FFMPEG_CONFIG_LOG_LENGTH} - 6000")
+ string(SUBSTRING "${FFMPEG_CONFIG_LOG_TEXT}"
+ ${FFMPEG_CONFIG_LOG_OFFSET} -1 FFMPEG_CONFIG_LOG_TEXT)
+ endif()
+
+ message("---- tail of ${FFMPEG_CONFIG_LOG} ----")
+ message("${FFMPEG_CONFIG_LOG_TEXT}")
+ message("---- end of config.log ----")
+ endif()
+
message(FATAL_ERROR
"Building FFmpeg failed (${BUILD_RESULT}). The script that ran is "
- "${FFMPEG_BUILD_SCRIPT}; see ${FFMPEG_BUILD_DIR}/ffbuild/config.log for what "
- "configure could not find.")
+ "${FFMPEG_BUILD_SCRIPT}.")
endif()
ffmpeg_is_installed(FFMPEG_INSTALLED)