diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 00000000..657a13cc
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,89 @@
+name: Build and publish snapshots
+on:
+ push:
+ branches: [ javabuild ]
+
+jobs:
+ build:
+ strategy:
+ matrix:
+ os: [ ubuntu-latest, macos-latest ]
+ runs-on: ${{ matrix.os }}
+ steps:
+ - uses: actions/checkout@v2
+ with:
+ ref: javabuild
+ - name: Install latest bash on macOS
+ if: runner.os == 'macOS'
+ run: |
+ brew update
+ brew install bash
+ brew install coreutils
+ - name: Set environment variables
+ run: |
+ echo "OS=${RUNNER_OS,,}" >> $GITHUB_ENV
+ echo "ARCH=${RUNNER_ARCH,,}" >> $GITHUB_ENV
+ if [[ ${{ runner.os }} == "Linux" ]]
+ then
+ platform=unix
+ classifier=linux-x86_64
+ elif [[ ${{ runner.os }} == "macOS" ]]
+ then
+ platform=mac
+ if [[ ${{ runner.arch }} == "X64" ]]
+ then
+ classifier=darwin-x86_64
+ else
+ classifier=darwin-aarch64
+ fi
+ elif [[ ${{ runner.os }} == "Windows" ]]
+ then
+ platform=windows
+ classifier=win-x86_64
+ fi
+ echo "PLATFORM=$platform" >> $GITHUB_ENV
+ echo "CLASSIFIER=$classifier" >> $GITHUB_ENV
+ shell: bash
+ - name: Download OpenJDK 19
+ id: download
+ uses: oracle-actions/setup-java@v1
+ with:
+ website: jdk.java.net
+ release: 19
+ install: false
+ - name: Setup Java and Apache Maven
+ uses: actions/setup-java@v3
+ with:
+ distribution: jdkfile
+ jdkFile: ${{ steps.download.outputs.archive }}
+ java-version: ${{ steps.download.outputs.version }}
+ server-id: gluon-nexus
+ server-username: MAVEN_USERNAME
+ server-password: MAVEN_CENTRAL_TOKEN
+ - name: Checkout tools repo
+ run: |
+ echo "print env variables"
+ echo ${{ env.OS }}
+ echo ${{ env.ARCH }}
+ echo ${{ env.PLATFORM }}
+ echo ${{ env.CLASSIFIER }}
+ cd $GITHUB_WORKSPACE
+ git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
+ - name: Install rustup
+ run: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
+ - name: Download and extract JEXTRACT
+ run: |
+ download_url="https://download.java.net/java/early_access/jextract/2/openjdk-19-jextract+2-3_${{ env.OS }}-${{ env.ARCH }}_bin.tar.gz"
+ wget -q --show-progress -O $RUNNER_TEMP/jextract.tar.gz $download_url
+ tar -xvzf $RUNNER_TEMP/jextract.tar.gz -C $GITHUB_WORKSPACE
+ - name: Build
+ run: |
+ export PATH=$GITHUB_WORKSPACE/depot_tools/:$PATH
+ make java PLATFORM=${{ env.PLATFORM }} JEXTRACT=$GITHUB_WORKSPACE/jextract-19 JDK=$JAVA_HOME TARGET_ARCH=${{ env.ARCH }} MACOS_SDK_VERSION=12.1
+ - name: Deploy snapshot
+ run: |
+ cd src/java/tring
+ mvn -Dclassifier=${{ env.CLASSIFIER }} deploy
+ env:
+ MAVEN_USERNAME: ${{ secrets.GLUON_NEXUS_USERNAME }}
+ MAVEN_CENTRAL_TOKEN: ${{ secrets.GLUON_NEXUS_PASSWORD }}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 271f8a73..3736b890 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,3 +14,9 @@ src/jar.list
.project
.classpath
org.eclipse.buildship.core.prefs
+src/java/build
+src/java/tring/src/gen-sources
+src/java/tring/src/main/resources/libringrtc.dylib
+src/java/tring/target
+src/java/tringapi/target/
+src/rust/tringlib.h
diff --git a/Makefile b/Makefile
index 4f514df9..d489afa9 100644
--- a/Makefile
+++ b/Makefile
@@ -32,10 +32,11 @@ help:
$(Q) echo " ios -- download WebRTC and build for the iOS platform"
$(Q) echo " android -- download WebRTC and build for the Android platform"
$(Q) echo " electron -- build an Electron library"
+ $(Q) echo " java -- build a Java library"
$(Q) echo " cli -- build the test cli (1:1 calls)"
$(Q) echo " gctc -- build the test cli (group calls)"
$(Q) echo
- $(Q) echo "For the electon/cli/gctc builds, you can specify an optional platform"
+ $(Q) echo "For the electron/java/cli/gctc builds, you can specify an optional platform"
$(Q) echo "which will download WebRTC. For example:"
$(Q) echo " $ make electron PLATFORM=unix"
$(Q) echo
@@ -86,6 +87,14 @@ electron:
fi
$(Q) (cd src/node && yarn install && yarn build)
+java:
+ $(Q) if [ "$(PLATFORM)" != "" ] ; then \
+ echo "java: Preparing workspace for $(PLATFORM)" ; \
+ ./bin/prepare-workspace $(PLATFORM) ; \
+ fi
+ echo "java: Release build" ; \
+ ./bin/build-java -r
+
cli:
$(Q) if [ "$(PLATFORM)" != "" ] ; then \
echo "cli: Preparing workspace for $(PLATFORM)" ; \
@@ -119,6 +128,7 @@ clean:
$(Q) ./bin/build-gctc --clean
$(Q) ./bin/build-electron --clean
$(Q) ./bin/build-ios --clean
+ $(Q) ./bin/build-java --clean
$(Q) rm -rf ./src/webrtc/src/out
PHONY += distclean
diff --git a/bin/build-java b/bin/build-java
new file mode 100755
index 00000000..989f40a9
--- /dev/null
+++ b/bin/build-java
@@ -0,0 +1,163 @@
+#!/bin/sh
+
+#
+# Copyright 2019-2021 Signal Messenger, LLC
+# SPDX-License-Identifier: AGPL-3.0-only
+#
+
+set -e
+
+# shellcheck source=bin/env.sh
+. "$(dirname "$0")"/env.sh
+
+JEXTRACT=${JEXTRACT:-/opt/jextract-19}
+JDK=${JDK:-/opt/jdk-19}
+TARGET_ARCH=${TARGET_ARCH:-x64}
+
+# darwin only
+DEFAULT_MACOS_SDK_VERSION="12.3"
+MACOS_SDK_VERSION=${MACOS_SDK_VERSION:-$DEFAULT_MACOS_SDK_VERSION}
+MACOS_SDK_PATH="/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX${MACOS_SDK_VERSION}.sdk"
+
+usage()
+{
+ echo 'usage: build-java [-d|-r|-c]
+ where:
+ -r to create a release build
+ -d to create a debug build (which may fail to build!)
+ -c to clean the build artifacts'
+}
+
+clean()
+{
+ # Remove all possible artifact directories.
+ rm -rf ./src/rust/target/debug
+ rm -rf ./src/rust/target/release
+ rm -rf ./src/webrtc/src/out/debug
+ rm -rf ./src/webrtc/src/out/release
+ mvn -f ./src/java/tring clean
+}
+
+BUILD_TYPE=release
+
+while [ "$1" != "" ]; do
+ case $1 in
+ -d | --debug )
+ BUILD_TYPE=debug
+ ;;
+ -r | --release )
+ BUILD_TYPE=release
+ ;;
+ -c | --clean )
+ clean
+ exit
+ ;;
+ -h | --help )
+ usage
+ exit
+ ;;
+ * )
+ usage
+ exit 1
+ esac
+ shift
+done
+
+case "$TARGET_ARCH" in
+ "x64")
+ GN_ARCH=x64
+ CARGO_ARCH=x86_64
+ ;;
+ "ia32")
+ GN_ARCH=x86
+ CARGO_ARCH=i686
+ ;;
+ "arm64")
+ GN_ARCH=arm64
+ CARGO_ARCH=aarch64
+ ;;
+ *)
+ echo "Unsupported architecture"
+ exit 1
+ ;;
+esac
+
+hash rustup 2>/dev/null || { echo >&2 "Make sure you have rustup installed and properly configured! Aborting."; exit 1; }
+
+RUSTFLAGS_WIN=
+
+case "$(rustup show active-toolchain)" in
+ *"x86_64-apple-darwin"* | *"aarch64-apple-darwin"* )
+ DEFAULT_PLATFORM="darwin"
+ CARGO_TARGET="${CARGO_ARCH}-apple-darwin"
+ ;;
+ *"x86_64-pc-windows"* )
+ DEFAULT_PLATFORM="win32"
+ CARGO_TARGET="${CARGO_ARCH}-pc-windows-msvc"
+ # Static linking to prevent build errors on Windows ia32
+ RUSTFLAGS_WIN="-C target-feature=+crt-static"
+ ;;
+ *"x86_64-unknown-linux"* )
+ DEFAULT_PLATFORM="linux"
+ CARGO_TARGET="${CARGO_ARCH}-unknown-linux-gnu"
+ ;;
+ * )
+ printf "Unknown platform detected!\nPlease make sure you have installed a valid Rust toolchain via rustup! Aborting.\n"
+ exit 1
+esac
+
+echo "Building for platform ${DEFAULT_PLATFORM}, TARGET_ARCH=${TARGET_ARCH}, GN_ARCH=${GN_ARCH}, CARGO_TARGET=${CARGO_TARGET}", OUTPUT_DIR=${OUTPUT_DIR}
+
+export MACOSX_DEPLOYMENT_TARGET="10.10"
+
+# Build WebRTC.
+(
+ cd src/webrtc/src
+ WEBRTC_ARGS="target_cpu=\"${GN_ARCH}\" rtc_build_examples=false rtc_build_tools=false rtc_include_tests=false rtc_enable_protobuf=false rtc_use_x11=false rtc_enable_sctp=false rtc_libvpx_build_vp9=true rtc_include_ilbc=false"
+
+ if [ "${BUILD_TYPE}" = "debug" ]
+ then
+ gn gen -C "${OUTPUT_DIR}"/debug "--args=${WEBRTC_ARGS}"
+ ninja -C "${OUTPUT_DIR}"/debug
+ else
+ gn gen -C "${OUTPUT_DIR}"/release "--args=${WEBRTC_ARGS} is_debug=false"
+ ninja -C "${OUTPUT_DIR}"/release
+ fi
+)
+
+# Build and link the final RingRTC library.
+(
+ cd src/rust
+
+ if [ "${BUILD_TYPE}" = "debug" ]
+ then
+ RUSTFLAGS="${RUSTFLAGS_WIN}" OUTPUT_DIR="${OUTPUT_DIR}" cargo build --target ${CARGO_TARGET} --features java
+ else
+ OUTPUT_DIR="${OUTPUT_DIR}" cargo build --target ${CARGO_TARGET} --lib --features java --release
+ fi
+
+ mkdir -p ../java/tring/src/main/resources
+ if [ $DEFAULT_PLATFORM = "darwin" ]
+ then
+ mkdir -p ../java/build/darwin
+ cp -f target/${CARGO_TARGET}/${BUILD_TYPE}/libringrtc.dylib ../java/tring/src/main/resources/libringrtc.dylib
+ # cp -f target/${CARGO_TARGET}/${BUILD_TYPE}/libringrtc.dylib ../java/build/darwin/libringrtc-"${TARGET_ARCH}"
+ ${JEXTRACT}/bin/jextract -I${JDK}include -I${JDK}/include/darwin -I${MACOS_SDK_PATH}/usr/include --output ../java/tring/src/gen-sources/java --source -t io.privacyresearch.tring tringlib.h
+ elif [ $DEFAULT_PLATFORM = "win32" ]
+ then
+ mkdir -p ../java/build/win32
+ cp -f target/${CARGO_TARGET}/${BUILD_TYPE}/ringrtc.dll ../java/build/win32/libringrtc-"${TARGET_ARCH}".java
+ ${JEXTRACT}/bin/jextract -I${JDK}include -I${JDK}/include/win32 --output ../java/tring/src/main/java --source -t io.privacyresearch.tring tringlib.h
+ elif [ $DEFAULT_PLATFORM = "linux" ]
+ then
+ mkdir -p ../java/build/linux
+ cp -f target/${CARGO_TARGET}/${BUILD_TYPE}/libringrtc.so ../java/tring/src/main/resources/libringrtc.so
+ #cp -f target/${CARGO_TARGET}/${BUILD_TYPE}/libringrtc.so ../java/build/linux/libringrtc-"${TARGET_ARCH}".java
+ ${JEXTRACT}/bin/jextract -I${JDK}include -I${JDK}/include/linux --output ../java/tring/src/main/java --source -t io.privacyresearch.tring tringlib.h
+ fi
+ cd ../java/tringapi
+ mvn clean install
+ cd ../tring
+ echo "mvn -Dclassifier=$DEFAULT_PLATFORM-$CARGO_ARCH clean install"
+ mvn -Dclassifier=$DEFAULT_PLATFORM-$CARGO_ARCH clean install
+)
diff --git a/src/java/README.md b/src/java/README.md
new file mode 100644
index 00000000..18ee0c7e
--- /dev/null
+++ b/src/java/README.md
@@ -0,0 +1,14 @@
+The Java implementation consists of 2 Java projects, and a Rust component.
+
+tringapi: this contains the API that can be accessed by the application,
+without any dependencies (no application model classes or ringrtc structs).
+It uses the Java ServiceLoader concept to find implementations of the
+TringService.
+
+tring: this contains platform-specific implementations of the TringService.
+The source code is mainly auto-generated by jextract. The implementation
+itself is in TringServiceImpl, and it is made available via module-info.java
+
+Rust component:
+java.rs contains the implementation of methods invoked by TringServiceImpl.
+It is similar to electron.js
diff --git a/src/java/tring/pom.xml b/src/java/tring/pom.xml
new file mode 100644
index 00000000..9de8632d
--- /dev/null
+++ b/src/java/tring/pom.xml
@@ -0,0 +1,88 @@
+
+
+ 4.0.0
+ io.privacyresearch
+ tring
+ 0.0.12-SNAPSHOT
+ jar
+
+ UTF-8
+ 19
+ 19
+
+
+
+ io.privacyresearch
+ tringapi
+ 0.0.9-SNAPSHOT
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.8.1
+
+
+ --enable-preview
+
+
+
+
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+ 3.3.0
+
+
+ generate-sources
+
+ add-source
+
+
+
+ src/gen-sources
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 3.0.0-M5
+
+ false
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 3.2.2
+
+
+ classifier-build
+
+ jar
+
+
+ ${classifier}
+
+
+
+
+
+
+
+
+ gluon-nexus
+ https://nexus.gluonhq.com/nexus/content/repositories/releases
+
+
+ gluon-nexus
+ https://nexus.gluonhq.com/nexus/content/repositories/public-snapshots
+
+
+
+
diff --git a/src/java/tring/src/main/java/io/privacyresearch/tring/NativeLibLoader.java b/src/java/tring/src/main/java/io/privacyresearch/tring/NativeLibLoader.java
new file mode 100644
index 00000000..9c81b3e1
--- /dev/null
+++ b/src/java/tring/src/main/java/io/privacyresearch/tring/NativeLibLoader.java
@@ -0,0 +1,20 @@
+package io.privacyresearch.tring;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+
+public class NativeLibLoader {
+
+ public static String loadLibrary() throws IOException {
+ String libName = "/"+System.mapLibraryName("ringrtc");
+ System.err.println("Will try to load "+libName);
+ InputStream is = NativeLibLoader.class.getResourceAsStream(libName);
+ Path target = Files.createTempFile("", "");
+ Files.copy(is, target, StandardCopyOption.REPLACE_EXISTING);
+ System.load(target.toString());
+ return libName;
+ }
+}
diff --git a/src/java/tring/src/main/java/io/privacyresearch/tring/OldTringApi.java b/src/java/tring/src/main/java/io/privacyresearch/tring/OldTringApi.java
new file mode 100644
index 00000000..2d8cc37b
--- /dev/null
+++ b/src/java/tring/src/main/java/io/privacyresearch/tring/OldTringApi.java
@@ -0,0 +1,18 @@
+package io.privacyresearch.tring;
+
+import java.util.List;
+
+/**
+ *
+ * @author johan
+ */
+@Deprecated
+public interface OldTringApi {
+
+ void statusCallback(long callId, long peerId, int dir, int type);
+
+ void answerCallback(byte[] opaque);
+
+ void iceUpdateCallback(List iceCandidates);
+
+}
diff --git a/src/java/tring/src/main/java/io/privacyresearch/tring/OldTringBridge.java b/src/java/tring/src/main/java/io/privacyresearch/tring/OldTringBridge.java
new file mode 100644
index 00000000..7cbc3c98
--- /dev/null
+++ b/src/java/tring/src/main/java/io/privacyresearch/tring/OldTringBridge.java
@@ -0,0 +1,44 @@
+package io.privacyresearch.tring;
+
+//import io.privacyresearch.tringapi.TringApi;
+//import io.privacyresearch.tringapi.TringService;
+//
+//import java.util.List;
+//import java.util.Optional;
+//import java.util.ServiceLoader;
+
+/**
+ *
+ * @author johan
+ */
+@Deprecated
+public class OldTringBridge {
+ // ready to be removed in next commit
+//
+// private TringService service;
+//
+// public OldTringBridge(TringApi api) {
+// ServiceLoader loader = ServiceLoader.load(TringService.class);
+// Optional serviceOpt = loader.findFirst();
+// this.service = serviceOpt.get();
+// this.service.setApi(api);
+// }
+//
+// public void acceptCall() {
+// service.acceptCall();
+// }
+//
+// public void proceed(long callId) {
+// service.proceed(callId);
+// }
+//
+// public void receivedIce(long callId, int senderDeviceId, List ice) {
+// receivedIce(callId, senderDeviceId, ice);
+// }
+//
+// public void receivedOffer(String peerId, long callId, int senderDeviceId, int receiverDeviceId,
+// byte[] senderKey, byte[] receiverKey, byte[] opaque) {
+// receivedOffer(peerId, callId, senderDeviceId, receiverDeviceId, senderKey, receiverKey, opaque);
+// }
+
+}
diff --git a/src/java/tring/src/main/java/io/privacyresearch/tring/TringServiceImpl.java b/src/java/tring/src/main/java/io/privacyresearch/tring/TringServiceImpl.java
new file mode 100644
index 00000000..22b92687
--- /dev/null
+++ b/src/java/tring/src/main/java/io/privacyresearch/tring/TringServiceImpl.java
@@ -0,0 +1,459 @@
+package io.privacyresearch.tring;
+
+import io.privacyresearch.tringapi.TringFrame;
+import io.privacyresearch.tringapi.TringService;
+import java.lang.foreign.Addressable;
+import java.lang.foreign.MemoryAddress;
+import java.lang.foreign.MemorySegment;
+import java.lang.foreign.MemorySession;
+import java.lang.foreign.ValueLayout;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class TringServiceImpl implements TringService {
+
+ static final int BANDWIDTH_QUALITY_HIGH = 2;
+ private static final TringService instance = new TringServiceImpl();
+ private static boolean nativeSupport = false;
+ private static long nativeVersion = 0;
+
+ private MemorySession scope;
+ private long callEndpoint;
+ private io.privacyresearch.tringapi.TringApi api;
+ private long activeCallId;
+ static String libName = "unknown";
+ BlockingQueue frameQueue = new LinkedBlockingQueue();
+
+ private static final Logger LOG = Logger.getLogger(TringServiceImpl.class.getName());
+
+ static {
+ try {
+ libName = NativeLibLoader.loadLibrary();
+ nativeSupport = true;
+ nativeVersion = tringlib_h.getVersion();
+
+ } catch (Throwable ex) {
+ System.err.println("No native RingRTC support: ");
+ ex.printStackTrace();
+ }
+ }
+
+
+ public TringServiceImpl() {
+ // no-op
+ }
+
+ public static TringService provider() {
+ return instance;
+ }
+
+ public String getVersionInfo() {
+ return "TringServiceImpl using "+libName;
+ }
+
+ public static long getNativeVersion() {
+ return nativeVersion;
+ }
+
+ @Override
+ public void setApi(io.privacyresearch.tringapi.TringApi api) {
+ this.api = api;
+ initiate();
+ }
+
+ private void initiate() {
+ scope = MemorySession.openShared();
+ tringlib_h.initRingRTC(toJString(scope, "Hello from Java"));
+ this.callEndpoint = tringlib_h.createCallEndpoint(createStatusCallback(),
+ createAnswerCallback(), createOfferCallback(),
+ createIceUpdateCallback(),
+ createGenericCallback(),
+ createVideoFrameCallback());
+
+ }
+
+ private void processAudioInputs() {
+ LOG.warning("Process Audio Inputs asked, not supported!");
+ MemorySegment audioInputs = tringlib_h.getAudioInputs(scope, callEndpoint,0);
+ MemorySegment name = TringDevice.name$slice(audioInputs);
+ int namelen = (int)RString.len$get(name);
+ MemoryAddress namebuff = RString.buff$get(name);
+ MemorySegment ofAddress = MemorySegment.ofAddress(namebuff, namelen, scope);
+ ByteBuffer bb = ofAddress.asByteBuffer();
+ byte[] bname = new byte[namelen];
+ bb.get(bname, 0, (int)namelen);
+ String myname = new String(bname);
+ }
+
+ @Override
+ public void receivedOffer(String peerId, long callId, int senderDeviceId, int receiverDeviceId,
+ byte[] senderKey, byte[] receiverKey, byte[] opaque) {
+ int mediaType = 0;
+ long ageSec = 0;
+ this.activeCallId = callId;
+ LOG.info("Pass received offer to tringlib");
+ tringlib_h.receivedOffer(callEndpoint, toJString(scope, peerId), callId, mediaType, senderDeviceId,
+ receiverDeviceId, toJByteArray(scope, senderKey), toJByteArray(scope, receiverKey),
+ toJByteArray(scope, opaque),
+ ageSec);
+ }
+
+ @Override
+ public void receivedOpaqueMessage(byte[] senderUuid, int senderDeviceId,
+ int localDeviceId, byte[] opaque, long age) {
+ tringlib_h.receivedOpaqueMessage(callEndpoint,
+ toJByteArray(scope, senderUuid),
+ senderDeviceId,
+ localDeviceId,
+ toJByteArray(scope, opaque),
+ age);
+ }
+
+ @Override
+ public void receivedAnswer(String peerId, long callId, int senderDeviceId,
+ byte[] senderKey, byte[] receiverKey, byte[] opaque) {
+ int mediaType = 0;
+ long ageSec = 0;
+ this.activeCallId = callId;
+ LOG.info("Pass received answer to tringlib");
+ tringlib_h.receivedAnswer(callEndpoint, toJString(scope, peerId), callId, senderDeviceId,
+ toJByteArray(scope, senderKey), toJByteArray(scope, receiverKey),
+ toJByteArray(scope, opaque));
+ }
+
+ public void setSelfUuid(String uuid) {
+ tringlib_h.setSelfUuid(callEndpoint, toJString(scope, uuid));
+ }
+
+ @Override
+ public void proceed(long callId, String iceUser, String icePwd, List ice) {
+ MemorySegment icePack = toJByteArray2D(scope, ice);
+ tringlib_h.setOutgoingAudioEnabled(callEndpoint, true);
+ tringlib_h.proceedCall(callEndpoint, callId, BANDWIDTH_QUALITY_HIGH, 0,
+ toJString(scope, iceUser), toJString(scope, icePwd), icePack);
+ }
+
+ @Override
+ public void receivedIce(long callId, int senderDeviceId, List ice) {
+ MemorySegment icePack = toJByteArray2D(scope, ice);
+ tringlib_h.receivedIce(callEndpoint, callId, senderDeviceId, icePack);
+ }
+
+ @Override
+ public void acceptCall() {
+ LOG.info("Set audioInput to 0");
+ tringlib_h.setAudioInput(callEndpoint, (short)0);
+ LOG.info("Set audiorecording");
+ tringlib_h.setOutgoingAudioEnabled(callEndpoint, true);
+ LOG.info("And now accept the call");
+ tringlib_h.acceptCall(callEndpoint, activeCallId);
+ LOG.info("Accepted the call");
+ }
+
+ @Override
+ public void ignoreCall() {
+ LOG.info("Ignore the call");
+ tringlib_h.ignoreCall(callEndpoint, activeCallId);
+ }
+
+ @Override
+ public void hangupCall() {
+ LOG.info("Hangup the call");
+ tringlib_h.hangupCall(callEndpoint);
+ }
+
+ /**
+ * start a call and return the call_id
+ * @return the same call_id as the one we were passed, if success
+ */
+ @Override
+ public long startOutgoingCall(long callId, String peerId, int localDeviceId, boolean enableVideo) {
+ LOG.info("Tring will start outgoing call to "+peerId+" with localDevice "+localDeviceId+" and enableVideo = "+enableVideo);
+ tringlib_h.setAudioInput(callEndpoint, (short)0);
+ tringlib_h.setAudioOutput(callEndpoint, (short)0);
+ tringlib_h.createOutgoingCall(callEndpoint, toJString(scope, peerId), enableVideo, localDeviceId, callId);
+ return callId;
+ }
+
+ @Override
+ public void peekGroupCall(byte[] membershipProof) {
+ tringlib_h.peekGroupCall(callEndpoint, toJByteArray(scope, membershipProof));
+ }
+
+ // for testing only
+ public void setArray() {
+ LOG.info("SET ARRAY");
+ int CAP = 1000000;
+ for (int i = 0; i < 1000; i++) {
+ try (MemorySession rscope = MemorySession.openConfined()) {
+ MemorySegment segment = MemorySegment.allocateNative(CAP, scope);
+ tringlib_h.fillLargeArray(123, segment);
+ ByteBuffer bb = segment.asByteBuffer();
+ byte[] bar = new byte[CAP];
+ bb.get(bar, 0, CAP);
+ LOG.info("Got Array " + i + " sized " + bar.length);
+ }
+ }
+ LOG.info("DONE");
+ }
+
+ @Override
+ public TringFrame getRemoteVideoFrame(boolean skip) {
+ int CAP = 5000000;
+ try (MemorySession rscope = MemorySession.openShared()) {
+ MemorySegment segment = MemorySegment.allocateNative(CAP, rscope);
+ long res = tringlib_h.fillRemoteVideoFrame(callEndpoint, segment, CAP);
+ if (res != 0) {
+ int w = (int) (res >> 16);
+ int h = (int) (res % (1 <<16));
+ byte[] raw = new byte[w * h * 4];
+ ByteBuffer bb = segment.asByteBuffer();
+ bb.get(raw);
+ TringFrame answer = new TringFrame(w, h, -1, raw);
+ return answer;
+ }
+ } catch (Throwable t) {
+ t.printStackTrace();
+ }
+ return null;
+ }
+
+ @Override
+ public void enableOutgoingVideo(boolean enable) {
+ tringlib_h.setOutgoingVideoEnabled(callEndpoint, enable);
+ }
+
+ @Override
+ public void sendVideoFrame(int w, int h, int pixelFormat, byte[] raw) {
+ try ( MemorySession session = MemorySession.openConfined()) {
+ MemorySegment buff = session.allocateArray(ValueLayout.JAVA_BYTE, raw);
+ tringlib_h.sendVideoFrame(callEndpoint, w, h, pixelFormat, buff);
+ }
+ }
+
+ static MemorySegment toJByteArray2D(MemorySession ms, List rows) {
+ MemorySegment answer = JByteArray2D.allocate(ms);
+ JByteArray2D.len$set(answer, rows.size());
+ MemorySegment rowsSegment = JByteArray2D.buff$slice(answer);
+ for (int i = 0; i < rows.size(); i++) {
+ MemorySegment singleRowSegment = toJByteArray(ms, rows.get(i));
+ MemorySegment row = rowsSegment.asSlice(16 * i, 16);
+ row.copyFrom(singleRowSegment);
+ }
+ return answer;
+ }
+
+ static MemorySegment toJByteArray(MemorySession ms, byte[] bytes) {
+ MemorySegment answer = JByteArray.allocate(ms);
+ JByteArray.len$set(answer, bytes.length);
+ MemorySegment byteBuffer = MemorySegment.allocateNative(bytes.length, ms);
+ MemorySegment.copy(bytes, 0, byteBuffer, ValueLayout.JAVA_BYTE, 0, bytes.length);
+ JByteArray.buff$set(answer, byteBuffer.address());
+ return answer;
+ }
+
+ static byte[] fromJArrayByte(MemorySession ms, MemorySegment jArrayByte) {
+ int len = (int)JArrayByte.len$get(jArrayByte);
+ MemorySegment dataSegment = JArrayByte.data$slice(jArrayByte).asSlice(0, len);
+ byte[] destArr = new byte[len];
+ MemorySegment dstSeq = MemorySegment.ofArray(destArr);
+ dstSeq.copyFrom(dataSegment);
+ return destArr;
+ }
+
+ static byte[] fromJByteArray(MemorySession ms, MemorySegment jByteArray) {
+ long len = JByteArray.len$get(jByteArray);
+ System.err.println("Need to read byte array with "+len+" bytes");
+ for (int j = 0; j < 16; j++) {
+ byte b = jByteArray.get(ValueLayout.JAVA_BYTE, j);
+ System.err.println("b["+j+"] = "+b);
+ }
+ //VarHandle buffHandle = JByteArray.$struct$LAYOUT.varHandle(long.class, MemoryLayout.PathElement.groupElement("buff"));
+
+ MemoryAddress pointer = JByteArray.buff$get(jByteArray);
+ System.err.println("pointer at "+ pointer.address());
+MemorySegment segment = MemorySegment.ofAddress(pointer, len, ms);
+byte[] destArr = new byte[(int)len];
+ MemorySegment dstSeq = MemorySegment.ofArray(destArr);
+ dstSeq.copyFrom(segment);
+ System.err.println("After copy, destArr = "+java.util.Arrays.toString(destArr));
+
+
+
+ for (int j = 0; j < len; j++) {
+ byte b = segment.get(ValueLayout.JAVA_BYTE, j);
+ System.err.println("Bb[" + j + "] = " + b);
+
+ }
+
+
+ // MemoryAddress pointer = ptr.get(ValueLayout.ADDRESS, 0);
+ System.err.println("ptr = "+pointer+", val = " + pointer.toRawLongValue());
+ System.err.println("ptr address = "+pointer.address());
+ byte[] data = new byte[(int)len];
+ for (int i =0; i < data.length; i++) {
+ data[i] = pointer.get(ValueLayout.JAVA_BYTE, i);
+ }
+ System.err.println("got data: "+java.util.Arrays.toString(data));
+ byte p0 = pointer.address().get(ValueLayout.JAVA_BYTE, 0);
+ byte p1 = pointer.address().get(ValueLayout.JAVA_BYTE, 1);
+ byte p8 = pointer.address().get(ValueLayout.JAVA_BYTE, 8);
+ System.err.println("p0 = "+p0+", p1 = "+p1+", p8 = "+p8);
+// MemorySegment byteSegment = JByteArray.ofAddress(pointer, ms);
+// byte[] data = byteSegment.toArray(ValueLayout.JAVA_BYTE);
+ return data;
+ }
+
+ static MemorySegment toJString(MemorySession ms, String src) {
+ MemorySegment answer = JString.allocate(ms);
+ byte[] bytes = src.getBytes();
+ JString.len$set(answer, bytes.length);
+ MemorySegment byteBuffer = MemorySegment.allocateNative(bytes.length, ms);
+ MemorySegment.copy(bytes, 0, byteBuffer, ValueLayout.JAVA_BYTE, 0, bytes.length);
+ JString.buff$set(answer, byteBuffer.address());
+ return answer;
+ }
+
+ Addressable createStatusCallback() {
+ StatusCallbackImpl sci = new StatusCallbackImpl();
+ MemorySegment seg = createCallEndpoint$statusCallback.allocate(sci, scope);
+ return seg.address();
+ }
+
+
+ class StatusCallbackImpl implements createCallEndpoint$statusCallback {
+ @Override
+ public void apply(long id, long _x1, int direction, int type) {
+ LOG.info("Got new status from ringrtc, id = " + id+", x1 = " + _x1+", dir = " + direction+", type = "+type);
+ api.statusCallback(id, _x1, direction, type);
+ sendAck();
+ }
+ }
+
+ Addressable createAnswerCallback() {
+ AnswerCallbackImpl sci = new AnswerCallbackImpl();
+ MemorySegment seg = createCallEndpoint$answerCallback.allocate(sci, scope);
+ return seg.address();
+ }
+
+ class AnswerCallbackImpl implements createCallEndpoint$answerCallback {
+ @Override
+ public void apply(MemorySegment opaque) {
+ System.err.println("TRINGBRIDGE, send answer!");
+ byte[] bytes = fromJArrayByte(scope, opaque);
+ System.err.println("TRING, bytes to send = "+java.util.Arrays.toString(bytes));
+ api.answerCallback(bytes);
+ System.err.println("TRING, answer sent");
+ sendAck();
+ System.err.println("TRING, ack sent");
+ }
+ }
+
+ Addressable createOfferCallback() {
+ OfferCallbackImpl sci = new OfferCallbackImpl();
+ MemorySegment seg = createCallEndpoint$offerCallback.allocate(sci, scope);
+ return seg.address();
+ }
+
+ class OfferCallbackImpl implements createCallEndpoint$offerCallback {
+ @Override
+ public void apply(MemorySegment opaque) {
+ byte[] bytes = fromJArrayByte(scope, opaque);
+ api.offerCallback(bytes);
+ System.err.println("TRING, offer sent");
+ sendAck();
+ System.err.println("TRING, ack sent");
+ }
+ }
+
+ Addressable createIceUpdateCallback() {
+ IceUpdateCallbackImpl sci = new IceUpdateCallbackImpl();
+ MemorySegment seg = createCallEndpoint$iceUpdateCallback.allocate(sci, scope);
+ return seg.address();
+ }
+
+ class IceUpdateCallbackImpl implements createCallEndpoint$iceUpdateCallback {
+
+ @Override
+ public void apply(MemorySegment icePack) {
+ byte[] bytes = fromJArrayByte(scope, icePack);
+ List iceCandidates = new ArrayList<>();
+ iceCandidates.add(bytes);
+
+ api.iceUpdateCallback(iceCandidates);
+ sendAck();
+ LOG.info("iceUpdate done!");
+ }
+ }
+ MemorySegment createGenericCallback() {
+ GenericCallbackImpl sci = new GenericCallbackImpl();
+ MemorySegment seg = createCallEndpoint$genericCallback.allocate(sci, scope);
+ return seg;
+ }
+
+ class GenericCallbackImpl implements createCallEndpoint$genericCallback {
+
+ @Override
+ public void apply(int opcode, MemorySegment data) {
+ byte[] bytes = fromJArrayByte(scope, data);
+ LOG.info("Got generic callback, opcode = " + opcode + " and data = " + Arrays.toString(bytes));
+ if (opcode == 1) {
+ // groupId
+ ByteBuffer bb = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN);
+ int groupIdLen = bb.getInt();
+ byte[] groupId = new byte[groupIdLen];
+ bb.get(groupId);
+ long ringId = bb.getLong();
+ byte[] senderBytes = new byte[bb.remaining()-1];
+ bb.get(senderBytes);
+ byte status = bb.get();
+ System.err.println("GroupId = "+Arrays.toString(groupId));
+ System.err.println("ringid = "+ringId);
+ System.err.println("senderBytes = "+Arrays.toString(senderBytes));
+ System.err.println("status = "+status);
+ api.groupCallUpdateRing(groupId, ringId, senderBytes, status);
+ }
+ }
+
+ }
+
+ Addressable createVideoFrameCallback() {
+ VideoFrameCallbackImpl sci = new VideoFrameCallbackImpl();
+ MemorySegment seg = createCallEndpoint$videoFrameCallback.allocate(sci, scope);
+ return seg.address();
+ }
+
+ @Deprecated
+ class VideoFrameCallbackImpl implements createCallEndpoint$videoFrameCallback {
+ @Override
+ public void apply(MemoryAddress opaque, int w, int h, long size) {
+ LOG.info("Got incoming video frame in Java layer, w = "+w+", h = " + h+", size = " + size);
+ System.err.println("Opaque = " + opaque);
+ MemorySegment segment = MemorySegment.ofAddress(opaque, size, scope);
+ byte[] raw = segment.toArray(ValueLayout.JAVA_BYTE);
+ synchronized (frameQueue) {
+ LOG.info("Add frame to queue");
+ frameQueue.add(new TringFrame(w,h,-1,raw));
+ frameQueue.notifyAll();
+ }
+ LOG.info("Processed incoming video frame in Java layer");
+ sendAck();
+ }
+ }
+
+ // We need to inform ringrtc that we handled a message, so that it is ok
+ // with sending the next message
+ void sendAck() {
+ MemorySegment callid = MemorySegment.allocateNative(8, scope);
+ callid.set(ValueLayout.JAVA_LONG, 0l, activeCallId);
+ tringlib_h.signalMessageSent(callEndpoint, callid);
+ }
+
+}
diff --git a/src/java/tring/src/main/java/module-info.java b/src/java/tring/src/main/java/module-info.java
new file mode 100644
index 00000000..d8cb3692
--- /dev/null
+++ b/src/java/tring/src/main/java/module-info.java
@@ -0,0 +1,7 @@
+module io.privacyresearch.tring {
+ requires java.logging;
+ requires io.privacyresearch.tringapi;
+
+ exports io.privacyresearch.tring;
+ provides io.privacyresearch.tringapi.TringService with io.privacyresearch.tring.TringServiceImpl;
+}
diff --git a/src/java/tring/src/main/resources/META-INF/services/io.privacyresearch.tringapi.TringService b/src/java/tring/src/main/resources/META-INF/services/io.privacyresearch.tringapi.TringService
new file mode 100644
index 00000000..6202e88e
--- /dev/null
+++ b/src/java/tring/src/main/resources/META-INF/services/io.privacyresearch.tringapi.TringService
@@ -0,0 +1 @@
+io.privacyresearch.tring.TringServiceImpl
diff --git a/src/java/tring/test.sh b/src/java/tring/test.sh
new file mode 100644
index 00000000..fcc7ca2a
--- /dev/null
+++ b/src/java/tring/test.sh
@@ -0,0 +1 @@
+java --enable-preview -cp ../tringapi/target/tringapi-0.0.8-SNAPSHOT.jar:target/tring-0.0.11-SNAPSHOT-linux-x86_64.jar io.privacyresearch.tringtest.Main
diff --git a/src/java/tringapi/pom.xml b/src/java/tringapi/pom.xml
new file mode 100644
index 00000000..fdd89f8c
--- /dev/null
+++ b/src/java/tringapi/pom.xml
@@ -0,0 +1,52 @@
+
+
+ 4.0.0
+ io.privacyresearch
+ tringapi
+ 0.0.9-SNAPSHOT
+ jar
+
+ UTF-8
+ 19
+ 19
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.8.1
+
+
+ --enable-preview
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 3.0.0-M5
+
+ false
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 3.2.2
+
+
+
+
+
+ gluon-nexus
+ https://nexus.gluonhq.com/nexus/content/repositories/releases
+
+
+ gluon-nexus
+ https://nexus.gluonhq.com/nexus/content/repositories/public-snapshots
+
+
+
+
diff --git a/src/java/tringapi/src/main/java/io/privacyresearch/tringapi/TringApi.java b/src/java/tringapi/src/main/java/io/privacyresearch/tringapi/TringApi.java
new file mode 100644
index 00000000..42837053
--- /dev/null
+++ b/src/java/tringapi/src/main/java/io/privacyresearch/tringapi/TringApi.java
@@ -0,0 +1,22 @@
+package io.privacyresearch.tringapi;
+
+import java.util.List;
+
+/**
+ *
+ * @author johan
+ */
+public interface TringApi {
+
+ void statusCallback(long callId, long peerId, int dir, int type);
+
+ void answerCallback(byte[] opaque);
+
+ void offerCallback(byte[] opaque);
+
+ void iceUpdateCallback(List iceCandidates);
+
+ void groupCallUpdateRing(byte[] groupId, long ringId, byte[] senderBytes, byte status);
+ // void getVideoFrame(int w, int h, byte[] raw);
+
+}
diff --git a/src/java/tringapi/src/main/java/io/privacyresearch/tringapi/TringBridge.java b/src/java/tringapi/src/main/java/io/privacyresearch/tringapi/TringBridge.java
new file mode 100644
index 00000000..5b073b9d
--- /dev/null
+++ b/src/java/tringapi/src/main/java/io/privacyresearch/tringapi/TringBridge.java
@@ -0,0 +1,103 @@
+package io.privacyresearch.tringapi;
+
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import java.util.Optional;
+import java.util.ServiceLoader;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
+
+ /**
+ * This class provides the access points for the application to interact with
+ * RingRTC.
+ * Methods here are invoked by Equation.
+ * When RingRTC wants to call back into the application, the TringApi interface
+ * is used.
+ * @author johan
+ */
+public class TringBridge {
+
+ private TringService service;
+ private static final Logger LOG = Logger.getLogger(TringBridge.class.getName());
+
+ public TringBridge(final TringApi api) {
+ ServiceLoader loader = ServiceLoader.load(TringService.class);
+ Optional serviceOpt = loader.findFirst();
+ serviceOpt.ifPresentOrElse(s -> {
+ this.service = s;
+ this.service.setApi(api);
+ }, () -> {
+ LOG.warning("No tring service!");
+ });
+
+ }
+
+ public String getVersionInfo() {
+ if (service == null) {
+ return "No TringService registered";
+ } else {
+ return service.getVersionInfo();
+ }
+ }
+
+ public void acceptCall() {
+ service.acceptCall();
+ }
+
+ public void ignoreCall() {
+ service.ignoreCall();
+ }
+
+ public void hangupCall() {
+ service.hangupCall();
+ }
+
+ public void proceed(long callId, String iceUser, String icePassword, List ice) {
+ List iceb = ice.stream().map(s -> s.getBytes(StandardCharsets.UTF_8)).collect(Collectors.toList());
+ service.proceed(callId, iceUser, icePassword, iceb);
+ }
+
+ public void receivedIce(long callId, int senderDeviceId, List ice) {
+ service.receivedIce(callId, senderDeviceId, ice);
+ }
+
+ public void receivedOffer(String peerId, long callId, int senderDeviceId, int receiverDeviceId,
+ byte[] senderKey, byte[] receiverKey, byte[] opaque) {
+ service.receivedOffer(peerId, callId, senderDeviceId, receiverDeviceId, senderKey, receiverKey, opaque);
+ }
+
+ public void receivedOpaqueMessage(byte[] uuid, int senderDeviceId, int receiverDeviceId,
+ byte[] opaque, long ageMessage) {
+ service.receivedOpaqueMessage(uuid, senderDeviceId, receiverDeviceId, opaque, ageMessage);
+ }
+
+ public void receivedAnswer(String peerId, long callId, int receiverDeviceId,
+ byte[] senderKey, byte[] receiverKey, byte[] opaque) {
+ service.receivedAnswer(peerId, callId, receiverDeviceId, senderKey, receiverKey, opaque);
+ }
+
+ public long startOutgoingCall(long callId, String peerId, int localDeviceId, boolean enableVideo) {
+ return service.startOutgoingCall(callId, peerId, localDeviceId, enableVideo);
+ }
+
+ public void enableOutgoingVideo(boolean enable) {
+ service.enableOutgoingVideo(enable);
+ }
+
+ public TringFrame getRemoteVideoFrame() {
+ return service.getRemoteVideoFrame();
+ }
+
+ public void sendVideoFrame(int width, int height, int pixelFormat, byte[] raw) {
+ service.sendVideoFrame(width, height, pixelFormat, raw);
+ }
+
+ public void setArray() {
+ service.setArray();
+ }
+
+ public void peekGroupCall(byte[] membershipProof) {
+ service.peekGroupCall(membershipProof);
+ }
+
+}
diff --git a/src/java/tringapi/src/main/java/io/privacyresearch/tringapi/TringFrame.java b/src/java/tringapi/src/main/java/io/privacyresearch/tringapi/TringFrame.java
new file mode 100644
index 00000000..de4e352d
--- /dev/null
+++ b/src/java/tringapi/src/main/java/io/privacyresearch/tringapi/TringFrame.java
@@ -0,0 +1,14 @@
+package io.privacyresearch.tringapi;
+
+public class TringFrame {
+
+ public final int width, height, pixelFormat;
+ public final byte[] data;
+
+ public TringFrame(int width, int height, int pixelFormat, byte[] data) {
+ this.width = width;
+ this.height = height;
+ this.pixelFormat = pixelFormat;
+ this.data = data;
+ }
+}
diff --git a/src/java/tringapi/src/main/java/io/privacyresearch/tringapi/TringService.java b/src/java/tringapi/src/main/java/io/privacyresearch/tringapi/TringService.java
new file mode 100644
index 00000000..9500f8f8
--- /dev/null
+++ b/src/java/tringapi/src/main/java/io/privacyresearch/tringapi/TringService.java
@@ -0,0 +1,60 @@
+ package io.privacyresearch.tringapi;
+
+import java.util.List;
+
+ /**
+ * Implementations of this interface provides the access points for the application to interact with
+ * RingRTC.
+ * Methods here are invoked by TringBridge, which is invoked by Equation.
+ * When RingRTC wants to call back into the application, the TringApi interface
+ * is used.
+ * @author johan
+ */
+public interface TringService {
+
+ public void setApi(TringApi api);
+
+ public void acceptCall();
+ public void ignoreCall();
+ public void hangupCall();
+
+ public void proceed(long callId, String iceUser, String icePwd, List ice);
+
+ public void receivedIce(long callId, int senderDeviceId, List ice);
+
+ public void receivedOffer(String peerId, long callId, int senderDeviceId, int receiverDeviceId,
+ byte[] senderKey, byte[] receiverKey, byte[] opaque);
+
+ public void receivedOpaqueMessage(byte[] senderUuid, int senderDeviceId,
+ int localDeviceId, byte[] opaque, long age);
+
+ public void receivedAnswer(String peerId, long callId, int senderDeviceId,
+ byte[] senderKey, byte[] receiverKey, byte[] opaque);
+ public long startOutgoingCall(long callId, String peerId, int localDeviceId, boolean enableVideo);
+
+ public default String getVersionInfo() {
+ return "Unresolved TringService";
+ }
+
+ /**
+ * Disable or enable outgoing video.
+ * @param enable true if we want to enable outgoing video, false otherwise
+ */
+ public void enableOutgoingVideo(boolean enable);
+
+ /**
+ * Get a videoframe from the other side.
+ * @param skip if true, ignore all old frames, and return the most recent one
+ * @return a frame
+ */
+ public TringFrame getRemoteVideoFrame(boolean skip);
+
+ public default TringFrame getRemoteVideoFrame() {
+ return getRemoteVideoFrame(false);
+ }
+ public void sendVideoFrame(int w, int h, int pixelFormat, byte[] raw);
+
+ public void setArray();
+
+ public void peekGroupCall(byte[] membershipProof);
+}
diff --git a/src/java/tringapi/src/main/java/module-info.java b/src/java/tringapi/src/main/java/module-info.java
new file mode 100644
index 00000000..6d11de95
--- /dev/null
+++ b/src/java/tringapi/src/main/java/module-info.java
@@ -0,0 +1,6 @@
+module io.privacyresearch.tringapi {
+ requires java.logging;
+
+ exports io.privacyresearch.tringapi;
+ uses io.privacyresearch.tringapi.TringService;
+}
diff --git a/src/rust/Cargo.lock b/src/rust/Cargo.lock
index 589d6299..b914119f 100644
--- a/src/rust/Cargo.lock
+++ b/src/rust/Cargo.lock
@@ -4,9 +4,9 @@ version = 3
[[package]]
name = "aes"
-version = "0.8.1"
+version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bfe0133578c0986e1fe3dfcd4af1cc5b2dd6c3dbf534d69916ce16a2701d40ba"
+checksum = "433cfd6710c9986c576a25ca913c39d66a6474107b406f34f91d4a8923395241"
dependencies = [
"cfg-if",
"cipher",
@@ -15,18 +15,27 @@ dependencies = [
[[package]]
name = "aho-corasick"
-version = "0.7.19"
+version = "0.7.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e"
+checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac"
dependencies = [
"memchr",
]
+[[package]]
+name = "ansi_term"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
+dependencies = [
+ "winapi",
+]
+
[[package]]
name = "anyhow"
-version = "1.0.65"
+version = "1.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602"
+checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6"
[[package]]
name = "atty"
@@ -47,9 +56,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "base64"
-version = "0.13.0"
+version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
+checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
[[package]]
name = "bitflags"
@@ -68,9 +77,9 @@ dependencies = [
[[package]]
name = "bumpalo"
-version = "3.11.0"
+version = "3.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d"
+checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba"
[[package]]
name = "byteorder"
@@ -80,15 +89,34 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "bytes"
-version = "1.2.1"
+version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db"
+checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c"
+
+[[package]]
+name = "cbindgen"
+version = "0.20.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "51e3973b165dc0f435831a9e426de67e894de532754ff7a3f307c03ee5dec7dc"
+dependencies = [
+ "clap",
+ "heck 0.3.3",
+ "indexmap",
+ "log",
+ "proc-macro2",
+ "quote",
+ "serde",
+ "serde_json",
+ "syn",
+ "tempfile",
+ "toml",
+]
[[package]]
name = "cc"
-version = "1.0.73"
+version = "1.0.77"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
+checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4"
[[package]]
name = "cesu8"
@@ -118,6 +146,21 @@ dependencies = [
"inout",
]
+[[package]]
+name = "clap"
+version = "2.34.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
+dependencies = [
+ "ansi_term",
+ "atty",
+ "bitflags",
+ "strsim",
+ "textwrap",
+ "unicode-width",
+ "vec_map",
+]
+
[[package]]
name = "combine"
version = "4.6.6"
@@ -180,9 +223,9 @@ dependencies = [
[[package]]
name = "digest"
-version = "0.10.5"
+version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c"
+checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f"
dependencies = [
"block-buffer",
"crypto-common",
@@ -197,9 +240,22 @@ checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
[[package]]
name = "env_logger"
-version = "0.9.1"
+version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c90bf5f19754d10198ccb95b70664fc925bd1fc090a0fd9a6ebc54acc8cd6272"
+checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3"
+dependencies = [
+ "atty",
+ "humantime",
+ "log",
+ "regex",
+ "termcolor",
+]
+
+[[package]]
+name = "env_logger"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7"
dependencies = [
"atty",
"humantime",
@@ -234,9 +290,9 @@ dependencies = [
[[package]]
name = "futures"
-version = "0.3.24"
+version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f21eda599937fba36daeb58a22e8f5cee2d14c4a17b5b7739c7c8e5e3b8230c"
+checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0"
dependencies = [
"futures-channel",
"futures-core",
@@ -249,9 +305,9 @@ dependencies = [
[[package]]
name = "futures-channel"
-version = "0.3.24"
+version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050"
+checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed"
dependencies = [
"futures-core",
"futures-sink",
@@ -259,15 +315,15 @@ dependencies = [
[[package]]
name = "futures-core"
-version = "0.3.24"
+version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf"
+checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac"
[[package]]
name = "futures-executor"
-version = "0.3.24"
+version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9ff63c23854bee61b6e9cd331d523909f238fc7636290b96826e9cfa5faa00ab"
+checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2"
dependencies = [
"futures-core",
"futures-task",
@@ -276,15 +332,15 @@ dependencies = [
[[package]]
name = "futures-io"
-version = "0.3.24"
+version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bbf4d2a7a308fd4578637c0b17c7e1c7ba127b8f6ba00b29f717e9655d85eb68"
+checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb"
[[package]]
name = "futures-macro"
-version = "0.3.24"
+version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "42cd15d1c7456c04dbdf7e88bcd69760d74f3a798d6444e16974b505b0e62f17"
+checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d"
dependencies = [
"proc-macro2",
"quote",
@@ -293,21 +349,21 @@ dependencies = [
[[package]]
name = "futures-sink"
-version = "0.3.24"
+version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "21b20ba5a92e727ba30e72834706623d94ac93a725410b6a6b6fbc1b07f7ba56"
+checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9"
[[package]]
name = "futures-task"
-version = "0.3.24"
+version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1"
+checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea"
[[package]]
name = "futures-util"
-version = "0.3.24"
+version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90"
+checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6"
dependencies = [
"futures-channel",
"futures-core",
@@ -348,6 +404,15 @@ version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
+[[package]]
+name = "heck"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
+dependencies = [
+ "unicode-segmentation",
+]
+
[[package]]
name = "heck"
version = "0.4.0"
@@ -387,7 +452,7 @@ version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
dependencies = [
- "digest 0.10.5",
+ "digest 0.10.6",
]
[[package]]
@@ -408,9 +473,9 @@ dependencies = [
[[package]]
name = "indexmap"
-version = "1.9.1"
+version = "1.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
+checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399"
dependencies = [
"autocfg",
"hashbrown",
@@ -445,9 +510,9 @@ dependencies = [
[[package]]
name = "itoa"
-version = "1.0.3"
+version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754"
+checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc"
[[package]]
name = "jni"
@@ -486,9 +551,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
-version = "0.2.134"
+version = "0.2.138"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "329c933548736bc49fd575ee68c89e8be4d260064184389a5b77517cddd99ffb"
+checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8"
[[package]]
name = "libloading"
@@ -564,9 +629,9 @@ dependencies = [
[[package]]
name = "num_cpus"
-version = "1.13.1"
+version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1"
+checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5"
dependencies = [
"hermit-abi",
"libc",
@@ -595,9 +660,9 @@ dependencies = [
[[package]]
name = "once_cell"
-version = "1.15.0"
+version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1"
+checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
[[package]]
name = "percent-encoding"
@@ -629,9 +694,19 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "ppv-lite86"
-version = "0.2.16"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
+
+[[package]]
+name = "prettyplease"
+version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
+checksum = "c142c0e46b57171fe0c528bee8c5b7569e80f0c17e377cd0e30ea57dbc11bb51"
+dependencies = [
+ "proc-macro2",
+ "syn",
+]
[[package]]
name = "proc-macro-crate"
@@ -646,18 +721,18 @@ dependencies = [
[[package]]
name = "proc-macro2"
-version = "1.0.46"
+version = "1.0.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "94e2ef8dbfc347b10c094890f778ee2e36ca9bb4262e86dc99cd217e35f3470b"
+checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
dependencies = [
"unicode-ident",
]
[[package]]
name = "prost"
-version = "0.11.0"
+version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "399c3c31cdec40583bb68f0b18403400d01ec4289c383aa047560439952c4dd7"
+checksum = "c0b18e655c21ff5ac2084a5ad0611e827b3f92badf79f4910b5a5c58f4d87ff0"
dependencies = [
"bytes",
"prost-derive",
@@ -665,29 +740,31 @@ dependencies = [
[[package]]
name = "prost-build"
-version = "0.11.1"
+version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f835c582e6bd972ba8347313300219fed5bfa52caf175298d860b61ff6069bb"
+checksum = "e330bf1316db56b12c2bcfa399e8edddd4821965ea25ddb2c134b610b1c1c604"
dependencies = [
"bytes",
- "heck",
+ "heck 0.4.0",
"itertools",
"lazy_static",
"log",
"multimap",
"petgraph",
+ "prettyplease",
"prost",
"prost-types",
"regex",
+ "syn",
"tempfile",
"which",
]
[[package]]
name = "prost-derive"
-version = "0.11.0"
+version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7345d5f0e08c0536d7ac7229952590239e77abf0a0100a1b1d890add6ea96364"
+checksum = "164ae68b6587001ca506d3bf7f1000bfa248d0e1217b618108fba4ec1d0cc306"
dependencies = [
"anyhow",
"itertools",
@@ -698,9 +775,9 @@ dependencies = [
[[package]]
name = "prost-types"
-version = "0.11.1"
+version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4dfaa718ad76a44b3415e6c4d53b17c8f99160dcb3a99b10470fce8ad43f6e3e"
+checksum = "747761bc3dc48f9a34553bf65605cf6cb6288ba219f3450b4275dbd81539551a"
dependencies = [
"bytes",
"prost",
@@ -767,9 +844,9 @@ dependencies = [
[[package]]
name = "regex"
-version = "1.6.0"
+version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b"
+checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a"
dependencies = [
"aho-corasick",
"memchr",
@@ -797,9 +874,9 @@ dependencies = [
[[package]]
name = "regex-syntax"
-version = "0.6.27"
+version = "0.6.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244"
+checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
[[package]]
name = "remove_dir_all"
@@ -833,8 +910,10 @@ dependencies = [
"anyhow",
"base64",
"bytes",
+ "cbindgen",
"ctr",
- "env_logger",
+ "env_logger 0.8.4",
+ "env_logger 0.9.3",
"futures",
"hex",
"hkdf",
@@ -919,18 +998,18 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
[[package]]
name = "serde"
-version = "1.0.145"
+version = "1.0.149"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b"
+checksum = "256b9932320c590e707b94576e3cc1f7c9024d0ee6612dfbcf1cb106cbe8e055"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
-version = "1.0.145"
+version = "1.0.149"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c"
+checksum = "b4eae9b04cbffdfd550eb462ed33bc6a1b68c935127d008b27444d08380f94e4"
dependencies = [
"proc-macro2",
"quote",
@@ -939,9 +1018,9 @@ dependencies = [
[[package]]
name = "serde_json"
-version = "1.0.85"
+version = "1.0.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44"
+checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db"
dependencies = [
"itoa",
"ryu",
@@ -956,7 +1035,7 @@ checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0"
dependencies = [
"cfg-if",
"cpufeatures",
- "digest 0.10.5",
+ "digest 0.10.6",
]
[[package]]
@@ -986,6 +1065,12 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+[[package]]
+name = "strsim"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
+
[[package]]
name = "subtle"
version = "2.4.1"
@@ -994,9 +1079,9 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
[[package]]
name = "syn"
-version = "1.0.101"
+version = "1.0.105"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e90cde112c4b9690b8cbe810cba9ddd8bc1d7472e2cae317b69e9438c1cba7d2"
+checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908"
dependencies = [
"proc-macro2",
"quote",
@@ -1049,6 +1134,15 @@ dependencies = [
"winapi-util",
]
+[[package]]
+name = "textwrap"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
+dependencies = [
+ "unicode-width",
+]
+
[[package]]
name = "thiserror"
version = "1.0.37"
@@ -1086,13 +1180,14 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
[[package]]
name = "tokio"
-version = "1.21.2"
+version = "1.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099"
+checksum = "eab6d665857cc6ca78d6e80303a02cea7a7851e85dfbd77cbdc09bd129f1ef46"
dependencies = [
"autocfg",
"num_cpus",
"pin-project-lite",
+ "windows-sys",
]
[[package]]
@@ -1106,9 +1201,9 @@ dependencies = [
[[package]]
name = "typenum"
-version = "1.15.0"
+version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
+checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
[[package]]
name = "unicode-bidi"
@@ -1118,9 +1213,9 @@ checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"
[[package]]
name = "unicode-ident"
-version = "1.0.4"
+version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd"
+checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
[[package]]
name = "unicode-normalization"
@@ -1131,6 +1226,18 @@ dependencies = [
"tinyvec",
]
+[[package]]
+name = "unicode-segmentation"
+version = "1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a"
+
+[[package]]
+name = "unicode-width"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
+
[[package]]
name = "unicode-xid"
version = "0.2.4"
@@ -1170,6 +1277,12 @@ dependencies = [
"percent-encoding",
]
+[[package]]
+name = "vec_map"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
+
[[package]]
name = "version_check"
version = "0.9.4"
@@ -1318,6 +1431,63 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+[[package]]
+name = "windows-sys"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
+
[[package]]
name = "x25519-dalek"
version = "1.2.0"
@@ -1340,9 +1510,9 @@ dependencies = [
[[package]]
name = "zeroize_derive"
-version = "1.3.2"
+version = "1.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f8f187641dad4f680d25c4bfc4225b418165984179f26ca76ec4fb6441d3a17"
+checksum = "44bf07cb3e50ea2003396695d58bf46bc9887a1f362260446fad6bc4e79bd36c"
dependencies = [
"proc-macro2",
"quote",
diff --git a/src/rust/Cargo.toml b/src/rust/Cargo.toml
index 152e12f6..8ad25f7e 100644
--- a/src/rust/Cargo.toml
+++ b/src/rust/Cargo.toml
@@ -13,6 +13,7 @@ license = "AGPL-3.0-only"
[lib]
crate_type = ["cdylib", "staticlib", "lib"]
+path = "src/lib.rs"
[[bin]]
name = "cli"
@@ -51,6 +52,7 @@ subtle = { version = "2.3.0" }
thiserror = { version = "1.0.20" }
tokio = { version = "1.13.0", features = ["rt-multi-thread", "time"] }
x25519-dalek = { version = "1.1" }
+env_logger = { version = "0.8.1" }
# Optional, needed by the "electron" feature
neon = { version = "0.10.1", optional = true, default-features = false, features = ["napi-6", "channel-api"] }
@@ -67,6 +69,7 @@ webpki = { version = "0.21", optional = true }
default = []
sim = []
electron = ["neon", "native"]
+java = ["native"]
native = [] # We have this so we can more easily disable things only native clients need
simnet = [] # We have this so we can more easily disable things only simulated native client need
http = ["ureq", "rustls", "webpki"]
@@ -86,6 +89,7 @@ required-features = ["sim"]
jni = { version = "0.19.0", default-features = false }
[build-dependencies]
+cbindgen = "0.20.0"
prost-build = { version = "0.11" }
[dev-dependencies]
diff --git a/src/rust/build.rs b/src/rust/build.rs
index 9fc1c783..ed156580 100644
--- a/src/rust/build.rs
+++ b/src/rust/build.rs
@@ -21,6 +21,15 @@ fn build_protos() {
}
fn main() {
+ if cfg!(feature = "java") {
+ let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
+ cbindgen::Builder::new()
+ .with_crate(crate_dir)
+ .with_language(cbindgen::Language::C)
+ .generate()
+ .expect("unable to generate bindings")
+ .write_to_file("tringlib.h");
+ }
let target = env::var("TARGET").unwrap();
let profile = env::var("PROFILE").unwrap();
let out_dir = env::var("OUTPUT_DIR");
diff --git a/src/rust/src/common/mod.rs b/src/rust/src/common/mod.rs
index 1cfd1ad6..0d271d36 100644
--- a/src/rust/src/common/mod.rs
+++ b/src/rust/src/common/mod.rs
@@ -16,6 +16,7 @@ pub type Result = std::result::Result;
/// Unique call identification number.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
+#[repr(C)]
pub struct CallId {
id: u64,
}
diff --git a/src/rust/src/core/connection.rs b/src/rust/src/core/connection.rs
index b78ece67..99880e66 100644
--- a/src/rust/src/core/connection.rs
+++ b/src/rust/src/core/connection.rs
@@ -2062,6 +2062,7 @@ where
_video_frame_metadata: VideoFrameMetadata,
video_frame: Option,
) -> Result<()> {
+ info!("Hello, incoming video frame");
if let (Some(incoming_video_sink), Some(video_frame)) =
(self.incoming_video_sink.as_ref(), video_frame)
{
diff --git a/src/rust/src/java/java.rs b/src/rust/src/java/java.rs
new file mode 100644
index 00000000..bf7359b3
--- /dev/null
+++ b/src/rust/src/java/java.rs
@@ -0,0 +1,1032 @@
+#![allow(unused_parens)]
+
+use std::collections::HashMap;
+use std::convert::TryInto;
+
+use std::slice;
+use std::sync::atomic::AtomicBool;
+use std::sync::mpsc::{channel, Sender};
+use std::sync::{Arc, Mutex};
+use std::time::Duration;
+
+use libc::size_t;
+
+use crate::common::{CallDirection, CallId, CallMediaType, DeviceId, Result};
+use crate::core::bandwidth_mode::BandwidthMode;
+use crate::core::call_manager::CallManager;
+use crate::core::group_call;
+use crate::core::group_call::{GroupId, SignalingMessageUrgency};
+use crate::core::signaling;
+use crate::core::util::ptr_as_mut;
+
+use crate::java::jtypes::{
+ JArrayByte, JArrayByte2D, JByteArray, JByteArray2D, JString, TringDevice,
+};
+
+use crate::lite::http;
+use crate::lite::sfu::UserId;
+use crate::native::{
+ CallState, CallStateHandler, GroupUpdate, GroupUpdateHandler, NativeCallContext,
+ NativePlatform, PeerId, SignalingSender,
+};
+use crate::webrtc::logging;
+use crate::webrtc::media::{
+ AudioTrack, VideoFrame, VideoPixelFormat, VideoSink, VideoSource, VideoTrack,
+};
+
+use crate::webrtc::peer_connection::AudioLevel;
+
+use crate::webrtc::peer_connection_factory::{
+ self as pcf, AudioDevice, IceServer, PeerConnectionFactory,
+};
+use crate::webrtc::peer_connection_observer::NetworkRoute;
+
+fn init_logging() {
+ env_logger::builder()
+ .filter(None, log::LevelFilter::Debug)
+ .init();
+ println!("LOGINIT done");
+ // let is_first_time_initializing_logger = log::set_logger(&LOG).is_ok();
+ let is_first_time_initializing_logger = true;
+ println!("EXTRALOG? {}", is_first_time_initializing_logger);
+ if is_first_time_initializing_logger {
+ // log::set_max_level(log::LevelFilter::Debug);
+ logging::set_logger(log::LevelFilter::Warn);
+ println!("EXTRALOG? yes");
+ }
+ // logging::set_logger(log::LevelFilter::Trace);
+ info!("INFO logging enabled");
+}
+
+// When the Java layer processes events, we want everything to go through a common queue that
+// combines all the things we want to "push" to it.
+pub enum Event {
+ // The JavaScript should send the following signaling message to the given
+ // PeerId in context of the given CallId. If the DeviceId is None, then
+ // broadcast to all devices of that PeerId.
+ SendSignaling(PeerId, Option, CallId, signaling::Message),
+ // The JavaScript should send the following opaque call message to the
+ // given recipient UUID.
+ SendCallMessage {
+ recipient_uuid: UserId,
+ message: Vec,
+ urgency: group_call::SignalingMessageUrgency,
+ },
+ // The JavaScript should send the following opaque call message to all
+ // other members of the given group
+ SendCallMessageToGroup {
+ group_id: GroupId,
+ message: Vec,
+ urgency: group_call::SignalingMessageUrgency,
+ },
+ // The call with the given remote PeerId has changed state.
+ // We assume only one call per remote PeerId at a time.
+ CallState(PeerId, CallId, CallState),
+ // The state of the remote video (whether enabled or not) changed.
+ // Like call state, we ID the call by PeerId and assume there is only one.
+ RemoteVideoStateChange(PeerId, bool),
+ // Whether the remote is sharing its screen or not changed.
+ // Like call state, we ID the call by PeerId and assume there is only one.
+ RemoteSharingScreenChange(PeerId, bool),
+ // The group call has an update.
+ GroupUpdate(GroupUpdate),
+ // JavaScript should initiate an HTTP request.
+ SendHttpRequest {
+ request_id: u32,
+ request: http::Request,
+ },
+ // The network route changed for a 1:1 call
+ NetworkRouteChange(PeerId, NetworkRoute),
+ AudioLevels {
+ peer_id: PeerId,
+ captured_level: AudioLevel,
+ received_level: AudioLevel,
+ },
+}
+
+/// Wraps a [`std::sync::mpsc::Sender`] with a callback to report new events.
+#[derive(Clone)]
+#[repr(C)]
+#[allow(non_snake_case)]
+struct EventReporter {
+ pub statusCallback: unsafe extern "C" fn(u64, u64, i32, i32),
+ pub answerCallback: unsafe extern "C" fn(JArrayByte),
+ pub offerCallback: unsafe extern "C" fn(JArrayByte),
+ pub iceUpdateCallback: unsafe extern "C" fn(JArrayByte),
+ pub genericCallback: unsafe extern "C" fn(i32, JArrayByte),
+ sender: Sender,
+ report: Arc,
+}
+
+fn string_to_bytes(v:String) -> Vec {
+ let mut answer:Vec = Vec::new();
+ let ul = v.len();
+ answer.extend_from_slice(&ul.to_le_bytes());
+ answer.extend_from_slice(v.as_bytes());
+ answer
+}
+
+impl EventReporter {
+ fn new(
+ statusCallback: extern "C" fn(u64, u64, i32, i32),
+ answerCallback: extern "C" fn(JArrayByte),
+ offerCallback: extern "C" fn(JArrayByte),
+ iceUpdateCallback: extern "C" fn(JArrayByte),
+ genericCallback: extern "C" fn(i32, JArrayByte),
+ sender: Sender,
+ report: impl Fn() + Send + Sync + 'static,
+ ) -> Self {
+ Self {
+ statusCallback,
+ answerCallback,
+ offerCallback,
+ iceUpdateCallback,
+ genericCallback,
+ sender,
+ report: Arc::new(report),
+ }
+ }
+
+ fn send(&self, event: Event) -> Result<()> {
+ match event {
+ Event::SendSignaling(_peer_id, _maybe_device_id, call_id, signal) => {
+ info!("JavaPlatform needs to send SignalingEvent to app");
+ match signal {
+ signaling::Message::Offer(offer) => {
+ info!(
+ "[JV] SendSignaling OFFER Event and call_id = {}",
+ call_id.as_u64()
+ );
+ let op = JArrayByte::new(offer.opaque);
+ unsafe {
+ (self.offerCallback)(op);
+ }
+ }
+ signaling::Message::Answer(answer) => {
+ info!("[JV] SendSignaling ANSWER Event");
+ let op = JArrayByte::new(answer.opaque);
+ unsafe {
+ (self.answerCallback)(op);
+ }
+ }
+ signaling::Message::Ice(ice) => {
+ info!("[JV] SendSignaling ICE Event");
+ let ilen = ice.candidates.len();
+ unsafe {
+ for i in 0..ilen {
+ (self.iceUpdateCallback)(JArrayByte::new(
+ ice.candidates[i].opaque.clone(),
+ ));
+ }
+ }
+ }
+ signaling::Message::Hangup(hangup) => {
+ let (hangup_type, hangup_device_id) = hangup.to_type_and_device_id();
+ let device_id: u64 = match hangup_device_id {
+ Some(device_id) => device_id.into(),
+ None => 0,
+ };
+ info!("[JV] SendSignaling Hangup Event");
+ unsafe {
+ (self.statusCallback)(
+ call_id.as_u64(),
+ device_id,
+ 11,
+ hangup_type as i32,
+ );
+ }
+ }
+ _ => {
+ info!("[JV] unknownSendSignalingEvent WHICH IS WHAT WE NEED TO FIX NOW!");
+ }
+ }
+ info!("JavaPlatform asked app to send SignalingEvent");
+ }
+ Event::CallState(_peer_id, call_id, CallState::Incoming(call_media_type)) => {
+ info!("[JV] CALLSTATEEVEMNT");
+ let direction = 0;
+ unsafe {
+ (self.statusCallback)(call_id.as_u64(), 1, direction, call_media_type as i32);
+ }
+ }
+ Event::CallState(_peer_id, call_id, CallState::Outgoing(call_media_type)) => {
+ info!("[JV] CALLSTATEEVEMNT");
+ let direction = 1;
+ unsafe {
+ (self.statusCallback)(call_id.as_u64(), 1, direction, call_media_type as i32);
+ }
+ }
+ Event::CallState(_peer_id, call_id, state) => {
+ info!("[JV] CallState changed");
+ let (state_string, state_index) = match state {
+ CallState::Ringing => ("ringing", 1),
+ CallState::Connected => ("connected", 2),
+ CallState::Connecting => ("connecting", 3),
+ CallState::Concluded => ("Concluded", 4),
+ CallState::Incoming(_) => ("incoming", 5),
+ CallState::Outgoing(_) => ("outgoing", 6),
+ CallState::Ended(_) => ("ended", 7),
+ };
+ info!("New state = {} and index = {}", state_string, state_index);
+ unsafe {
+ (self.statusCallback)(call_id.as_u64(), 1, 10 * state_index, 0);
+ }
+ }
+ Event::RemoteVideoStateChange(peer_id, enabled) => {
+ info!("RemoveVideoStateChange to {}", enabled);
+ unsafe {
+ if enabled {
+ (self.statusCallback)(1, 1, 22, 31);
+ } else {
+ (self.statusCallback)(1, 1, 22, 32);
+ }
+ }
+ }
+ Event::SendHttpRequest {
+ request_id,
+ request:
+ http::Request {
+ method,
+ url,
+ headers,
+ body,
+ },
+ } => {
+ info!("NYI SendHttpReq");
+ info!("Request id = {}", request_id);
+ info!("Requestmethod = {:?}", method);
+ info!("Requesturl = {:?}", url);
+ info!("Requestheaders = {:?}", headers);
+ info!("Requestbody = {:?}", body);
+ let mut payload:Vec = Vec::new();
+ let rid = request_id as i32;
+ payload.extend_from_slice(&rid.to_le_bytes());
+
+ payload.push(method as u8);
+
+ payload.extend(string_to_bytes(url));
+
+ let mut hdr:Vec = Vec::new();
+ for (name, value) in headers.iter() {
+ hdr.extend(string_to_bytes(name.to_string()));
+ hdr.extend(string_to_bytes(value.to_string()));
+ }
+ let hdrlen = hdr.len();
+ payload.extend_from_slice(&hdrlen.to_le_bytes());
+ payload.extend(hdr);
+
+ let bl = body.as_ref().map_or(0, |v| v.len());
+ payload.extend_from_slice(&bl.to_le_bytes());
+ payload.extend(body.unwrap_or_default());
+ let data = JArrayByte::new(payload);
+ unsafe {
+ (self.genericCallback)(2, data);
+ }
+ }
+ Event::GroupUpdate(GroupUpdate::RequestMembershipProof(client_id)) => {
+ info!("NYI RMP");
+ }
+ Event::GroupUpdate(GroupUpdate::RequestGroupMembers(client_id)) => {
+ info!("NYI RGM");
+ }
+ Event::GroupUpdate(GroupUpdate::ConnectionStateChanged(
+ client_id,
+ connection_state,
+ )) => {
+ info!("NYI CSTATEChanged");
+ }
+ Event::GroupUpdate(GroupUpdate::NetworkRouteChanged(client_id, network_route)) => {
+ info!("NYI NetworkRouteChanged");
+ }
+ Event::GroupUpdate(GroupUpdate::JoinStateChanged(client_id, join_state)) => {
+ info!("NYI JoinStatesChanged");
+ }
+ Event::GroupUpdate(GroupUpdate::RemoteDeviceStatesChanged(
+ client_id,
+ remote_device_states,
+ )) => {
+ info!("NYI RemoteDeviceStatesChanged");
+ }
+ Event::GroupUpdate(GroupUpdate::PeekChanged {
+ client_id,
+ peek_info,
+ }) => {
+ info!("NYI PeekChanged");
+ }
+ Event::GroupUpdate(GroupUpdate::PeekResult {
+ request_id,
+ peek_result,
+ }) => {
+ info!("NYI PeekResult");
+ }
+ Event::GroupUpdate(GroupUpdate::Ended(client_id, reason)) => {
+ info!("NYI ENDED");
+ }
+ Event::GroupUpdate(GroupUpdate::Ring {
+ group_id,
+ ring_id,
+ sender,
+ update,
+ }) => {
+ info!("[JV] GroupUpdate, gid = {:?}, ringid = {:?}, sender = {:?}, update = {:?}", group_id, ring_id, sender, update);
+ let mut payload:Vec = Vec::new();
+ let glen: i32 = group_id.len().try_into().unwrap();
+ payload.extend_from_slice(&glen.to_le_bytes());
+ payload.extend(group_id);
+ let rid: i64 = ring_id.into();
+ payload.extend_from_slice(&rid.to_le_bytes());
+ payload.extend(sender);
+ payload.push(update as u8);
+ let data = JArrayByte::new(payload);
+ unsafe {
+ (self.genericCallback)(1, data);
+ }
+ }
+ _ => {
+ info!("[JV] unknownevent");
+ }
+ };
+
+ Ok(())
+ }
+
+ fn report(&self) {
+ (self.report)();
+ }
+}
+
+impl SignalingSender for EventReporter {
+ fn send_signaling(
+ &self,
+ recipient_id: &str,
+ call_id: CallId,
+ receiver_device_id: Option,
+ msg: signaling::Message,
+ ) -> Result<()> {
+ info!("Need to send SIGNALING msg {:?}", msg);
+ self.send(Event::SendSignaling(
+ recipient_id.to_string(),
+ receiver_device_id,
+ call_id,
+ msg,
+ ))?;
+ Ok(())
+ }
+
+ fn send_call_message(
+ &self,
+ recipient_uuid: UserId,
+ message: Vec,
+ urgency: SignalingMessageUrgency,
+ ) -> Result<()> {
+ self.send(Event::SendCallMessage {
+ recipient_uuid,
+ message,
+ urgency,
+ })?;
+ Ok(())
+ }
+
+ fn send_call_message_to_group(
+ &self,
+ group_id: GroupId,
+ message: Vec,
+ urgency: group_call::SignalingMessageUrgency,
+ ) -> Result<()> {
+ self.send(Event::SendCallMessageToGroup {
+ group_id,
+ message,
+ urgency,
+ })?;
+ Ok(())
+ }
+}
+
+impl CallStateHandler for EventReporter {
+ fn handle_call_state(
+ &self,
+ remote_peer_id: &str,
+ call_id: CallId,
+ call_state: CallState,
+ ) -> Result<()> {
+ info!("[JV] CallStatehandler, invoke self.send");
+
+ self.send(Event::CallState(
+ remote_peer_id.to_string(),
+ call_id,
+ call_state,
+ ))?;
+ Ok(())
+ }
+
+ fn handle_network_route(
+ &self,
+ remote_peer_id: &str,
+ network_route: NetworkRoute,
+ ) -> Result<()> {
+ self.send(Event::NetworkRouteChange(
+ remote_peer_id.to_string(),
+ network_route,
+ ))?;
+ Ok(())
+ }
+
+ fn handle_remote_video_state(&self, remote_peer_id: &str, enabled: bool) -> Result<()> {
+ self.send(Event::RemoteVideoStateChange(
+ remote_peer_id.to_string(),
+ enabled,
+ ))?;
+ Ok(())
+ }
+
+ fn handle_remote_sharing_screen(&self, remote_peer_id: &str, enabled: bool) -> Result<()> {
+ self.send(Event::RemoteSharingScreenChange(
+ remote_peer_id.to_string(),
+ enabled,
+ ))?;
+ Ok(())
+ }
+
+ fn handle_audio_levels(
+ &self,
+ remote_peer_id: &str,
+ captured_level: AudioLevel,
+ received_level: AudioLevel,
+ ) -> Result<()> {
+ self.send(Event::AudioLevels {
+ peer_id: remote_peer_id.to_string(),
+ captured_level,
+ received_level,
+ })?;
+ Ok(())
+ }
+}
+
+impl http::Delegate for EventReporter {
+ fn send_request(&self, request_id: u32, request: http::Request) {
+ let _ = self.send(Event::SendHttpRequest {
+ request_id,
+ request,
+ });
+ }
+}
+
+impl GroupUpdateHandler for EventReporter {
+ fn handle_group_update(&self, update: GroupUpdate) -> Result<()> {
+ info!("Handle group update {:?}", update);
+ self.send(Event::GroupUpdate(update))?;
+ Ok(())
+ }
+}
+
+pub struct CallEndpoint {
+ call_manager: CallManager,
+ // This is what we use to control mute/not.
+ // It should probably be per-call, but for now it's easier to have only one.
+ outgoing_audio_track: AudioTrack,
+ // This is what we use to push video frames out.
+ outgoing_video_source: VideoSource,
+ // We only keep this around so we can pass it to PeerConnectionFactory::create_peer_connection
+ // via the NativeCallContext.
+ outgoing_video_track: VideoTrack,
+ // Boxed so we can pass it as a Box
+ incoming_video_sink: Box,
+ peer_connection_factory: PeerConnectionFactory,
+ videoFrameCallback: extern "C" fn(*const u8, u32, u32, size_t),
+}
+
+impl CallEndpoint {
+ fn new<'a>(
+ use_new_audio_device_module: bool,
+ statusCallback: extern "C" fn(u64, u64, i32, i32),
+ answerCallback: extern "C" fn(JArrayByte),
+ offerCallback: extern "C" fn(JArrayByte),
+ iceUpdateCallback: extern "C" fn(JArrayByte),
+ genericCallback: extern "C" fn(i32, JArrayByte),
+ videoFrameCallback: extern "C" fn(*const u8, u32, u32, size_t),
+ ) -> Result {
+ // Relevant for both group calls and 1:1 calls
+ let (events_sender, _events_receiver) = channel::();
+ let peer_connection_factory = PeerConnectionFactory::new(pcf::Config {
+ use_new_audio_device_module,
+ ..Default::default()
+ })?;
+ let outgoing_audio_track = peer_connection_factory.create_outgoing_audio_track()?;
+ outgoing_audio_track.set_enabled(false);
+ let outgoing_video_source = peer_connection_factory.create_outgoing_video_source()?;
+ let outgoing_video_track =
+ peer_connection_factory.create_outgoing_video_track(&outgoing_video_source)?;
+ outgoing_video_track.set_enabled(false);
+ let incoming_video_sink = Box::new(LastFramesVideoSink::default());
+
+ let event_reported = Arc::new(AtomicBool::new(false));
+
+ let event_reporter = EventReporter::new(
+ statusCallback,
+ answerCallback,
+ offerCallback,
+ iceUpdateCallback,
+ genericCallback,
+ events_sender,
+ move || {
+ info!("[JV] EVENT_REPORTER, NYI");
+ if event_reported.swap(true, std::sync::atomic::Ordering::Relaxed) {
+ return;
+ }
+ },
+ );
+ // Only relevant for 1:1 calls
+ let signaling_sender = Box::new(event_reporter.clone());
+ let should_assume_messages_sent = false; // Use async notification from app to send next message.
+ let state_handler = Box::new(event_reporter.clone());
+
+ // Only relevant for group calls
+ let http_client = http::DelegatingClient::new(event_reporter.clone());
+ let group_handler = Box::new(event_reporter);
+
+ let platform = NativePlatform::new(
+ peer_connection_factory.clone(),
+ signaling_sender,
+ should_assume_messages_sent,
+ state_handler,
+ group_handler,
+ );
+
+ let call_manager = CallManager::new(platform, http_client)?;
+ Ok(Self {
+ call_manager,
+ outgoing_audio_track,
+ outgoing_video_source,
+ outgoing_video_track,
+ incoming_video_sink,
+ peer_connection_factory,
+ videoFrameCallback,
+ })
+ }
+}
+
+#[derive(Clone, Default)]
+struct LastFramesVideoSink {
+ last_frame_by_track_id: Arc>>,
+}
+
+impl VideoSink for LastFramesVideoSink {
+ fn on_video_frame(&self, track_id: u32, frame: VideoFrame) {
+ info!("Got videoframe!");
+ // let myframe: &mut[u8;512] = &mut [0;512];
+ // frame.to_rgba(myframe.as_mut_slice());
+ // info!("uploading frame = {:?}", myframe);
+ // info!("frame uploaded");
+ self.last_frame_by_track_id
+ .lock()
+ .unwrap()
+ .insert(track_id, frame);
+ }
+
+ fn box_clone(&self) -> Box {
+ Box::new(self.clone())
+ }
+}
+
+impl LastFramesVideoSink {
+ fn pop(&self, track_id: u32) -> Option {
+ self.last_frame_by_track_id
+ .lock()
+ .unwrap()
+ .remove(&track_id)
+ }
+
+ fn clear(&self) {
+ self.last_frame_by_track_id.lock().unwrap().clear();
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn initRingRTC(ts: JString) -> i64 {
+ println!("Initialize RingRTC, init logging");
+ init_logging();
+ println!("Initialize RingRTC, init logging done");
+ println!("Ready to print {:?}", ts);
+ let txt = ts.to_string();
+ info!("Got text: {}", txt);
+ info!("Initialized RingRTC, using logging");
+ 1
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn getVersion() -> i64 {
+ 1
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn createCallEndpoint(
+ statusCallback: extern "C" fn(u64, u64, i32, i32),
+ answerCallback: extern "C" fn(JArrayByte),
+ offerCallback: extern "C" fn(JArrayByte),
+ iceUpdateCallback: extern "C" fn(JArrayByte),
+ genericCallback: extern "C" fn(i32, JArrayByte),
+ videoFrameCallback: extern "C" fn(*const u8, u32, u32, size_t),
+) -> i64 {
+ let call_endpoint = CallEndpoint::new(
+ false,
+ statusCallback,
+ answerCallback,
+ offerCallback,
+ iceUpdateCallback,
+ genericCallback,
+ videoFrameCallback,
+ )
+ .unwrap();
+ let call_endpoint_box = Box::new(call_endpoint);
+ let boxx: Result<*mut CallEndpoint> = Ok(Box::into_raw(call_endpoint_box));
+
+ let answer: i64 = match boxx {
+ Ok(v) => v as i64,
+ Err(e) => {
+ info!("Error creating callEndpoint: {}", e);
+ 0
+ }
+ };
+ info!("[tring] CallEndpoint created at {}", answer);
+ answer
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn setSelfUuid(endpoint: i64, ts: JString) -> i64 {
+ let txt = ts.to_string();
+ info!("setSelfUuid to : {}", txt);
+ let uuid = txt.into_bytes();
+ let callendpoint = ptr_as_mut(endpoint as *mut CallEndpoint).unwrap();
+ callendpoint.call_manager.set_self_uuid(uuid);
+ 1
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn receivedOffer(
+ endpoint: i64,
+ peerId: JString,
+ call_id: u64,
+ offer_type: i32,
+ sender_device_id: u32,
+ receiver_device_id: u32,
+ sender_key: JByteArray,
+ receiver_key: JByteArray,
+ opaque: JByteArray,
+ age_sec: u64,
+) -> i64 {
+ let callendpoint = ptr_as_mut(endpoint as *mut CallEndpoint).unwrap();
+ let peer_id = JString::from(peerId);
+ let call_id = CallId::new(call_id);
+ let call_media_type = match offer_type {
+ 1 => CallMediaType::Video,
+ _ => CallMediaType::Audio, // TODO: Do something better. Default matches are evil.
+ };
+ let offer = signaling::Offer::new(call_media_type, opaque.to_vec_u8()).unwrap();
+ callendpoint.call_manager.received_offer(
+ peer_id.to_string(),
+ call_id,
+ signaling::ReceivedOffer {
+ offer,
+ age: Duration::from_secs(age_sec),
+ sender_device_id,
+ receiver_device_id,
+ // A Java desktop client cannot be the primary device.
+ receiver_device_is_primary: false,
+ sender_identity_key: sender_key.to_vec_u8(),
+ receiver_identity_key: receiver_key.to_vec_u8(),
+ },
+ );
+ 1
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn receivedOpaqueMessage(
+ endpoint: i64,
+ sender_juuid: JByteArray,
+ sender_device_id: DeviceId,
+ local_device_id: DeviceId,
+ opaque: JByteArray,
+ message_age_sec: u64) -> i64 {
+ info!("Create opaque message!");
+ let message = opaque.to_vec_u8();
+ let sender_uuid = sender_juuid.to_vec_u8();
+ let callendpoint = ptr_as_mut(endpoint as *mut CallEndpoint).unwrap();
+ callendpoint.call_manager.received_call_message(sender_uuid, sender_device_id, local_device_id, message, Duration::from_secs(message_age_sec));
+1
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn receivedAnswer(
+ endpoint: i64,
+ peerId: JString,
+ call_id: u64,
+ sender_device_id: u32,
+ sender_key: JByteArray,
+ receiver_key: JByteArray,
+ opaque: JByteArray,
+) -> i64 {
+ let callendpoint = ptr_as_mut(endpoint as *mut CallEndpoint).unwrap();
+ let peer_id = JString::from(peerId);
+ let call_id = CallId::new(call_id);
+ let answer = signaling::Answer::new(opaque.to_vec_u8()).unwrap();
+ callendpoint.call_manager.received_answer(
+ call_id,
+ signaling::ReceivedAnswer {
+ answer,
+ sender_device_id,
+ sender_identity_key: sender_key.to_vec_u8(),
+ receiver_identity_key: receiver_key.to_vec_u8(),
+ },
+ );
+ 1
+}
+
+// suppy a random callid
+#[no_mangle]
+pub unsafe extern "C" fn createOutgoingCall(
+ endpoint: i64,
+ peer_id: JString,
+ video_enabled: bool,
+ local_device_id: u32,
+ call_id: i64,
+) -> i64 {
+ info!("create outgoing call");
+ let endpoint = ptr_as_mut(endpoint as *mut CallEndpoint).unwrap();
+ let peer_id = peer_id.to_string();
+ let media_type = if video_enabled {
+ CallMediaType::Video
+ } else {
+ CallMediaType::Audio
+ };
+ let call_id = CallId::from(call_id);
+ endpoint
+ .call_manager
+ .create_outgoing_call(peer_id, call_id, media_type, local_device_id);
+ 1
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn proceedCall(
+ endpoint: i64,
+ call_id: u64,
+ bandwidth_mode: i32,
+ audio_levels_interval_millis: i32,
+ ice_user: JString,
+ ice_pwd: JString,
+ icepack: JByteArray2D,
+) -> i64 {
+ info!("Proceeding with call");
+ let endpoint = ptr_as_mut(endpoint as *mut CallEndpoint).unwrap();
+ let call_id = CallId::from(call_id);
+ let mut ice_candidates = Vec::new();
+ for j in 0..icepack.len {
+ let row = &icepack.buff[j];
+ let opaque = row.to_vec_u8();
+ ice_candidates.push(String::from_utf8(opaque).unwrap());
+ }
+ let ice_server = IceServer::new(ice_user.to_string(), ice_pwd.to_string(), ice_candidates);
+ let context = NativeCallContext::new(
+ false,
+ ice_server,
+ endpoint.outgoing_audio_track.clone(),
+ endpoint.outgoing_video_track.clone(),
+ endpoint.incoming_video_sink.clone(),
+ );
+ let audio_levels_interval = if audio_levels_interval_millis <= 0 {
+ None
+ } else {
+ Some(Duration::from_millis(audio_levels_interval_millis as u64))
+ };
+ endpoint.call_manager.proceed(
+ call_id,
+ context,
+ BandwidthMode::from_i32(bandwidth_mode),
+ audio_levels_interval,
+ );
+
+ 147
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn receivedIce(
+ endpoint: i64,
+ call_id: u64,
+ sender_device_id: DeviceId,
+ icepack: JByteArray2D,
+) {
+ info!("receivedIce from app with length = {}", icepack.len);
+ let callendpoint = ptr_as_mut(endpoint as *mut CallEndpoint).unwrap();
+ let call_id = CallId::from(call_id);
+ let mut ice_candidates = Vec::new();
+ for j in 0..icepack.len {
+ let row = &icepack.buff[j];
+ let opaque = row.to_vec_u8();
+ ice_candidates.push(signaling::IceCandidate::new(opaque));
+ }
+ callendpoint.call_manager.received_ice(
+ call_id,
+ signaling::ReceivedIce {
+ ice: signaling::Ice {
+ candidates: ice_candidates,
+ },
+ sender_device_id,
+ },
+ );
+ info!("receivedIce invoked call_manager and will now return to app");
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn acceptCall(endpoint: i64, call_id: u64) -> i64 {
+ let endpoint = ptr_as_mut(endpoint as *mut CallEndpoint).unwrap();
+ info!("acceptCall requested by app");
+ let call_id = CallId::from(call_id);
+ endpoint.call_manager.accept_call(call_id);
+ 573
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn ignoreCall(endpoint: i64, call_id: u64) -> i64 {
+ let endpoint = ptr_as_mut(endpoint as *mut CallEndpoint).unwrap();
+ info!("now drop (ignore) call");
+ let call_id = CallId::from(call_id);
+ endpoint.call_manager.drop_call(call_id);
+ 1
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn hangupCall(endpoint: i64) -> i64 {
+ let endpoint = ptr_as_mut(endpoint as *mut CallEndpoint).unwrap();
+ info!("now hangup call");
+ endpoint.call_manager.hangup();
+ 1
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn signalMessageSent(endpoint: i64, call_id: CallId) -> i64 {
+ let callendpoint = ptr_as_mut(endpoint as *mut CallEndpoint).unwrap();
+ info!("Received signalmessagesent, endpoint = {:?}", endpoint);
+ callendpoint.call_manager.message_sent(call_id);
+ 135
+}
+
+#[no_mangle]
+#[allow(non_snake_case)]
+pub unsafe extern "C" fn getAudioInputs(endpoint: i64, idx: u32) -> TringDevice<'static> {
+ let callendpoint = ptr_as_mut(endpoint as *mut CallEndpoint).unwrap();
+ let devices = callendpoint
+ .peer_connection_factory
+ .get_audio_recording_devices()
+ .unwrap();
+ // let mut answer: [TringDevice;16] = [TringDevice::empty();16];
+ let mut answer: TringDevice = TringDevice::empty();
+ for (i, device) in devices.iter().enumerate() {
+ let wd = TringDevice::from_fields(
+ i as u32,
+ device.name.clone(),
+ device.unique_id.clone(),
+ device.i18n_key.clone(),
+ );
+ if (i as u32 == idx) {
+ answer = wd;
+ }
+ // answer[i] = wd;
+ }
+ answer
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn setAudioInput(endpoint: i64, index: u16) -> i64 {
+ let endpoint = ptr_as_mut(endpoint as *mut CallEndpoint).unwrap();
+ info!("Have to set audio_recordig_device to {}", index);
+ endpoint
+ .peer_connection_factory
+ .set_audio_recording_device(index);
+ 1
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn getAudioOutputs(endpoint: i64) -> i64 {
+ let callendpoint = ptr_as_mut(endpoint as *mut CallEndpoint).unwrap();
+ let devices = callendpoint
+ .peer_connection_factory
+ .get_audio_playout_devices();
+
+ for device in devices.iter() {
+ info!("OUTDEVICE = {:#?}", device);
+ }
+ 1
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn setAudioOutput(endpoint: i64, index: u16) -> i64 {
+ let endpoint = ptr_as_mut(endpoint as *mut CallEndpoint).unwrap();
+ info!("Have to set audio_output_device to {}", index);
+ endpoint
+ .peer_connection_factory
+ .set_audio_playout_device(index);
+ 1
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn setOutgoingAudioEnabled(endpoint: i64, enable: bool) -> i64 {
+ let endpoint = ptr_as_mut(endpoint as *mut CallEndpoint).unwrap();
+ info!("Have to set outgoing audio enabled to {}", enable);
+ endpoint.outgoing_audio_track.set_enabled(enable);
+ 1
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn setOutgoingVideoEnabled(endpoint: i64, enable: bool) -> i64 {
+ info!("Hava to setOutgoingVideoEnabled({})", enable);
+ let endpoint = ptr_as_mut(endpoint as *mut CallEndpoint).unwrap();
+ endpoint.outgoing_video_track.set_enabled(enable);
+ let mut active_connection = endpoint.call_manager.active_connection();
+ if (active_connection.is_ok()) {
+ active_connection
+ .expect("No active connection!")
+ .update_sender_status(signaling::SenderStatus {
+ video_enabled: Some(enable),
+ ..Default::default()
+ });
+ } else {
+ info!("No active connection")
+ }
+ 1
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sendVideoFrame(
+ endpoint: i64,
+ width: u32,
+ height: u32,
+ pixel_format: i32,
+ raw: *const u8,
+) -> i64 {
+ let endpoint = ptr_as_mut(endpoint as *mut CallEndpoint).unwrap();
+ let mut size = width * height * 2;
+ if (pixel_format == 1) {
+ size = size * 2;
+ }
+ info!(
+ "Will send VideoFrame, width = {}, heigth = {}, pixelformat = {}, size = {}",
+ width, height, pixel_format, size
+ );
+ let buffer: &[u8] = unsafe { slice::from_raw_parts(raw, size as usize) };
+
+ let pixel_format = VideoPixelFormat::from_i32(pixel_format);
+ let pixel_format = pixel_format.unwrap();
+ info!(
+ "buf[0] = {} and buf[1] = {} and buf[300] = {}, size = {}",
+ buffer[0], buffer[1], buffer[300], size
+ );
+ let frame = VideoFrame::copy_from_slice(width, height, pixel_format, buffer);
+ endpoint.outgoing_video_source.push_frame(frame);
+ 1
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn fillLargeArray(endpoint: i64, mybuffer: *mut u8) -> i64 {
+ let zero = *mybuffer.offset(0);
+ let first = *mybuffer.offset(1);
+ let second = *mybuffer.offset(12);
+ info!("VAL 1 = {} and VAL2 = {}", first, second);
+ *mybuffer.offset(12) = 13;
+ 1
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn fillRemoteVideoFrame(endpoint: i64, mybuffer: *mut u8, len: usize) -> i64 {
+ info!("Have to retrieve remote video frame");
+ let endpoint = ptr_as_mut(endpoint as *mut CallEndpoint).unwrap();
+ let frame = endpoint.incoming_video_sink.pop(0);
+ if let Some(frame) = frame {
+ let frame = frame.apply_rotation();
+ let width: u32 = frame.width();
+ let height: u32 = frame.height();
+ let myframe: &mut [u8] = slice::from_raw_parts_mut(mybuffer, len);
+ frame.to_rgba(myframe);
+ info!(
+ "Frame0 = {}, w = {}, h = {}",
+ myframe[0],
+ frame.width(),
+ frame.height()
+ );
+ let mut size: i64 = (frame.width() << 16).into();
+ size = size + frame.height() as i64;
+ size
+ } else {
+ 0
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn peekGroupCall(endpoint: i64,
+ mp: JByteArray,
+) -> i64 {
+ let membership_proof = mp.to_vec_u8();
+ let endpoint = ptr_as_mut(endpoint as *mut CallEndpoint).unwrap();
+ info!("peekGroupCall, not fully implemented");
+ let sfu = String::from("https://sfu.voip.signal.org");
+ endpoint.call_manager.peek_group_call(1, sfu, membership_proof, Vec::new());
+ 1
+}
diff --git a/src/rust/src/java/jtypes.rs b/src/rust/src/java/jtypes.rs
new file mode 100644
index 00000000..88a1a430
--- /dev/null
+++ b/src/rust/src/java/jtypes.rs
@@ -0,0 +1,249 @@
+#![allow(unused_parens)]
+
+use crate::core::signaling;
+use crate::webrtc::peer_connection_factory::AudioDevice;
+
+use core::slice;
+use std::fmt;
+
+#[repr(C)]
+#[derive(Debug)]
+pub struct JString {
+ len: usize,
+ buff: *mut u8,
+}
+
+impl JString {
+ pub fn to_string(&self) -> String {
+ let answer = unsafe { String::from_raw_parts(self.buff, self.len, self.len) };
+ answer
+ }
+
+ /*
+ pub fn from_string(src: String) -> Self {
+ let string_len = src.len();
+ let mut string_bytes = src.as_bytes().as_mut_ptr();
+ Self {
+ len: string_len,
+ buff: string_bytes
+ }
+ }
+ */
+}
+
+#[repr(C)]
+#[derive(Clone, Copy, Debug)]
+pub struct RString<'a> {
+ len: usize,
+ buff: *const u8,
+ phantom: std::marker::PhantomData<&'a u8>,
+}
+
+impl<'a> RString<'a> {
+ pub fn from_string(src: String) -> Self {
+ let string_len = src.len();
+ let mut string_bytes = src.as_bytes().as_ptr();
+ Self {
+ len: string_len,
+ buff: string_bytes,
+ phantom: std::marker::PhantomData,
+ }
+ }
+}
+
+#[repr(C)]
+#[derive(Clone, Copy, Debug)]
+pub struct JArrayByte {
+ pub len: usize,
+ pub data: [u8; 256],
+}
+
+impl fmt::Display for JArrayByte {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(
+ f,
+ "JArrayByte with {} bytes at {:?}",
+ self.len,
+ &(self.data)
+ )
+ }
+}
+impl JArrayByte {
+ pub fn new(vector: Vec) -> Self {
+ let vlen = vector.len();
+ let mut vdata = [0; 256];
+ for i in 0..vlen {
+ vdata[i] = vector[i];
+ }
+ JArrayByte {
+ len: vlen,
+ data: vdata,
+ }
+ }
+
+ pub fn empty() -> Self {
+ let data = [0; 256];
+ JArrayByte { len: 0, data: data }
+ }
+}
+
+#[repr(C)]
+#[derive(Debug)]
+pub struct JArrayByte2D {
+ pub len: usize,
+ pub data: [u8; 256],
+ // pub data: [JArrayByte;25],
+}
+
+impl JArrayByte2D {
+ pub fn new(vector: Vec) -> Self {
+ info!(
+ "I have to create a jArrayByte with {} elements",
+ vector.len()
+ );
+ let vlen = vector.len();
+ let mut myrows: [JArrayByte; 25] = [JArrayByte::empty(); 25];
+ for i in 0..25 {
+ if (i < vlen) {
+ myrows[i] = JArrayByte::new(vector[i].opaque.clone());
+ // myrows[i] = JByteArray::from_data(vector[i].opaque.as_ptr(), vector[i].opaque.len());
+ info!("IceVec[{}] = {:?}", i, vector[i].opaque);
+ } else {
+ // myrows[i] = JByteArray::new(Vec::new());
+ myrows[i] = JArrayByte::new(Vec::new());
+ }
+ info!("Myrow[{}] : {}", i, myrows[i]);
+ }
+ info!("data at {:?}", myrows);
+ JArrayByte2D {
+ len: vlen,
+ data: [1; 256],
+ }
+ }
+}
+
+#[repr(C)]
+#[derive(Clone, Copy, Debug)]
+pub struct JByteArray {
+ len: usize,
+ pub buff: *const u8,
+}
+
+impl fmt::Display for JByteArray {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let address = &self.buff;
+ write!(f, "jByteArray with {} bytes at {:p}", self.len, self.buff)
+ }
+}
+
+impl JByteArray {
+ pub fn new(vector: Vec) -> Self {
+ let slice = vector.as_slice();
+ let buffer = slice.as_ptr();
+ JByteArray {
+ len: vector.len(),
+ buff: buffer,
+ }
+ }
+
+ pub fn to_vec_u8(&self) -> Vec {
+ let answer = unsafe { slice::from_raw_parts(self.buff, self.len).to_vec() };
+ answer
+ }
+
+ pub fn empty() -> Self {
+ let bar = Vec::new().as_ptr();
+ JByteArray { len: 0, buff: bar }
+ }
+
+ pub fn from_data(data: *const u8, len: usize) -> Self {
+ JByteArray {
+ len: len,
+ buff: data,
+ }
+ }
+}
+
+#[repr(C)]
+#[derive(Debug)]
+pub struct JByteArray2D {
+ pub len: usize,
+ pub buff: [JByteArray; 32],
+}
+
+impl JByteArray2D {
+ pub fn new(vector: Vec) -> Self {
+ let vlen = vector.len();
+ // let mut myrows = [Opaque::empty(); 25];
+ let mut myrows: [JByteArray; 32] = [JByteArray::empty(); 32];
+ for i in 0..25 {
+ if (i < vlen) {
+ myrows[i] =
+ JByteArray::from_data(vector[i].opaque.as_ptr(), vector[i].opaque.len());
+ } else {
+ myrows[i] = JByteArray::new(Vec::new());
+ }
+ }
+ JByteArray2D {
+ len: vlen,
+ buff: myrows,
+ }
+ }
+}
+
+#[repr(C)]
+struct Buffer {
+ data: *mut u8,
+ len: usize,
+}
+
+#[repr(C)]
+#[derive(Clone, Copy, Debug)]
+pub struct TringDevice<'a> {
+ index: u32,
+ name: RString<'a>,
+ unique_id: RString<'a>,
+ int_key: RString<'a>,
+}
+
+impl<'a> TringDevice<'a> {
+ pub fn empty() -> Self {
+ let name = RString::from_string("empty".to_string());
+ let unique_id = RString::from_string("empty".to_string());
+ let int_key = RString::from_string("empty".to_string());
+ Self {
+ index: 99,
+ name: name,
+ unique_id: unique_id,
+ int_key: int_key,
+ }
+ }
+
+ pub fn from_audio_device(index: u32, src: AudioDevice) -> Self {
+ let src_name = RString::from_string(src.name);
+ let src_unique_id = RString::from_string(src.unique_id);
+ let src_int_key = RString::from_string(src.i18n_key);
+ Self {
+ index: index,
+ name: src_name,
+ unique_id: src_unique_id,
+ int_key: src_int_key,
+ }
+ }
+ pub fn from_fields(
+ index: u32,
+ src_name: String,
+ src_unique_id: String,
+ src_i18n_key: String,
+ ) -> Self {
+ let src_name = RString::from_string(src_name);
+ let src_unique_id = RString::from_string(src_unique_id);
+ let src_int_key = RString::from_string(src_i18n_key);
+ Self {
+ index: index,
+ name: src_name,
+ unique_id: src_unique_id,
+ int_key: src_int_key,
+ }
+ }
+}
diff --git a/src/rust/src/lib.rs b/src/rust/src/lib.rs
index df7e51cb..134ed48a 100644
--- a/src/rust/src/lib.rs
+++ b/src/rust/src/lib.rs
@@ -57,6 +57,7 @@ pub mod protobuf;
#[cfg(any(target_os = "android", feature = "check-all"))]
/// Android specific implementation.
+/// cbindgen:ignore
mod android {
#[macro_use]
mod jni_util;
@@ -75,6 +76,7 @@ mod android {
#[cfg(any(target_os = "ios", feature = "check-all"))]
/// iOS specific implementation.
+/// cbindgen:ignore
mod ios {
mod api {
pub mod call_manager_interface;
@@ -91,6 +93,12 @@ pub mod electron;
#[cfg(feature = "native")]
pub mod native;
+#[cfg(feature = "java")]
+pub mod java {
+ mod java;
+ mod jtypes;
+}
+
/// Foreign Function Interface (FFI) to WebRTC C++ library.
pub mod webrtc {
pub mod arc;