diff --git a/RTCUtil.js b/RTCUtil.js index 82b71ec54..6fb829b5c 100644 --- a/RTCUtil.js +++ b/RTCUtil.js @@ -1,5 +1,9 @@ 'use strict'; +import { NativeModules } from 'react-native'; + +const { WebRTCModule } = NativeModules; + /** * Internal util for deep clone object. Object.assign() only does a shallow copy * @@ -33,3 +37,15 @@ export function mergeMediaConstraints(custom, def) { } return constraints; } + +export function enableSoftwareAEC() { + if(WebRTCModule?.enableSoftwareAEC) { + WebRTCModule?.enableSoftwareAEC() + } +} + +export function disableSoftwareAEC() { + if(WebRTCModule?.disableSoftwareAEC) { + WebRTCModule?.disableSoftwareAEC() + } +} diff --git a/android/build.gradle b/android/build.gradle index 84307f59d..69bb10eba 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -28,5 +28,6 @@ android { dependencies { implementation 'com.facebook.react:react-native:+' + api 'com.bugsnag:bugsnag-android:5.2.2' api fileTree(dir: 'libs', include: ['*.jar']) } diff --git a/android/libs/libjingle_peerconnection.so.jar b/android/libs/libjingle_peerconnection.so.jar index f4645ce4f..2a8406027 100644 Binary files a/android/libs/libjingle_peerconnection.so.jar and b/android/libs/libjingle_peerconnection.so.jar differ diff --git a/android/libs/libwebrtc.jar b/android/libs/libwebrtc.jar index 7cdea660a..73d282dfe 100644 Binary files a/android/libs/libwebrtc.jar and b/android/libs/libwebrtc.jar differ diff --git a/android/src/main/java/com/oney/WebRTCModule/ThreadUtils.java b/android/src/main/java/com/oney/WebRTCModule/ThreadUtils.java index c82a29387..627ff196a 100644 --- a/android/src/main/java/com/oney/WebRTCModule/ThreadUtils.java +++ b/android/src/main/java/com/oney/WebRTCModule/ThreadUtils.java @@ -2,6 +2,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import com.bugsnag.android.Bugsnag; final class ThreadUtils { /** @@ -19,4 +20,21 @@ final class ThreadUtils { public static void runOnExecutor(Runnable runnable) { executor.execute(runnable); } + + public static void addExceptionHandlerForThread(Thread.UncaughtExceptionHandler h, String threadName) { + boolean foundAThread = false; + for (Thread t : Thread.getAllStackTraces().keySet()) { + if (t.getName().equals(threadName) && t.isAlive()) { + foundAThread = true; + t.setUncaughtExceptionHandler(h); + } + } + if (!foundAThread) { + try { + Bugsnag.notify(new IllegalAccessError("Thread not found in webrtc: " + threadName)); + } catch (Exception exception) { + exception.printStackTrace(); + } + } + } } diff --git a/android/src/main/java/com/oney/WebRTCModule/UnSurfaceViewRenderer.java b/android/src/main/java/com/oney/WebRTCModule/UnSurfaceViewRenderer.java index 53db466dc..efdef0947 100644 --- a/android/src/main/java/com/oney/WebRTCModule/UnSurfaceViewRenderer.java +++ b/android/src/main/java/com/oney/WebRTCModule/UnSurfaceViewRenderer.java @@ -14,6 +14,7 @@ import java.util.concurrent.CountDownLatch; import org.webrtc.EglBase; +import org.webrtc.EglError; import org.webrtc.EglRenderer; import org.webrtc.EglRenderer.FrameListener; import org.webrtc.GlRectDrawer; @@ -43,9 +44,9 @@ public class UnSurfaceViewRenderer extends SurfaceView implements Callback, Vide private int surfaceWidth; private int surfaceHeight; - public UnSurfaceViewRenderer(Context context) { + public UnSurfaceViewRenderer(Context context, EglError eglError) { super(context); - this.eglRenderer = new EglRenderer(this.resourceName); + this.eglRenderer = new EglRenderer(this.resourceName, eglError); this.getHolder().addCallback(this); // this.setZOrderMediaOverlay(true); // this.getHolder().setFormat(PixelFormat.TRANSPARENT); diff --git a/android/src/main/java/com/oney/WebRTCModule/WebRTCGreenScreenView.java b/android/src/main/java/com/oney/WebRTCModule/WebRTCGreenScreenView.java index 76e328971..324b349ef 100644 --- a/android/src/main/java/com/oney/WebRTCModule/WebRTCGreenScreenView.java +++ b/android/src/main/java/com/oney/WebRTCModule/WebRTCGreenScreenView.java @@ -4,24 +4,29 @@ import android.content.Context; import android.graphics.Color; import android.graphics.Point; -import androidx.core.view.ViewCompat; +import android.util.Log; import android.view.View; import android.view.ViewGroup; -import android.util.Log; +import android.widget.Toast; -import com.facebook.react.bridge.ReactContext; +import androidx.core.view.ViewCompat; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.List; +import com.bugsnag.android.Bugsnag; +import com.bugsnag.android.Severity; +import com.facebook.react.bridge.ReactContext; import org.webrtc.EglBase; +import org.webrtc.EglError; import org.webrtc.MediaStream; import org.webrtc.RendererCommon; import org.webrtc.RendererCommon.RendererEvents; import org.webrtc.RendererCommon.ScalingType; import org.webrtc.VideoTrack; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.List; + public class WebRTCGreenScreenView extends ViewGroup { /** * The scaling type to be utilized by default. @@ -168,7 +173,20 @@ public void run() { public WebRTCGreenScreenView(Context context) { super(context); - surfaceViewRenderer = new UnSurfaceViewRenderer(getContext()); + surfaceViewRenderer = new UnSurfaceViewRenderer(getContext(), new EglError() { + @Override + public void onSurfaceCreationFailed(Exception e) { + Toast.makeText(context, "Error in loading video. Please reopen the class", Toast.LENGTH_LONG).show(); + try { + Bugsnag.notify(e, event -> { + event.setSeverity(Severity.INFO); + return true; + }); + } catch (Exception exception) { + exception.printStackTrace(); + } + } + }); addView(surfaceViewRenderer); setMirror(true); setScalingType(DEFAULT_SCALING_TYPE); diff --git a/android/src/main/java/com/oney/WebRTCModule/WebRTCModule.java b/android/src/main/java/com/oney/WebRTCModule/WebRTCModule.java index 0cc8e1846..75f4a5bd9 100644 --- a/android/src/main/java/com/oney/WebRTCModule/WebRTCModule.java +++ b/android/src/main/java/com/oney/WebRTCModule/WebRTCModule.java @@ -23,15 +23,22 @@ import java.util.Map; import org.webrtc.*; +import org.webrtc.audio.AudioDeviceModule; +import org.webrtc.audio.JavaAudioDeviceModule; +import org.webrtc.voiceengine.WebRtcAudioUtils; @ReactModule(name = "WebRTCModule") public class WebRTCModule extends ReactContextBaseJavaModule { static final String TAG = WebRTCModule.class.getCanonicalName(); PeerConnectionFactory mFactory; + PeerConnectionFactory mFactory1; + private final SparseArray mPeerConnectionObservers; final Map localStreams; + private boolean enableSoftwareBasedNoiseCancellation = false; + /** * The implementation of {@code getUserMedia} extracted into a separate file * in order to reduce complexity and to (somewhat) separate concerns. @@ -77,13 +84,26 @@ private void initAsync() { } mFactory - = PeerConnectionFactory.builder() + = PeerConnectionFactory.builder() + .setVideoEncoderFactory(encoderFactory) + .setVideoDecoderFactory(decoderFactory) + .createPeerConnectionFactory(); + + AudioDeviceModule adm = JavaAudioDeviceModule.builder(reactContext) + .setUseHardwareAcousticEchoCanceler(false) + .setUseHardwareNoiseSuppressor(false) + .createAudioDeviceModule(); + + mFactory1 + = PeerConnectionFactory.builder() + .setAudioDeviceModule(adm) .setVideoEncoderFactory(encoderFactory) .setVideoDecoderFactory(decoderFactory) .createPeerConnectionFactory(); if (eglContext != null) { mFactory.setVideoHwAccelerationOptions(eglContext, eglContext); + mFactory1.setVideoHwAccelerationOptions(eglContext, eglContext); } getUserMediaImpl = new GetUserMediaImpl(this, reactContext); @@ -347,6 +367,22 @@ private PeerConnection.RTCConfiguration parseRTCConfiguration(ReadableMap map) { return conf; } + @ReactMethod + public void enableSoftwareAEC() { + enableSoftwareBasedNoiseCancellation = true; + WebRtcAudioUtils.setWebRtcBasedAutomaticGainControl(true); + WebRtcAudioUtils.setWebRtcBasedAcousticEchoCanceler(true); + WebRtcAudioUtils.setWebRtcBasedNoiseSuppressor(true); + } + + @ReactMethod + public void disableSoftwareAEC() { + enableSoftwareBasedNoiseCancellation = false; + WebRtcAudioUtils.setWebRtcBasedAutomaticGainControl(false); + WebRtcAudioUtils.setWebRtcBasedAcousticEchoCanceler(false); + WebRtcAudioUtils.setWebRtcBasedNoiseSuppressor(false); + } + @ReactMethod public void peerConnectionInit(ReadableMap configuration, int id) { PeerConnection.RTCConfiguration rtcConfiguration @@ -360,8 +396,14 @@ private void peerConnectionInitAsync( PeerConnection.RTCConfiguration configuration, int id) { PeerConnectionObserver observer = new PeerConnectionObserver(this, id); - PeerConnection peerConnection - = mFactory.createPeerConnection(configuration, observer); + + PeerConnection peerConnection; + if(enableSoftwareBasedNoiseCancellation) { + peerConnection = mFactory1.createPeerConnection(configuration, observer); + } else { + peerConnection = mFactory.createPeerConnection(configuration, observer); + } + observer.setPeerConnection(peerConnection); mPeerConnectionObservers.put(id, observer); diff --git a/android/src/main/java/com/oney/WebRTCModule/WebRTCView.java b/android/src/main/java/com/oney/WebRTCModule/WebRTCView.java index d38ade13f..f51c72a28 100644 --- a/android/src/main/java/com/oney/WebRTCModule/WebRTCView.java +++ b/android/src/main/java/com/oney/WebRTCModule/WebRTCView.java @@ -1,6 +1,7 @@ package com.oney.WebRTCModule; import android.annotation.SuppressLint; +import android.content.res.Resources; import android.content.Context; import android.graphics.Color; import android.graphics.Point; @@ -8,7 +9,10 @@ import android.view.View; import android.view.ViewGroup; import android.util.Log; +import android.widget.Toast; +import com.bugsnag.android.Bugsnag; +import com.bugsnag.android.Severity; import com.facebook.react.bridge.ReactContext; import java.lang.reflect.InvocationTargetException; @@ -16,6 +20,7 @@ import java.util.List; import org.webrtc.EglBase; +import org.webrtc.EglError; import org.webrtc.MediaStream; import org.webrtc.RendererCommon; import org.webrtc.RendererCommon.RendererEvents; @@ -168,7 +173,20 @@ public void run() { public WebRTCView(Context context) { super(context); - surfaceViewRenderer = new SurfaceViewRenderer(context); + surfaceViewRenderer = new SurfaceViewRenderer(context, new EglError() { + @Override + public void onSurfaceCreationFailed(Exception e) { + Toast.makeText(context, "Error in loading video. Please reopen the class", Toast.LENGTH_LONG).show(); + try { + Bugsnag.notify(e, event -> { + event.setSeverity(Severity.INFO); + return true; + }); + } catch (Exception exception) { + exception.printStackTrace(); + } + } + }); addView(surfaceViewRenderer); setMirror(false); diff --git a/index.js b/index.js index 4a90b25d9..5032259c6 100644 --- a/index.js +++ b/index.js @@ -9,6 +9,7 @@ import MediaStreamTrack from './MediaStreamTrack'; import mediaDevices from './MediaDevices'; import permissions from './Permissions'; import GreenScreenView from './GreenScreenView' +import { enableSoftwareAEC, disableSoftwareAEC } from './RTCUtil' export { RTCPeerConnection, @@ -19,5 +20,7 @@ export { MediaStreamTrack, mediaDevices, permissions, - GreenScreenView + GreenScreenView, + enableSoftwareAEC, + disableSoftwareAEC, };