Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/actions/build-macos-x86_64/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,14 @@ runs:
shell: bash

- name: Set up WebRTC cache
uses: actions/cache@v4
uses: actions/cache@v6
with:
path: ~/${{ env.WEBRTC_INSTALL_FOLDER }}
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: Set up Maven cache
uses: actions/cache@v4
uses: actions/cache@v6
with:
path: ~/.m2/repository
key: maven-${{ inputs.platform-name }}-${{ hashFiles('**/pom.xml') }}
Expand Down
6 changes: 3 additions & 3 deletions .github/actions/build/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ runs:
shell: bash

- name: Set up WebRTC cache
uses: actions/cache@v4
uses: actions/cache@v6
with:
path: ~/${{ env.WEBRTC_INSTALL_FOLDER }}
key: webrtc-${{ env.WEBRTC_CACHE_BRANCH }}-${{ inputs.platform-name }}-${{ hashFiles('webrtc-jni/src/main/cpp/dependencies/webrtc/CMakeLists.txt') }}-${{ hashFiles('webrtc-jni/pom.xml') }}
Expand All @@ -39,14 +39,14 @@ runs:
shell: bash

- name: Set up FFmpeg cache
uses: actions/cache@v4
uses: actions/cache@v6
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
uses: actions/cache@v6
with:
path: ~/.m2/repository
key: maven-${{ inputs.platform-name }}-${{ hashFiles('**/pom.xml') }}
Expand Down
14 changes: 9 additions & 5 deletions .github/actions/prepare-linux/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,16 @@ runs:
# 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
# Use a more stable version of Clang
# Chromium Clang to be used with the clang toolchain file. The JNI code
# compiles against the libc++ headers of the WebRTC build, which only
# support the Clang that WebRTC branch ships with, so this must be the
# package pinned by CLANG_REVISION and CLANG_SUB_REVISION in
# tools/clang/scripts/update.py of the webrtc.branch set in
# webrtc-jni/pom.xml. Update it whenever that branch changes.
CLANG_PACKAGE=clang-llvmorg-23-init-19482-g53d18800-1.tar.xz
sudo mkdir -p /opt/clang
wget https://commondatastorage.googleapis.com/chromium-browser-clang/Linux_x64/clang-llvmorg-20-init-9764-gb81d8e90-72.tar.xz
sudo tar -xvf clang-llvmorg-20-init-9764-gb81d8e90-72.tar.xz -C /opt/clang
wget https://commondatastorage.googleapis.com/chromium-browser-clang/Linux_x64/$CLANG_PACKAGE
sudo tar -xvf $CLANG_PACKAGE -C /opt/clang
shell: bash

- name: Install required packages for x86-64
Expand Down
2 changes: 1 addition & 1 deletion .github/actions/prepare-windows/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ runs:
steps:
- name: Set up Python 3.11
if: false
uses: actions/setup-python@v5
uses: actions/setup-python@v7
with:
python-version: "3.11"

Expand Down
6 changes: 3 additions & 3 deletions .github/actions/release-macos-x86_64/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@ runs:
shell: bash

- name: Set up WebRTC cache
uses: actions/cache@v4
uses: actions/cache@v6
with:
path: ~/${{ env.WEBRTC_INSTALL_FOLDER }}
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: Set up Maven cache
uses: actions/cache@v4
uses: actions/cache@v6
with:
path: ~/.m2/repository
key: maven-${{ inputs.platform-name }}-${{ hashFiles('**/pom.xml') }}
Expand All @@ -52,7 +52,7 @@ runs:
# deploy. The native library jar is handed to the aggregating publish-central
# job instead, which attaches every platform to a single deployment.
- name: Upload native library jar
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: natives-${{ inputs.platform-name }}
path: webrtc-jni/target/webrtc-java-*.jar
Expand Down
8 changes: 4 additions & 4 deletions .github/actions/release/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ runs:
shell: bash

- name: Set up WebRTC cache
uses: actions/cache@v4
uses: actions/cache@v6
with:
path: ~/${{ env.WEBRTC_INSTALL_FOLDER }}
key: webrtc-${{ env.WEBRTC_CACHE_BRANCH }}-${{ inputs.platform-name }}-${{ hashFiles('webrtc-jni/src/main/cpp/dependencies/webrtc/CMakeLists.txt') }}-${{ hashFiles('webrtc-jni/pom.xml') }}
Expand All @@ -30,14 +30,14 @@ runs:
shell: bash

- name: Set up FFmpeg cache
uses: actions/cache@v4
uses: actions/cache@v6
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
uses: actions/cache@v6
with:
path: ~/.m2/repository
key: maven-${{ inputs.platform-name }}-${{ hashFiles('**/pom.xml') }}
Expand Down Expand Up @@ -71,7 +71,7 @@ runs:
# deploy. The native library jar is handed to the aggregating publish-central
# job instead, which attaches every platform to a single deployment.
- name: Upload native library jar
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: natives-${{ inputs.platform-name }}
path: webrtc-jni/target/webrtc-java-*.jar
Expand Down
2 changes: 1 addition & 1 deletion .github/actions/test-natives/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ runs:
using: "composite"
steps:
- name: Download natives
uses: actions/download-artifact@v4
uses: actions/download-artifact@v8
with:
name: natives-${{ inputs.platform-name }}
path: natives
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ jobs:

- name: Upload natives for native testing
if: matrix.platform.name == 'windows_arm64'
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: natives-${{ matrix.platform.name }}
path: |
Expand Down Expand Up @@ -96,7 +96,7 @@ jobs:

- name: Upload natives for native testing
if: matrix.platform.name == 'linux_arm' || matrix.platform.name == 'linux_arm64'
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: natives-${{ matrix.platform.name }}
path: |
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
cache-dependency-path: docs/package-lock.json

- name: Setup Pages
uses: actions/configure-pages@v5
uses: actions/configure-pages@v6

- name: Install dependencies
run: |
Expand All @@ -45,7 +45,7 @@ jobs:
npm run build

- name: Upload artifact
uses: actions/upload-pages-artifact@v4
uses: actions/upload-pages-artifact@v5
with:
path: docs/.vitepress/dist

Expand All @@ -59,4 +59,4 @@ jobs:
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
uses: actions/deploy-pages@v5
6 changes: 3 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ jobs:
fetch-tags: true

- name: Set up Maven cache
uses: actions/cache@v4
uses: actions/cache@v6
with:
path: ~/.m2/repository
key: maven-publish-${{ hashFiles('**/pom.xml') }}
Expand Down Expand Up @@ -311,7 +311,7 @@ jobs:
echo "version=$version" >> "$GITHUB_OUTPUT"

- name: Download the native library jars of all platforms
uses: actions/download-artifact@v4
uses: actions/download-artifact@v8
with:
pattern: natives-*
path: natives
Expand Down Expand Up @@ -380,7 +380,7 @@ jobs:

- name: Upload the deployment bundle for inspection
if: ${{ inputs.dryRun }}
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: deployment-bundle-dry-run
path: |
Expand Down
1 change: 0 additions & 1 deletion webrtc-jni/src/main/cpp/include/JNI_RTCPeerConnection.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

41 changes: 40 additions & 1 deletion webrtc-jni/src/main/cpp/src/api/RTCDataChannelSendObserver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,44 @@
#include "JavaString.h"
#include "JNI_WebRTC.h"

namespace
{
// Hands an exception thrown by the observer to the calling thread's uncaught
// exception handler, as the JVM does for an exception escaping a Java thread.
// ExceptionDescribe would print the "Exception in thread" prefix straight to
// native stderr and the stack trace to System.err, splitting the report
// across two streams and leaving the prefix on an unterminated line.
void ReportUncaughtException(JNIEnv * env, jthrowable exception)
{
jni::JavaLocalRef<jclass> threadClass(env, env->FindClass("java/lang/Thread"));
jni::JavaLocalRef<jclass> handlerClass(env, env->FindClass("java/lang/Thread$UncaughtExceptionHandler"));
if (env->ExceptionCheck()) {
env->ExceptionClear();
return;
}

jmethodID currentThread = env->GetStaticMethodID(threadClass.get(), "currentThread", "()Ljava/lang/Thread;");
jmethodID getHandler = env->GetMethodID(threadClass.get(), "getUncaughtExceptionHandler",
"()Ljava/lang/Thread$UncaughtExceptionHandler;");
jmethodID uncaughtException = env->GetMethodID(handlerClass.get(), "uncaughtException",
"(Ljava/lang/Thread;Ljava/lang/Throwable;)V");
if (env->ExceptionCheck()) {
env->ExceptionClear();
return;
}

jni::JavaLocalRef<jobject> thread(env, env->CallStaticObjectMethod(threadClass.get(), currentThread));
jni::JavaLocalRef<jobject> handler(env, env->CallObjectMethod(thread.get(), getHandler));
if (!env->ExceptionCheck() && handler.get()) {
env->CallVoidMethod(handler.get(), uncaughtException, thread.get(), exception);
}
// Like the JVM, ignore an exception thrown by the handler itself.
if (env->ExceptionCheck()) {
env->ExceptionClear();
}
}
}

namespace jni
{
RTCDataChannelSendObserver::RTCDataChannelSendObserver(JNIEnv * env, jobject observer) :
Expand Down Expand Up @@ -58,8 +96,9 @@ namespace jni
}
// A Java exception must not escape into WebRTC's network task or a destructor.
if (env->ExceptionCheck()) {
env->ExceptionDescribe();
JavaLocalRef<jthrowable> exception(env, env->ExceptionOccurred());
env->ExceptionClear();
ReportUncaughtException(env, exception.get());
}
}

Expand Down
3 changes: 3 additions & 0 deletions webrtc/src/main/java/module-info.java
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// The qualified export below names webrtc.java.media, which depends on this
// module and so is never on the module path while this module compiles.
@SuppressWarnings("module")
module webrtc.java {

requires java.desktop;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ void rejectsInvalidArgumentsWithoutCallingObserver() {

@Test
void callbackExceptionDoesNotStopLaterSends() throws Exception {
try (TestDataChannelPair pair = new TestDataChannelPair(factory)) {
try (UncaughtCapture uncaught = new UncaughtCapture();
TestDataChannelPair pair = new TestDataChannelPair(factory)) {
pair.connect();
CountDownLatch entered = new CountDownLatch(1);
RTCDataChannelBuffer buffer = new RTCDataChannelBuffer(ByteBuffer.allocate(1), true);
Expand All @@ -80,12 +81,14 @@ public void onFailure(String error) {
pair.sender.sendAsync(buffer, next);
assertNull(next.result.get(5, TimeUnit.SECONDS));
assertEquals(1, next.calls.get());
uncaught.assertReceived("Send observer test exception");
}
}

@Test
void failureCallbackExceptionDoesNotStopLaterSends() throws Exception {
try (TestDataChannelPair pair = new TestDataChannelPair(factory)) {
try (UncaughtCapture uncaught = new UncaughtCapture();
TestDataChannelPair pair = new TestDataChannelPair(factory)) {
CountDownLatch entered = new CountDownLatch(1);
RTCDataChannelBuffer buffer = new RTCDataChannelBuffer(ByteBuffer.allocate(1), true);
pair.sender.sendAsync(buffer, new RTCDataChannelSendObserver() {
Expand All @@ -103,6 +106,7 @@ public void onFailure(String error) {
});
assertTrue(entered.await(5, TimeUnit.SECONDS), "Observer was not called");
assertRejected(pair.sender);
uncaught.assertReceived("Send failure observer test exception");
}
}

Expand Down Expand Up @@ -196,4 +200,28 @@ public void onFailure(String error) {
result.complete(error);
}
}

/**
* Replaces the default uncaught exception handler while open, so an exception
* an observer throws on purpose is captured and asserted instead of printed.
*/
private static class UncaughtCapture implements AutoCloseable {
final CompletableFuture<Throwable> exception = new CompletableFuture<>();
final Thread.UncaughtExceptionHandler previous = Thread.getDefaultUncaughtExceptionHandler();

UncaughtCapture() {
Thread.setDefaultUncaughtExceptionHandler((thread, e) -> exception.complete(e));
}

void assertReceived(String message) throws Exception {
Throwable received = exception.get(5, TimeUnit.SECONDS);
assertEquals(IllegalStateException.class, received.getClass());
assertEquals(message, received.getMessage());
}

@Override
public void close() {
Thread.setDefaultUncaughtExceptionHandler(previous);
}
}
}