From 8c6dc2227f9cb9d1d686c0cf00082347ba6cc616 Mon Sep 17 00:00:00 2001 From: USAMAWIZARD Date: Fri, 12 Sep 2025 23:06:53 +0530 Subject: [PATCH 01/28] update reconneciton mechanism --- webrtc-android-framework/build.gradle | 1 + .../api/DefaultConferenceWebRTCListener.java | 5 +- .../api/DefaultWebRTCListener.java | 11 +- .../api/IWebRTCClient.java | 4 + .../api/IWebRTCListener.java | 6 +- .../core/DataChannelConstants.java | 1 + .../core/WebRTCClient.java | 177 +++++++++-------- .../websocket/WebSocketHandler.java | 181 ++++++++++-------- .../WebSocketHandlerTest.java | 2 - .../DefaultConferenceWebRTCListenerTest.java | 18 +- .../api/DefaultWebRTCListenerTest.java | 10 +- .../basic/DynamicConferenceActivity.java | 67 ++++++- 12 files changed, 283 insertions(+), 200 deletions(-) diff --git a/webrtc-android-framework/build.gradle b/webrtc-android-framework/build.gradle index 550c0c13..b7d02c29 100644 --- a/webrtc-android-framework/build.gradle +++ b/webrtc-android-framework/build.gradle @@ -74,6 +74,7 @@ task jacocoTestReport(type: JacocoReport, dependsOn: ['testDebugUnitTest','creat dependencies { api fileTree(include: ['*.jar'], dir: 'libs') + implementation("com.squareup.okhttp3:okhttp:4.12.0") implementation 'com.google.code.findbugs:jsr305:3.0.2' implementation 'androidx.annotation:annotation:1.5.0' testImplementation 'junit:junit:4.13.2' diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/api/DefaultConferenceWebRTCListener.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/api/DefaultConferenceWebRTCListener.java index e0dc4324..ded7c6fa 100644 --- a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/api/DefaultConferenceWebRTCListener.java +++ b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/api/DefaultConferenceWebRTCListener.java @@ -2,6 +2,7 @@ import org.webrtc.VideoTrack; +import io.antmedia.webrtcandroidframework.core.WebRTCClient; import io.antmedia.webrtcandroidframework.websocket.Broadcast; /** @@ -84,8 +85,8 @@ public void onPlayFinished(String streamId) { } @Override - public void onReconnectionAttempt(String streamId) { - super.onReconnectionAttempt(streamId); + public void onReconnectionAttempt(String streamId, WebRTCClient.Mode mode) { + super.onReconnectionAttempt(streamId, mode); if(streamId.equals(this.streamId)) { publishReconnecting = true; } diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/api/DefaultWebRTCListener.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/api/DefaultWebRTCListener.java index 091a962b..57f0e901 100644 --- a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/api/DefaultWebRTCListener.java +++ b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/api/DefaultWebRTCListener.java @@ -7,8 +7,8 @@ import java.util.ArrayList; -import de.tavendo.autobahn.WebSocket; import io.antmedia.webrtcandroidframework.core.StreamInfo; +import io.antmedia.webrtcandroidframework.core.WebRTCClient; import io.antmedia.webrtcandroidframework.websocket.Broadcast; /** @@ -77,11 +77,6 @@ public void onError(String description, String streamId) { callbackCalled(messageText); } - @Override - public void onSignalChannelClosed(WebSocket.WebSocketConnectionObserver.WebSocketCloseNotification code, String streamId) { - String messageText = "Signal channel closed for " + streamId + " : " + code; - callbackCalled(messageText); - } @Override public void streamIdInUse(String streamId) { @@ -157,7 +152,7 @@ public void onVideoTrackEnded(VideoTrack track) { } @Override - public void onReconnectionAttempt(String streamId) { + public void onReconnectionAttempt(String streamId, WebRTCClient.Mode mode) { String messageText = "Reconnection attempt for " + streamId; callbackCalled(messageText); } @@ -280,4 +275,4 @@ protected void callbackCalled(String messageText) { Log.d(DefaultWebRTCListener.class.getName(), messageText); } -} \ No newline at end of file +} diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/api/IWebRTCClient.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/api/IWebRTCClient.java index 78362306..53c450a3 100644 --- a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/api/IWebRTCClient.java +++ b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/api/IWebRTCClient.java @@ -263,6 +263,10 @@ void publish(String streamId, String token, boolean videoCallEnabled, boolean au */ boolean isReconnectionInProgress(); + boolean isPlayConnected(); + + boolean isPlayReconnecting(); + /** * Get the error * diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/api/IWebRTCListener.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/api/IWebRTCListener.java index f38a3558..113cfaa0 100644 --- a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/api/IWebRTCListener.java +++ b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/api/IWebRTCListener.java @@ -4,8 +4,8 @@ import java.util.ArrayList; -import de.tavendo.autobahn.WebSocket; import io.antmedia.webrtcandroidframework.core.StreamInfo; +import io.antmedia.webrtcandroidframework.core.WebRTCClient; import io.antmedia.webrtcandroidframework.websocket.Broadcast; /** @@ -60,8 +60,6 @@ public interface IWebRTCListener { void onError(String description, String streamId); - void onSignalChannelClosed(WebSocket.WebSocketConnectionObserver.WebSocketCloseNotification code, String streamId); - /** * It's called if client tried to stream with a stream id that is currently used in another stream. * @@ -137,7 +135,7 @@ public interface IWebRTCListener { * @param streamId * It's called when reconnection attempt is started */ - void onReconnectionAttempt(String streamId); + void onReconnectionAttempt(String streamId, WebRTCClient.Mode ClientType); /** * It's called when joiened the room diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/DataChannelConstants.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/DataChannelConstants.java index 05d9f25c..00f16daa 100644 --- a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/DataChannelConstants.java +++ b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/DataChannelConstants.java @@ -3,6 +3,7 @@ public class DataChannelConstants { public static final String VIDEO_TRACK_ASSIGNMENT_LIST = "VIDEO_TRACK_ASSIGNMENT_LIST"; + public static final String TRACK_LIST_UPDATED = "TRACK_LIST_UPDATED"; public static final String PAYLOAD = "payload"; public static final String TRACK_ID = "trackId"; public static final String VIDEO_LABEL = "videoLabel"; diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java index 872f0241..4e5ff3ba 100644 --- a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java +++ b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java @@ -16,7 +16,6 @@ import android.util.DisplayMetrics; import android.util.Log; import android.view.WindowManager; -import android.widget.GridLayout; import android.widget.Toast; import androidx.annotation.NonNull; @@ -112,7 +111,8 @@ public enum Mode { private String errorString = null; private boolean streamStoppedByUser = false; - private boolean reconnectionInProgress = false; + private boolean publishReconnectionInProgress = false; + private boolean playReconnectionInProgress = false; private boolean autoPlayTracks = false; private boolean waitingForPlay = false; @@ -259,6 +259,9 @@ public void createReconnectorRunnables() { if(released || streamStoppedByUser){ return; } + if(wsHandler.pingPongExecutor == null){ + wsHandler.startPingPongTimer(); + } publishReconnectionHandler.postDelayed(publishReconnectorRunnable, PEER_RECONNECTION_RETRY_DELAY_MS); for (PeerInfo peerInfo : peers.values()) { @@ -269,17 +272,17 @@ public void createReconnectorRunnables() { && pc.iceConnectionState() != PeerConnection.IceConnectionState.COMPLETED)) { - if (pc != null) { - pc.close(); - /* - This is a FIX of a reconnection bug. - If dispose is used instead of close, in one of consequent reconnection attempts segmentation fault occurs. - pc.dispose(); - */ - } - - config.webRTCListener.onReconnectionAttempt(peerInfo.id); if (peerInfo.mode.equals(Mode.PUBLISH)) { + if (pc != null) { + pc.close(); + /* + This is a FIX of a reconnection bug. + If dispose is used instead of close, in one of consequent reconnection attempts segmentation fault occurs. + pc.dispose(); + */ + } + + config.webRTCListener.onReconnectionAttempt(peerInfo.id,peerInfo.mode); Log.d(TAG, "Reconnect attempt for publish"); wsHandler.stop(peerInfo.id); @@ -295,7 +298,9 @@ public void createReconnectorRunnables() { if(released || streamStoppedByUser){ return; } - releaseRemoteRenderers(); + if(wsHandler.pingPongExecutor == null){ + wsHandler.startPingPongTimer(); + } playReconnectionHandler.postDelayed(playReconnectorRunnable, PEER_RECONNECTION_RETRY_DELAY_MS); @@ -306,18 +311,20 @@ public void createReconnectorRunnables() { && pc.iceConnectionState() != PeerConnection.IceConnectionState.CONNECTED && pc.iceConnectionState() != PeerConnection.IceConnectionState.COMPLETED)) { + if (peerInfo.mode.equals(Mode.PLAY)) { - if (pc != null) { - pc.close(); - /* - This is a FIX of a reconnection bug. - If dispose is used instead of close, in one of consequent reconnection attempts segmentation fault occurs. - pc.dispose(); - */ - } + releaseRemoteRenderers(); - config.webRTCListener.onReconnectionAttempt(peerInfo.id); - if (peerInfo.mode.equals(Mode.PLAY)) { + if (pc != null) { + pc.close(); + /* + This is a FIX of a reconnection bug. + If dispose is used instead of close, in one of consequent reconnection attempts segmentation fault occurs. + pc.dispose(); + */ + } + + config.webRTCListener.onReconnectionAttempt(peerInfo.id,peerInfo.mode); Log.d(TAG, "Reconnect attempt for play"); play(peerInfo.id, @@ -355,7 +362,7 @@ public void createReconnectorRunnables() { */ } - config.webRTCListener.onReconnectionAttempt(peerInfo.id); + config.webRTCListener.onReconnectionAttempt(peerInfo.id,peerInfo.mode); if (peerInfo.mode.equals(Mode.PUBLISH)) { Log.d(TAG, "Reconnect attempt for publish"); @@ -516,7 +523,7 @@ public void onIceConnectionChange(final PeerConnection.IceConnectionState newSta } else if (newState == PeerConnection.IceConnectionState.DISCONNECTED || newState == PeerConnection.IceConnectionState.CLOSED) { onIceDisconnected(streamId); } else if (newState == PeerConnection.IceConnectionState.FAILED) { - onIceFailed(streamId); + onIceDisconnected(streamId); } }); } @@ -531,6 +538,7 @@ public void onConnectionChange(final PeerConnection.PeerConnectionState newState onDisconnected(); } else if (newState == PeerConnection.PeerConnectionState.FAILED) { reportError(streamId, "DTLS connection failed."); + onDisconnected(); } }); } @@ -970,20 +978,22 @@ public void onWebSocketConnected() { } private void publishPlayIfRequested() { - if(wsHandler == null){ - return; - } - for (Map.Entry entry : peers.entrySet()) { - PeerInfo peerInfo = entry.getValue(); - Mode peerMode = peerInfo.mode; - if (peerMode == Mode.PUBLISH && peerInfo.peerConnection == null) { - Log.i(TAG, "Processing publish request for peer streamId: " + peerInfo.id); - wsHandler.startPublish(peerInfo.id, peerInfo.token, peerInfo.videoCallEnabled, peerInfo.audioCallEnabled, peerInfo.subscriberId, peerInfo.subscriberCode, peerInfo.streamName, peerInfo.mainTrackId); + synchronized (this) { + if (wsHandler == null) { + return; } + for (Map.Entry entry : peers.entrySet()) { + PeerInfo peerInfo = entry.getValue(); + Mode peerMode = peerInfo.mode; + if (!publishReconnectionInProgress && peerMode == Mode.PUBLISH && peerInfo.peerConnection == null) { + Log.i(TAG, "Processing publish request for peer streamId: " + peerInfo.id); + wsHandler.startPublish(peerInfo.id, peerInfo.token, peerInfo.videoCallEnabled, peerInfo.audioCallEnabled, peerInfo.subscriberId, peerInfo.subscriberCode, peerInfo.streamName, peerInfo.mainTrackId); + } - if (peerMode == Mode.PLAY && peerInfo.peerConnection == null) { - Log.i(TAG, "Processing play request for peer streamId: " + peerInfo.id); - wsHandler.startPlay(peerInfo.id, peerInfo.token, null, peerInfo.subscriberId, peerInfo.subscriberCode, peerInfo.metaData); + if (!playReconnectionInProgress && peerMode == Mode.PLAY && peerInfo.peerConnection == null) { + Log.i(TAG, "Processing play request for peer streamId: " + peerInfo.id); + wsHandler.startPlay(peerInfo.id, peerInfo.token, null, peerInfo.subscriberId, peerInfo.subscriberCode, peerInfo.metaData); + } } } } @@ -1054,7 +1064,7 @@ public void play(String streamId, String token, String[] tracks, String subscrib }); createPeerInfo(streamId, token, false, false, subscriberId, subscriberCode, "", "", viewerInfo, Mode.PLAY); - if (!isReconnectionInProgress()) { + if (playReconnectionInProgress) { init(); } @@ -1173,11 +1183,7 @@ public void release(boolean closeWebsocket) { releaseRenderer(config.localVideoRenderer, localVideoTrack, localVideoSink); } - for (SurfaceViewRenderer remoteVideoRenderer : config.remoteVideoRenderers) { - if (remoteVideoRenderer.getTag() != null) { - releaseRenderer(remoteVideoRenderer); - } - } + releaseRemoteRenderers(); localVideoTrack = null; localAudioTrack = null; @@ -1347,17 +1353,28 @@ public void onIceConnected(String streamId) { } public void rePublishPlay() { - if (streamStoppedByUser || reconnectionInProgress) { - return; - } - reconnectionInProgress = true; + synchronized (this) { + if (streamStoppedByUser) { + return; + } - if(isConference()){ - Log.i(TAG, "Conference! Will try to republish in " + PEER_RECONNECTION_DELAY_MS + " ms."); - publishReconnectionHandler.postDelayed(publishReconnectorRunnable, PEER_RECONNECTION_DELAY_MS); - }else{ - Log.i(TAG, "Peer was connected before. Will try to republish/replay in " + PEER_RECONNECTION_DELAY_MS + " ms."); - peerReconnectionHandler.postDelayed(peerReconnectorRunnable, PEER_RECONNECTION_DELAY_MS); + if (isConference()) { + Log.i(TAG, "Conference! Will try to republish in " + PEER_RECONNECTION_DELAY_MS + " ms."); + Log.i(TAG,"publish connected :"+ isPublishConnected() +"play connected" +isPlayConnected()); + if (!isPublishConnected() && !publishReconnectionInProgress) { + publishReconnectionInProgress = true; + publishReconnectionHandler.postDelayed(publishReconnectorRunnable, PEER_RECONNECTION_DELAY_MS); + Log.d(TAG, "------------------------------------- Publish Reconnection --------------------------------------"); + } + if (!isPlayConnected() && !playReconnectionInProgress) { + playReconnectionInProgress = true; + playReconnectionHandler.postDelayed(playReconnectorRunnable, PEER_RECONNECTION_DELAY_MS); + Log.d(TAG, "------------------------------------- Play Reconection --------------------------------------"); + } + } else { + Log.i(TAG, "Peer was connected before. Will try to republish/replay in " + PEER_RECONNECTION_DELAY_MS + " ms."); + peerReconnectionHandler.postDelayed(peerReconnectorRunnable, PEER_RECONNECTION_DELAY_MS); + } } } @@ -1366,21 +1383,16 @@ public void onIceDisconnected(String streamId) { this.handler.post(() -> { Log.d(TAG, "ICE disconnected"); + if (config.reconnectionEnabled) { + rePublishPlay(); + } + if (config.webRTCListener != null) { - config.webRTCListener.onIceDisconnected(streamId); + //config.webRTCListener.onIceDisconnected(streamId); } if (streamStoppedByUser) { release(true); - return; - } - - if (config.reconnectionEnabled) { - rePublishPlay(); - } - - if(isConference()){ - releaseRemoteRenderers(); } }); @@ -1418,21 +1430,21 @@ public void onIceFailed(String streamId) { }); } - private boolean isPublishConnected(){ + public boolean isPublishConnected(){ for (Map.Entry entry : peers.entrySet()) { PeerConnection peerConnection = entry.getValue().peerConnection; if(peerConnection == null){ return false; } - PeerConnection.PeerConnectionState peerConnectionState = peerConnection.connectionState(); - if(entry.getValue().mode == Mode.PUBLISH && peerConnectionState != PeerConnection.PeerConnectionState.CONNECTED){ + PeerConnection.IceConnectionState peerConnectionState = peerConnection.iceConnectionState(); + if(entry.getValue().mode == Mode.PUBLISH && peerConnectionState != PeerConnection.IceConnectionState.CONNECTED){ return false; } } return true; } - private boolean isPlayConnected(){ + public boolean isPlayConnected(){ for (Map.Entry entry : peers.entrySet()) { PeerConnection peerConnection = entry.getValue().peerConnection; if(peerConnection == null){ @@ -1446,22 +1458,26 @@ private boolean isPlayConnected(){ return true; } + @Override + public boolean isPlayReconnecting() { + return playReconnectionInProgress; + } + public void onConnected(String streamId) { Log.i(TAG, "Connected for streamId:" + streamId); - if(config.reconnectionEnabled && reconnectionInProgress && isConference() && isPublishConnected() && !isPlayConnected()){ + if(config.reconnectionEnabled && isConference() && isPublishConnected()){ Log.i(TAG,"Conference reconnection. Publish connected. Play not connected. Try to reconnect play."); publishReconnectionHandler.removeCallbacksAndMessages(null); - playReconnectionHandler.postDelayed(playReconnectorRunnable, PEER_RECONNECTION_DELAY_MS); - return; + publishReconnectionInProgress = false; } - if (config.reconnectionEnabled && reconnectionInProgress && isAllPeersConnected()) { + if (config.reconnectionEnabled && isAllPeersConnected()) { Log.i(TAG, "All peers reconnected. Reconnection completed successfully."); - reconnectionInProgress = false; peerReconnectionHandler.removeCallbacksAndMessages(null); publishReconnectionHandler.removeCallbacksAndMessages(null); playReconnectionHandler.removeCallbacksAndMessages(null); + playReconnectionInProgress = false; this.handler.post(() -> { if (config.webRTCListener != null) { config.webRTCListener.onReconnectionSuccess(); @@ -1513,6 +1529,9 @@ public void onTakeConfiguration(String streamId, SessionDescription sdp) { @Override public void onPublishFinished(String streamId) { + if(!isStreamStoppedByUser()){ + rePublishPlay(); + } this.handler.post(() -> { if (config.webRTCListener != null) { config.webRTCListener.onPublishFinished(streamId); @@ -1524,6 +1543,9 @@ public void onPublishFinished(String streamId) { @Override public void onPlayFinished(String streamId) { waitingForPlay = false; + if(!isStreamStoppedByUser()){ + rePublishPlay(); + } this.handler.post(() -> { if (config.webRTCListener != null) { config.webRTCListener.onPlayFinished(streamId); @@ -1535,6 +1557,7 @@ public void onPlayFinished(String streamId) { public void onPublishStarted(String streamId) { Log.d(TAG,"Publish started."); streamStoppedByUser = false; + publishReconnectionInProgress = false; this.handler.post(() -> { if (config.webRTCListener != null) { @@ -1549,7 +1572,7 @@ public void onPlayStarted(String streamId) { Log.d(TAG, "Play started."); streamStoppedByUser = false; - reconnectionInProgress = false; + playReconnectionInProgress = false; waitingForPlay = false; this.handler.post(() -> { @@ -1595,6 +1618,7 @@ public void onLeftTheRoom(String roomId) { config.webRTCListener.onLeftTheRoom(roomId); } + @Override public void onSessionRestored(String streamId) { streamStoppedByUser = false; @@ -1609,7 +1633,7 @@ public void onSessionRestored(String streamId) { public void onBroadcastObject(Broadcast broadcast) { this.handler.post(() -> { if (config.webRTCListener != null) { - config.webRTCListener.onBroadcastObject(broadcast); + //config.webRTCListener.onBroadcastObject(broadcast); } }); } @@ -2205,7 +2229,8 @@ public void closeInternal() { onPeerConnectionClosed(); clearStatsCollector(); - reconnectionInProgress = false; + playReconnectionInProgress = false; + publishReconnectionInProgress = false; peerReconnectionHandler.removeCallbacksAndMessages(null); publishReconnectionHandler.removeCallbacksAndMessages(null); playReconnectionHandler.removeCallbacksAndMessages(null); @@ -2794,7 +2819,7 @@ public void setAutoPlayTracks(boolean autoPlayTracks) { } public boolean isReconnectionInProgress() { - return reconnectionInProgress; + return publishReconnectionInProgress || playReconnectionInProgress; } public CustomWebRtcAudioRecord getAudioInput() { diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/websocket/WebSocketHandler.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/websocket/WebSocketHandler.java index 19c5fcad..fcda8dc9 100644 --- a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/websocket/WebSocketHandler.java +++ b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/websocket/WebSocketHandler.java @@ -17,23 +17,31 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; -import de.tavendo.autobahn.WebSocket; -import de.tavendo.autobahn.WebSocketConnection; -import de.tavendo.autobahn.WebSocketException; import io.antmedia.webrtcandroidframework.core.StreamInfo; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.WebSocket; +import okhttp3.WebSocketListener; +import okio.ByteString; import static io.antmedia.webrtcandroidframework.websocket.WebSocketConstants.DEFINITION; import static io.antmedia.webrtcandroidframework.websocket.WebSocketConstants.NOTIFICATION_COMMAND; import static io.antmedia.webrtcandroidframework.websocket.WebSocketConstants.WEBSOCKET_CONNECTION_TIMEOUT; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + import com.google.gson.Gson; import com.google.gson.GsonBuilder; -public class WebSocketHandler implements WebSocket.WebSocketConnectionObserver { +public class WebSocketHandler extends WebSocketListener{ private static final String TAG = "WebSocketHandler"; private static final int CLOSE_TIMEOUT = 1000; - private WebSocketConnection ws; + public boolean isWsOpen = false; + + private WebSocket ws; private final Handler handler; private String wsServerUrl; private final Object closeEventLock = new Object(); @@ -50,6 +58,7 @@ public class WebSocketHandler implements WebSocket.WebSocketConnectionObserver { private Handler wsReconnectionHandler = new Handler(); + OkHttpClient client = new OkHttpClient(); Gson gson; @@ -62,53 +71,39 @@ public WebSocketHandler(AntMediaSignallingEvents signallingListener, Handler han gson = builder.create(); } - public WebSocketConnection creteWebSocket(){ - return new WebSocketConnection(); + public WebSocket connectWebSocket(String wsServerUrl){ + synchronized (this) { + if (isWsOpen) { + ws.cancel(); + } + Request request = new Request.Builder().url(wsServerUrl).build(); + return client.newWebSocket(request, this); + } } public void connect(final String wsUrl) { checkIfCalledOnValidThread(); if(wsUrl==null || wsUrl.isBlank()) return; wsServerUrl = wsUrl; - - ws = creteWebSocket(); - //Thread connectorThread = new Thread(() -> { - try { - ws.connect(new URI(wsServerUrl), this); - } catch (WebSocketException e) { - e.printStackTrace(); - disconnect(false); - } catch (URISyntaxException e) { - e.printStackTrace(); - disconnect(false); - } - //}); - /* - connectorThread.start(); - handler.postDelayed(new Runnable() { - public void run() { - if (connectorThread.isAlive()) { - connectorThread.interrupt(); - Log.e(TAG, "exception occurred while waiting for websocket"); - } - } - },WEBSOCKET_CONNECTION_TIMEOUT); - */ + ws = connectWebSocket(wsServerUrl); } public void sendTextMessage(String message) { - if (ws.isConnected()) { - ws.sendTextMessage(message); - Log.e(TAG, "sent websocket message:" + message); - } else { - Log.d(TAG, "Web Socket is not connected"); - } + handler.post(()->{ + if (isConnected()) { + ws.send(message); + Log.e(TAG, "sent websocket message:" + message); + } else { + Log.d(TAG, "Web Socket is not connected"); + } + }); } public void disconnect(boolean waitForComplete) { - checkIfCalledOnValidThread(); Log.d(TAG, "Disconnect WebSocket."); - ws.disconnect(); + ws.close(1000, "Disconnecting WebSocket"); + ws.cancel(); + stopPingPongTimer(); // Wait for websocket close event to prevent websocket library from // sending any pending messages to deleted looper thread. if (waitForComplete) { @@ -134,24 +129,33 @@ public void checkIfCalledOnValidThread() { } @Override - public void onOpen() { + public void onOpen(@NonNull WebSocket webSocket, @NonNull Response response) { Log.d(TAG, "WebSocket connection opened."); - signallingListener.onWebSocketConnected(); + handler.post(() -> { + isWsOpen = true; + signallingListener.onWebSocketConnected(); + startPingPongTimer(); + }); } @Override - public void onClose(WebSocketCloseNotification webSocketCloseNotification, String s) { - Log.d(TAG, "WebSocket connection closed."); - signallingListener.onWebSocketDisconnected(); - synchronized (closeEventLock) { - closeEvent = true; - closeEventLock.notify(); - stopPingPongTimer(); + public void onClosed(@NonNull WebSocket webSocket, int code, @NonNull String reason) { + synchronized (this) { + isWsOpen = false; + handler.post(() -> { + Log.d(TAG, "WebSocket connection closed."); + signallingListener.onWebSocketDisconnected(); + synchronized (closeEventLock) { + closeEvent = true; + closeEventLock.notify(); + stopPingPongTimer(); + } + }); } } @Override - public void onTextMessage(String msg) { + public void onMessage(@NonNull WebSocket webSocket, @NonNull String msg) { Log.e(TAG, "onTextMessage: "+msg); if (!isConnected()) { Log.e(TAG, "Got WebSocket message in non registered state."); @@ -218,11 +222,9 @@ else if (commandText.equals(NOTIFICATION_COMMAND)) { Log.d(TAG, "notification: " + definition); if (definition.equals(WebSocketConstants.PUBLISH_STARTED)) { signallingListener.onPublishStarted(streamId); - startPingPongTimer(); } else if (definition.equals(WebSocketConstants.PUBLISH_FINISHED)) { signallingListener.onPublishFinished(streamId); - stopPingPongTimer(); } else if (definition.equals(WebSocketConstants.PLAY_STARTED)) { signallingListener.onPlayStarted(streamId); @@ -230,6 +232,9 @@ else if (definition.equals(WebSocketConstants.PLAY_STARTED)) { } else if (definition.equals(WebSocketConstants.PLAY_FINISHED)) { signallingListener.onPlayFinished(streamId); + } + else if(definition.equals(WebSocketConstants.PLAY_FINISHED)){ + } else if (definition.equals(WebSocketConstants.SESSION_RESTORED_DESCRIPTION)) { signallingListener.onSessionRestored(streamId); @@ -329,12 +334,19 @@ else if (commandText.equals(WebSocketConstants.PONG_COMMAND)) } @Override - public void onRawTextMessage(byte[] bytes) { - - } - - @Override - public void onBinaryMessage(byte[] bytes) { + public void onFailure(@NonNull WebSocket webSocket, @NonNull Throwable t, @Nullable Response response) { + + synchronized (this) { + isWsOpen = false; + handler.post(() -> { + signallingListener.onWebSocketDisconnected(); + stopPingPongTimer(); + System.out.println("⚠️ WebSocket failure: " + t.getClass().getName() + " - " + t.getMessage()); + if (response != null) { + System.out.println("HTTP code: " + response.code() + " / message: " + response.message()); + } + }); + } } @@ -502,36 +514,43 @@ public void enableTrack(String streamId, String trackId, boolean enabled) { public void startPingPongTimer(){ - Log.d(TAG, "Ping Pong timer is started"); - - Runnable timerTask = new Runnable() { - @Override - public void run() { - Log.d(TAG, "Ping Pong timer is executed"); - sendPingPongMessage(); - if (pingPongTimoutCount == 2){ - Log.d(TAG, "Ping Pong websocket response not received for 4 seconds"); - stopPingPongTimer(); - disconnect(true); - } - pingPongTimoutCount++; - + synchronized (this) { + Log.d(TAG, "Ping Pong timer is started"); + if(this.pingPongExecutor != null) { + Log.d(TAG,"-================================== no starting ping pong as its already registerd"); + return; } - }; - pingPongExecutor = Executors.newSingleThreadScheduledExecutor(); - pingPongExecutor.scheduleAtFixedRate(timerTask, TIMER_DELAY, TIMER_PERIOD, TimeUnit.MILLISECONDS); + Runnable timerTask = new Runnable() { + @Override + public void run() { + Log.d(TAG, "Ping Pong timer is executed"); + sendPingPongMessage(); + if (pingPongTimoutCount >= 3) { + Log.d(TAG, "Ping Pong websocket response not received for 4 seconds"); + stopPingPongTimer(); + disconnect(true); + } + pingPongTimoutCount++; + + } + }; + + pingPongExecutor = Executors.newSingleThreadScheduledExecutor(); + pingPongExecutor.scheduleAtFixedRate(timerTask, TIMER_DELAY, TIMER_PERIOD, TimeUnit.MILLISECONDS); + } } public void stopPingPongTimer(){ + synchronized (this) { + Log.d(TAG, "Ping Pong timer stop called"); - Log.d(TAG, "Ping Pong timer stop called"); - - if (pingPongExecutor != null) { - pingPongExecutor.shutdown(); - pingPongExecutor = null; - pingPongTimoutCount = 0; + if (pingPongExecutor != null) { + pingPongExecutor.shutdown(); + pingPongExecutor = null; + pingPongTimoutCount = 0; + } } } @@ -614,7 +633,7 @@ public AntMediaSignallingEvents getSignallingListener() { } public boolean isConnected() { - return ws !=null && ws.isConnected(); + return ws !=null && isWsOpen; } public void forceStreamQuality(String mainTrackStreamId, String subTrackStreamId, int height) { diff --git a/webrtc-android-framework/src/test/java/io/antmedia/webrtcandroidframework/WebSocketHandlerTest.java b/webrtc-android-framework/src/test/java/io/antmedia/webrtcandroidframework/WebSocketHandlerTest.java index 079b2c23..4b83c0bb 100644 --- a/webrtc-android-framework/src/test/java/io/antmedia/webrtcandroidframework/WebSocketHandlerTest.java +++ b/webrtc-android-framework/src/test/java/io/antmedia/webrtcandroidframework/WebSocketHandlerTest.java @@ -19,8 +19,6 @@ import java.util.ArrayList; import java.util.concurrent.Executors; -import de.tavendo.autobahn.WebSocketConnection; -import de.tavendo.autobahn.WebSocketException; import io.antmedia.webrtcandroidframework.websocket.AntMediaSignallingEvents; import io.antmedia.webrtcandroidframework.websocket.Broadcast; import io.antmedia.webrtcandroidframework.websocket.WebSocketConstants; diff --git a/webrtc-android-framework/src/test/java/io/antmedia/webrtcandroidframework/api/DefaultConferenceWebRTCListenerTest.java b/webrtc-android-framework/src/test/java/io/antmedia/webrtcandroidframework/api/DefaultConferenceWebRTCListenerTest.java index d6642379..d2b74a85 100644 --- a/webrtc-android-framework/src/test/java/io/antmedia/webrtcandroidframework/api/DefaultConferenceWebRTCListenerTest.java +++ b/webrtc-android-framework/src/test/java/io/antmedia/webrtcandroidframework/api/DefaultConferenceWebRTCListenerTest.java @@ -19,7 +19,8 @@ import java.util.ArrayList; -import de.tavendo.autobahn.WebSocket; +import io.antmedia.webrtcandroidframework.core.WebRTCClient; + public class DefaultConferenceWebRTCListenerTest{ private String roomId; @@ -82,11 +83,6 @@ public void testOnError() { verify(defaultWebRTCListener, times(1)).callbackCalled(anyString()); } - @Test - public void testOnSignalChannelClosed() { - defaultWebRTCListener.onSignalChannelClosed(WebSocket.WebSocketConnectionObserver.WebSocketCloseNotification.NORMAL, "streamId"); - verify(defaultWebRTCListener, times(1)).callbackCalled(anyString()); - } @Test public void testStreamIdInUse() { @@ -157,7 +153,7 @@ public void testOnVideoTrackEnded() { @Test public void testOnReconnectionAttempt() { - defaultWebRTCListener.onReconnectionAttempt("streamId"); + defaultWebRTCListener.onReconnectionAttempt("streamId", WebRTCClient.Mode.PUBLISH); verify(defaultWebRTCListener, times(1)).callbackCalled(anyString()); } @@ -199,16 +195,16 @@ public void testOnSatatusUpdateFor() { @Test public void testReconnecting() { - defaultWebRTCListener.onReconnectionAttempt(roomId); + defaultWebRTCListener.onReconnectionAttempt(roomId,WebRTCClient.Mode.PUBLISH); assertFalse(defaultWebRTCListener.isPublishReconnectingForTest()); - defaultWebRTCListener.onReconnectionAttempt(streamId); + defaultWebRTCListener.onReconnectionAttempt(streamId,WebRTCClient.Mode.PUBLISH); assertTrue(defaultWebRTCListener.isPublishReconnectingForTest()); defaultWebRTCListener.onPublishStarted(streamId); assertFalse(defaultWebRTCListener.isPublishReconnectingForTest()); - defaultWebRTCListener.onReconnectionAttempt(streamId); + defaultWebRTCListener.onReconnectionAttempt(streamId,WebRTCClient.Mode.PUBLISH); assertTrue(defaultWebRTCListener.isPublishReconnectingForTest()); defaultWebRTCListener.onSessionRestored(streamId); @@ -224,7 +220,7 @@ public void testStartAfterPublishStarted() { verify(mockWebRTCClient, times(1)).play(roomId); //playStarted false, but play should not be called because publish is reconnecting state - defaultWebRTCListener.onReconnectionAttempt(streamId); + defaultWebRTCListener.onReconnectionAttempt(streamId,WebRTCClient.Mode.PUBLISH); assertTrue(defaultWebRTCListener.isPublishReconnectingForTest()); defaultWebRTCListener.onPublishStarted(streamId); verify(mockWebRTCClient, times(2)).play(roomId); diff --git a/webrtc-android-framework/src/test/java/io/antmedia/webrtcandroidframework/api/DefaultWebRTCListenerTest.java b/webrtc-android-framework/src/test/java/io/antmedia/webrtcandroidframework/api/DefaultWebRTCListenerTest.java index c5360854..f1387981 100644 --- a/webrtc-android-framework/src/test/java/io/antmedia/webrtcandroidframework/api/DefaultWebRTCListenerTest.java +++ b/webrtc-android-framework/src/test/java/io/antmedia/webrtcandroidframework/api/DefaultWebRTCListenerTest.java @@ -7,7 +7,8 @@ import org.mockito.MockitoAnnotations; import org.webrtc.SurfaceViewRenderer; import org.webrtc.VideoTrack; -import de.tavendo.autobahn.WebSocket; + +import io.antmedia.webrtcandroidframework.core.WebRTCClient; import io.antmedia.webrtcandroidframework.websocket.Broadcast; import java.util.ArrayList; @@ -72,11 +73,6 @@ public void testOnError() { verify(defaultWebRTCListener, times(1)).callbackCalled(anyString()); } - @Test - public void testOnSignalChannelClosed() { - defaultWebRTCListener.onSignalChannelClosed(WebSocket.WebSocketConnectionObserver.WebSocketCloseNotification.NORMAL, "streamId"); - verify(defaultWebRTCListener, times(1)).callbackCalled(anyString()); - } @Test public void testStreamIdInUse() { @@ -147,7 +143,7 @@ public void testOnVideoTrackEnded() { @Test public void testOnReconnectionAttempt() { - defaultWebRTCListener.onReconnectionAttempt("streamId"); + defaultWebRTCListener.onReconnectionAttempt("streamId", WebRTCClient.Mode.PUBLISH); verify(defaultWebRTCListener, times(1)).callbackCalled(anyString()); } diff --git a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/basic/DynamicConferenceActivity.java b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/basic/DynamicConferenceActivity.java index 1c9f2646..97854344 100644 --- a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/basic/DynamicConferenceActivity.java +++ b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/basic/DynamicConferenceActivity.java @@ -26,6 +26,7 @@ import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; @@ -39,9 +40,11 @@ import io.antmedia.webrtcandroidframework.api.IDataChannelObserver; import io.antmedia.webrtcandroidframework.api.IWebRTCClient; import io.antmedia.webrtcandroidframework.core.DataChannelConstants; +import io.antmedia.webrtcandroidframework.core.WebRTCClient; import io.antmedia.webrtcandroidframework.core.model.PlayStats; import io.antmedia.webrtcandroidframework.core.model.TrackStats; import io.antmedia.webrtcandroidframework.core.PermissionHandler; +import io.antmedia.webrtcandroidframework.websocket.Broadcast; public class DynamicConferenceActivity extends TestableActivity { @@ -102,6 +105,8 @@ public class DynamicConferenceActivity extends TestableActivity { */ private HashMap streamIdVideoTrackMap = new HashMap<>(); + private List allParticipants = new ArrayList<>(); + @Override protected void onCreate(Bundle savedInstanceState) { @@ -222,10 +227,17 @@ public void textMessageReceived(String messageText) { super.textMessageReceived(messageText); try{ JSONObject msgJsonObj = new JSONObject(messageText); - if(msgJsonObj.has(DataChannelConstants.EVENT_TYPE) && msgJsonObj.getString(DataChannelConstants.EVENT_TYPE).equals(DataChannelConstants.VIDEO_TRACK_ASSIGNMENT_LIST)){ - trackAssignments = msgJsonObj.getJSONArray(DataChannelConstants.PAYLOAD); - matchStreamIdAndVideoTrack(); + if(msgJsonObj.has(DataChannelConstants.EVENT_TYPE)){ + String eventType = msgJsonObj.getString(DataChannelConstants.EVENT_TYPE); + if(eventType.equals(DataChannelConstants.VIDEO_TRACK_ASSIGNMENT_LIST)){ + trackAssignments = msgJsonObj.getJSONArray(DataChannelConstants.PAYLOAD); + matchStreamIdAndVideoTrack(); + } + else if(eventType.equals(DataChannelConstants.TRACK_LIST_UPDATED)){ + webRTCClient.getBroadcastObject(streamId); + } } + }catch (Exception e){ Log.e(getClass().getSimpleName(),"Cant parse data channel message to JSON object. "+e.getMessage()); } @@ -262,6 +274,21 @@ public void onPublishStarted(String streamId) { publishStarted = true; joinButton.setEnabled(true); } + @Override + public void onReconnectionAttempt(String streamId, WebRTCClient.Mode mode) { + if(mode == WebRTCClient.Mode.PLAY) + removeAllRenderers(); + } + + @Override + public void onBroadcastObject(Broadcast broadcast) { + if (broadcast.getStreamId().equals(streamId)) { + handleMainTrackBroadcastObject(broadcast); + }else{ + handleSubTrackBroadcastObject(broadcast); + } + + } @Override public void onShutdown() { @@ -313,6 +340,14 @@ public void onPlayStarted(String streamId) { decrementIdle(); joinButton.setEnabled(true); } + public SurfaceViewRenderer getVideoRenderer(VideoTrack track){ +// for (SurfaceViewRenderer r : webRTCClient.getConfig().remoteVideoRenderers) { +// if (videoTrackID != null && !videoTrackID.isEmpty() && videoTrackID.equals(track.id())) { +// return r; +// } +// } + return addSurfaceViewRenderer(); + } @Override public void onVideoTrackEnded(VideoTrack track) { String messageText = "Video track ended"; @@ -334,11 +369,14 @@ public void onNewVideoTrack(VideoTrack track, String trackId) { callbackCalled(messageText); runOnUiThread(() -> { - SurfaceViewRenderer r = addSurfaceViewRenderer(); + SurfaceViewRenderer r = getVideoRenderer(track); + r.setTag(R.id.accelerate,track.id()); + if (r.getTag() == null) { r.setTag(track); webRTCClient.setRendererForVideoTrack(r, track); } + r.setTag(R.id.accelerate,track.id()); videoTrackList.add(track); if(trackAssignments != null){ matchStreamIdAndVideoTrack(); @@ -365,7 +403,6 @@ public void toggleSendVideo() { if(webRTCClient.isSendVideoEnabled()){ webRTCClient.toggleSendVideo(false); toggleSendVideoButton.setImageDrawable(ContextCompat.getDrawable(getApplicationContext(), R.drawable.ic_camera_off)); - }else{ webRTCClient.toggleSendVideo(true); toggleSendVideoButton.setImageDrawable(ContextCompat.getDrawable(getApplicationContext(), R.drawable.ic_camera_on)); @@ -584,14 +621,26 @@ public void removeSurfaceViewRenderer(SurfaceViewRenderer renderer){ remoteParticipantsGridLayout.removeView(renderer); webRTCClient.getConfig().remoteVideoRenderers.remove(renderer); } + public void handleSubTrackBroadcastObject(Broadcast broadcast){ + allParticipants.add(broadcast.getStreamId()); + } + public void handleMainTrackBroadcastObject(Broadcast broadcast){ + List currentTracks = allParticipants; + List subTracks = broadcast.getSubTrackStreamIds(); + + for(String trackId: currentTracks){ + if (!subTracks.contains(trackId)) { + allParticipants.remove(trackId); + } + } + } + public void removeAllRenderers(){ ArrayList toRemove = new ArrayList<>(webRTCClient.getConfig().remoteVideoRenderers); for (SurfaceViewRenderer r : toRemove) { webRTCClient.releaseRenderer(r); - runOnUiThread(() -> { - remoteParticipantsGridLayout.removeView(r); - webRTCClient.getConfig().remoteVideoRenderers.remove(r); - }); + remoteParticipantsGridLayout.removeView(r); + webRTCClient.getConfig().remoteVideoRenderers.remove(r); } } } From 7e88f5fbc5065d65dfcda9ac3d6a4737d05f17a9 Mon Sep 17 00:00:00 2001 From: USAMAWIZARD Date: Fri, 12 Sep 2025 23:50:26 +0530 Subject: [PATCH 02/28] update reconneciton mechanism --- .../core/WebRTCClient.java | 25 +++++++++++++++---- .../basic/DynamicConferenceActivity.java | 1 - 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java index 4e5ff3ba..bc0b682d 100644 --- a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java +++ b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java @@ -1388,7 +1388,7 @@ public void onIceDisconnected(String streamId) { } if (config.webRTCListener != null) { - //config.webRTCListener.onIceDisconnected(streamId); + config.webRTCListener.onIceDisconnected(streamId); } if (streamStoppedByUser) { @@ -1466,10 +1466,25 @@ public boolean isPlayReconnecting() { public void onConnected(String streamId) { Log.i(TAG, "Connected for streamId:" + streamId); - if(config.reconnectionEnabled && isConference() && isPublishConnected()){ - Log.i(TAG,"Conference reconnection. Publish connected. Play not connected. Try to reconnect play."); - publishReconnectionHandler.removeCallbacksAndMessages(null); - publishReconnectionInProgress = false; + if(isConference() && config.reconnectionEnabled){ + if(isPublishConnected()){ + Log.i(TAG,"Conference reconnection. Publish connected. Play not connected. Try to reconnect play."); + publishReconnectionHandler.removeCallbacksAndMessages(null); + publishReconnectionInProgress = false; + } + if(isPlayConnected()){ + playReconnectionHandler.removeCallbacksAndMessages(null); + playReconnectionInProgress = false; + } + if(isPlayConnected() && isPlayConnected()){ + this.handler.post(() -> { + if (config.webRTCListener != null) { + config.webRTCListener.onReconnectionSuccess(); + } + }); + } + streamStoppedByUser = false; + return; } if (config.reconnectionEnabled && isAllPeersConnected()) { diff --git a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/basic/DynamicConferenceActivity.java b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/basic/DynamicConferenceActivity.java index 97854344..2d6a6ab6 100644 --- a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/basic/DynamicConferenceActivity.java +++ b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/basic/DynamicConferenceActivity.java @@ -314,7 +314,6 @@ public void onIceDisconnected(String streamId) { }else{ statusIndicatorTextView.setTextColor(getResources().getColor(R.color.red)); statusIndicatorTextView.setText(getResources().getString(R.string.disconnected)); - removeAllRenderers(); joinButton.setEnabled(true); joinButton.setImageDrawable(ContextCompat.getDrawable(getApplicationContext(), R.drawable.ic_join_call)); } From b510e8def8b4e4eaa99f24c4e320f71669f0e7da Mon Sep 17 00:00:00 2001 From: USAMAWIZARD Date: Mon, 15 Sep 2025 15:06:14 +0530 Subject: [PATCH 03/28] update reconneciton mechanism --- .../api/IWebRTCListener.java | 1 + .../core/WebRTCClient.java | 6 ++-- .../websocket/WebSocketHandler.java | 9 +----- .../WebSocketHandlerTest.java | 20 ++++++------- .../basic/DynamicConferenceActivity.java | 28 +------------------ 5 files changed, 16 insertions(+), 48 deletions(-) diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/api/IWebRTCListener.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/api/IWebRTCListener.java index 113cfaa0..47917f65 100644 --- a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/api/IWebRTCListener.java +++ b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/api/IWebRTCListener.java @@ -250,3 +250,4 @@ public interface IWebRTCListener { */ void onLeft(String streamId); } + diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java index bc0b682d..12496332 100644 --- a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java +++ b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java @@ -1064,7 +1064,7 @@ public void play(String streamId, String token, String[] tracks, String subscrib }); createPeerInfo(streamId, token, false, false, subscriberId, subscriberCode, "", "", viewerInfo, Mode.PLAY); - if (playReconnectionInProgress) { + if (!isPlayReconnecting()) { init(); } @@ -1369,7 +1369,7 @@ public void rePublishPlay() { if (!isPlayConnected() && !playReconnectionInProgress) { playReconnectionInProgress = true; playReconnectionHandler.postDelayed(playReconnectorRunnable, PEER_RECONNECTION_DELAY_MS); - Log.d(TAG, "------------------------------------- Play Reconection --------------------------------------"); + Log.d(TAG, "------------------------------------- Play Reconnection --------------------------------------"); } } else { Log.i(TAG, "Peer was connected before. Will try to republish/replay in " + PEER_RECONNECTION_DELAY_MS + " ms."); @@ -1648,7 +1648,7 @@ public void onSessionRestored(String streamId) { public void onBroadcastObject(Broadcast broadcast) { this.handler.post(() -> { if (config.webRTCListener != null) { - //config.webRTCListener.onBroadcastObject(broadcast); + config.webRTCListener.onBroadcastObject(broadcast); } }); } diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/websocket/WebSocketHandler.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/websocket/WebSocketHandler.java index fcda8dc9..9e4bcc62 100644 --- a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/websocket/WebSocketHandler.java +++ b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/websocket/WebSocketHandler.java @@ -10,8 +10,6 @@ import org.webrtc.IceCandidate; import org.webrtc.SessionDescription; -import java.net.URI; -import java.net.URISyntaxException; import java.util.ArrayList; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; @@ -23,7 +21,6 @@ import okhttp3.Response; import okhttp3.WebSocket; import okhttp3.WebSocketListener; -import okio.ByteString; import static io.antmedia.webrtcandroidframework.websocket.WebSocketConstants.DEFINITION; import static io.antmedia.webrtcandroidframework.websocket.WebSocketConstants.NOTIFICATION_COMMAND; @@ -232,9 +229,6 @@ else if (definition.equals(WebSocketConstants.PLAY_STARTED)) { } else if (definition.equals(WebSocketConstants.PLAY_FINISHED)) { signallingListener.onPlayFinished(streamId); - } - else if(definition.equals(WebSocketConstants.PLAY_FINISHED)){ - } else if (definition.equals(WebSocketConstants.SESSION_RESTORED_DESCRIPTION)) { signallingListener.onSessionRestored(streamId); @@ -515,9 +509,7 @@ public void enableTrack(String streamId, String trackId, boolean enabled) { public void startPingPongTimer(){ synchronized (this) { - Log.d(TAG, "Ping Pong timer is started"); if(this.pingPongExecutor != null) { - Log.d(TAG,"-================================== no starting ping pong as its already registerd"); return; } @@ -537,6 +529,7 @@ public void run() { }; pingPongExecutor = Executors.newSingleThreadScheduledExecutor(); + Log.d(TAG, "Ping Pong timer is started"); pingPongExecutor.scheduleAtFixedRate(timerTask, TIMER_DELAY, TIMER_PERIOD, TimeUnit.MILLISECONDS); } diff --git a/webrtc-android-framework/src/test/java/io/antmedia/webrtcandroidframework/WebSocketHandlerTest.java b/webrtc-android-framework/src/test/java/io/antmedia/webrtcandroidframework/WebSocketHandlerTest.java index 4b83c0bb..50ea369e 100644 --- a/webrtc-android-framework/src/test/java/io/antmedia/webrtcandroidframework/WebSocketHandlerTest.java +++ b/webrtc-android-framework/src/test/java/io/antmedia/webrtcandroidframework/WebSocketHandlerTest.java @@ -19,10 +19,12 @@ import java.util.ArrayList; import java.util.concurrent.Executors; +import de.tavendo.autobahn.WebSocketConnection; import io.antmedia.webrtcandroidframework.websocket.AntMediaSignallingEvents; import io.antmedia.webrtcandroidframework.websocket.Broadcast; import io.antmedia.webrtcandroidframework.websocket.WebSocketConstants; import io.antmedia.webrtcandroidframework.websocket.WebSocketHandler; +import okhttp3.WebSocket; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.*; @@ -39,7 +41,7 @@ public class WebSocketHandlerTest { private Handler handler; @Mock - private WebSocketConnection ws; + private WebSocket ws; @Mock private IceCandidate iceCandidate; @@ -61,7 +63,7 @@ public void testOnTextMessageStartCommand() { String message = "{\"command\": \"start\", \"streamId\": \"stream123\"}"; doReturn(true).when(webSocketHandler).isConnected(); - webSocketHandler.onTextMessage(message); + webSocketHandler.onMessage(ws,message); verify(signallingListener).onStartStreaming("stream123"); @@ -85,7 +87,7 @@ public void testOnTextMessageTakeConfigurationCommand() { } String message = json.toString(); - webSocketHandler.onTextMessage(message); + webSocketHandler.onMessage(ws,message); verify(signallingListener).onTakeConfiguration(eq(streamId), any(SessionDescription.class)); } @@ -109,7 +111,7 @@ public void testOnTextMessageTakeCandidateCommand() { String message = json.toString(); - webSocketHandler.onTextMessage(message); + webSocketHandler.onMessage(ws,message); verify(signallingListener).onRemoteIceCandidate(eq(streamId), any(IceCandidate.class)); @@ -129,7 +131,7 @@ public void testOnTextMessageLeavedTheRoomNotification() { doReturn(true).when(webSocketHandler).isConnected(); String message = json.toString(); - webSocketHandler.onTextMessage(message); + webSocketHandler.onMessage(ws,message); verify(signallingListener).onLeftTheRoom(null); } @@ -442,7 +444,7 @@ public void testOnBroadcastObjectNotification() { String message = json.toString(); - webSocketHandler.onTextMessage(message); + webSocketHandler.onMessage(ws,message); ArgumentCaptor captor = ArgumentCaptor.forClass(Broadcast.class); verify(signallingListener).onBroadcastObject(captor.capture()); @@ -455,12 +457,10 @@ public void testOnBroadcastObjectNotification() { } @Test - public void testWsConnect() throws InterruptedException, URISyntaxException, WebSocketException { + public void testWsConnect() throws InterruptedException, URISyntaxException { String url = "wss://test.antmedia.io:5443/LiveApp/websocket"; - doReturn(ws).when(webSocketHandler).creteWebSocket(); webSocketHandler.connect(url); - Thread.sleep(3000); - verify(ws,times(1)).connect(new URI(url),webSocketHandler); + verify(webSocketHandler,times(1)).connectWebSocket(url); } @Test diff --git a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/basic/DynamicConferenceActivity.java b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/basic/DynamicConferenceActivity.java index 2d6a6ab6..d20a156d 100644 --- a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/basic/DynamicConferenceActivity.java +++ b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/basic/DynamicConferenceActivity.java @@ -282,11 +282,6 @@ public void onReconnectionAttempt(String streamId, WebRTCClient.Mode mode) { @Override public void onBroadcastObject(Broadcast broadcast) { - if (broadcast.getStreamId().equals(streamId)) { - handleMainTrackBroadcastObject(broadcast); - }else{ - handleSubTrackBroadcastObject(broadcast); - } } @@ -339,14 +334,6 @@ public void onPlayStarted(String streamId) { decrementIdle(); joinButton.setEnabled(true); } - public SurfaceViewRenderer getVideoRenderer(VideoTrack track){ -// for (SurfaceViewRenderer r : webRTCClient.getConfig().remoteVideoRenderers) { -// if (videoTrackID != null && !videoTrackID.isEmpty() && videoTrackID.equals(track.id())) { -// return r; -// } -// } - return addSurfaceViewRenderer(); - } @Override public void onVideoTrackEnded(VideoTrack track) { String messageText = "Video track ended"; @@ -368,7 +355,7 @@ public void onNewVideoTrack(VideoTrack track, String trackId) { callbackCalled(messageText); runOnUiThread(() -> { - SurfaceViewRenderer r = getVideoRenderer(track); + SurfaceViewRenderer r = addSurfaceViewRenderer(); r.setTag(R.id.accelerate,track.id()); if (r.getTag() == null) { @@ -620,19 +607,6 @@ public void removeSurfaceViewRenderer(SurfaceViewRenderer renderer){ remoteParticipantsGridLayout.removeView(renderer); webRTCClient.getConfig().remoteVideoRenderers.remove(renderer); } - public void handleSubTrackBroadcastObject(Broadcast broadcast){ - allParticipants.add(broadcast.getStreamId()); - } - public void handleMainTrackBroadcastObject(Broadcast broadcast){ - List currentTracks = allParticipants; - List subTracks = broadcast.getSubTrackStreamIds(); - - for(String trackId: currentTracks){ - if (!subTracks.contains(trackId)) { - allParticipants.remove(trackId); - } - } - } public void removeAllRenderers(){ ArrayList toRemove = new ArrayList<>(webRTCClient.getConfig().remoteVideoRenderers); From e568eae4714d5985face17e846186f26ce56a7fb Mon Sep 17 00:00:00 2001 From: USAMAWIZARD Date: Mon, 15 Sep 2025 15:53:02 +0530 Subject: [PATCH 04/28] update reconneciton mechanism --- .../api/IWebRTCClient.java | 2 ++ .../core/WebRTCClient.java | 9 +++++ .../WebRTCClientTest.java | 36 +++++++++++++++++++ 3 files changed, 47 insertions(+) diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/api/IWebRTCClient.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/api/IWebRTCClient.java index 53c450a3..e00523e8 100644 --- a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/api/IWebRTCClient.java +++ b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/api/IWebRTCClient.java @@ -267,6 +267,8 @@ void publish(String streamId, String token, boolean videoCallEnabled, boolean au boolean isPlayReconnecting(); + boolean isPublishReconnecting(); + /** * Get the error * diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java index 12496332..6bcd5cbe 100644 --- a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java +++ b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java @@ -1402,6 +1402,10 @@ private boolean isConference(){ return roomId != null; } + public void setRoomId(String roomId) { + this.roomId = roomId; + } + private boolean isAllPeersConnected() { for (Map.Entry entry : peers.entrySet()) { PeerConnection peerConnection = entry.getValue().peerConnection; @@ -1462,6 +1466,11 @@ public boolean isPlayConnected(){ public boolean isPlayReconnecting() { return playReconnectionInProgress; } + @Override + public boolean isPublishReconnecting() { + return publishReconnectionInProgress; + } + public void onConnected(String streamId) { Log.i(TAG, "Connected for streamId:" + streamId); diff --git a/webrtc-android-framework/src/test/java/io/antmedia/webrtcandroidframework/WebRTCClientTest.java b/webrtc-android-framework/src/test/java/io/antmedia/webrtcandroidframework/WebRTCClientTest.java index 36dfefa7..895f71e2 100644 --- a/webrtc-android-framework/src/test/java/io/antmedia/webrtcandroidframework/WebRTCClientTest.java +++ b/webrtc-android-framework/src/test/java/io/antmedia/webrtcandroidframework/WebRTCClientTest.java @@ -1,6 +1,7 @@ package io.antmedia.webrtcandroidframework; import static org.awaitility.Awaitility.await; +import static org.awaitility.Awaitility.reset; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -661,6 +662,38 @@ public void testSendPlayOtherTracks() { assertEquals("other2", capturedTracks[2]); } + @Test + public void testRepublishPlay(){ + + WebRTCClient webRTCClientSpy = spy(IWebRTCClient.builder() + .setActivity(context) + .setWebRTCListener(listener) + .build()); + webRTCClientSpy.setRoomId("test"); + + // publish already connected + doReturn(true).when(webRTCClientSpy).isPublishConnected(); + doReturn(false).when(webRTCClientSpy).isPlayConnected(); + + webRTCClientSpy.rePublishPlay(); + assertTrue(webRTCClientSpy.isPlayReconnecting()); + assertFalse(webRTCClientSpy.isPublishReconnecting()); + + webRTCClientSpy = spy(IWebRTCClient.builder() + .setActivity(context) + .setWebRTCListener(listener) + .build()); + webRTCClientSpy.setRoomId("test"); + + //play already connected + doReturn(false).when(webRTCClientSpy).isPublishConnected(); + doReturn(true).when(webRTCClientSpy).isPlayConnected(); + + webRTCClientSpy.rePublishPlay(); + assertTrue(webRTCClientSpy.isPublishReconnecting()); + assertFalse(webRTCClientSpy.isPlayReconnecting()); + + } @Test @@ -692,6 +725,9 @@ public void testReconnection() { verify(wsHandler, timeout(WebRTCClient.PEER_RECONNECTION_DELAY_MS + 1000).atLeast(1)).startPublish(anyString(),anyString(),anyBoolean(),anyBoolean(),anyString(),anyString(),anyString(),anyString()); + + + } @Test From 697d12ed441285e5ad0bd55ee50287040d092a33 Mon Sep 17 00:00:00 2001 From: USAMAWIZARD Date: Mon, 22 Sep 2025 12:25:28 +0530 Subject: [PATCH 05/28] fix merge conflit --- .../webrtcandroidframework/core/WebRTCClient.java | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java index 9baccedb..1f57adba 100644 --- a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java +++ b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java @@ -994,16 +994,10 @@ private void publishPlayIfRequested() { Log.i(TAG, "Processing publish request for peer streamId: " + peerInfo.id); wsHandler.startPublish(peerInfo.id, peerInfo.token, peerInfo.videoCallEnabled, peerInfo.audioCallEnabled, peerInfo.subscriberId, peerInfo.subscriberCode, peerInfo.streamName, peerInfo.mainTrackId); } - - if (!playReconnectionInProgress && peerMode == Mode.PLAY && peerInfo.peerConnection == null) { - Log.i(TAG, "Processing play request for peer streamId: " + peerInfo.id); - wsHandler.startPlay(peerInfo.id, peerInfo.token, null, peerInfo.subscriberId, peerInfo.subscriberCode, peerInfo.metaData); - } - - if (peerMode == Mode.PLAY && peerInfo.peerConnection == null) { Log.i(TAG, "Processing play request for peer streamId: " + peerInfo.id); wsHandler.startPlay(peerInfo.id, peerInfo.token, null, peerInfo.subscriberId, peerInfo.subscriberName, peerInfo.subscriberCode, peerInfo.metaData, peerInfo.disableTracksByDefault); + } } } From 46824edf5c34e631c999007be65ccf89c07f32b5 Mon Sep 17 00:00:00 2001 From: USAMAWIZARD Date: Mon, 22 Sep 2025 14:17:23 +0530 Subject: [PATCH 06/28] update min sdk version --- webrtc-android-framework/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webrtc-android-framework/build.gradle b/webrtc-android-framework/build.gradle index b7d02c29..8e095f84 100644 --- a/webrtc-android-framework/build.gradle +++ b/webrtc-android-framework/build.gradle @@ -11,7 +11,7 @@ android { compileSdkVersion 34 defaultConfig { - minSdkVersion 21 + minSdkVersion 24 targetSdkVersion 34 testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" From bf8bdcd8d6b17d1ca76ae0ebeb0e91c551d55c5a Mon Sep 17 00:00:00 2001 From: USAMAWIZARD Date: Wed, 24 Sep 2025 12:01:28 +0530 Subject: [PATCH 07/28] imporove reconnection --- .../core/WebRTCClient.java | 14 ++-- .../websocket/WebSocketHandler.java | 74 +++++++++---------- 2 files changed, 44 insertions(+), 44 deletions(-) diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java index 1f57adba..e83ae305 100644 --- a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java +++ b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java @@ -674,7 +674,7 @@ public void onCreateSuccess(final SessionDescription desc) { PeerConnection pc = peerInfo.peerConnection; if (pc != null) { Log.d(TAG, "Set local SDP from " + desc.type); - pc.setLocalDescription(this, newDesc); + pc.setLocalDescription(this, newDesc); } }); } @@ -1386,6 +1386,7 @@ public void rePublishPlay() { } } else { Log.i(TAG, "Peer was connected before. Will try to republish/replay in " + PEER_RECONNECTION_DELAY_MS + " ms."); + publishReconnectionInProgress = true; peerReconnectionHandler.postDelayed(peerReconnectorRunnable, PEER_RECONNECTION_DELAY_MS); } } @@ -1396,18 +1397,18 @@ public void onIceDisconnected(String streamId) { this.handler.post(() -> { Log.d(TAG, "ICE disconnected"); - if (config.reconnectionEnabled) { - rePublishPlay(); - } - - if (config.webRTCListener != null) { + if (config.webRTCListener != null) { config.webRTCListener.onIceDisconnected(streamId); } if (streamStoppedByUser) { release(true); + return; } + if (config.reconnectionEnabled) { + rePublishPlay(); + } }); } @@ -1515,6 +1516,7 @@ public void onConnected(String streamId) { publishReconnectionHandler.removeCallbacksAndMessages(null); playReconnectionHandler.removeCallbacksAndMessages(null); playReconnectionInProgress = false; + publishReconnectionInProgress = false; this.handler.post(() -> { if (config.webRTCListener != null) { config.webRTCListener.onReconnectionSuccess(); diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/websocket/WebSocketHandler.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/websocket/WebSocketHandler.java index adb381b0..6270bdee 100644 --- a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/websocket/WebSocketHandler.java +++ b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/websocket/WebSocketHandler.java @@ -71,7 +71,7 @@ public WebSocketHandler(AntMediaSignallingEvents signallingListener, Handler han public WebSocket connectWebSocket(String wsServerUrl){ synchronized (this) { if (isWsOpen) { - ws.cancel(); + disconnect(true); } Request request = new Request.Builder().url(wsServerUrl).build(); return client.newWebSocket(request, this); @@ -79,28 +79,25 @@ public WebSocket connectWebSocket(String wsServerUrl){ } public void connect(final String wsUrl) { checkIfCalledOnValidThread(); - if(wsUrl==null || wsUrl.isBlank()) + if(wsUrl == null || wsUrl.isBlank()) return; wsServerUrl = wsUrl; ws = connectWebSocket(wsServerUrl); } public void sendTextMessage(String message) { - handler.post(()->{ - if (isConnected()) { - ws.send(message); - Log.e(TAG, "sent websocket message:" + message); - } else { - Log.d(TAG, "Web Socket is not connected"); - } - }); + if (isConnected()) { + ws.send(message); + Log.e(TAG, "sent websocket message:" + message); + } else { + Log.d(TAG, "Web Socket is not connected"); + } } public void disconnect(boolean waitForComplete) { Log.d(TAG, "Disconnect WebSocket."); - ws.close(1000, "Disconnecting WebSocket"); - ws.cancel(); stopPingPongTimer(); + ws.close(1000, "Disconnecting WebSocket"); // Wait for websocket close event to prevent websocket library from // sending any pending messages to deleted looper thread. if (waitForComplete) { @@ -127,25 +124,25 @@ public void checkIfCalledOnValidThread() { @Override public void onOpen(@NonNull WebSocket webSocket, @NonNull Response response) { - Log.d(TAG, "WebSocket connection opened."); - handler.post(() -> { + synchronized (this) { + Log.d(TAG, "WebSocket connection opened."); isWsOpen = true; - signallingListener.onWebSocketConnected(); startPingPongTimer(); - }); + signallingListener.onWebSocketConnected(); + } } @Override public void onClosed(@NonNull WebSocket webSocket, int code, @NonNull String reason) { synchronized (this) { - isWsOpen = false; + stopPingPongTimer(); handler.post(() -> { Log.d(TAG, "WebSocket connection closed."); signallingListener.onWebSocketDisconnected(); + isWsOpen = false; synchronized (closeEventLock) { closeEvent = true; closeEventLock.notify(); - stopPingPongTimer(); } }); } @@ -311,7 +308,6 @@ else if (commandText.equals(WebSocketConstants.ERROR_COMMAND)) String definition= json.getString(DEFINITION); Log.d(TAG, "error command received: "+ definition); - //stopPingPongTimer(); signallingListener.onError(streamId, definition); @@ -347,9 +343,9 @@ public void onFailure(@NonNull WebSocket webSocket, @NonNull Throwable t, @Nulla synchronized (this) { isWsOpen = false; + stopPingPongTimer(); handler.post(() -> { signallingListener.onWebSocketDisconnected(); - stopPingPongTimer(); System.out.println("⚠️ WebSocket failure: " + t.getClass().getName() + " - " + t.getMessage()); if (response != null) { System.out.println("HTTP code: " + response.code() + " / message: " + response.message()); @@ -556,25 +552,27 @@ public void startPingPongTimer(){ return; } - Runnable timerTask = new Runnable() { - @Override - public void run() { - Log.d(TAG, "Ping Pong timer is executed"); - sendPingPongMessage(); - if (pingPongTimoutCount >= 3) { - Log.d(TAG, "Ping Pong websocket response not received for 4 seconds"); - stopPingPongTimer(); - disconnect(true); - } - pingPongTimoutCount++; - + } + Runnable timerTask = new Runnable() { + @Override + public void run() { + Log.d(TAG, "Ping Pong timer is executed"); + sendPingPongMessage(); + if (pingPongTimoutCount >= 3) { + Log.d(TAG, "Ping Pong websocket response not received for 4 seconds"); + stopPingPongTimer(); + isWsOpen = false; + ws.cancel(); + disconnect(true); } - }; + pingPongTimoutCount++; - pingPongExecutor = Executors.newSingleThreadScheduledExecutor(); - Log.d(TAG, "Ping Pong timer is started"); - pingPongExecutor.scheduleAtFixedRate(timerTask, TIMER_DELAY, TIMER_PERIOD, TimeUnit.MILLISECONDS); - } + } + }; + + pingPongExecutor = Executors.newSingleThreadScheduledExecutor(); + Log.d(TAG, "Ping Pong timer is started"); + pingPongExecutor.scheduleAtFixedRate(timerTask, TIMER_DELAY, TIMER_PERIOD, TimeUnit.MILLISECONDS); } @@ -669,7 +667,7 @@ public AntMediaSignallingEvents getSignallingListener() { } public boolean isConnected() { - return ws !=null && isWsOpen; + return ws != null && isWsOpen; } public void forceStreamQuality(String mainTrackStreamId, String subTrackStreamId, int height) { From 919671104b9b0906d3d6d7c0fe857d7df7168b71 Mon Sep 17 00:00:00 2001 From: USAMAWIZARD Date: Wed, 24 Sep 2025 12:45:26 +0530 Subject: [PATCH 08/28] imporove reconnection --- .../webrtcandroidframework/websocket/WebSocketHandler.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/websocket/WebSocketHandler.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/websocket/WebSocketHandler.java index 6270bdee..744af204 100644 --- a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/websocket/WebSocketHandler.java +++ b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/websocket/WebSocketHandler.java @@ -78,7 +78,6 @@ public WebSocket connectWebSocket(String wsServerUrl){ } } public void connect(final String wsUrl) { - checkIfCalledOnValidThread(); if(wsUrl == null || wsUrl.isBlank()) return; wsServerUrl = wsUrl; @@ -382,7 +381,6 @@ public void getSubscriberList(String streamId, long offset, long size) { } public void startPublish(String streamId, String token, boolean videoEnabled, boolean audioEnabled, String subscriberId, String subscriberCode, String streamName, String mainTrackId){ - checkIfCalledOnValidThread(); JSONObject json = new JSONObject(); try { json.put(WebSocketConstants.COMMAND, WebSocketConstants.PUBLISH_COMMAND); From ebe375f31fa171bd5fdb3638edc60b22bbcc5459 Mon Sep 17 00:00:00 2001 From: USAMAWIZARD Date: Wed, 24 Sep 2025 18:34:41 +0530 Subject: [PATCH 09/28] imporove reconnection --- .../antmedia/webrtcandroidframework/core/WebRTCClient.java | 2 +- .../webrtcandroidframework/websocket/WebSocketHandler.java | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java index e83ae305..a6506c97 100644 --- a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java +++ b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java @@ -1499,7 +1499,7 @@ public void onConnected(String streamId) { playReconnectionHandler.removeCallbacksAndMessages(null); playReconnectionInProgress = false; } - if(isPlayConnected() && isPlayConnected()){ + if(isPlayConnected() && isPublishConnected()){ this.handler.post(() -> { if (config.webRTCListener != null) { config.webRTCListener.onReconnectionSuccess(); diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/websocket/WebSocketHandler.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/websocket/WebSocketHandler.java index 744af204..9ff6b288 100644 --- a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/websocket/WebSocketHandler.java +++ b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/websocket/WebSocketHandler.java @@ -126,8 +126,10 @@ public void onOpen(@NonNull WebSocket webSocket, @NonNull Response response) { synchronized (this) { Log.d(TAG, "WebSocket connection opened."); isWsOpen = true; - startPingPongTimer(); - signallingListener.onWebSocketConnected(); + handler.post(() -> { + signallingListener.onWebSocketConnected(); + startPingPongTimer(); + }); } } From ba1f5f05fbf1c5fb82ce34e952b85832fac1d306 Mon Sep 17 00:00:00 2001 From: USAMAWIZARD Date: Wed, 24 Sep 2025 23:59:17 +0530 Subject: [PATCH 10/28] imporove reconnection --- .../websocket/WebSocketHandler.java | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/websocket/WebSocketHandler.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/websocket/WebSocketHandler.java index 9ff6b288..55df2811 100644 --- a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/websocket/WebSocketHandler.java +++ b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/websocket/WebSocketHandler.java @@ -128,8 +128,8 @@ public void onOpen(@NonNull WebSocket webSocket, @NonNull Response response) { isWsOpen = true; handler.post(() -> { signallingListener.onWebSocketConnected(); - startPingPongTimer(); }); + startPingPongTimer(); } } @@ -547,12 +547,6 @@ public void enableTrack(String streamId, String trackId, boolean enabled) { public void startPingPongTimer(){ - synchronized (this) { - if(this.pingPongExecutor != null) { - return; - } - - } Runnable timerTask = new Runnable() { @Override public void run() { @@ -562,7 +556,6 @@ public void run() { Log.d(TAG, "Ping Pong websocket response not received for 4 seconds"); stopPingPongTimer(); isWsOpen = false; - ws.cancel(); disconnect(true); } pingPongTimoutCount++; @@ -570,9 +563,15 @@ public void run() { } }; - pingPongExecutor = Executors.newSingleThreadScheduledExecutor(); - Log.d(TAG, "Ping Pong timer is started"); - pingPongExecutor.scheduleAtFixedRate(timerTask, TIMER_DELAY, TIMER_PERIOD, TimeUnit.MILLISECONDS); + synchronized (this) { + if(this.pingPongExecutor != null) { + return; + } + + pingPongExecutor = Executors.newSingleThreadScheduledExecutor(); + Log.d(TAG, "Ping Pong timer is started"); + pingPongExecutor.scheduleAtFixedRate(timerTask, TIMER_DELAY, TIMER_PERIOD, TimeUnit.MILLISECONDS); + } } From 5c069be7677d396b8618bac5587c40125cdb69c2 Mon Sep 17 00:00:00 2001 From: USAMAWIZARD Date: Thu, 25 Sep 2025 00:58:32 +0530 Subject: [PATCH 11/28] imporove reconnection --- .../io/antmedia/webrtcandroidframework/core/WebRTCClient.java | 3 ++- .../webrtcandroidframework/websocket/WebSocketHandler.java | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java index a6506c97..c845fca2 100644 --- a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java +++ b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java @@ -1491,11 +1491,12 @@ public void onConnected(String streamId) { if(isConference() && config.reconnectionEnabled){ if(isPublishConnected()){ - Log.i(TAG,"Conference reconnection. Publish connected. Play not connected. Try to reconnect play."); + Log.i(TAG,"Publish reconnected"); publishReconnectionHandler.removeCallbacksAndMessages(null); publishReconnectionInProgress = false; } if(isPlayConnected()){ + Log.i(TAG,"Play reconnected"); playReconnectionHandler.removeCallbacksAndMessages(null); playReconnectionInProgress = false; } diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/websocket/WebSocketHandler.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/websocket/WebSocketHandler.java index 55df2811..d60b54f0 100644 --- a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/websocket/WebSocketHandler.java +++ b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/websocket/WebSocketHandler.java @@ -126,10 +126,10 @@ public void onOpen(@NonNull WebSocket webSocket, @NonNull Response response) { synchronized (this) { Log.d(TAG, "WebSocket connection opened."); isWsOpen = true; + startPingPongTimer(); handler.post(() -> { signallingListener.onWebSocketConnected(); }); - startPingPongTimer(); } } From d8524a154cef740969a58bb54afc1ed413510bf5 Mon Sep 17 00:00:00 2001 From: USAMAWIZARD Date: Thu, 25 Sep 2025 02:02:01 +0530 Subject: [PATCH 12/28] imporove reconnection --- .../io/antmedia/webrtcandroidframework/core/WebRTCClient.java | 2 +- .../webrtc_android_sample_app/ConferenceActivityTest.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java index c845fca2..fa79f729 100644 --- a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java +++ b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java @@ -992,7 +992,7 @@ private void publishPlayIfRequested() { Mode peerMode = peerInfo.mode; if (!publishReconnectionInProgress && peerMode == Mode.PUBLISH && peerInfo.peerConnection == null) { Log.i(TAG, "Processing publish request for peer streamId: " + peerInfo.id); - wsHandler.startPublish(peerInfo.id, peerInfo.token, peerInfo.videoCallEnabled, peerInfo.audioCallEnabled, peerInfo.subscriberId, peerInfo.subscriberCode, peerInfo.streamName, peerInfo.mainTrackId); + //wsHandler.startPublish(peerInfo.id, peerInfo.token, peerInfo.videoCallEnabled, peerInfo.audioCallEnabled, peerInfo.subscriberId, peerInfo.subscriberCode, peerInfo.streamName, peerInfo.mainTrackId); } if (!playReconnectionInProgress && peerMode == Mode.PLAY && peerInfo.peerConnection == null) { Log.i(TAG, "Processing play request for peer streamId: " + peerInfo.id); diff --git a/webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/ConferenceActivityTest.java b/webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/ConferenceActivityTest.java index 6b058d8d..f43a8494 100644 --- a/webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/ConferenceActivityTest.java +++ b/webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/ConferenceActivityTest.java @@ -184,7 +184,7 @@ public void testJoinWithExternalParticipant() throws InterruptedException { onView(withId(R.id.multitrack_stats_popup_play_stats_video_track_recyclerview)).inRoot(isDialog()).check(matches(isDisplayed())); - // Thread.sleep(5000); + Thread.sleep(5000); onView(withId(R.id.multitrack_stats_popup_play_stats_video_track_recyclerview)) .check((view, noViewFoundException) -> { @@ -200,7 +200,7 @@ public void testJoinWithExternalParticipant() throws InterruptedException { onView(withId(R.id. stats_popup_container)).perform(swipeUp()); - // Thread.sleep(3000); + Thread.sleep(3000); onView(withId(R.id.multitrack_stats_popup_close_button)).perform(click()); From 46c3d3301536bf1e653bbe8ea560696bf0ee7564 Mon Sep 17 00:00:00 2001 From: USAMAWIZARD Date: Thu, 25 Sep 2025 02:44:15 +0530 Subject: [PATCH 13/28] imporove reconnection --- .../io/antmedia/webrtcandroidframework/core/WebRTCClient.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java index fa79f729..c845fca2 100644 --- a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java +++ b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java @@ -992,7 +992,7 @@ private void publishPlayIfRequested() { Mode peerMode = peerInfo.mode; if (!publishReconnectionInProgress && peerMode == Mode.PUBLISH && peerInfo.peerConnection == null) { Log.i(TAG, "Processing publish request for peer streamId: " + peerInfo.id); - //wsHandler.startPublish(peerInfo.id, peerInfo.token, peerInfo.videoCallEnabled, peerInfo.audioCallEnabled, peerInfo.subscriberId, peerInfo.subscriberCode, peerInfo.streamName, peerInfo.mainTrackId); + wsHandler.startPublish(peerInfo.id, peerInfo.token, peerInfo.videoCallEnabled, peerInfo.audioCallEnabled, peerInfo.subscriberId, peerInfo.subscriberCode, peerInfo.streamName, peerInfo.mainTrackId); } if (!playReconnectionInProgress && peerMode == Mode.PLAY && peerInfo.peerConnection == null) { Log.i(TAG, "Processing play request for peer streamId: " + peerInfo.id); From 5db2007fe26d4192c88814df1172bf585d99ba1a Mon Sep 17 00:00:00 2001 From: USAMAWIZARD Date: Sat, 27 Sep 2025 01:03:26 +0530 Subject: [PATCH 14/28] imporove reconnection --- .../core/WebRTCClient.java | 60 ++++++++++--------- .../websocket/WebSocketHandler.java | 22 +++---- .../org/webrtc/audio/WebRtcAudioTrack.java | 10 ++-- 3 files changed, 49 insertions(+), 43 deletions(-) diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java index c845fca2..50410fe0 100644 --- a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java +++ b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java @@ -277,17 +277,17 @@ public void createReconnectorRunnables() { && pc.iceConnectionState() != PeerConnection.IceConnectionState.COMPLETED)) { - if (peerInfo.mode.equals(Mode.PUBLISH)) { - if (pc != null) { - pc.close(); - /* - This is a FIX of a reconnection bug. - If dispose is used instead of close, in one of consequent reconnection attempts segmentation fault occurs. - pc.dispose(); - */ - } + if (pc != null) { + pc.close(); + /* + This is a FIX of a reconnection bug. + If dispose is used instead of close, in one of consequent reconnection attempts segmentation fault occurs. + pc.dispose(); + */ + } - config.webRTCListener.onReconnectionAttempt(peerInfo.id,peerInfo.mode); + config.webRTCListener.onReconnectionAttempt(peerInfo.id,peerInfo.mode); + if (peerInfo.mode.equals(Mode.PUBLISH)) { Log.d(TAG, "Reconnect attempt for publish"); wsHandler.stop(peerInfo.id); @@ -316,20 +316,19 @@ public void createReconnectorRunnables() { && pc.iceConnectionState() != PeerConnection.IceConnectionState.CONNECTED && pc.iceConnectionState() != PeerConnection.IceConnectionState.COMPLETED)) { - if (peerInfo.mode.equals(Mode.PLAY)) { - - releaseRemoteRenderers(); - if (pc != null) { - pc.close(); - /* - This is a FIX of a reconnection bug. - If dispose is used instead of close, in one of consequent reconnection attempts segmentation fault occurs. - pc.dispose(); - */ - } + releaseRemoteRenderers(); + if (pc != null) { + pc.close(); + /* + This is a FIX of a reconnection bug. + If dispose is used instead of close, in one of consequent reconnection attempts segmentation fault occurs. + pc.dispose(); + */ + } - config.webRTCListener.onReconnectionAttempt(peerInfo.id,peerInfo.mode); + config.webRTCListener.onReconnectionAttempt(peerInfo.id,peerInfo.mode); + if (peerInfo.mode.equals(Mode.PLAY)) { Log.d(TAG, "Reconnect attempt for play"); play(peerInfo.id, @@ -348,6 +347,10 @@ public void createReconnectorRunnables() { if(released || streamStoppedByUser){ return; } + if(wsHandler.pingPongExecutor == null){ + wsHandler.startPingPongTimer(); + } + peerReconnectionHandler.postDelayed(peerReconnectorRunnable, PEER_RECONNECTION_RETRY_DELAY_MS); for (PeerInfo peerInfo : peers.values()) { @@ -674,7 +677,7 @@ public void onCreateSuccess(final SessionDescription desc) { PeerConnection pc = peerInfo.peerConnection; if (pc != null) { Log.d(TAG, "Set local SDP from " + desc.type); - pc.setLocalDescription(this, newDesc); + pc.setLocalDescription(this, newDesc); } }); } @@ -1379,11 +1382,6 @@ public void rePublishPlay() { publishReconnectionHandler.postDelayed(publishReconnectorRunnable, PEER_RECONNECTION_DELAY_MS); Log.d(TAG, "------------------------------------- Publish Reconnection --------------------------------------"); } - if (!isPlayConnected() && !playReconnectionInProgress) { - playReconnectionInProgress = true; - playReconnectionHandler.postDelayed(playReconnectorRunnable, PEER_RECONNECTION_DELAY_MS); - Log.d(TAG, "------------------------------------- Play Reconnection --------------------------------------"); - } } else { Log.i(TAG, "Peer was connected before. Will try to republish/replay in " + PEER_RECONNECTION_DELAY_MS + " ms."); publishReconnectionInProgress = true; @@ -1490,6 +1488,12 @@ public void onConnected(String streamId) { Log.i(TAG, "Connected for streamId:" + streamId); if(isConference() && config.reconnectionEnabled){ + if (!isPlayConnected() && !playReconnectionInProgress) { + playReconnectionInProgress = true; + playReconnectionHandler.postDelayed(playReconnectorRunnable, PEER_RECONNECTION_DELAY_MS); + Log.d(TAG, "------------------------------------- Play Reconnection --------------------------------------"); + } + if(isPublishConnected()){ Log.i(TAG,"Publish reconnected"); publishReconnectionHandler.removeCallbacksAndMessages(null); diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/websocket/WebSocketHandler.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/websocket/WebSocketHandler.java index d60b54f0..bb252b7c 100644 --- a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/websocket/WebSocketHandler.java +++ b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/websocket/WebSocketHandler.java @@ -69,19 +69,19 @@ public WebSocketHandler(AntMediaSignallingEvents signallingListener, Handler han } public WebSocket connectWebSocket(String wsServerUrl){ - synchronized (this) { - if (isWsOpen) { - disconnect(true); - } - Request request = new Request.Builder().url(wsServerUrl).build(); - return client.newWebSocket(request, this); + if (isWsOpen) { + disconnect(true); } + Request request = new Request.Builder().url(wsServerUrl).build(); + return client.newWebSocket(request, this); } public void connect(final String wsUrl) { - if(wsUrl == null || wsUrl.isBlank()) - return; - wsServerUrl = wsUrl; - ws = connectWebSocket(wsServerUrl); + synchronized (this) { + if (wsUrl == null || wsUrl.isBlank()) + return; + wsServerUrl = wsUrl; + ws = connectWebSocket(wsServerUrl); + } } public void sendTextMessage(String message) { @@ -136,11 +136,11 @@ public void onOpen(@NonNull WebSocket webSocket, @NonNull Response response) { @Override public void onClosed(@NonNull WebSocket webSocket, int code, @NonNull String reason) { synchronized (this) { + isWsOpen = false; stopPingPongTimer(); handler.post(() -> { Log.d(TAG, "WebSocket connection closed."); signallingListener.onWebSocketDisconnected(); - isWsOpen = false; synchronized (closeEventLock) { closeEvent = true; closeEventLock.notify(); diff --git a/webrtc-android-framework/src/main/java/org/webrtc/audio/WebRtcAudioTrack.java b/webrtc-android-framework/src/main/java/org/webrtc/audio/WebRtcAudioTrack.java index 6b32e682..3e97b312 100644 --- a/webrtc-android-framework/src/main/java/org/webrtc/audio/WebRtcAudioTrack.java +++ b/webrtc-android-framework/src/main/java/org/webrtc/audio/WebRtcAudioTrack.java @@ -538,10 +538,12 @@ public static void setSpeakerMute(boolean mute) { // Releases the native AudioTrack resources. private void releaseAudioResources() { - Logging.d(TAG, "releaseAudioResources"); - if (audioTrack != null) { - audioTrack.release(); - audioTrack = null; + synchronized (this) { + Logging.d(TAG, "releaseAudioResources"); + if (audioTrack != null) { + audioTrack.release(); + audioTrack = null; + } } } From 99142a3e3eac8f8697d61ce1b2d0351f9fa75cf5 Mon Sep 17 00:00:00 2001 From: USAMAWIZARD Date: Sat, 27 Sep 2025 01:30:27 +0530 Subject: [PATCH 15/28] imporove reconnection --- .../WebRTCClientTest.java | 32 ------------------- 1 file changed, 32 deletions(-) diff --git a/webrtc-android-framework/src/test/java/io/antmedia/webrtcandroidframework/WebRTCClientTest.java b/webrtc-android-framework/src/test/java/io/antmedia/webrtcandroidframework/WebRTCClientTest.java index 1dab32ec..cc7673d4 100644 --- a/webrtc-android-framework/src/test/java/io/antmedia/webrtcandroidframework/WebRTCClientTest.java +++ b/webrtc-android-framework/src/test/java/io/antmedia/webrtcandroidframework/WebRTCClientTest.java @@ -680,38 +680,6 @@ public void testSendPlayOtherTracks() { assertEquals("other2", capturedTracks[2]); } - @Test - public void testRepublishPlay(){ - - WebRTCClient webRTCClientSpy = spy(IWebRTCClient.builder() - .setActivity(context) - .setWebRTCListener(listener) - .build()); - webRTCClientSpy.setRoomId("test"); - - // publish already connected - doReturn(true).when(webRTCClientSpy).isPublishConnected(); - doReturn(false).when(webRTCClientSpy).isPlayConnected(); - - webRTCClientSpy.rePublishPlay(); - assertTrue(webRTCClientSpy.isPlayReconnecting()); - assertFalse(webRTCClientSpy.isPublishReconnecting()); - - webRTCClientSpy = spy(IWebRTCClient.builder() - .setActivity(context) - .setWebRTCListener(listener) - .build()); - webRTCClientSpy.setRoomId("test"); - - //play already connected - doReturn(false).when(webRTCClientSpy).isPublishConnected(); - doReturn(true).when(webRTCClientSpy).isPlayConnected(); - - webRTCClientSpy.rePublishPlay(); - assertTrue(webRTCClientSpy.isPublishReconnecting()); - assertFalse(webRTCClientSpy.isPlayReconnecting()); - - } @Test From f76a19861cbad6a4357e9c89ac17c8b64150ab51 Mon Sep 17 00:00:00 2001 From: USAMAWIZARD Date: Wed, 1 Oct 2025 14:39:57 +0530 Subject: [PATCH 16/28] imporove reconnection --- .../core/WebRTCClient.java | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java index 50410fe0..3b023387 100644 --- a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java +++ b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java @@ -1375,6 +1375,11 @@ public void rePublishPlay() { } if (isConference()) { + if(isPublishConnected() && isPlayConnected()){ + onConnected(getPublishStreamId()); + Log.i(TAG, "------ Connected Automatically ----"); + return; + } Log.i(TAG, "Conference! Will try to republish in " + PEER_RECONNECTION_DELAY_MS + " ms."); Log.i(TAG,"publish connected :"+ isPublishConnected() +"play connected" +isPlayConnected()); if (!isPublishConnected() && !publishReconnectionInProgress) { @@ -1382,6 +1387,11 @@ public void rePublishPlay() { publishReconnectionHandler.postDelayed(publishReconnectorRunnable, PEER_RECONNECTION_DELAY_MS); Log.d(TAG, "------------------------------------- Publish Reconnection --------------------------------------"); } + if (!isPlayConnected() && !playReconnectionInProgress) { + playReconnectionInProgress = true; + playReconnectionHandler.postDelayed(playReconnectorRunnable, PEER_RECONNECTION_DELAY_MS); + Log.d(TAG, "------------------------------------- Play Reconnection --------------------------------------"); + } } else { Log.i(TAG, "Peer was connected before. Will try to republish/replay in " + PEER_RECONNECTION_DELAY_MS + " ms."); publishReconnectionInProgress = true; @@ -1488,12 +1498,6 @@ public void onConnected(String streamId) { Log.i(TAG, "Connected for streamId:" + streamId); if(isConference() && config.reconnectionEnabled){ - if (!isPlayConnected() && !playReconnectionInProgress) { - playReconnectionInProgress = true; - playReconnectionHandler.postDelayed(playReconnectorRunnable, PEER_RECONNECTION_DELAY_MS); - Log.d(TAG, "------------------------------------- Play Reconnection --------------------------------------"); - } - if(isPublishConnected()){ Log.i(TAG,"Publish reconnected"); publishReconnectionHandler.removeCallbacksAndMessages(null); From ae34ba51ee78dca1d0070c10d0663da39d33d0ec Mon Sep 17 00:00:00 2001 From: USAMA Date: Thu, 2 Oct 2025 15:44:28 +0530 Subject: [PATCH 17/28] Update gradle.yml --- .github/workflows/gradle.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index b8d3c2fd..40f109c1 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -194,6 +194,13 @@ jobs: adb logcat io.antmedia:I >> emulator.log & ./gradlew jacocoTestReport; EXIT_CODE=$?; exit $EXIT_CODE + - name: Upload Emulator Logs + if: always() + uses: actions/upload-artifact@v4 + with: + name: emulator-logs + path: emulator.log + - name: Archive Test Report if: always() uses: actions/upload-artifact@v4 From 77ccde3a646c0dbe8ea59caef5b6fe028f200a43 Mon Sep 17 00:00:00 2001 From: USAMA Date: Thu, 2 Oct 2025 16:09:10 +0530 Subject: [PATCH 18/28] Update gradle.yml --- .github/workflows/gradle.yml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 40f109c1..54bbf6f8 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -194,13 +194,6 @@ jobs: adb logcat io.antmedia:I >> emulator.log & ./gradlew jacocoTestReport; EXIT_CODE=$?; exit $EXIT_CODE - - name: Upload Emulator Logs - if: always() - uses: actions/upload-artifact@v4 - with: - name: emulator-logs - path: emulator.log - - name: Archive Test Report if: always() uses: actions/upload-artifact@v4 @@ -241,6 +234,14 @@ jobs: runs-on: ubuntu-latest if: always() steps: + + - name: Upload Emulator Logs + if: always() + uses: actions/upload-artifact@v4 + with: + name: emulator-logs + path: emulator.log + - name: Checkout repository uses: actions/checkout@v2 - name: Delete runner From f42b287752423f1c515bd357ebbbf7e8cd3c574b Mon Sep 17 00:00:00 2001 From: USAMA Date: Thu, 2 Oct 2025 16:25:42 +0530 Subject: [PATCH 19/28] Update gradle.yml --- .github/workflows/gradle.yml | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 54bbf6f8..54d168ee 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -192,7 +192,7 @@ jobs: touch emulator.log chmod 777 emulator.log adb logcat io.antmedia:I >> emulator.log & - ./gradlew jacocoTestReport; EXIT_CODE=$?; exit $EXIT_CODE + ./gradlew jacocoTestReport; - name: Archive Test Report if: always() @@ -234,13 +234,6 @@ jobs: runs-on: ubuntu-latest if: always() steps: - - - name: Upload Emulator Logs - if: always() - uses: actions/upload-artifact@v4 - with: - name: emulator-logs - path: emulator.log - name: Checkout repository uses: actions/checkout@v2 From b5e1ed0fcec9cf607bc0fdaf974b2e6a5dc66758 Mon Sep 17 00:00:00 2001 From: USAMAWIZARD Date: Fri, 3 Oct 2025 00:20:39 +0530 Subject: [PATCH 20/28] improve reconnection --- .../webrtcandroidframework/core/WebRTCClient.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java index 3b023387..0c3ea814 100644 --- a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java +++ b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java @@ -1206,11 +1206,6 @@ public void release(boolean closeWebsocket) { remoteVideoSinks.clear(); - mainHandler.post(() -> { - //if closeInternal works before releasing renderer, app stucks - executor.execute(this::closeInternal); - }); - if (audioManager != null) { audioManager.stop(); @@ -1219,6 +1214,11 @@ public void release(boolean closeWebsocket) { config.webRTCListener.onShutdown(); + mainHandler.post(() -> { + //if closeInternal works before releasing renderer, app stucks + executor.execute(this::closeInternal); + }); + } public void releaseRenderer(SurfaceViewRenderer renderer, VideoTrack track, VideoSink sink) { From 58b019af8a9544a6d34446d12977e50fafb5d91e Mon Sep 17 00:00:00 2001 From: USAMAWIZARD Date: Tue, 7 Oct 2025 12:28:58 +0530 Subject: [PATCH 21/28] imporove reconnection --- .../io/antmedia/webrtcandroidframework/core/WebRTCClient.java | 2 +- .../webrtc_android_sample_app/ConferenceActivityTest.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java index 0c3ea814..f885cd6e 100644 --- a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java +++ b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java @@ -2215,7 +2215,7 @@ public void closeInternal() { Log.d(TAG, "Closing peer connections for " + entry.getValue().id); PeerConnection peerConnection = entry.getValue().peerConnection; if (peerConnection != null) { - peerConnection.dispose(); + peerConnection.close(); entry.getValue().peerConnection = null; } diff --git a/webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/ConferenceActivityTest.java b/webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/ConferenceActivityTest.java index f43a8494..6b058d8d 100644 --- a/webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/ConferenceActivityTest.java +++ b/webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/ConferenceActivityTest.java @@ -184,7 +184,7 @@ public void testJoinWithExternalParticipant() throws InterruptedException { onView(withId(R.id.multitrack_stats_popup_play_stats_video_track_recyclerview)).inRoot(isDialog()).check(matches(isDisplayed())); - Thread.sleep(5000); + // Thread.sleep(5000); onView(withId(R.id.multitrack_stats_popup_play_stats_video_track_recyclerview)) .check((view, noViewFoundException) -> { @@ -200,7 +200,7 @@ public void testJoinWithExternalParticipant() throws InterruptedException { onView(withId(R.id. stats_popup_container)).perform(swipeUp()); - Thread.sleep(3000); + // Thread.sleep(3000); onView(withId(R.id.multitrack_stats_popup_close_button)).perform(click()); From 92b59e5203a109693acb90a50b422a6635c92df9 Mon Sep 17 00:00:00 2001 From: USAMAWIZARD Date: Wed, 8 Oct 2025 12:56:21 +0530 Subject: [PATCH 22/28] imporove reconnection --- .../io/antmedia/webrtcandroidframework/core/WebRTCClient.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java index f885cd6e..e6a8d448 100644 --- a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java +++ b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java @@ -1184,9 +1184,6 @@ public void onAudioManagerDevicesChanged( // Disconnect from remote resources, dispose of local resources, and exit. public void release(boolean closeWebsocket) { - if(released){ - return; - } released = true; Log.i(getClass().getSimpleName(), "Releasing resources"); From e8e137b5fc959bacd5ca303d530efe1339c5e8da Mon Sep 17 00:00:00 2001 From: USAMAWIZARD Date: Wed, 8 Oct 2025 23:34:18 +0530 Subject: [PATCH 23/28] imporove reconnection --- .../core/WebRTCClient.java | 84 +++++++++++++------ 1 file changed, 60 insertions(+), 24 deletions(-) diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java index e6a8d448..67438dcf 100644 --- a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java +++ b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java @@ -75,6 +75,8 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.Semaphore; +import java.util.concurrent.locks.Condition; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -88,6 +90,8 @@ import io.antmedia.webrtcandroidframework.websocket.Broadcast; import io.antmedia.webrtcandroidframework.websocket.Subscriber; import io.antmedia.webrtcandroidframework.websocket.WebSocketHandler; +import java.util.concurrent.locks.ReentrantLock; + public class WebRTCClient implements IWebRTCClient, AntMediaSignallingEvents { private static final String TAG = "WebRTCClient"; @@ -100,6 +104,7 @@ public enum Mode { public static final String VIDEO_ROTATION_EXT_LINE = "a=extmap:3 urn:3gpp:video-orientation\r\n"; public static final String USER_REVOKED_CAPTURE_SCREEN_PERMISSION = "User revoked permission to capture the screen."; public static int STAT_CALLBACK_PERIOD = 1000; + private final Semaphore releaseLock = new Semaphore(1); protected final ProxyVideoSink localVideoSink = new ProxyVideoSink(); protected final List remoteVideoSinks = new ArrayList<>(); @@ -248,7 +253,6 @@ public void setQueuedRemoteCandidates(List queuedRemoteCandidates) public static final long PEER_RECONNECTION_RETRY_DELAY_MS = 10000; private boolean released = false; - private String roomId; private BlackFrameSender blackFrameSender; @@ -1014,6 +1018,11 @@ public void publish(String streamId) { public void publish(String streamId, String token, boolean videoCallEnabled, boolean audioCallEnabled, String subscriberId, String subscriberCode, String streamName, String mainTrackId) { + try { + releaseLock.acquire(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } Log.i(TAG, "Publish: " + streamId); this.handler.post(() -> { @@ -1026,6 +1035,7 @@ public void publish(String streamId, String token, boolean videoCallEnabled, boo init(); if(!PermissionHandler.checkPublishPermissions(config.activity, config.bluetoothEnabled, videoCallEnabled || this.config.videoCallEnabled)){ + releaseLock.release(); Toast.makeText(config.activity,"Publish permissions not granted. Cant publish.", Toast.LENGTH_LONG).show(); Log.e(TAG,"Publish permissions not granted. Cant publish."); return; @@ -1039,6 +1049,7 @@ public void publish(String streamId, String token, boolean videoCallEnabled, boo } else { Log.w(TAG, "Websocket is not connected. Set publish requested. It will be processed when ws is connected."); } + releaseLock.release(); } private void createPeerInfo(String streamId, String token, boolean videoCallEnabled, boolean audioCallEnabled, String subscriberId, String subscriberName, String subscriberCode, String streamName, String mainTrackId, String metaData, boolean disableTracksByDefault, Mode mode) { @@ -1067,6 +1078,11 @@ public void play(String streamId, String[] tracks) { @Override public void play(PlayParams params) { + try { + releaseLock.acquire(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } Log.i(TAG, "Play: " + params.getStreamId()); this.handler.post(() -> { if (config.webRTCListener != null) { @@ -1082,6 +1098,7 @@ public void play(PlayParams params) { if(!PermissionHandler.checkPlayPermissions(config.activity, config.bluetoothEnabled)){ Toast.makeText(config.activity,"Play permissions not granted. Cant play.", Toast.LENGTH_LONG).show(); Log.e(TAG,"Play permissions not granted. Cant play."); + releaseLock.release(); return; } @@ -1095,6 +1112,7 @@ public void play(PlayParams params) { } else { Log.w(TAG, "Websocket is not connected. Set play requested. It will be processed when ws is connected."); } + releaseLock.release(); } public void play(String streamId, String token, String[] tracks, String subscriberId, String subscriberCode, String viewerInfo) { @@ -1119,8 +1137,12 @@ public void sendPushNotification(String subscriberId, String authToken, JSONObje } public void join(String streamId, String token) { + try { + releaseLock.acquire(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } Log.e(TAG, "Join: " + streamId); - this.handler.post(() -> { if (config.webRTCListener != null) { config.webRTCListener.onJoinAttempt(streamId); @@ -1136,6 +1158,7 @@ public void join(String streamId, String token) { wsHandler.joinToPeer(streamId, token); + releaseLock.release(); } public void getTrackList(String streamId, String token) { @@ -1184,36 +1207,48 @@ public void onAudioManagerDevicesChanged( // Disconnect from remote resources, dispose of local resources, and exit. public void release(boolean closeWebsocket) { - released = true; - Log.i(getClass().getSimpleName(), "Releasing resources"); + executor.execute(()->{ + try { + releaseLock.acquire(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } - if (closeWebsocket && wsHandler != null) { - wsHandler.disconnect(true); - wsHandler.stopReconnector(); - wsHandler = null; - } - if (config.localVideoRenderer != null) { - releaseRenderer(config.localVideoRenderer, localVideoTrack, localVideoSink); - } + if (released) { + releaseLock.release(); + return; + } + released = true; + Log.i(getClass().getSimpleName(), "Releasing resources"); - releaseRemoteRenderers(); + if (closeWebsocket && wsHandler != null) { + wsHandler.disconnect(true); + wsHandler.stopReconnector(); + wsHandler = null; + } + if (config.localVideoRenderer != null) { + releaseRenderer(config.localVideoRenderer, localVideoTrack, localVideoSink); + } - localVideoTrack = null; - localAudioTrack = null; + releaseRemoteRenderers(); - remoteVideoSinks.clear(); + localVideoTrack = null; + localAudioTrack = null; + remoteVideoSinks.clear(); - if (audioManager != null) { - audioManager.stop(); - audioManager = null; - } - config.webRTCListener.onShutdown(); + mainHandler.post(()->{ + if (audioManager != null) { + audioManager.stop(); + audioManager = null; + } + }); + + config.webRTCListener.onShutdown(); + + closeInternal(); - mainHandler.post(() -> { - //if closeInternal works before releasing renderer, app stucks - executor.execute(this::closeInternal); }); } @@ -2279,6 +2314,7 @@ public void closeInternal() { peerReconnectionHandler.removeCallbacksAndMessages(null); publishReconnectionHandler.removeCallbacksAndMessages(null); playReconnectionHandler.removeCallbacksAndMessages(null); + releaseLock.release(); } private void clearStatsCollector(){ From 2fbe969fbb430eecae10f584d43386c9bf468c34 Mon Sep 17 00:00:00 2001 From: USAMAWIZARD Date: Thu, 9 Oct 2025 00:38:21 +0530 Subject: [PATCH 24/28] imporove reconnection --- .../webrtcandroidframework/core/WebRTCClient.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java index 67438dcf..98d22d1a 100644 --- a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java +++ b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java @@ -13,6 +13,7 @@ import android.app.Activity; import android.media.projection.MediaProjection; import android.os.Handler; +import android.os.Looper; import android.util.DisplayMetrics; import android.util.Log; import android.view.WindowManager; @@ -1231,6 +1232,8 @@ public void release(boolean closeWebsocket) { } releaseRemoteRenderers(); + localAudioTrack.setEnabled(false); + localVideoTrack.setEnabled(false); localVideoTrack = null; localAudioTrack = null; @@ -1260,6 +1263,9 @@ public void releaseRenderer(SurfaceViewRenderer renderer, VideoTrack track, Vide if (videoTrack != null && videoSink != null) videoTrack.removeSink(videoSink); + else{ + Log.d("test","test"); + } renderer.clearAnimation(); mainHandler.postAtFrontOfQueue(renderer::clearImage); @@ -1970,7 +1976,10 @@ public void createPeerConnectionFactory(PeerConnectionFactory.Options options) { if (factory != null) { throw new IllegalStateException("PeerConnectionFactory has already been constructed"); } - executor.execute(() -> createPeerConnectionFactoryInternal(options)); + Handler handler = new Handler(Looper.getMainLooper()); + handler.post(() -> { + createPeerConnectionFactoryInternal(options); + }); } public void createPeerConnection(String streamId, boolean createLocalTrack) { From d33419cbad68a72c60033d2ce5df90ec6c5a0b79 Mon Sep 17 00:00:00 2001 From: USAMAWIZARD Date: Thu, 9 Oct 2025 01:35:53 +0530 Subject: [PATCH 25/28] imporove reconnection --- .../core/WebRTCClient.java | 88 ++++++++++--------- 1 file changed, 45 insertions(+), 43 deletions(-) diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java index 98d22d1a..929915b0 100644 --- a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java +++ b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java @@ -1208,50 +1208,48 @@ public void onAudioManagerDevicesChanged( // Disconnect from remote resources, dispose of local resources, and exit. public void release(boolean closeWebsocket) { - executor.execute(()->{ - try { - releaseLock.acquire(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - - if (released) { - releaseLock.release(); - return; - } - released = true; - Log.i(getClass().getSimpleName(), "Releasing resources"); + try { + releaseLock.acquire(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } - if (closeWebsocket && wsHandler != null) { - wsHandler.disconnect(true); - wsHandler.stopReconnector(); - wsHandler = null; - } - if (config.localVideoRenderer != null) { - releaseRenderer(config.localVideoRenderer, localVideoTrack, localVideoSink); - } + if (released) { + releaseLock.release(); + return; + } + released = true; + Log.i(getClass().getSimpleName(), "Releasing resources"); - releaseRemoteRenderers(); - localAudioTrack.setEnabled(false); - localVideoTrack.setEnabled(false); + if (closeWebsocket && wsHandler != null) { + wsHandler.disconnect(true); + wsHandler.stopReconnector(); + wsHandler = null; + } + if (config.localVideoRenderer != null) { + releaseRenderer(config.localVideoRenderer, localVideoTrack, localVideoSink); + } - localVideoTrack = null; - localAudioTrack = null; + releaseRemoteRenderers(); + localAudioTrack.setEnabled(false); + localVideoTrack.setEnabled(false); - remoteVideoSinks.clear(); + localVideoTrack = null; + localAudioTrack = null; + remoteVideoSinks.clear(); - mainHandler.post(()->{ - if (audioManager != null) { - audioManager.stop(); - audioManager = null; - } - }); - config.webRTCListener.onShutdown(); + if (audioManager != null) { + audioManager.stop(); + audioManager = null; + } - closeInternal(); + config.webRTCListener.onShutdown(); + mainHandler.post(() -> { + //if closeInternal works before releasing renderer, app stucks + executor.execute(this::closeInternal); }); } @@ -1263,9 +1261,6 @@ public void releaseRenderer(SurfaceViewRenderer renderer, VideoTrack track, Vide if (videoTrack != null && videoSink != null) videoTrack.removeSink(videoSink); - else{ - Log.d("test","test"); - } renderer.clearAnimation(); mainHandler.postAtFrontOfQueue(renderer::clearImage); @@ -1425,11 +1420,6 @@ public void rePublishPlay() { publishReconnectionHandler.postDelayed(publishReconnectorRunnable, PEER_RECONNECTION_DELAY_MS); Log.d(TAG, "------------------------------------- Publish Reconnection --------------------------------------"); } - if (!isPlayConnected() && !playReconnectionInProgress) { - playReconnectionInProgress = true; - playReconnectionHandler.postDelayed(playReconnectorRunnable, PEER_RECONNECTION_DELAY_MS); - Log.d(TAG, "------------------------------------- Play Reconnection --------------------------------------"); - } } else { Log.i(TAG, "Peer was connected before. Will try to republish/replay in " + PEER_RECONNECTION_DELAY_MS + " ms."); publishReconnectionInProgress = true; @@ -1536,6 +1526,11 @@ public void onConnected(String streamId) { Log.i(TAG, "Connected for streamId:" + streamId); if(isConference() && config.reconnectionEnabled){ + if (!isPlayConnected() && !playReconnectionInProgress) { + playReconnectionInProgress = true; + playReconnectionHandler.postDelayed(playReconnectorRunnable, PEER_RECONNECTION_DELAY_MS); + Log.d(TAG, "------------------------------------- Play Reconnection --------------------------------------"); + } if(isPublishConnected()){ Log.i(TAG,"Publish reconnected"); publishReconnectionHandler.removeCallbacksAndMessages(null); @@ -2256,6 +2251,13 @@ public void closeInternal() { Log.d(TAG, "Closing peer connections for " + entry.getValue().id); PeerConnection peerConnection = entry.getValue().peerConnection; if (peerConnection != null) { + for (RtpSender sender : peerConnection.getSenders()) { + MediaStreamTrack track = sender.track(); + if (track != null) { + track.setEnabled(false); + track.dispose(); + } + } peerConnection.close(); entry.getValue().peerConnection = null; } From 759a3786bc4d40b0aa8125ac24fd08091fe2f8f1 Mon Sep 17 00:00:00 2001 From: USAMAWIZARD Date: Thu, 9 Oct 2025 02:44:35 +0530 Subject: [PATCH 26/28] imporove reconnection --- .../webrtcandroidframework/core/WebRTCClient.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java index 929915b0..c5bf0fd9 100644 --- a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java +++ b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java @@ -1247,6 +1247,8 @@ public void release(boolean closeWebsocket) { config.webRTCListener.onShutdown(); + releaseLock.release(); + mainHandler.post(() -> { //if closeInternal works before releasing renderer, app stucks executor.execute(this::closeInternal); @@ -1261,6 +1263,9 @@ public void releaseRenderer(SurfaceViewRenderer renderer, VideoTrack track, Vide if (videoTrack != null && videoSink != null) videoTrack.removeSink(videoSink); + else{ + Log.d("test","test"); + } renderer.clearAnimation(); mainHandler.postAtFrontOfQueue(renderer::clearImage); @@ -2242,6 +2247,12 @@ public void setDegradationPreference(RtpParameters.DegradationPreference degrada } public void closeInternal() { + try { + releaseLock.acquire(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + Log.d(TAG, "Closing resources."); if (statsTimer != null) { statsTimer.cancel(); From cec428aa8a138647c2257f79d2554b3f0fbaa809 Mon Sep 17 00:00:00 2001 From: USAMAWIZARD Date: Thu, 9 Oct 2025 02:55:58 +0530 Subject: [PATCH 27/28] imporove reconnection --- .../webrtcandroidframework/core/WebRTCClient.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java index c5bf0fd9..64b75f98 100644 --- a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java +++ b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java @@ -1231,11 +1231,16 @@ public void release(boolean closeWebsocket) { } releaseRemoteRenderers(); - localAudioTrack.setEnabled(false); - localVideoTrack.setEnabled(false); - localVideoTrack = null; - localAudioTrack = null; + if(localAudioTrack != null) { + localAudioTrack.setEnabled(false); + localAudioTrack = null; + } + + if(localVideoTrack!=null) { + localVideoTrack.setEnabled(false); + localVideoTrack = null; + } remoteVideoSinks.clear(); From fec6891465a620e08e772c2b30af7e72d6f4102e Mon Sep 17 00:00:00 2001 From: USAMAWIZARD Date: Mon, 10 Nov 2025 23:37:52 +0530 Subject: [PATCH 28/28] locks for release --- .../core/WebRTCClient.java | 143 +++++++++--------- 1 file changed, 71 insertions(+), 72 deletions(-) diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java index 64b75f98..03e1509e 100644 --- a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java +++ b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java @@ -1208,75 +1208,75 @@ public void onAudioManagerDevicesChanged( // Disconnect from remote resources, dispose of local resources, and exit. public void release(boolean closeWebsocket) { - try { - releaseLock.acquire(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } + handler.post(()->{ + try { + releaseLock.acquire(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } - if (released) { - releaseLock.release(); - return; - } - released = true; - Log.i(getClass().getSimpleName(), "Releasing resources"); + if (released) { + releaseLock.release(); + return; + } + released = true; + Log.i(getClass().getSimpleName(), "Releasing resources"); - if (closeWebsocket && wsHandler != null) { - wsHandler.disconnect(true); - wsHandler.stopReconnector(); - wsHandler = null; - } - if (config.localVideoRenderer != null) { - releaseRenderer(config.localVideoRenderer, localVideoTrack, localVideoSink); - } + if (closeWebsocket && wsHandler != null) { + wsHandler.disconnect(true); + wsHandler.stopReconnector(); + wsHandler = null; + } + if (config.localVideoRenderer != null) { + releaseRenderer(config.localVideoRenderer, localVideoTrack, localVideoSink); + } - releaseRemoteRenderers(); + releaseRemoteRenderers(); - if(localAudioTrack != null) { - localAudioTrack.setEnabled(false); - localAudioTrack = null; - } + if(localAudioTrack != null) { + localAudioTrack.setEnabled(false); + localAudioTrack = null; + } - if(localVideoTrack!=null) { - localVideoTrack.setEnabled(false); - localVideoTrack = null; - } + if(localVideoTrack!=null) { + localVideoTrack.setEnabled(false); + localVideoTrack = null; + } - remoteVideoSinks.clear(); + remoteVideoSinks.clear(); + config.webRTCListener.onShutdown(); - if (audioManager != null) { - audioManager.stop(); - audioManager = null; - } - config.webRTCListener.onShutdown(); + executor.execute(()->{ + closeInternal(); + }); - releaseLock.release(); + if (audioManager != null) { + audioManager.stop(); + audioManager = null; + } - mainHandler.post(() -> { - //if closeInternal works before releasing renderer, app stucks - executor.execute(this::closeInternal); + + releaseLock.release(); }); } public void releaseRenderer(SurfaceViewRenderer renderer, VideoTrack track, VideoSink sink) { - mainHandler.post(() -> { - VideoTrack videoTrack = (track != null) ? track : (VideoTrack) renderer.getTag(); - VideoSink videoSink = (sink != null) ? sink : (VideoSink) renderer.getTag(renderer.getId()); + VideoTrack videoTrack = (track != null) ? track : (VideoTrack) renderer.getTag(); + VideoSink videoSink = (sink != null) ? sink : (VideoSink) renderer.getTag(renderer.getId()); - if (videoTrack != null && videoSink != null) - videoTrack.removeSink(videoSink); - else{ - Log.d("test","test"); - } - renderer.clearAnimation(); - mainHandler.postAtFrontOfQueue(renderer::clearImage); + if (videoTrack != null && videoSink != null) + videoTrack.removeSink(videoSink); + else{ + Log.d("test","test"); + } + renderer.clearAnimation(); + mainHandler.postAtFrontOfQueue(renderer::clearImage); - renderer.release(); - renderer.setTag(null); - }); + renderer.release(); + renderer.setTag(null); } private void releaseRemoteRenderers() { @@ -1430,6 +1430,11 @@ public void rePublishPlay() { publishReconnectionHandler.postDelayed(publishReconnectorRunnable, PEER_RECONNECTION_DELAY_MS); Log.d(TAG, "------------------------------------- Publish Reconnection --------------------------------------"); } + if (!isPlayConnected() && !playReconnectionInProgress) { + playReconnectionInProgress = true; + playReconnectionHandler.postDelayed(playReconnectorRunnable, PEER_RECONNECTION_DELAY_MS); + Log.d(TAG, "------------------------------------- Play Reconnection --------------------------------------"); + } } else { Log.i(TAG, "Peer was connected before. Will try to republish/replay in " + PEER_RECONNECTION_DELAY_MS + " ms."); publishReconnectionInProgress = true; @@ -1536,11 +1541,7 @@ public void onConnected(String streamId) { Log.i(TAG, "Connected for streamId:" + streamId); if(isConference() && config.reconnectionEnabled){ - if (!isPlayConnected() && !playReconnectionInProgress) { - playReconnectionInProgress = true; - playReconnectionHandler.postDelayed(playReconnectorRunnable, PEER_RECONNECTION_DELAY_MS); - Log.d(TAG, "------------------------------------- Play Reconnection --------------------------------------"); - } + if(isPublishConnected()){ Log.i(TAG,"Publish reconnected"); publishReconnectionHandler.removeCallbacksAndMessages(null); @@ -2252,39 +2253,38 @@ public void setDegradationPreference(RtpParameters.DegradationPreference degrada } public void closeInternal() { - try { - releaseLock.acquire(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - Log.d(TAG, "Closing resources."); if (statsTimer != null) { statsTimer.cancel(); } - + PeerConnection peerConnection =null; for (Map.Entry entry : peers.entrySet()) { + Log.d(TAG, "Closing data channels for " + entry.getValue().id); + DataChannel dataChannel = entry.getValue().dataChannel; + if (dataChannel != null) { + dataChannel.dispose(); + entry.getValue().dataChannel = null; + } + Log.d(TAG, "Closing peer connections for " + entry.getValue().id); - PeerConnection peerConnection = entry.getValue().peerConnection; + + peerConnection = entry.getValue().peerConnection; if (peerConnection != null) { for (RtpSender sender : peerConnection.getSenders()) { MediaStreamTrack track = sender.track(); if (track != null) { track.setEnabled(false); - track.dispose(); + //track.dispose(); } } peerConnection.close(); entry.getValue().peerConnection = null; } - Log.d(TAG, "Closing data channels for " + entry.getValue().id); - DataChannel dataChannel = entry.getValue().dataChannel; - if (dataChannel != null) { - dataChannel.dispose(); - entry.getValue().dataChannel = null; - } + } + if(peerConnection !=null) + peerConnection.dispose(); if (streamStoppedByUser) { peers.clear(); } @@ -2341,7 +2341,6 @@ public void closeInternal() { peerReconnectionHandler.removeCallbacksAndMessages(null); publishReconnectionHandler.removeCallbacksAndMessages(null); playReconnectionHandler.removeCallbacksAndMessages(null); - releaseLock.release(); } private void clearStatsCollector(){