roc-streaming / roc-java Goto Github PK
View Code? Open in Web Editor NEWJNI bindings for Roc Toolkit.
Home Page: https://roc-streaming.org
License: MIT License
JNI bindings for Roc Toolkit.
Home Page: https://roc-streaming.org
License: MIT License
Is it okay to do that?
Sender :
new Endpoint("rtp+rs8m://239.244.0.100:10001");
Receiver :
new Endpoint("rtp+rs8m://239.244.0.100:10001");
Add travis build:
Platforms:
See roc-go bindings: https://github.com/roc-project/roc-go/blob/master/.travis.yml
Currently, if Logger.setCallback()
was never called, or it was called with null
argument, we use "default logger".
Currently, default logger is implemented in libroc (native code), and it just writes message to stderr.
It would be more convenient to implement default logger in java and make it writing to standard java logger instead of stderr.
We should make sure than on android, default logger will send logs to android log (which can be received with adb logcat).
Sometimes we have problem with Install Android SDK components
step on CI.
- name: Install Android SDK components
uses: maxim-lobanov/setup-android-tools@v1
with:
cache: true
packages: |
platforms;android-${{ matrix.api }}
build-tools;${{ matrix.build_tools }}
ndk;${{ matrix.ndk }}
cmake;${{ matrix.cmake }}
system-images;android-${{ matrix.api }};${{ matrix.avd_image }};${{ matrix.avd_arch }}
emulator
e.g. https://github.com/roc-streaming/roc-java/actions/runs/5083653562 :
Run maxim-lobanov/setup-android-tools@v1
Getting list of available components
Installing 'platforms;android-29'...
Package is already installed and update is not required
Installing 'build-tools;28.0.3'...
Package is already installed and update is not required
Installing 'ndk;[21](https://github.com/roc-streaming/roc-java/actions/runs/5083653562/jobs/9144799554#step:3:23).1.635[24](https://github.com/roc-streaming/roc-java/actions/runs/5083653562/jobs/9144799554#step:3:26)62'...
Trying to restore package from cache...
Package is restored from cache
Installing 'cmake;3.10.2.4988404'...
Trying to restore package from cache...
Package is restored from cache
Installing 'system-images;android-29;default;x86_64'...
Trying to restore package from cache...
Package is restored from cache
Installing 'emulator'...
Package is already installed but update is required
Trying to restore package from cache...
No cache found
Trying to download package via sdkmanager...
Package is downloaded and installed
Saving package to cache...
Error: reserveCache failed: Cache already exists. Scope: refs/pull/115/merge, Key: emulator/32.1.13/macos12, Version: a9f019e19b66fa[27](https://github.com/roc-streaming/roc-java/actions/runs/5083653562/jobs/9144799554#step:3:29)731f9a30544c8c38d8e005fcc1[59](https://github.com/roc-streaming/roc-java/actions/runs/5083653562/jobs/9144799554#step:3:63)c97da1[75](https://github.com/roc-streaming/roc-java/actions/runs/5083653562/jobs/9144799554#step:3:80)fea90accf[79](https://github.com/roc-streaming/roc-java/actions/runs/5083653562/jobs/9144799554#step:3:84)1
I suppose that because we have 2 similar job that saves to cache the same thing
Here are some things we need to document in README.
For user - what are requirements
For desktop - range of supported JDK versions needed when building the project from sources.
Minimum Java version (build and run-time) - what is it and where it is defined.
Lines 18 to 19 in b309665
roc-java/android/roc-android/build.gradle
Lines 41 to 42 in b309665
Minimum Android version - what is level, what is corresponding OS version, and where it is defined.
roc-java/android/roc-android/build.gradle
Line 26 in b309665
For developer - what versions to bump
roc-java developers should periodically bump the following versions to the latest available:
All these versions are defined in build.yml
as described in detail in this issue: #105. We can include some info and links from that issue to README.
See also #112. ROC_VERSION will also need periodical bump.
I think we should also mention android_docker.sh
, which has its own set of defaults. These defaults are used if the user did not specify any variables when building AAR locally.
On Android, if AAR or APK has native libraries depending on libc++_shared.so (C++ STL from Clang), they should ship a copy of the library.
This, theoretically, may lead to problems if the user wants to use more than one native library and they depend on different versions of libc++_shared.so. It seems that the official recommendation is not use more than one native library.
In our case, both libroc.so and libroc_jni.so depend on libc++_shared.so. Here are the dependencies of libroc.so:
$ aarch64-linux-android-readelf -d ./libroc.so | grep NEEDED
0x0000000000000001 (NEEDED) Shared library: [libc++_shared.so]
0x0000000000000001 (NEEDED) Shared library: [libm.so]
0x0000000000000001 (NEEDED) Shared library: [libdl.so]
0x0000000000000001 (NEEDED) Shared library: [libc.so]
Here, libc++_shared.so is the only library needed to be shipped. Other libraries, AFAIK, are guaranteed to be available on every Android system.
But actually, I think we could get rid of the dependency on libc++_shared.so:
For libroc.so, we can link it statically into libroc.so or maybe even don't use it on Android at all (Roc does not use much of STL).
For libroc_jni.so, we can actually don't use C++. It seems that most files can be just renamed from .cpp to .c. It seems that the only C++ feature we're using is std::mutex. We can replace it with pthread_mutex for now (and add non-unix implementation later when needed), or maybe we can somehow move synchronization from C++ side to Java side.
@MatteoArella Thoughts?
Automatically build and publish javadoc documentation.
Should be done after #10. For Maven Central and JCenter, there is https://www.javadoc.io/. For https://jitpack.io/ there is built-in javadoc support.
RocContextConfig, RocSenderConfig, RocReceiverConfig have some fields that have int or long types, but actually are not allowed to be negative.
If the user tries to make them negative, they'll get error when opening context, sender, and receiver.
It would be more convenient to catch these errors earlier - immediately when trying to set negative value to config. We already provide type checking for other values - e.g. enums are checked by compiler.
Fields to be checked to be >= 0
:
RocContextConfig
RocSenderConfig
RocReceiverConfig
I think we should add a new method to Check
class and invoke this method in setters and builder methods for corresponding fields.
Need to add cache for Build Roc step for android/osx ci jobs.
cache key could be head commit from roc-toolkit
As recently posted to the mailing list, I've renamed github org to "roc-streaming" and also renamed the main repo from just "roc" to "roc-toolkit".
So the full name of this roc-java project is now something like "JNI bindings to Roc Toolkit".
I've also registered a domain name, finally: https://roc-streaming.org/
"Roc Toolkit" is considered as a "sub-project" of the Roc Streaming organization.
So now I suggest to rename Java package from com.github.rocproject.roc
to something based on the new domain, e.g.:
org.rocstreaming.roc
org.rocstreaming.roctoolkit
org.rocstreaming.rocjava
@MatteoArella @ortex Thoughts?
This should be done after #57
When the library is initialized, we need to retrieve versions of native lib and go bindings, and check the following invariant:
(See "Versioning" section in Readme for explanation)
If the invariant is not satisfied, we should throw an exception with a message including the retrieved versions and explanation of what is wrong.
We can do this check in RocLibrary.loadLibrary.
Currently, when CI builds AAR, it uses libroc from master branch of roc-toolkit. It's not good because this way AAR builds are not reproducible.
What we need to do:
add ROC_VERSION parameter to android_docker.sh script; default value should be "master"; when cloning roc, checkout specified version
in release job on CI, specify ROC_VERSION explicitly, set it to roc-toolkit tag which is currently the latest
ROC_VERSION should support any git revision notation: tag name, branch name, or commit hash.
There are a few android-related versions that we specify in build scripts and CI:
Android NDK version
. Does not affect runtime requirements. Generally should be set to latest available version because newer versions have more bug-fixes and optimizations.
Android build-tools version
and CMake version
. Similarly, should be set to latest available version.
Android API levels:
minSdkVersion
. Defines runtime requirement of the app. The app will support only systems with this version and later. Should be set to the lowest possible version.
targetSdkVersion
. Affects runtime behavior of the app. Should be set to the latest API level on which the app was tested. Android runtime will take this into account and will avoid behavior that was introduced in later versions.
compileSdkVersion
. Defines compile-time version of Android SDK. Generally should be set to latest available version.
Currently, minSdkVersion
is defined in android/roc-android/build.gradle
, and other versions are defined in .github/workflows/build.yml
and passed to gradle via environment variables.
What we need to do:
in scripts/android_docker.sh
:
rename API
to SDK_LEVEL
(corresponds to compileSdkVersion
)
the rename should be also reflected in:
scripts/android/build_roc.sh
scripts/android/start_emulator.sh
README.md
in .github/workflows/build.yml
:
in all jobs:
API
to SDK_LEVEL
in android-linux
and release
jobs:
BUILD_TOOLS_VERSION
and CMAKE_VERSION
vars (currently they're missing and we rely on defaults)in android-linux
, android-osx
, and release
jobs:
SDK_LEVEL
(compileSdkVersion) to the latest available versionNDK_VERSION
(Android NDK version) to the latest available versionBUILD_TOOLS_VERSION
(Android build-tools version) to the latest available versionCMAKE_VERSION
to the latest available versionin android-linux
and android-osx
jobs:
in addition to bumping versions as described above, keep one step with the currently used, older, versions of sdk, ndk, build-tools, and cmake; it will allow us to keep testing compatibility with older build environments
in android-linux
we'll need to add a new step for older versions; in android-osx
we'll need to use the existing last step for that
in android-linux
job:
scripts/android_docker.sh build
without exporting any environment variables (SDK_LEVEL, NDK_VERSION, etc); this step will allow us to keep testing that defaults defined in android_docker.sh
are good and the script works when no configuration is providedIn android/roc-android/build.gradle
:
API
env var to SDK_LEVEL
apiLevel
var to sdkLevel
compileSdkVersion
and targetSdkVersion
to sdkLevel
(obtained from SDK_LEVEL
)Logger doesn't work on android.
A/.rocandroidsin: java_vm_ext.cc:570] JNI DETECTED ERROR IN APPLICATION: JNI GetStaticFieldID called with pending exception java.lang.ClassNotFoundException: Didn't find class "com.github.rocproject.roc.LogLevel" on path: DexPathList[[directory "."],nativeLibraryDirectories=[/system/lib64, /system/product/lib64, /system/lib64, /system/product/lib64]]
java_vm_ext.cc:570] at java.lang.Class dalvik.system.BaseDexClassLoader.findClass(java.lang.String) (BaseDexClassLoader.java:196)
java_vm_ext.cc:570] at java.lang.Class java.lang.ClassLoader.loadClass(java.lang.String, boolean) (ClassLoader.java:379)
java_vm_ext.cc:570] at java.lang.Class java.lang.ClassLoader.loadClass(java.lang.String) (ClassLoader.java:312)
java_vm_ext.cc:570]
java_vm_ext.cc:570] in call to GetStaticFieldID
A/.rocandroidsin: thread.cc:2348] No pending exception expected: java.lang.ClassNotFoundException: Didn't find class "com.github.rocproject.roc.LogLevel" on path: DexPathList[[directory "."],nativeLibraryDirectories=[/system/lib64, /system/product/lib64, /system/lib64, /system/product/lib64]]
thread.cc:2348] at java.lang.Class dalvik.system.BaseDexClassLoader.findClass(java.lang.String) (BaseDexClassLoader.java:196)
thread.cc:2348] at java.lang.Class java.lang.ClassLoader.loadClass(java.lang.String, boolean) (ClassLoader.java:379)
thread.cc:2348] at java.lang.Class java.lang.ClassLoader.loadClass(java.lang.String) (ClassLoader.java:312)
thread.cc:2348]
This could help: https://developer.android.com/training/articles/perf-jni.html#faq:-why-didnt-findclass-find-my-class
Do your FindClass lookups once, in JNI_OnLoad, and cache the class references for later use. Any FindClass calls made as part of executing JNI_OnLoad will use the class loader associated with the function that called System.loadLibrary (this is a special rule, provided to make library initialization more convenient). If your app code is loading the library, FindClass will use the correct class loader.
What do we need:
References:
Currently we use AutoClosable
for Context, Receiver, and Sender.
Probably we could also implement some automatic closing for them to reduce the damage from forgetting to call close() in long-running applications.
As discussed in #2:
Finalizers have been deprecated since Java 9 in favour of PhantomReferences, Cleaners and ReferenceQueues. A better approach should be studied deeply and implemented maybe in a separate issue.
In 0.2 we'll replace roc_address with roc_endpoint, which also has to be deinitialized. Since roc_endpoint does not hold any resources except memory, it would be very convenient to employ something like finalizers for it as well to make it manageable by GC.
Currently, Android minSdkVersion (i.e. Android API level which we support) is set to 26
:
roc-java/android/roc-android/build.gradle
Line 26 in b309665
We should check whether this value can be lowered or it is indeed minimum version we can work with.
Theoretically it could be lowered because the requirement of the underlying native library (libroc) is 21
:
Chance are that roc-java has higher requirements than 21, but probably still lower than current 26. In this case, we'll need to update it in android/roc-android/build.gradle
, scripts/android_docker.sh
, and examples in README.md
.
See also #105
Add debug logs on java side for all non-realtime operations:
These operations should log their arguments.
If operation does a call to a native method, it should write to log before and after native call. This is useful when debugging crashes in native code.
Background: roc-streaming/roc-droid#45 (comment)
In receiver_config_unmarshal and in sender_config_unmarshal, for some fields we have checks if (tempObj == NULL) return -1
, and for some we don't have them.
I've tried to add checks for missing fields, and for every field when I tried to add a check, test started failing, with an exception stating that sender or receiver config is invalid (i.e. the added checks are failing).
Below I list the fields which don't have checks (and I was unable to add them).
Receiver:
Sender:
We build two versions of AAR, debug and release, and both use release version of native libroc.so.
It would be convenient to build debug version of libroc, and use it in debug version of AAR. Debug version of libroc should have better stacktraces.
libroc is built using scripts/android/build_roc.sh
. Currently it builds 4 versions of the lib (for 4 android ABIs). Now it'll need to build 8 versions (debug + release for each ABI), and gradle will need to use appropriate version.
To enable debugging, pass --enable-debug
option to scons.
Automatically build and publish our library.
Maybe use jitpack.io or Maven Central or JCenter.
Rationale:
.cpp
extension, most of the code is written in C style, and C++ dependency in redundantWe should add at least one integration test in Java that creates a sender, a receiver, transfers samples, and checks that they were transferred correctly.
Such an integration test for the C API can be found here: https://github.com/roc-streaming/roc-toolkit/blob/master/src/tests/public_api/test_sender_receiver.cpp
We can go further and add a few more tests, similar to tests described roc-streaming/roc-toolkit#328. I don't think we need to replicate all of them, but it'd be nice to test at least some of the key features.
See also roc-streaming/roc-go#29 for more hints.
New libroc API (starting from 0.2), log messages provide more granular information. Instead of three arguments (level, component, message), logging callback in native library now receives a struct roc_log_message, defined here:
Consider passing the following fields to jul:
As discussed in chat:
According to Travis docs https://docs.travis-ci.com/user/using-workspaces:
Travis workspaces allow jobs within a build to share files. They are useful when you want to use build artifacts from a previous job
In the Travis Release
stage the rebuilding of android artifact can be avoided (since it's already done during previous Android
build stage) using Travis Workspaces (even though they are currently still in beta).
In particular the steps involved would be:
Android
build stage, caching artifactRelease
stage publishing the previously built artifact.See #25.
Currently, I'm able to build roc-java using OpenJDK 8, but not OpenJDK 17.
OpenJDK 8 is not available in next debian stable candidate, so it would be nice to fix build with recent version. Otherwise you need to install older JDK manually.
When using OpenJDK 17, I get the following error:
Starting a Gradle Daemon, 1 incompatible Daemon could not be reused, use --status for details
FAILURE: Build failed with an exception.
* What went wrong:
A problem occurred configuring project ':roc_jni'.
> Could not open proj remapped class cache for 6ln1p28aqpsv1qjk04pyeijya (/home/victor/.gradle/caches/6.4/scripts-remapped/build_bvwlj1vorvl1q0nbtcfbai48m/6ln1p28aqpsv1qjk04pyeijya/proj68a30bfc7da5ef859cd7b983445462e5).
> Could not open proj generic class cache for build file '/home/victor/dev/roc-streaming/roc-java/roc_jni/build.gradle' (/home/victor/.gradle/caches/6.4/scripts/6ln1p28aqpsv1qjk04pyeijya/proj/proj68a30bfc7da5ef859cd7b983445462e5).
> BUG! exception in phase 'semantic analysis' in source unit '_BuildScript_' Unsupported class file major version 61
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.
* Get more help at https://help.gradle.org
BUILD FAILED in 2s
If I download JDK 8 from here and added it to PATH:
PATH="/some/path/jdk8u362-b09/bin:$PATH" ./gradlew build
everything works.
Find all places in java code where we check for invalid arguments and throw IllegalArgumentException.
Refactor them: add information to the thrown exception about which exact field is invalid and why (e.g. it's null).
Find all fields in RocSenderConfig and RocReceiverConfig that hold durations (in nanoseconds), e.g. targetLatency
, and convert them from long
to Duration
. Adjust JNI side accordingly.
Fields of interest:
SenderConfig:
ReceiverConfig
Built aar doesn't work
java.lang.UnsatisfiedLinkError: dlopen failed: library "libroc.so.0.1" not found
at java.lang.Runtime.loadLibrary0(Runtime.java:1071)
at java.lang.Runtime.loadLibrary0(Runtime.java:1007)
at java.lang.System.loadLibrary(System.java:1667)
at com.github.rocproject.roc.RocLibrary.loadLibrary(RocLibrary.java:5)
at com.github.rocproject.roc.ChannelSet.<init>(ChannelSet.java:18)
at com.github.rocproject.roc.ChannelSet.<clinit>(ChannelSet.java:14)
how to build:
export JAVA_VERSION=8
export ANDROID_API=28
export ANDROID_BUILD_TOOLS_VERSION=28.0.3
export ANDROID_NDK_VERSION=21.1.6352462
export ROC_BASE_DIR=$HOME/roc-build
./scripts/travis/android/install.sh
./scripts/travis/android/script.sh
how to reproduce:
As a temporary workaround to build arr I changed in https://github.com/roc-project/roc/blob/master/SConstruct#L1079
from
lib_env['SHLIBSUFFIX'] = '%s.%s' % (lib_env['SHLIBSUFFIX'], abi_version)
to
lib_env['SHLIBSUFFIX'] = '%s' % (lib_env['SHLIBSUFFIX'], abi_version)
Numeric value of enum is actually not needed outside of roc-java package.
getValue
method from all enumsvalue
field of all enums from private to package-privatevalue
instead of getValue
everywhere inside roc-javapacketInterleaving should be boolean, not integer.
We need to update RocSenderConfig, RocSenderConfig.Builder, and their methods, as well as the JNI side.
JCenter and bintray are shutting down: https://jfrog.com/blog/into-the-sunset-bintray-jcenter-gocenter-and-chartcenter/
We should migrate to some other service, e.g. Maven Central or JitPack.
See #10 for background.
roc-toolkit 0.2.3 added two new functions:
See API reference for description.
We should add two corresponding methods to bindings:
Comments should be copied and adopted from C header.
We also need to add tests for both successful and unsuccessful invocations of the two newly added methods. No need to test how the option itself work, just a simple "smoke" test.
Actually we are calling AttachCurrentThread
and DetachCurrentThread
inside the logger_handler
function (https://github.com/roc-project/roc-java/blob/master/roc_jni/src/main/cpp/logger.cpp);
As it has been discussed at #18 this can slow down performances.
A more efficient solution would involve detaching thread just before its exit, as it's described at https://developer.android.com/training/articles/perf-jni#threads:
you can use
pthread_key_create()
to define a destructor function that will be called before the thread exits, and callDetachCurrentThread()
from there. (Use that key withpthread_setspecific()
to store theJNIEnv
in thread-local-storage).
C library provides version info: https://roc-streaming.org/toolkit/docs/api/reference.html#roc-version
It would be nice to add this feature to the java library too. We need to provide two versions actually: version of the native library (libroc), and version of the bindings (which may be slightly different).
This issue requires updating both JNI and java parts.
For some reason, when there is a crash in native code, the backtrace does not show symbols in frames in native codes. Neither in console output, nor in gdb when I open core dump.
I checked that both libroc_jni and libroc have debug symbols, that gdb has correct paths to these libs, and tried to force gdb to load symbols from them (using sharedlibrary
command). It did not help.
Without this, it's very hard to debug crashes in native code.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.