From 49bd3f6942e8a1b62f673fa80f6a9c3f6a506cd7 Mon Sep 17 00:00:00 2001 From: Jeff Swartz Date: Mon, 22 Jan 2024 23:53:41 -0800 Subject: [PATCH 1/8] Fix subscribeToSelf --- .../opentokreactnative/OTSessionManager.java | 4 +++- ios/OpenTokReactNative/OTSessionManager.swift | 2 ++ ios/OpenTokReactNative/Utils/EventUtils.swift | 2 +- src/OTSubscriber.js | 22 +++++++++++++++++++ src/helpers/OTSessionHelper.js | 4 ++++ 5 files changed, 32 insertions(+), 2 deletions(-) diff --git a/android/src/main/java/com/opentokreactnative/OTSessionManager.java b/android/src/main/java/com/opentokreactnative/OTSessionManager.java index 3bab921b..4524e9a5 100644 --- a/android/src/main/java/com/opentokreactnative/OTSessionManager.java +++ b/android/src/main/java/com/opentokreactnative/OTSessionManager.java @@ -865,9 +865,10 @@ public void onStreamCreated(PublisherKit publisherKit, Stream stream) { ConcurrentHashMap mSubscriberStreams = sharedState.getSubscriberStreams(); mSubscriberStreams.put(stream.getStreamId(), stream); if (publisherId.length() > 0) { - String event = publisherId + ":" + publisherPreface + "onStreamCreated";; + String event = publisherId + ":" + publisherPreface + "onStreamCreated"; WritableMap streamInfo = EventUtils.prepareJSStreamMap(stream, publisherKit.getSession()); sendEventMap(this.getReactApplicationContext(), event, streamInfo); + sendEventMap(this.getReactApplicationContext(), "publisherStreamCreated", streamInfo); } printLogs("onStreamCreated: Publisher Stream Created. Own stream "+stream.getStreamId()); @@ -884,6 +885,7 @@ public void onStreamDestroyed(PublisherKit publisherKit, Stream stream) { if (publisherId.length() > 0) { WritableMap streamInfo = EventUtils.prepareJSStreamMap(stream, publisherKit.getSession()); sendEventMap(this.getReactApplicationContext(), event, streamInfo); + sendEventMap(this.getReactApplicationContext(), "publisherStreamDestroyed", streamInfo); } Callback mCallback = sharedState.getPublisherDestroyedCallbacks().get(publisherId); if (mCallback != null) { diff --git a/ios/OpenTokReactNative/OTSessionManager.swift b/ios/OpenTokReactNative/OTSessionManager.swift index 30966d9a..575654ee 100644 --- a/ios/OpenTokReactNative/OTSessionManager.swift +++ b/ios/OpenTokReactNative/OTSessionManager.swift @@ -615,6 +615,7 @@ extension OTSessionManager: OTPublisherDelegate { OTRN.sharedState.isPublishing[publisherId] = true; let streamInfo: Dictionary = EventUtils.prepareJSStreamEventData(stream); self.emitEvent("\(publisherId):\(EventUtils.publisherPreface)streamCreated", data: streamInfo); + self.emitEvent("publisherStreamCreated", data: streamInfo); setStreamObservers(stream: stream, isPublisherStream: true) } printLogs("OTRN: Publisher Stream created") @@ -630,6 +631,7 @@ extension OTSessionManager: OTPublisherDelegate { OTRN.sharedState.isPublishing[publisherId] = false; let streamInfo: Dictionary = EventUtils.prepareJSStreamEventData(stream); self.emitEvent("\(publisherId):\(EventUtils.publisherPreface)streamDestroyed", data: streamInfo); + self.emitEvent("publisherStreamDestroyed", data: streamInfo); } OTRN.sharedState.publishers[publisherId] = nil; OTRN.sharedState.isPublishing[publisherId] = nil; diff --git a/ios/OpenTokReactNative/Utils/EventUtils.swift b/ios/OpenTokReactNative/Utils/EventUtils.swift index 6cde39e7..fd7c2ca1 100644 --- a/ios/OpenTokReactNative/Utils/EventUtils.swift +++ b/ios/OpenTokReactNative/Utils/EventUtils.swift @@ -124,7 +124,7 @@ class EventUtils { } static func getSupportedEvents() -> [String] { - return ["\(sessionPreface)streamCreated", "\(sessionPreface)streamDestroyed", "\(sessionPreface)sessionDidConnect", "\(sessionPreface)sessionDidDisconnect", "\(sessionPreface)connectionCreated", "\(sessionPreface)connectionDestroyed", "\(sessionPreface)didFailWithError", "\(publisherPreface)streamCreated", "\(sessionPreface)signal", "\(publisherPreface)streamDestroyed", "\(publisherPreface)didFailWithError", "\(publisherPreface)audioLevelUpdated", "\(publisherPreface)rtcStatsReport", "\(subscriberPreface)subscriberDidConnect", "\(subscriberPreface)subscriberDidDisconnect", "\(subscriberPreface)didFailWithError", "\(subscriberPreface)videoNetworkStatsUpdated", "\(subscriberPreface)audioNetworkStatsUpdated", "\(subscriberPreface)audioLevelUpdated", "\(subscriberPreface)subscriberVideoEnabled", "\(subscriberPreface)subscriberVideoDisabled", "\(subscriberPreface)subscriberVideoDisableWarning", "\(subscriberPreface)subscriberVideoDisableWarningLifted", "\(subscriberPreface)subscriberVideoDataReceived", "\(sessionPreface)archiveStartedWithId", "\(sessionPreface)archiveStoppedWithId", "\(sessionPreface)sessionDidBeginReconnecting", "\(sessionPreface)sessionDidReconnect", "\(sessionPreface)streamPropertyChanged", "\(subscriberPreface)subscriberDidReconnect", "\(subscriberPreface)subscriberCaptionReceived"]; + return ["\(sessionPreface)streamCreated", "\(sessionPreface)streamDestroyed", "\(sessionPreface)sessionDidConnect", "\(sessionPreface)sessionDidDisconnect", "\(sessionPreface)connectionCreated", "\(sessionPreface)connectionDestroyed", "\(sessionPreface)didFailWithError", "\(publisherPreface)streamCreated", "\(sessionPreface)signal", "\(publisherPreface)streamDestroyed", "\(publisherPreface)didFailWithError", "\(publisherPreface)audioLevelUpdated", "\(publisherPreface)rtcStatsReport", "publisherStreamCreated", "publisherStreamDestroyed", "\(subscriberPreface)subscriberDidConnect", "\(subscriberPreface)subscriberDidDisconnect", "\(subscriberPreface)didFailWithError", "\(subscriberPreface)videoNetworkStatsUpdated", "\(subscriberPreface)audioNetworkStatsUpdated", "\(subscriberPreface)audioLevelUpdated", "\(subscriberPreface)subscriberVideoEnabled", "\(subscriberPreface)subscriberVideoDisabled", "\(subscriberPreface)subscriberVideoDisableWarning", "\(subscriberPreface)subscriberVideoDisableWarningLifted", "\(subscriberPreface)subscriberVideoDataReceived", "\(sessionPreface)archiveStartedWithId", "\(sessionPreface)archiveStoppedWithId", "\(sessionPreface)sessionDidBeginReconnecting", "\(sessionPreface)sessionDidReconnect", "\(sessionPreface)streamPropertyChanged", "\(subscriberPreface)subscriberDidReconnect", "\(subscriberPreface)subscriberCaptionReceived"]; } static func convertDateToString(_ creationTime: Date) -> String { diff --git a/src/OTSubscriber.js b/src/OTSubscriber.js index a0564851..4e8aff9f 100644 --- a/src/OTSubscriber.js +++ b/src/OTSubscriber.js @@ -19,6 +19,8 @@ export default class OTSubscriber extends Component { streamDestroyed: Platform.OS === 'android' ? 'session:onStreamDropped' : 'session:streamDestroyed', streamCreated: Platform.OS === 'android' ? 'session:onStreamReceived' : 'session:streamCreated', captionReceived: Platform.OS === 'android' ? 'session:onCaptionText' : 'subscriber:subscriberCaptionReceived:', + publisherStreamCreated: 'publisherStreamCreated', + publisherStreamDestroyed: 'publisherStreamDestroyed' }; this.componentEventsArray = Object.values(this.componentEvents); this.otrnEventHandler = getOtrnErrorEventHandler(this.props.eventHandlers); @@ -36,6 +38,14 @@ export default class OTSubscriber extends Component { OT.setJSComponentEvents(this.componentEventsArray); setNativeEvents(subscriberEvents); } + this.publisherStreamCreated = nativeEvents.addListener( + 'publisherStreamCreated', + stream => this.publisherStreamCreatedHandler(stream) + ); + this.publisherStreamDestroyed = nativeEvents.addListener( + 'publisherStreamDestroyed', + stream => this.publisherStreamDestroyedHandler(stream) + ); } componentDidUpdate() { const { streamProperties } = this.props; @@ -67,6 +77,8 @@ export default class OTSubscriber extends Component { componentWillUnmount() { this.streamCreated.remove(); this.streamDestroyed.remove(); + this.publisherStreamCreated.remove(); + this.publisherStreamDestroyed.remove(); OT.removeJSComponentEvents(this.componentEventsArray); const events = sanitizeSubscriberEvents(this.props.eventHandlers); removeNativeEvents(events); @@ -106,6 +118,16 @@ export default class OTSubscriber extends Component { } }); } + publisherStreamCreatedHandler = (stream) => { + if (this.state.subscribeToSelf) { + this.streamCreatedHandler(stream); + } + } + publisherStreamDestroyedHandler = (stream) => { + if (this.state.subscribeToSelf) { + this.streamDestroyedHandler(stream); + } + } getRtcStatsReport(streamId) { OT.getSubscriberRtcStatsReport(streamId); } diff --git a/src/helpers/OTSessionHelper.js b/src/helpers/OTSessionHelper.js index 6d38763a..63e04fd6 100644 --- a/src/helpers/OTSessionHelper.js +++ b/src/helpers/OTSessionHelper.js @@ -39,6 +39,8 @@ const sanitizeSessionEvents = (sessionId, events) => { archiveStopped: 'archiveStoppedWithId', streamPropertyChanged: 'streamPropertyChanged', muteForced: 'muteForced', + publisherStreamCreated: 'publisherStreamCreated', + publisherStreamDestroyed: 'publisherStreamDestroyed', }, android: { streamCreated: 'onStreamReceived', @@ -55,6 +57,8 @@ const sanitizeSessionEvents = (sessionId, events) => { archiveStopped: 'onArchiveStopped', streamPropertyChanged: 'onStreamPropertyChanged', muteForced: 'onMuteForced', + publisherStreamCreated: 'publisherStreamCreated', + publisherStreamDestroyed: 'publisherStreamDestroyed', } }; return reassignEvents('session', customEvents, events, sessionId); From 1c66c27a9c0ef68891cf7ab86592b887414d196e Mon Sep 17 00:00:00 2001 From: Jeff Swartz Date: Thu, 14 Mar 2024 11:06:42 -0700 Subject: [PATCH 2/8] Update version and docs --- CHANGELOG.md | 4 ++++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e608a93f..ba8a38ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 2.27.2 (April 2023) + +- [Fix] The `subscribeToSelf` prop of the OTSubscriber component was not working. This version fixes the issue (issue #612). + # 2.26.1 (October 2023) - [Update]: The new `OTPublisher.setVideoTransformers()` method lets you set (and clear) diff --git a/package-lock.json b/package-lock.json index 6bfb8740..47505dba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "opentok-react-native", - "version": "2.26.1", + "version": "2.27.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "opentok-react-native", - "version": "2.26.1", + "version": "2.27.2", "license": "MIT", "dependencies": { "axios": "^0.21.1", diff --git a/package.json b/package.json index 5be70640..509d24f2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "opentok-react-native", - "version": "2.26.1", + "version": "2.27.2", "description": "React Native components for OpenTok iOS and Android SDKs", "main": "src/index.js", "homepage": "https://www.tokbox.com", From 27a611b801e2056cef710114af43921b753f7557 Mon Sep 17 00:00:00 2001 From: Jeff Swartz Date: Sun, 5 May 2024 22:46:46 -0700 Subject: [PATCH 3/8] Fix OTSubscriber.getRtcStatsReport() (#721) * Fix OTSubscriber.getRtcStatsReport() * Fix iOS getSubscriberRtcStatsReport implementation * minor docs edit * Rev version to 2.27.5 --- @types/index.d.ts | 6 +++--- CHANGELOG.md | 4 ++++ .../main/java/com/opentokreactnative/OTSessionManager.java | 6 +++--- ios/OpenTokReactNative/OTSessionManager.m | 3 +-- ios/OpenTokReactNative/OTSessionManager.swift | 7 ++++--- package-lock.json | 4 ++-- package.json | 2 +- src/OTSubscriber.js | 4 ++-- 8 files changed, 20 insertions(+), 16 deletions(-) diff --git a/@types/index.d.ts b/@types/index.d.ts index b96dc71a..ed6c5c63 100644 --- a/@types/index.d.ts +++ b/@types/index.d.ts @@ -671,9 +671,9 @@ declare module "opentok-react-native" { */ export class OTSubscriber extends React.Component { /** - * Gets the RTC stats report for the subscriber. This is an asynchronous operation. - * The OTSubscriber object dispatches an rtcStatsReport event when RTC statistics for - * the publisher are available. + * Gets the RTC stats report for the subscribers. This is an asynchronous operation. + * The OTSubscriber object dispatches rtcStatsReport events when RTC statistics for + * the subscribers are available. */ getRtcStatsReport: () => void; } diff --git a/CHANGELOG.md b/CHANGELOG.md index 75b6ab8f..7cd7fb73 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 2.27.5 (May 2024) + +- [Fix]: Calling `OTSubscriber.getRtcStatsReport()` method was resulting in an error. This version fixes the issue. + # 2.27.4 (April 2024) - [Update]: This version updates the Vonage Video iOS SDK version to 2.27.3. This version adds a [privacy manifest required by Apple's App store](https://developer.apple.com/support/third-party-SDK-requirements). Issue #737. diff --git a/android/src/main/java/com/opentokreactnative/OTSessionManager.java b/android/src/main/java/com/opentokreactnative/OTSessionManager.java index 226206b9..16cc1c9b 100644 --- a/android/src/main/java/com/opentokreactnative/OTSessionManager.java +++ b/android/src/main/java/com/opentokreactnative/OTSessionManager.java @@ -494,11 +494,11 @@ public void setAudioVolume(String streamId, Float audioVolume) { } @ReactMethod - public void getSubscriberRtcStatsReport(String streamId) { + public void getSubscriberRtcStatsReport() { ConcurrentHashMap mSubscribers = sharedState.getSubscribers(); - Subscriber mSubscriber = mSubscribers.get(streamId); - if (mSubscriber != null) { + ArrayList mSubscriberList = new ArrayList<>(mSubscribers.values()); + for (Subscriber mSubscriber : mSubscriberList) { mSubscriber.getRtcStatsReport(); } } diff --git a/ios/OpenTokReactNative/OTSessionManager.m b/ios/OpenTokReactNative/OTSessionManager.m index b12469e5..9b923ce2 100644 --- a/ios/OpenTokReactNative/OTSessionManager.m +++ b/ios/OpenTokReactNative/OTSessionManager.m @@ -52,8 +52,7 @@ @interface RCT_EXTERN_MODULE(OTSessionManager, RCTEventEmitter) pubAudio:(BOOL)publishCaptions) RCT_EXTERN_METHOD(getRtcStatsReport: (NSString*)publisherId) -RCT_EXTERN_METHOD(getSubscriberRtcStatsReport: - (NSString*)subscriberId) +RCT_EXTERN_METHOD(getSubscriberRtcStatsReport) RCT_EXTERN_METHOD(subscribeToAudio: (NSString*)streamId subAudio:(BOOL)subAudio) diff --git a/ios/OpenTokReactNative/OTSessionManager.swift b/ios/OpenTokReactNative/OTSessionManager.swift index eb86832b..7888daf7 100644 --- a/ios/OpenTokReactNative/OTSessionManager.swift +++ b/ios/OpenTokReactNative/OTSessionManager.swift @@ -252,9 +252,10 @@ class OTSessionManager: RCTEventEmitter { subscriber.audioVolume = audioVolume; } - @objc func getSubscriberRtcStatsReport(_ streamId: String) -> Void { - guard let subscriber = OTRN.sharedState.subscribers[streamId] else { return } - subscriber.getRtcStatsReport() + @objc func getSubscriberRtcStatsReport() -> Void { + for subscriber in OTRN.sharedState.subscribers { + subscriber.value.getRtcStatsReport() + } } @objc func changeCameraPosition(_ publisherId: String, cameraPosition: String) -> Void { diff --git a/package-lock.json b/package-lock.json index 9a4810bb..de92526f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "opentok-react-native", - "version": "2.27.4", + "version": "2.27.5", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "opentok-react-native", - "version": "2.27.4", + "version": "2.27.5", "license": "MIT", "dependencies": { "axios": "^1.6.8", diff --git a/package.json b/package.json index af31c5ce..fe970081 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "opentok-react-native", - "version": "2.27.4", + "version": "2.27.5", "description": "React Native components for OpenTok iOS and Android SDKs", "main": "src/index.js", "homepage": "https://www.tokbox.com", diff --git a/src/OTSubscriber.js b/src/OTSubscriber.js index a0564851..e0ad50c0 100644 --- a/src/OTSubscriber.js +++ b/src/OTSubscriber.js @@ -106,8 +106,8 @@ export default class OTSubscriber extends Component { } }); } - getRtcStatsReport(streamId) { - OT.getSubscriberRtcStatsReport(streamId); + getRtcStatsReport() { + OT.getSubscriberRtcStatsReport(); } render() { if (!this.props.children) { From a9d767f6a5e3e14cca594bf0977ba6e1bdea11a0 Mon Sep 17 00:00:00 2001 From: Jeff Swartz Date: Wed, 22 May 2024 09:58:18 -0700 Subject: [PATCH 4/8] Adding system requirements info to the README (#752) --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 666832b8..8bf796c7 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,10 @@ In this repo, you'll find the OpenTok React Native library. 3. Install and update [Android Studio](https://developer.android.com/studio/index.html). (See the React Native Android installation [instructions](https://facebook.github.io/react-native/docs/getting-started.html).) +## System requirements + +See the system requirements for the [OpenTok Android SDK](https://tokbox.com/developer/sdks/android/#requirements) and [OpenTok iOS SDK](https://tokbox.com/developer/sdks/ios/#system-requirements). (The OpenTok React Native SDK has the same requirements for Android and iOS.) + ## Installation 1. In your terminal, change into your React Native project's directory. From 187a4b699f00ad019fbfb89977c36fc835e8b188 Mon Sep 17 00:00:00 2001 From: Jeff Swartz Date: Tue, 4 Jun 2024 18:59:56 -0700 Subject: [PATCH 5/8] Disable enableStereoOutput in Android (#751) The custom audio driver (used in Android when the `enableStereoOutput` option is set) is broken. --- CHANGELOG.md | 3 + .../OTCustomAudioDevice.java | 1146 ----------------- .../opentokreactnative/OTSessionManager.java | 5 - 3 files changed, 3 insertions(+), 1151 deletions(-) delete mode 100644 android/src/main/java/com/opentokreactnative/OTCustomAudioDevice.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 079d491a..3eab19e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,11 @@ - [Fix]: Calling `OTSubscriber.getRtcStatsReport()` method was resulting in an error. This version fixes the issue. +- [Fix]: Setting the `enableStereoOutput` option of the OTSession component was causing apps to crash in Android. The custom audio driver (used in Android when the `enableStereoOutput` option is set) is broken. This version disables the `enableStereoOutput` option in Android. + - [Fix] The `subscribeToSelf` prop of the OTSubscriber component was not working. This version fixes the issue (issue #612). + # 2.27.4 (April 2024) - [Update]: This version updates the Vonage Video iOS SDK version to 2.27.3. This version adds a [privacy manifest required by Apple's App store](https://developer.apple.com/support/third-party-SDK-requirements). Issue #737. diff --git a/android/src/main/java/com/opentokreactnative/OTCustomAudioDevice.java b/android/src/main/java/com/opentokreactnative/OTCustomAudioDevice.java deleted file mode 100644 index 7ffd5062..00000000 --- a/android/src/main/java/com/opentokreactnative/OTCustomAudioDevice.java +++ /dev/null @@ -1,1146 +0,0 @@ -package com.opentokreactnative; - -import android.bluetooth.BluetoothAdapter; -import android.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothHeadset; -import android.bluetooth.BluetoothProfile; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.media.AudioFormat; -import android.media.AudioManager; -import android.media.AudioRecord; -import android.media.AudioTrack; -import android.media.MediaRecorder.AudioSource; -import android.media.audiofx.AcousticEchoCanceler; -import android.media.audiofx.NoiseSuppressor; -import android.os.Build; -import android.os.Handler; -import android.telephony.PhoneStateListener; -import android.telephony.TelephonyManager; -import android.util.Log; - -import com.opentok.android.BaseAudioDevice; - -import java.nio.ByteBuffer; -import java.util.List; -import java.util.Locale; -import java.util.concurrent.locks.Condition; -import java.util.concurrent.locks.ReentrantLock; - -class OTCustomAudioDriver extends BaseAudioDevice { - private final static String TAG = OTCustomAudioDriver.class.getSimpleName(); - - private static final int NUM_CHANNELS_CAPTURING = 1; - private static final int NUM_CHANNELS_RENDERING = 2; - private static final int STEREO_CHANNELS = 2; - private static final int DEFAULT_SAMPLE_RATE = 44100; - private static final int SAMPLE_SIZE_IN_BYTES = 2; - private static final int DEFAULT_SAMPLES_PER_BUFFER = (DEFAULT_SAMPLE_RATE / 1000) * 10; // 10ms - private static final int DEFAULT_BUFFER_SIZE = - SAMPLE_SIZE_IN_BYTES * DEFAULT_SAMPLES_PER_BUFFER * STEREO_CHANNELS; - // Max 10 ms @ 48 kHz - Stereo - private static final int DEFAULT_START_RENDERER_AND_CAPTURER_DELAY = 5 * 1000; - private static final int DEFAULT_BLUETOOTH_SCO_START_DELAY = 2000; - - private Context context; - - private AudioTrack audioTrack; - private AudioRecord audioRecord; - - // Capture & render buffers - private ByteBuffer playBuffer; - private ByteBuffer recBuffer; - private byte[] tempBufPlay; - private byte[] tempBufRec; - - private final ReentrantLock rendererLock = new ReentrantLock(true); - private final Condition renderEvent = rendererLock.newCondition(); - private volatile boolean isRendering = false; - private volatile boolean shutdownRenderThread = false; - - private final ReentrantLock captureLock = new ReentrantLock(true); - private final Condition captureEvent = captureLock.newCondition(); - private volatile boolean isCapturing = false; - private volatile boolean shutdownCaptureThread = false; - - private AudioSettings captureSettings; - private AudioSettings rendererSettings; - private NoiseSuppressor noiseSuppressor; - private AcousticEchoCanceler echoCanceler; - - // Capturing delay estimation - private int estimatedCaptureDelay = 0; - - // Rendering delay estimation - private int bufferedPlaySamples = 0; - private int playPosition = 0; - private int estimatedRenderDelay = 0; - - private AudioManager audioManager; - private AudioManagerMode audioManagerMode = new AudioManagerMode(); - - private int outputSamplingRate = DEFAULT_SAMPLE_RATE; - private int captureSamplingRate = DEFAULT_SAMPLE_RATE; - private int samplesPerBuffer = DEFAULT_SAMPLES_PER_BUFFER; - - // For headset receiver. - private static final String HEADSET_PLUG_STATE_KEY = "state"; - - private BluetoothState bluetoothState; - private BluetoothAdapter bluetoothAdapter; - private BluetoothProfile bluetoothProfile; - private final Object bluetoothLock = new Object(); - private TelephonyManager telephonyManager; - - private boolean isPaused; - - private enum OutputType { - SPEAKER_PHONE, - EAR_PIECE, - HEAD_PHONES, - BLUETOOTH - } - - private OutputType audioOutputType = OutputType.SPEAKER_PHONE; - - private OutputType getOutputType() { - return audioOutputType; - } - - private void setOutputType(OutputType type) { - audioOutputType = type; - } - - private static class AudioManagerMode { - private int oldMode; - private int naquire; - - AudioManagerMode() { - oldMode = 0; - naquire = 0; - } - - void acquireMode(AudioManager audioManager) { - if (0 == naquire++) { - oldMode = audioManager.getMode(); - audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION); - } - } - - void releaseMode(AudioManager audioManager) { - if (0 == --naquire) { - audioManager.setMode(oldMode); - } - } - } - - private static class AudioState { - private int lastStreamVolume = 0; - private int lastKnownFocusState = 0; - private OutputType lastOutputType = OutputType.SPEAKER_PHONE; - - int getLastStreamVolume() { - return lastStreamVolume; - } - - void setLastStreamVolume(int lastStreamVolume) { - this.lastStreamVolume = lastStreamVolume; - } - - int getLastKnownFocusState() { - return lastKnownFocusState; - } - - void setLastKnownFocusState(int lastKnownFocusState) { - this.lastKnownFocusState = lastKnownFocusState; - } - - OutputType getLastOutputType() { - return this.lastOutputType; - } - - void setLastOutputType(OutputType lastOutputType) { - this.lastOutputType = lastOutputType; - } - - } - - private AudioState audioState = new AudioState(); - - private BroadcastReceiver headsetBroadcastReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - Log.d(TAG, "headsetBroadcastReceiver.onReceive()"); - if (intent.getAction().equals(Intent.ACTION_HEADSET_PLUG)) { - if (intent.getIntExtra(HEADSET_PLUG_STATE_KEY, 0) == 1) { - Log.d(TAG, "headsetBroadcastReceiver.onReceive(): Headphones connected"); - audioState.setLastOutputType(getOutputType()); - setOutputType(OutputType.HEAD_PHONES); - audioManager.setSpeakerphoneOn(false); - audioManager.setBluetoothScoOn(false); - } else { - Log.d(TAG, "headsetBroadcastReceiver.onReceive(): Headphones disconnected"); - if (getOutputType() == OutputType.HEAD_PHONES) { - if (audioState.getLastOutputType() == OutputType.BLUETOOTH && - BluetoothState.Connected == bluetoothState) { - audioManager.setBluetoothScoOn(true); - startBluetoothSco(); - setOutputType(OutputType.BLUETOOTH); - } else { - if (audioState.getLastOutputType() == OutputType.SPEAKER_PHONE) { - setOutputType(OutputType.SPEAKER_PHONE); - audioManager.setSpeakerphoneOn(true); - } - if (audioState.getLastOutputType() == OutputType.EAR_PIECE) { - setOutputType(OutputType.EAR_PIECE); - audioManager.setSpeakerphoneOn(false); - } - } - } - } - } - } - }; - - // Intent broadcast receiver which handles changes in Bluetooth device availability. - // Detects headset changes and Bluetooth SCO state changes. - private final BroadcastReceiver bluetoothHeadsetReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - final String action = intent.getAction(); - if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) { - final int state = intent.getIntExtra( - BluetoothHeadset.EXTRA_STATE, -1); - switch (state) { - case BluetoothHeadset.STATE_AUDIO_DISCONNECTED: - Log.d(TAG, "bluetoothHeadsetReceiver.onReceive(): STATE_AUDIO_DISCONNECTED"); - break; - - case BluetoothHeadset.STATE_AUDIO_CONNECTING: - Log.d(TAG, "bluetoothHeadsetReceiver.onReceive(): STATE_AUDIO_CONNECTING"); - break; - - case BluetoothHeadset.STATE_AUDIO_CONNECTED: - Log.d(TAG, "bluetoothHeadsetReceiver.onReceive(): STATE_AUDIO_CONNECTED"); - break; - - default: - break; - } - } - } - }; - - private void restoreAudioAfterBluetoothDisconnect() { - if (audioManager.isWiredHeadsetOn()) { - setOutputType(OutputType.HEAD_PHONES); - audioManager.setSpeakerphoneOn(false); - } else { - if (audioState.getLastOutputType() == OutputType.SPEAKER_PHONE) { - setOutputType(OutputType.SPEAKER_PHONE); - super.setOutputMode(OutputMode.SpeakerPhone); - audioManager.setSpeakerphoneOn(true); - } else if (audioState.getLastOutputType() == OutputType.EAR_PIECE) { - setOutputType(OutputType.EAR_PIECE); - super.setOutputMode(OutputMode.Handset); - audioManager.setSpeakerphoneOn(false); - } - } - } - - private final BroadcastReceiver bluetoothBroadcastReceiver = new BroadcastReceiver() { - public void onReceive(Context context, Intent intent) { - final String action = intent.getAction(); - if (null != action && action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) { - int state = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE, -1); - switch (state) { - case BluetoothHeadset.STATE_CONNECTED: - Log.d(TAG, "bluetoothBroadcastReceiver.onReceive(): BluetoothHeadset.STATE_CONNECTED"); - new Handler().postDelayed(() -> connectBluetooth(), DEFAULT_BLUETOOTH_SCO_START_DELAY); - break; - case BluetoothHeadset.STATE_DISCONNECTING: - Log.d(TAG, "bluetoothBroadcastReceiver.onReceive(): BluetoothHeadset.STATE_DISCONNECTING"); - break; - case BluetoothHeadset.STATE_DISCONNECTED: - Log.d(TAG, "bluetoothBroadcastReceiver.onReceive(): BluetoothHeadset.STATE_DISCONNECTED"); - stopBluetoothSco(); - audioManager.setBluetoothScoOn(false); - break; - default: - break; - } - } else if (null != action && action.equals(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED)) { - int state = intent.getIntExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, -1); - switch (state) { - case AudioManager.SCO_AUDIO_STATE_CONNECTED: - Log.d(TAG, "bluetoothBroadcastReceiver.onReceive(): AudioManager.SCO_AUDIO_STATE_CONNECTED"); - bluetoothState = BluetoothState.Connected; - setOutputType(OutputType.BLUETOOTH); - OTCustomAudioDriver.super.setOutputMode(OutputMode.Handset); // When BT is connected it replaces the handset - break; - case AudioManager.SCO_AUDIO_STATE_ERROR: - Log.d(TAG, "bluetoothBroadcastReceiver.onReceive(): AudioManager.SCO_AUDIO_STATE_ERROR"); - break; - case AudioManager.SCO_AUDIO_STATE_DISCONNECTED: - Log.d(TAG, "bluetoothBroadcastReceiver.onReceive(): AudioManager.SCO_AUDIO_STATE_DISCONNECTED"); - restoreAudioAfterBluetoothDisconnect(); - bluetoothState = BluetoothState.Disconnected; - break; - case AudioManager.SCO_AUDIO_STATE_CONNECTING: - Log.d(TAG, "bluetoothBroadcastReceiver.onReceive(): AudioManager.SCO_AUDIO_STATE_CONNECTING"); - break; - default: - break; - } - } - } - }; - private PhoneStateListener phoneStateListener = new PhoneStateListener(){ - @Override - public void onCallStateChanged(int state, String incomingNumber) { - Log.d(TAG, "PhoneStateListener.onCallStateChanged()"); - - super.onCallStateChanged(state, incomingNumber); - switch (state) { - - case TelephonyManager.CALL_STATE_IDLE: - //Initial state - Log.d(TAG, "PhoneStateListener.onCallStateChanged(): TelephonyManager.CALL_STATE_IDLE"); - // We delay a bit here the action of start capturing and rendering again because Android has to - // finish routing audio to the earpiece. It is an Android behaviour we have to deal with. - new Handler().postDelayed(() -> startRendererAndCapturer(), DEFAULT_START_RENDERER_AND_CAPTURER_DELAY); - break; - - case TelephonyManager.CALL_STATE_RINGING: - // Incoming call Ringing - Log.d(TAG, "PhoneStateListener.onCallStateChanged(): TelephonyManager.CALL_STATE_RINGING"); - stopRendererAndCapturer(); - break; - - case TelephonyManager.CALL_STATE_OFFHOOK: - // Outgoing Call | Accepted incoming call - Log.d(TAG, "PhoneStateListener.onCallStateChanged(): TelephonyManager.CALL_STATE_OFFHOOK"); - stopRendererAndCapturer(); - break; - - default: - Log.d(TAG, "PhoneStateListener.onCallStateChanged() default"); - break; - } - } - }; - - private boolean wasRendering; - private boolean wasCapturing; - - private void startRendererAndCapturer() { - if (wasRendering) { - startRenderer(); - } - - if (wasCapturing) { - startCapturer(); - } - } - - private void stopRendererAndCapturer() { - if (isRendering) { - stopRenderer(); - wasRendering = true; - } - - if (isCapturing) { - stopCapturer(); - wasCapturing = true; - } - } - - private AudioManager.OnAudioFocusChangeListener audioFocusChangeListener = new AudioManager.OnAudioFocusChangeListener() { - @Override - public void onAudioFocusChange(int focusChange) { - Log.d(TAG, "AudioManager.OnAudioFocusChangeListener.onAudioFocusChange(" + focusChange + ")"); - switch (focusChange) { - case AudioManager.AUDIOFOCUS_GAIN: - Log.d(TAG, "AudioManager.OnAudioFocusChangeListener.onAudioFocusChange(" + focusChange + "): "); - //Check if coming back from a complete loss or a transient loss - switch (audioState.getLastKnownFocusState()) { - case AudioManager.AUDIOFOCUS_LOSS: - break; - case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: - break; - case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: - audioManager.setStreamVolume(AudioManager.STREAM_VOICE_CALL, - audioState.getLastStreamVolume(), 0); - break; - default: - Log.d(TAG, "focusChange = " + focusChange); - break; - } - setOutputType(audioState.getLastOutputType()); - connectBluetooth(); - forceInvokeConnectBluetooth(); - break; - - case AudioManager.AUDIOFOCUS_LOSS: - // -1 Loss for indefinite time - Log.d(TAG, "AudioManager.OnAudioFocusChangeListener.onAudioFocusChange(" + focusChange + "): AudioManager.AUDIOFOCUS_LOSS"); - break; - case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: - // -2 Loss for short duration - Log.d(TAG, "AudioManager.OnAudioFocusChangeListener.onAudioFocusChange(" + focusChange + "): AudioManager.AUDIOFOCUS_LOSS_TRANSIENT"); - break; - case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: - // -3 stay quite in background - Log.d(TAG, "AudioManager.OnAudioFocusChangeListener.onAudioFocusChange(" + focusChange + "): AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK"); - audioState.setLastStreamVolume(audioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL)); - audioManager.setStreamVolume(AudioManager.STREAM_VOICE_CALL, 0, 0); - break; - case AudioManager.AUDIOFOCUS_NONE: - Log.d(TAG, "AudioManager.OnAudioFocusChangeListener.onAudioFocusChange(" + focusChange + "): AudioManager.AUDIOFOCUS_NONE"); - break; - default: - Log.d(TAG, "AudioManager.OnAudioFocusChangeListener.onAudioFocusChange(" + focusChange + "): default"); - break; - } - audioState.setLastOutputType(getOutputType()); - audioState.setLastKnownFocusState(focusChange); - } - }; - - private void connectBluetooth() { - Log.d(TAG, "connectBluetooth() called"); - audioManager.setBluetoothScoOn(true); - startBluetoothSco(); - } - - public OTCustomAudioDriver(Context context) { - this.context = context; - - try { - recBuffer = ByteBuffer.allocateDirect(DEFAULT_BUFFER_SIZE); - } catch (Exception e) { - Log.e(TAG, e.getMessage()); - } - tempBufRec = new byte[DEFAULT_BUFFER_SIZE]; - - audioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE); - bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); - bluetoothProfile = null; - - int outputBufferSize = DEFAULT_BUFFER_SIZE; - - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN) { - try { - outputSamplingRate = Integer.parseInt( - audioManager.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE)); - } finally { - if (outputSamplingRate == 0) { - outputSamplingRate = DEFAULT_SAMPLE_RATE; - } - } - try { - samplesPerBuffer = Integer.parseInt( - audioManager.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER)); - outputBufferSize = SAMPLE_SIZE_IN_BYTES - * samplesPerBuffer - * NUM_CHANNELS_RENDERING; - } catch(NumberFormatException numberFormatException) { - Log.e(TAG, "DefaultAudioDevice(): " + numberFormatException.getMessage()); - } finally { - if (outputBufferSize == 0) { - outputBufferSize = DEFAULT_BUFFER_SIZE; - samplesPerBuffer = DEFAULT_SAMPLES_PER_BUFFER; - } - } - } - - try { - playBuffer = ByteBuffer.allocateDirect(outputBufferSize); - } catch (Exception e) { - Log.e(TAG, e.getMessage()); - } - - tempBufPlay = new byte[outputBufferSize]; - - captureSettings = new AudioSettings(captureSamplingRate, NUM_CHANNELS_CAPTURING); - rendererSettings = new AudioSettings(outputSamplingRate, NUM_CHANNELS_RENDERING); - - try { - telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); - } catch (SecurityException e) { - e.printStackTrace(); - } - - isPhoneStateListenerRegistered = false; - wasCapturing = false; - wasRendering = false; - isPaused = false; - Log.d(TAG, "DefaultAudioDevice() exit " + this); - - } - - @Override - public boolean initCapturer() { - // get the minimum buffer size that can be used - int minRecBufSize = AudioRecord.getMinBufferSize( - captureSettings.getSampleRate(), - NUM_CHANNELS_CAPTURING == 1 ? AudioFormat.CHANNEL_IN_MONO : AudioFormat.CHANNEL_IN_STEREO, - AudioFormat.ENCODING_PCM_16BIT - ); - - // double size to be more safe - int recBufSize = minRecBufSize * 2; - - // release the object - if (noiseSuppressor != null) { - noiseSuppressor.release(); - noiseSuppressor = null; - } - - if (echoCanceler != null) { - echoCanceler.release(); - echoCanceler = null; - } - - if (audioRecord != null) { - audioRecord.release(); - audioRecord = null; - } - - try { - int channelConfig = NUM_CHANNELS_CAPTURING == 1 ? AudioFormat.CHANNEL_IN_MONO : AudioFormat.CHANNEL_IN_STEREO; - - audioRecord = new AudioRecord( - AudioSource.VOICE_COMMUNICATION, - captureSettings.getSampleRate(), - channelConfig, - AudioFormat.ENCODING_PCM_16BIT, recBufSize); - - if (NoiseSuppressor.isAvailable()) { - noiseSuppressor = NoiseSuppressor.create(audioRecord.getAudioSessionId()); - } - - if (AcousticEchoCanceler.isAvailable()) { - echoCanceler = AcousticEchoCanceler.create(audioRecord.getAudioSessionId()); - } - - } catch (Exception e) { - throw new RuntimeException(e.getMessage()); - } - - // Check that the audioRecord is ready to be used. - if (audioRecord.getState() != AudioRecord.STATE_INITIALIZED) { - String errorDescription = String.format(Locale.getDefault(), "Audio capture could not be initialized.\n" + - "Requested parameters\n" + - " Sampling Rate: %d\n" + - " Number of channels: %d\n" + - " Buffer size: %d\n", - captureSettings.getSampleRate(), - captureSettings.getNumChannels(), - minRecBufSize); - Log.e(TAG, errorDescription); - throw new RuntimeException(errorDescription); - } - - registerPhoneStateListener(); - - shutdownCaptureThread = false; - new Thread(captureThread).start(); - return true; - } - - @Override - public boolean destroyCapturer() { - captureLock.lock(); - - // release the object - if (null != echoCanceler) { - echoCanceler.release(); - echoCanceler = null; - } - - if (null != noiseSuppressor) { - noiseSuppressor.release(); - noiseSuppressor = null; - } - - audioRecord.release(); - audioRecord = null; - shutdownCaptureThread = true; - captureEvent.signal(); - - captureLock.unlock(); - - unRegisterPhoneStateListener(); - wasCapturing = false; - return true; - } - - public int getEstimatedCaptureDelay() { - return estimatedCaptureDelay; - } - - @Override - public boolean startCapturer() { - if (audioRecord == null) { - throw new IllegalStateException("startCapturer(): startRecording() called on an " - + "uninitialized AudioRecord"); - } - try { - audioRecord.startRecording(); - - } catch (IllegalStateException e) { - throw new RuntimeException(e.getMessage()); - } - - captureLock.lock(); - isCapturing = true; - captureEvent.signal(); - captureLock.unlock(); - return true; - } - - @Override - public boolean stopCapturer() { - - if (audioRecord == null) { - throw new IllegalStateException("stopCapturer(): stop() called on an uninitialized AudioRecord"); - } - captureLock.lock(); - try { - // Only stop if we are recording. - if (audioRecord.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING) { - audioRecord.stop(); - } - } catch (Exception e) { - throw new RuntimeException(e.getMessage()); - } finally { - // Ensure we always unlock - isCapturing = false; - captureLock.unlock(); - } - return true; - } - - private Runnable captureThread = () -> { - int samplesToRec = captureSamplingRate / 100; - int samplesRead; - - try { - android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO); - } catch (Exception e) { - Log.e(TAG, "android.os.Process.setThreadPriority(): " + e.getMessage()); - } - - while (!shutdownCaptureThread) { - captureLock.lock(); - try { - if (!this.isCapturing) { - captureEvent.await(); - continue; - } else { - if (audioRecord == null) { - continue; - } - int lengthInBytes = (samplesToRec << 1) * NUM_CHANNELS_CAPTURING; - int readBytes = audioRecord.read(tempBufRec, 0, lengthInBytes); - if (readBytes >= 0) { - recBuffer.rewind(); - recBuffer.put(tempBufRec); - samplesRead = (readBytes >> 1) / NUM_CHANNELS_CAPTURING; - } else { - switch (readBytes) { - case AudioRecord.ERROR_BAD_VALUE: - throw new RuntimeException("captureThread(): AudioRecord.ERROR_BAD_VALUE"); - case AudioRecord.ERROR_INVALID_OPERATION: - throw new RuntimeException("captureThread(): AudioRecord.ERROR_INVALID_OPERATION"); - case AudioRecord.ERROR: - default: - throw new RuntimeException("captureThread(): AudioRecord.ERROR or default"); - } - } - } - } catch (Exception e) { - throw new RuntimeException(e.getMessage()); - } finally { - // Ensure we always unlock - captureLock.unlock(); - } - getAudioBus().writeCaptureData(recBuffer, samplesRead); - estimatedCaptureDelay = samplesRead * 1000 / captureSamplingRate; - } - }; - - - @Override - public boolean initRenderer() { - - // Request audio focus for playback - int result = audioManager.requestAudioFocus(audioFocusChangeListener, - // Use the music stream. - AudioManager.STREAM_VOICE_CALL, - // Request permanent focus. - AudioManager.AUDIOFOCUS_GAIN); - - if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { - Log.d("AUDIO_FOCUS", "Audio Focus request GRANTED !"); - } else { - Log.e("AUDIO_FOCUS", "Audio Focus request DENIED !"); - return false; - } - - // initalize default values - bluetoothState = BluetoothState.Disconnected; - /* register for bluetooth sco callbacks and attempt to enable it */ - enableBluetoothEvents(); - // get the minimum buffer size that can be used - int minPlayBufSize = AudioTrack.getMinBufferSize( - rendererSettings.getSampleRate(), - NUM_CHANNELS_RENDERING == 1 ? AudioFormat.CHANNEL_OUT_MONO : AudioFormat.CHANNEL_OUT_STEREO, - AudioFormat.ENCODING_PCM_16BIT - ); - - // release the object - if (audioTrack != null) { - audioTrack.release(); - audioTrack = null; - } - - try { - int channelConfig = (NUM_CHANNELS_RENDERING == 1) ? AudioFormat.CHANNEL_OUT_MONO : AudioFormat.CHANNEL_OUT_STEREO; - - audioTrack = new AudioTrack( - AudioManager.STREAM_VOICE_CALL, - rendererSettings.getSampleRate(), - channelConfig, - AudioFormat.ENCODING_PCM_16BIT, - minPlayBufSize >= 6000 ? minPlayBufSize : minPlayBufSize * 2, - AudioTrack.MODE_STREAM - ); - } catch (Exception e) { - throw new RuntimeException(e.getMessage()); - } - - // Check that the audioRecord is ready to be used. - if (audioTrack.getState() != AudioTrack.STATE_INITIALIZED) { - throw new RuntimeException("Audio renderer not initialized " + rendererSettings.getSampleRate()); - } - - bufferedPlaySamples = 0; - - registerPhoneStateListener(); - - shutdownRenderThread = false; - new Thread(renderThread).start(); - return true; - } - - private void destroyAudioTrack() { - rendererLock.lock(); - audioTrack.release(); - audioTrack = null; - shutdownRenderThread = true; - renderEvent.signal(); - rendererLock.unlock(); - } - - @Override - public boolean destroyRenderer() { - destroyAudioTrack(); - disableBluetoothEvents(); - unregisterHeadsetReceiver(); - audioManager.setSpeakerphoneOn(false); - audioManager.abandonAudioFocus(audioFocusChangeListener); - - unRegisterPhoneStateListener(); - wasRendering = false; - return true; - } - - public int getEstimatedRenderDelay() { - return estimatedRenderDelay; - } - - @Override - public boolean startRenderer() { - Log.d("AUDIO_FOCUS", "Start Renderer"); - - // Enable speakerphone unless headset is connected. - synchronized (bluetoothLock) { - if (BluetoothState.Connected != bluetoothState) { - if (audioManager.isWiredHeadsetOn()) { - Log.d(TAG, "Turn off Speaker phone"); - audioManager.setSpeakerphoneOn(false); - } else { - Log.d(TAG, "Turn on Speaker phone"); - if (getOutputType() == OutputType.SPEAKER_PHONE) { - audioManager.setSpeakerphoneOn(true); - } - } - } - } - - // Start playout. - if (audioTrack == null) { - throw new IllegalStateException("startRenderer(): play() called on uninitialized AudioTrack"); - } - try { - audioTrack.play(); - } catch (IllegalStateException e) { - throw new RuntimeException(e.getMessage()); - } - - rendererLock.lock(); - isRendering = true; - renderEvent.signal(); - rendererLock.unlock(); - registerBtReceiver(); - registerHeadsetReceiver(); - return true; - } - - @Override - public boolean stopRenderer() { - Log.d("AUDIO_FOCUS", "Stop Renderer"); - - if (audioTrack == null) { - throw new IllegalStateException("stopRenderer(): stop() called on uninitialized AudioTrack"); - } - - rendererLock.lock(); - - try { - // Only stop if we are playing. - if (audioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) { - audioTrack.stop(); - - } - audioTrack.flush(); - } catch (Exception e) { - throw new RuntimeException(e.getMessage()); - } finally { - isRendering = false; - rendererLock.unlock(); - } - audioManagerMode.releaseMode(audioManager); - - unregisterHeadsetReceiver(); - unregisterBtReceiver(); - return true; - } - - private Runnable renderThread = () -> { - int samplesToPlay = samplesPerBuffer; - try { - android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO); - } catch (Exception e) { - Log.e(TAG, "android.os.Process.setThreadPriority(): " + e.getMessage()); - } - - while (!shutdownRenderThread) { - rendererLock.lock(); - try { - if (!this.isRendering) { - renderEvent.await(); - continue; - - } else { - rendererLock.unlock(); - - // Don't lock on audioBus calls - playBuffer.clear(); - int samplesRead = getAudioBus().readRenderData(playBuffer, samplesToPlay); - - rendererLock.lock(); - - // After acquiring the lock again we must check if we are still playing - if (audioTrack == null || !this.isRendering) { - continue; - } - - int bytesRead = (samplesRead << 1) * NUM_CHANNELS_RENDERING; - playBuffer.get(tempBufPlay, 0, bytesRead); - - int bytesWritten = audioTrack.write(tempBufPlay, 0, bytesRead); - - if (bytesWritten > 0) { - // increase by number of written samples - bufferedPlaySamples += (bytesWritten >> 1) / NUM_CHANNELS_RENDERING; - - // decrease by number of played samples - int pos = audioTrack.getPlaybackHeadPosition(); - - if (pos < playPosition) { - // wrap or reset by driver - playPosition = 0; - } - - bufferedPlaySamples -= (pos - playPosition); - playPosition = pos; - - // we calculate the estimated delay based on the buffered samples - estimatedRenderDelay = bufferedPlaySamples * 1000 / outputSamplingRate; - } else { - switch (bytesWritten) { - case AudioTrack.ERROR_BAD_VALUE: - throw new RuntimeException("renderThread(): AudioTrack.ERROR_BAD_VALUE"); - case AudioTrack.ERROR_INVALID_OPERATION: - throw new RuntimeException("renderThread(): AudioTrack.ERROR_INVALID_OPERATION"); - case AudioTrack.ERROR: - default: - throw new RuntimeException( - "renderThread(): AudioTrack.ERROR or default"); - } - } - } - } catch (Exception e) { - throw new RuntimeException(e.getMessage()); - } finally { - rendererLock.unlock(); - } - } - }; - - @Override - public AudioSettings getCaptureSettings() { - return this.captureSettings; - } - - @Override - public AudioSettings getRenderSettings() { - return this.rendererSettings; - } - - /* - * Communication modes handling. - */ - public boolean setOutputMode(OutputMode mode) { - //This is public API and also called during initialization - Log.d("AUDIO_FOCUS", "outputmode set to : " + mode); - super.setOutputMode(mode); - - if(OutputMode.SpeakerPhone == mode) { - audioState.setLastOutputType(getOutputType()); - setOutputType(OutputType.SPEAKER_PHONE); - audioManager.setSpeakerphoneOn(true); - stopBluetoothSco(); - audioManager.setBluetoothScoOn(false); - } else { - if (audioState.getLastOutputType() == OutputType.BLUETOOTH || bluetoothState == BluetoothState.Connected) { - connectBluetooth(); - } else { - audioState.setLastOutputType(getOutputType()); - audioManager.setSpeakerphoneOn(false); - setOutputType(OutputType.EAR_PIECE); - stopBluetoothSco(); - audioManager.setBluetoothScoOn(false); - } - } - - return true; - } - - private boolean isHeadsetReceiverRegistered; - - private void registerHeadsetReceiver() { - Log.d(TAG, "registerHeadsetReceiver() called ... isHeadsetReceiverRegistered = " + isHeadsetReceiverRegistered); - - if (isHeadsetReceiverRegistered) { - return; - } - - context.registerReceiver(headsetBroadcastReceiver, new IntentFilter(Intent.ACTION_HEADSET_PLUG)); - isHeadsetReceiverRegistered = true; - } - - private void unregisterHeadsetReceiver() { - Log.d(TAG, "unregisterHeadsetReceiver() called .. isHeadsetReceiverRegistered = " + isHeadsetReceiverRegistered); - - if (!isHeadsetReceiverRegistered) { - return; - } - - context.unregisterReceiver(headsetBroadcastReceiver); - isHeadsetReceiverRegistered = false; - } - - private boolean isBluetoothHeadSetReceiverRegistered; - - private void registerBtReceiver() { - Log.d(TAG, "registerBtReceiver() called .. isBluetoothHeadSetReceiverRegistered = " + isBluetoothHeadSetReceiverRegistered); - - if (isBluetoothHeadSetReceiverRegistered) { - return; - } - - IntentFilter btFilter = new IntentFilter(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); - btFilter.addAction(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED); - context.registerReceiver(bluetoothBroadcastReceiver, btFilter); - - // Register receiver for change in audio connection state of the Headset profile. - context.registerReceiver(bluetoothHeadsetReceiver, new IntentFilter(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)); - - isBluetoothHeadSetReceiverRegistered = true; - } - - - private void unregisterBtReceiver() { - Log.d(TAG, "unregisterBtReceiver() called .. bluetoothHeadSetReceiverRegistered = " + isBluetoothHeadSetReceiverRegistered); - - if (!isBluetoothHeadSetReceiverRegistered) { - return; - } - - context.unregisterReceiver(bluetoothBroadcastReceiver); - context.unregisterReceiver(bluetoothHeadsetReceiver); - isBluetoothHeadSetReceiverRegistered = false; - } - - private boolean isPhoneStateListenerRegistered; - - private void registerPhoneStateListener() { - Log.d(TAG, "registerPhoneStateListener() called"); - - if (isPhoneStateListenerRegistered) { - return; - } - - if (telephonyManager != null) { - telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE); - isPhoneStateListenerRegistered = true; - } - } - - private void unRegisterPhoneStateListener() { - Log.d(TAG, "unRegisterPhoneStateListener() called"); - - if (!isPhoneStateListenerRegistered) { - return; - } - - if (telephonyManager != null) { - telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE); - isPhoneStateListenerRegistered = false; - } - } - - @Override - public synchronized void onPause() { - audioState.setLastOutputType(getOutputType()); - unregisterBtReceiver(); - unregisterHeadsetReceiver(); - isPaused = true; - } - - @Override - public synchronized void onResume() { - Log.d(TAG, "onResume() called"); - if (!isPaused) { - return; - } - - if (bluetoothState == BluetoothState.Disconnected) { - if (isRendering && (audioState.getLastOutputType() == OutputType.SPEAKER_PHONE)) { - if (!audioManager.isWiredHeadsetOn()) { - Log.d(TAG, "onResume() - Set Speaker Phone ON True"); - audioManager.setSpeakerphoneOn(true); - } - } - } - - /* register handler for phonejack notifications */ - registerBtReceiver(); - registerHeadsetReceiver(); - connectBluetooth(); - forceInvokeConnectBluetooth(); - - isPaused = false; - } - - @Override - public BluetoothState getBluetoothState() { - return bluetoothState; - } - - private void enableBluetoothEvents() { - if (audioManager.isBluetoothScoAvailableOffCall()) { - registerBtReceiver(); - connectBluetooth(); - } - - } - - private void disableBluetoothEvents() { - if (null != bluetoothProfile && bluetoothAdapter != null) { - bluetoothAdapter.closeProfileProxy(BluetoothProfile.HEADSET, bluetoothProfile); - } - - unregisterBtReceiver(); - - // Force a shutdown of bluetooth: when a call comes in, the handler is not invoked by system. - Intent intent = new Intent(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); - intent.putExtra(BluetoothHeadset.EXTRA_STATE, BluetoothHeadset.STATE_DISCONNECTED); - bluetoothBroadcastReceiver.onReceive(context, intent); - } - - private void startBluetoothSco() { - try { - audioManager.startBluetoothSco(); - } catch (NullPointerException e) { - Log.d(TAG, e.getMessage()); - } - } - - private void stopBluetoothSco() { - try { - audioManager.stopBluetoothSco(); - } catch (NullPointerException e) { - Log.d(TAG, e.getMessage()); - } - } - - private final BluetoothProfile.ServiceListener bluetoothProfileServiceListener = new BluetoothProfile.ServiceListener() { - @Override - public void onServiceConnected(int type, BluetoothProfile profile) { - Log.d(TAG, "BluetoothProfile.ServiceListener.onServiceConnected()"); - if (BluetoothProfile.HEADSET == type) { - bluetoothProfile = profile; - List devices = profile.getConnectedDevices(); - - Log.d(TAG, "Service Proxy Connected"); - - if (!devices.isEmpty() && - BluetoothHeadset.STATE_CONNECTED == profile.getConnectionState(devices.get(0))) { - // Force a init of bluetooth: the handler will not send a connected event if a - // device is already connected at the time of proxy connection request. - Intent intent = new Intent(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); - intent.putExtra(BluetoothHeadset.EXTRA_STATE, BluetoothHeadset.STATE_CONNECTED); - bluetoothBroadcastReceiver.onReceive(context, intent); - } - } - } - - @Override - public void onServiceDisconnected(int type) { - Log.d(TAG, "BluetoothProfile.ServiceListener.onServiceDisconnected()"); - } - }; - - private void forceInvokeConnectBluetooth() { - Log.d(TAG, "forceConnectBluetooth() called"); - - // Force reconnection of bluetooth in the event of a phone call. - synchronized (bluetoothLock) { - bluetoothState = BluetoothState.Disconnected; - if (bluetoothAdapter != null) { - bluetoothAdapter.getProfileProxy( - context, - bluetoothProfileServiceListener, - BluetoothProfile.HEADSET - ); - } - } - } -} \ No newline at end of file diff --git a/android/src/main/java/com/opentokreactnative/OTSessionManager.java b/android/src/main/java/com/opentokreactnative/OTSessionManager.java index 490df629..7b7b862e 100644 --- a/android/src/main/java/com/opentokreactnative/OTSessionManager.java +++ b/android/src/main/java/com/opentokreactnative/OTSessionManager.java @@ -98,11 +98,6 @@ public void initSession(String apiKey, String sessionId, ReadableMap sessionOpti final boolean useTextureViews = sessionOptions.getBoolean("useTextureViews"); final boolean connectionEventsSuppressed = sessionOptions.getBoolean("connectionEventsSuppressed"); final boolean ipWhitelist = sessionOptions.getBoolean("ipWhitelist"); - final boolean enableStereoOutput = sessionOptions.getBoolean("enableStereoOutput"); - if (enableStereoOutput) { - OTCustomAudioDriver otCustomAudioDriver = new OTCustomAudioDriver(this.getReactApplicationContext()); - AudioDeviceManager.setAudioDevice(otCustomAudioDriver); - } final List iceServersList = Utils.sanitizeIceServer(sessionOptions.getArray("customServers")); final IncludeServers includeServers = Utils.sanitizeIncludeServer(sessionOptions.getString("includeServers")); final TransportPolicy transportPolicy = Utils.sanitizeTransportPolicy(sessionOptions.getString("transportPolicy")); From ae06aa5a5b89503742a94894dbcc2873af532db5 Mon Sep 17 00:00:00 2001 From: Jeff Swartz Date: Mon, 10 Jun 2024 16:16:59 -0700 Subject: [PATCH 6/8] Fix captions (#741) * Fix captions API * Fixes iOS subscribeToCaptions method * Change log edits * Typo correction * Minor edit to changelog --- CHANGELOG.md | 4 +++- ios/OpenTokReactNative/OTSessionManager.m | 4 ++-- ios/OpenTokReactNative/OTSessionManager.swift | 10 ++++++++-- src/helpers/OTSubscriberHelper.js | 2 +- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3eab19e8..c80e3ca9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,6 @@ -# 2.27.5 (May 2024) +# 2.27.5 (June 2024) + +- [Fix]: This version fixes the `OTSubscriber captionReceived` event handler. It also fixes the `OTPublisher publishCaptions` option in iOS. - [Fix]: Calling `OTSubscriber.getRtcStatsReport()` method was resulting in an error. This version fixes the issue. diff --git a/ios/OpenTokReactNative/OTSessionManager.m b/ios/OpenTokReactNative/OTSessionManager.m index 9b923ce2..518a2531 100644 --- a/ios/OpenTokReactNative/OTSessionManager.m +++ b/ios/OpenTokReactNative/OTSessionManager.m @@ -49,7 +49,7 @@ @interface RCT_EXTERN_MODULE(OTSessionManager, RCTEventEmitter) pubVideo:(BOOL)pubVideo) RCT_EXTERN_METHOD(publishCaptions: (NSString*)publisherId - pubAudio:(BOOL)publishCaptions) + pubCaptions:(BOOL)publishCaptions) RCT_EXTERN_METHOD(getRtcStatsReport: (NSString*)publisherId) RCT_EXTERN_METHOD(getSubscriberRtcStatsReport) @@ -61,7 +61,7 @@ @interface RCT_EXTERN_MODULE(OTSessionManager, RCTEventEmitter) subVideo:(BOOL)subVideo) RCT_EXTERN_METHOD(subscribeToCaptions: (NSString*)streamId - subAudio:(BOOL)subCaptions) + subCaptions:(BOOL)subCaptions) RCT_EXTERN_METHOD(setPreferredResolution: (NSString*)streamId resolution:(NSDictionary*)resolution) diff --git a/ios/OpenTokReactNative/OTSessionManager.swift b/ios/OpenTokReactNative/OTSessionManager.swift index c30f8dc0..daf61d55 100644 --- a/ios/OpenTokReactNative/OTSessionManager.swift +++ b/ios/OpenTokReactNative/OTSessionManager.swift @@ -168,6 +168,7 @@ class OTSessionManager: RCTEventEmitter { subscriber.audioVolume = audioVolume; } subscriber.rtcStatsReportDelegate = self; + subscriber.captionsDelegate = self; if let err = error { self.dispatchErrorViaCallback(callback, error: err) } else { @@ -217,6 +218,11 @@ class OTSessionManager: RCTEventEmitter { publisher.publishVideo = pubVideo; } + @objc func publishCaptions(_ publisherId: String, pubCaptions: Bool) -> Void { + guard let publisher = OTRN.sharedState.publishers[publisherId] else { return } + publisher.publishCaptions = pubCaptions; + } + @objc func getRtcStatsReport(_ publisherId: String) -> Void { guard let publisher = OTRN.sharedState.publishers[publisherId] else { return } publisher.getRtcStatsReport() @@ -927,10 +933,10 @@ extension OTSessionManager: OTSubscriberKitCaptionsDelegate { subscriberInfo["text"] = text; subscriberInfo["isFinal"] = isFinal; guard let stream = subscriber.stream else { - self.emitEvent("\(EventUtils.subscriberPreface)subscriberDidConnect", data: subscriberInfo); + self.emitEvent("\(EventUtils.subscriberPreface)subscriberCaptionReceived", data: subscriberInfo); return; } subscriberInfo["stream"] = EventUtils.prepareJSStreamEventData(stream); - self.emitEvent("\(EventUtils.subscriberPreface)subscriberDidConnect", data: subscriberInfo); + self.emitEvent("\(EventUtils.subscriberPreface)subscriberCaptionReceived", data: subscriberInfo); } } diff --git a/src/helpers/OTSubscriberHelper.js b/src/helpers/OTSubscriberHelper.js index 28543446..2802c09f 100644 --- a/src/helpers/OTSubscriberHelper.js +++ b/src/helpers/OTSubscriberHelper.js @@ -27,7 +27,7 @@ const sanitizeSubscriberEvents = (events) => { videoDisableWarning: 'subscriberVideoDisableWarning', videoDisableWarningLifted: 'subscriberVideoDisableWarningLifted', videoDataReceived: 'subscriberVideoDataReceived', - captionReceived: 'subscriberCaptionReceived:', + captionReceived: 'subscriberCaptionReceived', }, android: { connected: 'onConnected', From 5b5cc1aec2b92b1775240923874dfeec380a9aa1 Mon Sep 17 00:00:00 2001 From: Jeff Swartz Date: Mon, 17 Jun 2024 10:39:11 -0700 Subject: [PATCH 7/8] Fix Android crash introduced with subscribeToSelf --- .../opentokreactnative/OTSessionManager.java | 6 +-- ios/OpenTokReactNative/OTSessionManager.swift | 4 +- src/OTPublisher.js | 47 +++++++++++++++++++ src/helpers/OTPublisherHelper.js | 4 +- src/helpers/OTSessionHelper.js | 4 -- 5 files changed, 53 insertions(+), 12 deletions(-) diff --git a/android/src/main/java/com/opentokreactnative/OTSessionManager.java b/android/src/main/java/com/opentokreactnative/OTSessionManager.java index 7b7b862e..5d0f292d 100644 --- a/android/src/main/java/com/opentokreactnative/OTSessionManager.java +++ b/android/src/main/java/com/opentokreactnative/OTSessionManager.java @@ -878,9 +878,8 @@ public void onStreamCreated(PublisherKit publisherKit, Stream stream) { ConcurrentHashMap mSubscriberStreams = sharedState.getSubscriberStreams(); mSubscriberStreams.put(stream.getStreamId(), stream); if (publisherId.length() > 0) { - String event = publisherId + ":" + publisherPreface + "onStreamCreated"; WritableMap streamInfo = EventUtils.prepareJSStreamMap(stream, publisherKit.getSession()); - sendEventMap(this.getReactApplicationContext(), event, streamInfo); + streamInfo.putString("publisherId", publisherId); sendEventMap(this.getReactApplicationContext(), "publisherStreamCreated", streamInfo); } printLogs("onStreamCreated: Publisher Stream Created. Own stream "+stream.getStreamId()); @@ -891,13 +890,12 @@ public void onStreamCreated(PublisherKit publisherKit, Stream stream) { public void onStreamDestroyed(PublisherKit publisherKit, Stream stream) { String publisherId = Utils.getPublisherId(publisherKit); - String event = publisherId + ":" + publisherPreface + "onStreamDestroyed"; ConcurrentHashMap mSubscriberStreams = sharedState.getSubscriberStreams(); String mStreamId = stream.getStreamId(); mSubscriberStreams.remove(mStreamId); if (publisherId.length() > 0) { WritableMap streamInfo = EventUtils.prepareJSStreamMap(stream, publisherKit.getSession()); - sendEventMap(this.getReactApplicationContext(), event, streamInfo); + streamInfo.putString("publisherId", publisherId); sendEventMap(this.getReactApplicationContext(), "publisherStreamDestroyed", streamInfo); } Callback mCallback = sharedState.getPublisherDestroyedCallbacks().get(publisherId); diff --git a/ios/OpenTokReactNative/OTSessionManager.swift b/ios/OpenTokReactNative/OTSessionManager.swift index daf61d55..b9db9970 100644 --- a/ios/OpenTokReactNative/OTSessionManager.swift +++ b/ios/OpenTokReactNative/OTSessionManager.swift @@ -652,7 +652,7 @@ extension OTSessionManager: OTPublisherDelegate { if (publisherId.count > 0) { OTRN.sharedState.isPublishing[publisherId] = true; let streamInfo: Dictionary = EventUtils.prepareJSStreamEventData(stream); - self.emitEvent("\(publisherId):\(EventUtils.publisherPreface)streamCreated", data: streamInfo); + streamInfo["publisherId"] = publisherId; self.emitEvent("publisherStreamCreated", data: streamInfo); setStreamObservers(stream: stream, isPublisherStream: true) } @@ -668,7 +668,7 @@ extension OTSessionManager: OTPublisherDelegate { if (publisherId.count > 0) { OTRN.sharedState.isPublishing[publisherId] = false; let streamInfo: Dictionary = EventUtils.prepareJSStreamEventData(stream); - self.emitEvent("\(publisherId):\(EventUtils.publisherPreface)streamDestroyed", data: streamInfo); + streamInfo["publisherId"] = publisherId; self.emitEvent("publisherStreamDestroyed", data: streamInfo); } OTRN.sharedState.publishers[publisherId] = nil; diff --git a/src/OTPublisher.js b/src/OTPublisher.js index a73a5cae..8eceefbf 100644 --- a/src/OTPublisher.js +++ b/src/OTPublisher.js @@ -31,6 +31,8 @@ class OTPublisher extends Component { } initComponent = () => { this.componentEvents = { + publisherStreamCreated: 'publisherStreamCreated', + publisherStreamDestroyed: 'publisherStreamDestroyed:', sessionConnected: Platform.OS === 'android' ? 'session:onConnected' @@ -43,7 +45,40 @@ class OTPublisher extends Component { this.props.eventHandlers ); setNativeEvents(this.publisherEvents); + setNativeEvents({ + publisherCreated: (event) => { + if ( + this.props.eventHandlers + && this.props.eventHandlers.streamCreated + && event.publisherId === this.state.publisherId + ) { + // don't forward publisherId in client event: + delete event.publisherId; + this.props.eventHandlers.streamCreated(event); + } + }, + publisherDestroyed: (event) => { + if ( + this.props.eventHandlers + && this.props.eventHandlers.streamDestroyed + && event.publisherId === this.state.publisherId + ) { + // don't forward publisherId in client event: + delete event.publisherId; + this.props.eventHandlers.streamDestroyed(event); + } + }, + + }); OT.setJSComponentEvents(this.componentEventsArray); + this.publisherStreamCreated = nativeEvents.addListener( + 'publisherStreamCreated', + stream => this.publisherStreamCreatedHandler(stream) + ); + this.publisherStreamDestroyed = nativeEvents.addListener( + 'publisherStreamDestroyed', + stream => this.publisherStreamDestroyedHandler(stream) + ); if (this.context.sessionId) { this.sessionConnected = nativeEvents.addListener( `${this.context.sessionId}:${this.componentEvents.sessionConnected}`, @@ -159,6 +194,18 @@ class OTPublisher extends Component { OT.getRtcStatsReport(this.state.publisherId); } + publisherStreamCreatedHandler = (stream) => { + if (this.props.eventHandlers && this.props.eventHandlers.streamCreated) { + this.props.eventHandlers.streamCreated(stream); + } + } + + publisherStreamDestroyedHandler = (stream) => { + if (this.props.eventHandlers && this.props.eventHandlers.streamDestroyed) { + this.props.eventHandlers.streamDestroyed(stream); + } + } + setVideoTransformers(videoTransformers) { OT.setVideoTransformers(this.state.publisherId, videoTransformers); } diff --git a/src/helpers/OTPublisherHelper.js b/src/helpers/OTPublisherHelper.js index 7f28166b..0168de04 100644 --- a/src/helpers/OTPublisherHelper.js +++ b/src/helpers/OTPublisherHelper.js @@ -149,8 +149,8 @@ const sanitizePublisherEvents = (publisherId, events) => { videoDisableWarningLifted: 'videoDisableWarningLifted', }, android: { - streamCreated: 'onStreamCreated', - streamDestroyed: 'onStreamDestroyed', + streamCreated: 'streamCreated', + streamDestroyed: 'streamDestroyed', error: 'onError', audioLevel: 'onAudioLevelUpdated', audioNetworkStats: 'onAudioStats', diff --git a/src/helpers/OTSessionHelper.js b/src/helpers/OTSessionHelper.js index 2c1d2fec..562d9f76 100644 --- a/src/helpers/OTSessionHelper.js +++ b/src/helpers/OTSessionHelper.js @@ -39,8 +39,6 @@ const sanitizeSessionEvents = (sessionId, events) => { archiveStopped: 'archiveStoppedWithId', streamPropertyChanged: 'streamPropertyChanged', muteForced: 'muteForced', - publisherStreamCreated: 'publisherStreamCreated', - publisherStreamDestroyed: 'publisherStreamDestroyed', }, android: { streamCreated: 'onStreamReceived', @@ -57,8 +55,6 @@ const sanitizeSessionEvents = (sessionId, events) => { archiveStopped: 'onArchiveStopped', streamPropertyChanged: 'onStreamPropertyChanged', muteForced: 'onMuteForced', - publisherStreamCreated: 'publisherStreamCreated', - publisherStreamDestroyed: 'publisherStreamDestroyed', } }; return reassignEvents('session', customEvents, events, sessionId); From d6c8b89631d412d9d267b77fb169dbca84eeb8de Mon Sep 17 00:00:00 2001 From: Jeff Swartz Date: Mon, 17 Jun 2024 12:49:47 -0700 Subject: [PATCH 8/8] Remove duplicate publisher events --- src/OTPublisher.js | 37 ++++++++++--------------------------- 1 file changed, 10 insertions(+), 27 deletions(-) diff --git a/src/OTPublisher.js b/src/OTPublisher.js index 8eceefbf..f9e7c021 100644 --- a/src/OTPublisher.js +++ b/src/OTPublisher.js @@ -45,31 +45,6 @@ class OTPublisher extends Component { this.props.eventHandlers ); setNativeEvents(this.publisherEvents); - setNativeEvents({ - publisherCreated: (event) => { - if ( - this.props.eventHandlers - && this.props.eventHandlers.streamCreated - && event.publisherId === this.state.publisherId - ) { - // don't forward publisherId in client event: - delete event.publisherId; - this.props.eventHandlers.streamCreated(event); - } - }, - publisherDestroyed: (event) => { - if ( - this.props.eventHandlers - && this.props.eventHandlers.streamDestroyed - && event.publisherId === this.state.publisherId - ) { - // don't forward publisherId in client event: - delete event.publisherId; - this.props.eventHandlers.streamDestroyed(event); - } - }, - - }); OT.setJSComponentEvents(this.componentEventsArray); this.publisherStreamCreated = nativeEvents.addListener( 'publisherStreamCreated', @@ -195,13 +170,21 @@ class OTPublisher extends Component { } publisherStreamCreatedHandler = (stream) => { - if (this.props.eventHandlers && this.props.eventHandlers.streamCreated) { + if ( + this.props.eventHandlers + && this.props.eventHandlers.streamCreated + && stream.publisherId === this.state.publisherId + ) { this.props.eventHandlers.streamCreated(stream); } } publisherStreamDestroyedHandler = (stream) => { - if (this.props.eventHandlers && this.props.eventHandlers.streamDestroyed) { + if ( + this.props.eventHandlers + && this.props.eventHandlers.streamCreated + && stream.publisherId === this.state.publisherId + ) { this.props.eventHandlers.streamDestroyed(stream); } }