From 328a9f3375751f3fa4e744fd1ef1a09f58a3a0d0 Mon Sep 17 00:00:00 2001 From: burak-58 Date: Sun, 26 Nov 2023 18:24:23 +0300 Subject: [PATCH 01/11] create builder --- .../DefaultWebRTCListener.java | 306 ++++++++++++++++++ .../webrtcandroidframework/IWebRTCClient.java | 19 ++ .../webrtcandroidframework/WebRTCClient.java | 306 +++++++++--------- .../WebRTCClientBuilder.java | 103 ++++++ .../WebRTCClientConfig.java | 110 +++++++ .../WebSocketHandler.java | 2 +- .../src/main/AndroidManifest.xml | 4 + .../HomeActivity.java | 2 + .../PublishActivity.java | 70 ++++ 9 files changed, 773 insertions(+), 149 deletions(-) create mode 100644 webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/DefaultWebRTCListener.java create mode 100644 webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/WebRTCClientBuilder.java create mode 100644 webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/WebRTCClientConfig.java create mode 100644 webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/PublishActivity.java diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/DefaultWebRTCListener.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/DefaultWebRTCListener.java new file mode 100644 index 00000000..93d84e94 --- /dev/null +++ b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/DefaultWebRTCListener.java @@ -0,0 +1,306 @@ +package io.antmedia.webrtcandroidframework; + +import android.Manifest; +import android.annotation.SuppressLint; +import android.app.Activity; +import android.content.Context; +import android.content.pm.PackageManager; +import android.os.Build; +import android.os.Bundle; +import android.util.Log; +import android.view.Window; +import android.view.WindowManager; +import android.widget.Toast; + +import androidx.core.app.ActivityCompat; + +import org.webrtc.DataChannel; +import org.webrtc.VideoTrack; + +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import de.tavendo.autobahn.WebSocket; + +public class DefaultWebRTCListener implements IWebRTCListener, IDataChannelObserver { + private final Activity activity; + private PermissionCallback permissionCallback; + + public static final String[] REQUIRED_PUBLISH_PERMISSIONS = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S ? + new String[] {Manifest.permission.RECORD_AUDIO, Manifest.permission.CAMERA, Manifest.permission.BLUETOOTH_CONNECT} + : + new String[] {Manifest.permission.RECORD_AUDIO, Manifest.permission.CAMERA}; + + // List of mandatory application permissions. + public static final String[] REQUIRED_MINIMUM_PERMISSIONS = {"android.permission.MODIFY_AUDIO_SETTINGS", + "android.permission.INTERNET"}; + + public DefaultWebRTCListener(Activity activity) { + this.activity = activity; + } + + + @Override + public void onBufferedAmountChange(long previousAmount, String dataChannelLabel) { + String messageText = "Data channel buffered amount changed: " + dataChannelLabel + ": " + previousAmount; + Log.d(DefaultWebRTCListener.class.getName(), messageText); + //Toast.makeText(this, messageText, Toast.LENGTH_LONG).show(); + } + + @Override + public void onStateChange(DataChannel.State state, String dataChannelLabel) { + String messageText = "Data channel state changed: " + dataChannelLabel + ": " + state; + Log.d(DefaultWebRTCListener.class.getName(), messageText); + //Toast.makeText(this, messageText, Toast.LENGTH_LONG).show(); + } + + @Override + public void onMessage(DataChannel.Buffer buffer, String dataChannelLabel) { + ByteBuffer data = buffer.data; + String messageText = new String(data.array(), StandardCharsets.UTF_8); + makeToast("New Message: " + messageText, Toast.LENGTH_LONG); + } + + @Override + public void onMessageSent(DataChannel.Buffer buffer, boolean successful) { + if (successful) { + ByteBuffer data = buffer.data; + final byte[] bytes = new byte[data.capacity()]; + data.get(bytes); + String messageText = new String(bytes, StandardCharsets.UTF_8); + + makeToast("Message is sent", Toast.LENGTH_SHORT); + } else { + makeToast("Could not send the text message", Toast.LENGTH_LONG); + } + } + + @Override + public void onDisconnected(String streamId) { + String messageText = "Disconnected for " + streamId; + Log.d(DefaultWebRTCListener.class.getName(), messageText); + makeToast(messageText, Toast.LENGTH_LONG); + } + + @Override + public void onPublishFinished(String streamId) { + String messageText = "Publish finished for " + streamId; + Log.d(DefaultWebRTCListener.class.getName(), messageText); + makeToast(messageText, Toast.LENGTH_LONG); + } + + @Override + public void onPlayFinished(String streamId) { + String messageText = "Play finished for " + streamId; + Log.d(DefaultWebRTCListener.class.getName(), messageText); + makeToast(messageText, Toast.LENGTH_LONG); + } + + @Override + public void onPublishStarted(String streamId) { + String messageText = "Publish started for " + streamId; + Log.d(DefaultWebRTCListener.class.getName(), messageText); + makeToast(messageText, Toast.LENGTH_LONG); + } + + @Override + public void onPlayStarted(String streamId) { + String messageText = "Play started for " + streamId; + Log.d(DefaultWebRTCListener.class.getName(), messageText); + makeToast(messageText, Toast.LENGTH_LONG); + } + + @Override + public void noStreamExistsToPlay(String streamId) { + String messageText = "No stream exists to play for " + streamId; + Log.d(DefaultWebRTCListener.class.getName(), messageText); + makeToast(messageText, Toast.LENGTH_LONG); + } + + @Override + public void onError(String description, String streamId) { + String messageText = "Error for " + streamId + " : " + description; + Log.d(DefaultWebRTCListener.class.getName(), messageText); + makeToast(messageText, Toast.LENGTH_LONG); + } + + @Override + public void onSignalChannelClosed(WebSocket.WebSocketConnectionObserver.WebSocketCloseNotification code, String streamId) { + String messageText = "Signal channel closed for " + streamId + " : " + code; + Log.d(DefaultWebRTCListener.class.getName(), messageText); + makeToast(messageText, Toast.LENGTH_LONG); + } + + @Override + public void streamIdInUse(String streamId) { + String messageText = "Stream id is already in use " + streamId; + Log.d(DefaultWebRTCListener.class.getName(), messageText); + makeToast(messageText, Toast.LENGTH_LONG); + } + + @Override + public void onIceConnected(String streamId) { + String messageText = "Ice connected for " + streamId; + Log.d(DefaultWebRTCListener.class.getName(), messageText); + makeToast(messageText, Toast.LENGTH_LONG); + } + + @Override + public void onIceDisconnected(String streamId) { + String messageText = "Ice disconnected for " + streamId; + Log.d(DefaultWebRTCListener.class.getName(), messageText); + makeToast(messageText, Toast.LENGTH_LONG); + } + + public void makeToast(String messageText, int lengthLong) { + //runOnUiThread(() -> Toast.makeText(DefaultWebRTCListener.this, messageText, lengthLong).show()); + } + + @Override + public void onTrackList(String[] tracks) { + String messageText = "Track list received"; + Log.d(DefaultWebRTCListener.class.getName(), messageText); + //Toast.makeText(this, messageText, Toast.LENGTH_LONG).show(); + } + + @Override + public void onBitrateMeasurement(String streamId, int targetBitrate, int videoBitrate, int audioBitrate) { + String messageText = "Bitrate measurement received"; + Log.d(DefaultWebRTCListener.class.getName(), messageText); + //Toast.makeText(this, messageText, Toast.LENGTH_LONG).show(); + } + + @Override + public void onStreamInfoList(String streamId, ArrayList streamInfoList) { + String messageText = "Stream info list received"; + Log.d(DefaultWebRTCListener.class.getName(), messageText); + //Toast.makeText(this, messageText, Toast.LENGTH_LONG).show(); + } + + @Override + public void onNewVideoTrack(VideoTrack track) { + String messageText = "New video track received"; + Log.d(DefaultWebRTCListener.class.getName(), messageText); + makeToast(messageText, Toast.LENGTH_LONG); + } + + @Override + public void onVideoTrackEnded(VideoTrack track) { + String messageText = "Video track ended"; + Log.d(DefaultWebRTCListener.class.getName(), messageText); + makeToast(messageText, Toast.LENGTH_LONG); + } + + @Override + public void onReconnectionAttempt(String streamId) { + String messageText = "Reconnection attempt for " + streamId; + Log.d(DefaultWebRTCListener.class.getName(), messageText); + makeToast(messageText, Toast.LENGTH_LONG); + } + + @Override + public void onJoinedTheRoom(String streamId, String[] streams) { + String messageText = "Joined the room for " + streamId; + Log.d(DefaultWebRTCListener.class.getName(), messageText); + makeToast(messageText, Toast.LENGTH_LONG); + } + + + @Override + public void onRoomInformation(String[] streams) { + String messageText = "Room information received"; + Log.d(DefaultWebRTCListener.class.getName(), messageText); + //Toast.makeText(this, messageText, Toast.LENGTH_LONG).show(); + } + + @Override + public void onLeftTheRoom(String roomId) { + String messageText = "Left the room for " + roomId; + Log.d(DefaultWebRTCListener.class.getName(), messageText); + //Toast.makeText(this, messageText, Toast.LENGTH_LONG).show(); + } + + @Override + public void onMutedFor(String streamId) { + String messageText = "Microphone is muted for " + streamId; + Log.d(DefaultWebRTCListener.class.getName(), messageText); + makeToast(messageText, Toast.LENGTH_LONG); + } + @Override + public void onUnmutedFor(String streamId) { + String messageText = "Microphone is unmuted for " + streamId; + Log.d(DefaultWebRTCListener.class.getName(), messageText); + makeToast(messageText, Toast.LENGTH_LONG); + } + + @Override + public void onCameraTurnOnFor(String streamId) { + String messageText = "Camera is turned on for " + streamId; + Log.d(DefaultWebRTCListener.class.getName(), messageText); + makeToast(messageText, Toast.LENGTH_LONG); + } + + @Override + public void onCameraTurnOffFor(String streamId) { + String messageText = "Camera is turned off for " + streamId; + Log.d(DefaultWebRTCListener.class.getName(), messageText); + makeToast(messageText, Toast.LENGTH_LONG); + } + + @Override + public void onSatatusUpdateFor(String streamId, boolean micStatus, boolean cameraStatus) { + String messageText = "Status update for " + streamId + " mic: " + micStatus + " camera: " + cameraStatus; + Log.d(DefaultWebRTCListener.class.getName(), messageText); + makeToast(messageText, Toast.LENGTH_LONG); + } + + @Override + public boolean checkAndRequestPermisssions(boolean isForPublish, PermissionCallback permissionCallback) { + ArrayList permissions = new ArrayList<>(); + permissions.addAll(Arrays.asList(REQUIRED_MINIMUM_PERMISSIONS)); + if(isForPublish) { + permissions.addAll(Arrays.asList(REQUIRED_PUBLISH_PERMISSIONS)); + } + + if (hasPermissions(activity.getApplicationContext(), permissions)) { + return true; + } + else { + this.permissionCallback = permissionCallback; + showPermissionsErrorAndRequest(permissions); + return false; + } + } + + public boolean hasPermissions(Context context, List permissions) { + if (context != null && permissions != null) { + for (String permission : permissions) { + if (ActivityCompat.checkSelfPermission(context, permission) + != PackageManager.PERMISSION_GRANTED) { + Log.w(DefaultWebRTCListener.class.getSimpleName(), "Permission required:"+permission); + return false; + } + } + } + return true; + } + public void showPermissionsErrorAndRequest(List permissions) { + makeToast("You need permissions before", Toast.LENGTH_SHORT); + String[] permissionArray = new String[permissions.size()]; + permissions.toArray(permissionArray); + ActivityCompat.requestPermissions(activity, permissionArray, 1); + } + + public void onRequestPermissionsResult( + int requestCode, + String[] permissions, + int[] grantResults + ) { + permissionCallback.onPermissionResult(); + } + +} + diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/IWebRTCClient.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/IWebRTCClient.java index 84a313a5..2aa858f9 100644 --- a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/IWebRTCClient.java +++ b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/IWebRTCClient.java @@ -171,4 +171,23 @@ public interface IWebRTCClient { void onCaptureFormatChange(int width, int height, int framerate); boolean onToggleMic(); + public static WebRTCClientBuilder builder() { + return new WebRTCClientBuilder(); + } + + /** + * This is used to strart a WebRTC publish stream + * @param streamId: any name + */ + public void publish(String streamId); + + + /** + * This is used to strart a WebRTC publish stream + * @param streamId: any name + * @param token: token for stream + * TODO: add comment + */ + public void publish(String streamId, String token, boolean videoCallEnabled, boolean audioCallEnabled, + String subscriberId, String subscriberCode, String streamName, String mainTrackId); } diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/WebRTCClient.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/WebRTCClient.java index a57cbf9c..f8f71979 100644 --- a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/WebRTCClient.java +++ b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/WebRTCClient.java @@ -119,7 +119,6 @@ public class WebRTCClient implements IWebRTCClient, AntMediaSignallingEvents, ID private final ProxyVideoSink remoteProxyRenderer = new ProxyVideoSink(); private final ProxyVideoSink localProxyVideoSink = new ProxyVideoSink(); //private final List remoteProxyRendererList = new ArrayList<>(); - private final IWebRTCListener webRTCListener; private final Handler mainHandler; @Nullable public AppRTCAudioManager audioManager = null; @@ -136,7 +135,6 @@ public class WebRTCClient implements IWebRTCClient, AntMediaSignallingEvents, ID private boolean screencaptureEnabled = false; private static Intent mediaProjectionPermissionResultData; private int mediaProjectionPermissionResultCode; - private final Context context; private EglBase eglBase; private final String saveRemoteVideoToFile = null; private String errorString = null; @@ -179,8 +177,6 @@ public class WebRTCClient implements IWebRTCClient, AntMediaSignallingEvents, ID private IDataChannelObserver dataChannelObserver; private String initialStreamId; - private String url; - private String token; private boolean dataChannelOnly = false; private String subscriberId = ""; private String subscriberCode = ""; @@ -290,7 +286,6 @@ public PeerInfo(String id, String mode) { @androidx.annotation.Nullable private AudioTrack localAudioTrack; - private boolean dataChannelEnabled; // Enable RtcEventLog. @androidx.annotation.Nullable private RtcEventLog rtcEventLog; @@ -302,19 +297,12 @@ public PeerInfo(String id, String mode) { private static final Map captureTimeMsMap = new ConcurrentHashMap<>(); //PeerConnection Parameters - private boolean videoCallEnabled; - public boolean loopback; + + private WebRTCClientConfig config; + public boolean tracing; - private int videoWidth; - private int videoHeight; - private int videoFps; - private int videoStartBitrate; - private String videoCodec; - private boolean hwCodecAcceleration; - private boolean videoFlexfecEnabled; - private int audioStartBitrate; - private String audioCodec; - private boolean noAudioProcessing; + + public boolean loopback; private boolean aecDump; private boolean useOpenSLES; private boolean disableBuiltInAEC; @@ -386,7 +374,7 @@ public void run() { } } Log.i(TAG, "Try to reconnect in reconnectionRunnable " + streamMode); - webRTCListener.onReconnectionAttempt(initialStreamId); + config.webRTCListener.onReconnectionAttempt(initialStreamId); if (streamMode == IWebRTCClient.MODE_JOIN) { pipRenderer.setZOrderOnTop(true); } @@ -453,7 +441,7 @@ public void run() { if(!trackLive) { videoTracks.remove(track); handler.post(() -> { - webRTCListener.onVideoTrackEnded(track); + config.webRTCListener.onVideoTrackEnded(track); }); } } @@ -596,7 +584,7 @@ public void onRemoveStream(final MediaStream stream) {} public void onDataChannel(final DataChannel dc) { Log.d(TAG, "New Data channel " + dc.label()); - if (!dataChannelEnabled) + if (!config.dataChannelEnabled) return; if (peers.get(streamId).dataChannel == null) { @@ -615,7 +603,7 @@ public void onRenegotiationNeeded() { public void onAddTrack(final RtpReceiver receiver, final MediaStream[] mediaStreams) { if(receiver.track() instanceof VideoTrack) { VideoTrack videoTrack = (VideoTrack) receiver.track(); - webRTCListener.onNewVideoTrack(videoTrack); + config.webRTCListener.onNewVideoTrack(videoTrack); if(streamMode.equals(MODE_MULTI_TRACK_PLAY) || streamMode.equals(MODE_TRACK_BASED_CONFERENCE)) { trackCheckerTask.getVideoTracks().add(videoTrack); } @@ -650,7 +638,7 @@ public void onCreateSuccess(final SessionDescription desc) { sdp = preferCodec(sdp, AUDIO_CODEC_ISAC, true); } if (isVideoCallEnabled()) { - sdp = preferCodec(sdp, getSdpVideoCodecName(videoCodec), false); + sdp = preferCodec(sdp, getSdpVideoCodecName(config.videoCodec), false); } if(removeVideoRotationExtention) { @@ -720,14 +708,21 @@ public void onSetFailure(final String error) { } } + WebRTCClient(WebRTCClientConfig config) { + this.config = config; + mainHandler = new Handler(config.context.getMainLooper()); + iceServers.add(new PeerConnection.IceServer(stunServerUri)); + } public WebRTCClient(IWebRTCListener webRTCListener, Context context) { - this.webRTCListener = webRTCListener; - this.context = context; - mainHandler = new Handler(context.getMainLooper()); + config = new WebRTCClientConfig(); + config.webRTCListener = webRTCListener; + config.context = context; + mainHandler = new Handler(config.context.getMainLooper()); iceServers.add(new PeerConnection.IceServer(stunServerUri)); } + public void setRemoteRendererList(List rendererList) { this.remoteRendererList = rendererList; } @@ -740,23 +735,23 @@ public void setSelfStreamId(String streamId) { public void init(String url, String streamId, String mode, String token, Intent intent) { if (url == null) { - Log.d(TAG, this.context.getString(R.string.missing_url)); + Log.d(TAG, this.config.context.getString(R.string.missing_url)); return; } - this.url = url; + config.serverUrl = url; if (streamId == null || streamId.length() == 0) { - Log.d(TAG, this.context.getString(R.string.missing_stream_id)); + Log.d(TAG, this.config.context.getString(R.string.missing_stream_id)); return; } this.initialStreamId = streamId; if (mode == null || mode.length() == 0) { - Log.d(TAG, this.context.getString(R.string.missing_stream_id)); + Log.d(TAG, this.config.context.getString(R.string.missing_stream_id)); return; } this.streamMode = mode; - this.token = token; + config.token = token; if (intent != null) { this.intent = intent; } @@ -795,7 +790,7 @@ public boolean checkPermissions(PermissionCallback permissionCallback) { boolean isForPublish = streamMode.equals(MODE_PUBLISH) || streamMode.equals(MODE_TRACK_BASED_CONFERENCE); - return webRTCListener.checkAndRequestPermisssions(isForPublish, permissionCallback); + return config.webRTCListener.checkAndRequestPermisssions(isForPublish, permissionCallback); } private void initializeTrackChecker() { @@ -827,19 +822,19 @@ public void initializeParameters() { loopback = intent.getBooleanExtra(CallActivity.EXTRA_LOOPBACK, false); tracing = intent.getBooleanExtra(CallActivity.EXTRA_TRACING, false); - videoWidth = intent.getIntExtra(CallActivity.EXTRA_VIDEO_WIDTH, 0); - videoHeight = intent.getIntExtra(CallActivity.EXTRA_VIDEO_HEIGHT, 0); + config.videoWidth = intent.getIntExtra(CallActivity.EXTRA_VIDEO_WIDTH, 0); + config.videoHeight = intent.getIntExtra(CallActivity.EXTRA_VIDEO_HEIGHT, 0); screencaptureEnabled = intent.getBooleanExtra(CallActivity.EXTRA_SCREENCAPTURE, false); // If capturing format is not specified for screencapture, use screen resolution. - if (screencaptureEnabled && videoWidth == 0 && videoHeight == 0) { + if (screencaptureEnabled && config.videoWidth == 0 && config.videoHeight == 0) { DisplayMetrics displayMetrics = getDisplayMetrics(); - videoWidth = displayMetrics.widthPixels; - videoHeight = displayMetrics.heightPixels; + config.videoWidth = displayMetrics.widthPixels; + config.videoHeight = displayMetrics.heightPixels; } - dataChannelEnabled = intent.getBooleanExtra(CallActivity.EXTRA_DATA_CHANNEL_ENABLED, true); - if (dataChannelEnabled) { + config.dataChannelEnabled = intent.getBooleanExtra(CallActivity.EXTRA_DATA_CHANNEL_ENABLED, true); + if (config.dataChannelEnabled) { dataChannelOrdered = intent.getBooleanExtra(CallActivity.EXTRA_ORDERED, true); dataChannelMaxRetransmitTimeMs = intent.getIntExtra(CallActivity.EXTRA_MAX_RETRANSMITS_MS, -1); dataChannelMaxRetransmits = intent.getIntExtra(CallActivity.EXTRA_MAX_RETRANSMITS, -1); @@ -848,41 +843,41 @@ public void initializeParameters() { dataChannelId = intent.getIntExtra(CallActivity.EXTRA_ID, -1); } - videoFps = intent.getIntExtra(CallActivity.EXTRA_VIDEO_FPS, 0); + config.videoFps = intent.getIntExtra(CallActivity.EXTRA_VIDEO_FPS, 0); - videoCodec = intent.getStringExtra(CallActivity.EXTRA_VIDEOCODEC); - if (videoCodec == null) { - videoCodec = this.context.getString(R.string.pref_videocodec_default); + config.videoCodec = intent.getStringExtra(CallActivity.EXTRA_VIDEOCODEC); + if (config.videoCodec == null) { + config.videoCodec = this.config.context.getString(R.string.pref_videocodec_default); } - videoStartBitrate = this.intent.getIntExtra(CallActivity.EXTRA_VIDEO_BITRATE, 0); + config.videoStartBitrate = this.intent.getIntExtra(CallActivity.EXTRA_VIDEO_BITRATE, 0); - if (videoStartBitrate == 0) { - videoStartBitrate = Integer.parseInt(this.context.getString(R.string.pref_maxvideobitratevalue_default)); + if (config.videoStartBitrate == 0) { + config.videoStartBitrate = Integer.parseInt(this.config.context.getString(R.string.pref_maxvideobitratevalue_default)); } - audioStartBitrate = this.intent.getIntExtra(CallActivity.EXTRA_AUDIO_BITRATE, 0); - if (audioStartBitrate == 0) { - audioStartBitrate = Integer.parseInt(this.context.getString(R.string.pref_startaudiobitratevalue_default)); + config.audioStartBitrate = this.intent.getIntExtra(CallActivity.EXTRA_AUDIO_BITRATE, 0); + if (config.audioStartBitrate == 0) { + config.audioStartBitrate = Integer.parseInt(this.config.context.getString(R.string.pref_startaudiobitratevalue_default)); } - videoCallEnabled = intent.getBooleanExtra(CallActivity.EXTRA_VIDEO_CALL, true); + config.videoCallEnabled = intent.getBooleanExtra(CallActivity.EXTRA_VIDEO_CALL, true); if (isDataChannelOnly() || streamMode.equals(MODE_PLAY) || streamMode.equals(MODE_MULTI_TRACK_PLAY)) { - videoCallEnabled = false; + config.videoCallEnabled = false; audioCallEnabled = false; } if(streamMode.equals(MODE_TRACK_BASED_CONFERENCE)){ - videoCallEnabled = true; + config.videoCallEnabled = true; audioCallEnabled = true; } - hwCodecAcceleration = intent.getBooleanExtra(CallActivity.EXTRA_HWCODEC_ENABLED, true); - videoFlexfecEnabled = intent.getBooleanExtra(CallActivity.EXTRA_FLEXFEC_ENABLED, false); - audioCodec = intent.getStringExtra(CallActivity.EXTRA_AUDIOCODEC); - noAudioProcessing = intent.getBooleanExtra(CallActivity.EXTRA_NOAUDIOPROCESSING_ENABLED, false); + config.hwCodecAcceleration = intent.getBooleanExtra(CallActivity.EXTRA_HWCODEC_ENABLED, true); + config.videoFlexfecEnabled = intent.getBooleanExtra(CallActivity.EXTRA_FLEXFEC_ENABLED, false); + config.audioCodec = intent.getStringExtra(CallActivity.EXTRA_AUDIOCODEC); + config.noAudioProcessing = intent.getBooleanExtra(CallActivity.EXTRA_NOAUDIOPROCESSING_ENABLED, false); aecDump = intent.getBooleanExtra(CallActivity.EXTRA_AECDUMP_ENABLED, false); useOpenSLES = intent.getBooleanExtra(CallActivity.EXTRA_OPENSLES_ENABLED, false); disableBuiltInAEC = intent.getBooleanExtra(CallActivity.EXTRA_DISABLE_BUILT_IN_AEC, false); @@ -890,7 +885,7 @@ public void initializeParameters() { disableBuiltInNS = intent.getBooleanExtra(CallActivity.EXTRA_DISABLE_BUILT_IN_NS, false); disableWebRtcAGCAndHPF = intent.getBooleanExtra(CallActivity.EXTRA_DISABLE_WEBRTC_AGC_AND_HPF, false); enableRtcEventLog = intent.getBooleanExtra(CallActivity.EXTRA_ENABLE_RTCEVENTLOG, false); - captureToTexture = intent.getBooleanExtra(CallActivity.EXTRA_CAPTURETOTEXTURE_ENABLED, false); + captureToTexture = intent.getBooleanExtra(CallActivity.EXTRA_CAPTURETOTEXTURE_ENABLED, true); } public void initializeRenderers() { @@ -933,7 +928,8 @@ public void initializeRenderers() { if (saveRemoteVideoToFile != null) { try { videoFileRenderer = new VideoFileRenderer( - saveRemoteVideoToFile, videoWidth, videoHeight, eglBase.getEglBaseContext()); + saveRemoteVideoToFile, config.videoWidth, + config.videoHeight, eglBase.getEglBaseContext()); remoteSinks.add(videoFileRenderer); } catch (IOException e) { throw new RuntimeException( @@ -963,12 +959,12 @@ public void initializePeerConnectionFactory() { } // Create peer connection client. - Log.d(TAG, "Preferred video codec: " + getSdpVideoCodecName(videoCodec)); - final String fieldTrials = getFieldTrials(videoFlexfecEnabled, disableWebRtcAGCAndHPF); + Log.d(TAG, "Preferred video codec: " + getSdpVideoCodecName(config.videoCodec)); + final String fieldTrials = getFieldTrials(config.videoFlexfecEnabled, disableWebRtcAGCAndHPF); executor.execute(() -> { Log.d(TAG, "Initialize WebRTC. Field trials: " + fieldTrials); PeerConnectionFactory.initialize( - PeerConnectionFactory.InitializationOptions.builder(context) + PeerConnectionFactory.InitializationOptions.builder(config.context) .setFieldTrials(fieldTrials) .setEnableInternalTracer(true) .createInitializationOptions()); @@ -987,7 +983,7 @@ public void initializeAudioManager() { if (audioCallEnabled && audioManager == null) { // Create and audio manager that will take care of audio routing, // audio modes, audio device enumeration etc. - audioManager = AppRTCAudioManager.create(this.context.getApplicationContext()); + audioManager = AppRTCAudioManager.create(this.config.context.getApplicationContext()); // Store existing audio settings and change audio mode to // MODE_IN_COMMUNICATION for best possible VoIP performance. Log.d(TAG, "Starting the audio manager..."); @@ -1010,7 +1006,7 @@ public void initializeVideoCapturer() { // if video capture is null or disposed, we should recreate it. // we should also check if video capturer is an instance of ScreenCapturerAndroid // because other implementations of VideoCapturer doesn't have a dispose() method. - if (videoCallEnabled + if (config.videoCallEnabled && (videoCapturer == null || (videoCapturer instanceof ScreenCapturerAndroid)) ) { @@ -1060,12 +1056,12 @@ public void connectWebSocket() { if (wsHandler == null) { Log.i(TAG, "WebsocketHandler is null and creating a new instance"); wsHandler = new WebSocketHandler(this, handler); - wsHandler.connect(url); + wsHandler.connect(config.serverUrl); } else if (!wsHandler.isConnected()) { Log.i(TAG, "WebSocketHandler already exists but not connected. Disconnecting and creating new one"); wsHandler.disconnect(true); wsHandler = new WebSocketHandler(this, handler); - wsHandler.connect(url); + wsHandler.connect(config.serverUrl); } } @@ -1084,7 +1080,7 @@ public void startStream() { public DisplayMetrics getDisplayMetrics() { DisplayMetrics displayMetrics = new DisplayMetrics(); WindowManager windowManager = - (WindowManager) this.context.getSystemService(Context.WINDOW_SERVICE); + (WindowManager) this.config.context.getSystemService(config.context.WINDOW_SERVICE); windowManager.getDefaultDisplay().getRealMetrics(displayMetrics); return displayMetrics; } @@ -1101,11 +1097,11 @@ private static int getSystemUiVisibility() { @TargetApi(21) public void startScreenCapture() { mediaProjectionManager = - (MediaProjectionManager) this.context.getSystemService( - Context.MEDIA_PROJECTION_SERVICE); + (MediaProjectionManager) this.config.context.getSystemService( + config.context.MEDIA_PROJECTION_SERVICE); - if (this.context instanceof Activity) { - ((Activity) this.context).startActivityForResult( + if (this.config.context instanceof Activity) { + ((Activity) this.config.context).startActivityForResult( mediaProjectionManager.createScreenCaptureIntent(), CallActivity.CAPTURE_PERMISSION_REQUEST_CODE); } } @@ -1135,7 +1131,7 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) { } public boolean useCamera2() { - return Camera2Enumerator.isSupported(this.context) && this.intent.getBooleanExtra(CallActivity.EXTRA_CAMERA2, true); + return Camera2Enumerator.isSupported(this.config.context) && this.intent.getBooleanExtra(CallActivity.EXTRA_CAMERA2, true); } public void setOpenFrontCamera(boolean openFrontCamera) { @@ -1274,24 +1270,31 @@ public boolean toggleMic() { } private void startCall(String streamId) { - Log.d(TAG, this.context.getString(R.string.connecting_to, url)); + Log.d(TAG, this.config.context.getString(R.string.connecting_to, config.serverUrl)); if (streamMode.equals(IWebRTCClient.MODE_PUBLISH)) { - publish(streamId, token, videoCallEnabled, audioCallEnabled, subscriberId, subscriberCode, streamName, mainTrackId); + publish(streamId, config.token, config.videoCallEnabled, config.audioCallEnabled, subscriberId, subscriberCode, streamName, mainTrackId); } else if (streamMode.equals(IWebRTCClient.MODE_PLAY)) { - play(streamId, token, null, subscriberId, subscriberCode, viewerInfo); + play(streamId, config.token, null, subscriberId, subscriberCode, viewerInfo); } else if (streamMode.equals(IWebRTCClient.MODE_JOIN)) { - init(this.url, streamId, this.streamMode, token, this.intent); - wsHandler.joinToPeer(streamId, token); + init(config.serverUrl, streamId, this.streamMode, config.token, this.intent); + wsHandler.joinToPeer(streamId, config.token); } else if (streamMode.equals(IWebRTCClient.MODE_MULTI_TRACK_PLAY)) { - init(this.url, streamId, this.streamMode, token, this.intent); - wsHandler.getTrackList(streamId, token); + init(config.serverUrl, streamId, this.streamMode, config.token, this.intent); + wsHandler.getTrackList(streamId, config.token); } } - public void publish(String streamId, String token, boolean videoCallEnabled, boolean audioCallEnabled, String subscriberId, String subscriberCode, String streamName, String mainTrackId) { + public void publish(String streamId) { + publish(streamId, null, true, true, + null, null, streamId, null); + } + + + public void publish(String streamId, String token, boolean videoCallEnabled, boolean audioCallEnabled, + String subscriberId, String subscriberCode, String streamName, String mainTrackId) { Log.e(TAG, "Publish: "+streamId); PeerInfo peerInfo = new PeerInfo(streamId, MODE_PUBLISH); @@ -1304,7 +1307,14 @@ public void publish(String streamId, String token, boolean videoCallEnabled, boo peerInfo.mainTrackId = mainTrackId; peers.put(streamId, peerInfo); - init(this.url, streamId, this.streamMode, this.token, this.intent); + init(config.serverUrl, streamId, MODE_PUBLISH, config.token, this.intent); + while (!wsHandler.isConnected()) { + try { + Thread.sleep(100); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } wsHandler.startPublish(streamId, token, videoCallEnabled, audioCallEnabled, subscriberId, subscriberCode, streamName, mainTrackId); } @@ -1322,7 +1332,7 @@ public void play(String streamId, String token, String[] tracks, String subscri peerInfo.metaData = viewerInfo; peers.put(streamId, peerInfo); - init(this.url, streamId, this.streamMode, token, this.intent); + init(config.serverUrl, streamId, this.streamMode, token, this.intent); wsHandler.startPlay(streamId, token, tracks, subscriberId, subscriberCode, viewerInfo); } @@ -1415,8 +1425,8 @@ public void reportError(String streamId, final String description) { errorString = description; disconnectWithErrorMessage(description); - if (webRTCListener != null) { - webRTCListener.onError(description, streamId); + if (config.webRTCListener != null) { + config.webRTCListener.onError(description, streamId); } } @@ -1475,12 +1485,12 @@ public void changeVideoSource(String newSource) { return new CustomVideoCapturer(); } else { if (!captureToTexture) { - reportError(initialStreamId, this.context.getString(R.string.camera2_texture_only_error)); + reportError(initialStreamId, this.config.context.getString(R.string.camera2_texture_only_error)); return null; } Logging.d(TAG, "Creating capturer using camera2 API."); - videoCapturer = createCameraCapturer(new Camera2Enumerator(this.context)); + videoCapturer = createCameraCapturer(new Camera2Enumerator(this.config.context)); } if (videoCapturer == null) { reportError(initialStreamId, "Failed to open camera"); @@ -1526,7 +1536,7 @@ private void setUpReconnection() { if(!streamStoppedByUser) { Log.i(getClass().getSimpleName(),"Disconnected. Trying to reconnect"); reconnectionInProgress = true; - //Toast.makeText(context, "Disconnected.Trying to reconnect "+streamStoppedByUser, Toast.LENGTH_LONG).show(); + //Toast.makeText(config.context, "Disconnected.Trying to reconnect "+streamStoppedByUser, Toast.LENGTH_LONG).show(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { if (!reconnectionHandler.hasCallbacks(reconnectionRunnable)) { reconnectionHandler.postDelayed(reconnectionRunnable, RECONNECTION_PERIOD_MLS); @@ -1597,9 +1607,9 @@ public void onLocalDescription(String streamId, final SessionDescription sdp) { } } //check peerConnectionClient null because in very slow devices(emulator), it may cause crash - if (videoStartBitrate > 0) { - Log.d(TAG, "Set video maximum bitrate: " + videoStartBitrate); - setVideoMaxBitrate(videoStartBitrate); + if (config.videoStartBitrate > 0) { + Log.d(TAG, "Set video maximum bitrate: " + config.videoStartBitrate); + setVideoMaxBitrate(config.videoStartBitrate); } }); } @@ -1610,8 +1620,8 @@ public void onIceConnected(String streamId) { Log.d(TAG, "ICE connected, delay=" + delta + "ms"); callConnected(streamId); - if (webRTCListener != null) { - webRTCListener.onIceConnected(streamId); + if (config.webRTCListener != null) { + config.webRTCListener.onIceConnected(streamId); } }); } @@ -1620,8 +1630,8 @@ public void onIceDisconnected(String streamId) { this.handler.post(() -> { Log.d(TAG, "ICE disconnected"); //release(false); - if (webRTCListener != null) { - webRTCListener.onIceDisconnected(streamId); + if (config.webRTCListener != null) { + config.webRTCListener.onIceDisconnected(streamId); } }); } @@ -1682,8 +1692,8 @@ public void onTakeConfiguration(String streamId, SessionDescription sdp) { @Override public void onPublishFinished(String streamId) { this.handler.post(() -> { - if (webRTCListener != null) { - webRTCListener.onPublishFinished(streamId); + if (config.webRTCListener != null) { + config.webRTCListener.onPublishFinished(streamId); } release(false); }); @@ -1695,8 +1705,8 @@ public void onPlayFinished(String streamId) { waitingForPlay = false; this.handler.post(() -> { release(false); - if (webRTCListener != null) { - webRTCListener.onPlayFinished(streamId); + if (config.webRTCListener != null) { + config.webRTCListener.onPlayFinished(streamId); } }); } @@ -1708,8 +1718,8 @@ public void onPublishStarted(String streamId) { streamStarted = true; this.handler.post(() -> { - if (webRTCListener != null) { - webRTCListener.onPublishStarted(streamId); + if (config.webRTCListener != null) { + config.webRTCListener.onPublishStarted(streamId); } }); @@ -1723,8 +1733,8 @@ public void onPlayStarted(String streamId) { waitingForPlay = false; this.handler.post(() -> { - if (webRTCListener != null) { - webRTCListener.onPlayStarted(streamId); + if (config.webRTCListener != null) { + config.webRTCListener.onPlayStarted(streamId); } }); } @@ -1740,33 +1750,33 @@ public void onStartStreaming(String streamId) { @Override public void onJoinedTheRoom(String streamId, String[] streams) { - webRTCListener.onJoinedTheRoom(streamId, streams); + config.webRTCListener.onJoinedTheRoom(streamId, streams); } @Override public void onRoomInformation(String[] streams) { - webRTCListener.onRoomInformation(streams); + config.webRTCListener.onRoomInformation(streams); } @Override public void noStreamExistsToPlay(String streamId) { this.handler.post(() -> { - if (webRTCListener != null) { - webRTCListener.noStreamExistsToPlay(streamId); + if (config.webRTCListener != null) { + config.webRTCListener.noStreamExistsToPlay(streamId); } }); } @Override public void onLeftTheRoom (String roomId) { - webRTCListener.onLeftTheRoom(roomId); + config.webRTCListener.onLeftTheRoom(roomId); } @Override public void streamIdInUse(String streamId){ this.handler.post(() -> { - if (webRTCListener != null) { - webRTCListener.streamIdInUse(streamId); + if (config.webRTCListener != null) { + config.webRTCListener.streamIdInUse(streamId); } }); } @@ -1779,8 +1789,8 @@ public void onRemoteIceCandidate(String streamId, IceCandidate candidate) { @Override public void onDisconnected() { this.handler.post(() -> { - if (webRTCListener != null) { - webRTCListener.onDisconnected(initialStreamId); + if (config.webRTCListener != null) { + config.webRTCListener.onDisconnected(initialStreamId); } if(reconnectionEnabled && !reconnectionInProgress) { @@ -1792,8 +1802,8 @@ public void onDisconnected() { @Override public void onTrackList(String[] tracks) { this.handler.post(()-> { - if (webRTCListener != null) { - webRTCListener.onTrackList(tracks); + if (config.webRTCListener != null) { + config.webRTCListener.onTrackList(tracks); } }); @@ -1803,7 +1813,7 @@ public void onTrackList(String[] tracks) { public void sendPlayOtherTracks(String[] tracks) { if(autoPlayTracks && !isStreaming() && !waitingForPlay) { waitingForPlay = true; - init(this.url, this.initialStreamId, this.streamMode, this.token, this.intent); + init(config.serverUrl, this.initialStreamId, this.streamMode, config.token, this.intent); //don't send play for its own stream id for (int i = 0; i < tracks.length; i++) { @@ -1812,15 +1822,15 @@ public void sendPlayOtherTracks(String[] tracks) { break; } } - play(mainTrackId, token, tracks); + play(mainTrackId, config.token, tracks); } } @Override public void onBitrateMeasurement(String streamId, int targetBitrate, int videoBitrate, int audioBitrate) { this.handler.post(()-> { - if (webRTCListener != null) { - webRTCListener.onBitrateMeasurement(streamId, targetBitrate, videoBitrate, audioBitrate); + if (config.webRTCListener != null) { + config.webRTCListener.onBitrateMeasurement(streamId, targetBitrate, videoBitrate, audioBitrate); } }); } @@ -1828,8 +1838,8 @@ public void onBitrateMeasurement(String streamId, int targetBitrate, int videoBi @Override public void onStreamInfoList(String streamId, ArrayList streamInfoList) { this.handler.post(()-> { - if (webRTCListener != null) { - webRTCListener.onStreamInfoList(streamId, streamInfoList); + if (config.webRTCListener != null) { + config.webRTCListener.onStreamInfoList(streamId, streamInfoList); } }); } @@ -1837,8 +1847,8 @@ public void onStreamInfoList(String streamId, ArrayList streamInfoLi @Override public void onError(String streamId, String definition) { this.handler.post(()-> { - if (webRTCListener != null) { - webRTCListener.onError(definition, streamId); + if (config.webRTCListener != null) { + config.webRTCListener.onError(definition, streamId); } }); @@ -1849,7 +1859,7 @@ public void onError(String streamId, String definition) { @Override public boolean isDataChannelEnabled() { - return dataChannelEnabled; + return config.dataChannelEnabled; } /** @@ -2042,7 +2052,7 @@ public void createPeerConnection(String streamId) { } private boolean isVideoCallEnabled() { - return videoCallEnabled && videoCapturer != null; + return config.videoCallEnabled && videoCapturer != null; } private boolean isAudioEnabled() { @@ -2059,7 +2069,7 @@ private void createPeerConnectionFactoryInternal(PeerConnectionFactory.Options o } // Check if ISAC is used by default. - preferIsac = audioCodec != null && audioCodec.equals(AUDIO_CODEC_ISAC); + preferIsac = config.audioCodec != null && config.audioCodec.equals(AUDIO_CODEC_ISAC); adm = (JavaAudioDeviceModule) createJavaAudioDevice(); @@ -2068,11 +2078,11 @@ private void createPeerConnectionFactoryInternal(PeerConnectionFactory.Options o Log.d(TAG, "Factory networkIgnoreMask option: " + options.networkIgnoreMask); } final boolean enableH264HighProfile = - VIDEO_CODEC_H264_HIGH.equals(videoCodec); + VIDEO_CODEC_H264_HIGH.equals(config.videoCodec); final VideoEncoderFactory encoderFactory; final VideoDecoderFactory decoderFactory; - if (hwCodecAcceleration) { + if (config.hwCodecAcceleration) { encoderFactory = new DefaultVideoEncoderFactory(eglBase.getEglBaseContext(), true /* enableIntelVp8Encoder */, enableH264HighProfile); decoderFactory = new DefaultVideoDecoderFactory(eglBase.getEglBaseContext()); } else { @@ -2183,29 +2193,29 @@ public void onWebRtcAudioTrackStop() { } public JavaAudioDeviceModule.Builder getADMBuilder() { - return JavaAudioDeviceModule.builder(context); + return JavaAudioDeviceModule.builder(config.context); } public void createMediaConstraintsInternal() { // Create video constraints if video call is enabled. if (isVideoCallEnabled()) { // If video resolution is not specified, default to HD. - if (videoWidth == 0 || videoHeight == 0) { - videoWidth = HD_VIDEO_WIDTH; - videoHeight = HD_VIDEO_HEIGHT; + if (config.videoWidth == 0 || config.videoHeight == 0) { + config.videoWidth = HD_VIDEO_WIDTH; + config.videoHeight = HD_VIDEO_HEIGHT; } // If fps is not specified, default to 30. - if (videoFps == 0) { - videoFps = 30; + if (config.videoFps == 0) { + config.videoFps = 30; } - Logging.d(TAG, "Capturing format: " + videoWidth + "x" + videoHeight + "@" + videoFps); + Logging.d(TAG, "Capturing format: " + config.videoWidth + "x" + config.videoHeight + "@" + config.videoFps); } // Create audio constraints. audioConstraints = new MediaConstraints(); // added for audio performance measurements - if (noAudioProcessing) { + if (config.noAudioProcessing) { Log.d(TAG, "Disabling audio processing"); audioConstraints.mandatory.add( new MediaConstraints.KeyValuePair(AUDIO_ECHO_CANCELLATION_CONSTRAINT, "false")); @@ -2300,7 +2310,7 @@ public PCObserver getPCObserver(String streamId) { } public void initDataChannel(String streamId) { - if (dataChannelEnabled && isDataChannelCreator()) { + if (config.dataChannelEnabled && isDataChannelCreator()) { DataChannel.Init init = new DataChannel.Init(); init.ordered = dataChannelOrdered; init.negotiated = dataChannelNegotiated; @@ -2322,7 +2332,7 @@ private boolean isDataChannelCreator() { public void setDegradationPreference(String streamId , RtpParameters.DegradationPreference degradationPreference) { - if (context == null || peers.get(streamId) == null || peers.get(streamId).peerConnection == null) { + if (config.context == null || peers.get(streamId) == null || peers.get(streamId).peerConnection == null) { Log.d(TAG, "Cannot set Degradation Preference"); return; } @@ -2346,11 +2356,11 @@ private File createRtcEventLogOutputFile() { DateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_hhmm_ss", Locale.getDefault()); Date date = new Date(); final String outputFileName = "event_log_" + dateFormat.format(date) + ".log"; - return new File(context.getDir(RTCEVENTLOG_OUTPUT_DIR_NAME, Context.MODE_PRIVATE), outputFileName); + return new File(config.context.getDir(RTCEVENTLOG_OUTPUT_DIR_NAME, config.context.MODE_PRIVATE), outputFileName); } public void maybeCreateAndStartRtcEventLog(String streamId) { - if (context == null || peers.get(streamId).peerConnection == null) { + if (config.context == null || peers.get(streamId).peerConnection == null) { return; } if (!enableRtcEventLog) { @@ -2443,7 +2453,7 @@ public void closeInternal() { } public boolean isHDVideo() { - return isVideoCallEnabled() && videoWidth * videoHeight >= 1280 * 720; + return isVideoCallEnabled() && config.videoWidth * config.videoHeight >= 1280 * 720; } public void getStats(String streamId) { @@ -2483,7 +2493,7 @@ public void setAudioEnabled(final boolean enable) { } public void setVideoEnabled(final boolean enable) { - this.videoCallEnabled = enable; + config.videoCallEnabled = enable; executor.execute(() -> { renderVideo = enable; if (localVideoTrack != null) { @@ -2569,10 +2579,10 @@ public void setRemoteDescription(String streamId, final SessionDescription desc) sdp = preferCodec(sdp, AUDIO_CODEC_ISAC, true); } if (isVideoCallEnabled()) { - sdp = preferCodec(sdp, getSdpVideoCodecName(videoCodec), false); + sdp = preferCodec(sdp, getSdpVideoCodecName(config.videoCodec), false); } - if (audioStartBitrate > 0) { - sdp = setStartBitrate(AUDIO_CODEC_OPUS, false, sdp, audioStartBitrate); + if (config.audioStartBitrate > 0) { + sdp = setStartBitrate(AUDIO_CODEC_OPUS, false, sdp, config.audioStartBitrate); } Log.d(TAG, "Set remote SDP."); SessionDescription sdpRemote = new SessionDescription(desc.type, sdp); @@ -2595,7 +2605,7 @@ private void stopVideoSourceInternal() { private void startVideoSourceInternal() { if (videoCapturer != null && videoCapturerStopped) { Log.d(TAG, "Restart video source."); - videoCapturer.startCapture(videoWidth, videoHeight, videoFps); + videoCapturer.startCapture(config.videoWidth, config.videoHeight, config.videoFps); videoCapturerStopped = false; } } @@ -2644,8 +2654,8 @@ private VideoTrack createVideoTrack(VideoCapturer capturer) { surfaceTextureHelper = SurfaceTextureHelper.create("CaptureThread", eglBase.getEglBaseContext()); videoSource = factory.createVideoSource(capturer.isScreencast()); - capturer.initialize(surfaceTextureHelper, context, videoSource.getCapturerObserver()); - capturer.startCapture(videoWidth, videoHeight, videoFps); + capturer.initialize(surfaceTextureHelper, config.context, videoSource.getCapturerObserver()); + capturer.startCapture(config.videoWidth, config.videoHeight, config.videoFps); localVideoTrack = factory.createVideoTrack(VIDEO_TRACK_ID, videoSource); localVideoTrack.setEnabled(renderVideo); @@ -2922,15 +2932,15 @@ public String getStreamMode() { } public void setToken(String token) { - this.token = token; + config.token = token; } public void setVideoCallEnabled(boolean videoCallEnabled) { - this.videoCallEnabled = videoCallEnabled; + config.videoCallEnabled = videoCallEnabled; } public boolean getVideoCallEnabled() { - return videoCallEnabled; + return config.videoCallEnabled; } public boolean getAudioCallEnabled() { @@ -3028,7 +3038,7 @@ public void setReconnectionHandler(Handler reconnectionHandler) { } public void setDataChannelEnabled(boolean dataChannelEnabled) { - this.dataChannelEnabled = dataChannelEnabled; + this.config.dataChannelEnabled = dataChannelEnabled; } public void setFactory(@androidx.annotation.Nullable PeerConnectionFactory factory) { diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/WebRTCClientBuilder.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/WebRTCClientBuilder.java new file mode 100644 index 00000000..fc2dcfc9 --- /dev/null +++ b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/WebRTCClientBuilder.java @@ -0,0 +1,103 @@ +package io.antmedia.webrtcandroidframework; + +import android.content.Context; + +import org.webrtc.SurfaceViewRenderer; + +public class WebRTCClientBuilder { + + private WebRTCClientConfig webRTCClientConfig; + + WebRTCClientBuilder() { + webRTCClientConfig = new WebRTCClientConfig(); + } + + public WebRTCClient build() { + return new WebRTCClient(webRTCClientConfig); + } + + public WebRTCClientBuilder setServerUrl(String serverUrl) { + webRTCClientConfig.serverUrl = serverUrl; + return this; + } + + public WebRTCClientBuilder setStreamId(String streamId) { + webRTCClientConfig.streamId = streamId; + return this; + } + + public WebRTCClientBuilder setToken(String token) { + webRTCClientConfig.token = token; + return this; + } + + public WebRTCClientBuilder setVideoCallEnabled(boolean videoCallEnabled) { + webRTCClientConfig.videoCallEnabled = videoCallEnabled; + return this; + } + + public WebRTCClientBuilder setAudioCallEnabled(boolean audioCallEnabled) { + webRTCClientConfig.audioCallEnabled = audioCallEnabled; + return this; + } + + public WebRTCClientBuilder setDataChannelEnabled(boolean dataChannelEnabled) { + webRTCClientConfig.dataChannelEnabled = dataChannelEnabled; + return this; + } + + public WebRTCClientBuilder setVideoWidth(int videoWidth) { + webRTCClientConfig.videoWidth = videoWidth; + return this; + } + + public WebRTCClientBuilder setVideoHeight(int videoHeight) { + webRTCClientConfig.videoHeight = videoHeight; + return this; + } + + public WebRTCClientBuilder setVideoFps(int videoFps) { + webRTCClientConfig.videoFps = videoFps; + return this; + } + + public WebRTCClientBuilder setVideoStartBitrate(int videoStartBitrate) { + webRTCClientConfig.videoStartBitrate = videoStartBitrate; + return this; + } + + public WebRTCClientBuilder setVideoCodec(String videoCodec) { + webRTCClientConfig.videoCodec = videoCodec; + return this; + } + + public WebRTCClientBuilder setAudioStartBitrate(int audioStartBitrate) { + webRTCClientConfig.audioStartBitrate = audioStartBitrate; + return this; + } + + public WebRTCClientBuilder setAudioCodec(String audioCodec) { + webRTCClientConfig.audioCodec = audioCodec; + return this; + } + + public WebRTCClientBuilder setFullScreenRenderer(SurfaceViewRenderer fullScreenRenderer) { + webRTCClientConfig.fullScreenRenderer = fullScreenRenderer; + return this; + } + + public WebRTCClientBuilder setPipRenderer(SurfaceViewRenderer pipRenderer) { + webRTCClientConfig.pipRenderer = pipRenderer; + return this; + } + + public WebRTCClientBuilder setWebRTCListener(IWebRTCListener webRTCListener) { + webRTCClientConfig.webRTCListener = webRTCListener; + return this; + } + + public WebRTCClientBuilder setContext(Context context) { + webRTCClientConfig.context = context; + return this; + } +} diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/WebRTCClientConfig.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/WebRTCClientConfig.java new file mode 100644 index 00000000..582a4628 --- /dev/null +++ b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/WebRTCClientConfig.java @@ -0,0 +1,110 @@ +package io.antmedia.webrtcandroidframework; + +import android.content.Context; + +import org.webrtc.SurfaceViewRenderer; + +public class WebRTCClientConfig { + + /* + * websocket connection url to Ant Media Server + * ex. wss://{AMS URL}:5443/{AppName}/websocket + */ + public String serverUrl; + + /* + * stream id for stream + */ + public String streamId; + + /* + * token for stream + */ + public String token; + + /* + * Flag indicating whether video call is enabled + */ + public boolean videoCallEnabled; + + /* + * Flag indicating whether audio call is enabled + */ + public boolean audioCallEnabled; + + /* + * Flag indicating whether data channel is enabled + */ + public boolean dataChannelEnabled; + + /* + * Width of the video in pixels + */ + public int videoWidth; + + /* + * Height of the video in pixels + */ + public int videoHeight; + + /* + * Frames per second for the video + */ + public int videoFps; + + /* + * Initial bitrate for video transmission + */ + public int videoStartBitrate; + + /* + * Codec used for video encoding and decoding + */ + public String videoCodec; + + /* + * Flag for hardware codec acceleration + */ + public boolean hwCodecAcceleration; + + /* + * Flag indicating whether flexible forward error correction (FlexFEC) is enabled for video + */ + public boolean videoFlexfecEnabled; + + /* + * Initial bitrate for audio transmission + */ + public int audioStartBitrate; + + /* + * Codec used for audio encoding and decoding + */ + public String audioCodec; + + /* + * Flag indicating whether audio processing is disabled + */ + public boolean noAudioProcessing; + + /* + * WebRTC listener for callbacks + */ + public IWebRTCListener webRTCListener; + + /* + * Context for WebRTCClient + */ + public Context context; + + + /* + * Fullscreen video renderer + */ + public SurfaceViewRenderer fullScreenRenderer; + + /* + * PIP video renderer + */ + public SurfaceViewRenderer pipRenderer; +} diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/WebSocketHandler.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/WebSocketHandler.java index c35f48eb..22955578 100644 --- a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/WebSocketHandler.java +++ b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/WebSocketHandler.java @@ -99,7 +99,7 @@ public void checkIfCalledOnValidThread() { @Override public void onOpen() { - + Log.d(TAG, "WebSocket connection opened."); } @Override diff --git a/webrtc-android-sample-app/src/main/AndroidManifest.xml b/webrtc-android-sample-app/src/main/AndroidManifest.xml index 4c35a6e6..d009c08d 100644 --- a/webrtc-android-sample-app/src/main/AndroidManifest.xml +++ b/webrtc-android-sample-app/src/main/AndroidManifest.xml @@ -124,6 +124,10 @@ android:exported="true" android:theme="@style/Theme.AppCompat.DayNight" android:configChanges="orientation|keyboard|screenSize|smallestScreenSize|screenLayout"/> + diff --git a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/HomeActivity.java b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/HomeActivity.java index 40483873..9964803a 100644 --- a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/HomeActivity.java +++ b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/HomeActivity.java @@ -54,6 +54,8 @@ private void createList() { "Settings")); activities.add(new ActivityLink(new Intent(this, TrackBasedConferenceActivity.class), "Multitrack Conference")); + activities.add(new ActivityLink(new Intent(this, PublishActivity.class), + "Publish")); } private void setListAdapter(List activities) { diff --git a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/PublishActivity.java b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/PublishActivity.java new file mode 100644 index 00000000..175bf334 --- /dev/null +++ b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/PublishActivity.java @@ -0,0 +1,70 @@ +package io.antmedia.webrtc_android_sample_app; + +import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_CAPTURETOTEXTURE_ENABLED; +import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_DATA_CHANNEL_ENABLED; +import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_VIDEO_BITRATE; +import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_VIDEO_FPS; + +import android.app.Activity; +import android.content.DialogInterface; +import android.content.SharedPreferences; +import android.os.Build; +import android.os.Bundle; +import android.preference.PreferenceManager; +import android.util.Log; +import android.view.View; +import android.view.Window; +import android.view.WindowManager; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.EditText; +import android.widget.Spinner; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.annotation.RequiresApi; +import androidx.appcompat.app.AlertDialog; + +import org.webrtc.DataChannel; +import org.webrtc.RendererCommon; +import org.webrtc.SurfaceViewRenderer; +import org.webrtc.VideoTrack; + +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; + +import de.tavendo.autobahn.WebSocket; +import io.antmedia.webrtcandroidframework.DefaultWebRTCListener; +import io.antmedia.webrtcandroidframework.IWebRTCClient; +import io.antmedia.webrtcandroidframework.StreamInfo; +import io.antmedia.webrtcandroidframework.WebRTCClient; + +public class PublishActivity extends Activity { + @RequiresApi(api = Build.VERSION_CODES.M) + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + View cameraViewRenderer = findViewById(R.id.camera_view_renderer); + View pipViewRenderer = findViewById(R.id.pip_view_renderer); + + IWebRTCClient webRTCClient = IWebRTCClient.builder() + .setPipRenderer((SurfaceViewRenderer) pipViewRenderer) + .setFullScreenRenderer((SurfaceViewRenderer) cameraViewRenderer) + .setServerUrl("wss://test.antmedia.io:5443/LiveApp/websocket") + .setContext(getApplicationContext()) + .setWebRTCListener(new DefaultWebRTCListener(this)) + .build(); + + View startStreamingButton = findViewById(R.id.start_streaming_button); + startStreamingButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + webRTCClient.publish("stream1"); + } + }); + } +} From 634e89cc582550f5335dfc45458cf22484ba644d Mon Sep 17 00:00:00 2001 From: burak-58 Date: Wed, 6 Dec 2023 23:45:43 +0300 Subject: [PATCH 02/11] refactor the sdk --- .../ConferenceManager.java | 486 ----- .../DefaultWebRTCListener.java | 306 ---- .../IDataChannelMessageSender.java | 7 - .../IEncoderStatisticsListener.java | 17 - .../webrtcandroidframework/IWebRTCClient.java | 193 -- .../webrtcandroidframework/MP3Publisher.java | 256 --- .../MultitrackConferenceManager.java | 562 ------ .../PermissionCallback.java | 5 - .../WebRTCClientConfig.java | 110 -- .../api/DefaultConferenceWebRTCListener.java | 127 ++ .../api/DefaultDataChannelObserver.java | 52 + .../api/DefaultWebRTCListener.java | 200 +++ .../{ => api}/IDataChannelObserver.java | 2 +- .../api/IWebRTCClient.java | 201 +++ .../{ => api}/IWebRTCListener.java | 28 +- .../apprtc/CallActivity.java | 130 -- .../{ => core}/CustomVideoCapturer.java | 5 +- .../core/MediaFileReader.java | 265 +++ .../core/PermissionsHandler.java | 86 + .../{ => core}/ProxyVideoSink.java | 2 +- .../core/ScreenCapturer.java | 242 +++ .../{ => core}/StatsCollector.java | 14 +- .../{ => core}/StreamInfo.java | 2 +- .../{ => core}/WebRTCClient.java | 1594 +++++------------ .../{ => core}/WebRTCClientBuilder.java | 60 +- .../core/WebRTCClientConfig.java | 140 ++ .../AntMediaSignallingEvents.java | 4 +- .../{ => websocket}/WebSocketConstants.java | 2 +- .../{ => websocket}/WebSocketHandler.java | 7 +- .../src/main/java/org/webrtc/VideoSource.java | 1 + .../CustomVideoCapturerTest.java | 2 + .../MP3PublisherTest.java | 139 -- .../MultitrackConferenceManagerTest.java | 133 -- .../ProxyVideoSinkTest.java | 2 + .../StatsCollectorTest.java | 10 +- .../WebRTCClientTest.java | 755 ++++---- .../WebSocketHandlerTest.java | 5 +- webrtc-android-sample-app/build.gradle | 5 +- .../ActivityTest.java | 154 -- .../ConferenceActivityTest.java | 132 -- .../HomeActivityTest.java | 23 - .../MP3PublishActivityTest.java | 140 -- .../MultitrackConferenceActivityTest.java | 146 -- ...ctivityTest.java => PlayActivityTest.java} | 54 +- ...vityTest.java => PublishActivityTest.java} | 47 +- .../PushToTalkActivityTest.java | 68 - .../RemoteParticipant.java | 95 - .../ScreenCaptureActivityTest.java | 143 -- .../SettingsActivityTest.java | 122 -- .../webrtc_android_sample_app/TestLogger.java | 26 + .../TrackBasedConferenceActivityTest.java | 295 --- .../src/main/AndroidManifest.xml | 96 +- .../AbstractSampleSDKActivity.java | 335 ---- .../ActivityLink.java | 22 - .../ButtonAdapter.java | 58 - .../ConferenceActivity.java | 222 --- .../CustomFrameActivity.java | 237 --- .../CustomHWFrameActivity.java | 222 --- .../DataChannelActivity.java | 541 ------ .../DataChannelOnlyActivity.java | 237 --- .../HomeActivity.java | 71 - .../MP3PublishActivity.java | 182 -- .../MP4PublishActivity.java | 307 ---- .../MP4PublishWithSurfaceActivity.java | 322 ---- .../MainActivity.java | 460 +---- .../MultiTrackPlayActivity.java | 187 -- .../MultitrackConferenceActivity.java | 192 -- .../PublishActivity.java | 70 - .../PushToTalkActivity.java | 139 -- .../ScreenCaptureActivity.java | 254 --- .../TestableActivity.java | 47 + .../TrackBasedConferenceActivity.java | 410 ----- .../advanced/MP3PublishActivity.java | 123 ++ .../advanced/MP4PublishActivity.java | 148 ++ .../MP4PublishWithSurfaceActivity.java | 161 ++ .../advanced/MultiTrackPlayActivity.java | 143 ++ .../{ => advanced}/USBCameraActivity.java | 170 +- .../basic/ConferenceActivity.java | 133 ++ .../basic/DataChannelOnlyActivity.java | 156 ++ .../{ => basic}/MediaProjectionService.java | 5 +- .../basic/PeerActivity.java | 148 ++ .../basic/PlayActivity.java | 147 ++ .../basic/PublishActivity.java | 144 ++ .../basic/ScreenCaptureActivity.java | 178 ++ .../{ => basic}/SettingsActivity.java | 4 +- .../{ => basic}/chat/ImageMessage.java | 2 +- .../{ => basic}/chat/Message.java | 2 +- .../{ => basic}/chat/MessageAdapter.java | 2 +- .../{ => basic}/chat/SettingsActivity.java | 2 +- .../{ => basic}/chat/TextMessage.java | 2 +- .../minimal/SimplePublishActivity.java | 27 + .../src/main/res/layout/activity_cam.xml | 11 +- .../main/res/layout/activity_conference.xml | 20 +- .../res/layout/activity_data_channel_only.xml | 2 +- .../src/main/res/layout/activity_home.xml | 23 - .../src/main/res/layout/activity_main.xml | 86 +- .../main/res/layout/activity_multitrack.xml | 80 +- .../layout/activity_multitrack_conference.xml | 115 -- .../src/main/res/layout/activity_peer.xml | 80 + .../src/main/res/layout/activity_play.xml | 71 + .../src/main/res/layout/activity_ptt.xml | 2 +- .../src/main/res/layout/activity_publish.xml | 71 + .../main/res/layout/activity_screenshare.xml | 24 +- .../res/layout/activity_simple_publish.xml | 23 + .../src/main/res/values/ids.xml | 4 + .../AbstractSampleSDKActivityTest.java | 256 --- .../TrackBasedConferenceActivityUnitTest.java | 57 - 107 files changed, 4303 insertions(+), 10532 deletions(-) delete mode 100644 webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/ConferenceManager.java delete mode 100644 webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/DefaultWebRTCListener.java delete mode 100644 webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/IDataChannelMessageSender.java delete mode 100644 webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/IEncoderStatisticsListener.java delete mode 100644 webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/IWebRTCClient.java delete mode 100644 webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/MP3Publisher.java delete mode 100644 webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/MultitrackConferenceManager.java delete mode 100644 webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/PermissionCallback.java delete mode 100644 webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/WebRTCClientConfig.java create mode 100644 webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/api/DefaultConferenceWebRTCListener.java create mode 100644 webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/api/DefaultDataChannelObserver.java create mode 100644 webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/api/DefaultWebRTCListener.java rename webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/{ => api}/IDataChannelObserver.java (91%) create mode 100644 webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/api/IWebRTCClient.java rename webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/{ => api}/IWebRTCListener.java (88%) delete mode 100644 webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/apprtc/CallActivity.java rename webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/{ => core}/CustomVideoCapturer.java (96%) create mode 100644 webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/MediaFileReader.java create mode 100644 webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/PermissionsHandler.java rename webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/{ => core}/ProxyVideoSink.java (92%) create mode 100644 webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/ScreenCapturer.java rename webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/{ => core}/StatsCollector.java (83%) rename webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/{ => core}/StreamInfo.java (94%) rename webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/{ => core}/WebRTCClient.java (58%) rename webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/{ => core}/WebRTCClientBuilder.java (56%) create mode 100644 webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClientConfig.java rename webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/{ => websocket}/AntMediaSignallingEvents.java (96%) rename webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/{ => websocket}/WebSocketConstants.java (99%) rename webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/{ => websocket}/WebSocketHandler.java (98%) delete mode 100644 webrtc-android-framework/src/test/java/io/antmedia/webrtcandroidframework/MP3PublisherTest.java delete mode 100644 webrtc-android-framework/src/test/java/io/antmedia/webrtcandroidframework/MultitrackConferenceManagerTest.java delete mode 100644 webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/ActivityTest.java delete mode 100644 webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/ConferenceActivityTest.java delete mode 100644 webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/HomeActivityTest.java delete mode 100644 webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/MP3PublishActivityTest.java delete mode 100644 webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/MultitrackConferenceActivityTest.java rename webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/{DataChannelActivityTest.java => PlayActivityTest.java} (60%) rename webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/{CustomVideoFeedActivityTest.java => PublishActivityTest.java} (63%) delete mode 100644 webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/PushToTalkActivityTest.java delete mode 100644 webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/RemoteParticipant.java delete mode 100644 webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/ScreenCaptureActivityTest.java delete mode 100644 webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/SettingsActivityTest.java create mode 100644 webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/TestLogger.java delete mode 100644 webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/TrackBasedConferenceActivityTest.java delete mode 100644 webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/AbstractSampleSDKActivity.java delete mode 100644 webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/ActivityLink.java delete mode 100644 webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/ButtonAdapter.java delete mode 100644 webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/ConferenceActivity.java delete mode 100644 webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/CustomFrameActivity.java delete mode 100644 webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/CustomHWFrameActivity.java delete mode 100644 webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/DataChannelActivity.java delete mode 100644 webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/DataChannelOnlyActivity.java delete mode 100644 webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/HomeActivity.java delete mode 100644 webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/MP3PublishActivity.java delete mode 100644 webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/MP4PublishActivity.java delete mode 100644 webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/MP4PublishWithSurfaceActivity.java delete mode 100644 webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/MultiTrackPlayActivity.java delete mode 100644 webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/MultitrackConferenceActivity.java delete mode 100644 webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/PublishActivity.java delete mode 100644 webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/PushToTalkActivity.java delete mode 100644 webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/ScreenCaptureActivity.java create mode 100644 webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/TestableActivity.java delete mode 100644 webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/TrackBasedConferenceActivity.java create mode 100644 webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/advanced/MP3PublishActivity.java create mode 100644 webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/advanced/MP4PublishActivity.java create mode 100644 webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/advanced/MP4PublishWithSurfaceActivity.java create mode 100644 webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/advanced/MultiTrackPlayActivity.java rename webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/{ => advanced}/USBCameraActivity.java (72%) create mode 100644 webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/basic/ConferenceActivity.java create mode 100644 webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/basic/DataChannelOnlyActivity.java rename webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/{ => basic}/MediaProjectionService.java (97%) create mode 100644 webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/basic/PeerActivity.java create mode 100644 webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/basic/PlayActivity.java create mode 100644 webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/basic/PublishActivity.java create mode 100644 webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/basic/ScreenCaptureActivity.java rename webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/{ => basic}/SettingsActivity.java (96%) rename webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/{ => basic}/chat/ImageMessage.java (91%) rename webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/{ => basic}/chat/Message.java (97%) rename webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/{ => basic}/chat/MessageAdapter.java (98%) rename webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/{ => basic}/chat/SettingsActivity.java (94%) rename webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/{ => basic}/chat/TextMessage.java (90%) create mode 100644 webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/minimal/SimplePublishActivity.java delete mode 100644 webrtc-android-sample-app/src/main/res/layout/activity_home.xml delete mode 100644 webrtc-android-sample-app/src/main/res/layout/activity_multitrack_conference.xml create mode 100644 webrtc-android-sample-app/src/main/res/layout/activity_peer.xml create mode 100644 webrtc-android-sample-app/src/main/res/layout/activity_play.xml create mode 100644 webrtc-android-sample-app/src/main/res/layout/activity_publish.xml create mode 100644 webrtc-android-sample-app/src/main/res/layout/activity_simple_publish.xml create mode 100644 webrtc-android-sample-app/src/main/res/values/ids.xml delete mode 100644 webrtc-android-sample-app/src/test/java/io/antmedia/webrtc_android_sample_app/AbstractSampleSDKActivityTest.java delete mode 100644 webrtc-android-sample-app/src/test/java/io/antmedia/webrtc_android_sample_app/TrackBasedConferenceActivityUnitTest.java diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/ConferenceManager.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/ConferenceManager.java deleted file mode 100644 index e71b0fa2..00000000 --- a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/ConferenceManager.java +++ /dev/null @@ -1,486 +0,0 @@ -package io.antmedia.webrtcandroidframework; - -import android.content.Context; -import android.content.Intent; -import android.os.Build; -import android.os.Handler; -import android.util.Log; - -import org.json.JSONException; -import org.json.JSONObject; -import org.webrtc.DataChannel; -import org.webrtc.IceCandidate; -import org.webrtc.SessionDescription; -import org.webrtc.SurfaceViewRenderer; - -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Set; - -import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_DATA_CHANNEL_ENABLED; - -/* - * This class manages WebRTCClients for Stream Based Conference Solution - * https://antmedia.io/reveal-the-secrets-of-3-types-of-video-conference-solutions/ - * - * @deprecated you can use a single WebRTCClient object to handle all the streams - */ -@Deprecated -public class ConferenceManager implements AntMediaSignallingEvents, IDataChannelMessageSender { - private final Context context; - private final Intent intent; - private final String serverUrl; - private final String roomName; - private String streamId; - private HashMap peers = new HashMap<>(); - private LinkedHashMap playRendererAllocationMap = new LinkedHashMap<>(); - private SurfaceViewRenderer publishViewRenderer; - private final IWebRTCListener webRTCListener; - private final IDataChannelObserver dataChannelObserver; - private WebSocketHandler wsHandler; - private Handler handler = new Handler(); - private boolean joined = false; - - private boolean openFrontCamera = false; - - private int ROOM_INFO_POLLING_MILLIS = 5000; - private Runnable getRoomInfoRunnable = new Runnable() { - - @Override - public void run() { - getRoomInfo(); - handler.postDelayed(this, ROOM_INFO_POLLING_MILLIS); - } - }; - private boolean playOnlyMode = false; - - - public ConferenceManager(Context context, IWebRTCListener webRTCListener, Intent intent, String serverUrl, String roomName, SurfaceViewRenderer publishViewRenderer, ArrayList playViewRenderers, String streamId, IDataChannelObserver dataChannelObserver) { - this.context = context; - this.intent = intent; - this.publishViewRenderer = publishViewRenderer; - if (playViewRenderers != null) { - for (SurfaceViewRenderer svr : playViewRenderers) { - this.playRendererAllocationMap.put(svr, null); - } - } - this.serverUrl = serverUrl; - this.roomName = roomName; - this.webRTCListener = webRTCListener; - this.streamId = streamId; - this.dataChannelObserver = dataChannelObserver; - if (dataChannelObserver != null) { - this.intent.putExtra(EXTRA_DATA_CHANNEL_ENABLED, true); - } - initWebSocketHandler(); - } - - public void setPlayOnlyMode(boolean playOnlyMode) { - this.playOnlyMode = playOnlyMode; - } - - public boolean isPlayOnlyMode() { - return playOnlyMode; - } - - public boolean isJoined() { - return joined; - } - - public void joinTheConference() { - initWebSocketHandler(); - wsHandler.joinToConferenceRoom(roomName, streamId); - } - - private void initWebSocketHandler() { - if (wsHandler == null) { - wsHandler = new WebSocketHandler(this, handler); - wsHandler.connect(serverUrl); - } - } - - public void leaveFromConference() { - - for (WebRTCClient peer : peers.values()) { - peer.stopStream(); - deallocateRenderer(peer); - } - - wsHandler.leaveFromTheConferenceRoom(roomName); - joined = false; - - // remove periodic room information polling - clearGetRoomInfoSchedule(); - - } - - private WebRTCClient createPeer(String streamId, String mode) { - WebRTCClient webRTCClient = new WebRTCClient(webRTCListener, context); - - webRTCClient.setWsHandler(wsHandler); - - String tokenId = ""; - - if(mode == IWebRTCClient.MODE_PUBLISH) { - webRTCClient.setOpenFrontCamera(openFrontCamera); - webRTCClient.setVideoRenderers(null, publishViewRenderer); - } - else { - webRTCClient.setVideoRenderers(null, allocateRenderer(webRTCClient)); - } - - if (dataChannelObserver != null) { - webRTCClient.setDataChannelObserver(dataChannelObserver); - } - - webRTCClient.init(serverUrl, streamId, mode, tokenId, intent); - - return webRTCClient; - } - - private SurfaceViewRenderer allocateRenderer(WebRTCClient peer) { - - for (Map.Entry entry : playRendererAllocationMap.entrySet()) { - if(entry.getValue() == null) { - entry.setValue(peer); - return entry.getKey(); - } - } - return null; - } - - private void deallocateRenderer(WebRTCClient peer) { - for (Map.Entry entry : playRendererAllocationMap.entrySet()) { - if(entry.getValue() == peer) { - entry.setValue(null); - } - } - } - - - //AntMediaSignallingEvents - @Override - public void onPublishStarted(String streamId) { - peers.get(streamId).onPublishStarted(streamId); - } - - @Override - public void onRemoteIceCandidate(String streamId, IceCandidate candidate) { - peers.get(streamId).onRemoteIceCandidate(streamId, candidate); - } - - @Override - public void onTakeConfiguration(String streamId, SessionDescription sdp) { - peers.get(streamId).onTakeConfiguration(streamId, sdp); - } - - @Override - public void onPublishFinished(String streamId) { - peers.get(streamId).onPublishFinished(streamId); - } - - public String getStreamId() { - return streamId; - } - - @Override - public void onPlayStarted(String streamId) { - peers.get(streamId).onPlayStarted(streamId); - } - - @Override - public void onPlayFinished(String streamId) { - //it has been deleted because of stream leaved message - if(peers.containsKey(streamId)) { - peers.get(streamId).onPlayFinished(streamId); - } - - streamLeft(streamId); - } - - @Override - public void noStreamExistsToPlay(String streamId) { - peers.get(streamId).noStreamExistsToPlay(streamId); - } - - @Override - public void streamIdInUse(String streamId){ - peers.get(streamId).streamIdInUse(streamId); - } - - @Override - public void onStartStreaming(String streamId) { - peers.get(streamId).onStartStreaming(streamId); - } - - - public void setOpenFrontCamera(boolean openFrontCamera) { - this.openFrontCamera = openFrontCamera; - } - - - public void publishStream(String streamId) { - if (!this.playOnlyMode) { - WebRTCClient publisher = createPeer(streamId, IWebRTCClient.MODE_PUBLISH); - this.streamId = streamId; - peers.put(streamId, publisher); - publisher.startStream(); - } - else { - Log.i(getClass().getSimpleName(), "Play only mode. No publishing"); - } - } - - @Override - public void onJoinedTheRoom(String streamId, String[] streams) { - Log.w(this.getClass().getSimpleName(), "On Joined the Room "); - publishStream(streamId); - - if (streams != null) - { - for (String id : streams) { - WebRTCClient player = createPeer(id, IWebRTCClient.MODE_PLAY); - peers.put(id, player); - player.startStream(); - } - } - - joined = true; - // start periodic polling of room info - scheduleGetRoomInfo(); - } - - @Override - public void onRoomInformation(String[] streams) { - Set streamSet = new HashSet<>(); - Collections.addAll(streamSet, streams); - Set oldStreams = new HashSet<>(peers.keySet()); - // remove publisher stream id - oldStreams.remove(streamId); - - // find newly removed streams - ArrayList streamsLeft = new ArrayList<>(); - for (String oldStream : oldStreams) { - // old stream has left now - if (!streamSet.contains(oldStream)) { - streamsLeft.add(oldStream); - } - } - - // find newly added streams - ArrayList streamsJoined = new ArrayList<>(); - for (String stream : streams) { - // a new stream joined now - if (!oldStreams.contains(stream)) { - streamsJoined.add(stream); - } - } - - // remove them - for (String leftStream : streamsLeft) { - streamLeft(leftStream); - Log.i("ConferenceManager", "left stream: " + leftStream); - } - // add them - for (String joinedStream : streamsJoined) { - streamJoined(joinedStream); - Log.i("ConferenceManager", "joined stream: " + joinedStream); - } - - WebRTCClient publisherClient = peers.get(streamId); - if (publisherClient != null && !publisherClient.isStreaming()) { - publishStream(streamId); - } - } - - public void switchCamera() - { - WebRTCClient publisherClient = peers.get(streamId); - if (publisherClient != null) { - publisherClient.switchCamera(); - } - } - - - private void streamJoined(String streamId) { - WebRTCClient player = createPeer(streamId, IWebRTCClient.MODE_PLAY); - peers.put(streamId, player); - player.startStream(); - } - - private void streamLeft(String streamId) { - WebRTCClient peer = peers.remove(streamId); - if (peer != null) { - deallocateRenderer(peer); - peer.stopStream(); - Log.i(ConferenceManager.class.getSimpleName(), "Stream left: " + streamId); - } - else { - Log.w(ConferenceManager.class.getSimpleName(), "Stream left (" + streamId +") but there is no associated peer "); - } - } - - @Override - public void onDisconnected() { - clearGetRoomInfoSchedule(); - - } - - @Override - public void onTrackList(String[] tracks) { - - } - - @Override - public void onBitrateMeasurement(String streamId, int targetBitrate, int videoBitrate, int audioBitrate) { - - } - - @Override - public void onStreamInfoList(String streamId, ArrayList streamInfoList) { - - } - - @Override - public void onError(String streamId, String definition) { - - } - - @Override - public void onLeftTheRoom(String roomId) { - - } - - @Override - public void sendMessageViaDataChannel(DataChannel.Buffer buffer) { - WebRTCClient publishStream = peers.get(streamId); - - if (publishStream != null) { - publishStream.sendMessageViaDataChannel(buffer); - } else { - Log.w(this.getClass().getSimpleName(), "It did not joined to the conference room yet "); - } - } - - private void sendNotificationEvent(String eventType) { - JSONObject jsonObject = new JSONObject(); - try { - jsonObject.put("streamId", streamId); - jsonObject.put("eventType", eventType); - - String notificationEventText = jsonObject.toString(); - - final ByteBuffer buffer = ByteBuffer.wrap(notificationEventText.getBytes(StandardCharsets.UTF_8)); - DataChannel.Buffer buf = new DataChannel.Buffer(buffer, false); - sendMessageViaDataChannel(buf); - } catch (JSONException e) { - Log.e(this.getClass().getSimpleName(), "JSON write error when creating notification event"); - } - } - - public void disableVideo() { - WebRTCClient publishStream = peers.get(streamId); - - if (publishStream != null) { - if (publishStream.isStreaming()) { - publishStream.disableVideo(); - } - - sendNotificationEvent("CAM_TURNED_OFF"); - } else { - Log.w(this.getClass().getSimpleName(), "It did not joined to the conference room yet "); - } - } - - public void enableVideo() { - WebRTCClient publishStream = peers.get(streamId); - - if (publishStream != null) { - if (publishStream.isStreaming()) { - publishStream.enableVideo(); - } - sendNotificationEvent("CAM_TURNED_ON"); - } else { - Log.w(this.getClass().getSimpleName(), "It did not joined to the conference room yet "); - } - } - - public void disableAudio() { - WebRTCClient publishStream = peers.get(streamId); - - if (publishStream != null) { - if (publishStream.isStreaming()) { - publishStream.disableAudio(); - } - - sendNotificationEvent("MIC_MUTED"); - } else { - Log.w(this.getClass().getSimpleName(), "It did not joined to the conference room yet "); - } - } - - public void enableAudio() { - WebRTCClient publishStream = peers.get(streamId); - - if (publishStream != null) { - if (publishStream.isStreaming()) { - publishStream.enableAudio(); - } - sendNotificationEvent("MIC_UNMUTED"); - } else { - Log.w(this.getClass().getSimpleName(), "It did not joined to the conference room yet "); - } - } - - public boolean isPublisherAudioOn() { - WebRTCClient publishStream = peers.get(streamId); - if (publishStream != null) { - return publishStream.isAudioOn(); - } else { - Log.w(this.getClass().getSimpleName(), "It did not joined to the conference room yet "); - return false; - } - } - - public boolean isPublisherVideoOn() { - WebRTCClient publishStream = peers.get(streamId); - if (publishStream != null) { - return publishStream.isVideoOn(); - } else { - Log.w(this.getClass().getSimpleName(), "It did not joined to the conference room yet "); - return false; - } - } - - private void scheduleGetRoomInfo() { - handler.postDelayed(getRoomInfoRunnable, ROOM_INFO_POLLING_MILLIS); - } - - private void clearGetRoomInfoSchedule() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - if (handler.hasCallbacks(getRoomInfoRunnable)) { - handler.removeCallbacks(getRoomInfoRunnable); - } - } else { - handler.removeCallbacks(getRoomInfoRunnable); - } - - } - - private void getRoomInfo() { - // call getRoomInfo in web socket handler - if (wsHandler.isConnected()) { - wsHandler.getRoomInfo(roomName, streamId); - } - } - - public HashMap getPeers() { - return peers; - } -} diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/DefaultWebRTCListener.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/DefaultWebRTCListener.java deleted file mode 100644 index 93d84e94..00000000 --- a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/DefaultWebRTCListener.java +++ /dev/null @@ -1,306 +0,0 @@ -package io.antmedia.webrtcandroidframework; - -import android.Manifest; -import android.annotation.SuppressLint; -import android.app.Activity; -import android.content.Context; -import android.content.pm.PackageManager; -import android.os.Build; -import android.os.Bundle; -import android.util.Log; -import android.view.Window; -import android.view.WindowManager; -import android.widget.Toast; - -import androidx.core.app.ActivityCompat; - -import org.webrtc.DataChannel; -import org.webrtc.VideoTrack; - -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import de.tavendo.autobahn.WebSocket; - -public class DefaultWebRTCListener implements IWebRTCListener, IDataChannelObserver { - private final Activity activity; - private PermissionCallback permissionCallback; - - public static final String[] REQUIRED_PUBLISH_PERMISSIONS = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S ? - new String[] {Manifest.permission.RECORD_AUDIO, Manifest.permission.CAMERA, Manifest.permission.BLUETOOTH_CONNECT} - : - new String[] {Manifest.permission.RECORD_AUDIO, Manifest.permission.CAMERA}; - - // List of mandatory application permissions. - public static final String[] REQUIRED_MINIMUM_PERMISSIONS = {"android.permission.MODIFY_AUDIO_SETTINGS", - "android.permission.INTERNET"}; - - public DefaultWebRTCListener(Activity activity) { - this.activity = activity; - } - - - @Override - public void onBufferedAmountChange(long previousAmount, String dataChannelLabel) { - String messageText = "Data channel buffered amount changed: " + dataChannelLabel + ": " + previousAmount; - Log.d(DefaultWebRTCListener.class.getName(), messageText); - //Toast.makeText(this, messageText, Toast.LENGTH_LONG).show(); - } - - @Override - public void onStateChange(DataChannel.State state, String dataChannelLabel) { - String messageText = "Data channel state changed: " + dataChannelLabel + ": " + state; - Log.d(DefaultWebRTCListener.class.getName(), messageText); - //Toast.makeText(this, messageText, Toast.LENGTH_LONG).show(); - } - - @Override - public void onMessage(DataChannel.Buffer buffer, String dataChannelLabel) { - ByteBuffer data = buffer.data; - String messageText = new String(data.array(), StandardCharsets.UTF_8); - makeToast("New Message: " + messageText, Toast.LENGTH_LONG); - } - - @Override - public void onMessageSent(DataChannel.Buffer buffer, boolean successful) { - if (successful) { - ByteBuffer data = buffer.data; - final byte[] bytes = new byte[data.capacity()]; - data.get(bytes); - String messageText = new String(bytes, StandardCharsets.UTF_8); - - makeToast("Message is sent", Toast.LENGTH_SHORT); - } else { - makeToast("Could not send the text message", Toast.LENGTH_LONG); - } - } - - @Override - public void onDisconnected(String streamId) { - String messageText = "Disconnected for " + streamId; - Log.d(DefaultWebRTCListener.class.getName(), messageText); - makeToast(messageText, Toast.LENGTH_LONG); - } - - @Override - public void onPublishFinished(String streamId) { - String messageText = "Publish finished for " + streamId; - Log.d(DefaultWebRTCListener.class.getName(), messageText); - makeToast(messageText, Toast.LENGTH_LONG); - } - - @Override - public void onPlayFinished(String streamId) { - String messageText = "Play finished for " + streamId; - Log.d(DefaultWebRTCListener.class.getName(), messageText); - makeToast(messageText, Toast.LENGTH_LONG); - } - - @Override - public void onPublishStarted(String streamId) { - String messageText = "Publish started for " + streamId; - Log.d(DefaultWebRTCListener.class.getName(), messageText); - makeToast(messageText, Toast.LENGTH_LONG); - } - - @Override - public void onPlayStarted(String streamId) { - String messageText = "Play started for " + streamId; - Log.d(DefaultWebRTCListener.class.getName(), messageText); - makeToast(messageText, Toast.LENGTH_LONG); - } - - @Override - public void noStreamExistsToPlay(String streamId) { - String messageText = "No stream exists to play for " + streamId; - Log.d(DefaultWebRTCListener.class.getName(), messageText); - makeToast(messageText, Toast.LENGTH_LONG); - } - - @Override - public void onError(String description, String streamId) { - String messageText = "Error for " + streamId + " : " + description; - Log.d(DefaultWebRTCListener.class.getName(), messageText); - makeToast(messageText, Toast.LENGTH_LONG); - } - - @Override - public void onSignalChannelClosed(WebSocket.WebSocketConnectionObserver.WebSocketCloseNotification code, String streamId) { - String messageText = "Signal channel closed for " + streamId + " : " + code; - Log.d(DefaultWebRTCListener.class.getName(), messageText); - makeToast(messageText, Toast.LENGTH_LONG); - } - - @Override - public void streamIdInUse(String streamId) { - String messageText = "Stream id is already in use " + streamId; - Log.d(DefaultWebRTCListener.class.getName(), messageText); - makeToast(messageText, Toast.LENGTH_LONG); - } - - @Override - public void onIceConnected(String streamId) { - String messageText = "Ice connected for " + streamId; - Log.d(DefaultWebRTCListener.class.getName(), messageText); - makeToast(messageText, Toast.LENGTH_LONG); - } - - @Override - public void onIceDisconnected(String streamId) { - String messageText = "Ice disconnected for " + streamId; - Log.d(DefaultWebRTCListener.class.getName(), messageText); - makeToast(messageText, Toast.LENGTH_LONG); - } - - public void makeToast(String messageText, int lengthLong) { - //runOnUiThread(() -> Toast.makeText(DefaultWebRTCListener.this, messageText, lengthLong).show()); - } - - @Override - public void onTrackList(String[] tracks) { - String messageText = "Track list received"; - Log.d(DefaultWebRTCListener.class.getName(), messageText); - //Toast.makeText(this, messageText, Toast.LENGTH_LONG).show(); - } - - @Override - public void onBitrateMeasurement(String streamId, int targetBitrate, int videoBitrate, int audioBitrate) { - String messageText = "Bitrate measurement received"; - Log.d(DefaultWebRTCListener.class.getName(), messageText); - //Toast.makeText(this, messageText, Toast.LENGTH_LONG).show(); - } - - @Override - public void onStreamInfoList(String streamId, ArrayList streamInfoList) { - String messageText = "Stream info list received"; - Log.d(DefaultWebRTCListener.class.getName(), messageText); - //Toast.makeText(this, messageText, Toast.LENGTH_LONG).show(); - } - - @Override - public void onNewVideoTrack(VideoTrack track) { - String messageText = "New video track received"; - Log.d(DefaultWebRTCListener.class.getName(), messageText); - makeToast(messageText, Toast.LENGTH_LONG); - } - - @Override - public void onVideoTrackEnded(VideoTrack track) { - String messageText = "Video track ended"; - Log.d(DefaultWebRTCListener.class.getName(), messageText); - makeToast(messageText, Toast.LENGTH_LONG); - } - - @Override - public void onReconnectionAttempt(String streamId) { - String messageText = "Reconnection attempt for " + streamId; - Log.d(DefaultWebRTCListener.class.getName(), messageText); - makeToast(messageText, Toast.LENGTH_LONG); - } - - @Override - public void onJoinedTheRoom(String streamId, String[] streams) { - String messageText = "Joined the room for " + streamId; - Log.d(DefaultWebRTCListener.class.getName(), messageText); - makeToast(messageText, Toast.LENGTH_LONG); - } - - - @Override - public void onRoomInformation(String[] streams) { - String messageText = "Room information received"; - Log.d(DefaultWebRTCListener.class.getName(), messageText); - //Toast.makeText(this, messageText, Toast.LENGTH_LONG).show(); - } - - @Override - public void onLeftTheRoom(String roomId) { - String messageText = "Left the room for " + roomId; - Log.d(DefaultWebRTCListener.class.getName(), messageText); - //Toast.makeText(this, messageText, Toast.LENGTH_LONG).show(); - } - - @Override - public void onMutedFor(String streamId) { - String messageText = "Microphone is muted for " + streamId; - Log.d(DefaultWebRTCListener.class.getName(), messageText); - makeToast(messageText, Toast.LENGTH_LONG); - } - @Override - public void onUnmutedFor(String streamId) { - String messageText = "Microphone is unmuted for " + streamId; - Log.d(DefaultWebRTCListener.class.getName(), messageText); - makeToast(messageText, Toast.LENGTH_LONG); - } - - @Override - public void onCameraTurnOnFor(String streamId) { - String messageText = "Camera is turned on for " + streamId; - Log.d(DefaultWebRTCListener.class.getName(), messageText); - makeToast(messageText, Toast.LENGTH_LONG); - } - - @Override - public void onCameraTurnOffFor(String streamId) { - String messageText = "Camera is turned off for " + streamId; - Log.d(DefaultWebRTCListener.class.getName(), messageText); - makeToast(messageText, Toast.LENGTH_LONG); - } - - @Override - public void onSatatusUpdateFor(String streamId, boolean micStatus, boolean cameraStatus) { - String messageText = "Status update for " + streamId + " mic: " + micStatus + " camera: " + cameraStatus; - Log.d(DefaultWebRTCListener.class.getName(), messageText); - makeToast(messageText, Toast.LENGTH_LONG); - } - - @Override - public boolean checkAndRequestPermisssions(boolean isForPublish, PermissionCallback permissionCallback) { - ArrayList permissions = new ArrayList<>(); - permissions.addAll(Arrays.asList(REQUIRED_MINIMUM_PERMISSIONS)); - if(isForPublish) { - permissions.addAll(Arrays.asList(REQUIRED_PUBLISH_PERMISSIONS)); - } - - if (hasPermissions(activity.getApplicationContext(), permissions)) { - return true; - } - else { - this.permissionCallback = permissionCallback; - showPermissionsErrorAndRequest(permissions); - return false; - } - } - - public boolean hasPermissions(Context context, List permissions) { - if (context != null && permissions != null) { - for (String permission : permissions) { - if (ActivityCompat.checkSelfPermission(context, permission) - != PackageManager.PERMISSION_GRANTED) { - Log.w(DefaultWebRTCListener.class.getSimpleName(), "Permission required:"+permission); - return false; - } - } - } - return true; - } - public void showPermissionsErrorAndRequest(List permissions) { - makeToast("You need permissions before", Toast.LENGTH_SHORT); - String[] permissionArray = new String[permissions.size()]; - permissions.toArray(permissionArray); - ActivityCompat.requestPermissions(activity, permissionArray, 1); - } - - public void onRequestPermissionsResult( - int requestCode, - String[] permissions, - int[] grantResults - ) { - permissionCallback.onPermissionResult(); - } - -} - diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/IDataChannelMessageSender.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/IDataChannelMessageSender.java deleted file mode 100644 index 4c722f4f..00000000 --- a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/IDataChannelMessageSender.java +++ /dev/null @@ -1,7 +0,0 @@ -package io.antmedia.webrtcandroidframework; - -import org.webrtc.DataChannel.Buffer; - -public interface IDataChannelMessageSender { - void sendMessageViaDataChannel(Buffer buffer); -} diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/IEncoderStatisticsListener.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/IEncoderStatisticsListener.java deleted file mode 100644 index d5890c09..00000000 --- a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/IEncoderStatisticsListener.java +++ /dev/null @@ -1,17 +0,0 @@ -package io.antmedia.webrtcandroidframework; - -import org.webrtc.StatsReport; - -/** - * Created by karinca on 28.10.2017. - */ - -public interface IEncoderStatisticsListener { - - /** - * It is called on other thread then UI, if this function changes something on UI - * run view updates in UI Thread with runOnUIThread - * @param reports - */ - public void updateEncoderStatistics(final StatsReport[] reports); -} diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/IWebRTCClient.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/IWebRTCClient.java deleted file mode 100644 index 2aa858f9..00000000 --- a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/IWebRTCClient.java +++ /dev/null @@ -1,193 +0,0 @@ -package io.antmedia.webrtcandroidframework; - -import android.Manifest; -import android.content.Intent; -import android.os.Build; - -import org.webrtc.RendererCommon; -import org.webrtc.SurfaceViewRenderer; - -/** - * Created by karinca on 20.10.2017. - */ - -public interface IWebRTCClient { - - public static final String[] REQUIRED_PERMISSIONS = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S ? - new String[] {Manifest.permission.RECORD_AUDIO, Manifest.permission.CAMERA, Manifest.permission.BLUETOOTH_CONNECT} - : - new String[] {Manifest.permission.RECORD_AUDIO, Manifest.permission.CAMERA}; - - /** - * Publish mode - */ - String MODE_PUBLISH = "publish"; - - /** - * Play mode - */ - String MODE_PLAY = "play"; - - /** - * Join mode - */ - String MODE_JOIN = "join"; - - /** - * Multi track play - */ - String MODE_MULTI_TRACK_PLAY = "multi_track_play"; - - /** - * Used for track based conference - */ - String MODE_TRACK_BASED_CONFERENCE = "track_based_conference"; - - - /** - * Camera open order - * By default front camera is attempted to be opened at first, - * if it is set to false, another camera that is not front will be tried to be open - * @param openFrontCamera if it is true, front camera will tried to be opened - * if it is false, another camera that is not front will be tried to be opened - */ - void setOpenFrontCamera(boolean openFrontCamera); - - - /** - - * If mode is MODE_PUBLISH, stream with streamId field will be published to the Server - * if mode is MODE_PLAY, stream with streamId field will be played from the Server - * - * @param url websocket url to connect - * @param streamId is the stream id in the server to process - * @param mode one of the MODE_PUBLISH, MODE_PLAY, MODE_JOIN - * @param token one time token string - */ - void init(String url, String streamId, String mode, String token, Intent intent); - - - /** - * Starts the streaming according to mode - */ - void startStream(); - - /** - * Stops the streaming - */ - void stopStream(); - - /** - * Switches the cameras - */ - void switchCamera(); - - /** - * Switches the video according to type and its aspect ratio - * @param scalingType - */ - void switchVideoScaling(RendererCommon.ScalingType scalingType); - - /** - * toggle microphone - * @return - */ - boolean toggleMic(); - - /** - * Stops the video source - */ - void stopVideoSource(); - - /** - * Starts or restarts the video source - */ - void startVideoSource(); - - /** - * Swapeed the fullscreen renderer and pip renderer - * @param b - */ - void setSwappedFeeds(boolean b); - - /** - * Set's the video renderers, - * @param pipRenderer can be nullable - * @param fullscreenRenderer cannot be nullable - */ - void setVideoRenderers(SurfaceViewRenderer pipRenderer, SurfaceViewRenderer fullscreenRenderer); - - /** - * Get the error - * @return error or null if not - */ - String getError(); - - - - void setMediaProjectionParams(int resultCode, Intent data); - - /** - * Return if data channel is enabled and open - * @return true if data channel is available - * false if it's not opened either by mobile or server side - */ - boolean isDataChannelEnabled(); - - /** - * This is used to get stream info list - */ - void getStreamInfoList(); - - /** - * This is used to play the specified resolution - * @param height - */ - void forceStreamQuality(int height); - - /** - * This is used to set subscriber parameters for TOTP (time-based one time password) - * @param subscriberId: Id for publisher or player - * @param subscriberCode - */ - void setSubscriberParams(String subscriberId, String subscriberCode); - - /** - * This is used to set any metadata for WebRTC player - * @param viewerInfo: metadata e.g name, location or anything - */ - void setViewerInfo(String viewerInfo); - - /** - * This is used to set the name of WebRTC stream - * @param streamName: any name - */ - void setStreamName(String streamName); - - - //FIXME: add comment - void onCameraSwitch(); - void onVideoScalingSwitch(RendererCommon.ScalingType scalingType); - void onCaptureFormatChange(int width, int height, int framerate); - boolean onToggleMic(); - - public static WebRTCClientBuilder builder() { - return new WebRTCClientBuilder(); - } - - /** - * This is used to strart a WebRTC publish stream - * @param streamId: any name - */ - public void publish(String streamId); - - - /** - * This is used to strart a WebRTC publish stream - * @param streamId: any name - * @param token: token for stream - * TODO: add comment - */ - public void publish(String streamId, String token, boolean videoCallEnabled, boolean audioCallEnabled, - String subscriberId, String subscriberCode, String streamName, String mainTrackId); -} diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/MP3Publisher.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/MP3Publisher.java deleted file mode 100644 index 5d3af21b..00000000 --- a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/MP3Publisher.java +++ /dev/null @@ -1,256 +0,0 @@ -package io.antmedia.webrtcandroidframework; - -import android.app.Activity; -import android.media.MediaCodec; -import android.media.MediaExtractor; -import android.media.MediaFormat; -import android.os.Process; -import android.util.Log; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import org.webrtc.audio.CustomWebRtcAudioRecord; - -import java.io.FileInputStream; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; - -public class MP3Publisher { - - private static final int DESIRED_SAMPLE_RATE = 48000; - private final Activity activity; - private String filePath; - private WebRTCClient webRTCClient; - private boolean stoppedStream = false; - private String TAG = MP3Publisher.class.getSimpleName(); - private boolean audioPushingEnabled = false; - private Thread publisherThread; - - public MP3Publisher(WebRTCClient webRTCClient, Activity activity, String filePath) { - this.webRTCClient = webRTCClient; - this.activity = activity; - this.filePath = filePath; - - webRTCClient.setInputSampleRate(48000); - webRTCClient.setStereoInput(false); - //default AudioFormat.ENCODING_PCM_16BIT - webRTCClient.setAudioInputFormat(CustomWebRtcAudioRecord.DEFAULT_AUDIO_FORMAT); - webRTCClient.setCustomAudioFeed(true); - } - - public void startStreaming() { - publisherThread = new Thread() { - @Override - public void run() { - CustomWebRtcAudioRecord audioInput = webRTCClient.getAudioInput(); - while (audioInput.isStarted() == false) { - //It means that it's not initialized - try { - Thread.sleep(10); - Log.i("Audio", "Audio input is not initialized"); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - MP3Publisher.this.audioPushingEnabled = true; - Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_AUDIO); - - Log.i("Audio ", "Audio input is started"); - startFilePublishing(); - } - }; - publisherThread.start(); - } - - - - public void startFilePublishing() { - try { - /* - final String uriPath="android.resource://"+activity.getPackageName()+"/raw/"+R.raw.sample_44100_stereo; - final Uri uri= Uri.parse(uriPath); - MediaExtractor extractor = new MediaExtractor(); - extractor.setDataSource(activity, uri, null); - */ - - - MediaExtractor extractor = getMediaExtractor(); - MediaFormat format = getMediaFormat(extractor); - - if (format == null) { - Log.e(TAG, "No audio track found in MP3 file"); - return; - } - - MediaCodec codec = getMediaCodec(format); - - readAndPublishFile(extractor, format, codec); - - // Release resources - codec.stop(); - codec.release(); - extractor.release(); - } catch (IOException e) { - Log.e(TAG, "Error decoding MP3 to PCM: " + e.getMessage()); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - } - - @NonNull - public MediaCodec getMediaCodec(MediaFormat format) throws IOException { - // Create a MediaCodec to decode the MP3 file - MediaCodec codec = getCodecByName(format); - codec.configure(format, null, null, 0); - codec.start(); - return codec; - } - - public MediaCodec getCodecByName(MediaFormat format) throws IOException { - return MediaCodec.createDecoderByType(format.getString(MediaFormat.KEY_MIME)); - } - - @NonNull - private MediaExtractor getMediaExtractor() throws IOException { - // Read input file - FileInputStream inputStream = new FileInputStream(filePath); - - // Create a MediaExtractor to extract audio data from the file - MediaExtractor extractor = new MediaExtractor(); - extractor.setDataSource(inputStream.getFD()); - return extractor; - } - - public void readAndPublishFile(MediaExtractor extractor, MediaFormat format, MediaCodec codec) throws InterruptedException { - CustomWebRtcAudioRecord audioInput = webRTCClient.getAudioInput(); - - int bufferLength = audioInput.getBufferByteLength(); // this is the length of 10ms data - ByteBuffer rawAudioBuffer = getRawAudioByteBuffer(bufferLength); - - ByteBuffer[] inputBuffers = codec.getInputBuffers(); - ByteBuffer[] outputBuffers = codec.getOutputBuffers(); - - MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); - - long presentationTimeUs = 0; - boolean isEOS = false; - - while (!isEOS && stoppedStream == false) { - Log.i("Audio", "push audio"); - int inputBufferIndex = codec.dequeueInputBuffer(10000); - if (inputBufferIndex >= 0) { - ByteBuffer inputBuffer = inputBuffers[inputBufferIndex]; - int sampleSize = extractor.readSampleData(inputBuffer, 0); - if (sampleSize < 0) { - codec.queueInputBuffer(inputBufferIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM); - isEOS = true; - } else { - presentationTimeUs = extractor.getSampleTime(); - codec.queueInputBuffer(inputBufferIndex, 0, sampleSize, presentationTimeUs, 0); - extractor.advance(); - } - } - - int outputBufferIndex = codec.dequeueOutputBuffer(info, 10000); - if (outputBufferIndex >= 0) { - ByteBuffer outputBuffer = outputBuffers[outputBufferIndex]; - rawAudioBuffer.put(outputBuffer); - int length = rawAudioBuffer.position(); - - rawAudioBuffer.position(0); - int channelCount = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT); - int readBufferLength = bufferLength * format.getInteger(MediaFormat.KEY_SAMPLE_RATE) / DESIRED_SAMPLE_RATE*channelCount; - Log.d(TAG, "pushAudio: length: " + length + " bufferLength: " + bufferLength); - while(length - rawAudioBuffer.position() >= readBufferLength) { - byte[] pcmData = new byte[readBufferLength]; - rawAudioBuffer.get(pcmData); - byte[] resampledData = modifySampleRate(pcmData, format.getInteger(MediaFormat.KEY_SAMPLE_RATE), DESIRED_SAMPLE_RATE, channelCount); - - Log.d(TAG, "length: " + length+ " position: " + rawAudioBuffer.position()); - - - audioInput.pushAudio(resampledData, resampledData.length); - Log.i("Audio", "push audio: " + pcmData[0] + " : " + pcmData[1] + " : " + pcmData[2] + " : " + pcmData[3] + " : "); - //emulate real time streaming by waiting 10ms because we're reading from the file directly - //When you decode the audio from incoming RTSP stream, you don't need to sleep, just send it immediately when you get - Thread.sleep(10); - } - - byte[] moreData = new byte[length - rawAudioBuffer.position()]; - rawAudioBuffer.get(moreData); - rawAudioBuffer.clear(); - rawAudioBuffer.put(moreData); - - - - codec.releaseOutputBuffer(outputBufferIndex, false); - } else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { - format = codec.getOutputFormat(); - } - } - } - - @NonNull - public ByteBuffer getRawAudioByteBuffer(int bufferLength) { - ByteBuffer rawAudioBuffer = ByteBuffer.allocate(bufferLength *20); - return rawAudioBuffer; - } - - @Nullable - private MediaFormat getMediaFormat(MediaExtractor extractor) { - // Find and select the MP3 track - MediaFormat format = null; - int trackCount = extractor.getTrackCount(); - for (int i = 0; i < trackCount; i++) { - format = extractor.getTrackFormat(i); - String mime = format.getString(MediaFormat.KEY_MIME); - if (mime != null && mime.startsWith("audio/")) { - extractor.selectTrack(i); - break; - } - } - return format; - } - - private byte[] modifySampleRate(byte[] pcmData, int inputSampleRate, int outputSampleRate, int channelCount) { - int inputLength = pcmData.length / 2; // Dividing by 2 assuming 16-bit PCM data - int outputLength = (int) ((inputLength / (float) inputSampleRate) * outputSampleRate)/channelCount; - short[] inputSamples = new short[inputLength]; - short[] outputSamples = new short[outputLength]; - - // Convert byte array to short array - ByteBuffer.wrap(pcmData).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(inputSamples); - - // Perform linear interpolation and stereo to mono conversion - float ratio = (float) inputSampleRate / outputSampleRate; - for (int i = 0; i < outputLength; i++) { - float index = i * ratio; - int leftSampleIndex = (int) index; - int rightSampleIndex = Math.min(leftSampleIndex + 1, inputLength - channelCount); - float fraction = index - leftSampleIndex; - - short leftSample = inputSamples[leftSampleIndex * channelCount]; - short rightSample = inputSamples[Math.min(rightSampleIndex * channelCount, inputLength - 1)]; - - // Linear interpolation and stereo to mono conversion - outputSamples[i] = (short) (((1 - fraction) * leftSample + fraction * rightSample) / 2); - } - - // Convert short array to byte array - byte[] outputData = new byte[outputLength * 2]; // Multiplying by 2 assuming 16-bit PCM data - ByteBuffer.wrap(outputData).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().put(outputSamples); - - return outputData; - } - - public void stopStreaming() { - stoppedStream = true; - } - - public void setFilePath(String path) { - this.filePath = path; - } - -} \ No newline at end of file diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/MultitrackConferenceManager.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/MultitrackConferenceManager.java deleted file mode 100644 index b66c8df0..00000000 --- a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/MultitrackConferenceManager.java +++ /dev/null @@ -1,562 +0,0 @@ -package io.antmedia.webrtcandroidframework; - -import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_DATA_CHANNEL_ENABLED; - -import android.content.Context; -import android.content.Intent; -import android.os.Build; -import android.os.Handler; -import android.util.Log; -import android.view.View; - -import org.json.JSONException; -import org.json.JSONObject; -import org.webrtc.DataChannel; -import org.webrtc.IceCandidate; -import org.webrtc.MediaStreamTrack; -import org.webrtc.NetworkChangeDetector; -import org.webrtc.NetworkMonitorAutoDetect; -import org.webrtc.SessionDescription; -import org.webrtc.SurfaceViewRenderer; -import org.webrtc.VideoTrack; - -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Random; - -/* - * This class manages the Stream Based Conference Solution with 2 WebRTCClient;one for publishing the participants video, - * the other one for playing the main track which includes all participants streams as subtrack. - * https://antmedia.io/reveal-the-secrets-of-3-types-of-video-conference-solutions/ - * - * @deprecated you can use a single WebRTCClient object to handle all the streams - */ -@Deprecated -public class MultitrackConferenceManager implements AntMediaSignallingEvents, IDataChannelMessageSender { - public static final String TAG = "Multitrack Conf"; - public static final int MAX_BITRATE = 2000; - public static final int MIN_BITRATE = 500; - private final Context context; - private final Intent intent; - private final String serverUrl; - private final String roomName; - private final ArrayList playViewRenderers; - private WebRTCClient publishWebRTCClient; - private WebRTCClient playWebRTCClient; - private String streamId; - private SurfaceViewRenderer publishViewRenderer; - private final IWebRTCListener webRTCListener; - private IDataChannelObserver dataChannelObserver; - private WebSocketHandler wsHandler; - private Handler handler = new Handler(); - private boolean joined = false; - private boolean audioOnly = false; - - - private boolean openFrontCamera = false; - - private int ROOM_INFO_POLLING_MILLIS = 5000; - - private boolean reconnectionEnabled = false; - - private LinkedHashMap playRendererAllocationMap = new LinkedHashMap<>(); - - private Runnable getRoomInfoRunnable = new Runnable() { - @Override - public void run() { - getRoomInfo(); - handler.postDelayed(this, ROOM_INFO_POLLING_MILLIS); - } - }; - private boolean playOnlyMode = false; - - private boolean playMessageSent = false; - private int minABRResolution; - - public MultitrackConferenceManager(Context context, IWebRTCListener webRTCListener, Intent intent, String serverUrl, String roomName, SurfaceViewRenderer publishViewRenderer, ArrayList playViewRenderers, String streamId, IDataChannelObserver dataChannelObserver) { - this.context = context; - this.intent = intent; - this.publishViewRenderer = publishViewRenderer; - this.playViewRenderers = playViewRenderers; - if (playViewRenderers != null) { - for (SurfaceViewRenderer svr : playViewRenderers) { - this.playRendererAllocationMap.put(svr, null); - } - } - this.serverUrl = serverUrl; - this.roomName = roomName; - this.webRTCListener = webRTCListener; - this.streamId = streamId == null ? "stream"+new Random().nextInt(99999) : streamId; - this.dataChannelObserver = dataChannelObserver; - if (dataChannelObserver != null) { - this.intent.putExtra(EXTRA_DATA_CHANNEL_ENABLED, true); - } - } - - public void init() { - initWebSocketHandler(); - if (!this.playOnlyMode) { - initPublishWebRTCClient(); - } - - initPlayWebRTCClient(); - } - - private void initPublishWebRTCClient() { - publishWebRTCClient = new WebRTCClient(webRTCListener, context); - publishWebRTCClient.setWsHandler(wsHandler); - publishWebRTCClient.setReconnectionEnabled(reconnectionEnabled); - publishWebRTCClient.setCheckStreamIdValidity(false); - if (dataChannelObserver != null) { - publishWebRTCClient.setDataChannelObserver(dataChannelObserver); - } - - String tokenId = ""; - - //publishWebRTCClient.setOpenFrontCamera(openFrontCamera); - publishWebRTCClient.setVideoRenderers(null, publishViewRenderer); - - publishWebRTCClient.setMainTrackId(roomName); - publishWebRTCClient.init(serverUrl, streamId, IWebRTCClient.MODE_PUBLISH, tokenId, intent); - } - - private void initPlayWebRTCClient() { - playWebRTCClient = new WebRTCClient(webRTCListener, context); - playWebRTCClient.setWsHandler(wsHandler); - playWebRTCClient.setReconnectionEnabled(reconnectionEnabled); - playWebRTCClient.setRemoteRendererList(playViewRenderers); - playWebRTCClient.setAutoPlayTracks(true); - playWebRTCClient.setMainTrackId(roomName); - playWebRTCClient.setSelfStreamId(streamId); - String tokenId = ""; - - if (dataChannelObserver != null) { - playWebRTCClient.setDataChannelObserver(dataChannelObserver); - } - - playWebRTCClient.init(serverUrl, roomName, IWebRTCClient.MODE_MULTI_TRACK_PLAY, tokenId, intent); - } - - public void setPlayOnlyMode(boolean playOnlyMode) { - this.playOnlyMode = playOnlyMode; - } - - public boolean isPlayOnlyMode() { - return playOnlyMode; - } - - public boolean isJoined() { - return joined; - } - - public void joinTheConference() { - wsHandler.joinToConferenceRoom(roomName, streamId); - } - - public void initWebSocketHandler() { - if (wsHandler == null) { - wsHandler = new WebSocketHandler(this, handler); - wsHandler.connect(serverUrl); - } - } - - public void leaveFromConference() { - - for (SurfaceViewRenderer peer : playRendererAllocationMap.keySet()) { - } - - wsHandler.leaveFromTheConferenceRoom(roomName); - joined = false; - playMessageSent = false; - - // remove periodic room information polling - clearGetRoomInfoSchedule(); - - } - - private SurfaceViewRenderer allocateRenderer(WebRTCClient peer) { - return null; - } - - private void deallocateRenderer(WebRTCClient peer) { - - } - - - //AntMediaSignallingEvents - @Override - public void onPublishStarted(String streamId) { - - if(publishWebRTCClient.isReconnectionInProgress()) { - //this is a trick to add participant id to conference - //structure again after reconnection - joinTheConference(); - } - - publishWebRTCClient.onPublishStarted(streamId); - } - - @Override - public void onRemoteIceCandidate(String streamId, IceCandidate candidate) { - if(streamId.equals(this.streamId)) { - publishWebRTCClient.onRemoteIceCandidate(streamId, candidate); - } - else if(streamId.equals(this.roomName)) { - playWebRTCClient.onRemoteIceCandidate(streamId, candidate); - } - } - - @Override - public void onTakeConfiguration(String streamId, SessionDescription sdp) { - if(streamId.equals(this.streamId)) { - publishWebRTCClient.onTakeConfiguration(streamId, sdp); - } - else if(streamId.equals(this.roomName)) { - playWebRTCClient.onTakeConfiguration(streamId, sdp); - } - } - - @Override - public void onPublishFinished(String streamId) { - publishWebRTCClient.onPublishFinished(streamId); - } - - public String getStreamId() { - return streamId; - } - - @Override - public void onPlayStarted(String streamId) { - playWebRTCClient.onPlayStarted(streamId); - //playStarted = true; - } - - @Override - public void onPlayFinished(String streamId) { - playWebRTCClient.onPlayFinished(streamId); - } - - @Override - public void noStreamExistsToPlay(String streamId) { - playWebRTCClient.noStreamExistsToPlay(streamId); - } - - @Override - public void streamIdInUse(String streamId){ - publishWebRTCClient.streamIdInUse(streamId); - } - - @Override - public void onStartStreaming(String streamId) { - if(streamId.equals(this.streamId)) { - publishWebRTCClient.onStartStreaming(streamId); - } - else if(streamId.equals(this.roomName)) { - playWebRTCClient.onStartStreaming(streamId); - } - } - - - public void setOpenFrontCamera(boolean openFrontCamera) { - this.openFrontCamera = openFrontCamera; - } - - - public void publishStream(String streamId) { - if (!this.playOnlyMode) { - publishWebRTCClient.startStream(); - //setPublishBitrate(networkDetector.getCurrentConnectionType()); - } - else { - Log.i(getClass().getSimpleName(), "Play only mode. No publishing"); - } - } - - @Override - public void onJoinedTheRoom(String streamId, String[] streams) { - Log.w(this.getClass().getSimpleName(), "On Joined the Room "); - - if(!publishWebRTCClient.isReconnectionInProgress() && !playOnlyMode) { - publishStream(streamId); - } - - if(playOnlyMode) { - startPlaying(); - } - - joined = true; - // start periodic polling of room info - scheduleGetRoomInfo(); - if(streams.length > 0) { - //on track list triggers start playing - onTrackList(streams); - } - } - - private void startPlaying() { - if(!playMessageSent) { - playWebRTCClient.startStream(); - playMessageSent = true; - } - } - - @Override - public void onRoomInformation(String[] streams) { - if (playWebRTCClient != null && !playWebRTCClient.isStreamStarted()) { - playWebRTCClient.startStream(); - } - } - - public void switchCamera() - { - if (publishWebRTCClient != null) { - publishWebRTCClient.switchCamera(); - } - } - - - private void streamJoined(String streamId) { - - } - - private void trackLeft(String streamId) { - /* - WebRTCClient peer = peers.remove(streamId); - if (peer != null) { - deallocateRenderer(peer); - peer.stopStream(); - Log.i(MultitrackConferenceManager.class.getSimpleName(), "Stream left: " + streamId); - } - else { - Log.w(MultitrackConferenceManager.class.getSimpleName(), "Stream left (" + streamId +") but there is no associated peer "); - } - - */ - } - - @Override - public void onDisconnected() { - clearGetRoomInfoSchedule(); - - } - - @Override - public void onTrackList(String[] tracks) { - //add own stream id to the list as !+streamId - ArrayList trackList = new ArrayList<>(); - trackList.addAll(Arrays.asList(tracks)); - trackList.remove(streamId); - trackList.add("!"+streamId); - - playWebRTCClient.onTrackList(trackList.toArray(new String[0])); - } - - @Override - public void onBitrateMeasurement(String streamId, int targetBitrate, int videoBitrate, int audioBitrate) { - - } - - @Override - public void onStreamInfoList(String streamId, ArrayList streamInfoList) { - String[] stringArray = new String[streamInfoList.size()]; - minABRResolution = 0; //automatic abr - int i = 0; - for (StreamInfo si : streamInfoList) { - minABRResolution = minABRResolution > si.getHeight() || minABRResolution == 0 ? si.getHeight() : minABRResolution; - } - - } - - @Override - public void onError(String streamId, String definition) { - if(streamId != null && streamId.equals(this.streamId)) { - publishWebRTCClient.onError(streamId, definition); - } - else if(streamId != null && streamId.equals(this.roomName)) { - playWebRTCClient.onError(streamId, definition); - } - } - - @Override - public void onLeftTheRoom(String roomId) { - - } - - @Override - public void sendMessageViaDataChannel(DataChannel.Buffer buffer) { - if (publishWebRTCClient != null) { - publishWebRTCClient.sendMessageViaDataChannel(buffer); - } else { - Log.w(this.getClass().getSimpleName(), "It did not joined to the conference room yet "); - } - } - - private void sendNotificationEvent(String eventType) { - JSONObject jsonObject = new JSONObject(); - try { - jsonObject.put("streamId", streamId); - jsonObject.put("eventType", eventType); - - String notificationEventText = jsonObject.toString(); - - final ByteBuffer buffer = ByteBuffer.wrap(notificationEventText.getBytes(StandardCharsets.UTF_8)); - DataChannel.Buffer buf = new DataChannel.Buffer(buffer, false); - sendMessageViaDataChannel(buf); - } catch (JSONException e) { - Log.e(this.getClass().getSimpleName(), "JSON write error when creating notification event"); - } - } - - public void disableVideo() { - if (publishWebRTCClient != null) { - //if (publishWebRTCClient.isStreaming()) { - publishWebRTCClient.disableVideo(); - // } - - sendNotificationEvent("CAM_TURNED_OFF"); - } else { - Log.w(this.getClass().getSimpleName(), "It did not joined to the conference room yet "); - } - } - - public void enableVideo() { - if (publishWebRTCClient != null) { - //if (publishWebRTCClient.isStreaming()) { - publishWebRTCClient.enableVideo(); - //} - sendNotificationEvent("CAM_TURNED_ON"); - } else { - Log.w(this.getClass().getSimpleName(), "It did not joined to the conference room yet "); - } - } - - public void disableAudio() { - if (publishWebRTCClient != null) { - publishWebRTCClient.disableAudio(); - - sendNotificationEvent("MIC_MUTED"); - } else { - Log.w(this.getClass().getSimpleName(), "It did not joined to the conference room yet "); - } - } - - public void enableAudio() { - if (publishWebRTCClient != null) { - //if (publishWebRTCClient.isStreaming()) { - publishWebRTCClient.enableAudio(); - //} - sendNotificationEvent("MIC_UNMUTED"); - } else { - Log.w(this.getClass().getSimpleName(), "It did not joined to the conference room yet "); - } - } - - public boolean isPublisherAudioOn() { - if (publishWebRTCClient != null) { - return publishWebRTCClient.isAudioOn(); - } else { - Log.w(this.getClass().getSimpleName(), "It did not joined to the conference room yet "); - return false; - } - } - - public boolean isPublisherVideoOn() { - if (publishWebRTCClient != null) { - return publishWebRTCClient.isVideoOn(); - } else { - Log.w(this.getClass().getSimpleName(), "It did not joined to the conference room yet "); - return false; - } - } - - private void scheduleGetRoomInfo() { - handler.postDelayed(getRoomInfoRunnable, ROOM_INFO_POLLING_MILLIS); - } - - private void clearGetRoomInfoSchedule() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - if (handler.hasCallbacks(getRoomInfoRunnable)) { - handler.removeCallbacks(getRoomInfoRunnable); - } - } else { - handler.removeCallbacks(getRoomInfoRunnable); - } - - } - - private void getRoomInfo() { - // call getRoomInfo in web socket handler - if (wsHandler.isConnected()) { - wsHandler.getRoomInfo(roomName, streamId); - } - } - - public void updateAudioLevel(int level) { - JSONObject json = new JSONObject(); - try { - json.put(WebSocketConstants.STREAM_ID, streamId); - json.put("eventType", "UPDATE_AUDIO_LEVEL"); - json.put("audioLevel", level); - - final ByteBuffer buffer = ByteBuffer.wrap(json.toString().getBytes(StandardCharsets.UTF_8)); - DataChannel.Buffer buf= new DataChannel.Buffer(buffer,false); - publishWebRTCClient.sendMessageViaDataChannel(buf); - } catch (JSONException e) { - Log.e(this.getClass().getSimpleName(), "Connect to conference room JSON error: " + e.getMessage()); - } - } - - public boolean isReconnectionEnabled() { - return reconnectionEnabled; - } - - public void setReconnectionEnabled(boolean reconnectionEnabled) { - this.reconnectionEnabled = reconnectionEnabled; - if(publishWebRTCClient != null) { - publishWebRTCClient.setReconnectionEnabled(reconnectionEnabled); - } - if(playWebRTCClient != null) { - playWebRTCClient.setReconnectionEnabled(reconnectionEnabled); - } - } - - public void setPublishBitrate(NetworkChangeDetector.ConnectionType newConnectionType) { - if (newConnectionType.equals(NetworkChangeDetector.ConnectionType.CONNECTION_WIFI)) { - Log.d(TAG, "Network Wifi"); - if(publishWebRTCClient != null) { - publishWebRTCClient.setBitrate(MAX_BITRATE); - } - if(playWebRTCClient != null && playWebRTCClient.isStreaming()) { - playWebRTCClient.forceStreamQuality(-1); //unlimited - } - } else { - Log.d(TAG, "newConnectionType:" + newConnectionType); - if(publishWebRTCClient != null) { - publishWebRTCClient.setBitrate(MIN_BITRATE); - } - if(playWebRTCClient != null) { - playWebRTCClient.forceStreamQuality(minABRResolution); - } - } - } - - public void addTrackToRenderer(VideoTrack track, SurfaceViewRenderer renderer) { - playWebRTCClient.addTrackToRenderer(track, renderer); - } - - public void setWsHandler(WebSocketHandler wsHandler) { - this.wsHandler = wsHandler; - } - - public boolean isPlayMessageSent() { - return playMessageSent; - } - - public void setPublishWebRTCClient(WebRTCClient publishWebRTCClient) { - this.publishWebRTCClient = publishWebRTCClient; - } -} diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/PermissionCallback.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/PermissionCallback.java deleted file mode 100644 index 1dacc7fe..00000000 --- a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/PermissionCallback.java +++ /dev/null @@ -1,5 +0,0 @@ -package io.antmedia.webrtcandroidframework; - -public interface PermissionCallback { - void onPermissionResult(); -} diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/WebRTCClientConfig.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/WebRTCClientConfig.java deleted file mode 100644 index 582a4628..00000000 --- a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/WebRTCClientConfig.java +++ /dev/null @@ -1,110 +0,0 @@ -package io.antmedia.webrtcandroidframework; - -import android.content.Context; - -import org.webrtc.SurfaceViewRenderer; - -public class WebRTCClientConfig { - - /* - * websocket connection url to Ant Media Server - * ex. wss://{AMS URL}:5443/{AppName}/websocket - */ - public String serverUrl; - - /* - * stream id for stream - */ - public String streamId; - - /* - * token for stream - */ - public String token; - - /* - * Flag indicating whether video call is enabled - */ - public boolean videoCallEnabled; - - /* - * Flag indicating whether audio call is enabled - */ - public boolean audioCallEnabled; - - /* - * Flag indicating whether data channel is enabled - */ - public boolean dataChannelEnabled; - - /* - * Width of the video in pixels - */ - public int videoWidth; - - /* - * Height of the video in pixels - */ - public int videoHeight; - - /* - * Frames per second for the video - */ - public int videoFps; - - /* - * Initial bitrate for video transmission - */ - public int videoStartBitrate; - - /* - * Codec used for video encoding and decoding - */ - public String videoCodec; - - /* - * Flag for hardware codec acceleration - */ - public boolean hwCodecAcceleration; - - /* - * Flag indicating whether flexible forward error correction (FlexFEC) is enabled for video - */ - public boolean videoFlexfecEnabled; - - /* - * Initial bitrate for audio transmission - */ - public int audioStartBitrate; - - /* - * Codec used for audio encoding and decoding - */ - public String audioCodec; - - /* - * Flag indicating whether audio processing is disabled - */ - public boolean noAudioProcessing; - - /* - * WebRTC listener for callbacks - */ - public IWebRTCListener webRTCListener; - - /* - * Context for WebRTCClient - */ - public Context context; - - - /* - * Fullscreen video renderer - */ - public SurfaceViewRenderer fullScreenRenderer; - - /* - * PIP video renderer - */ - public SurfaceViewRenderer pipRenderer; -} 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 new file mode 100644 index 00000000..51f337ce --- /dev/null +++ b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/api/DefaultConferenceWebRTCListener.java @@ -0,0 +1,127 @@ +package io.antmedia.webrtcandroidframework.api; + +import android.os.Build; +import android.os.Handler; +import android.util.Log; + +public class DefaultConferenceWebRTCListener extends DefaultWebRTCListener { + private String roomId; + private String streamId; + + private boolean stoppedStream; + private boolean playOnlyMode = false; + private boolean joined = false; + + private boolean videoCallEnabled = true; + private boolean audioCallEnabled = true; + private boolean playMessageSent; + + private int ROOM_INFO_POLLING_MILLIS = 5000; + + private Handler handler = new Handler(); + private Runnable getRoomInfoRunnable = new Runnable() { + @Override + public void run() { + getRoomInfo(); + handler.postDelayed(this, ROOM_INFO_POLLING_MILLIS); + } + }; + + public DefaultConferenceWebRTCListener(String roomId, String streamId) { + super(); + this.roomId = roomId; + this.streamId = streamId; + } + + private void getRoomInfo() { + webRTCClient.getRoomInfo(roomId, streamId); + } + + @Override + public void onPublishStarted(String streamId) { + super.onPublishStarted(streamId); + } + + @Override + public void onPublishFinished(String streamId) { + super.onPublishFinished(streamId); + } + + + @Override + public void onDisconnected() { + super.onDisconnected(); + } + + @Override + public void onJoinedTheRoom(String streamId, String[] streams) { + super.onJoinedTheRoom(streamId, streams); + + + if (!webRTCClient.isReconnectionInProgress() && !playOnlyMode) { + publishStream(streamId); + } + + if (playOnlyMode) { + startPlaying(streams); + } + + joined = true; + // start periodic polling of room info + scheduleGetRoomInfo(); + if (streams.length > 0) { + //on track list triggers start playing + onTrackList(streams); + } + } + + @Override + public void onLeftTheRoom(String roomId) { + super.onLeftTheRoom(roomId); + clearGetRoomInfoSchedule(); + joined = false; + playMessageSent = false; + } + + @Override + public void onRoomInformation(String[] streams) { + if (webRTCClient != null) { + startPlaying(streams); + } + } + + private void scheduleGetRoomInfo() { + handler.postDelayed(getRoomInfoRunnable, ROOM_INFO_POLLING_MILLIS); + } + + private void clearGetRoomInfoSchedule() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + if (handler.hasCallbacks(getRoomInfoRunnable)) { + handler.removeCallbacks(getRoomInfoRunnable); + } + } else { + handler.removeCallbacks(getRoomInfoRunnable); + } + } + + public void publishStream(String streamId) { + if (playOnlyMode) { + webRTCClient.publish(streamId, "", videoCallEnabled, audioCallEnabled, "", "", + streamId, roomId); + } else { + Log.i(getClass().getSimpleName(), "Play only mode. No publishing"); + } + } + + private void startPlaying(String[] streams) { + if (!playMessageSent) { + webRTCClient.play(roomId, "", streams, "", "", ""); + playMessageSent = true; + } + } + + public void setPlayOnly(boolean b) { + playOnlyMode = b; + } +} + diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/api/DefaultDataChannelObserver.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/api/DefaultDataChannelObserver.java new file mode 100644 index 00000000..aa70d311 --- /dev/null +++ b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/api/DefaultDataChannelObserver.java @@ -0,0 +1,52 @@ +package io.antmedia.webrtcandroidframework.api; + +import android.util.Log; + +import androidx.annotation.NonNull; + +import org.webrtc.DataChannel; + +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; + +public class DefaultDataChannelObserver implements IDataChannelObserver { + @Override + public void onBufferedAmountChange(long previousAmount, String dataChannelLabel) { + String logText = "Data channel buffered amount changed: " + dataChannelLabel + ": " + previousAmount; + Log.d(DefaultDataChannelObserver.class.getName(), logText); + } + + @Override + public void onStateChange(DataChannel.State state, String dataChannelLabel) { + String logText = "Data channel state changed: " + dataChannelLabel + ": " + state; + Log.d(DefaultDataChannelObserver.class.getName(), logText); + } + + @Override + public void onMessage(DataChannel.Buffer buffer, String dataChannelLabel) { + String messageText = toTextMessage(buffer); + textMessageReceived(messageText); + } + + @NonNull + protected String toTextMessage(DataChannel.Buffer buffer) { + ByteBuffer data = buffer.data; + String messageText = new String(data.array(), StandardCharsets.UTF_8); + return messageText; + } + + @Override + public void onMessageSent(DataChannel.Buffer buffer, boolean successful) { + if (successful) { + String messageText = toTextMessage(buffer); + Log.i(DefaultDataChannelObserver.class.getSimpleName(), "Message is sent"); + } else { + Log.e(DefaultDataChannelObserver.class.getSimpleName(),"Could not send the text message"); + } + } + + public void textMessageReceived(String messageText) { + Log.i(DefaultDataChannelObserver.class.getSimpleName(), "Text message received: " + messageText); + } +} + 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 new file mode 100644 index 00000000..41c2af15 --- /dev/null +++ b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/api/DefaultWebRTCListener.java @@ -0,0 +1,200 @@ +package io.antmedia.webrtcandroidframework.api; + +import android.util.Log; + +import org.webrtc.SurfaceViewRenderer; +import org.webrtc.VideoTrack; + +import java.util.ArrayList; + +import de.tavendo.autobahn.WebSocket; +import io.antmedia.webrtcandroidframework.core.StreamInfo; +import io.antmedia.webrtcandroidframework.core.WebRTCClientConfig; + +public class DefaultWebRTCListener implements IWebRTCListener { + private WebRTCClientConfig config; + protected IWebRTCClient webRTCClient; + + public void setWebRTCClient(IWebRTCClient webRTCClient) { + this.webRTCClient = webRTCClient; + } + + @Override + public void onDisconnected() { + String messageText = "Disconnected"; + callbackCalled(messageText); + } + + @Override + public void onPublishFinished(String streamId) { + String messageText = "Publish finished for " + streamId; + callbackCalled(messageText); + } + + @Override + public void onPlayFinished(String streamId) { + String messageText = "Play finished for " + streamId; + callbackCalled(messageText); + } + + @Override + public void onPublishStarted(String streamId) { + String messageText = "Publish started for " + streamId; + callbackCalled(messageText); + } + + @Override + public void onPlayStarted(String streamId) { + String messageText = "Play started for " + streamId; + callbackCalled(messageText); + + } + + @Override + public void noStreamExistsToPlay(String streamId) { + String messageText = "No stream exists to play for " + streamId; + callbackCalled(messageText); + } + + @Override + public void onError(String description, String streamId) { + String messageText = "Error for " + streamId + " : " + description; + 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) { + String messageText = "Stream id is already in use " + streamId; + callbackCalled(messageText); + } + + @Override + public void onIceConnected(String streamId) { + String messageText = "Ice connected for " + streamId; + callbackCalled(messageText); + } + + @Override + public void onIceDisconnected(String streamId) { + String messageText = "Ice disconnected for " + streamId; + callbackCalled(messageText); + } + + @Override + public void onTrackList(String[] tracks) { + String messageText = "Track list received"; + callbackCalled(messageText); + } + + @Override + public void onBitrateMeasurement(String streamId, int targetBitrate, int videoBitrate, int audioBitrate) { + String messageText = "Bitrate measurement received"; + callbackCalled(messageText); + } + + @Override + public void onStreamInfoList(String streamId, ArrayList streamInfoList) { + String messageText = "Stream info list received"; + callbackCalled(messageText); + } + + @Override + public void onNewVideoTrack(VideoTrack track) { + String messageText = "New video track received"; + callbackCalled(messageText); + + for (SurfaceViewRenderer r : config.remoteVideoRenderers) { + if (r.getTag() == null) { + r.setTag(track); + webRTCClient.setRendererForVideoTrack(r, track); + break; + } + } + } + + @Override + public void onVideoTrackEnded(VideoTrack track) { + String messageText = "Video track ended"; + callbackCalled(messageText); + for (SurfaceViewRenderer r : config.remoteVideoRenderers) { + if (r.getTag() == track) { + r.setTag(null); + return; + } + } + } + + @Override + public void onReconnectionAttempt(String streamId) { + String messageText = "Reconnection attempt for " + streamId; + callbackCalled(messageText); + } + + @Override + public void onJoinedTheRoom(String streamId, String[] streams) { + String messageText = "Joined the room for " + streamId; + callbackCalled(messageText); + } + + + @Override + public void onRoomInformation(String[] streams) { + String messageText = "Room information received"; + callbackCalled(messageText); + } + + @Override + public void onLeftTheRoom(String roomId) { + String messageText = "Left the room for " + roomId; + callbackCalled(messageText); + } + + @Override + public void onMutedFor(String streamId) { + String messageText = "Microphone is muted for " + streamId; + callbackCalled(messageText); + + } + @Override + public void onUnmutedFor(String streamId) { + String messageText = "Microphone is unmuted for " + streamId; + callbackCalled(messageText); + } + + @Override + public void onCameraTurnOnFor(String streamId) { + String messageText = "Camera is turned on for " + streamId; + callbackCalled(messageText); + + } + + @Override + public void onCameraTurnOffFor(String streamId) { + String messageText = "Camera is turned off for " + streamId; + callbackCalled(messageText); + } + + @Override + public void onSatatusUpdateFor(String streamId, boolean micStatus, boolean cameraStatus) { + String messageText = "Status update for " + streamId + " mic: " + micStatus + " camera: " + cameraStatus; + callbackCalled(messageText); + } + + @Override + public void setConfig(WebRTCClientConfig webRTCClientConfig) { + this.config = webRTCClientConfig; + } + + private void callbackCalled(String messageText) { + Log.d(DefaultWebRTCListener.class.getName(), messageText); + } + + +} + diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/IDataChannelObserver.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/api/IDataChannelObserver.java similarity index 91% rename from webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/IDataChannelObserver.java rename to webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/api/IDataChannelObserver.java index 1afacdcd..121192fa 100644 --- a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/IDataChannelObserver.java +++ b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/api/IDataChannelObserver.java @@ -1,4 +1,4 @@ -package io.antmedia.webrtcandroidframework; +package io.antmedia.webrtcandroidframework.api; import org.webrtc.DataChannel; 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 new file mode 100644 index 00000000..240577a4 --- /dev/null +++ b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/api/IWebRTCClient.java @@ -0,0 +1,201 @@ +package io.antmedia.webrtcandroidframework.api; + +import org.webrtc.DataChannel; +import org.webrtc.SurfaceViewRenderer; +import org.webrtc.VideoCapturer; +import org.webrtc.VideoTrack; +import org.webrtc.audio.CustomWebRtcAudioRecord; + +import io.antmedia.webrtcandroidframework.core.WebRTCClientBuilder; +import io.antmedia.webrtcandroidframework.core.WebRTCClientConfig; + +/** + * Created by karinca on 20.10.2017. + */ + +public interface IWebRTCClient { + + + enum StreamSource + { + SCREEN, + FRONT_CAMERA, + REAR_CAMERA, + CUSTOM + } + + + /** + * Switches the cameras + */ + void switchCamera(); + + + /** + * toggle microphone + * @return + */ + boolean toggleMic(); + + /** + * Stops the video source + */ + void stopVideoSource(); + + /** + * Starts or restarts the video source + */ + void startVideoSource(); + + /** + * Swapeed the fullscreen renderer and pip renderer + * @param b + */ + void setSwappedFeeds(boolean b); + + /** + * Get the error + * @return error or null if not + */ + String getError(); + + /** + * Return if data channel is enabled and open + * @return true if data channel is available + * false if it's not opened either by mobile or server side + */ + boolean isDataChannelEnabled(); + + /** + * This is used to get stream info list + */ + void getStreamInfoList(String streamId); + + /** + * This is used to play the specified resolution + * @param height + */ + void forceStreamQuality(String streamId, int height); + + //FIXME: add comment + void onCameraSwitch(); + void onCaptureFormatChange(int width, int height, int framerate); + boolean onToggleMic(); + + static WebRTCClientBuilder builder() { + return new WebRTCClientBuilder(); + } + + /** + * This is used to strart a WebRTC publish stream + * @param streamId: any name + */ + void publish(String streamId); + + + /** + * This is used to strart a WebRTC publish stream + * @param streamId: any name + * @param token: token for stream + * TODO: add comment + */ + void publish(String streamId, String token, boolean videoCallEnabled, boolean audioCallEnabled, + String subscriberId, String subscriberCode, String streamName, String mainTrackId); + + + /** + * This is used to play a WebRTC stream + * @param streamId + */ + void play(String streamId); + + /** + * This is used to play a multitrack WebRTC stream + * @param streamId + */ + void play(String streamId, String[] tracks); + + /** + * This is used to play a WebRTC stream with all parameters + * @param streamId + */ + void play(String streamId, String token, String[] tracks, String subscriberId, String subscriberCode, String viewerInfo); + + + + /** + * This is used to get streaming status for a stream id + * @param streamId + * @return + */ + boolean isStreaming(String streamId); + + /** + * This is used to join a conference room + * @param roomId + * @param streamId + * @return + */ + void joinToConferenceRoom(String roomId, String streamId); + + /** + * This is used to leave from a conference room + * @param roomId + */ + void leaveFromConference(String roomId); + + /** + * This is used to send data via data channel + * @param streamId + * @param buffer + */ + void sendMessageViaDataChannel(String streamId, DataChannel.Buffer buffer); + + /** + * This is used to stop a stream + * @param streamId + */ + void stop(String streamId); + + /** + * This is used to join a peer to peer stream + * @param streamId + */ + void join(String streamId); + + /** + * This is used to get reconnecting status + */ + boolean isReconnectionInProgress(); + + /** + * This is used to get room info + * @param roomId + * @param streamId + */ + void getRoomInfo(String roomId, String streamId); + + /** + * This is used to change video source on the fly + * @param newSource + */ + void changeVideoSource(StreamSource newSource); + + + WebRTCClientConfig getConfig(); + + VideoCapturer getVideoCapturer(); + + CustomWebRtcAudioRecord getAudioInput(); + + void setVideoEnabled(boolean b); + + void setAudioEnabled(boolean b); + + void enableTrack(String streamId, String selecetedTrack, boolean enabled); + + void getTrackList(String streamId, String token); + + void setRendererForVideoTrack(SurfaceViewRenderer renderer, VideoTrack videoTrack); + +} diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/IWebRTCListener.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/api/IWebRTCListener.java similarity index 88% rename from webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/IWebRTCListener.java rename to webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/api/IWebRTCListener.java index 40b7be51..a626b459 100644 --- a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/IWebRTCListener.java +++ b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/api/IWebRTCListener.java @@ -1,19 +1,23 @@ -package io.antmedia.webrtcandroidframework; +package io.antmedia.webrtcandroidframework.api; import org.webrtc.VideoTrack; import java.util.ArrayList; -import de.tavendo.autobahn.WebSocket; /** +import de.tavendo.autobahn.WebSocket; +import io.antmedia.webrtcandroidframework.core.StreamInfo; +import io.antmedia.webrtcandroidframework.core.WebRTCClientConfig; + +/** * Created by karinca on 23.10.2017. */ public interface IWebRTCListener { - /** + /** * It's called when websocket connection has been disconnected */ - void onDisconnected(String streamId); + void onDisconnected(); /** * This method is fired when publishing(broadcasting) to the server has been finished @@ -86,7 +90,9 @@ public interface IWebRTCListener { /** * It's called when a new video track is added. + * * @param track + * @return */ void onNewVideoTrack(VideoTrack track); @@ -159,13 +165,13 @@ public interface IWebRTCListener { */ void onSatatusUpdateFor(String streamId, boolean micStatus, boolean cameraStatus); - /** - * Permissions are checked when needed - * @param isForPublish if it's for publish, it checks camera and mic permissions - * @param permissionCallback callback to be called when permissions are granted or denied - * - * @return + /* + * It's called in WebRTCClient constructor when config is set */ - boolean checkAndRequestPermisssions(boolean isForPublish, PermissionCallback permissionCallback); + void setConfig(WebRTCClientConfig webRTCClientConfig); + /* + * It's called in WebRTCClient constructor to set + */ + void setWebRTCClient(IWebRTCClient webRTCClient); } diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/apprtc/CallActivity.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/apprtc/CallActivity.java deleted file mode 100644 index 5207b53b..00000000 --- a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/apprtc/CallActivity.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright 2015 The WebRTC Project Authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -package io.antmedia.webrtcandroidframework.apprtc; - -import android.app.Activity; -import android.app.AlertDialog; -import android.app.FragmentTransaction; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.media.projection.MediaProjection; -import android.media.projection.MediaProjectionManager; -import android.net.Uri; -import android.os.Bundle; -import android.os.Handler; -import android.util.DisplayMetrics; -import android.util.Log; -import android.view.View; -import android.view.Window; -import android.view.WindowManager; -import android.view.WindowManager.LayoutParams; -import android.widget.Toast; - -import androidx.annotation.Nullable; - -import org.webrtc.Camera1Enumerator; -import org.webrtc.Camera2Enumerator; -import org.webrtc.CameraEnumerator; -import org.webrtc.EglBase; -import org.webrtc.FileVideoCapturer; -import org.webrtc.IceCandidate; -import org.webrtc.Logging; -import org.webrtc.MediaStream; -import org.webrtc.PeerConnectionFactory; -import org.webrtc.RTCStatsReport; -import org.webrtc.RendererCommon.ScalingType; -import org.webrtc.RtpReceiver; -import org.webrtc.ScreenCapturerAndroid; -import org.webrtc.SessionDescription; -import org.webrtc.SurfaceViewRenderer; -import org.webrtc.VideoCapturer; -import org.webrtc.VideoFileRenderer; -import org.webrtc.VideoFrame; -import org.webrtc.VideoSink; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; - -import io.antmedia.webrtcandroidframework.R; - -/** - * Class which contains configuration names - * @deprecated - * Instead of passing configurations with Intent, - * set them directly accessors in @WebRTCClient - */ - -@Deprecated -public class CallActivity { - private static final String TAG = "CallRTCClient"; - - public static final String EXTRA_ROOMID = "org.appspot.apprtc.ROOMID"; - public static final String EXTRA_URLPARAMETERS = "org.appspot.apprtc.URLPARAMETERS"; - public static final String EXTRA_LOOPBACK = "org.appspot.apprtc.LOOPBACK"; - public static final String EXTRA_VIDEO_CALL = "org.appspot.apprtc.VIDEO_CALL"; - public static final String EXTRA_SCREENCAPTURE = "org.appspot.apprtc.SCREENCAPTURE"; - public static final String EXTRA_CAMERA2 = "org.appspot.apprtc.CAMERA2"; - public static final String EXTRA_VIDEO_WIDTH = "org.appspot.apprtc.VIDEO_WIDTH"; - public static final String EXTRA_VIDEO_HEIGHT = "org.appspot.apprtc.VIDEO_HEIGHT"; - public static final String EXTRA_VIDEO_FPS = "org.appspot.apprtc.VIDEO_FPS"; - public static final String EXTRA_VIDEO_CAPTUREQUALITYSLIDER_ENABLED = - "org.appsopt.apprtc.VIDEO_CAPTUREQUALITYSLIDER"; - public static final String EXTRA_VIDEO_BITRATE = "org.appspot.apprtc.VIDEO_BITRATE"; - public static final String EXTRA_VIDEOCODEC = "org.appspot.apprtc.VIDEOCODEC"; - public static final String EXTRA_HWCODEC_ENABLED = "org.appspot.apprtc.HWCODEC"; - public static final String EXTRA_CAPTURETOTEXTURE_ENABLED = "org.appspot.apprtc.CAPTURETOTEXTURE"; - public static final String EXTRA_FLEXFEC_ENABLED = "org.appspot.apprtc.FLEXFEC"; - public static final String EXTRA_AUDIO_BITRATE = "org.appspot.apprtc.AUDIO_BITRATE"; - public static final String EXTRA_AUDIOCODEC = "org.appspot.apprtc.AUDIOCODEC"; - public static final String EXTRA_NOAUDIOPROCESSING_ENABLED = - "org.appspot.apprtc.NOAUDIOPROCESSING"; - public static final String EXTRA_AECDUMP_ENABLED = "org.appspot.apprtc.AECDUMP"; - public static final String EXTRA_SAVE_INPUT_AUDIO_TO_FILE_ENABLED = - "org.appspot.apprtc.SAVE_INPUT_AUDIO_TO_FILE"; - public static final String EXTRA_OPENSLES_ENABLED = "org.appspot.apprtc.OPENSLES"; - public static final String EXTRA_DISABLE_BUILT_IN_AEC = "org.appspot.apprtc.DISABLE_BUILT_IN_AEC"; - public static final String EXTRA_DISABLE_BUILT_IN_AGC = "org.appspot.apprtc.DISABLE_BUILT_IN_AGC"; - public static final String EXTRA_DISABLE_BUILT_IN_NS = "org.appspot.apprtc.DISABLE_BUILT_IN_NS"; - public static final String EXTRA_DISABLE_WEBRTC_AGC_AND_HPF = - "org.appspot.apprtc.DISABLE_WEBRTC_GAIN_CONTROL"; - public static final String EXTRA_DISPLAY_HUD = "org.appspot.apprtc.DISPLAY_HUD"; - public static final String EXTRA_TRACING = "org.appspot.apprtc.TRACING"; - public static final String EXTRA_CMDLINE = "org.appspot.apprtc.CMDLINE"; - public static final String EXTRA_RUNTIME = "org.appspot.apprtc.RUNTIME"; - public static final String EXTRA_VIDEO_FILE_AS_CAMERA = "org.appspot.apprtc.VIDEO_FILE_AS_CAMERA"; - public static final String EXTRA_SAVE_REMOTE_VIDEO_TO_FILE = - "org.appspot.apprtc.SAVE_REMOTE_VIDEO_TO_FILE"; - public static final String EXTRA_SAVE_REMOTE_VIDEO_TO_FILE_WIDTH = - "org.appspot.apprtc.SAVE_REMOTE_VIDEO_TO_FILE_WIDTH"; - public static final String EXTRA_SAVE_REMOTE_VIDEO_TO_FILE_HEIGHT = - "org.appspot.apprtc.SAVE_REMOTE_VIDEO_TO_FILE_HEIGHT"; - public static final String EXTRA_USE_VALUES_FROM_INTENT = - "org.appspot.apprtc.USE_VALUES_FROM_INTENT"; - public static final String EXTRA_DATA_CHANNEL_ENABLED = "org.appspot.apprtc.DATA_CHANNEL_ENABLED"; - public static final String EXTRA_ORDERED = "org.appspot.apprtc.ORDERED"; - public static final String EXTRA_MAX_RETRANSMITS_MS = "org.appspot.apprtc.MAX_RETRANSMITS_MS"; - public static final String EXTRA_MAX_RETRANSMITS = "org.appspot.apprtc.MAX_RETRANSMITS"; - public static final String EXTRA_PROTOCOL = "org.appspot.apprtc.PROTOCOL"; - public static final String EXTRA_NEGOTIATED = "org.appspot.apprtc.NEGOTIATED"; - public static final String EXTRA_ID = "org.appspot.apprtc.ID"; - public static final String EXTRA_ENABLE_RTCEVENTLOG = "org.appspot.apprtc.ENABLE_RTCEVENTLOG"; - - public static final int CAPTURE_PERMISSION_REQUEST_CODE = 1234; - - // Peer connection statistics callback period in ms. - public static final int STAT_CALLBACK_PERIOD = 1000; - - -} diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/CustomVideoCapturer.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/CustomVideoCapturer.java similarity index 96% rename from webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/CustomVideoCapturer.java rename to webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/CustomVideoCapturer.java index 83d40a2c..ca1982c0 100644 --- a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/CustomVideoCapturer.java +++ b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/CustomVideoCapturer.java @@ -1,4 +1,4 @@ -package io.antmedia.webrtcandroidframework; +package io.antmedia.webrtcandroidframework.core; import android.content.Context; import android.os.SystemClock; @@ -29,9 +29,6 @@ public class CustomVideoCapturer implements VideoCapturer { public SurfaceTextureHelper surfaceTextureHelper; - public CustomVideoCapturer() { - - } public void writeFrame(VideoFrame videoFrame) { capturerObserver.onFrameCaptured(videoFrame); diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/MediaFileReader.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/MediaFileReader.java new file mode 100644 index 00000000..c05035b3 --- /dev/null +++ b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/MediaFileReader.java @@ -0,0 +1,265 @@ +package io.antmedia.webrtcandroidframework.core; + +import static org.webrtc.audio.WebRtcAudioRecord.BUFFERS_PER_SECOND; + +import android.content.res.Resources; +import android.media.Image; +import android.media.MediaCodec; +import android.media.MediaExtractor; +import android.media.MediaFormat; +import android.os.Build; +import android.util.Log; + +import androidx.annotation.Nullable; +import androidx.annotation.RequiresApi; + +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.concurrent.atomic.AtomicBoolean; + +/* + * Created by Ant Media on 03.12.2023 + * + * This file reads mp3 or mp4 files to extract video audio data + * + */ +public class MediaFileReader { + private static final int DESIRED_SAMPLE_RATE = 48000; + + + private AtomicBoolean stopRequested = new AtomicBoolean(false); + private MediaFormat format; + + public interface VideoFrameListener { + void onYuvImage(Image yuvImage); + } + + public interface AudioFrameListener { + void onAudioData(byte[] resampledData); + } + + public enum FrameType { + video, + audio + } + private VideoFrameListener videoFrameListener; + + private AudioFrameListener audioFrameListener; + private FrameType frameType; + + private final MediaExtractor extractor; + private String TAG = MediaFileReader.class.getSimpleName(); + + private MediaFileReader(MediaExtractor extractor) { + this.extractor = extractor; + } + + @RequiresApi(api = Build.VERSION_CODES.N) + public static MediaFileReader fromResources(Resources resources, int resourceId) { + MediaExtractor extractor = new MediaExtractor(); + try { + extractor.setDataSource(resources.openRawResourceFd(resourceId)); + } catch (IOException e) { + e.printStackTrace(); + } + + return new MediaFileReader(extractor); + } + + public static MediaFileReader fromPath(String filePath) { + MediaExtractor extractor = new MediaExtractor(); + try { + FileInputStream inputStream = new FileInputStream(filePath); + extractor.setDataSource(inputStream.getFD()); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new MediaFileReader(extractor); + } + + public MediaFileReader withFrameType(FrameType frameType) { + this.frameType = frameType; + return this; + } + + public MediaFileReader withVideoFrameListener(VideoFrameListener videoFrameListener) { + this.videoFrameListener = videoFrameListener; + return this; + } + + public MediaFileReader withAudioFrameListener(AudioFrameListener audioFrameListener) { + this.audioFrameListener = audioFrameListener; + return this; + } + + public void start() { + format = getMediaFormat(); + Thread t = new Thread() { + @RequiresApi(api = Build.VERSION_CODES.N) + @Override + public void run() { + decodeFrames(); + } + }; + t.start(); + } + + @Nullable + private MediaFormat getMediaFormat() { + MediaFormat format = null; + int trackCount = extractor.getTrackCount(); + for (int i = 0; i < trackCount; i++) { + format = extractor.getTrackFormat(i); + String mime = format.getString(MediaFormat.KEY_MIME); + if (mime != null && mime.startsWith(frameType.toString()+"/")) { + extractor.selectTrack(i); + break; + } + } + + return format; + } + + + private void decodeFrames() { + try { + MediaCodec decoder = MediaCodec.createDecoderByType(format.getString(MediaFormat.KEY_MIME)); + decoder.configure(format, null, null, 0); + decoder.start(); + + MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo(); + boolean isEOS = false; + + ByteBuffer rawAudioBuffer = ByteBuffer.allocate(get10MsBufferLength() *20); + + + while (!Thread.interrupted() && !isEOS && !stopRequested.get()) { + int inputIndex = decoder.dequeueInputBuffer(10000); + System.out.println("inputIndex: " + inputIndex); + if (inputIndex >= 0) { + ByteBuffer inputBuffer = decoder.getInputBuffer(inputIndex); + int sampleSize = extractor.readSampleData(inputBuffer, 0); + if (sampleSize < 0) { + isEOS = true; + decoder.queueInputBuffer(inputIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM); + } else { + decoder.queueInputBuffer(inputIndex, 0, sampleSize, extractor.getSampleTime(), 0); + extractor.advance(); + } + } + + int outputIndex = decoder.dequeueOutputBuffer(bufferInfo, 10000); + System.out.println("outputIndex: " + outputIndex); + + if (outputIndex >= 0) { + if(frameType == FrameType.video) { + Image yuvImage = decoder.getOutputImage(outputIndex); + videoFrameListener.onYuvImage(yuvImage); + yuvImage.close(); + } + else if(frameType == FrameType.audio){ + ByteBuffer outputBuffer = decoder.getOutputBuffer(outputIndex); + rawAudioBuffer.put(outputBuffer); + processAudio(rawAudioBuffer); + } + decoder.releaseOutputBuffer(outputIndex, false); + } else if (outputIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { + // Handle format change if needed + } + + Thread.sleep(sleepTime(frameType)); + } + + decoder.stop(); + decoder.release(); + extractor.release(); + stopRequested.set(false); + } catch (IOException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + private void processAudio(ByteBuffer rawAudioBuffer) { + + int bufferLength = get10MsBufferLength(); + + + int length = rawAudioBuffer.position(); + + rawAudioBuffer.position(0); + int channelCount = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT); + int readBufferLength = bufferLength * format.getInteger(MediaFormat.KEY_SAMPLE_RATE) / DESIRED_SAMPLE_RATE*channelCount; + Log.d(TAG, "pushAudio: length: " + length + " bufferLength: " + bufferLength); + while(length - rawAudioBuffer.position() >= readBufferLength) { + byte[] pcmData = new byte[readBufferLength]; + rawAudioBuffer.get(pcmData); + byte[] resampledData = modifySampleRate(pcmData, format.getInteger(MediaFormat.KEY_SAMPLE_RATE), DESIRED_SAMPLE_RATE, channelCount); + + Log.d(TAG, "length: " + length+ " position: " + rawAudioBuffer.position()); + + audioFrameListener.onAudioData(resampledData); + + Log.i("Audio", "push audio: " + pcmData[0] + " : " + pcmData[1] + " : " + pcmData[2] + " : " + pcmData[3] + " : "); + //emulate real time streaming by waiting 10ms because we're reading from the file directly + //When you decode the audio from incoming RTSP stream, you don't need to sleep, just send it immediately when you get + } + + byte[] moreData = new byte[length - rawAudioBuffer.position()]; + rawAudioBuffer.get(moreData); + rawAudioBuffer.clear(); + rawAudioBuffer.put(moreData); + } + + private int get10MsBufferLength() { + int channels = 1; + int bytesPerSample = 2; //WebRtcAudioRecord.getBytesPerSample(WebRtcAudioRecord.DEFAULT_AUDIO_FORMAT) + + int bytesPerFrame = channels * bytesPerSample; + int framesPerBuffer = 44100 / BUFFERS_PER_SECOND; + int bufferLength = bytesPerFrame * framesPerBuffer; + return bufferLength; + } + + private byte[] modifySampleRate(byte[] pcmData, int inputSampleRate, int outputSampleRate, int channelCount) { + int inputLength = pcmData.length / 2; // Dividing by 2 assuming 16-bit PCM data + int outputLength = (int) ((inputLength / (float) inputSampleRate) * outputSampleRate)/channelCount; + short[] inputSamples = new short[inputLength]; + short[] outputSamples = new short[outputLength]; + + // Convert byte array to short array + ByteBuffer.wrap(pcmData).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(inputSamples); + + // Perform linear interpolation and stereo to mono conversion + float ratio = (float) inputSampleRate / outputSampleRate; + for (int i = 0; i < outputLength; i++) { + float index = i * ratio; + int leftSampleIndex = (int) index; + int rightSampleIndex = Math.min(leftSampleIndex + 1, inputLength - channelCount); + float fraction = index - leftSampleIndex; + + short leftSample = inputSamples[leftSampleIndex * channelCount]; + short rightSample = inputSamples[Math.min(rightSampleIndex * channelCount, inputLength - 1)]; + + // Linear interpolation and stereo to mono conversion + outputSamples[i] = (short) (((1 - fraction) * leftSample + fraction * rightSample) / 2); + } + + // Convert short array to byte array + byte[] outputData = new byte[outputLength * 2]; // Multiplying by 2 assuming 16-bit PCM data + ByteBuffer.wrap(outputData).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().put(outputSamples); + + return outputData; + } + + private long sleepTime(FrameType frameType) { + return frameType == FrameType.video ? 50 : 10; + } + + public void stop() { + stopRequested.set(true); + } +} diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/PermissionsHandler.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/PermissionsHandler.java new file mode 100644 index 00000000..6dfc291c --- /dev/null +++ b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/PermissionsHandler.java @@ -0,0 +1,86 @@ +package io.antmedia.webrtcandroidframework.core; + +import android.Manifest; +import android.app.Activity; +import android.content.Context; +import android.content.pm.PackageManager; +import android.os.Build; +import android.util.Log; +import android.widget.Toast; + +import androidx.core.app.ActivityCompat; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class PermissionsHandler { + public interface PermissionCallback { + void onPermissionResult(); + } + private final Activity activity; + private PermissionCallback permissionCallback; + + public static final String[] REQUIRED_EXTENDED_PERMISSIONS = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S ? + new String[] {Manifest.permission.RECORD_AUDIO, Manifest.permission.CAMERA, Manifest.permission.BLUETOOTH_CONNECT} + : + new String[] {Manifest.permission.RECORD_AUDIO, Manifest.permission.CAMERA}; + + // List of mandatory application permissions. + public static final String[] REQUIRED_MINIMUM_PERMISSIONS = {"android.permission.MODIFY_AUDIO_SETTINGS", + "android.permission.INTERNET"}; + + public PermissionsHandler(Activity activity) { + this.activity = activity; + } + + public boolean checkAndRequestPermisssions(boolean isExtended, PermissionCallback permissionCallback) { + ArrayList permissions = new ArrayList<>(); + permissions.addAll(Arrays.asList(REQUIRED_MINIMUM_PERMISSIONS)); + if(isExtended) { + permissions.addAll(Arrays.asList(REQUIRED_EXTENDED_PERMISSIONS)); + } + + if (hasPermissions(activity.getApplicationContext(), permissions)) { + return true; + } + else { + this.permissionCallback = permissionCallback; + showPermissionsErrorAndRequest(permissions); + return false; + } + } + + public boolean hasPermissions(Context context, List permissions) { + if (context != null && permissions != null) { + for (String permission : permissions) { + if (ActivityCompat.checkSelfPermission(context, permission) + != PackageManager.PERMISSION_GRANTED) { + Log.w(PermissionsHandler.class.getSimpleName(), "Permission required:"+permission); + return false; + } + } + } + return true; + } + public void showPermissionsErrorAndRequest(List permissions) { + makeToast("You need permissions before", Toast.LENGTH_SHORT); + String[] permissionArray = new String[permissions.size()]; + permissions.toArray(permissionArray); + ActivityCompat.requestPermissions(activity, permissionArray, 1); + } + + public void onRequestPermissionsResult( + int requestCode, + String[] permissions, + int[] grantResults + ) { + permissionCallback.onPermissionResult(); + } + + public void makeToast(String messageText, int lengthLong) { + activity.runOnUiThread(() -> Toast.makeText(activity, messageText, lengthLong).show()); + } + +} + diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/ProxyVideoSink.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/ProxyVideoSink.java similarity index 92% rename from webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/ProxyVideoSink.java rename to webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/ProxyVideoSink.java index 5a8897eb..a2faa197 100644 --- a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/ProxyVideoSink.java +++ b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/ProxyVideoSink.java @@ -1,4 +1,4 @@ -package io.antmedia.webrtcandroidframework; +package io.antmedia.webrtcandroidframework.core; import org.webrtc.Logging; import org.webrtc.VideoFrame; diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/ScreenCapturer.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/ScreenCapturer.java new file mode 100644 index 00000000..3fe652cd --- /dev/null +++ b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/ScreenCapturer.java @@ -0,0 +1,242 @@ +/* + * Copyright 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +package io.antmedia.webrtcandroidframework.core; + +import android.content.Context; +import android.hardware.display.DisplayManager; +import android.hardware.display.VirtualDisplay; +import android.media.projection.MediaProjection; +import android.util.Log; +import android.view.Surface; + +import androidx.annotation.Nullable; + +import org.webrtc.CapturerObserver; +import org.webrtc.SurfaceTextureHelper; +import org.webrtc.ThreadUtils; +import org.webrtc.VideoCapturer; +import org.webrtc.VideoFrame; +import org.webrtc.VideoSink; + +public class ScreenCapturer implements VideoCapturer, VideoSink { + private static final int DISPLAY_FLAGS = + DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC | DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION; + // DPI for VirtualDisplay, does not seem to matter for us. + public static final int VIRTUAL_DISPLAY_DPI = 400; + private int width; + private int height; + @Nullable private VirtualDisplay virtualDisplay; + @Nullable private SurfaceTextureHelper surfaceTextureHelper; + @Nullable private CapturerObserver capturerObserver; + private long numCapturedFrames; + @Nullable private MediaProjection mediaProjection; + private boolean isDisposed; + public int deviceRotation = 0; + private static final String TAG = ScreenCapturer.class.getSimpleName(); + private MediaProjection.Callback mediaProjectionCallback; + + public ScreenCapturer(MediaProjection mediaProjection, MediaProjection.Callback callback) { + this.mediaProjection = mediaProjection; + this.mediaProjectionCallback = callback; + } + + public boolean isDisposed() { + return isDisposed; + } + + private void checkNotDisposed() { + if (isDisposed) { + throw new RuntimeException("capturer is disposed."); + } + } + + @Nullable + public MediaProjection getMediaProjection() { + return mediaProjection; + } + + @Override + // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression. + @SuppressWarnings("NoSynchronizedMethodCheck") + public synchronized void initialize(final SurfaceTextureHelper surfaceTextureHelper, + final Context applicationContext, final CapturerObserver capturerObserver) { + checkNotDisposed(); + + if (capturerObserver == null) { + throw new RuntimeException("capturerObserver not set."); + } + this.capturerObserver = capturerObserver; + + if (surfaceTextureHelper == null) { + throw new RuntimeException("surfaceTextureHelper not set."); + } + this.surfaceTextureHelper = surfaceTextureHelper; + } + + public void setMediaProjection(@Nullable MediaProjection mediaProjection) { + this.mediaProjection = mediaProjection; + } + + @Override + // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression. + @SuppressWarnings("NoSynchronizedMethodCheck") + public synchronized void startCapture( + final int width, final int height, final int ignoredFramerate) { + checkNotDisposed(); + + this.width = width; + this.height = height; + + + // Let MediaProjection callback use the SurfaceTextureHelper thread. + mediaProjection.registerCallback(mediaProjectionCallback, surfaceTextureHelper.getHandler()); + + createVirtualDisplay(); + capturerObserver.onCapturerStarted(true); + surfaceTextureHelper.startListening(ScreenCapturer.this); + } + + @Override + // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression. + @SuppressWarnings("NoSynchronizedMethodCheck") + public synchronized void stopCapture() { + checkNotDisposed(); + ThreadUtils.invokeAtFrontUninterruptibly(surfaceTextureHelper.getHandler(), new Runnable() { + @Override + public void run() { + surfaceTextureHelper.stopListening(); + capturerObserver.onCapturerStopped(); + + if (virtualDisplay != null) { + virtualDisplay.release(); + virtualDisplay = null; + } + + if (mediaProjection != null) { + // Unregister the callback before stopping, otherwise the callback recursively + // calls this method. + mediaProjection.unregisterCallback(mediaProjectionCallback); + mediaProjection.stop(); + mediaProjection = null; + } + } + }); + } + + @Override + // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression. + @SuppressWarnings("NoSynchronizedMethodCheck") + public synchronized void dispose() { + Log.i(TAG, "ScreenCapturer is disposed"); + isDisposed = true; + } + + /** + * Changes output video format. This method can be used to scale the output + * video, or to change orientation when the captured screen is rotated for example. + * + * @param width new output video width + * @param height new output video height + * @param ignoredFramerate ignored + */ + @Override + // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression. + @SuppressWarnings("NoSynchronizedMethodCheck") + public synchronized void changeCaptureFormat( + final int width, final int height, final int ignoredFramerate) { + checkNotDisposed(); + + this.width = width; + this.height = height; + + if (virtualDisplay == null) { + // Capturer is stopped, the virtual display will be created in startCaptuer(). + return; + } + + // Create a new virtual display on the surfaceTextureHelper thread to avoid interference + // with frame processing, which happens on the same thread (we serialize events by running + // them on the same thread). + ThreadUtils.invokeAtFrontUninterruptibly(surfaceTextureHelper.getHandler(), new Runnable() { + @Override + public void run() { + virtualDisplay.release(); + createVirtualDisplay(); + } + }); + } + + private void createVirtualDisplay() { + surfaceTextureHelper.setTextureSize(width, height); + virtualDisplay = mediaProjection.createVirtualDisplay("WebRTC_ScreenCapture", width, height, + VIRTUAL_DISPLAY_DPI, DISPLAY_FLAGS, new Surface(surfaceTextureHelper.getSurfaceTexture()), + null /* callback */, null /* callback handler */); + } + + public void rotateScreen(int rotation) { + if (deviceRotation != rotation) { + Log.w("Rotation", "onFrame: " + rotation); + deviceRotation = rotation; + + if (deviceRotation == 0) { + virtualDisplay.resize(width, height, VIRTUAL_DISPLAY_DPI); + surfaceTextureHelper.setTextureSize(width, height); + } else if (deviceRotation == 180) { + // 180 degree is not supported by MediaProjection + } else { + virtualDisplay.resize(height, width, VIRTUAL_DISPLAY_DPI); + surfaceTextureHelper.setTextureSize(height, width); + } + } + } + + // This is called on the internal looper thread of {@Code SurfaceTextureHelper}. + @Override + public void onFrame(VideoFrame frame) { + numCapturedFrames++; + Log.v(TAG, "Frame received " + numCapturedFrames); + capturerObserver.onFrameCaptured(frame); + } + + @Override + public boolean isScreencast() { + return true; + } + + public long getNumCapturedFrames() { + return numCapturedFrames; + } + + + public void setVirtualDisplay(VirtualDisplay virtualDisplay) { + this.virtualDisplay = virtualDisplay; + } + + public void setSurfaceTextureHelper(SurfaceTextureHelper surfaceTextureHelper) { + this.surfaceTextureHelper = surfaceTextureHelper; + } + + public void setCapturerObserver(CapturerObserver capturerObserver) { + this.capturerObserver = capturerObserver; + } + + public void setWidth(int width) { + this.width = width; + } + + public void setHeight(int height) { + this.height = height; + } + + public MediaProjection.Callback getMediaProjectionCallback() { + return mediaProjectionCallback; + } +} diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/StatsCollector.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/StatsCollector.java similarity index 83% rename from webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/StatsCollector.java rename to webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/StatsCollector.java index 97e8b619..baea635b 100644 --- a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/StatsCollector.java +++ b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/StatsCollector.java @@ -1,4 +1,4 @@ -package io.antmedia.webrtcandroidframework; +package io.antmedia.webrtcandroidframework.core; import android.util.Log; @@ -21,15 +21,9 @@ public class StatsCollector { private long audioBitrate; private long videoBitrate; - public void onStatsReport(RTCStatsReport report, String mode) { - //Log.i("Stats", "onStatsReport:\n"+report.toString()); - if (mode.equals(IWebRTCClient.MODE_PUBLISH)) { - onSenderReport(report); - } else if (mode.equals(IWebRTCClient.MODE_PLAY)) { - onReceiverReport(report); - } else if (mode.equals(IWebRTCClient.MODE_MULTI_TRACK_PLAY)) { - onMultitrackReceiverReport(report); - } + public void onStatsReport(RTCStatsReport report) { + Log.i("Stats", "onStatsReport:\n"+report.toString()); + } private void onMultitrackReceiverReport(RTCStatsReport report) { diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/StreamInfo.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/StreamInfo.java similarity index 94% rename from webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/StreamInfo.java rename to webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/StreamInfo.java index d2073aaf..28b2d395 100644 --- a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/StreamInfo.java +++ b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/StreamInfo.java @@ -1,4 +1,4 @@ -package io.antmedia.webrtcandroidframework; +package io.antmedia.webrtcandroidframework.core; public class StreamInfo { private int height; diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/WebRTCClient.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java similarity index 58% rename from webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/WebRTCClient.java rename to webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java index f8f71979..d8ecce7f 100644 --- a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/WebRTCClient.java +++ b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClient.java @@ -8,21 +8,14 @@ * be found in the AUTHORS file in the root of the source tree. */ -package io.antmedia.webrtcandroidframework; +package io.antmedia.webrtcandroidframework.core; -import android.annotation.TargetApi; import android.app.Activity; -import android.content.Context; -import android.content.Intent; import android.media.projection.MediaProjection; -import android.media.projection.MediaProjectionManager; import android.os.Build; -import android.os.Environment; import android.os.Handler; -import android.os.ParcelFileDescriptor; import android.util.DisplayMetrics; import android.util.Log; -import android.view.View; import android.view.WindowManager; import androidx.annotation.NonNull; @@ -38,7 +31,6 @@ import org.webrtc.DefaultVideoDecoderFactory; import org.webrtc.DefaultVideoEncoderFactory; import org.webrtc.EglBase; -import org.webrtc.FileVideoCapturer; import org.webrtc.IceCandidate; import org.webrtc.IceCandidateErrorEvent; import org.webrtc.Logging; @@ -48,13 +40,9 @@ import org.webrtc.PeerConnection; import org.webrtc.PeerConnectionFactory; import org.webrtc.RTCStatsReport; -import org.webrtc.RendererCommon; -import org.webrtc.RendererCommon.ScalingType; import org.webrtc.RtpParameters; import org.webrtc.RtpReceiver; import org.webrtc.RtpSender; -import org.webrtc.RtpTransceiver; -import org.webrtc.ScreenCapturerAndroid; import org.webrtc.SdpObserver; import org.webrtc.SessionDescription; import org.webrtc.SoftwareVideoDecoderFactory; @@ -64,132 +52,76 @@ import org.webrtc.VideoCapturer; import org.webrtc.VideoDecoderFactory; import org.webrtc.VideoEncoderFactory; -import org.webrtc.VideoFileRenderer; -import org.webrtc.VideoSink; import org.webrtc.VideoSource; import org.webrtc.VideoTrack; import org.webrtc.audio.AudioDeviceModule; import org.webrtc.audio.CustomWebRtcAudioRecord; import org.webrtc.audio.JavaAudioDeviceModule; -import java.io.File; -import java.io.IOException; import java.nio.ByteBuffer; -import java.text.DateFormat; -import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.Date; import java.util.Iterator; import java.util.List; -import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.regex.Matcher; import java.util.regex.Pattern; + import javax.annotation.Nullable; -import io.antmedia.webrtcandroidframework.apprtc.AppRTCAudioManager; -import io.antmedia.webrtcandroidframework.apprtc.CallActivity; -import io.antmedia.webrtcandroidframework.apprtc.RtcEventLog; +import io.antmedia.webrtcandroidframework.R; +import io.antmedia.webrtcandroidframework.api.IWebRTCClient; +import io.antmedia.webrtcandroidframework.apprtc.AppRTCAudioManager; +import io.antmedia.webrtcandroidframework.websocket.AntMediaSignallingEvents; +import io.antmedia.webrtcandroidframework.websocket.WebSocketHandler; /** * Activity for peer connection call setup, call waiting * and call view. */ -public class WebRTCClient implements IWebRTCClient, AntMediaSignallingEvents, IDataChannelMessageSender { +public class WebRTCClient implements IWebRTCClient, AntMediaSignallingEvents { private static final String TAG = "WebRTCClient"; - public static final String SOURCE_FILE = "FILE"; - public static final String SOURCE_SCREEN = "SCREEN"; - public static final String SOURCE_FRONT = "FRONT"; - public static final String SOURCE_REAR = "REAR"; - public static final String SOURCE_CUSTOM = "CUSTOM"; - public static final String ERROR_USER_REVOKED_CAPTURE_SCREEN_PERMISSION = "USER_REVOKED_CAPTURE_SCREEN_PERMISSION"; + public enum Mode { + PUBLISH, PLAY, P2P, MULTI_TRACK_PLAY + } 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 final int STAT_CALLBACK_PERIOD = 1000; + + private final ProxyVideoSink localVideoSink = new ProxyVideoSink(); + private final List remoteVideoSinks = new ArrayList<>(); - private final ProxyVideoSink remoteProxyRenderer = new ProxyVideoSink(); - private final ProxyVideoSink localProxyVideoSink = new ProxyVideoSink(); - //private final List remoteProxyRendererList = new ArrayList<>(); private final Handler mainHandler; @Nullable public AppRTCAudioManager audioManager = null; - @Nullable - private SurfaceViewRenderer pipRenderer; - @Nullable - private SurfaceViewRenderer fullscreenRenderer; - @Nullable - private VideoFileRenderer videoFileRenderer; - private List remoteSinks = new ArrayList<>(); private boolean isError; private final long callStartedTimeMs = 0; private boolean micEnabled = true; - private boolean screencaptureEnabled = false; - private static Intent mediaProjectionPermissionResultData; - private int mediaProjectionPermissionResultCode; + + private EglBase eglBase; - private final String saveRemoteVideoToFile = null; private String errorString = null; - private String streamMode; - private boolean openFrontCamera = true; private boolean streamStoppedByUser = false; - private boolean reconnectionInProgress = false; private boolean autoPlayTracks = false; - private boolean renderersInitiated = false; - private boolean checkStreamIdValidity = true; - - private boolean renderersProvidedAtStart = false; - //dynamic means created after stream started on the fly - private ConcurrentLinkedQueue dynamicRenderers = new ConcurrentLinkedQueue<>(); - private ConcurrentLinkedQueue dynamicRemoteSinks = new ConcurrentLinkedQueue<>(); - private boolean streamStarted = false; - private long TRACK_CHECK_PERIDOD_MS = 3000; private boolean waitingForPlay = false; - - private int inputSampleRate = 48000; - private boolean stereoInput = false; - private int audioInputFormat; - private boolean customAudioFeed; - - private boolean customCapturerEnabled = false; - private VideoCapturer videoCapturer; private VideoTrack localVideoTrack; - private Intent intent = new Intent(); private Handler handler = new Handler(); private WebSocketHandler wsHandler; - private final String stunServerUri = "stun:stun1.l.google.com:19302"; private final ArrayList iceServers = new ArrayList<>(); - private boolean videoOn = true; - private boolean audioOn = true; - private List remoteRendererList = null; - @Nullable - private IDataChannelObserver dataChannelObserver; - - private String initialStreamId; - private boolean dataChannelOnly = false; - private String subscriberId = ""; - private String subscriberCode = ""; - private String streamName = ""; - private String viewerInfo = ""; - private String currentSource; - private boolean screenPermissionNeeded = true; - - public MediaProjection mediaProjection; - public MediaProjectionManager mediaProjectionManager; - private String mainTrackId; - - private StatsCollector statsCollector = new StatsCollector(); + private PermissionsHandler permissionsHandler; + private final StatsCollector statsCollector = new StatsCollector(); public static final String VIDEO_TRACK_ID = "ARDAMSv0"; public static final String AUDIO_TRACK_ID = "ARDAMSa0"; @@ -200,7 +132,6 @@ public class WebRTCClient implements IWebRTCClient, AntMediaSignallingEvents, ID private static final String VIDEO_CODEC_H264_BASELINE = "H264 Baseline"; private static final String VIDEO_CODEC_H264_HIGH = "H264 High"; private static final String VIDEO_CODEC_AV1 = "AV1"; - private static final String AUDIO_CODEC_OPUS = "opus"; private static final String AUDIO_CODEC_ISAC = "ISAC"; private static final String VIDEO_CODEC_PARAM_START_BITRATE = "x-google-start-bitrate"; private static final String VIDEO_FLEXFEC_FIELDTRIAL = @@ -213,11 +144,7 @@ public class WebRTCClient implements IWebRTCClient, AntMediaSignallingEvents, ID private static final String AUDIO_AUTO_GAIN_CONTROL_CONSTRAINT = "googAutoGainControl"; private static final String AUDIO_HIGH_PASS_FILTER_CONSTRAINT = "googHighpassFilter"; private static final String AUDIO_NOISE_SUPPRESSION_CONSTRAINT = "googNoiseSuppression"; - private static final String DTLS_SRTP_KEY_AGREEMENT_CONSTRAINT = "DtlsSrtpKeyAgreement"; - private static final int HD_VIDEO_WIDTH = 1280; - private static final int HD_VIDEO_HEIGHT = 720; private static final int BPS_IN_KBPS = 1000; - private static final String RTCEVENTLOG_OUTPUT_DIR_NAME = "rtc_event_log"; // Executor thread is started once in private ctor and is used for all // peer connection API calls to ensure new peer connection factory is @@ -227,14 +154,11 @@ public class WebRTCClient implements IWebRTCClient, AntMediaSignallingEvents, ID @androidx.annotation.Nullable private PeerConnectionFactory factory; - - public void setStreamId(String streamId) { - this.initialStreamId = streamId; - } + private boolean requestExtendedRights = false; public static class PeerInfo { - public PeerInfo(String id, String mode) { + public PeerInfo(String id, Mode mode) { this.id = id; this.mode = mode; } @@ -242,7 +166,7 @@ public PeerInfo(String id, String mode) { public String id; public PeerConnection peerConnection; public DataChannel dataChannel; - public String mode; + public Mode mode; public String token; public boolean videoCallEnabled; public boolean audioCallEnabled; @@ -278,61 +202,24 @@ public PeerInfo(String id, String mode) { // enableVideo is set to true if video should be rendered and sent. private boolean renderVideo = true; @androidx.annotation.Nullable - private VideoTrack remoteVideoTrack; - @androidx.annotation.Nullable private RtpSender localVideoSender; - // enableAudio is set to true if audio should be sent. - private boolean enableAudio = true; - @androidx.annotation.Nullable - private AudioTrack localAudioTrack; - // Enable RtcEventLog. @androidx.annotation.Nullable - private RtcEventLog rtcEventLog; + private AudioTrack localAudioTrack; @androidx.annotation.Nullable public JavaAudioDeviceModule adm; - private String selfStreamId; - - private static final Map captureTimeMsMap = new ConcurrentHashMap<>(); //PeerConnection Parameters + private final WebRTCClientConfig config; - private WebRTCClientConfig config; - - public boolean tracing; - - public boolean loopback; - private boolean aecDump; - private boolean useOpenSLES; - private boolean disableBuiltInAEC; - private boolean disableBuiltInAGC; - private boolean disableBuiltInNS; - private boolean disableWebRtcAGCAndHPF; - private boolean enableRtcEventLog; - private boolean audioCallEnabled = true; - private boolean captureToTexture; - - //DataChannel Parameters - private boolean dataChannelOrdered; - private int dataChannelMaxRetransmitTimeMs; - private int dataChannelMaxRetransmits; - private String dataChannelProtocol = ""; - private boolean dataChannelNegotiated; - private int dataChannelId; private boolean removeVideoRotationExtention = false; //reconnection parameters private Handler reconnectionHandler = new Handler(); - - private boolean reconnectionEnabled = false; - final int RECONNECTION_PERIOD_MLS = 1000; - public static final int RECONNECTION_CONTROL_PERIOD_MLS = 10000; - Runnable reconnectionRunnable; - public void createReconnectionRunnable() { reconnectionRunnable = new Runnable() { @Override @@ -343,8 +230,7 @@ public void run() { PeerConnection pc = peerInfo.peerConnection; if (pc == null || - (pc != null - && pc.iceConnectionState() != PeerConnection.IceConnectionState.CHECKING + (pc.iceConnectionState() != PeerConnection.IceConnectionState.CHECKING && pc.iceConnectionState() != PeerConnection.IceConnectionState.CONNECTED && pc.iceConnectionState() != PeerConnection.IceConnectionState.COMPLETED)) { @@ -354,7 +240,9 @@ public void run() { pc.dispose(); } - if (peerInfo.mode.equals(MODE_PUBLISH)) { + config.webRTCListener.onReconnectionAttempt(peerInfo.id); + + if (peerInfo.mode.equals(Mode.PUBLISH)) { publish(peerInfo.id, peerInfo.token, peerInfo.videoCallEnabled, @@ -363,7 +251,7 @@ public void run() { peerInfo.subscriberCode, peerInfo.streamName, peerInfo.mainTrackId); - } else if (peerInfo.mode.equals(MODE_PLAY)) { + } else if (peerInfo.mode.equals(Mode.PLAY)) { play(peerInfo.id, peerInfo.token, null, @@ -371,13 +259,14 @@ public void run() { peerInfo.subscriberCode, peerInfo.metaData); } + else if (peerInfo.mode.equals(Mode.P2P)) { + config.localVideoRenderer.setZOrderOnTop(true); + + join(peerInfo.id, peerInfo.token); + } } } - Log.i(TAG, "Try to reconnect in reconnectionRunnable " + streamMode); - config.webRTCListener.onReconnectionAttempt(initialStreamId); - if (streamMode == IWebRTCClient.MODE_JOIN) { - pipRenderer.setZOrderOnTop(true); - } + Log.i(TAG, "Try to reconnect in reconnectionRunnable"); reconnectionInProgress = !noNeedToRetry; @@ -389,24 +278,37 @@ public void run() { }; } + WebRTCClient(WebRTCClientConfig config) { + this.config = config; + config.webRTCListener.setWebRTCClient(this); + permissionsHandler = new PermissionsHandler(config.activity); + mainHandler = new Handler(config.activity.getMainLooper()); + + iceServers.add(PeerConnection.IceServer.builder(config.stunServerUri) + .createIceServer()); + + + if(config.initiateBeforeStream) { + init(); + } + } + + @Override + public WebRTCClientConfig getConfig() { + return config; + } + public SDPObserver getSdpObserver(String streamId) { return new SDPObserver(streamId); } public void joinToConferenceRoom(String roomId, String streamId) { - handler.post(() -> { - if (wsHandler != null) { - wsHandler.joinToConferenceRoom(roomId, streamId); - } - }); + init(); + wsHandler.joinToConferenceRoom(roomId, streamId); } public void leaveFromConference(String roomId) { - handler.post(() -> { - if (wsHandler != null) { - wsHandler.leaveFromTheConferenceRoom(roomId); - } - }); + wsHandler.leaveFromTheConferenceRoom(roomId); } public void getRoomInfo(String roomId, String streamId) { @@ -417,61 +319,11 @@ public void getRoomInfo(String roomId, String streamId) { }); } - class TrackCheckTask extends TimerTask { - private MediaStream mediaStream; - private ConcurrentLinkedQueue videoTracks = new ConcurrentLinkedQueue<>(); - - @Override - public void run() { - if(mediaStream == null) { - return; - } - - List currentTracks = mediaStream.videoTracks; - - if (currentTracks.size() > 0) { - for (VideoTrack track : videoTracks) { - boolean trackLive = false; - for (VideoTrack currentTrack : currentTracks) { - if (currentTrack.id().equals(track.id())) { - trackLive = true; - break; - } - } - if(!trackLive) { - videoTracks.remove(track); - handler.post(() -> { - config.webRTCListener.onVideoTrackEnded(track); - }); - } - } - } - } - - public void setStream(MediaStream stream) { - this.mediaStream = stream; - } - - public ConcurrentLinkedQueue getVideoTracks() { - return videoTracks; - } - - }; - - /** - * This task is used to check if video track is still alive - * Different than others SDKs, WebRTC doesn't have any callback in Android for track ended - */ - TrackCheckTask trackCheckerTask; - - //Timer to check if video track check task - private Timer trackCheckerTimer; - // Implementation detail: observe ICE & stream changes and react accordingly. - class PCObserver implements PeerConnection.Observer { + public class PCObserver implements PeerConnection.Observer { - private String streamId; + private final String streamId; PCObserver(String streamId) { this.streamId = streamId; @@ -550,31 +402,6 @@ public void onSelectedCandidatePairChanged(CandidatePairChangeEvent event) { @Override public void onAddStream(final MediaStream stream) { - if(trackCheckerTask != null) { - trackCheckerTask.setStream(stream); - } - if (!isVideoCallEnabled() && !isAudioEnabled()) - { - if(renderersProvidedAtStart || !remoteSinks.isEmpty()) { - updateVideoTracks(streamId); - } - } - } - - private void updateVideoTracks(String streamId) { - List remoteVideoTrackList = getRemoteVideoTrackList(streamId); - for (int i = 0; i < remoteVideoTrackList.size(); i++) - { - VideoTrack videoTrack = remoteVideoTrackList.get(i); - - if (i < remoteSinks.size()) { - videoTrack.addSink(remoteSinks.get(i)); - - - } else { - Log.e(TAG, "There is no enough remote sinks to show video tracks"); - } - } } @Override @@ -587,57 +414,74 @@ public void onDataChannel(final DataChannel dc) { if (!config.dataChannelEnabled) return; - if (peers.get(streamId).dataChannel == null) { - peers.get(streamId).dataChannel = dc; + PeerInfo peerInfo = peers.get(streamId); + if (peerInfo != null && peerInfo.dataChannel == null) { + peerInfo.dataChannel = dc; } dc.registerObserver(new DataChannelInternalObserver(dc)); } @Override public void onRenegotiationNeeded() { - // No need to do anything; AppRTC follows a pre-agreed-upon - // signaling/negotiation protocol. + Log.d(TAG, "onRenegotiationNeeded"); } @Override public void onAddTrack(final RtpReceiver receiver, final MediaStream[] mediaStreams) { - if(receiver.track() instanceof VideoTrack) { - VideoTrack videoTrack = (VideoTrack) receiver.track(); - config.webRTCListener.onNewVideoTrack(videoTrack); - if(streamMode.equals(MODE_MULTI_TRACK_PLAY) || streamMode.equals(MODE_TRACK_BASED_CONFERENCE)) { - trackCheckerTask.getVideoTracks().add(videoTrack); - } + MediaStreamTrack addedTrack = receiver.track(); + if(addedTrack == null) { + return; + } + Log.d("antmedia","on add track "+addedTrack.kind()+" "+addedTrack.id()+" "+addedTrack.state()); - if(renderersProvidedAtStart || !remoteSinks.isEmpty()) { - updateVideoTracks(streamId); - } + if(addedTrack instanceof VideoTrack) { + VideoTrack videoTrack = (VideoTrack) addedTrack; + config.webRTCListener.onNewVideoTrack(videoTrack); } } @Override public void onRemoveTrack(RtpReceiver receiver) { - Log.d("antmedia","on remove track"); + MediaStreamTrack removedTrack = receiver.track(); + if(removedTrack == null) { + return; + } + Log.d("antmedia","on remove track "+removedTrack.kind()+" "+removedTrack.id()+" "+removedTrack.state()); + if(removedTrack instanceof VideoTrack) { + config.webRTCListener.onVideoTrackEnded((VideoTrack) removedTrack); + } } } + @Override + public void setRendererForVideoTrack(SurfaceViewRenderer renderer, VideoTrack videoTrack) { + mainHandler.post(() -> { + ProxyVideoSink remoteVideoSink = new ProxyVideoSink(); + if(renderer != null) { + remoteVideoSink.setTarget(renderer); + renderer.init(eglBase.getEglBaseContext(), null); + renderer.setScalingType(config.scalingType); + renderer.setEnableHardwareScaler(true); + } + videoTrack.addSink(remoteVideoSink); + remoteVideoSinks.add(remoteVideoSink); + }); + } + // Implementation detail: handle offer creation/signaling and answer setting, // as well as adding remote ICE candidates once the answer SDP is set. - class SDPObserver implements SdpObserver { + public class SDPObserver implements SdpObserver { private final String streamId; SDPObserver(String streamId) { this.streamId = streamId; } @Override public void onCreateSuccess(final SessionDescription desc) { - //if (localDescription != null) { - // reportError("Multiple SDP create."); - // return; - //} String sdp = desc.description; if (preferIsac) { sdp = preferCodec(sdp, AUDIO_CODEC_ISAC, true); } - if (isVideoCallEnabled()) { + if (config.videoCallEnabled) { sdp = preferCodec(sdp, getSdpVideoCodecName(config.videoCodec), false); } @@ -648,7 +492,7 @@ public void onCreateSuccess(final SessionDescription desc) { final SessionDescription newDesc = new SessionDescription(desc.type, sdp); localDescription = newDesc; executor.execute(() -> { - PeerConnection pc = peers.get(streamId).peerConnection; + PeerConnection pc = getPeerConnectionFor(streamId); if (pc != null && !isError) { Log.d(TAG, "Set local SDP from " + desc.type); pc.setLocalDescription(this, newDesc); @@ -661,8 +505,8 @@ public void onSetSuccess() { Log.i(TAG, "onSetSuccess: "); executor.execute(() -> { - PeerConnection pc = peers.get(streamId).peerConnection; - if (pc == null || isError) { + PeerConnection pc = getPeerConnectionFor(streamId); + if (pc == null) { return; } @@ -708,249 +552,57 @@ public void onSetFailure(final String error) { } } - WebRTCClient(WebRTCClientConfig config) { - this.config = config; - mainHandler = new Handler(config.context.getMainLooper()); - iceServers.add(new PeerConnection.IceServer(stunServerUri)); - } - - public WebRTCClient(IWebRTCListener webRTCListener, Context context) { - config = new WebRTCClientConfig(); - config.webRTCListener = webRTCListener; - config.context = context; - mainHandler = new Handler(config.context.getMainLooper()); - iceServers.add(new PeerConnection.IceServer(stunServerUri)); - } - - - public void setRemoteRendererList(List rendererList) { - this.remoteRendererList = rendererList; - } - - public void setSelfStreamId(String streamId) { - this.selfStreamId = streamId; - } - - @Override - public void init(String url, String streamId, String mode, String token, Intent intent) { - - if (url == null) { - Log.d(TAG, this.config.context.getString(R.string.missing_url)); - return; - } - config.serverUrl = url; - - if (streamId == null || streamId.length() == 0) { - Log.d(TAG, this.config.context.getString(R.string.missing_stream_id)); - return; - } - this.initialStreamId = streamId; - - if (mode == null || mode.length() == 0) { - Log.d(TAG, this.config.context.getString(R.string.missing_stream_id)); - return; - } - this.streamMode = mode; - config.token = token; - if (intent != null) { - this.intent = intent; - } - + public void init() { //if permissions are not granted yet return now bu set init as callback to call it again after grant result - if(!checkPermissions(() -> init(url, streamId, mode, token, intent))) { + if(!checkPermissions(this::init)) { return; } - if(reconnectionEnabled && reconnectionRunnable == null) { + if(config.reconnectionEnabled && reconnectionRunnable == null) { createReconnectionRunnable(); } - - /* - * TODO (burak): we need this track checker to be able to understand if the track is stopped. - * Normally we should have a callback from the peer connection but it does not have or - * we could not find it. So we are checking the track status periodically - */ - initializeTrackChecker(); - initializeRenderers(); initializeParameters(); initializePeerConnectionFactory(); - initializeVideoCapturer(); + if(config.videoCallEnabled) { + initializeVideoCapturer(); + } initializeAudioManager(); connectWebSocket(); } - public boolean checkPermissions(PermissionCallback permissionCallback) { - boolean isForPublish = streamMode.equals(MODE_PUBLISH) || - streamMode.equals(MODE_TRACK_BASED_CONFERENCE); - - return config.webRTCListener.checkAndRequestPermisssions(isForPublish, permissionCallback); - } - - private void initializeTrackChecker() { - if((streamMode.equals(MODE_MULTI_TRACK_PLAY) || streamMode.equals(MODE_TRACK_BASED_CONFERENCE)) - && trackCheckerTask == null) { - trackCheckerTask = new TrackCheckTask(); - trackCheckerTimer = new Timer(); - trackCheckerTimer.schedule(trackCheckerTask, TRACK_CHECK_PERIDOD_MS, TRACK_CHECK_PERIDOD_MS); - } - } - - - public void addTrackToRenderer(VideoTrack track, SurfaceViewRenderer renderer) { - mainHandler.post(() -> { - ProxyVideoSink remoteVideoSink = new ProxyVideoSink(); - remoteVideoSink.setTarget(renderer); - renderer.init(eglBase.getEglBaseContext(), null); - renderer.setScalingType(ScalingType.SCALE_ASPECT_FIT); - renderer.setEnableHardwareScaler(true); - track.addSink(remoteVideoSink); - - dynamicRenderers.add(renderer); - dynamicRemoteSinks.add(remoteVideoSink); - }); + public boolean checkPermissions(PermissionsHandler.PermissionCallback permissionCallback) { + return permissionsHandler.checkAndRequestPermisssions(requestExtendedRights, permissionCallback); } - public void initializeParameters() { - loopback = intent.getBooleanExtra(CallActivity.EXTRA_LOOPBACK, false); - tracing = intent.getBooleanExtra(CallActivity.EXTRA_TRACING, false); - - config.videoWidth = intent.getIntExtra(CallActivity.EXTRA_VIDEO_WIDTH, 0); - config.videoHeight = intent.getIntExtra(CallActivity.EXTRA_VIDEO_HEIGHT, 0); - - screencaptureEnabled = intent.getBooleanExtra(CallActivity.EXTRA_SCREENCAPTURE, false); // If capturing format is not specified for screencapture, use screen resolution. - if (screencaptureEnabled && config.videoWidth == 0 && config.videoHeight == 0) { + if (config.screencaptureEnabled) { DisplayMetrics displayMetrics = getDisplayMetrics(); config.videoWidth = displayMetrics.widthPixels; config.videoHeight = displayMetrics.heightPixels; } - - config.dataChannelEnabled = intent.getBooleanExtra(CallActivity.EXTRA_DATA_CHANNEL_ENABLED, true); - if (config.dataChannelEnabled) { - dataChannelOrdered = intent.getBooleanExtra(CallActivity.EXTRA_ORDERED, true); - dataChannelMaxRetransmitTimeMs = intent.getIntExtra(CallActivity.EXTRA_MAX_RETRANSMITS_MS, -1); - dataChannelMaxRetransmits = intent.getIntExtra(CallActivity.EXTRA_MAX_RETRANSMITS, -1); - dataChannelProtocol = intent.getStringExtra(CallActivity.EXTRA_PROTOCOL); - dataChannelNegotiated = intent.getBooleanExtra(CallActivity.EXTRA_NEGOTIATED, false); - dataChannelId = intent.getIntExtra(CallActivity.EXTRA_ID, -1); - } - - config.videoFps = intent.getIntExtra(CallActivity.EXTRA_VIDEO_FPS, 0); - - config.videoCodec = intent.getStringExtra(CallActivity.EXTRA_VIDEOCODEC); - if (config.videoCodec == null) { - config.videoCodec = this.config.context.getString(R.string.pref_videocodec_default); - } - config.videoStartBitrate = this.intent.getIntExtra(CallActivity.EXTRA_VIDEO_BITRATE, 0); - - if (config.videoStartBitrate == 0) { - config.videoStartBitrate = Integer.parseInt(this.config.context.getString(R.string.pref_maxvideobitratevalue_default)); - } - - config.audioStartBitrate = this.intent.getIntExtra(CallActivity.EXTRA_AUDIO_BITRATE, 0); - if (config.audioStartBitrate == 0) { - config.audioStartBitrate = Integer.parseInt(this.config.context.getString(R.string.pref_startaudiobitratevalue_default)); - } - - config.videoCallEnabled = intent.getBooleanExtra(CallActivity.EXTRA_VIDEO_CALL, true); - - if (isDataChannelOnly() || streamMode.equals(MODE_PLAY) || streamMode.equals(MODE_MULTI_TRACK_PLAY)) { - config.videoCallEnabled = false; - audioCallEnabled = false; - } - - if(streamMode.equals(MODE_TRACK_BASED_CONFERENCE)){ - config.videoCallEnabled = true; - audioCallEnabled = true; - } - - - - config.hwCodecAcceleration = intent.getBooleanExtra(CallActivity.EXTRA_HWCODEC_ENABLED, true); - config.videoFlexfecEnabled = intent.getBooleanExtra(CallActivity.EXTRA_FLEXFEC_ENABLED, false); - config.audioCodec = intent.getStringExtra(CallActivity.EXTRA_AUDIOCODEC); - config.noAudioProcessing = intent.getBooleanExtra(CallActivity.EXTRA_NOAUDIOPROCESSING_ENABLED, false); - aecDump = intent.getBooleanExtra(CallActivity.EXTRA_AECDUMP_ENABLED, false); - useOpenSLES = intent.getBooleanExtra(CallActivity.EXTRA_OPENSLES_ENABLED, false); - disableBuiltInAEC = intent.getBooleanExtra(CallActivity.EXTRA_DISABLE_BUILT_IN_AEC, false); - disableBuiltInAGC = intent.getBooleanExtra(CallActivity.EXTRA_DISABLE_BUILT_IN_AGC, false); - disableBuiltInNS = intent.getBooleanExtra(CallActivity.EXTRA_DISABLE_BUILT_IN_NS, false); - disableWebRtcAGCAndHPF = intent.getBooleanExtra(CallActivity.EXTRA_DISABLE_WEBRTC_AGC_AND_HPF, false); - enableRtcEventLog = intent.getBooleanExtra(CallActivity.EXTRA_ENABLE_RTCEVENTLOG, false); - captureToTexture = intent.getBooleanExtra(CallActivity.EXTRA_CAPTURETOTEXTURE_ENABLED, true); } public void initializeRenderers() { if(eglBase == null) { eglBase = EglBase.create(); } - if(renderersInitiated){ - return; - } - - if (remoteRendererList != null) { - renderersProvidedAtStart = true; - for (SurfaceViewRenderer renderer : remoteRendererList) { - //if we are performing reconnection, we shouldn't add remote sinks again - if (!renderersInitiated) { - ProxyVideoSink remoteVideoSink = new ProxyVideoSink(); - remoteSinks.add(remoteVideoSink); - } - - renderer.init(eglBase.getEglBaseContext(), null); - renderer.setScalingType(ScalingType.SCALE_ASPECT_FIT); - renderer.setEnableHardwareScaler(true); - - } - } - else { - remoteSinks.add(remoteProxyRenderer); - } - // else if(remoteProxyRenderer != null){ - // remoteSinks.add(remoteProxyRenderer); - // } - // Create video renderers. - if (pipRenderer != null) { - pipRenderer.init(eglBase.getEglBaseContext(), null); - pipRenderer.setScalingType(ScalingType.SCALE_ASPECT_FIT); + //init local renderer if available + if (config.localVideoRenderer != null && localVideoSink.getTarget() == null) { + config.localVideoRenderer.init(eglBase.getEglBaseContext(), null); + config.localVideoRenderer.setScalingType(config.scalingType); + config.localVideoRenderer.setZOrderMediaOverlay(true); + config.localVideoRenderer.setEnableHardwareScaler(true /* enabled */); + localVideoSink.setTarget(config.localVideoRenderer); } - - // When saveRemoteVideoToFile is set we save the video from the remote to a file. - if (saveRemoteVideoToFile != null) { - try { - videoFileRenderer = new VideoFileRenderer( - saveRemoteVideoToFile, config.videoWidth, - config.videoHeight, eglBase.getEglBaseContext()); - remoteSinks.add(videoFileRenderer); - } catch (IOException e) { - throw new RuntimeException( - "Failed to open video file for output: " + saveRemoteVideoToFile, e); - } - } - if(fullscreenRenderer != null) { - fullscreenRenderer.init(eglBase.getEglBaseContext(), null); - fullscreenRenderer.setScalingType(ScalingType.SCALE_ASPECT_FILL); - fullscreenRenderer.setEnableHardwareScaler(false /* enabled */); - Log.i(getClass().getSimpleName(), "Initializing the full screen renderer"); - } - - if (pipRenderer != null) { - pipRenderer.setZOrderMediaOverlay(true); - pipRenderer.setEnableHardwareScaler(true /* enabled */); - } - - // Start with local feed in fullscreen and swap it to the pip when the call is connected. - setSwappedFeeds(true /* isSwappedFeeds */); - renderersInitiated = true; } public void initializePeerConnectionFactory() { @@ -960,11 +612,11 @@ public void initializePeerConnectionFactory() { // Create peer connection client. Log.d(TAG, "Preferred video codec: " + getSdpVideoCodecName(config.videoCodec)); - final String fieldTrials = getFieldTrials(config.videoFlexfecEnabled, disableWebRtcAGCAndHPF); + final String fieldTrials = getFieldTrials(config.videoFlexfecEnabled, config.disableWebRtcAGCAndHPF); executor.execute(() -> { Log.d(TAG, "Initialize WebRTC. Field trials: " + fieldTrials); PeerConnectionFactory.initialize( - PeerConnectionFactory.InitializationOptions.builder(config.context) + PeerConnectionFactory.InitializationOptions.builder(config.activity) .setFieldTrials(fieldTrials) .setEnableInternalTracer(true) .createInitializationOptions()); @@ -972,28 +624,19 @@ public void initializePeerConnectionFactory() { PeerConnectionFactory.Options options = new PeerConnectionFactory.Options(); - if (loopback) { - options.networkIgnoreMask = 0; - } - //options.disableEncryption = true; createPeerConnectionFactory(options); } public void initializeAudioManager() { - if (audioCallEnabled && audioManager == null) { + if (config.audioCallEnabled && audioManager == null) { // Create and audio manager that will take care of audio routing, // audio modes, audio device enumeration etc. - audioManager = AppRTCAudioManager.create(this.config.context.getApplicationContext()); + audioManager = AppRTCAudioManager.create(this.config.activity.getApplicationContext()); // Store existing audio settings and change audio mode to // MODE_IN_COMMUNICATION for best possible VoIP performance. Log.d(TAG, "Starting the audio manager..."); - audioManager.start((audioDevice, availableAudioDevices) -> - { - // This method will be called each time the number of available audio devices has changed. - onAudioManagerDevicesChanged(audioDevice, availableAudioDevices); - }); - - + // This method will be called each time the number of available audio devices has changed. + audioManager.start(this::onAudioManagerDevicesChanged); } } @@ -1003,32 +646,22 @@ public void initializeVideoCapturer() { return; } - // if video capture is null or disposed, we should recreate it. - // we should also check if video capturer is an instance of ScreenCapturerAndroid - // because other implementations of VideoCapturer doesn't have a dispose() method. - if (config.videoCallEnabled - && (videoCapturer == null - || (videoCapturer instanceof ScreenCapturerAndroid)) - ) { + if (config.videoCallEnabled) { - String source = SOURCE_REAR; - String videoFileAsCamera = this.intent.getStringExtra(CallActivity.EXTRA_VIDEO_FILE_AS_CAMERA); + StreamSource source = StreamSource.REAR_CAMERA; - if (videoFileAsCamera != null) { - source = SOURCE_FILE; + if(config.screencaptureEnabled) { + source = StreamSource.SCREEN; } - else if(screencaptureEnabled) { - source = SOURCE_SCREEN; - } - else if(customCapturerEnabled) { - source = SOURCE_CUSTOM; + else if(config.customVideoCapturerEnabled) { + source = StreamSource.CUSTOM; } else if(useCamera2()) { - source = SOURCE_FRONT; + source = StreamSource.FRONT_CAMERA; } videoCapturer = createVideoCapturer(source); - currentSource = source; + config.videoSource = source; } executor.execute(() -> { @@ -1038,20 +671,10 @@ else if(useCamera2()) { }); } - public void setMediaProjection(MediaProjection mediaProjection){ - this.mediaProjection = mediaProjection; - adm.setMediaProjection(mediaProjection); - } - public void setBitrate(int bitrate) { setVideoMaxBitrate(bitrate); } - public void startStream(String streamId) { - Log.i(getClass().getSimpleName(), "Starting stream"); - startCall(streamId); - } - public void connectWebSocket() { if (wsHandler == null) { Log.i(TAG, "WebsocketHandler is null and creating a new instance"); @@ -1065,83 +688,32 @@ public void connectWebSocket() { } } - /** - * Starts the actual WebRTC session with the initial stream Id. - * @deprecated - * use {@link #startStream(String)} instead - */ - @Deprecated - public void startStream() { - startStream(initialStreamId); - } - - - @TargetApi(17) public DisplayMetrics getDisplayMetrics() { DisplayMetrics displayMetrics = new DisplayMetrics(); - WindowManager windowManager = - (WindowManager) this.config.context.getSystemService(config.context.WINDOW_SERVICE); + WindowManager windowManager = (WindowManager) config.activity.getSystemService(Activity.WINDOW_SERVICE); windowManager.getDefaultDisplay().getRealMetrics(displayMetrics); return displayMetrics; } - @TargetApi(19) - private static int getSystemUiVisibility() { - int flags = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - flags |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; - } - return flags; - } - - @TargetApi(21) - public void startScreenCapture() { - mediaProjectionManager = - (MediaProjectionManager) this.config.context.getSystemService( - config.context.MEDIA_PROJECTION_SERVICE); - - if (this.config.context instanceof Activity) { - ((Activity) this.config.context).startActivityForResult( - mediaProjectionManager.createScreenCaptureIntent(), CallActivity.CAPTURE_PERMISSION_REQUEST_CODE); - } - } - - @TargetApi(21) public @Nullable VideoCapturer createScreenCapturer() { - if (mediaProjectionPermissionResultCode != Activity.RESULT_OK) { - reportError(initialStreamId, "User didn't give permission to capture the screen."); - return null; - } - return new ScreenCapturerAndroid(mediaProjectionPermissionResultData, new MediaProjection.Callback() { + return new ScreenCapturer(config.mediaProjection, new MediaProjection.Callback() { @Override public void onStop() { - //this is self-explanatory error code - reportError(initialStreamId, ERROR_USER_REVOKED_CAPTURE_SCREEN_PERMISSION); + reportError(getPublishStreamId(), USER_REVOKED_CAPTURE_SCREEN_PERMISSION); } }); } - public void onActivityResult(int requestCode, int resultCode, Intent data) { - if (requestCode != CallActivity.CAPTURE_PERMISSION_REQUEST_CODE) - return; - mediaProjectionPermissionResultCode = resultCode; - mediaProjectionPermissionResultData = data; - screenPermissionNeeded = false; - changeVideoSource(SOURCE_SCREEN); - } public boolean useCamera2() { - return Camera2Enumerator.isSupported(this.config.context) && this.intent.getBooleanExtra(CallActivity.EXTRA_CAMERA2, true); + return Camera2Enumerator.isSupported(config.activity); } - public void setOpenFrontCamera(boolean openFrontCamera) { - this.openFrontCamera = openFrontCamera; - } - - private @Nullable VideoCapturer createCameraCapturer(CameraEnumerator enumerator) { + @Nullable + public VideoCapturer createCameraCapturer(CameraEnumerator enumerator) { final String[] deviceNames = enumerator.getDeviceNames(); - if (openFrontCamera) { + if (config.videoSource == StreamSource.FRONT_CAMERA) { // First, try to find front facing camera Logging.d(TAG, "Looking for front facing cameras."); for (String deviceName : deviceNames) { @@ -1189,39 +761,37 @@ public void setOpenFrontCamera(boolean openFrontCamera) { public void stopVideoSource() { // Don't stop the video when using screencapture to allow user to show other apps to the remote // end. - if (!screencaptureEnabled) { + if (!config.screencaptureEnabled) { executor.execute(this::stopVideoSourceInternal); } - localProxyVideoSink.setTarget(null); + localVideoSink.setTarget(null); } @Override public void startVideoSource() { // Video is not paused for screencapture. See onPause. - if (!screencaptureEnabled) { + if (!config.screencaptureEnabled) { executor.execute(this::startVideoSourceInternal); } } - /* - * Starts the actual WebRTC session with the given stream Id. - * @deprecated - * use {@link #stopStream(String)} instead - */ - @Deprecated @Override - public void stopStream() { - stopStream(initialStreamId, true); + public void setSwappedFeeds(boolean isSwappedFeeds) { + localVideoSink.setTarget(isSwappedFeeds ? config.remoteVideoRenderers.get(0) : config.localVideoRenderer); + remoteVideoSinks.get(0).setTarget(isSwappedFeeds ? config.localVideoRenderer : config.remoteVideoRenderers.get(0)); + config.remoteVideoRenderers.get(0).setMirror(isSwappedFeeds); + config.localVideoRenderer.setMirror(!isSwappedFeeds); } - public void stopStream(String streamId) { - stopStream(streamId, true); + + public void stop(String streamId) { + stop(streamId, true); } - public void stopStream(String streamId, boolean byUser) { + public void stop(String streamId, boolean byUser) { Log.i(getClass().getSimpleName(), "Stopping stream"); streamStoppedByUser = byUser; if (wsHandler != null && wsHandler.isConnected()) { @@ -1231,7 +801,11 @@ public void stopStream(String streamId, boolean byUser) { @Override public void switchCamera() { - openFrontCamera = !openFrontCamera; + if(config.videoSource == StreamSource.FRONT_CAMERA) { + config.videoSource = StreamSource.REAR_CAMERA; + } else if (config.videoSource == StreamSource.REAR_CAMERA) { + config.videoSource = StreamSource.FRONT_CAMERA; + } executor.execute(this ::switchCameraInternal); } @@ -1240,18 +814,6 @@ public void onCameraSwitch() { switchCamera(); } - @Override - public void onVideoScalingSwitch(ScalingType scalingType) { - if (fullscreenRenderer != null) { - fullscreenRenderer.setScalingType(scalingType); - } - } - - - public void switchVideoScaling(RendererCommon.ScalingType scalingType) { - onVideoScalingSwitch(scalingType); - } - @Override public void onCaptureFormatChange(int width, int height, int framerate) { executor.execute(() -> changeCaptureFormatInternal(width, height, framerate)); @@ -1269,24 +831,6 @@ public boolean toggleMic() { return onToggleMic(); } - private void startCall(String streamId) { - Log.d(TAG, this.config.context.getString(R.string.connecting_to, config.serverUrl)); - if (streamMode.equals(IWebRTCClient.MODE_PUBLISH)) { - publish(streamId, config.token, config.videoCallEnabled, config.audioCallEnabled, subscriberId, subscriberCode, streamName, mainTrackId); - } - else if (streamMode.equals(IWebRTCClient.MODE_PLAY)) { - play(streamId, config.token, null, subscriberId, subscriberCode, viewerInfo); - } - else if (streamMode.equals(IWebRTCClient.MODE_JOIN)) { - init(config.serverUrl, streamId, this.streamMode, config.token, this.intent); - wsHandler.joinToPeer(streamId, config.token); - } - else if (streamMode.equals(IWebRTCClient.MODE_MULTI_TRACK_PLAY)) { - init(config.serverUrl, streamId, this.streamMode, config.token, this.intent); - wsHandler.getTrackList(streamId, config.token); - } - } - public void publish(String streamId) { publish(streamId, null, true, true, null, null, streamId, null); @@ -1296,8 +840,9 @@ public void publish(String streamId) { public void publish(String streamId, String token, boolean videoCallEnabled, boolean audioCallEnabled, String subscriberId, String subscriberCode, String streamName, String mainTrackId) { Log.e(TAG, "Publish: "+streamId); + requestExtendedRights = true; - PeerInfo peerInfo = new PeerInfo(streamId, MODE_PUBLISH); + PeerInfo peerInfo = new PeerInfo(streamId, Mode.PUBLISH); peerInfo.token = token; peerInfo.videoCallEnabled = videoCallEnabled; peerInfo.audioCallEnabled = audioCallEnabled; @@ -1307,35 +852,52 @@ public void publish(String streamId, String token, boolean videoCallEnabled, boo peerInfo.mainTrackId = mainTrackId; peers.put(streamId, peerInfo); - init(config.serverUrl, streamId, MODE_PUBLISH, config.token, this.intent); - while (!wsHandler.isConnected()) { - try { - Thread.sleep(100); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } + init(); wsHandler.startPublish(streamId, token, videoCallEnabled, audioCallEnabled, subscriberId, subscriberCode, streamName, mainTrackId); } - public void play(String streamId, String token, String[] tracks) { - play(streamId, token, tracks, "", "", ""); + public void play(String streamId) { + play(streamId, "", null, "", "", ""); + } + + public void play(String streamId, String[] tracks) { + play(streamId, "", tracks, "", "", ""); } public void play(String streamId, String token, String[] tracks, String subscriberId, String subscriberCode, String viewerInfo) { Log.e(TAG, "Play: "+streamId); - PeerInfo peerInfo = new PeerInfo(streamId, MODE_PLAY); + PeerInfo peerInfo = new PeerInfo(streamId, Mode.PLAY); peerInfo.token = token; peerInfo.subscriberId = subscriberId; peerInfo.subscriberCode = subscriberCode; peerInfo.metaData = viewerInfo; peers.put(streamId, peerInfo); - init(config.serverUrl, streamId, this.streamMode, token, this.intent); + init(); wsHandler.startPlay(streamId, token, tracks, subscriberId, subscriberCode, viewerInfo); } + public void join(String streamId) { + join(streamId, ""); + } + + public void join(String streamId, String token) { + Log.e(TAG, "Join: "+streamId); + requestExtendedRights = true; + + PeerInfo peerInfo = new PeerInfo(streamId, Mode.P2P); + peerInfo.token = token; + peers.put(streamId, peerInfo); + + init(); + wsHandler.joinToPeer(streamId, token); + } + public void getTrackList(String streamId, String token) { + init(); + wsHandler.getTrackList(streamId, token); + } + public void enableTrack(String streamId, String trackId, boolean enabled) { wsHandler.enableTrack(streamId,trackId, enabled); } @@ -1349,13 +911,12 @@ private void callConnected(String streamId) { return; } // Enable statistics callback. - enableStatsEvents(streamId, true, CallActivity.STAT_CALLBACK_PERIOD); - setSwappedFeeds(false /* isSwappedFeeds */); + enableStatsEvents(streamId, true, STAT_CALLBACK_PERIOD); } // This method is called when the audio manager reports audio device change, // e.g. from wired headset to speakerphone. - void onAudioManagerDevicesChanged( + public void onAudioManagerDevicesChanged( final AppRTCAudioManager.AudioDevice device, final Set availableDevices) { Log.d(TAG, "onAudioManagerDevicesChanged: " + availableDevices + ", " + "selected: " + device); @@ -1369,39 +930,24 @@ void onAudioManagerDevicesChanged( public void release(boolean closeWebsocket) { Log.i(getClass().getSimpleName(), "Releasing resources"); - remoteProxyRenderer.setTarget(null); localVideoTrack = null; localAudioTrack = null; - if (closeWebsocket && wsHandler != null && wsHandler.getSignallingListener().equals(this)) { + if (closeWebsocket && wsHandler != null) { wsHandler.disconnect(true); wsHandler = null; } - if (pipRenderer != null) { - pipRenderer.release(); - // pipRenderer = null; Do not make renderer null, we can re-use + if (config.localVideoRenderer != null) { + config.localVideoRenderer.release(); } - if (remoteRendererList != null) { - for (SurfaceViewRenderer renderer : remoteRendererList) { - renderer.release(); + for (SurfaceViewRenderer remoteVideoRenderer : config.remoteVideoRenderers) { + if(remoteVideoRenderer.getTag() != null) { + remoteVideoRenderer.release(); + remoteVideoRenderer.setTag(null); } } - if (fullscreenRenderer != null) { - Log.i(getClass().getSimpleName(), "Releasing full screen renderer"); - fullscreenRenderer.release(); - // fullscreenRenderer = null; Do not make renderer null, we can re-use - } - if (videoFileRenderer != null) { - videoFileRenderer.release(); - // videoFileRenderer = null; Do not make renderer null, we can re-use - } - - for (SurfaceViewRenderer renderer : dynamicRenderers) { - renderer.release(); - } - dynamicRenderers.clear(); - dynamicRemoteSinks.clear(); + remoteVideoSinks.clear(); executor.execute(this ::closeInternal); @@ -1429,103 +975,62 @@ public void reportError(String streamId, final String description) { config.webRTCListener.onError(description, streamId); } } - }); } - public void changeVideoSource(String newSource) { - if(currentSource == null || !currentSource.equals(newSource)) { - if(newSource.equals(SOURCE_SCREEN) && screenPermissionNeeded) { - startScreenCapture(); - return; - } else if(newSource.equals(SOURCE_REAR)) { - openFrontCamera = false; - } else if(newSource.equals(SOURCE_FRONT)) { - openFrontCamera = true; + public void changeVideoSource(StreamSource newSource) { + if(config.videoSource == null || !config.videoSource.equals(newSource)) { + if(newSource.equals(StreamSource.SCREEN) && adm != null) { + adm.setMediaProjection(config.mediaProjection); } - VideoCapturer newVideoCapturer = createVideoCapturer(newSource); - int videoWidth = intent.getIntExtra(CallActivity.EXTRA_VIDEO_WIDTH, 0); - int videoHeight = intent.getIntExtra(CallActivity.EXTRA_VIDEO_HEIGHT, 0); - - // If capturing format is not specified for screencapture, use screen resolution. - if (videoWidth == 0 || videoHeight == 0) { - DisplayMetrics displayMetrics = getDisplayMetrics(); - videoWidth = displayMetrics.widthPixels; - videoHeight = displayMetrics.heightPixels; - } + VideoCapturer newVideoCapturer = createVideoCapturer(newSource); /* When user try to change video source after stopped the publishing * peerConnectionClient will null, until start another broadcast */ changeVideoCapturer(newVideoCapturer); - currentSource = newSource; + config.videoSource = newSource; } } - public @Nullable VideoCapturer createVideoCapturer(String source) { + public @Nullable VideoCapturer createVideoCapturer(StreamSource source) { final VideoCapturer videoCapturer; - if (SOURCE_FRONT.equals(source)) { - openFrontCamera = true; - } else if (SOURCE_REAR.equals(source)) { - openFrontCamera = false; - } - if (source.equals(SOURCE_FILE)) { - String videoFileAsCamera = this.intent.getStringExtra(CallActivity.EXTRA_VIDEO_FILE_AS_CAMERA); - try { - videoCapturer = new FileVideoCapturer(videoFileAsCamera); - } catch (IOException e) { - reportError(initialStreamId, "Failed to open video file for emulated camera"); - return null; - } - } else if (SOURCE_SCREEN.equals(source)) { - return createScreenCapturer(); - } else if (SOURCE_CUSTOM.equals(source)) { - return new CustomVideoCapturer(); + if (StreamSource.SCREEN.equals(source)) { + videoCapturer = createScreenCapturer(); + } else if (StreamSource.CUSTOM.equals(source)) { + videoCapturer = createCustomVideoCapturer(); } else { - if (!captureToTexture) { - reportError(initialStreamId, this.config.context.getString(R.string.camera2_texture_only_error)); - return null; - } - - Logging.d(TAG, "Creating capturer using camera2 API."); - videoCapturer = createCameraCapturer(new Camera2Enumerator(this.config.context)); + videoCapturer = createCameraCapturer(new Camera2Enumerator(this.config.activity)); } if (videoCapturer == null) { - reportError(initialStreamId, "Failed to open camera"); - return null; + reportError(getPublishStreamId(), "Failed to create capturer:" + source); } return videoCapturer; } - public void setSwappedFeeds(boolean isSwappedFeeds) { - Logging.d(TAG, "setSwappedFeeds: " + isSwappedFeeds); - if (this.streamMode.equals(MODE_PUBLISH)) { - localProxyVideoSink.setTarget(fullscreenRenderer); - } - else if (this.streamMode.equals(MODE_PLAY)) { - remoteProxyRenderer.setTarget(fullscreenRenderer); - } - else if (this.streamMode.equals(MODE_MULTI_TRACK_PLAY) || streamMode.equals(MODE_TRACK_BASED_CONFERENCE)) - { - if(renderersProvidedAtStart) { - for (int i = 0; i < remoteSinks.size(); i++) { - ((ProxyVideoSink) remoteSinks.get(i)).setTarget(remoteRendererList.get(i)); - } - } + @NonNull + public VideoCapturer createCustomVideoCapturer() { + return new CustomVideoCapturer(); + } - if(localProxyVideoSink != null) { - localProxyVideoSink.setTarget(fullscreenRenderer); - } + public String getPublishStreamId() { + for (PeerInfo peerInfo : peers.values()) { + if(peerInfo.mode.equals(Mode.PUBLISH)) { + return peerInfo.id; + } } - else if(fullscreenRenderer != null && pipRenderer != null) { - // True if local view is in the fullscreen renderer. - localProxyVideoSink.setTarget(isSwappedFeeds ? fullscreenRenderer : pipRenderer); - remoteProxyRenderer.setTarget(isSwappedFeeds ? pipRenderer : fullscreenRenderer); - fullscreenRenderer.setMirror(isSwappedFeeds); - pipRenderer.setMirror(!isSwappedFeeds); + return null; + } + + public String getMultiTrackStreamId() { + for (PeerInfo peerInfo : peers.values()) { + if(peerInfo.mode.equals(Mode.MULTI_TRACK_PLAY)) { + return peerInfo.id; + } } + return null; } public void setWsHandler(WebSocketHandler wsHandler) { @@ -1536,7 +1041,7 @@ private void setUpReconnection() { if(!streamStoppedByUser) { Log.i(getClass().getSimpleName(),"Disconnected. Trying to reconnect"); reconnectionInProgress = true; - //Toast.makeText(config.context, "Disconnected.Trying to reconnect "+streamStoppedByUser, Toast.LENGTH_LONG).show(); + //Toast.makeText(config.activity, "Disconnected.Trying to reconnect "+streamStoppedByUser, Toast.LENGTH_LONG).show(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { if (!reconnectionHandler.hasCallbacks(reconnectionRunnable)) { reconnectionHandler.postDelayed(reconnectionRunnable, RECONNECTION_PERIOD_MLS); @@ -1548,49 +1053,11 @@ private void setUpReconnection() { } - @Override - public void setVideoRenderers(SurfaceViewRenderer pipRenderer, SurfaceViewRenderer fullscreenRenderer) { - this.pipRenderer = pipRenderer; - this.fullscreenRenderer = fullscreenRenderer; - } - @Override public String getError() { return errorString; } - public void disableVideo() { - setVideoEnabled(false); - videoOn = false; - } - - public void enableVideo() { - setVideoEnabled(true); - videoOn = true; - } - - public void disableAudio() { - setAudioEnabled(false); - audioOn = false; - } - - public void enableAudio() { - setAudioEnabled(true); - audioOn = true; - } - - public boolean isVideoOn() { - return videoOn; - } - - public boolean isAudioOn() { - return audioOn; - } - - public int getMediaProjectionPermissionResultCode() { - return mediaProjectionPermissionResultCode; - } - // Send local peer connection SDP and ICE candidates to remote party. // All callbacks are invoked from peer connection client looper thread and // are routed to UI thread. @@ -1606,7 +1073,7 @@ public void onLocalDescription(String streamId, final SessionDescription sdp) { wsHandler.sendConfiguration(streamId, sdp, "answer"); } } - //check peerConnectionClient null because in very slow devices(emulator), it may cause crash + if (config.videoStartBitrate > 0) { Log.d(TAG, "Set video maximum bitrate: " + config.videoStartBitrate); setVideoMaxBitrate(config.videoStartBitrate); @@ -1642,42 +1109,25 @@ public void onConnected() { public void onPeerConnectionClosed() {} - public void onPeerConnectionStatsReady(RTCStatsReport report) { + public void onPeerConnectionStatsReady(RTCStatsReport report) { this.handler.post(() -> { if (!isError) { - //hudFragment.updateEncoderStatistics(reports); - //Log.i(TAG, "onPeerConnectionStatsReady"); - //Log.i(TAG, report.toString()); - statsCollector.onStatsReport(report, streamMode); + statsCollector.onStatsReport(report); } }); } - - public boolean isStreaming() { - return isStreaming(initialStreamId); - } - public boolean isStreaming(String streamId) { - PeerConnection pc = null; - PeerInfo peerInfo = peers.get(streamId); - if(peerInfo != null) { - pc = peerInfo.peerConnection; - } + PeerConnection pc = getPeerConnectionFor(streamId); return pc != null && pc.iceConnectionState().equals(PeerConnection.IceConnectionState.CONNECTED); } - @Override - public void setMediaProjectionParams(int resultCode, Intent data) { - mediaProjectionPermissionResultCode = resultCode; - mediaProjectionPermissionResultData = data; - } - @Override public void onTakeConfiguration(String streamId, SessionDescription sdp) { this.handler.post(() -> { if (sdp.type == SessionDescription.Type.OFFER) { - if(peers.get(streamId).peerConnection == null) { + PeerConnection pc = getPeerConnectionFor(streamId); + if(pc == null) { createPeerConnection(streamId); } setRemoteDescription(streamId, sdp); @@ -1715,7 +1165,6 @@ public void onPlayFinished(String streamId) { public void onPublishStarted(String streamId) { streamStoppedByUser = false; reconnectionInProgress = false; - streamStarted = true; this.handler.post(() -> { if (config.webRTCListener != null) { @@ -1729,7 +1178,6 @@ public void onPublishStarted(String streamId) { public void onPlayStarted(String streamId) { streamStoppedByUser = false; reconnectionInProgress = false; - streamStarted = true; waitingForPlay = false; this.handler.post(() -> { @@ -1790,10 +1238,10 @@ public void onRemoteIceCandidate(String streamId, IceCandidate candidate) { public void onDisconnected() { this.handler.post(() -> { if (config.webRTCListener != null) { - config.webRTCListener.onDisconnected(initialStreamId); + config.webRTCListener.onDisconnected(); } - if(reconnectionEnabled && !reconnectionInProgress) { + if(config.reconnectionEnabled && !reconnectionInProgress) { setUpReconnection(); } }); @@ -1811,18 +1259,18 @@ public void onTrackList(String[] tracks) { } public void sendPlayOtherTracks(String[] tracks) { - if(autoPlayTracks && !isStreaming() && !waitingForPlay) { + if(autoPlayTracks && !isStreaming(getMultiTrackStreamId()) && !waitingForPlay) { waitingForPlay = true; - init(config.serverUrl, this.initialStreamId, this.streamMode, config.token, this.intent); + init(); //don't send play for its own stream id for (int i = 0; i < tracks.length; i++) { - if(tracks[i].equals(selfStreamId)) { + if(tracks[i].equals(getPublishStreamId())) { tracks[i] = "!"+ tracks[i]; break; } } - play(mainTrackId, config.token, tracks); + play(getMultiTrackStreamId(), tracks); } } @@ -1862,75 +1310,14 @@ public boolean isDataChannelEnabled() { return config.dataChannelEnabled; } - /** - * @deprecated - * use {@link #getStreamInfoList(String)} instead - */ - @Deprecated - @Override - public void getStreamInfoList() { - getStreamInfoList(initialStreamId); - } - public void getStreamInfoList(String streamId) { wsHandler.getStreamInfoList(streamId); } - /** - * @deprecated - * use {@link #forceStreamQuality(String, int)} instead - */ - @Deprecated - @Override - public void forceStreamQuality(int height) { - forceStreamQuality(initialStreamId, height); - } - public void forceStreamQuality(String streamId, int height) { wsHandler.forceStreamQuality(streamId, height); } - @Override - public void setSubscriberParams(String subscriberId, String subscriberCode) { - this.subscriberId = subscriberId; - this.subscriberCode = subscriberCode; - } - - @Override - public void setViewerInfo(String viewerInfo) { - this.viewerInfo = viewerInfo; - } - - @Override - public void setStreamName(String streamName) { - this.streamName = streamName; - } - - public static void insertFrameId(long captureTimeMs) { - captureTimeMsMap.put(captureTimeMs, System.currentTimeMillis()); - } - - public static Map getCaptureTimeMsMapList() { - return captureTimeMsMap; - } - - public boolean isDataChannelOnly() { - return dataChannelOnly; - } - - public void setDataChannelOnly(boolean dataChannelOnly) { - this.dataChannelOnly = dataChannelOnly; - } - - public String getStreamId() { - return initialStreamId; - } - - public void setMainTrackId(String mainTrackId) { - this.mainTrackId = mainTrackId; - } - - class DataChannelInternalObserver implements DataChannel.Observer { private final DataChannel dataChannel; @@ -1940,17 +1327,17 @@ class DataChannelInternalObserver implements DataChannel.Observer { } @Override public void onBufferedAmountChange(long previousAmount) { - if(dataChannelObserver == null) return; + if(config.dataChannelObserver == null) return; Log.d(TAG, "Data channel buffered amount changed: " + dataChannel.label() + ": " + dataChannel.state()); - handler.post(() -> dataChannelObserver.onBufferedAmountChange(previousAmount, dataChannel.label())); + handler.post(() -> config.dataChannelObserver.onBufferedAmountChange(previousAmount, dataChannel.label())); } @Override public void onStateChange() { handler.post(() -> { - if(dataChannelObserver == null || dataChannel == null) return; - //Log.d(TAG, "Data channel state changed: " + dataChannel.label() + ": " + dataChannel.state()); - //TODO: dataChannelObserver.onStateChange(dataChannel.state(), dataChannel.label()); + if(config.dataChannelObserver != null && dataChannel != null) { + config.dataChannelObserver.onStateChange(dataChannel.state(), dataChannel.label()); + } }); } @@ -1963,43 +1350,37 @@ public void onMessage(final DataChannel.Buffer buffer) { boolean binary = buffer.binary; DataChannel.Buffer bufferCopy = new DataChannel.Buffer(copyByteBuffer, binary); handler.post(() -> { - if(dataChannelObserver == null || dataChannel == null) return; + if(config.dataChannelObserver == null || dataChannel == null) return; Log.d(TAG, "Received Message: " + dataChannel.label() + ": " + dataChannel.state()); - dataChannelObserver.onMessage(bufferCopy, dataChannel.label()); + config.dataChannelObserver.onMessage(bufferCopy, dataChannel.label()); }); } - }; - - /* - * sends message via data channel - * @deprecated - * use {@link #sendMessageViaDataChannel(String, DataChannel.Buffer)} instead - */ - @Deprecated - @Override - public void sendMessageViaDataChannel(DataChannel.Buffer buffer) { - sendMessageViaDataChannel(initialStreamId, buffer); } + public void sendMessageViaDataChannel(String streamId, DataChannel.Buffer buffer) { if (isDataChannelEnabled()) { executor.execute(() -> { try { - - boolean success = peers.get(streamId).dataChannel.send(buffer); + PeerInfo peer = peers.get(streamId); + if(peer == null || peer.dataChannel == null) { + reportError(streamId, "Peer not found for sending message via Data Channel"); + return; + } + boolean success = peer.dataChannel.send(buffer); buffer.data.rewind(); - if (dataChannelObserver != null) { + if (config.dataChannelObserver != null) { if (success) { - handler.post(() -> dataChannelObserver.onMessageSent(buffer, true)); + handler.post(() -> config.dataChannelObserver.onMessageSent(buffer, true)); } else { - handler.post(() -> dataChannelObserver.onMessageSent(buffer, false)); + handler.post(() -> config.dataChannelObserver.onMessageSent(buffer, false)); reportError(streamId, "Failed to send the message via Data Channel "); } } } catch (Exception e) { reportError(streamId, "An error occurred when sending the message via Data Channel " + e.getMessage()); - if (dataChannelObserver != null) { + if (config.dataChannelObserver != null) { buffer.data.rewind(); - handler.post(() -> dataChannelObserver.onMessageSent(buffer, false)); + handler.post(() -> config.dataChannelObserver.onMessageSent(buffer, false)); } } }); @@ -2008,23 +1389,21 @@ public void sendMessageViaDataChannel(String streamId, DataChannel.Buffer buffer } } - public void setLocalVideoTrack(@javax.annotation.Nullable VideoTrack localVideoTrack) { - this.localVideoTrack = localVideoTrack; - } - public void changeVideoCapturer(VideoCapturer newVideoCapturer) { try { - this.videoCapturer.stopCapture(); + if(videoCapturer != null) { + videoCapturer.stopCapture(); + } } catch (InterruptedException e) { e.printStackTrace(); } videoCapturerStopped = true; - this.videoCapturer = newVideoCapturer; - this.localVideoTrack = null; + videoCapturer = newVideoCapturer; + localVideoTrack = null; - MediaStreamTrack newTrack = (MediaStreamTrack) createVideoTrack(this.videoCapturer); - if(this.localVideoSender != null) { - this.localVideoSender.setTrack(newTrack, true); + MediaStreamTrack newTrack = (MediaStreamTrack) createVideoTrack(videoCapturer); + if(localVideoSender != null) { + localVideoSender.setTrack(newTrack, true); } } @@ -2043,7 +1422,6 @@ public void createPeerConnection(String streamId) { try { createMediaConstraintsInternal(); createPeerConnectionInternal(streamId); - maybeCreateAndStartRtcEventLog(streamId); } catch (Exception e) { reportError(streamId, "Failed to create peer connection: " + e.getMessage()); throw e; @@ -2051,23 +1429,9 @@ public void createPeerConnection(String streamId) { }); } - private boolean isVideoCallEnabled() { - return config.videoCallEnabled && videoCapturer != null; - } - - private boolean isAudioEnabled() { - return audioCallEnabled; - } - private void createPeerConnectionFactoryInternal(PeerConnectionFactory.Options options) { isError = false; - if (tracing) { - PeerConnectionFactory.startInternalTracingCapture( - Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator - + "webrtc-trace.txt"); - } - // Check if ISAC is used by default. preferIsac = config.audioCodec != null && config.audioCodec.equals(AUDIO_CODEC_ISAC); @@ -2090,10 +1454,6 @@ private void createPeerConnectionFactoryInternal(PeerConnectionFactory.Options o decoderFactory = new SoftwareVideoDecoderFactory(); } - // Disable encryption for loopback calls. - if (loopback) { - options.disableEncryption = true; - } factory = PeerConnectionFactory.builder() .setOptions(options) .setAudioDeviceModule(adm) @@ -2101,35 +1461,31 @@ private void createPeerConnectionFactoryInternal(PeerConnectionFactory.Options o .setVideoDecoderFactory(decoderFactory) .createPeerConnectionFactory(); Log.d(TAG, "Peer connection factory created."); - adm.release(); - } - - AudioDeviceModule createJavaAudioDevice() { - // Enable/disable OpenSL ES playback. - if (!useOpenSLES) { - Log.w(TAG, "External OpenSLES ADM not implemented yet."); - // TODO(magjed): Add support for external OpenSLES ADM. + if (adm != null) { + adm.release(); } + } + public AudioDeviceModule createJavaAudioDevice() { // Set audio record error callbacks. JavaAudioDeviceModule.AudioRecordErrorCallback audioRecordErrorCallback = new JavaAudioDeviceModule.AudioRecordErrorCallback() { @Override public void onWebRtcAudioRecordInitError(String errorMessage) { Log.e(TAG, "onWebRtcAudioRecordInitError: " + errorMessage); - reportError(initialStreamId, errorMessage); + reportError(getPublishStreamId(), errorMessage); } @Override public void onWebRtcAudioRecordStartError( JavaAudioDeviceModule.AudioRecordStartErrorCode errorCode, String errorMessage) { Log.e(TAG, "onWebRtcAudioRecordStartError: " + errorCode + ". " + errorMessage); - reportError(initialStreamId, errorMessage); + reportError(getPublishStreamId(), errorMessage); } @Override public void onWebRtcAudioRecordError(String errorMessage) { Log.e(TAG, "onWebRtcAudioRecordError: " + errorMessage); - reportError(initialStreamId, errorMessage); + reportError(getPublishStreamId(), errorMessage); } }; @@ -2137,20 +1493,20 @@ public void onWebRtcAudioRecordError(String errorMessage) { @Override public void onWebRtcAudioTrackInitError(String errorMessage) { Log.e(TAG, "onWebRtcAudioTrackInitError: " + errorMessage); - reportError(initialStreamId, errorMessage); + reportError(getPublishStreamId(), errorMessage); } @Override public void onWebRtcAudioTrackStartError( JavaAudioDeviceModule.AudioTrackStartErrorCode errorCode, String errorMessage) { Log.e(TAG, "onWebRtcAudioTrackStartError: " + errorCode + ". " + errorMessage); - reportError(initialStreamId,errorMessage); + reportError(getPublishStreamId(),errorMessage); } @Override public void onWebRtcAudioTrackError(String errorMessage) { Log.e(TAG, "onWebRtcAudioTrackError: " + errorMessage); - reportError(initialStreamId, errorMessage); + reportError(getPublishStreamId(), errorMessage); } }; @@ -2182,9 +1538,9 @@ public void onWebRtcAudioTrackStop() { JavaAudioDeviceModule.Builder admBuilder = getADMBuilder(); return admBuilder - .setCustomAudioFeed(customAudioFeed) - .setUseHardwareAcousticEchoCanceler(!disableBuiltInAEC) - .setUseHardwareNoiseSuppressor(!disableBuiltInNS) + .setCustomAudioFeed(config.customAudioFeed) + .setUseHardwareAcousticEchoCanceler(true) + .setUseHardwareNoiseSuppressor(true) .setAudioRecordErrorCallback(audioRecordErrorCallback) .setAudioTrackErrorCallback(audioTrackErrorCallback) .setAudioRecordStateCallback(audioRecordStateCallback) @@ -2193,25 +1549,10 @@ public void onWebRtcAudioTrackStop() { } public JavaAudioDeviceModule.Builder getADMBuilder() { - return JavaAudioDeviceModule.builder(config.context); + return JavaAudioDeviceModule.builder(config.activity); } public void createMediaConstraintsInternal() { - // Create video constraints if video call is enabled. - if (isVideoCallEnabled()) { - // If video resolution is not specified, default to HD. - if (config.videoWidth == 0 || config.videoHeight == 0) { - config.videoWidth = HD_VIDEO_WIDTH; - config.videoHeight = HD_VIDEO_HEIGHT; - } - - // If fps is not specified, default to 30. - if (config.videoFps == 0) { - config.videoFps = 30; - } - Logging.d(TAG, "Capturing format: " + config.videoWidth + "x" + config.videoHeight + "@" + config.videoFps); - } - // Create audio constraints. audioConstraints = new MediaConstraints(); // added for audio performance measurements @@ -2231,7 +1572,7 @@ public void createMediaConstraintsInternal() { sdpMediaConstraints.mandatory.add( new MediaConstraints.KeyValuePair("OfferToReceiveAudio", "true")); sdpMediaConstraints.mandatory.add(new MediaConstraints.KeyValuePair( - "OfferToReceiveVideo", Boolean.toString(isVideoCallEnabled()))); + "OfferToReceiveVideo", Boolean.toString(config.videoCallEnabled))); } public void createPeerConnectionInternal(String streamId) { @@ -2257,45 +1598,35 @@ public void createPeerConnectionInternal(String streamId) { rtcConfig.sdpSemantics = PeerConnection.SdpSemantics.UNIFIED_PLAN; PeerConnection peerConnection = factory.createPeerConnection(rtcConfig, getPCObserver(streamId)); - peers.get(streamId).peerConnection = peerConnection; + if(peerConnection != null) { - isInitiator = false; + PeerInfo peer = peers.get(streamId); + if (peer != null) { + peer.peerConnection = peerConnection; + } else { + Log.e(TAG, "Peer not found for streamId: " + streamId); + } - setWebRTCLogLevel(); + isInitiator = false; - List mediaStreamLabels = Collections.singletonList("ARDAMS"); - if (isVideoCallEnabled()) { - peerConnection.addTrack(createVideoTrack(videoCapturer), mediaStreamLabels); - // We can add the renderers right away because we don't need to wait for an - // answer to get the remote track. - remoteVideoTrack = getRemoteVideoTrack(streamId); - remoteVideoTrack.setEnabled(renderVideo); - for (VideoSink remoteSink : remoteSinks) { - remoteVideoTrack.addSink(remoteSink); - } - } - if (isAudioEnabled()) { - peerConnection.addTrack(createAudioTrack(), mediaStreamLabels); - } + setWebRTCLogLevel(); - if (isVideoCallEnabled()) { - findVideoSender(streamId); - } + List mediaStreamLabels = Collections.singletonList("ARDAMS"); + if (config.videoCallEnabled) { + peerConnection.addTrack(createVideoTrack(videoCapturer), mediaStreamLabels); + } + if (config.audioCallEnabled) { + peerConnection.addTrack(createAudioTrack(), mediaStreamLabels); + } - if (aecDump) { - try { - ParcelFileDescriptor aecDumpFileDescriptor = - ParcelFileDescriptor.open(new File(Environment.getExternalStorageDirectory().getPath() - + File.separator + "Download/audio.aecdump"), - ParcelFileDescriptor.MODE_READ_WRITE | ParcelFileDescriptor.MODE_CREATE - | ParcelFileDescriptor.MODE_TRUNCATE); - factory.startAecDump(aecDumpFileDescriptor.detachFd(), -1); - } catch (IOException e) { - Log.e(TAG, "Can not open aecdump file", e); + if (config.videoCallEnabled) { + findVideoSender(streamId); } - } - Log.d(TAG, "Peer connection created."); + Log.d(TAG, "Peer connection created."); + } else { + Log.e(TAG, "Peer connection is not created"); + } } public void setWebRTCLogLevel() { @@ -2310,37 +1641,37 @@ public PCObserver getPCObserver(String streamId) { } public void initDataChannel(String streamId) { - if (config.dataChannelEnabled && isDataChannelCreator()) { + if (config.dataChannelEnabled) { DataChannel.Init init = new DataChannel.Init(); - init.ordered = dataChannelOrdered; - init.negotiated = dataChannelNegotiated; - init.maxRetransmits = dataChannelMaxRetransmits; - init.maxRetransmitTimeMs = dataChannelMaxRetransmitTimeMs; - init.id = dataChannelId; - init.protocol = dataChannelProtocol == null ? "" : dataChannelProtocol; - DataChannel dataChannel = peers.get(streamId).peerConnection.createDataChannel(streamId, init); - dataChannel.registerObserver(new DataChannelInternalObserver(dataChannel)); - peers.get(streamId).dataChannel = dataChannel; + init.ordered = true; + init.negotiated = false; + init.maxRetransmits = -1; + init.maxRetransmitTimeMs = -1; + init.id = 1; + init.protocol = ""; + PeerInfo peer = peers.get(streamId); + if(peer != null) { + DataChannel dataChannel = peer.peerConnection.createDataChannel(streamId, init); + dataChannel.registerObserver(new DataChannelInternalObserver(dataChannel)); + peer.dataChannel = dataChannel; + } else { + Log.e(TAG, "Peer not found for streamId: " + streamId); + } } } - private boolean isDataChannelCreator() { - return streamMode.equals(IWebRTCClient.MODE_PUBLISH) - || streamMode.equals(IWebRTCClient.MODE_JOIN) - || streamMode.equals(IWebRTCClient.MODE_TRACK_BASED_CONFERENCE); - } - public void setDegradationPreference(String streamId , RtpParameters.DegradationPreference degradationPreference) { + PeerConnection pc = getPeerConnectionFor(streamId); - if (config.context == null || peers.get(streamId) == null || peers.get(streamId).peerConnection == null) { + if (config.activity == null || pc == null) { Log.d(TAG, "Cannot set Degradation Preference"); return; } - PeerConnection peerConnection = peers.get(streamId).peerConnection; - for (RtpSender sender : peerConnection.getSenders()) { - if (sender.track() != null) { - String trackType = sender.track().kind(); + for (RtpSender sender : pc.getSenders()) { + MediaStreamTrack track = sender.track(); + if (track != null) { + String trackType = track.kind(); if (trackType.equals(VIDEO_TRACK_TYPE)) { RtpParameters newParameters = sender.getParameters(); if(newParameters != null) { @@ -2352,44 +1683,12 @@ public void setDegradationPreference(String streamId , RtpParameters.Degradation } } - private File createRtcEventLogOutputFile() { - DateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_hhmm_ss", Locale.getDefault()); - Date date = new Date(); - final String outputFileName = "event_log_" + dateFormat.format(date) + ".log"; - return new File(config.context.getDir(RTCEVENTLOG_OUTPUT_DIR_NAME, config.context.MODE_PRIVATE), outputFileName); - } - - public void maybeCreateAndStartRtcEventLog(String streamId) { - if (config.context == null || peers.get(streamId).peerConnection == null) { - return; - } - if (!enableRtcEventLog) { - Log.d(TAG, "RtcEventLog is disabled."); - return; - } - rtcEventLog = new RtcEventLog(peers.get(streamId).peerConnection); - rtcEventLog.start(createRtcEventLogOutputFile()); - } - public void closeInternal() { Log.d(TAG, "Closing resources."); - if (factory != null && aecDump) { - factory.stopAecDump(); - } - if(statsTimer != null) { statsTimer.cancel(); } - if(trackCheckerTimer != null) { - trackCheckerTimer.cancel(); - trackCheckerTask.cancel(); - } - if (rtcEventLog != null) { - // RtcEventLog should stop before the peer connection is disposed. - rtcEventLog.stop(); - rtcEventLog = null; - } for (Map.Entry entry : peers.entrySet()) { Log.d(TAG, "Closing peer connections for " + entry.getValue().id); PeerConnection peerConnection = entry.getValue().peerConnection; @@ -2423,8 +1722,6 @@ public void closeInternal() { throw new RuntimeException(e); } videoCapturerStopped = true; - //videoCapturer.dispose(); - //videoCapturer = null; } Log.d(TAG, "Closing video source."); if (videoSource != null) { @@ -2447,20 +1744,13 @@ public void closeInternal() { } Log.d(TAG, "Closing peer connection done."); onPeerConnectionClosed(); - PeerConnectionFactory.stopInternalTracingCapture(); - PeerConnectionFactory.shutdownInternalTracer(); - streamStarted = false; - } - - public boolean isHDVideo() { - return isVideoCallEnabled() && config.videoWidth * config.videoHeight >= 1280 * 720; } public void getStats(String streamId) { - if (peers.get(streamId) == null || peers.get(streamId).peerConnection == null) { - return; + PeerConnection pc = getPeerConnectionFor(streamId); + if (pc != null) { + pc.getStats(this::onPeerConnectionStatsReady); } - peers.get(streamId).peerConnection.getStats(this::onPeerConnectionStatsReady); } public void enableStatsEvents(String streamId, boolean enable, int periodMs) { @@ -2482,12 +1772,11 @@ public void run() { } public void setAudioEnabled(final boolean enable) { - this.audioCallEnabled = enable; + config.audioCallEnabled = enable; executor.execute(() -> { - enableAudio = enable; if (localAudioTrack != null) { - localAudioTrack.setEnabled(enableAudio); + localAudioTrack.setEnabled(enable); } }); } @@ -2504,15 +1793,12 @@ public void setVideoEnabled(final boolean enable) { } localVideoTrack.setEnabled(renderVideo); } - if (remoteVideoTrack != null) { - remoteVideoTrack.setEnabled(renderVideo); - } }); } public void createOffer(String streamId) { executor.execute(() -> { - PeerConnection pc = peers.get(streamId).peerConnection; + PeerConnection pc = getPeerConnectionFor(streamId); if (pc != null && !isError) { Log.d(TAG, "PC Create OFFER"); isInitiator = true; @@ -2524,7 +1810,7 @@ public void createOffer(String streamId) { public void createAnswer(String streamId) { executor.execute(() -> { - PeerConnection pc = peers.get(streamId).peerConnection; + PeerConnection pc = getPeerConnectionFor(streamId); if (pc != null && !isError) { Log.d(TAG, "PC create ANSWER"); isInitiator = false; @@ -2535,7 +1821,7 @@ public void createAnswer(String streamId) { public void addRemoteIceCandidate(String streamId, final IceCandidate candidate) { executor.execute(() -> { - PeerConnection pc = peers.get(streamId).peerConnection; + PeerConnection pc = getPeerConnectionFor(streamId); if (pc != null && !isError) { if (queuedRemoteCandidates != null) { queuedRemoteCandidates.add(candidate); @@ -2557,20 +1843,19 @@ public void onAddFailure(String error) { public void removeRemoteIceCandidates(String streamId, final IceCandidate[] candidates) { executor.execute(() -> { - PeerConnection pc = peers.get(streamId).peerConnection; - if (pc == null || isError) { - return; + PeerConnection pc = getPeerConnectionFor(streamId); + if (pc != null && !isError) { + // Drain the queued remote candidates if there is any so that + // they are processed in the proper order. + drainCandidates(streamId); + pc.removeIceCandidates(candidates); } - // Drain the queued remote candidates if there is any so that - // they are processed in the proper order. - drainCandidates(streamId); - pc.removeIceCandidates(candidates); }); } public void setRemoteDescription(String streamId, final SessionDescription desc) { executor.execute(() -> { - PeerConnection pc = peers.get(streamId).peerConnection; + PeerConnection pc = getPeerConnectionFor(streamId); if (pc == null || isError) { return; } @@ -2578,11 +1863,14 @@ public void setRemoteDescription(String streamId, final SessionDescription desc) if (preferIsac) { sdp = preferCodec(sdp, AUDIO_CODEC_ISAC, true); } - if (isVideoCallEnabled()) { + if (config.videoCallEnabled) { sdp = preferCodec(sdp, getSdpVideoCodecName(config.videoCodec), false); } + if (config.videoStartBitrate > 0) { + sdp = setStartBitrate(config.videoCodec, true, sdp, config.videoStartBitrate); + } if (config.audioStartBitrate > 0) { - sdp = setStartBitrate(AUDIO_CODEC_OPUS, false, sdp, config.audioStartBitrate); + sdp = setStartBitrate(config.audioCodec, false, sdp, config.audioStartBitrate); } Log.d(TAG, "Set remote SDP."); SessionDescription sdpRemote = new SessionDescription(desc.type, sdp); @@ -2630,6 +1918,7 @@ public void setVideoMaxBitrate(@androidx.annotation.Nullable final Integer maxBi for (RtpParameters.Encoding encoding : parameters.encodings) { // Null value means no limit. encoding.maxBitrateBps = maxBitrateKbps == null ? null : maxBitrateKbps * BPS_IN_KBPS; + encoding.minBitrateBps = maxBitrateKbps == null ? null : maxBitrateKbps * BPS_IN_KBPS/2; } if (!localVideoSender.setParameters(parameters)) { Log.e(TAG, "RtpSender.setParameters failed."); @@ -2640,66 +1929,46 @@ public void setVideoMaxBitrate(@androidx.annotation.Nullable final Integer maxBi @androidx.annotation.Nullable public AudioTrack createAudioTrack() { - if (localAudioTrack == null) { + if (localAudioTrack == null && factory != null) { audioSource = factory.createAudioSource(audioConstraints); localAudioTrack = factory.createAudioTrack(AUDIO_TRACK_ID, audioSource); - localAudioTrack.setEnabled(enableAudio); + localAudioTrack.setEnabled(config.audioCallEnabled); } return localAudioTrack; } @androidx.annotation.Nullable private VideoTrack createVideoTrack(VideoCapturer capturer) { - if (localVideoTrack == null && capturer != null) { - surfaceTextureHelper = - SurfaceTextureHelper.create("CaptureThread", eglBase.getEglBaseContext()); + if (localVideoTrack == null && capturer != null && factory != null) { + surfaceTextureHelper = SurfaceTextureHelper.create("CaptureThread", eglBase.getEglBaseContext()); videoSource = factory.createVideoSource(capturer.isScreencast()); - capturer.initialize(surfaceTextureHelper, config.context, videoSource.getCapturerObserver()); + capturer.initialize(surfaceTextureHelper, config.activity, videoSource.getCapturerObserver()); capturer.startCapture(config.videoWidth, config.videoHeight, config.videoFps); localVideoTrack = factory.createVideoTrack(VIDEO_TRACK_ID, videoSource); - localVideoTrack.setEnabled(renderVideo); - localVideoTrack.addSink(localProxyVideoSink); + //localVideoTrack.setEnabled(renderVideo); + localVideoTrack.addSink(localVideoSink); videoCapturerStopped = false; } return localVideoTrack; } private void findVideoSender(String streamId) { - for (RtpSender sender : peers.get(streamId).peerConnection.getSenders()) { - if (sender.track() != null) { - String trackType = sender.track().kind(); - if (trackType.equals(VIDEO_TRACK_TYPE)) { - Log.d(TAG, "Found video sender."); - localVideoSender = sender; + PeerConnection pc = getPeerConnectionFor(streamId); + if (pc != null) { + for (RtpSender sender : pc.getSenders()) { + MediaStreamTrack track = sender.track(); + if (track != null) { + String trackType = track.kind(); + if (trackType.equals(VIDEO_TRACK_TYPE)) { + Log.d(TAG, "Found video sender."); + localVideoSender = sender; + } } } } } - private List getRemoteVideoTrackList(String streamId) { - List videoTrackList = new ArrayList<>(); - for (RtpTransceiver transceiver : peers.get(streamId).peerConnection.getTransceivers()) - { - MediaStreamTrack track = transceiver.getReceiver().track(); - if (track instanceof VideoTrack) { - videoTrackList.add((VideoTrack)track); - } - } - return videoTrackList; - } - - // Returns the remote VideoTrack, assuming there is only one. - private @androidx.annotation.Nullable VideoTrack getRemoteVideoTrack(String streamId) { - for (RtpTransceiver transceiver : peers.get(streamId).peerConnection.getTransceivers()) { - MediaStreamTrack track = transceiver.getReceiver().track(); - if (track instanceof VideoTrack) { - return (VideoTrack) track; - } - } - return null; - } - private static String getSdpVideoCodecName(String codec) { switch (codec) { case VIDEO_CODEC_VP8: @@ -2731,15 +2000,14 @@ private static String getFieldTrials(Boolean videoFlexfecEnabled, boolean disabl } @SuppressWarnings("StringSplitter") - private static String setStartBitrate( - String codec, boolean isVideoCodec, String sdp, int bitrateKbps) { + private static String setStartBitrate(String codec, boolean isVideoCodec, String sdp, int bitrateKbps) { String[] lines = sdp.split("\r\n"); int rtpmapLineIndex = -1; boolean sdpFormatUpdated = false; String codecRtpMap = null; // Search for codec rtpmap in format // a=rtpmap: / [/] - String regex = "^a=rtpmap:(\\d+) " + codec + "(/\\d+)+[\r]?$"; + String regex = "^a=rtpmap:(\\d+) " + codec + "(/\\d+)+''?$"; Pattern codecPattern = Pattern.compile(regex); for (int i = 0; i < lines.length; i++) { Matcher codecMatcher = codecPattern.matcher(lines[i]); @@ -2757,7 +2025,7 @@ private static String setStartBitrate( // Check if a=fmtp string already exist in remote SDP for this codec and // update it with new bitrate parameter. - regex = "^a=fmtp:" + codecRtpMap + " \\w+=\\d+.*[\r]?$"; + regex = "^a=fmtp:" + codecRtpMap + " \\w+=\\d+.*''?$"; codecPattern = Pattern.compile(regex); for (int i = 0; i < lines.length; i++) { Matcher codecMatcher = codecPattern.matcher(lines[i]); @@ -2853,7 +2121,7 @@ private static String preferCodec(String sdp, String codec, boolean isAudio) { // range 96-127, but they are stored as strings here. final List codecPayloadTypes = new ArrayList<>(); // a=rtpmap: / [/] - final Pattern codecPattern = Pattern.compile("^a=rtpmap:(\\d+) " + codec + "(/\\d+)+[\r]?$"); + final Pattern codecPattern = Pattern.compile("^a=rtpmap:(\\d+) " + codec + "(/\\d+)+''?$"); for (String line : lines) { Matcher codecMatcher = codecPattern.matcher(line); if (codecMatcher.matches()) { @@ -2878,26 +2146,38 @@ private void drainCandidates(String streamId) { if (queuedRemoteCandidates != null) { Log.d(TAG, "Add " + queuedRemoteCandidates.size() + " remote candidates"); for (IceCandidate candidate : queuedRemoteCandidates) { - peers.get(streamId).peerConnection.addIceCandidate(candidate, new AddIceObserver() { - @Override - public void onAddSuccess() { - Log.d(TAG, "Candidate " + candidate + " successfully added."); - } - @Override - public void onAddFailure(String error) { - Log.d(TAG, "Candidate " + candidate + " addition failed: " + error); - } - }); + PeerConnection pc = getPeerConnectionFor(streamId); + if(pc != null) { + pc.addIceCandidate(candidate, new AddIceObserver() { + @Override + public void onAddSuccess() { + Log.d(TAG, "Candidate " + candidate + " successfully added."); + } + + @Override + public void onAddFailure(String error) { + Log.d(TAG, "Candidate " + candidate + " addition failed: " + error); + } + }); + } } queuedRemoteCandidates = null; } } + private PeerConnection getPeerConnectionFor(String streamId) { + PeerInfo peer = peers.get(streamId); + if(peer != null) { + return peer.peerConnection; + } + return null; + } + private void switchCameraInternal() { if (videoCapturer instanceof CameraVideoCapturer) { - if (!isVideoCallEnabled() || isError) { + if (!config.videoCallEnabled || isError) { Log.e(TAG, - "Failed to switch camera. Video: " + isVideoCallEnabled() + ". Error : " + isError); + "Failed to switch camera. Video: " + config.videoCallEnabled + ". Error : " + isError); return; // No video is sent or only one camera is available or error happened. } Log.d(TAG, "Switch camera"); @@ -2909,9 +2189,9 @@ private void switchCameraInternal() { } private void changeCaptureFormatInternal(int width, int height, int framerate) { - if (!isVideoCallEnabled() || isError) { + if (!config.videoCallEnabled || videoSource == null || isError) { Log.e(TAG, - "Failed to change capture format. Video: " + isVideoCallEnabled() + "Failed to change capture format. Video: " + config.videoCallEnabled + ". Error : " + isError); return; } @@ -2919,46 +2199,14 @@ private void changeCaptureFormatInternal(int width, int height, int framerate) { videoSource.adaptOutputFormat(width, height, framerate); } - public void setDataChannelObserver(@androidx.annotation.Nullable IDataChannelObserver dataChannelObserver) { - this.dataChannelObserver = dataChannelObserver; - } - - public void setStreamMode(String streamMode) { - this.streamMode = streamMode; - } - - public String getStreamMode() { - return streamMode; - } - - public void setToken(String token) { - config.token = token; - } - - public void setVideoCallEnabled(boolean videoCallEnabled) { - config.videoCallEnabled = videoCallEnabled; - } - - public boolean getVideoCallEnabled() { - return config.videoCallEnabled; - } - - public boolean getAudioCallEnabled() { - return audioCallEnabled; - } - - public String getCurrentSource() { - return currentSource; - } - public void addPeerConnection(String streamId, @androidx.annotation.Nullable PeerConnection peerConnection) { - PeerInfo peerInfo = new PeerInfo(streamId, streamMode); + PeerInfo peerInfo = new PeerInfo(streamId, Mode.PLAY); peerInfo.peerConnection = peerConnection; this.peers.put(streamId, peerInfo); } - public List getRemoteSinks() { - return remoteSinks; + public List getRemoteVideoSinks() { + return remoteVideoSinks; } public void setInitiator(boolean initiator) { @@ -2969,18 +2217,6 @@ public void setHandler(Handler handler) { this.handler = handler; } - public void setCustomCapturerEnabled(boolean customCapturerEnabled) { - this.customCapturerEnabled = customCapturerEnabled; - } - - public boolean isReconnectionEnabled() { - return reconnectionEnabled; - } - - public void setReconnectionEnabled(boolean reconnectionEnabled) { - this.reconnectionEnabled = reconnectionEnabled; - } - public void setAutoPlayTracks(boolean autoPlayTracks) { this.autoPlayTracks = autoPlayTracks; } @@ -2989,36 +2225,11 @@ public boolean isReconnectionInProgress() { return reconnectionInProgress; } - public void setCheckStreamIdValidity(boolean checkStreamIdValidity) { - this.checkStreamIdValidity = checkStreamIdValidity; - } - public boolean isStreamStarted() { - return streamStarted; - } - - public void setRenderersProvidedAtStart(boolean renderersProvidedAtStart) { - this.renderersProvidedAtStart = renderersProvidedAtStart; - } - - public void setInputSampleRate(int sampleRate) { - this.inputSampleRate = sampleRate; - } - - - public void setStereoInput(boolean stereoInput) { - this.stereoInput = stereoInput; - } - - public void setAudioInputFormat(int audioFormat) { - this.audioInputFormat = audioFormat; - } - - public void setCustomAudioFeed(boolean customAudioFeed) { - this.customAudioFeed = customAudioFeed; - } - public CustomWebRtcAudioRecord getAudioInput() { - return adm.getAudioInput(); + if(adm != null) { + return adm.getAudioInput(); + } + return null; } public VideoCapturer getVideoCapturer() { @@ -3029,6 +2240,7 @@ public void setRemoveVideoRotationExtention(boolean removeVideoRotationExtention this.removeVideoRotationExtention = removeVideoRotationExtention; } + @androidx.annotation.Nullable public SessionDescription getLocalDescription() { return localDescription; } @@ -3048,4 +2260,8 @@ public void setFactory(@androidx.annotation.Nullable PeerConnectionFactory facto public void setQueuedRemoteCandidates(@androidx.annotation.Nullable List queuedRemoteCandidates) { this.queuedRemoteCandidates = queuedRemoteCandidates; } + + public void setPermissionsHandlerForTest(PermissionsHandler permissionsHandler) { + this.permissionsHandler = permissionsHandler; + } } diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/WebRTCClientBuilder.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClientBuilder.java similarity index 56% rename from webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/WebRTCClientBuilder.java rename to webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClientBuilder.java index fc2dcfc9..1ccce926 100644 --- a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/WebRTCClientBuilder.java +++ b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClientBuilder.java @@ -1,18 +1,25 @@ -package io.antmedia.webrtcandroidframework; +package io.antmedia.webrtcandroidframework.core; -import android.content.Context; +import android.app.Activity; +import org.webrtc.RendererCommon; import org.webrtc.SurfaceViewRenderer; +import java.util.Arrays; + +import io.antmedia.webrtcandroidframework.api.IDataChannelObserver; +import io.antmedia.webrtcandroidframework.api.IWebRTCListener; + public class WebRTCClientBuilder { private WebRTCClientConfig webRTCClientConfig; - WebRTCClientBuilder() { + public WebRTCClientBuilder() { webRTCClientConfig = new WebRTCClientConfig(); } public WebRTCClient build() { + webRTCClientConfig.webRTCListener.setConfig(webRTCClientConfig); return new WebRTCClient(webRTCClientConfig); } @@ -81,13 +88,13 @@ public WebRTCClientBuilder setAudioCodec(String audioCodec) { return this; } - public WebRTCClientBuilder setFullScreenRenderer(SurfaceViewRenderer fullScreenRenderer) { - webRTCClientConfig.fullScreenRenderer = fullScreenRenderer; + public WebRTCClientBuilder setLocalVideoRenderer(SurfaceViewRenderer localVideoRenderer) { + webRTCClientConfig.localVideoRenderer = localVideoRenderer; return this; } - public WebRTCClientBuilder setPipRenderer(SurfaceViewRenderer pipRenderer) { - webRTCClientConfig.pipRenderer = pipRenderer; + public WebRTCClientBuilder addRemoteVideoRenderer (SurfaceViewRenderer ... remoteVideoRenderers) { + webRTCClientConfig.remoteVideoRenderers.addAll(Arrays.asList(remoteVideoRenderers)); return this; } @@ -96,8 +103,43 @@ public WebRTCClientBuilder setWebRTCListener(IWebRTCListener webRTCListener) { return this; } - public WebRTCClientBuilder setContext(Context context) { - webRTCClientConfig.context = context; + public WebRTCClientBuilder setDataChannelObserver(IDataChannelObserver dataChannelObserver) { + webRTCClientConfig.dataChannelObserver = dataChannelObserver; + return this; + } + + public WebRTCClientBuilder setActivity(Activity activity) { + webRTCClientConfig.activity = activity; + return this; + } + + public WebRTCClientBuilder setCustomVideoCapturerEnabled(boolean b) { + webRTCClientConfig.customVideoCapturerEnabled = b; + return this; + } + + public WebRTCClientBuilder setInitiateBeforeStream(boolean b) { + webRTCClientConfig.initiateBeforeStream = b; + return this; + } + + public WebRTCClientBuilder setCustomAudioFeed(boolean b) { + webRTCClientConfig.customAudioFeed = b; + return this; + } + + public WebRTCClientBuilder setScalingType(RendererCommon.ScalingType scaleAspectFit) { + webRTCClientConfig.scalingType = scaleAspectFit; + return this; + } + + public WebRTCClientBuilder setStunServerUri(String stunServerUri) { + webRTCClientConfig.stunServerUri = stunServerUri; + return this; + } + + public WebRTCClientBuilder setReconnectionEnabled(boolean b) { + webRTCClientConfig.reconnectionEnabled = b; return this; } } diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClientConfig.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClientConfig.java new file mode 100644 index 00000000..c69436c4 --- /dev/null +++ b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/core/WebRTCClientConfig.java @@ -0,0 +1,140 @@ +package io.antmedia.webrtcandroidframework.core; + +import android.app.Activity; +import android.media.projection.MediaProjection; + +import org.webrtc.RendererCommon; +import org.webrtc.SurfaceViewRenderer; + +import java.util.ArrayList; + +import io.antmedia.webrtcandroidframework.api.DefaultDataChannelObserver; +import io.antmedia.webrtcandroidframework.api.DefaultWebRTCListener; +import io.antmedia.webrtcandroidframework.api.IDataChannelObserver; +import io.antmedia.webrtcandroidframework.api.IWebRTCClient; +import io.antmedia.webrtcandroidframework.api.IWebRTCListener; + +public class WebRTCClientConfig { + + /* + * Renderer for local video + */ + public SurfaceViewRenderer localVideoRenderer; + + /* + * Renderers for remote video + */ + public ArrayList remoteVideoRenderers = new ArrayList<>(); + + /* + * websocket connection url to Ant Media Server + * ex. wss://{AMS URL}:5443/{AppName}/websocket + */ + public String serverUrl; + + /* + * stream id for stream + */ + public String streamId; + + /* + * token for stream + */ + public String token; + + /* + * Flag indicating whether video call is enabled + */ + public boolean videoCallEnabled = true; + + /* + * Flag indicating whether audio call is enabled + */ + public boolean audioCallEnabled = true; + + /* + * Flag indicating whether data channel is enabled + */ + public boolean dataChannelEnabled = true; + + /* + * Width of the video in pixels + */ + public int videoWidth = 720; + + /* + * Height of the video in pixels + */ + public int videoHeight = 1280; + + /* + * Frames per second for the video + */ + public int videoFps = 30; + + /* + * Initial bitrate for video transmission + */ + public int videoStartBitrate = 1700; + + /* + * Codec used for video encoding and decoding, default VP8 + */ + public String videoCodec = "VP8"; + + /* + * Flag for hardware codec acceleration + */ + public boolean hwCodecAcceleration = true; + + /* + * Flag indicating whether flexible forward error correction (FlexFEC) is enabled for video + */ + public boolean videoFlexfecEnabled = false; + + /* + * Initial bitrate for audio transmission + */ + public int audioStartBitrate = 32; + + /* + * Codec used for audio encoding and decoding + */ + public String audioCodec = "OPUS"; + + /* + * Flag indicating whether audio processing is disabled + */ + public boolean noAudioProcessing = false; + + /* + * WebRTC listener for callbacks + */ + public IWebRTCListener webRTCListener = new DefaultWebRTCListener(); + + /* + * Running activity + */ + public Activity activity; + + /* + * Flag indicating whether screencapture is enabled + */ + public boolean screencaptureEnabled = false; + + public IDataChannelObserver dataChannelObserver = new DefaultDataChannelObserver(); + + /* + * MediaProjectionManager for screencapture + */ + public MediaProjection mediaProjection; + public IWebRTCClient.StreamSource videoSource; + public boolean customVideoCapturerEnabled; + public boolean initiateBeforeStream; + public boolean customAudioFeed; + public RendererCommon.ScalingType scalingType = RendererCommon.ScalingType.SCALE_ASPECT_FIT; + + public String stunServerUri = "stun:stun1.l.google.com:19302"; + public boolean reconnectionEnabled = true; + public boolean disableWebRtcAGCAndHPF = false; +} diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/AntMediaSignallingEvents.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/websocket/AntMediaSignallingEvents.java similarity index 96% rename from webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/AntMediaSignallingEvents.java rename to webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/websocket/AntMediaSignallingEvents.java index c94132ce..dacbb020 100644 --- a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/AntMediaSignallingEvents.java +++ b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/websocket/AntMediaSignallingEvents.java @@ -1,4 +1,4 @@ -package io.antmedia.webrtcandroidframework; +package io.antmedia.webrtcandroidframework.websocket; import org.webrtc.IceCandidate; @@ -6,6 +6,8 @@ import java.util.ArrayList; +import io.antmedia.webrtcandroidframework.core.StreamInfo; + public interface AntMediaSignallingEvents { /** diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/WebSocketConstants.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/websocket/WebSocketConstants.java similarity index 99% rename from webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/WebSocketConstants.java rename to webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/websocket/WebSocketConstants.java index 267f4442..35ae16ba 100644 --- a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/WebSocketConstants.java +++ b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/websocket/WebSocketConstants.java @@ -1,4 +1,4 @@ -package io.antmedia.webrtcandroidframework; +package io.antmedia.webrtcandroidframework.websocket; public class WebSocketConstants { diff --git a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/WebSocketHandler.java b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/websocket/WebSocketHandler.java similarity index 98% rename from webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/WebSocketHandler.java rename to webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/websocket/WebSocketHandler.java index 22955578..a17a87bf 100644 --- a/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/WebSocketHandler.java +++ b/webrtc-android-framework/src/main/java/io/antmedia/webrtcandroidframework/websocket/WebSocketHandler.java @@ -1,4 +1,4 @@ -package io.antmedia.webrtcandroidframework; +package io.antmedia.webrtcandroidframework.websocket; import android.os.Handler; import android.util.Log; @@ -19,9 +19,10 @@ import de.tavendo.autobahn.WebSocket; import de.tavendo.autobahn.WebSocketConnection; import de.tavendo.autobahn.WebSocketException; +import io.antmedia.webrtcandroidframework.core.StreamInfo; -import static io.antmedia.webrtcandroidframework.WebSocketConstants.DEFINITION; -import static io.antmedia.webrtcandroidframework.WebSocketConstants.NOTIFICATION_COMMAND; +import static io.antmedia.webrtcandroidframework.websocket.WebSocketConstants.DEFINITION; +import static io.antmedia.webrtcandroidframework.websocket.WebSocketConstants.NOTIFICATION_COMMAND; public class WebSocketHandler implements WebSocket.WebSocketConnectionObserver { private static final String TAG = "WebSocketHandler"; diff --git a/webrtc-android-framework/src/main/java/org/webrtc/VideoSource.java b/webrtc-android-framework/src/main/java/org/webrtc/VideoSource.java index 2e22d1a2..9306466e 100644 --- a/webrtc-android-framework/src/main/java/org/webrtc/VideoSource.java +++ b/webrtc-android-framework/src/main/java/org/webrtc/VideoSource.java @@ -68,6 +68,7 @@ public void onFrameCaptured(VideoFrame frame) { } } + VideoFrame adaptedFrame = VideoProcessor.applyFrameAdaptationParameters(frame, parameters); if (adaptedFrame != null) { nativeAndroidVideoTrackSource.onFrameCaptured(adaptedFrame); diff --git a/webrtc-android-framework/src/test/java/io/antmedia/webrtcandroidframework/CustomVideoCapturerTest.java b/webrtc-android-framework/src/test/java/io/antmedia/webrtcandroidframework/CustomVideoCapturerTest.java index 5f2f45ee..8a8a85a9 100644 --- a/webrtc-android-framework/src/test/java/io/antmedia/webrtcandroidframework/CustomVideoCapturerTest.java +++ b/webrtc-android-framework/src/test/java/io/antmedia/webrtcandroidframework/CustomVideoCapturerTest.java @@ -14,6 +14,8 @@ import static org.junit.Assert.assertFalse; import static org.mockito.Mockito.*; +import io.antmedia.webrtcandroidframework.core.CustomVideoCapturer; + public class CustomVideoCapturerTest { @Mock private CapturerObserver capturerObserver; diff --git a/webrtc-android-framework/src/test/java/io/antmedia/webrtcandroidframework/MP3PublisherTest.java b/webrtc-android-framework/src/test/java/io/antmedia/webrtcandroidframework/MP3PublisherTest.java deleted file mode 100644 index 9c87304d..00000000 --- a/webrtc-android-framework/src/test/java/io/antmedia/webrtcandroidframework/MP3PublisherTest.java +++ /dev/null @@ -1,139 +0,0 @@ -package io.antmedia.webrtcandroidframework; - -import android.app.Activity; -import android.media.MediaCodec; -import android.media.MediaExtractor; -import android.media.MediaFormat; - -import androidx.annotation.NonNull; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.MockitoAnnotations; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; -import org.webrtc.audio.CustomWebRtcAudioRecord; - -import java.io.FileDescriptor; -import java.io.FileInputStream; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.util.Timer; -import java.util.TimerTask; -import java.util.concurrent.TimeUnit; - -import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.*; - -public class MP3PublisherTest { - - @Mock - private WebRTCClient mockWebRTCClient; - - @Mock - private Activity mockActivity; - - @Mock - private CustomWebRtcAudioRecord mockAudioInput; - - @Mock - private MediaCodec mockMediaCodec; - - @Mock - private MediaExtractor mockMediaExtractor; - - @Mock - private MediaFormat mockMediaFormat; - - private MP3Publisher mp3Publisher; - private String testFilePath; - - @Before - public void setUp() throws IOException { - MockitoAnnotations.initMocks(this); - testFilePath = "test_audio.mp3"; - mp3Publisher = spy(new MP3Publisher(mockWebRTCClient, mockActivity, testFilePath)); - when(mockWebRTCClient.getAudioInput()).thenReturn(mockAudioInput); - - when(mockMediaExtractor.getTrackFormat(anyInt())).thenReturn(mockMediaFormat); - when(mockMediaExtractor.getTrackCount()).thenReturn(1); - when(mockMediaExtractor.getSampleTrackIndex()).thenReturn(0); - doNothing().when(mockMediaExtractor).selectTrack(anyInt()); - doNothing().when(mockMediaExtractor).release(); - - // Mock the MediaCodec behavior - when(mockMediaCodec.getOutputFormat()).thenReturn(mockMediaFormat); - doReturn(mockMediaCodec).when(mp3Publisher).getCodecByName(any()); - doNothing().when(mockMediaCodec).configure(any(MediaFormat.class), any(), any(), anyInt()); - doNothing().when(mockMediaCodec).start(); - doReturn(0).when(mockMediaCodec).dequeueInputBuffer(anyLong()); - doReturn(0).when(mockMediaCodec).dequeueOutputBuffer(any(MediaCodec.BufferInfo.class), anyLong()); - doNothing().when(mockMediaCodec).release(); - doNothing().when(mockMediaCodec).stop(); - doReturn(ByteBuffer.allocate(1024)).when(mockMediaCodec).getOutputBuffer(anyInt()); - - // Set the mock MediaCodec in MP3Publisher - doReturn(mockMediaCodec).when(mp3Publisher).getMediaCodec(any(MediaFormat.class)); - - when(mockMediaFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT)).thenReturn(1); - when(mockMediaFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE)).thenReturn(48000); - - when(mockAudioInput.getBufferByteLength()).thenReturn(1024); - - ByteBuffer inputBuffers[] = new ByteBuffer[] {ByteBuffer.allocate(2048)}; - when(mockMediaCodec.getInputBuffers()).thenReturn(inputBuffers); - - ByteBuffer outputBuffers[] = new ByteBuffer[] {ByteBuffer.allocate(2048)}; - when(mockMediaCodec.getOutputBuffers()).thenReturn(outputBuffers); - - when(mockMediaExtractor.readSampleData(any(), anyInt())).thenAnswer(new Answer() { - private int count = 0; - - @Override - public Integer answer(InvocationOnMock invocation) { - return count++ < 10 ? 100 : -1; - } - }); - } - - @After - public void tearDown() { - mp3Publisher = null; - } - - @Test - public void testStartStreaming() { - mp3Publisher.startStreaming(); - verify(mockWebRTCClient, timeout(1000)).getAudioInput(); - } - - @Test - public void testReadAndPublishFile() throws Exception { - // Mock raw audio data and its buffer - int bufferLength = 1024; // This should match the buffer length used in the MP3Publisher class - byte[] rawAudioData = new byte[bufferLength]; - ByteBuffer rawAudioBuffer = ByteBuffer.wrap(rawAudioData); - when(mockMediaCodec.dequeueOutputBuffer(any(MediaCodec.BufferInfo.class), anyLong())).thenReturn(0); - when(mockMediaCodec.getOutputBuffer(0)).thenReturn(rawAudioBuffer); - - doReturn(ByteBuffer.allocate(2048)).when(mp3Publisher).getRawAudioByteBuffer(anyInt()); - - // Run the readAndPublishFile method - mp3Publisher.readAndPublishFile(mockMediaExtractor, mockMediaFormat, mockMediaCodec); - - // Verify that the output buffer is used to push audio data - verify(mockAudioInput, atLeastOnce()).pushAudio(eq(rawAudioData), eq(bufferLength)); - - // Verify that the MediaCodec methods are called as expected - verify(mockMediaExtractor, atLeastOnce()).readSampleData(any(ByteBuffer.class), anyInt()); - verify(mockMediaExtractor, atLeastOnce()).getSampleTime(); - verify(mockMediaCodec, atLeastOnce()).dequeueInputBuffer(anyLong()); - verify(mockMediaCodec, atLeastOnce()).queueInputBuffer(anyInt(), anyInt(), anyInt(), anyLong(), anyInt()); - verify(mockMediaCodec, atLeastOnce()).dequeueOutputBuffer(any(MediaCodec.BufferInfo.class), anyLong()); - verify(mockMediaCodec, atLeastOnce()).releaseOutputBuffer(anyInt(), eq(false)); - } - -} diff --git a/webrtc-android-framework/src/test/java/io/antmedia/webrtcandroidframework/MultitrackConferenceManagerTest.java b/webrtc-android-framework/src/test/java/io/antmedia/webrtcandroidframework/MultitrackConferenceManagerTest.java deleted file mode 100644 index 680ada6c..00000000 --- a/webrtc-android-framework/src/test/java/io/antmedia/webrtcandroidframework/MultitrackConferenceManagerTest.java +++ /dev/null @@ -1,133 +0,0 @@ -package io.antmedia.webrtcandroidframework; - -import static org.junit.Assert.assertFalse; -import static org.mockito.Mockito.*; - -import android.content.Context; -import android.content.Intent; -import android.os.Handler; -import android.view.View; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.webrtc.DataChannel; -import org.webrtc.IceCandidate; -import org.webrtc.SessionDescription; -import org.webrtc.SurfaceViewRenderer; -import org.webrtc.VideoTrack; - -import java.nio.ByteBuffer; -import java.util.ArrayList; - -import io.antmedia.webrtcandroidframework.*; - -public class MultitrackConferenceManagerTest { - - @Mock - private Context context; - - @Mock - private IWebRTCListener webRTCListener; - - @Mock - private Intent intent; - - @Mock - private SurfaceViewRenderer publishViewRenderer; - - private ArrayList playViewRenderers = new ArrayList(); - - @Mock - private IDataChannelObserver dataChannelObserver; - - @Before - public void setup() { - MockitoAnnotations.initMocks(this); - } - - @Test - public void testJoinTheConference() { - // Create a MultitrackConferenceManager instance - MultitrackConferenceManager conferenceManager = new MultitrackConferenceManager( - context, - webRTCListener, - intent, - "serverUrl", - "roomName", - publishViewRenderer, - playViewRenderers, - "streamId", - dataChannelObserver - ); - - // Mock the WebSocketHandler - WebSocketHandler wsHandler = mock(WebSocketHandler.class); - conferenceManager.setWsHandler(wsHandler); - - // Call the joinTheConference method - conferenceManager.joinTheConference(); - - // Verify that the joinToConferenceRoom method of WebSocketHandler is called with the correct arguments - verify(wsHandler).joinToConferenceRoom("roomName", "streamId"); - } - - @Test - public void testLeaveFromConference() { - // Create a MultitrackConferenceManager instance - MultitrackConferenceManager conferenceManager = new MultitrackConferenceManager( - context, - webRTCListener, - intent, - "ws:/serverUrl", - "roomName", - publishViewRenderer, - playViewRenderers, - "streamId", - dataChannelObserver - ); - - // Mock the WebSocketHandler - WebSocketHandler wsHandler = mock(WebSocketHandler.class); - conferenceManager.setWsHandler(wsHandler); - - // Call the leaveFromConference method - conferenceManager.leaveFromConference(); - - // Verify that the leaveFromTheConferenceRoom method of WebSocketHandler is called with the correct argument - verify(wsHandler).leaveFromTheConferenceRoom("roomName"); - - // Verify that the joined and playMessageSent flags are updated correctly - assertFalse(conferenceManager.isJoined()); - assertFalse(conferenceManager.isPlayMessageSent()); - } - - @Test - public void testSwitchCamera() { - // Create a MultitrackConferenceManager instance - MultitrackConferenceManager conferenceManager = new MultitrackConferenceManager( - context, - webRTCListener, - intent, - "serverUrl", - "roomName", - publishViewRenderer, - playViewRenderers, - "streamId", - dataChannelObserver - ); - - // Mock the publishWebRTCClient - WebRTCClient publishWebRTCClient = mock(WebRTCClient.class); - conferenceManager.setPublishWebRTCClient(publishWebRTCClient); - - // Call the switchCamera method - conferenceManager.switchCamera(); - - // Verify that the switchCamera method of publishWebRTCClient is called - verify(publishWebRTCClient).switchCamera(); - } - - // Add more test cases to cover other methods and scenarios within the MultitrackConferenceManager class -} diff --git a/webrtc-android-framework/src/test/java/io/antmedia/webrtcandroidframework/ProxyVideoSinkTest.java b/webrtc-android-framework/src/test/java/io/antmedia/webrtcandroidframework/ProxyVideoSinkTest.java index c35d281a..579cd85e 100644 --- a/webrtc-android-framework/src/test/java/io/antmedia/webrtcandroidframework/ProxyVideoSinkTest.java +++ b/webrtc-android-framework/src/test/java/io/antmedia/webrtcandroidframework/ProxyVideoSinkTest.java @@ -7,6 +7,8 @@ import org.webrtc.VideoFrame; import org.webrtc.VideoSink; +import io.antmedia.webrtcandroidframework.core.ProxyVideoSink; + public class ProxyVideoSinkTest { @Test public void test() { diff --git a/webrtc-android-framework/src/test/java/io/antmedia/webrtcandroidframework/StatsCollectorTest.java b/webrtc-android-framework/src/test/java/io/antmedia/webrtcandroidframework/StatsCollectorTest.java index e64490c0..4d6fedb9 100644 --- a/webrtc-android-framework/src/test/java/io/antmedia/webrtcandroidframework/StatsCollectorTest.java +++ b/webrtc-android-framework/src/test/java/io/antmedia/webrtcandroidframework/StatsCollectorTest.java @@ -11,11 +11,11 @@ import java.util.HashMap; import java.util.Map; -import io.antmedia.webrtcandroidframework.IWebRTCClient; -import io.antmedia.webrtcandroidframework.StatsCollector; - import static org.mockito.Mockito.*; +import io.antmedia.webrtcandroidframework.core.StatsCollector; +import io.antmedia.webrtcandroidframework.core.WebRTCClient; + public class StatsCollectorTest { @Mock @@ -41,7 +41,7 @@ public void testOnStatsReportPublish() { when(rtcStats.getMembers()).thenReturn(createMembersMap("audio")); // Call the onStatsReport method with the publish mode - statsCollector.onStatsReport(report, IWebRTCClient.MODE_PUBLISH); + statsCollector.onStatsReport(report); // Verify that the audio bitrate is logged correctly // You can add additional verification based on your requirements @@ -63,7 +63,7 @@ public void testOnStatsReportPlay() { when(rtcStats.getMembers()).thenReturn(createMembersMap("video")); // Call the onStatsReport method with the play mode - statsCollector.onStatsReport(report, IWebRTCClient.MODE_PLAY); + statsCollector.onStatsReport(report); // Verify that the video bitrate is logged correctly // You can add additional verification based on your requirements 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 d5faa8fc..f121477f 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 @@ -2,9 +2,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyBoolean; @@ -12,7 +10,6 @@ import static org.mockito.Matchers.anyLong; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; @@ -25,14 +22,8 @@ import static org.mockito.Mockito.when; import android.app.Activity; -import android.content.Context; -import android.content.Intent; -import android.media.AudioManager; import android.media.projection.MediaProjection; import android.os.Handler; -import android.util.DisplayMetrics; - -import androidx.annotation.Nullable; import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.RandomUtils; @@ -40,11 +31,11 @@ import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; +import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.Matchers; import org.mockito.Mockito; -import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.webrtc.AudioTrack; import org.webrtc.DataChannel; @@ -58,22 +49,30 @@ import org.webrtc.RtpReceiver; import org.webrtc.RtpSender; import org.webrtc.RtpTransceiver; -import org.webrtc.ScreenCapturerAndroid; import org.webrtc.SessionDescription; -import org.webrtc.VideoSink; +import org.webrtc.VideoCapturer; import org.webrtc.VideoTrack; import org.webrtc.audio.AudioDeviceModule; import org.webrtc.audio.JavaAudioDeviceModule; import java.nio.ByteBuffer; import java.util.ArrayList; -import java.util.Arrays; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; +import io.antmedia.webrtcandroidframework.api.IDataChannelObserver; +import io.antmedia.webrtcandroidframework.api.IWebRTCClient; +import io.antmedia.webrtcandroidframework.api.IWebRTCListener; import io.antmedia.webrtcandroidframework.apprtc.AppRTCAudioManager; -import io.antmedia.webrtcandroidframework.apprtc.CallActivity; +import io.antmedia.webrtcandroidframework.core.PermissionsHandler; +import io.antmedia.webrtcandroidframework.core.ProxyVideoSink; +import io.antmedia.webrtcandroidframework.core.ScreenCapturer; +import io.antmedia.webrtcandroidframework.core.StreamInfo; +import io.antmedia.webrtcandroidframework.core.WebRTCClient; +import io.antmedia.webrtcandroidframework.websocket.WebSocketConstants; +import io.antmedia.webrtcandroidframework.websocket.WebSocketHandler; /** * Example local unit test, which will execute on the development machine (host). @@ -82,39 +81,73 @@ */ public class WebRTCClientTest { + private IWebRTCListener listener; + private Activity context; + private WebRTCClient webRTCClient; + private WebSocketHandler wsHandler; + + @Before + public void before() { + listener = mock(IWebRTCListener.class); + context = mock(Activity.class); + + WebRTCClient webRTCClientReal = IWebRTCClient.builder() + .setActivity(context) + .setWebRTCListener(listener) + .build(); + + webRTCClient = spy(webRTCClientReal); + wsHandler = spy(new WebSocketHandler(null, null)); + doNothing().when(wsHandler).checkIfCalledOnValidThread(); + doNothing().when(wsHandler).sendTextMessage(anyString()); + + webRTCClient.setWsHandler(wsHandler); + + final Handler handler = getMockHandler(); + webRTCClient.setHandler(handler); + } + + private Handler getMockHandler() { + final Handler handler = mock(Handler.class); + when(handler.post(any(Runnable.class))).thenAnswer((Answer) invocation -> { + invocation.getArgumentAt(0, Runnable.class).run(); + return null; + }); + + when(handler.postDelayed(any(Runnable.class), anyLong())).thenAnswer((Answer) invocation -> { + Long delay = invocation.getArgumentAt(1, Long.class); + Thread thread = new Thread(() -> { + try { + Thread.sleep(delay); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + invocation.getArgumentAt(0, Runnable.class).run(); + }); + thread.start(); + return null; + }); + return handler; + } + @Test public void testStreamPublishParameters() { boolean videoCallEnabled = RandomUtils.nextBoolean(); boolean audioCallEnabled = RandomUtils.nextBoolean(); String streamId = "stream" + RandomStringUtils.random(5); - String mode = IWebRTCClient.MODE_PUBLISH; String token = "token" + RandomStringUtils.random(5); String subscriberId = "mySubscriber" + RandomStringUtils.random(5); String subscriberCode = "code" + RandomStringUtils.random(5); String streamName = "stream" + RandomStringUtils.random(5); - IWebRTCListener listener = mock(IWebRTCListener.class); - Context context = mock(Context.class); - WebRTCClient webRTCClient = spy(new WebRTCClient(listener, context)); - WebSocketHandler wsHandler = spy(new WebSocketHandler(null, null)); - webRTCClient.setWsHandler(wsHandler); - - webRTCClient.setSubscriberParams(subscriberId, subscriberCode); - webRTCClient.setStreamName(streamName); - when(context.getString(anyInt(), Matchers.anyVararg())).thenReturn("asas"); - doNothing().when(webRTCClient).init(anyString(), anyString(), anyString(), anyString(), any()); + doNothing().when(webRTCClient).init(); doReturn(true).when(wsHandler).isConnected(); - doNothing().when(wsHandler).checkIfCalledOnValidThread(); - doNothing().when(wsHandler).sendTextMessage(anyString()); - webRTCClient.setStreamId(streamId); - webRTCClient.setStreamMode(mode); webRTCClient.setAudioEnabled(audioCallEnabled); webRTCClient.setVideoEnabled(videoCallEnabled); - webRTCClient.setToken(token); - webRTCClient.startStream(); + webRTCClient.publish(streamId, token, videoCallEnabled, audioCallEnabled, subscriberId, subscriberCode, streamName, null); verify(wsHandler, times(1)).startPublish(streamId, token, videoCallEnabled, audioCallEnabled, subscriberId, subscriberCode, streamName, null); @@ -137,7 +170,7 @@ public void testStreamPublishParameters() { assertEquals(json.toString(), jsonCaptor.getValue()); - webRTCClient.stopStream(); + webRTCClient.stop(streamId); verify(wsHandler, times(1)).stop(streamId); verify(wsHandler, times(2)).sendTextMessage(jsonCaptor.capture()); @@ -153,76 +186,26 @@ public void testStreamPublishParameters() { } - @Test - public void testOnAudioManagerDevicesChanged() { - // Create a mock of the AppRTCAudioManager and AudioManager - AppRTCAudioManager audioManager = Mockito.mock(AppRTCAudioManager.class); - AudioManager systemAudioManager = Mockito.mock(AudioManager.class); - - // Create the test instance - WebRTCClient webRTCClient = Mockito.spy(new WebRTCClient(null, mock(Context.class))); - - // Set up the test data - AppRTCAudioManager.AudioDevice device = AppRTCAudioManager.AudioDevice.BLUETOOTH; - Set availableDevices = new HashSet<>(); - availableDevices.add(AppRTCAudioManager.AudioDevice.SPEAKER_PHONE); - availableDevices.add(AppRTCAudioManager.AudioDevice.BLUETOOTH); - - webRTCClient.audioManager = null; - - // Invoke the method under test - webRTCClient.onAudioManagerDevicesChanged(device, availableDevices); - - // Verify that the audio device is not selected using the AudioManager - Mockito.verify(audioManager, never()).selectAudioDevice(device); - - webRTCClient.audioManager = audioManager; - - // Invoke the method under test - webRTCClient.onAudioManagerDevicesChanged(device, availableDevices); - - // Verify that the audio device is selected using the AudioManager - Mockito.verify(audioManager).selectAudioDevice(device); - } - - @Test public void testStreamPlayParameters() { boolean videoCallEnabled = RandomUtils.nextBoolean(); boolean audioCallEnabled = RandomUtils.nextBoolean(); String streamId = "stream" + RandomStringUtils.random(5); - String mode = IWebRTCClient.MODE_PLAY; String token = "token" + RandomStringUtils.random(5); String subscriberId = "mySubscriber" + RandomStringUtils.random(5); String subscriberCode = "code" + RandomStringUtils.random(5); String viewerInfo = "info" + RandomStringUtils.random(5); - String[] tracks = null; - - - IWebRTCListener listener = mock(IWebRTCListener.class); - Context context = mock(Context.class); - WebRTCClient webRTCClient = spy(new WebRTCClient(listener, context)); - WebSocketHandler wsHandler = spy(new WebSocketHandler(null, null)); - webRTCClient.setWsHandler(wsHandler); - - webRTCClient.setSubscriberParams(subscriberId, subscriberCode); - webRTCClient.setViewerInfo(viewerInfo); when(context.getString(anyInt(), Matchers.anyVararg())).thenReturn("asas"); - doNothing().when(webRTCClient).init(anyString(), anyString(), anyString(), anyString(), any()); + doNothing().when(webRTCClient).init(); doReturn(true).when(wsHandler).isConnected(); - doNothing().when(wsHandler).checkIfCalledOnValidThread(); - doNothing().when(wsHandler).sendTextMessage(anyString()); - webRTCClient.setStreamId(streamId); - webRTCClient.setStreamMode(mode); webRTCClient.setAudioEnabled(audioCallEnabled); webRTCClient.setVideoEnabled(videoCallEnabled); - webRTCClient.setToken(token); - webRTCClient.startStream(); + webRTCClient.play(streamId, token, null, subscriberId, subscriberCode, viewerInfo); - verify(wsHandler, times(1)).startPlay(streamId, token, tracks, subscriberId, subscriberCode, viewerInfo); + verify(wsHandler, times(1)).startPlay(streamId, token, null, subscriberId, subscriberCode, viewerInfo); ArgumentCaptor jsonCaptor = ArgumentCaptor.forClass(String.class); verify(wsHandler, times(1)).sendTextMessage(jsonCaptor.capture()); @@ -242,71 +225,166 @@ public void testStreamPlayParameters() { assertEquals(json.toString(), jsonCaptor.getValue()); - webRTCClient.setStreamMode(IWebRTCClient.MODE_JOIN); + webRTCClient.stop(streamId); + + verify(wsHandler, times(1)).stop(streamId); + verify(wsHandler, times(2)).sendTextMessage(jsonCaptor.capture()); + json = new JSONObject(); + try { + json.put(WebSocketConstants.COMMAND, WebSocketConstants.STOP_COMMAND); + json.put(WebSocketConstants.STREAM_ID, streamId); + } catch (JSONException e) { + e.printStackTrace(); + } + + assertEquals(json.toString(), jsonCaptor.getValue()); + + } + + + @Test + public void testJoinParameters() { + String streamId = "stream" + RandomStringUtils.random(5); + String token = "token" + RandomStringUtils.random(5); + + when(context.getString(anyInt(), Matchers.anyVararg())).thenReturn("asas"); + doNothing().when(webRTCClient).init(); + doReturn(true).when(wsHandler).isConnected(); + + + webRTCClient.join(streamId, token); - webRTCClient.startStream(); verify(wsHandler, times(1)).joinToPeer(streamId, token); - webRTCClient.setStreamMode(IWebRTCClient.MODE_MULTI_TRACK_PLAY); + ArgumentCaptor jsonCaptor = ArgumentCaptor.forClass(String.class); + verify(wsHandler, times(1)).sendTextMessage(jsonCaptor.capture()); + + JSONObject json = new JSONObject(); + try { + json.put(WebSocketConstants.COMMAND, WebSocketConstants.JOIN_COMMAND); + json.put(WebSocketConstants.STREAM_ID, streamId); + json.put(WebSocketConstants.TOKEN, token); + } catch (JSONException e) { + e.printStackTrace(); + } + + assertEquals(json.toString(), jsonCaptor.getValue()); + + webRTCClient.stop(streamId); - webRTCClient.startStream(); - verify(wsHandler, times(1)).getTrackList(streamId, token); + verify(wsHandler, times(1)).stop(streamId); + verify(wsHandler, times(2)).sendTextMessage(jsonCaptor.capture()); + json = new JSONObject(); + try { + json.put(WebSocketConstants.COMMAND, WebSocketConstants.STOP_COMMAND); + json.put(WebSocketConstants.STREAM_ID, streamId); + } catch (JSONException e) { + e.printStackTrace(); + } + + assertEquals(json.toString(), jsonCaptor.getValue()); } @Test - public void testCreateScreenCapturer() { + public void testJoinRoomParameters() { + String streamId = "stream" + RandomStringUtils.random(5); + String roomName = "room" + RandomStringUtils.random(5); - IWebRTCListener listener = mock(IWebRTCListener.class); - Context context = mock(Context.class); - WebRTCClient webRTCClient = spy(new WebRTCClient(listener, context)); - String streamId = "stream"; - webRTCClient.setStreamId(streamId); + when(context.getString(anyInt(), Matchers.anyVararg())).thenReturn("asas"); + doNothing().when(webRTCClient).init(); + doReturn(true).when(wsHandler).isConnected(); - ScreenCapturerAndroid screenCapturer = (ScreenCapturerAndroid) webRTCClient.createScreenCapturer(); - assertNull(screenCapturer); + webRTCClient.joinToConferenceRoom(roomName, streamId); - webRTCClient.setMediaProjectionParams(Activity.RESULT_OK, null); - screenCapturer = (ScreenCapturerAndroid) webRTCClient.createScreenCapturer(); - assertNotNull(screenCapturer); + verify(wsHandler, times(1)).joinToConferenceRoom(roomName, streamId); - MediaProjection.Callback callback = Mockito.spy(screenCapturer.getMediaProjectionCallback()); - callback.onStop(); + ArgumentCaptor jsonCaptor = ArgumentCaptor.forClass(String.class); + verify(wsHandler, times(1)).sendTextMessage(jsonCaptor.capture()); + + JSONObject json = new JSONObject(); + try { + json.put(WebSocketConstants.COMMAND, WebSocketConstants.JOIN_ROOM_COMMAND); + json.put(WebSocketConstants.ROOM, roomName); + json.put(WebSocketConstants.STREAM_ID, streamId); + } catch (JSONException e) { + e.printStackTrace(); + } + + assertEquals(json.toString(), jsonCaptor.getValue()); + + webRTCClient.leaveFromConference(roomName); + + verify(wsHandler, times(1)).leaveFromTheConferenceRoom(roomName); + verify(wsHandler, times(2)).sendTextMessage(jsonCaptor.capture()); + json = new JSONObject(); + try { + json.put(WebSocketConstants.COMMAND, WebSocketConstants.LEAVE_THE_ROOM); + json.put(WebSocketConstants.ROOM, roomName); + } catch (JSONException e) { + e.printStackTrace(); + } + + assertEquals(json.toString(), jsonCaptor.getValue()); - Mockito.verify(webRTCClient).reportError(streamId, "USER_REVOKED_CAPTURE_SCREEN_PERMISSION"); } + @Test - public void testOnActivityResult() { - IWebRTCListener listener = mock(IWebRTCListener.class); - Context context = mock(Context.class); - WebRTCClient webRTCClient = spy(new WebRTCClient(listener, context)); + public void testOnAudioManagerDevicesChanged() { + // Create a mock of the AppRTCAudioManager and AudioManager + AppRTCAudioManager audioManager = Mockito.mock(AppRTCAudioManager.class); - webRTCClient.changeVideoSource(WebRTCClient.SOURCE_SCREEN); - Mockito.verify(webRTCClient).startScreenCapture(); + // Set up the test data + AppRTCAudioManager.AudioDevice device = AppRTCAudioManager.AudioDevice.BLUETOOTH; + Set availableDevices = new HashSet<>(); + availableDevices.add(AppRTCAudioManager.AudioDevice.SPEAKER_PHONE); + availableDevices.add(AppRTCAudioManager.AudioDevice.BLUETOOTH); - webRTCClient.onActivityResult(0, Activity.RESULT_OK, null); - assertNotEquals(Activity.RESULT_OK, webRTCClient.getMediaProjectionPermissionResultCode()); + webRTCClient.audioManager = null; - Mockito.doNothing().when(webRTCClient).changeVideoCapturer(any()); - Mockito.doReturn(new DisplayMetrics()).when(webRTCClient).getDisplayMetrics(); - webRTCClient.onActivityResult(CallActivity.CAPTURE_PERMISSION_REQUEST_CODE, Activity.RESULT_OK, null); - assertEquals(Activity.RESULT_OK, webRTCClient.getMediaProjectionPermissionResultCode()); + // Invoke the method under test + webRTCClient.onAudioManagerDevicesChanged(device, availableDevices); + + // Verify that the audio device is not selected using the AudioManager + Mockito.verify(audioManager, never()).selectAudioDevice(device); - Mockito.verify(webRTCClient).createVideoCapturer(WebRTCClient.SOURCE_SCREEN); + webRTCClient.audioManager = audioManager; + + // Invoke the method under test + webRTCClient.onAudioManagerDevicesChanged(device, availableDevices); + + // Verify that the audio device is selected using the AudioManager + Mockito.verify(audioManager).selectAudioDevice(device); + } + + @Test + public void testCreateScreenCapturer() { + + String streamId = "stream"; + WebRTCClient.PeerInfo peerInfo = new WebRTCClient.PeerInfo(streamId, WebRTCClient.Mode.PUBLISH); + doNothing().when(wsHandler).disconnect(anyBoolean()); + + webRTCClient.peers.put(streamId, peerInfo); + + webRTCClient.getConfig().mediaProjection = mock(MediaProjection.class); + + ScreenCapturer screenCapturer = (ScreenCapturer) webRTCClient.createScreenCapturer(); + assertNotNull(screenCapturer); + + MediaProjection.Callback callback = Mockito.spy(screenCapturer.getMediaProjectionCallback()); + callback.onStop(); + + Mockito.verify(webRTCClient).reportError(streamId, WebRTCClient.USER_REVOKED_CAPTURE_SCREEN_PERMISSION); } + + @Test public void testReleaseCallback() { - IWebRTCListener listener = Mockito.mock(IWebRTCListener.class); - Context context = Mockito.mock(Context.class); - WebRTCClient webRTCClient = Mockito.spy(new WebRTCClient(listener, context)); - Mockito.doNothing().when(webRTCClient).release(anyBoolean()); - final Handler handler = getMockHandler(); - webRTCClient.setHandler(handler); + doNothing().when(wsHandler).disconnect(anyBoolean()); webRTCClient.onPublishFinished("streamId"); - Mockito.verify(webRTCClient, timeout(1000)).release(false); webRTCClient.onPlayFinished("streamId"); @@ -320,76 +398,67 @@ public void testReleaseCallback() { } + @Test public void testInitilization() { - IWebRTCListener listener = Mockito.mock(IWebRTCListener.class); - Context context = Mockito.mock(Context.class); - WebRTCClient webRTCClient = Mockito.spy(new WebRTCClient(listener, context)); - Mockito.doNothing().when(webRTCClient).initializeRenderers(); - Mockito.doNothing().when(webRTCClient).initializePeerConnectionFactory(); - Mockito.doNothing().when(webRTCClient).initializeVideoCapturer(); - Mockito.doNothing().when(webRTCClient).initializeAudioManager(); - Mockito.doNothing().when(webRTCClient).connectWebSocket(); + mockMethodsInInit(); Mockito.doReturn(true).when(webRTCClient).checkPermissions(any()); when(context.getString(R.string.pref_maxvideobitratevalue_default)).thenReturn("500"); when(context.getString(R.string.pref_startaudiobitratevalue_default)).thenReturn("500"); - - Intent intent = Mockito.mock(Intent.class); - when(intent.getBooleanExtra(CallActivity.EXTRA_VIDEO_CALL, true)).thenReturn(false); - when(intent.getBooleanExtra(CallActivity.EXTRA_DATA_CHANNEL_ENABLED, true)).thenReturn(true); - - webRTCClient.init("http://my.ams:5080/myapp/websocket", "stream", WebRTCClient.MODE_PUBLISH, "token", intent); - - assertEquals(false, webRTCClient.getVideoCallEnabled()); - assertEquals(true, webRTCClient.isDataChannelEnabled()); + webRTCClient.init(); + assertTrue(webRTCClient.isDataChannelEnabled()); } + @Test public void testInitilizeVideoCapturer() { - IWebRTCListener listener = Mockito.mock(IWebRTCListener.class); - Context context = Mockito.mock(Context.class); - WebRTCClient webRTCClient = Mockito.spy(new WebRTCClient(listener, context)); Mockito.doNothing().when(webRTCClient).initializeRenderers(); + Mockito.doReturn(null).when(webRTCClient).createVideoCapturer(any()); + Mockito.doReturn(true).when(webRTCClient).useCamera2(); + webRTCClient.initializeVideoCapturer(); + assertEquals(IWebRTCClient.StreamSource.FRONT_CAMERA, webRTCClient.getConfig().videoSource); - webRTCClient.setVideoCallEnabled(true); + Mockito.doReturn(false).when(webRTCClient).useCamera2(); webRTCClient.initializeVideoCapturer(); + assertEquals(IWebRTCClient.StreamSource.REAR_CAMERA, webRTCClient.getConfig().videoSource); - assertEquals(WebRTCClient.SOURCE_FRONT, webRTCClient.getCurrentSource()); - } + webRTCClient.getConfig().screencaptureEnabled = true; + webRTCClient.getConfig().customVideoCapturerEnabled = false; + webRTCClient.initializeVideoCapturer(); + assertEquals(IWebRTCClient.StreamSource.SCREEN, webRTCClient.getConfig().videoSource); + webRTCClient.getConfig().screencaptureEnabled = false; + webRTCClient.getConfig().customVideoCapturerEnabled = true; + webRTCClient.initializeVideoCapturer(); + assertEquals(IWebRTCClient.StreamSource.CUSTOM, webRTCClient.getConfig().videoSource); + + } @Test - public void testAccessors() { - IWebRTCListener listener = Mockito.mock(IWebRTCListener.class); - Context context = Mockito.mock(Context.class); - WebRTCClient webRTCClient = Mockito.spy(new WebRTCClient(listener, context)); + public void testCreateVideoCapturer() { - webRTCClient.setStreamMode(IWebRTCClient.MODE_PLAY); - assertEquals(IWebRTCClient.MODE_PLAY, webRTCClient.getStreamMode()); + VideoCapturer screenapturer = mock(VideoCapturer.class); + VideoCapturer customCapturer = mock(VideoCapturer.class); + VideoCapturer cameraCapturer = mock(VideoCapturer.class); + doReturn(screenapturer).when(webRTCClient).createScreenCapturer(); + doReturn(customCapturer).when(webRTCClient).createCustomVideoCapturer(); + doReturn(cameraCapturer).when(webRTCClient).createCameraCapturer(any()); + assertEquals(screenapturer, webRTCClient.createVideoCapturer(IWebRTCClient.StreamSource.SCREEN)); + assertEquals(customCapturer, webRTCClient.createVideoCapturer(IWebRTCClient.StreamSource.CUSTOM)); + assertEquals(cameraCapturer, webRTCClient.createVideoCapturer(IWebRTCClient.StreamSource.FRONT_CAMERA)); + assertEquals(cameraCapturer, webRTCClient.createVideoCapturer(IWebRTCClient.StreamSource.REAR_CAMERA)); } + @Test public void testPCObserver() { - IWebRTCListener listener = Mockito.mock(IWebRTCListener.class); - Context context = Mockito.mock(Context.class); - WebRTCClient webRTCClientReal = new WebRTCClient(listener, context); - WebRTCClient webRTCClient = spy(webRTCClientReal); - doNothing().when(webRTCClient).release(anyBoolean()); - - WebSocketHandler wsHandler = mock(WebSocketHandler.class); - webRTCClient.setWsHandler(wsHandler); - - final Handler handler = getMockHandler(); - webRTCClient.setHandler(handler); - String streamId = "stream1"; - webRTCClient.setStreamId(streamId); WebRTCClient.PCObserver pcObserver = webRTCClient.getPCObserver(streamId); assertNotNull(pcObserver); @@ -403,7 +472,7 @@ public void testPCObserver() { pcObserver.onConnectionChange(PeerConnection.PeerConnectionState.CONNECTED); pcObserver.onConnectionChange(PeerConnection.PeerConnectionState.DISCONNECTED); - verify(listener, timeout(1000)).onDisconnected(streamId); + verify(listener, timeout(1000)).onDisconnected(); pcObserver.onConnectionChange(PeerConnection.PeerConnectionState.FAILED); verify(listener, timeout(1000)).onError(anyString(), eq(streamId)); @@ -427,7 +496,7 @@ public void testPCObserver() { pcObserver.onRenegotiationNeeded(); pcObserver.onDataChannel(mock(DataChannel.class)); - webRTCClient.getRemoteSinks().add(mock(VideoSink.class)); + webRTCClient.getRemoteVideoSinks().add(mock(ProxyVideoSink.class)); MediaStream[] tracks = {new MediaStream(0)}; PeerConnection pc = mock(PeerConnection.class); RtpTransceiver transceiver = mock(RtpTransceiver.class); @@ -435,68 +504,21 @@ public void testPCObserver() { when(transceiver.getReceiver()).thenReturn(receiver); VideoTrack videoTrack= mock(VideoTrack.class); when(receiver.track()).thenReturn(videoTrack); - when(pc.getTransceivers()).thenReturn(Arrays.asList(transceiver)); + when(pc.getTransceivers()).thenReturn(Collections.singletonList(transceiver)); webRTCClient.addPeerConnection(streamId, pc); - webRTCClient.setRenderersProvidedAtStart(true); - webRTCClient.setStreamMode(IWebRTCClient.MODE_PLAY); pcObserver.onAddTrack(receiver, tracks); - verify(videoTrack, times(1)).addSink(any(VideoSink.class)); + verify(listener, times(1)).onNewVideoTrack(videoTrack); pcObserver.onRemoveTrack(mock(RtpReceiver.class)); assertNotNull(webRTCClient); } - @Nullable - private Handler getMockHandler() { - final Handler handler = mock(Handler.class); - when(handler.post(any(Runnable.class))).thenAnswer(new Answer() { - @Override - public Object answer(InvocationOnMock invocation) throws Throwable { - invocation.getArgumentAt(0, Runnable.class).run(); - return null; - } - - }); - - when(handler.postDelayed(any(Runnable.class), anyLong())).thenAnswer(new Answer() { - @Override - public Object answer(InvocationOnMock invocation) throws Throwable { - Long delay = invocation.getArgumentAt(1, Long.class); - Thread thread = new Thread(new Runnable() { - @Override - public void run() { - try { - Thread.sleep(delay); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - invocation.getArgumentAt(0, Runnable.class).run(); - } - }); - thread.start(); - return null; - } - - }); - return handler; - } @Test public void testSDPObserver() { - IWebRTCListener listener = Mockito.mock(IWebRTCListener.class); - Context context = Mockito.mock(Context.class); - WebRTCClient webRTCClient = new WebRTCClient(listener, context); - - WebSocketHandler wsHandler = mock(WebSocketHandler.class); - when(wsHandler.getSignallingListener()).thenReturn(webRTCClient); - webRTCClient.setWsHandler(wsHandler); - - final Handler handler = getMockHandler(); - webRTCClient.setHandler(handler); - + doNothing().when(wsHandler).disconnect(anyBoolean()); String streamId = "stream1"; - webRTCClient.setStreamId(streamId); WebRTCClient.SDPObserver sdpObserver = webRTCClient.getSdpObserver(streamId); assertNotNull(sdpObserver); @@ -504,7 +526,7 @@ public void testSDPObserver() { PeerConnection pc = mock(PeerConnection.class); webRTCClient.addPeerConnection(streamId, pc); - SessionDescription sessionDescription = mock(SessionDescription.class); + SessionDescription sessionDescription = new SessionDescription(SessionDescription.Type.OFFER, "sdp"); sdpObserver.onCreateSuccess(sessionDescription); verify(pc, timeout(1000)).setLocalDescription(eq(sdpObserver), any()); @@ -525,55 +547,10 @@ public void testSDPObserver() { verify(wsHandler, timeout(1000)).disconnect(true); } - @Test - public void testAudioVideoEnablement() { - IWebRTCListener listener = Mockito.mock(IWebRTCListener.class); - Context context = Mockito.mock(Context.class); - WebRTCClient webRTCClient = Mockito.spy(new WebRTCClient(listener, context)); - - when(context.getString(R.string.pref_maxvideobitratevalue_default)).thenReturn("500"); - when(context.getString(R.string.pref_startaudiobitratevalue_default)).thenReturn("500"); - - Intent intent = Mockito.mock(Intent.class); - - webRTCClient.setStreamMode(WebRTCClient.MODE_PLAY); - webRTCClient.initializeParameters(); - assertEquals(false, webRTCClient.getVideoCallEnabled()); - assertEquals(false, webRTCClient.getAudioCallEnabled()); - - webRTCClient.setVideoEnabled(false); - webRTCClient.setAudioEnabled(false); - - webRTCClient.setStreamMode(WebRTCClient.MODE_MULTI_TRACK_PLAY); - webRTCClient.initializeParameters(); - assertEquals(false, webRTCClient.getVideoCallEnabled()); - assertEquals(false, webRTCClient.getAudioCallEnabled()); - - webRTCClient.setVideoEnabled(false); - webRTCClient.setAudioEnabled(false); - - webRTCClient.setStreamMode(WebRTCClient.MODE_TRACK_BASED_CONFERENCE); - webRTCClient.initializeParameters(); - assertEquals(true, webRTCClient.getVideoCallEnabled()); - assertEquals(true, webRTCClient.getAudioCallEnabled()); - - webRTCClient.setVideoEnabled(false); - webRTCClient.setAudioEnabled(false); - - webRTCClient.setStreamMode("some other mode"); - webRTCClient.initializeParameters(); - assertEquals(false, webRTCClient.getVideoCallEnabled()); - assertEquals(false, webRTCClient.getAudioCallEnabled()); - - - } - @Test public void testAVideoRotationExtention() { String streamId = "stream1"; - IWebRTCListener listener = Mockito.mock(IWebRTCListener.class); - Context context = Mockito.mock(Context.class); - WebRTCClient webRTCClient = new WebRTCClient(listener, context); + PeerConnection pc = mock(PeerConnection.class); webRTCClient.addPeerConnection(streamId, pc); @@ -582,6 +559,7 @@ public void testAVideoRotationExtention() { "something else\r\n"; webRTCClient.getSdpObserver(streamId).onCreateSuccess(new SessionDescription(SessionDescription.Type.OFFER, fakeSdp)); + assertNotNull(webRTCClient.getLocalDescription()); assertTrue(webRTCClient.getLocalDescription().description.contains(WebRTCClient.VIDEO_ROTATION_EXT_LINE)); @@ -594,19 +572,20 @@ public void testAVideoRotationExtention() { @Test public void testSendPlayOtherTracks() { - WebRTCClient webRTCClient = spy(new WebRTCClient(null, mock(Context.class))); webRTCClient.setAutoPlayTracks(true); - webRTCClient.setSelfStreamId("self"); - String tracks[] = {"other1", "self", "other2"}; + String[] tracks; + tracks = new String[]{"other1", "self", "other2"}; - doNothing().when(webRTCClient).init(anyString(), anyString(), anyString(), anyString(), any()); + doNothing().when(webRTCClient).init(); doNothing().when(webRTCClient).play(anyString(), anyString(), any(), anyString(), anyString(), anyString()); - doReturn(false).when(webRTCClient).isStreaming(); + doReturn("self").when(webRTCClient).getPublishStreamId(); + doReturn("main").when(webRTCClient).getMultiTrackStreamId(); + doReturn(false).when(webRTCClient).isStreaming("main"); webRTCClient.sendPlayOtherTracks(tracks); ArgumentCaptor tracksCaptor = ArgumentCaptor.forClass(String[].class); - verify(webRTCClient, times(1)).play(anyString(), anyString(), tracksCaptor.capture()); + verify(webRTCClient, times(1)).play(anyString(), tracksCaptor.capture()); String[] capturedTracks = tracksCaptor.getValue(); assertEquals("other1", capturedTracks[0]); @@ -614,24 +593,19 @@ public void testSendPlayOtherTracks() { assertEquals("other2", capturedTracks[2]); } + + @Test public void testReconnection() { - IWebRTCListener listener = Mockito.mock(IWebRTCListener.class); - WebRTCClient webRTCClientReal = new WebRTCClient(listener, mock(Context.class)); - webRTCClientReal.setWsHandler(mock(WebSocketHandler.class)); - - WebRTCClient webRTCClient = spy(webRTCClientReal); webRTCClient.createReconnectionRunnable(); - String streamId = "stream1"; - webRTCClient.setStreamId(streamId); final Handler handler = getMockHandler(); webRTCClient.setHandler(handler); - webRTCClient.setReconnectionEnabled(true); + webRTCClient.getConfig().reconnectionEnabled = true; webRTCClient.setReconnectionHandler(handler); - doNothing().when(webRTCClient).init(anyString(), anyString(), anyString(), anyString(), any()); + doNothing().when(webRTCClient).init(); String playStreamId = "playStreamId"; webRTCClient.play(playStreamId, "", null, "", "", ""); @@ -641,7 +615,7 @@ public void testReconnection() { webRTCClient.onDisconnected(); - verify(listener, timeout(1000)).onDisconnected(streamId); + verify(listener, timeout(1000)).onDisconnected(); verify(webRTCClient, timeout(WebRTCClient.RECONNECTION_CONTROL_PERIOD_MLS).atLeast(2)).play(anyString(), anyString(), any(), anyString(), anyString(), anyString()); verify(webRTCClient, timeout(WebRTCClient.RECONNECTION_CONTROL_PERIOD_MLS).atLeast(2)).publish(anyString(), anyString(), anyBoolean(), anyBoolean(), anyString(), anyString(), anyString(), anyString()); @@ -649,17 +623,9 @@ public void testReconnection() { @Test public void testWSAndListenerMessages() { - IWebRTCListener listener = Mockito.mock(IWebRTCListener.class); - WebRTCClient webRTCClientReal = new WebRTCClient(listener, mock(Context.class)); - WebSocketHandler wsHandler = mock(WebSocketHandler.class); - webRTCClientReal.setWsHandler(wsHandler); - - WebRTCClient webRTCClient = spy(webRTCClientReal); - final Handler handler = getMockHandler(); - webRTCClient.setHandler(handler); - String streamId = "stream1"; String room = "room1"; + mockMethodsInInit(); webRTCClient.joinToConferenceRoom(room, streamId); verify(wsHandler, timeout(1000)).joinToConferenceRoom(room, streamId); @@ -670,6 +636,10 @@ public void testWSAndListenerMessages() { webRTCClient.getRoomInfo(room, streamId); verify(wsHandler, timeout(1000)).getRoomInfo(room, streamId); + webRTCClient.enableTrack("stream1", "track1", true); + verify(wsHandler, timeout(1000)).enableTrack("stream1", "track1", true); + + String[] streams = new String[1]; webRTCClient.onJoinedTheRoom(streamId, streams); @@ -710,84 +680,57 @@ public void testWSAndListenerMessages() { } @Test - public void testCheckPermissions() { - IWebRTCListener listener = Mockito.mock(IWebRTCListener.class); - WebRTCClient webRTCClientReal = new WebRTCClient(listener, mock(Context.class)); - WebSocketHandler wsHandler = mock(WebSocketHandler.class); - webRTCClientReal.setWsHandler(wsHandler); - - WebRTCClient webRTCClient = spy(webRTCClientReal); - final Handler handler = getMockHandler(); - webRTCClient.setHandler(handler); + public void testCheckPermissionsForPublish() { + mockMethodsInInit(); - webRTCClient.setStreamMode(WebRTCClient.MODE_TRACK_BASED_CONFERENCE); - webRTCClient.checkPermissions(any()); - verify(listener).checkAndRequestPermisssions(eq(true), any()); + doNothing().when(wsHandler).startPublish(anyString(), anyString(), anyBoolean(), anyBoolean(), anyString(), anyString(), anyString(), anyString()); + doNothing().when(wsHandler).startPlay(anyString(), anyString(), any(), anyString(), anyString(), anyString()); - webRTCClient.setStreamMode(WebRTCClient.MODE_PUBLISH); - webRTCClient.checkPermissions(any()); - verify(listener, times(2)).checkAndRequestPermisssions(eq(true), any()); - - webRTCClient.setStreamMode(WebRTCClient.MODE_PLAY); - webRTCClient.checkPermissions(any()); - verify(listener).checkAndRequestPermisssions(eq(false), any()); + PermissionsHandler permissionsHandler = spy(new PermissionsHandler(context)); + webRTCClient.setPermissionsHandlerForTest(permissionsHandler); + webRTCClient.publish("stream1"); + verify(permissionsHandler).checkAndRequestPermisssions(eq(true), any()); } @Test - public void testMultiplePlayAdaptations() { - IWebRTCListener listener = Mockito.mock(IWebRTCListener.class); - WebRTCClient webRTCClientReal = new WebRTCClient(listener, mock(Context.class)); - WebSocketHandler wsHandler = mock(WebSocketHandler.class); - webRTCClientReal.setWsHandler(wsHandler); - String initialStreamId = "initialStreamId"; - webRTCClientReal.setStreamId(initialStreamId); - - WebRTCClient webRTCClient = spy(webRTCClientReal); - final Handler handler = getMockHandler(); - webRTCClient.setHandler(handler); + public void testCheckPermissionsForPlay() { + mockMethodsInInit(); - when(wsHandler.isConnected()).thenReturn(true); - - String streamId = "stream1"; - webRTCClient.stopStream(streamId); - verify(wsHandler, timeout(1000)).stop(streamId); + doNothing().when(wsHandler).startPublish(anyString(), anyString(), anyBoolean(), anyBoolean(), anyString(), anyString(), anyString(), anyString()); + doNothing().when(wsHandler).startPlay(anyString(), anyString(), any(), anyString(), anyString(), anyString()); - WebRTCClient.PeerInfo peerInfo = new WebRTCClient.PeerInfo(streamId, WebRTCClient.MODE_PUBLISH); - PeerConnection pc = mock(PeerConnection.class); - when(pc.iceConnectionState()).thenReturn(PeerConnection.IceConnectionState.CONNECTED); - peerInfo.peerConnection = pc; - webRTCClient.peers.put(initialStreamId, peerInfo); - assertTrue(webRTCClient.isStreaming()); - when(pc.iceConnectionState()).thenReturn(PeerConnection.IceConnectionState.DISCONNECTED); - assertFalse(webRTCClient.isStreaming()); + PermissionsHandler permissionsHandler = spy(new PermissionsHandler(context)); + webRTCClient.setPermissionsHandlerForTest(permissionsHandler); - webRTCClient.getStreamInfoList(); - verify(wsHandler, timeout(1000)).getStreamInfoList(initialStreamId); + webRTCClient.play("stream1"); + verify(permissionsHandler).checkAndRequestPermisssions(eq(false), any()); + } - webRTCClient.forceStreamQuality(360); - verify(wsHandler, timeout(1000)).forceStreamQuality(initialStreamId, 360); + @Test + public void testCheckPermissionsForPeer() { + mockMethodsInInit(); - assertEquals(initialStreamId, webRTCClient.getStreamId()); + doNothing().when(wsHandler).startPublish(anyString(), anyString(), anyBoolean(), anyBoolean(), anyString(), anyString(), anyString(), anyString()); + doNothing().when(wsHandler).startPlay(anyString(), anyString(), any(), anyString(), anyString(), anyString()); - DataChannel.Buffer buffer = mock(DataChannel.Buffer.class); - webRTCClient.sendMessageViaDataChannel(buffer); - verify(webRTCClient, timeout(1000)).sendMessageViaDataChannel(initialStreamId, buffer); + PermissionsHandler permissionsHandler = spy(new PermissionsHandler(context)); + webRTCClient.setPermissionsHandlerForTest(permissionsHandler); + webRTCClient.join("stream1"); + verify(permissionsHandler, times(1)).checkAndRequestPermisssions(eq(true), any()); } + private void mockMethodsInInit() { + Mockito.doNothing().when(webRTCClient).initializeRenderers(); + Mockito.doNothing().when(webRTCClient).initializePeerConnectionFactory(); + Mockito.doNothing().when(webRTCClient).initializeVideoCapturer(); + Mockito.doNothing().when(webRTCClient).initializeAudioManager(); + Mockito.doNothing().when(webRTCClient).connectWebSocket(); + } @Test public void testOnStartStreaming() { - IWebRTCListener listener = Mockito.mock(IWebRTCListener.class); - WebRTCClient webRTCClientReal = new WebRTCClient(listener, mock(Context.class)); - WebSocketHandler wsHandler = mock(WebSocketHandler.class); - webRTCClientReal.setWsHandler(wsHandler); - - WebRTCClient webRTCClient = spy(webRTCClientReal); - final Handler handler = getMockHandler(); - webRTCClient.setHandler(handler); - String streamId = "stream1"; doNothing().when(webRTCClient).createPeerConnection(streamId); doNothing().when(webRTCClient).createOffer(streamId); @@ -795,22 +738,10 @@ public void testOnStartStreaming() { verify(webRTCClient, timeout(1000)).createPeerConnection(streamId); verify(webRTCClient, timeout(1000)).createOffer(streamId); - - - } @Test public void testOnTakeConfiguration() { - IWebRTCListener listener = Mockito.mock(IWebRTCListener.class); - WebRTCClient webRTCClientReal = new WebRTCClient(listener, mock(Context.class)); - WebSocketHandler wsHandler = mock(WebSocketHandler.class); - webRTCClientReal.setWsHandler(wsHandler); - - WebRTCClient webRTCClient = spy(webRTCClientReal); - final Handler handler = getMockHandler(); - webRTCClient.setHandler(handler); - String streamId = "stream1"; SessionDescription sdp = new SessionDescription(SessionDescription.Type.OFFER, "sdp"); @@ -819,7 +750,7 @@ public void testOnTakeConfiguration() { doNothing().when(webRTCClient).setRemoteDescription(streamId, sdp); doNothing().when(webRTCClient).createAnswer(streamId); - webRTCClient.peers.put(streamId, new WebRTCClient.PeerInfo(streamId, WebRTCClient.MODE_PUBLISH)); + webRTCClient.peers.put(streamId, new WebRTCClient.PeerInfo(streamId, WebRTCClient.Mode.PUBLISH)); webRTCClient.onTakeConfiguration(streamId, sdp); @@ -837,22 +768,9 @@ public void testOnTakeConfiguration() { @Test public void testDatachannel() { - IWebRTCListener listener = Mockito.mock(IWebRTCListener.class); - WebRTCClient webRTCClientReal = new WebRTCClient(listener, mock(Context.class)); - WebSocketHandler wsHandler = mock(WebSocketHandler.class); - webRTCClientReal.setWsHandler(wsHandler); - - WebRTCClient webRTCClient = spy(webRTCClientReal); - webRTCClient.setStreamMode(WebRTCClient.MODE_PUBLISH); - final Handler handler = getMockHandler(); - webRTCClient.setHandler(handler); - - IDataChannelObserver dcObserver = mock(IDataChannelObserver.class); - webRTCClient.setDataChannelObserver(dcObserver); - String streamId = "stream1"; - WebRTCClient.PeerInfo peerInfo = new WebRTCClient.PeerInfo(streamId, WebRTCClient.MODE_PUBLISH); + WebRTCClient.PeerInfo peerInfo = new WebRTCClient.PeerInfo(streamId, WebRTCClient.Mode.PUBLISH); PeerConnection pc = mock(PeerConnection.class); DataChannel dc = mock(DataChannel.class); when(pc.createDataChannel(anyString(), any())).thenReturn(dc); @@ -863,6 +781,8 @@ public void testDatachannel() { assertEquals(dc, peerInfo.dataChannel); + IDataChannelObserver dcObserver = mock(IDataChannelObserver.class); + webRTCClient.getConfig().dataChannelObserver = dcObserver; doNothing().when(webRTCClient).reportError(anyString(), anyString()); ByteBuffer bb = ByteBuffer.allocate(10); @@ -885,22 +805,13 @@ public void testDatachannel() { verify(dcObserver, timeout(1000).times(2)).onMessageSent(buffer, false); } + @Test public void testCreatePeerConnection() { - IWebRTCListener listener = Mockito.mock(IWebRTCListener.class); - WebRTCClient webRTCClientReal = new WebRTCClient(listener, mock(Context.class)); - WebSocketHandler wsHandler = mock(WebSocketHandler.class); - webRTCClientReal.setWsHandler(wsHandler); - - WebRTCClient webRTCClient = spy(webRTCClientReal); - final Handler handler = getMockHandler(); - webRTCClient.setHandler(handler); - String streamId = "stream1"; doNothing().when(webRTCClient).createMediaConstraintsInternal(); doNothing().when(webRTCClient).createPeerConnectionInternal(streamId); - doNothing().when(webRTCClient).maybeCreateAndStartRtcEventLog(streamId); doNothing().when(webRTCClient).reportError(anyString(), anyString()); webRTCClient.createPeerConnection(streamId); @@ -920,20 +831,11 @@ public void testCreatePeerConnection() { @Test public void testCreateAudioDevice() { - IWebRTCListener listener = Mockito.mock(IWebRTCListener.class); - WebRTCClient webRTCClientReal = new WebRTCClient(listener, mock(Context.class)); - WebSocketHandler wsHandler = mock(WebSocketHandler.class); - webRTCClientReal.setWsHandler(wsHandler); - - WebRTCClient webRTCClient = spy(webRTCClientReal); - final Handler handler = getMockHandler(); - webRTCClient.setHandler(handler); - String streamId = "stream1"; - webRTCClient.setStreamId(streamId); JavaAudioDeviceModule.Builder builder = mock(JavaAudioDeviceModule.Builder.class); doReturn(builder).when(webRTCClient).getADMBuilder(); doNothing().when(webRTCClient).reportError(anyString(), anyString()); + doReturn(streamId).when(webRTCClient).getPublishStreamId(); doReturn(builder).when(builder).setCustomAudioFeed(anyBoolean()); doReturn(builder).when(builder).setUseHardwareAcousticEchoCanceler(anyBoolean()); @@ -967,7 +869,7 @@ public void testCreateAudioDevice() { JavaAudioDeviceModule.AudioRecordStateCallback audioRecordStateCallback = audioRecordStateCallbackCaptor.getValue(); JavaAudioDeviceModule.AudioTrackStateCallback audioTrackStateCallback = audioTrackStateCallbackCaptor.getValue(); - audioRecordStateCallback.onWebRtcAudioRecordStop(); + audioRecordStateCallback.onWebRtcAudioRecordStart(); audioRecordStateCallback.onWebRtcAudioRecordStop(); audioTrackStateCallback.onWebRtcAudioTrackStart(); @@ -995,17 +897,7 @@ public void testCreateAudioDevice() { @Test public void testCreatePeerConnectionInternal() { - IWebRTCListener listener = Mockito.mock(IWebRTCListener.class); - WebRTCClient webRTCClientReal = new WebRTCClient(listener, mock(Context.class)); - WebSocketHandler wsHandler = mock(WebSocketHandler.class); - webRTCClientReal.setWsHandler(wsHandler); - - WebRTCClient webRTCClient = spy(webRTCClientReal); - final Handler handler = getMockHandler(); - webRTCClient.setHandler(handler); - String streamId = "stream1"; - webRTCClient.setStreamId(streamId); doNothing().when(webRTCClient).setWebRTCLogLevel(); doReturn(mock(AudioTrack.class)).when(webRTCClient).createAudioTrack(); @@ -1020,7 +912,7 @@ public void testCreatePeerConnectionInternal() { when(factory.createPeerConnection(any(PeerConnection.RTCConfiguration.class), any(PeerConnection.Observer.class))) .thenReturn(pc); - WebRTCClient.PeerInfo peerInfo = new WebRTCClient.PeerInfo(streamId, IWebRTCClient.MODE_PUBLISH); + WebRTCClient.PeerInfo peerInfo = new WebRTCClient.PeerInfo(streamId, WebRTCClient.Mode.PUBLISH); webRTCClient.peers.put(streamId, peerInfo); webRTCClient.createPeerConnectionInternal(streamId); @@ -1029,20 +921,11 @@ public void testCreatePeerConnectionInternal() { @Test public void testStatsTest() { - IWebRTCListener listener = Mockito.mock(IWebRTCListener.class); - WebRTCClient webRTCClientReal = new WebRTCClient(listener, mock(Context.class)); - WebSocketHandler wsHandler = mock(WebSocketHandler.class); - webRTCClientReal.setWsHandler(wsHandler); - - WebRTCClient webRTCClient = spy(webRTCClientReal); - final Handler handler = getMockHandler(); - webRTCClient.setHandler(handler); - String streamId = "stream1"; doNothing().when(webRTCClient).onPeerConnectionStatsReady(any()); - WebRTCClient.PeerInfo peerInfo = new WebRTCClient.PeerInfo(streamId, IWebRTCClient.MODE_PUBLISH); + WebRTCClient.PeerInfo peerInfo = new WebRTCClient.PeerInfo(streamId, WebRTCClient.Mode.PUBLISH); webRTCClient.peers.put(streamId, peerInfo); //no pc so nothing happens, return immediately @@ -1058,19 +941,10 @@ public void testStatsTest() { @Test public void testCreateSDP() { - IWebRTCListener listener = Mockito.mock(IWebRTCListener.class); - WebRTCClient webRTCClientReal = new WebRTCClient(listener, mock(Context.class)); - WebSocketHandler wsHandler = mock(WebSocketHandler.class); - webRTCClientReal.setWsHandler(wsHandler); - - WebRTCClient webRTCClient = spy(webRTCClientReal); - final Handler handler = getMockHandler(); - webRTCClient.setHandler(handler); - String streamId = "stream1"; doNothing().when(webRTCClient).initDataChannel(streamId); - WebRTCClient.PeerInfo peerInfo = new WebRTCClient.PeerInfo(streamId, IWebRTCClient.MODE_PUBLISH); + WebRTCClient.PeerInfo peerInfo = new WebRTCClient.PeerInfo(streamId, WebRTCClient.Mode.PUBLISH); webRTCClient.peers.put(streamId, peerInfo); PeerConnection pc = mock(PeerConnection.class); @@ -1087,19 +961,10 @@ public void testCreateSDP() { @Test public void testAddRemoveRemoteIceCandidate() { - IWebRTCListener listener = Mockito.mock(IWebRTCListener.class); - WebRTCClient webRTCClientReal = new WebRTCClient(listener, mock(Context.class)); - WebSocketHandler wsHandler = mock(WebSocketHandler.class); - webRTCClientReal.setWsHandler(wsHandler); - - WebRTCClient webRTCClient = spy(webRTCClientReal); - final Handler handler = getMockHandler(); - webRTCClient.setHandler(handler); - String streamId = "stream1"; doNothing().when(webRTCClient).initDataChannel(streamId); - WebRTCClient.PeerInfo peerInfo = new WebRTCClient.PeerInfo(streamId, IWebRTCClient.MODE_PUBLISH); + WebRTCClient.PeerInfo peerInfo = new WebRTCClient.PeerInfo(streamId, WebRTCClient.Mode.PUBLISH); webRTCClient.peers.put(streamId, peerInfo); PeerConnection pc = mock(PeerConnection.class); @@ -1127,21 +992,11 @@ public void testAddRemoveRemoteIceCandidate() { @Test public void testDegradationPreference() { String streamId = "stream1"; - - IWebRTCListener listener = Mockito.mock(IWebRTCListener.class); - WebRTCClient webRTCClientReal = new WebRTCClient(listener, mock(Context.class)); - WebSocketHandler wsHandler = mock(WebSocketHandler.class); - webRTCClientReal.setWsHandler(wsHandler); - - WebRTCClient webRTCClient = spy(webRTCClientReal); - final Handler handler = getMockHandler(); - webRTCClient.setHandler(handler); - RtpParameters.DegradationPreference degradationPreference = RtpParameters.DegradationPreference.BALANCED; webRTCClient.setDegradationPreference(streamId, degradationPreference); //will return imediately - WebRTCClient.PeerInfo peerInfo = new WebRTCClient.PeerInfo(streamId, IWebRTCClient.MODE_PUBLISH); + WebRTCClient.PeerInfo peerInfo = new WebRTCClient.PeerInfo(streamId, WebRTCClient.Mode.PUBLISH); webRTCClient.peers.put(streamId, peerInfo); PeerConnection pc = mock(PeerConnection.class); 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 717d3b6c..03e57836 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 @@ -13,11 +13,12 @@ import org.webrtc.IceCandidate; import org.webrtc.SessionDescription; -import java.net.URI; -import java.net.URISyntaxException; import java.util.concurrent.Executors; import de.tavendo.autobahn.WebSocketConnection; +import io.antmedia.webrtcandroidframework.websocket.AntMediaSignallingEvents; +import io.antmedia.webrtcandroidframework.websocket.WebSocketConstants; +import io.antmedia.webrtcandroidframework.websocket.WebSocketHandler; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.*; diff --git a/webrtc-android-sample-app/build.gradle b/webrtc-android-sample-app/build.gradle index 779edb04..d6931809 100644 --- a/webrtc-android-sample-app/build.gradle +++ b/webrtc-android-sample-app/build.gradle @@ -28,11 +28,12 @@ android { } } + /* packagingOptions { pickFirst 'lib/arm64-v8a/libyuv.so' pickFirst 'lib/armeabi-v7a/libyuv.so' - } + */ compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 @@ -98,12 +99,12 @@ dependencies { implementation "androidx.lifecycle:lifecycle-viewmodel:2.5.1" implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1" debugImplementation "androidx.test:core:1.5.0" - implementation "io.github.crow-misia.libyuv:libyuv-android:0.25.0" implementation 'org.apache.commons:commons-lang3:3.0' androidTestImplementation 'com.squareup.okhttp3:okhttp:4.9.3' androidTestImplementation 'com.squareup.okhttp3:logging-interceptor:4.9.3' androidTestImplementation 'com.google.code.gson:gson:2.8.9' implementation(name: 'sdk_uvc_camera_v23092707_debug', ext: 'aar') + //implementation "io.github.crow-misia.libyuv:libyuv-android:0.25.0" } diff --git a/webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/ActivityTest.java b/webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/ActivityTest.java deleted file mode 100644 index c216a92c..00000000 --- a/webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/ActivityTest.java +++ /dev/null @@ -1,154 +0,0 @@ -package io.antmedia.webrtc_android_sample_app; - -import static androidx.test.espresso.Espresso.onView; -import static androidx.test.espresso.action.ViewActions.clearText; -import static androidx.test.espresso.action.ViewActions.click; -import static androidx.test.espresso.action.ViewActions.typeText; -import static androidx.test.espresso.assertion.ViewAssertions.matches; -import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; -import static androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility; -import static androidx.test.espresso.matcher.ViewMatchers.withId; -import static androidx.test.espresso.matcher.ViewMatchers.withText; -import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; - -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; - -import androidx.test.core.app.ActivityScenario; -import androidx.test.core.app.ApplicationProvider; -import androidx.test.espresso.Espresso; -import androidx.test.espresso.IdlingRegistry; -import androidx.test.espresso.IdlingResource; -import androidx.test.espresso.assertion.ViewAssertions; -import androidx.test.espresso.matcher.ViewMatchers; -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.rule.GrantPermissionRule; -import androidx.test.uiautomator.By; -import androidx.test.uiautomator.UiDevice; -import androidx.test.uiautomator.UiObject2; -import androidx.test.uiautomator.Until; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; - -import static org.hamcrest.CoreMatchers.allOf; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.not; -import static org.junit.Assert.*; -import static io.antmedia.webrtc_android_sample_app.SettingsActivity.DEFAULT_WEBSOCKET_URL; -import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_VIDEO_BITRATE; -import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_VIDEO_FPS; -import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_VIDEO_HEIGHT; -import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_VIDEO_WIDTH; - -import io.antmedia.webrtcandroidframework.IWebRTCClient; - -/** - * Instrumented test, which will execute on an Android device. - * - * @see Testing documentation - */ -@RunWith(AndroidJUnit4.class) -public class ActivityTest { - - //match - private static final String START_NOW_TEXT = "Start now"; - - private IdlingResource mIdlingResource; - - @Rule - public GrantPermissionRule permissionRule - = GrantPermissionRule.grant(AbstractSampleSDKActivity.REQUIRED_PUBLISH_PERMISSIONS); - - @Before - public void before() { - //try before method to make @Rule run properly - } - - @Test - public void useAppContext() { - // Context of the app under test. - Context appContext = getInstrumentation().getTargetContext(); - assertEquals("io.antmedia.webrtc_android_sample_app", appContext.getPackageName()); - } - - @Test - public void testPlayStream() { - Intent intent = new Intent(ApplicationProvider.getApplicationContext(), MainActivity.class); - intent.putExtra(MainActivity.WEBRTC_MODE, IWebRTCClient.MODE_PLAY); - - ActivityScenario scenario = ActivityScenario.launch(intent); - - scenario.onActivity(new ActivityScenario.ActivityAction() { - @Override - public void perform(MainActivity activity) { - mIdlingResource = activity.getIdlingResource(); - IdlingRegistry.getInstance().register(mIdlingResource); - activity.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); - } - }); - - //stream556677i4d is the stream id in github actions - onView(withId(R.id.stream_id_edittext)).perform(clearText(), typeText("stream556677i4d")); - onView(withId(R.id.start_streaming_button)).check(matches(withText("Start Playing"))); - Espresso.closeSoftKeyboard(); - onView(withId(R.id.start_streaming_button)).perform(click()); - - - onView(withId(R.id.start_streaming_button)).check(matches(withText("Stop Playing"))); - - onView(withId(R.id.broadcasting_text_view)).check(ViewAssertions.matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))); - - //Stop playing - onView(withId(R.id.start_streaming_button)).perform(click()); - - onView(withId(R.id.broadcasting_text_view)).check(ViewAssertions.matches(withEffectiveVisibility(ViewMatchers.Visibility.GONE))); - - IdlingRegistry.getInstance().unregister(mIdlingResource); - - } - - /** - * Write test scenarios and implement it. - * Until that time, manuel test may be used - */ - @Test - public void testStartStopStream() { - - Intent intent = new Intent(ApplicationProvider.getApplicationContext(), MainActivity.class); - intent.putExtra(EXTRA_VIDEO_WIDTH, 160); - intent.putExtra(EXTRA_VIDEO_HEIGHT, 120); - intent.putExtra(EXTRA_VIDEO_FPS, 15); - intent.putExtra(EXTRA_VIDEO_BITRATE, 300); - ActivityScenario scenario = ActivityScenario.launch(intent); - - scenario.onActivity(new ActivityScenario.ActivityAction() { - @Override - public void perform(MainActivity activity) { - mIdlingResource = activity.getIdlingResource(); - IdlingRegistry.getInstance().register(mIdlingResource); - activity.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); - } - }); - //1. start stream and check that it's playing - - onView(withId(R.id.broadcasting_text_view)).check(matches(not(isDisplayed()))); - onView(withId(R.id.start_streaming_button)).check(matches(withText("Start Publishing"))); - onView(withId(R.id.start_streaming_button)).perform(click()); - - onView(withId(R.id.start_streaming_button)).check(matches(withText("Stop Publishing"))); - - onView(withId(R.id.broadcasting_text_view)).check(ViewAssertions.matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))); - - //2. stop stream and check that it's stopped - onView(withId(R.id.start_streaming_button)).perform(click()); - - onView(withId(R.id.broadcasting_text_view)).check(ViewAssertions.matches(withEffectiveVisibility(ViewMatchers.Visibility.GONE))); - - IdlingRegistry.getInstance().unregister(mIdlingResource); - - } -} 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 deleted file mode 100644 index cbac37b8..00000000 --- a/webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/ConferenceActivityTest.java +++ /dev/null @@ -1,132 +0,0 @@ -package io.antmedia.webrtc_android_sample_app; - -import static androidx.test.espresso.Espresso.onView; -import static androidx.test.espresso.action.ViewActions.click; -import static androidx.test.espresso.assertion.ViewAssertions.matches; -import static androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility; -import static androidx.test.espresso.matcher.ViewMatchers.withId; -import static androidx.test.espresso.matcher.ViewMatchers.withText; -import static org.junit.Assert.assertNotNull; - -import android.content.Intent; -import android.util.Log; - -import androidx.test.core.app.ActivityScenario; -import androidx.test.core.app.ApplicationProvider; -import androidx.test.espresso.IdlingRegistry; -import androidx.test.espresso.IdlingResource; -import androidx.test.espresso.assertion.ViewAssertions; -import androidx.test.espresso.matcher.ViewMatchers; -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.rule.GrantPermissionRule; - -import org.apache.commons.lang3.RandomStringUtils; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TestWatcher; -import org.junit.runner.Description; -import org.junit.runner.RunWith; - -import java.io.IOException; - -import okhttp3.Call; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.Response; - - -/** - * Instrumented test, which will execute on an Android device. - * - * @see Testing documentation - */ -@RunWith(AndroidJUnit4.class) -public class ConferenceActivityTest { - - //match - private static final String START_NOW_TEXT = "Start now"; - - private IdlingResource mIdlingResource; - - @Rule - public GrantPermissionRule permissionRule - = GrantPermissionRule.grant(AbstractSampleSDKActivity.REQUIRED_PUBLISH_PERMISSIONS); - private String roomName; - - @Before - public void before() { - //try before method to make @Rule run properly - System.out.println("before test"); - } - - @After - public void after() { - try { - Thread.sleep(5000); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - } - - @Rule - public TestWatcher watchman= new TestWatcher() { - @Override - protected void failed(Throwable e, Description description) { - Log.i("TestWatcher", "*** "+description + " failed!\n"); - } - - @Override - protected void succeeded(Description description) { - Log.i("TestWatcher", "*** "+description + " succeeded!\n"); - } - - protected void starting(Description description) { - Log.i("TestWatcher", "******\n*** "+description + " starting!\n"); - } - - protected void finished(Description description) { - Log.i("TestWatcher", "*** "+description + " finished!\n******\n"); - } - }; - - - @Test - public void testJoinConfereceActivity() { - Intent intent = new Intent(ApplicationProvider.getApplicationContext(), ConferenceActivity.class); - roomName = "room_"+RandomStringUtils.randomNumeric(3); - ActivityScenario scenario = ActivityScenario.launch(intent); - - scenario.onActivity(new ActivityScenario.ActivityAction() { - @Override - public void perform(ConferenceActivity activity) { - SettingsActivity.changeRoomName(activity, roomName); - mIdlingResource = activity.getIdlingResource(); - IdlingRegistry.getInstance().register(mIdlingResource); - activity.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); - } - }); - - onView(withId(R.id.join_conference_button)).check(matches(withText("Join Conference"))); - onView(withId(R.id.join_conference_button)).perform(click()); - - onView(withId(R.id.join_conference_button)).check(matches(withText("Leave"))); - - onView(withId(R.id.broadcasting_text_view)).check(ViewAssertions.matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))); - - try { - Thread.sleep(2000); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - - onView(withId(R.id.join_conference_button)).perform(click()); - - onView(withId(R.id.broadcasting_text_view)).check(ViewAssertions.matches(withEffectiveVisibility(ViewMatchers.Visibility.GONE))); - - IdlingRegistry.getInstance().unregister(mIdlingResource); - - } - -} diff --git a/webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/HomeActivityTest.java b/webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/HomeActivityTest.java deleted file mode 100644 index 0560243b..00000000 --- a/webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/HomeActivityTest.java +++ /dev/null @@ -1,23 +0,0 @@ -package io.antmedia.webrtc_android_sample_app; - -import androidx.test.ext.junit.rules.ActivityScenarioRule; -import androidx.test.ext.junit.runners.AndroidJUnit4; - -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; - -@RunWith(AndroidJUnit4.class) -public class HomeActivityTest { - - - @Rule - public ActivityScenarioRule activityScenarioRule - = new ActivityScenarioRule<>(HomeActivity.class); - - @Test - public void testHomeActivity() { - - } - -} diff --git a/webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/MP3PublishActivityTest.java b/webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/MP3PublishActivityTest.java deleted file mode 100644 index b2a6b6de..00000000 --- a/webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/MP3PublishActivityTest.java +++ /dev/null @@ -1,140 +0,0 @@ -package io.antmedia.webrtc_android_sample_app; - -import static androidx.test.espresso.Espresso.onView; -import static androidx.test.espresso.action.ViewActions.click; -import static androidx.test.espresso.assertion.ViewAssertions.matches; -import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; -import static androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility; -import static androidx.test.espresso.matcher.ViewMatchers.withId; -import static androidx.test.espresso.matcher.ViewMatchers.withText; -import static org.hamcrest.CoreMatchers.not; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import android.content.Context; -import android.content.Intent; -import android.util.Log; - -import androidx.test.core.app.ActivityScenario; -import androidx.test.core.app.ApplicationProvider; -import androidx.test.espresso.IdlingRegistry; -import androidx.test.espresso.IdlingResource; -import androidx.test.espresso.assertion.ViewAssertions; -import androidx.test.espresso.matcher.ViewMatchers; -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.platform.app.InstrumentationRegistry; -import androidx.test.rule.GrantPermissionRule; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; - -import io.antmedia.webrtcandroidframework.IWebRTCClient; - -/** - * Instrumented test, which will execute on an Android device. - * - * @see Testing documentation - */ -@RunWith(AndroidJUnit4.class) -public class MP3PublishActivityTest { - - private IdlingResource mIdlingResource; - - @Rule - public GrantPermissionRule permissionRule - = GrantPermissionRule.grant(AbstractSampleSDKActivity.REQUIRED_PUBLISH_PERMISSIONS); - - @Before - public void before() { - - } - - public String downloadTestFile() { - try { - Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); - - InputStream inputStream = appContext.getResources().openRawResource(R.raw.sample_44100_stereo); - - File destinationFile = new File(appContext.getCacheDir(), "sample_44100_stereo.mp3"); - - //File destinationFile = new File(Environment.DIRECTORY_DOWNLOADS, "sample_44100_stereo.mp3"); - FileOutputStream outputStream = new FileOutputStream(destinationFile); - - byte[] buffer = new byte[1024]; - int length; - while ((length = inputStream.read(buffer)) > 0) { - outputStream.write(buffer, 0, length); - } - - outputStream.close(); - inputStream.close(); - - return destinationFile.getAbsolutePath(); - - // File copied successfully - } catch (IOException e) { - fail("test file cannot be copied"); - e.printStackTrace(); - // Handle the exception - } - - return null; - } - - - @Test - public void testCustomAudioFeed() { - - Intent intent = new Intent(ApplicationProvider.getApplicationContext(), MP3PublishActivity.class); - intent.putExtra(MainActivity.WEBRTC_MODE, IWebRTCClient.MODE_PUBLISH); - - ActivityScenario scenario = ActivityScenario.launch(intent); - - scenario.onActivity(new ActivityScenario.ActivityAction() { - @Override - public void perform(MP3PublishActivity activity) { - mIdlingResource = activity.getIdlingResource(); - IdlingRegistry.getInstance().register(mIdlingResource); - activity.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); - String path = downloadTestFile(); - assertNotNull(path); - Log.d("MP3PublishActivityTest", "path: " + path); - activity.mp3Publisher.setFilePath(path); - } - }); - - - - onView(withId(R.id.broadcasting_text_view)).check(matches(not(isDisplayed()))); - onView(withId(R.id.start_streaming_button)).check(matches(withText("Start Publishing"))); - onView(withId(R.id.start_streaming_button)).perform(click()); - - - onView(withId(R.id.start_streaming_button)).check(matches(withText("Stop Publishing"))); - - onView(withId(R.id.broadcasting_text_view)).check(ViewAssertions.matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))); - - try { - Thread.sleep(5000); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - - //2. stop stream and check that it's stopped - onView(withId(R.id.start_streaming_button)).perform(click()); - - onView(withId(R.id.broadcasting_text_view)).check(ViewAssertions.matches(withEffectiveVisibility(ViewMatchers.Visibility.GONE))); - - IdlingRegistry.getInstance().unregister(mIdlingResource); - - } -} diff --git a/webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/MultitrackConferenceActivityTest.java b/webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/MultitrackConferenceActivityTest.java deleted file mode 100644 index e0ad5c4c..00000000 --- a/webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/MultitrackConferenceActivityTest.java +++ /dev/null @@ -1,146 +0,0 @@ -package io.antmedia.webrtc_android_sample_app; - -import static androidx.test.espresso.Espresso.onView; -import static androidx.test.espresso.action.ViewActions.click; -import static androidx.test.espresso.assertion.ViewAssertions.matches; -import static androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility; -import static androidx.test.espresso.matcher.ViewMatchers.withId; -import static androidx.test.espresso.matcher.ViewMatchers.withText; -import static org.hamcrest.CoreMatchers.not; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; - -import android.content.Intent; -import android.util.Log; - -import androidx.test.core.app.ActivityScenario; -import androidx.test.core.app.ApplicationProvider; -import androidx.test.espresso.IdlingRegistry; -import androidx.test.espresso.IdlingResource; -import androidx.test.espresso.assertion.ViewAssertions; -import androidx.test.espresso.matcher.ViewMatchers; -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.platform.app.InstrumentationRegistry; -import androidx.test.rule.GrantPermissionRule; - -import org.apache.commons.lang3.RandomStringUtils; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TestWatcher; -import org.junit.runner.Description; -import org.junit.runner.RunWith; - -import java.io.IOException; - -import okhttp3.Call; -import okhttp3.HttpUrl; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.Response; - - -/** - * Instrumented test, which will execute on an Android device. - * - * @see Testing documentation - */ -@RunWith(AndroidJUnit4.class) -public class MultitrackConferenceActivityTest { - - //match - private static final String START_NOW_TEXT = "Start now"; - - private IdlingResource mIdlingResource; - - @Rule - public GrantPermissionRule permissionRule - = GrantPermissionRule.grant(AbstractSampleSDKActivity.REQUIRED_PUBLISH_PERMISSIONS); - private String runningTest; - - @Before - public void before() { - //try before method to make @Rule run properly - System.out.println("before test"); - InstrumentationRegistry.getInstrumentation().waitForIdleSync(); - System.out.println("after sleep"); - - } - - @After - public void after() { - System.out.println("after test"); - try { - Thread.sleep(10000); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - - } - - @Rule - public TestWatcher watchman= new TestWatcher() { - @Override - protected void failed(Throwable e, Description description) { - Log.i("TestWatcher", "*** "+description + " failed!\n"); - } - - @Override - protected void succeeded(Description description) { - Log.i("TestWatcher", "*** "+description + " succeeded!\n"); - } - - protected void starting(Description description) { - Log.i("TestWatcher", "******\n*** "+description + " starting!\n"); - runningTest = description.toString(); - } - - protected void finished(Description description) { - Log.i("TestWatcher", "*** "+description + " finished!\n******\n"); - } - }; - - - @Test - public void testJoinMultitrackRoom() { - Intent intent = new Intent(ApplicationProvider.getApplicationContext(), MultitrackConferenceActivity.class); - final String roomName = "room_" + RandomStringUtils.randomNumeric(3); - - ActivityScenario scenario = ActivityScenario.launch(intent); - - scenario.onActivity(new ActivityScenario.ActivityAction() { - @Override - public void perform(MultitrackConferenceActivity activity) { - SettingsActivity.changeRoomName(activity, roomName); - - mIdlingResource = activity.getIdlingResource(); - IdlingRegistry.getInstance().register(mIdlingResource); - activity.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); - } - }); - - onView(withId(R.id.join_conference_button)).check(matches(withText("Join Conference"))); - onView(withId(R.id.join_conference_button)).perform(click()); - - onView(withId(R.id.join_conference_button)).check(matches(withText("Leave"))); - - //TODO remove sleep - try { - Thread.sleep(5000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - - onView(withId(R.id.broadcasting_text_view)).check(ViewAssertions.matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))); - - onView(withId(R.id.join_conference_button)).perform(click()); - - onView(withId(R.id.broadcasting_text_view)).check(ViewAssertions.matches(withEffectiveVisibility(ViewMatchers.Visibility.GONE))); - - IdlingRegistry.getInstance().unregister(mIdlingResource); - - } - - -} diff --git a/webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/DataChannelActivityTest.java b/webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/PlayActivityTest.java similarity index 60% rename from webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/DataChannelActivityTest.java rename to webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/PlayActivityTest.java index a8ce6b2b..3314aca5 100644 --- a/webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/DataChannelActivityTest.java +++ b/webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/PlayActivityTest.java @@ -12,37 +12,26 @@ import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; import static org.hamcrest.CoreMatchers.not; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static io.antmedia.webrtc_android_sample_app.SettingsActivity.DEFAULT_WEBSOCKET_URL; -import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_VIDEO_BITRATE; -import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_VIDEO_FPS; -import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_VIDEO_HEIGHT; -import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_VIDEO_WIDTH; import android.content.Context; import android.content.Intent; -import android.content.SharedPreferences; import androidx.test.core.app.ActivityScenario; import androidx.test.core.app.ApplicationProvider; import androidx.test.espresso.Espresso; import androidx.test.espresso.IdlingRegistry; import androidx.test.espresso.IdlingResource; -import androidx.test.espresso.assertion.ViewAssertions; import androidx.test.espresso.matcher.ViewMatchers; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.rule.GrantPermissionRule; -import androidx.test.uiautomator.By; -import androidx.test.uiautomator.UiDevice; -import androidx.test.uiautomator.UiObject2; -import androidx.test.uiautomator.Until; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; -import io.antmedia.webrtcandroidframework.IWebRTCClient; +import io.antmedia.webrtc_android_sample_app.basic.PlayActivity; +import io.antmedia.webrtcandroidframework.core.PermissionsHandler; /** * Instrumented test, which will execute on an Android device. @@ -50,22 +39,21 @@ * @see Testing documentation */ @RunWith(AndroidJUnit4.class) -public class DataChannelActivityTest { - - //match - private static final String START_NOW_TEXT = "Start now"; - +public class PlayActivityTest { private IdlingResource mIdlingResource; @Rule public GrantPermissionRule permissionRule - = GrantPermissionRule.grant(AbstractSampleSDKActivity.REQUIRED_PUBLISH_PERMISSIONS); + = GrantPermissionRule.grant(PermissionsHandler.REQUIRED_EXTENDED_PERMISSIONS); @Before public void before() { //try before method to make @Rule run properly } + @Rule + public TestLogger testLogger = new TestLogger(); + @Test public void useAppContext() { // Context of the app under test. @@ -74,40 +62,40 @@ public void useAppContext() { } @Test - public void testDataChannelOnlyActivityScreen() { - Intent intent = new Intent(ApplicationProvider.getApplicationContext(), DataChannelOnlyActivity.class); - ActivityScenario scenario = ActivityScenario.launch(intent); - - - scenario.onActivity(new ActivityScenario.ActivityAction() { + public void testPlaying() { + Intent intent = new Intent(ApplicationProvider.getApplicationContext(), PlayActivity.class); + ActivityScenario scenario = ActivityScenario.launch(intent); + scenario.onActivity(new ActivityScenario.ActivityAction() { @Override - public void perform(DataChannelOnlyActivity activity) { + public void perform(PlayActivity activity) { mIdlingResource = activity.getIdlingResource(); IdlingRegistry.getInstance().register(mIdlingResource); activity.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); } }); - UiDevice device = UiDevice.getInstance(getInstrumentation()); + onView(withId(R.id.broadcasting_text_view)).check(matches(not(isDisplayed()))); + //stream556677i4d is the stream id in github actions + onView(withId(R.id.stream_id_edittext)).perform(clearText(), typeText("stream556677i4d")); onView(withId(R.id.start_streaming_button)).check(matches(withText("Start"))); Espresso.closeSoftKeyboard(); onView(withId(R.id.start_streaming_button)).perform(click()); + onView(withId(R.id.start_streaming_button)).check(matches(withText("Stop"))); - onView(withId(R.id.broadcasting_text_view)).check(ViewAssertions.matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))); - onView(withId(R.id.message_text_input)).perform(typeText("hello")); - Espresso.closeSoftKeyboard(); - onView(withId(R.id.send_message_button)).perform(click()); + onView(withId(R.id.broadcasting_text_view)).check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))); - //2. stop stream and check that it's stopped + //Stop playing onView(withId(R.id.start_streaming_button)).perform(click()); - onView(withId(R.id.broadcasting_text_view)).check(ViewAssertions.matches(withEffectiveVisibility(ViewMatchers.Visibility.GONE))); + onView(withId(R.id.broadcasting_text_view)).check(matches(withEffectiveVisibility(ViewMatchers.Visibility.GONE))); IdlingRegistry.getInstance().unregister(mIdlingResource); + } + } diff --git a/webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/CustomVideoFeedActivityTest.java b/webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/PublishActivityTest.java similarity index 63% rename from webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/CustomVideoFeedActivityTest.java rename to webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/PublishActivityTest.java index ef88b2f6..499cd757 100644 --- a/webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/CustomVideoFeedActivityTest.java +++ b/webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/PublishActivityTest.java @@ -7,17 +7,18 @@ import static androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility; import static androidx.test.espresso.matcher.ViewMatchers.withId; import static androidx.test.espresso.matcher.ViewMatchers.withText; +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; import static org.hamcrest.CoreMatchers.not; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import android.content.Context; import android.content.Intent; import androidx.test.core.app.ActivityScenario; import androidx.test.core.app.ApplicationProvider; +import androidx.test.espresso.Espresso; import androidx.test.espresso.IdlingRegistry; import androidx.test.espresso.IdlingResource; -import androidx.test.espresso.assertion.ViewAssertions; import androidx.test.espresso.matcher.ViewMatchers; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.rule.GrantPermissionRule; @@ -27,7 +28,8 @@ import org.junit.Test; import org.junit.runner.RunWith; -import io.antmedia.webrtcandroidframework.IWebRTCClient; +import io.antmedia.webrtc_android_sample_app.basic.PublishActivity; +import io.antmedia.webrtcandroidframework.core.PermissionsHandler; /** * Instrumented test, which will execute on an Android device. @@ -35,50 +37,59 @@ * @see Testing documentation */ @RunWith(AndroidJUnit4.class) -public class CustomVideoFeedActivityTest { - +public class PublishActivityTest { private IdlingResource mIdlingResource; @Rule public GrantPermissionRule permissionRule - = GrantPermissionRule.grant(AbstractSampleSDKActivity.REQUIRED_PUBLISH_PERMISSIONS); + = GrantPermissionRule.grant(PermissionsHandler.REQUIRED_EXTENDED_PERMISSIONS); @Before public void before() { //try before method to make @Rule run properly } + @Rule + public TestLogger testLogger = new TestLogger(); + @Test - public void testCustomVideoFeed() { - Intent intent = new Intent(ApplicationProvider.getApplicationContext(), CustomFrameActivity.class); - intent.putExtra(MainActivity.WEBRTC_MODE, IWebRTCClient.MODE_PUBLISH); + public void useAppContext() { + // Context of the app under test. + Context appContext = getInstrumentation().getTargetContext(); + assertEquals("io.antmedia.webrtc_android_sample_app", appContext.getPackageName()); + } - ActivityScenario scenario = ActivityScenario.launch(intent); + @Test + public void testPublishing() { + Intent intent = new Intent(ApplicationProvider.getApplicationContext(), PublishActivity.class); + ActivityScenario scenario = ActivityScenario.launch(intent); - scenario.onActivity(new ActivityScenario.ActivityAction() { + scenario.onActivity(new ActivityScenario.ActivityAction() { @Override - public void perform(CustomFrameActivity activity) { + public void perform(PublishActivity activity) { mIdlingResource = activity.getIdlingResource(); IdlingRegistry.getInstance().register(mIdlingResource); activity.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); } }); - onView(withId(R.id.broadcasting_text_view)).check(matches(not(isDisplayed()))); - onView(withId(R.id.start_streaming_button)).check(matches(withText("Start Publishing"))); + onView(withId(R.id.start_streaming_button)).check(matches(withText("Start"))); + Espresso.closeSoftKeyboard(); onView(withId(R.id.start_streaming_button)).perform(click()); - onView(withId(R.id.start_streaming_button)).check(matches(withText("Stop Publishing"))); - onView(withId(R.id.broadcasting_text_view)).check(ViewAssertions.matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))); + onView(withId(R.id.start_streaming_button)).check(matches(withText("Stop"))); - //2. stop stream and check that it's stopped + onView(withId(R.id.broadcasting_text_view)).check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))); + + //Stop playing onView(withId(R.id.start_streaming_button)).perform(click()); - onView(withId(R.id.broadcasting_text_view)).check(ViewAssertions.matches(withEffectiveVisibility(ViewMatchers.Visibility.GONE))); + onView(withId(R.id.broadcasting_text_view)).check(matches(withEffectiveVisibility(ViewMatchers.Visibility.GONE))); IdlingRegistry.getInstance().unregister(mIdlingResource); } + } diff --git a/webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/PushToTalkActivityTest.java b/webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/PushToTalkActivityTest.java deleted file mode 100644 index 07fd3582..00000000 --- a/webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/PushToTalkActivityTest.java +++ /dev/null @@ -1,68 +0,0 @@ -package io.antmedia.webrtc_android_sample_app; - -import static androidx.test.espresso.Espresso.onView; -import static androidx.test.espresso.action.ViewActions.click; -import static androidx.test.espresso.action.ViewActions.longClick; -import static androidx.test.espresso.assertion.ViewAssertions.matches; -import static androidx.test.espresso.matcher.ViewMatchers.withId; -import static androidx.test.espresso.matcher.ViewMatchers.withText; -import static org.hamcrest.CoreMatchers.not; - -import android.content.Intent; - -import androidx.test.core.app.ActivityScenario; -import androidx.test.core.app.ApplicationProvider; -import androidx.test.espresso.IdlingRegistry; -import androidx.test.espresso.IdlingResource; -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.rule.GrantPermissionRule; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; - -import io.antmedia.webrtcandroidframework.IWebRTCClient; - -/** - * Instrumented test, which will execute on an Android device. - * - * @see Testing documentation - */ -@RunWith(AndroidJUnit4.class) -public class PushToTalkActivityTest { - - private IdlingResource mIdlingResource; - - @Rule - public GrantPermissionRule permissionRule - = GrantPermissionRule.grant(AbstractSampleSDKActivity.REQUIRED_PUBLISH_PERMISSIONS); - - @Before - public void before() { - //try before method to make @Rule run properly - } - - @Test - public void testCustomVideoFeed() { - Intent intent = new Intent(ApplicationProvider.getApplicationContext(), PushToTalkActivity.class); - intent.putExtra(MainActivity.WEBRTC_MODE, IWebRTCClient.MODE_PUBLISH); - - ActivityScenario scenario = ActivityScenario.launch(intent); - - scenario.onActivity(new ActivityScenario.ActivityAction() { - @Override - public void perform(PushToTalkActivity activity) { - mIdlingResource = activity.getIdlingResource(); - IdlingRegistry.getInstance().register(mIdlingResource); - activity.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); - } - }); - - onView(withId(R.id.talkButton)).perform(longClick()); - onView(withId(R.id.talkButton)).check(matches(withText("Talk"))); - - IdlingRegistry.getInstance().unregister(mIdlingResource); - - } -} diff --git a/webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/RemoteParticipant.java b/webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/RemoteParticipant.java deleted file mode 100644 index f261e46c..00000000 --- a/webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/RemoteParticipant.java +++ /dev/null @@ -1,95 +0,0 @@ -package io.antmedia.webrtc_android_sample_app; - -import static org.junit.Assert.assertNotNull; - -import android.util.Log; - -import org.apache.commons.lang3.RandomStringUtils; - -import java.io.IOException; - -import okhttp3.Call; -import okhttp3.HttpUrl; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.Response; - -class RemoteParticipant { - - NetworkClient client = new NetworkClient(); - String response = null; - String participantName = "p_"+ RandomStringUtils.randomNumeric(3); - - private String roomName; - private String runningTest; - - public RemoteParticipant(String roomName, String runningTest) { - this.roomName = roomName; - this.runningTest = runningTest; - } - - public class NetworkClient { - - private static final String REST_IP = "10.0.2.2"; - private static final int REST_PORT = 3030; - - private final OkHttpClient client = new OkHttpClient(); - - public String get(String path, String participantName) throws IOException { - HttpUrl httpUrl = new HttpUrl.Builder() - .scheme("http") - .host(REST_IP) - .port(REST_PORT) - .addPathSegment(path) - .addQueryParameter("room", roomName) - .addQueryParameter("test", runningTest) - .addQueryParameter("participant", participantName) - .build(); - - - Request request = new Request.Builder() - .url(httpUrl) - .header("Connection", "close") // <== solution, not declare in Interceptor - .build(); - - Call call = client.newCall(request); - Response response = call.execute(); - return response.body().string(); - } - } - - public void join() { - try { - response = client.get("create", participantName); - Log.i("RemoteParticipant", "create: " + response); - assertNotNull(response); - - response = client.get("join", participantName); - Log.i("RemoteParticipant", "join: " + response); - assertNotNull(response); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - public void leave() { - try { - response = client.get("leave", participantName); - Log.i("RemoteParticipant", "leave: " + response); - assertNotNull(response); - - response = client.get("delete", participantName); - Log.i("RemoteParticipant", "delete: " + response); - assertNotNull(response); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - public static RemoteParticipant addParticipant(String roomName, String runningTest) { - RemoteParticipant participant = new RemoteParticipant(roomName, runningTest); - participant.join(); - - return participant; - } -} \ No newline at end of file diff --git a/webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/ScreenCaptureActivityTest.java b/webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/ScreenCaptureActivityTest.java deleted file mode 100644 index 46d626df..00000000 --- a/webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/ScreenCaptureActivityTest.java +++ /dev/null @@ -1,143 +0,0 @@ -package io.antmedia.webrtc_android_sample_app; - -import static androidx.test.espresso.Espresso.onView; -import static androidx.test.espresso.action.ViewActions.clearText; -import static androidx.test.espresso.action.ViewActions.click; -import static androidx.test.espresso.action.ViewActions.typeText; -import static androidx.test.espresso.assertion.ViewAssertions.matches; -import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; -import static androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility; -import static androidx.test.espresso.matcher.ViewMatchers.withId; -import static androidx.test.espresso.matcher.ViewMatchers.withText; -import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; -import static org.hamcrest.CoreMatchers.not; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static io.antmedia.webrtc_android_sample_app.SettingsActivity.DEFAULT_WEBSOCKET_URL; -import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_VIDEO_BITRATE; -import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_VIDEO_FPS; -import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_VIDEO_HEIGHT; -import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_VIDEO_WIDTH; - -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; - -import androidx.test.core.app.ActivityScenario; -import androidx.test.core.app.ApplicationProvider; -import androidx.test.espresso.Espresso; -import androidx.test.espresso.IdlingRegistry; -import androidx.test.espresso.IdlingResource; -import androidx.test.espresso.assertion.ViewAssertions; -import androidx.test.espresso.matcher.ViewMatchers; -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.rule.GrantPermissionRule; -import androidx.test.uiautomator.By; -import androidx.test.uiautomator.UiDevice; -import androidx.test.uiautomator.UiObject2; -import androidx.test.uiautomator.Until; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; - -import io.antmedia.webrtcandroidframework.IWebRTCClient; - -/** - * Instrumented test, which will execute on an Android device. - * - * @see Testing documentation - */ -@RunWith(AndroidJUnit4.class) -public class ScreenCaptureActivityTest { - - //match - private static final String START_NOW_TEXT = "Start now"; - - private IdlingResource mIdlingResource; - - @Rule - public GrantPermissionRule permissionRule - = GrantPermissionRule.grant(AbstractSampleSDKActivity.REQUIRED_PUBLISH_PERMISSIONS); - - @Before - public void before() { - //try before method to make @Rule run properly - } - - @Test - public void useAppContext() { - // Context of the app under test. - Context appContext = getInstrumentation().getTargetContext(); - assertEquals("io.antmedia.webrtc_android_sample_app", appContext.getPackageName()); - } - - - /** - * This test should be in another method but cannot get the full logcat so it's moved here - */ - @Test - public void testPublishScreen() { - Intent intent = new Intent(ApplicationProvider.getApplicationContext(), ScreenCaptureActivity.class); - ActivityScenario scenario = ActivityScenario.launch(intent); - - - scenario.onActivity(new ActivityScenario.ActivityAction() { - - @Override - public void perform(ScreenCaptureActivity activity) { - mIdlingResource = activity.getIdlingResource(); - IdlingRegistry.getInstance().register(mIdlingResource); - activity.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); - } - }); - - UiDevice device = UiDevice.getInstance(getInstrumentation()); - - onView(withId(R.id.rbScreen)).perform(click()); - UiObject2 button = device.wait(Until.findObject(By.text("Start now")), 100000); - assertNotNull(button); - button.click(); - - //this switch operation causes to crash so that it's added here as test - onView(withId(R.id.rbFront)).perform(click()); - onView(withId(R.id.rbScreen)).perform(click()); - - - - onView(withId(R.id.start_streaming_button)).check(matches(withText("Start Streaming"))); - //Espresso.closeSoftKeyboard(); - onView(withId(R.id.start_streaming_button)).perform(click()); - - onView(withId(R.id.start_streaming_button)).check(matches(withText("Stop Streaming"))); - onView(withId(R.id.broadcasting_text_view)).check(ViewAssertions.matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))); - - //2. stop stream and check that it's stopped - onView(withId(R.id.start_streaming_button)).perform(click()); - onView(withId(R.id.broadcasting_text_view)).check(ViewAssertions.matches(withEffectiveVisibility(ViewMatchers.Visibility.GONE))); - - - //Publish again without because it was failing - onView(withId(R.id.start_streaming_button)).check(matches(withText("Start Streaming"))); - - //FIXME: without this sleep, it's failing because onFinish event received but resources are not closed yet - try { - Thread.sleep(30000); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - onView(withId(R.id.start_streaming_button)).perform(click()); - - //Check it's publishing again - onView(withId(R.id.start_streaming_button)).check(matches(withText("Stop Streaming"))); - onView(withId(R.id.broadcasting_text_view)).check(ViewAssertions.matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))); - - //Stop publishing - onView(withId(R.id.start_streaming_button)).perform(click()); - onView(withId(R.id.broadcasting_text_view)).check(ViewAssertions.matches(withEffectiveVisibility(ViewMatchers.Visibility.GONE))); - - - IdlingRegistry.getInstance().unregister(mIdlingResource); - } -} diff --git a/webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/SettingsActivityTest.java b/webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/SettingsActivityTest.java deleted file mode 100644 index 9d1a63a1..00000000 --- a/webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/SettingsActivityTest.java +++ /dev/null @@ -1,122 +0,0 @@ -package io.antmedia.webrtc_android_sample_app; - -import static androidx.test.espresso.Espresso.onView; -import static androidx.test.espresso.action.ViewActions.clearText; -import static androidx.test.espresso.action.ViewActions.click; -import static androidx.test.espresso.action.ViewActions.typeText; -import static androidx.test.espresso.assertion.ViewAssertions.matches; -import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; -import static androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility; -import static androidx.test.espresso.matcher.ViewMatchers.withId; -import static androidx.test.espresso.matcher.ViewMatchers.withText; -import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; -import static org.hamcrest.CoreMatchers.not; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static io.antmedia.webrtc_android_sample_app.SettingsActivity.DEFAULT_WEBSOCKET_URL; -import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_VIDEO_BITRATE; -import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_VIDEO_FPS; -import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_VIDEO_HEIGHT; -import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_VIDEO_WIDTH; - -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; - -import androidx.test.core.app.ActivityScenario; -import androidx.test.core.app.ApplicationProvider; -import androidx.test.espresso.Espresso; -import androidx.test.espresso.IdlingRegistry; -import androidx.test.espresso.IdlingResource; -import androidx.test.espresso.assertion.ViewAssertions; -import androidx.test.espresso.matcher.ViewMatchers; -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.rule.GrantPermissionRule; -import androidx.test.uiautomator.By; -import androidx.test.uiautomator.UiDevice; -import androidx.test.uiautomator.UiObject2; -import androidx.test.uiautomator.Until; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; - -import io.antmedia.webrtcandroidframework.IWebRTCClient; - -/** - * Instrumented test, which will execute on an Android device. - * - * @see Testing documentation - */ -@RunWith(AndroidJUnit4.class) -public class SettingsActivityTest { - - //match - private static final String START_NOW_TEXT = "Start now"; - - private IdlingResource mIdlingResource; - - @Rule - public GrantPermissionRule permissionRule - = GrantPermissionRule.grant(AbstractSampleSDKActivity.REQUIRED_PUBLISH_PERMISSIONS); - - @Before - public void before() { - //try before method to make @Rule run properly - } - - @Test - public void useAppContext() { - // Context of the app under test. - Context appContext = getInstrumentation().getTargetContext(); - assertEquals("io.antmedia.webrtc_android_sample_app", appContext.getPackageName()); - } - - @Test - public void testSettingsActivity() { - Intent intent = new Intent(ApplicationProvider.getApplicationContext(), SettingsActivity.class); - ActivityScenario scenario = ActivityScenario.launch(intent); - - - scenario.onActivity(new ActivityScenario.ActivityAction() { - - @Override - public void perform(SettingsActivity activity) { - activity.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); - } - }); - - UiDevice device = UiDevice.getInstance(getInstrumentation()); - - //if default value has changed, it fails. - onView(withId(R.id.server_address)).check(matches(withText(DEFAULT_WEBSOCKET_URL))); - - String websocketURL = "ws://example.com/WebRTCAppEE/websocket"; - onView(withId(R.id.server_address)).perform(clearText()); - onView(withId(R.id.server_address)).perform(typeText(websocketURL)); - Espresso.closeSoftKeyboard(); - onView(withId(R.id.save_button)).perform(click()); - - - - scenario.onActivity(new ActivityScenario.ActivityAction() { - - @Override - public void perform(SettingsActivity activity) { - SharedPreferences sharedPreferences = - android.preference.PreferenceManager.getDefaultSharedPreferences(activity /* Activity context */); - String url = sharedPreferences.getString(activity.getString(R.string.serverAddress), DEFAULT_WEBSOCKET_URL); - assertEquals(websocketURL, url); - - } - }); - - onView(withId(R.id.server_address)).perform(clearText()); - onView(withId(R.id.server_address)).perform(typeText(DEFAULT_WEBSOCKET_URL)); - Espresso.closeSoftKeyboard(); - onView(withId(R.id.save_button)).perform(click()); - - - } -} diff --git a/webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/TestLogger.java b/webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/TestLogger.java new file mode 100644 index 00000000..54a16426 --- /dev/null +++ b/webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/TestLogger.java @@ -0,0 +1,26 @@ +package io.antmedia.webrtc_android_sample_app; + +import android.util.Log; + +import org.junit.rules.TestWatcher; +import org.junit.runner.Description; + +public class TestLogger extends TestWatcher { + @Override + protected void failed(Throwable e, Description description) { + Log.i("TestWatcher", "*** "+description + " failed!\n"); + } + + @Override + protected void succeeded(Description description) { + Log.i("TestWatcher", "*** "+description + " succeeded!\n"); + } + + protected void starting(Description description) { + Log.i("TestWatcher", "******\n*** "+description + " starting!\n"); + } + + protected void finished(Description description) { + Log.i("TestWatcher", "*** "+description + " finished!\n******\n"); + } +} diff --git a/webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/TrackBasedConferenceActivityTest.java b/webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/TrackBasedConferenceActivityTest.java deleted file mode 100644 index bcd1048f..00000000 --- a/webrtc-android-sample-app/src/androidTest/java/io/antmedia/webrtc_android_sample_app/TrackBasedConferenceActivityTest.java +++ /dev/null @@ -1,295 +0,0 @@ -package io.antmedia.webrtc_android_sample_app; - -import static androidx.test.espresso.Espresso.onView; -import static androidx.test.espresso.action.ViewActions.click; -import static androidx.test.espresso.assertion.ViewAssertions.matches; -import static androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility; -import static androidx.test.espresso.matcher.ViewMatchers.withId; -import static androidx.test.espresso.matcher.ViewMatchers.withText; -import static org.junit.Assert.assertNotNull; - -import android.content.Intent; -import android.util.Log; - -import androidx.test.core.app.ActivityScenario; -import androidx.test.core.app.ApplicationProvider; -import androidx.test.espresso.IdlingRegistry; -import androidx.test.espresso.IdlingResource; -import androidx.test.espresso.assertion.ViewAssertions; -import androidx.test.espresso.matcher.ViewMatchers; -import androidx.test.ext.junit.rules.ActivityScenarioRule; -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.platform.app.InstrumentationRegistry; -import androidx.test.rule.GrantPermissionRule; - -import org.apache.commons.lang3.RandomStringUtils; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TestWatcher; -import org.junit.runner.Description; -import org.junit.runner.RunWith; - -import java.io.IOException; - -import okhttp3.Call; -import okhttp3.HttpUrl; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.Response; - - -/** - * Instrumented test, which will execute on an Android device. - * - * @see Testing documentation - */ -@RunWith(AndroidJUnit4.class) -public class TrackBasedConferenceActivityTest { - - //match - private static final String START_NOW_TEXT = "Start now"; - - private IdlingResource mIdlingResource; - - @Rule - public GrantPermissionRule permissionRule - = GrantPermissionRule.grant(AbstractSampleSDKActivity.REQUIRED_PUBLISH_PERMISSIONS); - - @Rule - public ActivityScenarioRule activityScenarioRule = new ActivityScenarioRule<>(TrackBasedConferenceActivity.class); - private String runningTest; - - @Before - public void before() { - //try before method to make @Rule run properly - System.out.println("before test"); - - InstrumentationRegistry.getInstrumentation().waitForIdleSync(); - } - - @After - public void after() { - System.out.println("after test"); - try { - Thread.sleep(10000); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - - } - - @Rule - public TestWatcher watchman= new TestWatcher() { - - @Override - protected void failed(Throwable e, Description description) { - Log.i("TestWatcher", "*** "+description + " failed!\n"); - } - - @Override - protected void succeeded(Description description) { - Log.i("TestWatcher", "*** "+description + " succeeded!\n"); - } - - protected void starting(Description description) { - Log.i("TestWatcher", "******\n*** "+description + " starting!\n"); - runningTest = description.toString(); - } - - protected void finished(Description description) { - Log.i("TestWatcher", "*** "+description + " finished!\n******\n"); - } - }; - - @Test - public void testJoinMultitrackRoom() { - final String roomName = "room_" + RandomStringUtils.randomNumeric(3); - - activityScenarioRule.getScenario().onActivity(new ActivityScenario.ActivityAction() { - @Override - public void perform(TrackBasedConferenceActivity activity) { - SettingsActivity.changeRoomName(activity, roomName); - mIdlingResource = activity.getIdlingResource(); - IdlingRegistry.getInstance().register(mIdlingResource); - activity.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); - } - }); - - onView(withId(R.id.join_conference_button)).check(matches(withText("Join Conference"))); - onView(withId(R.id.join_conference_button)).perform(click()); - - onView(withId(R.id.join_conference_button)).check(matches(withText("Leave"))); - try { - Thread.sleep(5000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - - onView(withId(R.id.broadcasting_text_view)).check(ViewAssertions.matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))); - - onView(withId(R.id.join_conference_button)).perform(click()); - - onView(withId(R.id.broadcasting_text_view)).check(ViewAssertions.matches(withEffectiveVisibility(ViewMatchers.Visibility.GONE))); - - IdlingRegistry.getInstance().unregister(mIdlingResource); - } - - - - @Test - public void testJoinWithExternalParticipant() { - final String roomName = "room_" + RandomStringUtils.randomNumeric(3); - activityScenarioRule.getScenario().onActivity(new ActivityScenario.ActivityAction() { - @Override - public void perform(TrackBasedConferenceActivity activity) { - SettingsActivity.changeRoomName(activity, roomName); - mIdlingResource = activity.getIdlingResource(); - IdlingRegistry.getInstance().register(mIdlingResource); - activity.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); - } - }); - - onView(withId(R.id.join_conference_button)).check(matches(withText("Join Conference"))); - onView(withId(R.id.join_conference_button)).perform(click()); - - - RemoteParticipant participant = RemoteParticipant.addParticipant(roomName, runningTest); - - onView(withId(R.id.join_conference_button)).check(matches(withText("Leave"))); - - onView(withId(R.id.broadcasting_text_view)).check(ViewAssertions.matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))); - - onView(withId(R.id.join_conference_button)).perform(click()); - - onView(withId(R.id.broadcasting_text_view)).check(ViewAssertions.matches(withEffectiveVisibility(ViewMatchers.Visibility.GONE))); - - participant.leave(); - IdlingRegistry.getInstance().unregister(mIdlingResource); - - } - - //@Test - public void testJoinWithoutVideo() { - final String roomName = "room_" + RandomStringUtils.randomNumeric(3); - - activityScenarioRule.getScenario().onActivity(new ActivityScenario.ActivityAction() { - @Override - public void perform(TrackBasedConferenceActivity activity) { - SettingsActivity.changeRoomName(activity, roomName); - mIdlingResource = activity.getIdlingResource(); - IdlingRegistry.getInstance().register(mIdlingResource); - activity.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); - } - }); - - onView(withId(R.id.control_audio_button)).check(matches(withText("Disable Audio"))); - onView(withId(R.id.control_audio_button)).perform(click()); - - onView(withId(R.id.control_video_button)).check(matches(withText("Disable Video"))); - onView(withId(R.id.control_video_button)).perform(click()); - - onView(withId(R.id.join_conference_button)).check(matches(withText("Join Conference"))); - onView(withId(R.id.join_conference_button)).perform(click()); - - RemoteParticipant participant = RemoteParticipant.addParticipant(roomName, runningTest); - - onView(withId(R.id.control_audio_button)).check(matches(withText("Enable Audio"))); - onView(withId(R.id.control_audio_button)).perform(click()); - - onView(withId(R.id.control_video_button)).check(matches(withText("Enable Video"))); - onView(withId(R.id.control_video_button)).perform(click()); - - onView(withId(R.id.join_conference_button)).check(matches(withText("Leave"))); - - onView(withId(R.id.broadcasting_text_view)).check(ViewAssertions.matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))); - - onView(withId(R.id.join_conference_button)).perform(click()); - - onView(withId(R.id.broadcasting_text_view)).check(ViewAssertions.matches(withEffectiveVisibility(ViewMatchers.Visibility.GONE))); - - participant.leave(); - IdlingRegistry.getInstance().unregister(mIdlingResource); - - } - - - @Test - public void testJoinPlayOnlyAsFirstPerson() { - final String roomName = "room_" + RandomStringUtils.randomNumeric(3); - - activityScenarioRule.getScenario().onActivity(new ActivityScenario.ActivityAction() { - @Override - public void perform(TrackBasedConferenceActivity activity) { - SettingsActivity.changeRoomName(activity, roomName); - mIdlingResource = activity.getIdlingResource(); - IdlingRegistry.getInstance().register(mIdlingResource); - activity.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); - } - }); - - onView(withId(R.id.play_only_switch)).check(matches(withText("Play Only"))); - onView(withId(R.id.play_only_switch)).perform(click()); - - onView(withId(R.id.join_conference_button)).check(matches(withText("Join Conference"))); - onView(withId(R.id.join_conference_button)).perform(click()); - - RemoteParticipant participant = RemoteParticipant.addParticipant(roomName, runningTest); - - onView(withId(R.id.join_conference_button)).check(matches(withText("Leave"))); - - onView(withId(R.id.join_conference_button)).perform(click()); - - participant.leave(); - IdlingRegistry.getInstance().unregister(mIdlingResource); - - } - - @Test - public void testReconnect() { - final String roomName = "room_" + RandomStringUtils.randomNumeric(3); - - final TrackBasedConferenceActivity[] mactivity = new TrackBasedConferenceActivity[1]; - activityScenarioRule.getScenario().onActivity(new ActivityScenario.ActivityAction() { - @Override - public void perform(TrackBasedConferenceActivity activity) { - SettingsActivity.changeRoomName(activity, roomName); - mIdlingResource = activity.getIdlingResource(); - IdlingRegistry.getInstance().register(mIdlingResource); - activity.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); - mactivity[0] = activity; - } - }); - - onView(withId(R.id.join_conference_button)).check(matches(withText("Join Conference"))); - onView(withId(R.id.join_conference_button)).perform(click()); - - RemoteParticipant participant = RemoteParticipant.addParticipant(roomName, runningTest); - - mactivity[0].changeWifiState(false); - - try { - Thread.sleep(3000); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - mactivity[0].changeWifiState(true); - - - - onView(withId(R.id.join_conference_button)).check(matches(withText("Leave"))); - - onView(withId(R.id.broadcasting_text_view)).check(ViewAssertions.matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))); - - onView(withId(R.id.join_conference_button)).perform(click()); - - //onView(withId(R.id.broadcasting_text_view)).check(ViewAssertions.matches(withEffectiveVisibility(ViewMatchers.Visibility.GONE))); - - participant.leave(); - IdlingRegistry.getInstance().unregister(mIdlingResource); - - } - - -} diff --git a/webrtc-android-sample-app/src/main/AndroidManifest.xml b/webrtc-android-sample-app/src/main/AndroidManifest.xml index d009c08d..72d25fbf 100644 --- a/webrtc-android-sample-app/src/main/AndroidManifest.xml +++ b/webrtc-android-sample-app/src/main/AndroidManifest.xml @@ -35,7 +35,7 @@ android:usesCleartextTraffic="true" android:requestLegacyExternalStorage="true"> - @@ -45,89 +45,63 @@ - - - - - - - - - - - - + - - - - - - - + - - - - + - - + + + + + + diff --git a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/AbstractSampleSDKActivity.java b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/AbstractSampleSDKActivity.java deleted file mode 100644 index 52f860d3..00000000 --- a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/AbstractSampleSDKActivity.java +++ /dev/null @@ -1,335 +0,0 @@ -package io.antmedia.webrtc_android_sample_app; - -import android.Manifest; -import android.annotation.SuppressLint; -import android.app.Activity; -import android.content.Context; -import android.content.pm.PackageManager; -import android.os.Build; -import android.os.Bundle; -import android.util.Log; -import android.view.Window; -import android.view.WindowManager; -import android.widget.Toast; - -import androidx.core.app.ActivityCompat; -import androidx.test.espresso.IdlingResource; -import androidx.test.espresso.idling.CountingIdlingResource; - -import org.webrtc.DataChannel; -import org.webrtc.VideoTrack; - -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.concurrent.atomic.AtomicBoolean; - -import de.tavendo.autobahn.WebSocket; -import io.antmedia.webrtcandroidframework.PermissionCallback; -import io.antmedia.webrtcandroidframework.IDataChannelObserver; -import io.antmedia.webrtcandroidframework.IWebRTCListener; -import io.antmedia.webrtcandroidframework.StreamInfo; - -public abstract class AbstractSampleSDKActivity extends Activity implements IWebRTCListener, IDataChannelObserver { - public CountingIdlingResource idlingResource = new CountingIdlingResource("Load", true); - private PermissionCallback permissionCallback; - - public void incrementIdle() { - idlingResource.increment(); - } - public void decrementIdle() { - if (!idlingResource.isIdleNow()) { - idlingResource.decrement(); - } - } - - public static final String[] REQUIRED_PUBLISH_PERMISSIONS = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S ? - new String[] {Manifest.permission.RECORD_AUDIO, Manifest.permission.CAMERA, Manifest.permission.BLUETOOTH_CONNECT} - : - new String[] {Manifest.permission.RECORD_AUDIO, Manifest.permission.CAMERA}; - - // List of mandatory application permissions. - public static final String[] REQUIRED_MINIMUM_PERMISSIONS = {"android.permission.MODIFY_AUDIO_SETTINGS", - "android.permission.INTERNET"}; - - public IdlingResource getIdlingResource() { - return idlingResource; - } - - @SuppressLint("WrongViewCast") - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - // Set window styles for fullscreen-window size. Needs to be done before - // adding content. - requestWindowFeature(Window.FEATURE_NO_TITLE); - getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON - | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED - | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON); - //getWindow().getDecorView().setSystemUiVisibility(getSystemUiVisibility()); - } - - @Override - public void onBufferedAmountChange(long previousAmount, String dataChannelLabel) { - String messageText = "Data channel buffered amount changed: " + dataChannelLabel + ": " + previousAmount; - Log.d(AbstractSampleSDKActivity.class.getName(), messageText); - //Toast.makeText(this, messageText, Toast.LENGTH_LONG).show(); - } - - @Override - public void onStateChange(DataChannel.State state, String dataChannelLabel) { - String messageText = "Data channel state changed: " + dataChannelLabel + ": " + state; - Log.d(AbstractSampleSDKActivity.class.getName(), messageText); - //Toast.makeText(this, messageText, Toast.LENGTH_LONG).show(); - } - - @Override - public void onMessage(DataChannel.Buffer buffer, String dataChannelLabel) { - ByteBuffer data = buffer.data; - String messageText = new String(data.array(), StandardCharsets.UTF_8); - makeToast("New Message: " + messageText, Toast.LENGTH_LONG); - } - - @Override - public void onMessageSent(DataChannel.Buffer buffer, boolean successful) { - if (successful) { - ByteBuffer data = buffer.data; - final byte[] bytes = new byte[data.capacity()]; - data.get(bytes); - String messageText = new String(bytes, StandardCharsets.UTF_8); - - makeToast("Message is sent", Toast.LENGTH_SHORT); - } else { - makeToast("Could not send the text message", Toast.LENGTH_LONG); - } - } - - @Override - public void onDisconnected(String streamId) { - String messageText = "Disconnected for " + streamId; - Log.d(AbstractSampleSDKActivity.class.getName(), messageText); - makeToast(messageText, Toast.LENGTH_LONG); - } - - @Override - public void onPublishFinished(String streamId) { - String messageText = "Publish finished for " + streamId; - Log.d(AbstractSampleSDKActivity.class.getName(), messageText); - makeToast(messageText, Toast.LENGTH_LONG); - } - - @Override - public void onPlayFinished(String streamId) { - String messageText = "Play finished for " + streamId; - Log.d(AbstractSampleSDKActivity.class.getName(), messageText); - makeToast(messageText, Toast.LENGTH_LONG); - } - - @Override - public void onPublishStarted(String streamId) { - String messageText = "Publish started for " + streamId; - Log.d(AbstractSampleSDKActivity.class.getName(), messageText); - makeToast(messageText, Toast.LENGTH_LONG); - } - - @Override - public void onPlayStarted(String streamId) { - String messageText = "Play started for " + streamId; - Log.d(AbstractSampleSDKActivity.class.getName(), messageText); - makeToast(messageText, Toast.LENGTH_LONG); - } - - @Override - public void noStreamExistsToPlay(String streamId) { - String messageText = "No stream exists to play for " + streamId; - Log.d(AbstractSampleSDKActivity.class.getName(), messageText); - makeToast(messageText, Toast.LENGTH_LONG); - } - - @Override - public void onError(String description, String streamId) { - String messageText = "Error for " + streamId + " : " + description; - Log.d(AbstractSampleSDKActivity.class.getName(), messageText); - makeToast(messageText, Toast.LENGTH_LONG); - } - - @Override - public void onSignalChannelClosed(WebSocket.WebSocketConnectionObserver.WebSocketCloseNotification code, String streamId) { - String messageText = "Signal channel closed for " + streamId + " : " + code; - Log.d(AbstractSampleSDKActivity.class.getName(), messageText); - makeToast(messageText, Toast.LENGTH_LONG); - } - - @Override - public void streamIdInUse(String streamId) { - String messageText = "Stream id is already in use " + streamId; - Log.d(AbstractSampleSDKActivity.class.getName(), messageText); - makeToast(messageText, Toast.LENGTH_LONG); - } - - @Override - public void onIceConnected(String streamId) { - String messageText = "Ice connected for " + streamId; - Log.d(AbstractSampleSDKActivity.class.getName(), messageText); - makeToast(messageText, Toast.LENGTH_LONG); - } - - @Override - public void onIceDisconnected(String streamId) { - String messageText = "Ice disconnected for " + streamId; - Log.d(AbstractSampleSDKActivity.class.getName(), messageText); - makeToast(messageText, Toast.LENGTH_LONG); - } - - public void makeToast(String messageText, int lengthLong) { - runOnUiThread(() -> Toast.makeText(AbstractSampleSDKActivity.this, messageText, lengthLong).show()); - } - - @Override - public void onTrackList(String[] tracks) { - String messageText = "Track list received"; - Log.d(AbstractSampleSDKActivity.class.getName(), messageText); - //Toast.makeText(this, messageText, Toast.LENGTH_LONG).show(); - } - - @Override - public void onBitrateMeasurement(String streamId, int targetBitrate, int videoBitrate, int audioBitrate) { - String messageText = "Bitrate measurement received"; - Log.d(AbstractSampleSDKActivity.class.getName(), messageText); - //Toast.makeText(this, messageText, Toast.LENGTH_LONG).show(); - } - - @Override - public void onStreamInfoList(String streamId, ArrayList streamInfoList) { - String messageText = "Stream info list received"; - Log.d(AbstractSampleSDKActivity.class.getName(), messageText); - //Toast.makeText(this, messageText, Toast.LENGTH_LONG).show(); - } - - @Override - public void onNewVideoTrack(VideoTrack track) { - String messageText = "New video track received"; - Log.d(AbstractSampleSDKActivity.class.getName(), messageText); - makeToast(messageText, Toast.LENGTH_LONG); - } - - @Override - public void onVideoTrackEnded(VideoTrack track) { - String messageText = "Video track ended"; - Log.d(AbstractSampleSDKActivity.class.getName(), messageText); - makeToast(messageText, Toast.LENGTH_LONG); - } - - @Override - public void onReconnectionAttempt(String streamId) { - String messageText = "Reconnection attempt for " + streamId; - Log.d(AbstractSampleSDKActivity.class.getName(), messageText); - makeToast(messageText, Toast.LENGTH_LONG); - } - - @Override - public void onJoinedTheRoom(String streamId, String[] streams) { - String messageText = "Joined the room for " + streamId; - Log.d(AbstractSampleSDKActivity.class.getName(), messageText); - makeToast(messageText, Toast.LENGTH_LONG); - } - - - @Override - public void onRoomInformation(String[] streams) { - String messageText = "Room information received"; - Log.d(AbstractSampleSDKActivity.class.getName(), messageText); - //Toast.makeText(this, messageText, Toast.LENGTH_LONG).show(); - } - - @Override - public void onLeftTheRoom(String roomId) { - String messageText = "Left the room for " + roomId; - Log.d(AbstractSampleSDKActivity.class.getName(), messageText); - //Toast.makeText(this, messageText, Toast.LENGTH_LONG).show(); - } - - @Override - public void onMutedFor(String streamId) { - String messageText = "Microphone is muted for " + streamId; - Log.d(AbstractSampleSDKActivity.class.getName(), messageText); - makeToast(messageText, Toast.LENGTH_LONG); - } - @Override - public void onUnmutedFor(String streamId) { - String messageText = "Microphone is unmuted for " + streamId; - Log.d(AbstractSampleSDKActivity.class.getName(), messageText); - makeToast(messageText, Toast.LENGTH_LONG); - } - - @Override - public void onCameraTurnOnFor(String streamId) { - String messageText = "Camera is turned on for " + streamId; - Log.d(AbstractSampleSDKActivity.class.getName(), messageText); - makeToast(messageText, Toast.LENGTH_LONG); - } - - @Override - public void onCameraTurnOffFor(String streamId) { - String messageText = "Camera is turned off for " + streamId; - Log.d(AbstractSampleSDKActivity.class.getName(), messageText); - makeToast(messageText, Toast.LENGTH_LONG); - } - - @Override - public void onSatatusUpdateFor(String streamId, boolean micStatus, boolean cameraStatus) { - String messageText = "Status update for " + streamId + " mic: " + micStatus + " camera: " + cameraStatus; - Log.d(AbstractSampleSDKActivity.class.getName(), messageText); - makeToast(messageText, Toast.LENGTH_LONG); - } - - @Override - public boolean checkAndRequestPermisssions(boolean isForPublish, PermissionCallback permissionCallback) { - ArrayList permissions = new ArrayList<>(); - permissions.addAll(Arrays.asList(REQUIRED_MINIMUM_PERMISSIONS)); - if(isForPublish) { - permissions.addAll(Arrays.asList(REQUIRED_PUBLISH_PERMISSIONS)); - } - - if (hasPermissions(this, permissions)) { - return true; - } - else { - this.permissionCallback = permissionCallback; - showPermissionsErrorAndRequest(permissions); - return false; - } - } - - public boolean hasPermissions(Context context, List permissions) { - if (context != null && permissions != null) { - for (String permission : permissions) { - if (ActivityCompat.checkSelfPermission(context, permission) - != PackageManager.PERMISSION_GRANTED) { - Log.w(HomeActivity.class.getSimpleName(), "Permission required:"+permission); - return false; - } - } - } - return true; - } - public void showPermissionsErrorAndRequest(List permissions) { - makeToast("You need permissions before", Toast.LENGTH_SHORT); - String[] permissionArray = new String[permissions.size()]; - permissions.toArray(permissionArray); - ActivityCompat.requestPermissions(this, permissionArray, 1); - } - - public void onRequestPermissionsResult( - int requestCode, - String[] permissions, - int[] grantResults - ) { - permissionCallback.onPermissionResult(); - } - -} - diff --git a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/ActivityLink.java b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/ActivityLink.java deleted file mode 100644 index 8e42ffb7..00000000 --- a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/ActivityLink.java +++ /dev/null @@ -1,22 +0,0 @@ -package io.antmedia.webrtc_android_sample_app; - -import android.content.Intent; - -public class ActivityLink { - private final String label; - private final Intent intent; - - public ActivityLink(Intent intent, String label) { - this.intent = intent; - this.label = label; - } - - public String getLabel() { - return label; - } - - public Intent getIntent() { - return intent; - } - -} diff --git a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/ButtonAdapter.java b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/ButtonAdapter.java deleted file mode 100644 index 4b467fd8..00000000 --- a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/ButtonAdapter.java +++ /dev/null @@ -1,58 +0,0 @@ -package io.antmedia.webrtc_android_sample_app; - -import android.content.res.Resources; -import androidx.core.content.res.ResourcesCompat; - -import android.graphics.Color; -import android.util.TypedValue; -import android.view.Gravity; -import android.view.View; -import android.view.ViewGroup; -import android.widget.BaseAdapter; -import android.widget.GridView; -import android.widget.TextView; -import io.antmedia.webrtc_android_sample_app.R; -import java.util.List; - -import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; -import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; - -public class ButtonAdapter extends BaseAdapter { - - private List links; - - public ButtonAdapter(List links) { - this.links = links; - } - - public int getCount() { - return links.size(); - } - - public ActivityLink getItem(int position) { - return links.get(position); - } - - public long getItemId(int position) { - return position; - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - TextView button; - Resources resources = parent.getResources(); - if (convertView == null) { - button = new TextView(parent.getContext()); - button.setLayoutParams(new GridView.LayoutParams(MATCH_PARENT, WRAP_CONTENT)); - button.setGravity(Gravity.CENTER); - button.setPadding(8, 48, 8, 48); - button.setTextColor(ResourcesCompat.getColor(resources, R.color.textColor, null)); - button.setBackgroundColor(ResourcesCompat.getColor(resources, R.color.colorPrimary, null)); - convertView = button; - } else { - button = (TextView) convertView; - } - button.setText(links.get(position).getLabel()); - return convertView; - } -} \ No newline at end of file diff --git a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/ConferenceActivity.java b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/ConferenceActivity.java deleted file mode 100644 index c4b65b9d..00000000 --- a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/ConferenceActivity.java +++ /dev/null @@ -1,222 +0,0 @@ -package io.antmedia.webrtc_android_sample_app; - -import android.app.Activity; -import android.content.SharedPreferences; -import android.content.pm.PackageManager; -import android.os.Bundle; -import android.os.Handler; -import android.preference.PreferenceManager; -import android.util.Log; -import android.view.View; -import android.view.Window; -import android.view.WindowManager; -import android.widget.Button; -import android.widget.TextView; -import android.widget.Toast; - -import org.json.JSONObject; -import org.webrtc.DataChannel; -import org.webrtc.SurfaceViewRenderer; -import org.webrtc.VideoTrack; - -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; - -import de.tavendo.autobahn.WebSocket; -import io.antmedia.webrtcandroidframework.ConferenceManager; -import io.antmedia.webrtcandroidframework.IDataChannelObserver; -import io.antmedia.webrtcandroidframework.IWebRTCListener; -import io.antmedia.webrtcandroidframework.StreamInfo; -import io.antmedia.webrtcandroidframework.WebRTCClient; -import io.antmedia.webrtcandroidframework.apprtc.CallActivity; - -import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_CAPTURETOTEXTURE_ENABLED; - -import androidx.test.espresso.IdlingResource; -import androidx.test.espresso.idling.CountingIdlingResource; - -/* - * This activity is a sample activity shows how to implement Stream Based Conference Solution - * https://antmedia.io/reveal-the-secrets-of-3-types-of-video-conference-solutions/ - * - * @deprecated use {@link TrackBasedConferenceActivity} instead - */ -@Deprecated -public class ConferenceActivity extends AbstractSampleSDKActivity { - - private ConferenceManager conferenceManager; - private Button audioButton; - private Button videoButton; - private String serverUrl; - private TextView broadcastingView; - - private boolean stoppedStream = false; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - // Set window styles for fullscreen-window size. Needs to be done before - // adding content. - requestWindowFeature(Window.FEATURE_NO_TITLE); - getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON - | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED - | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON); - //getWindow().getDecorView().setSystemUiVisibility(getSystemUiVisibility()); - - setContentView(R.layout.activity_conference); - - broadcastingView = findViewById(R.id.broadcasting_text_view); - - - SurfaceViewRenderer publishViewRenderer = findViewById(R.id.publish_view_renderer); - ArrayList playViewRenderers = new ArrayList<>(); - - playViewRenderers.add(findViewById(R.id.play_view_renderer1)); - playViewRenderers.add(findViewById(R.id.play_view_renderer2)); - playViewRenderers.add(findViewById(R.id.play_view_renderer3)); - playViewRenderers.add(findViewById(R.id.play_view_renderer4)); - - audioButton = findViewById(R.id.control_audio_button); - videoButton = findViewById(R.id.control_video_button); - - this.getIntent().putExtra(EXTRA_CAPTURETOTEXTURE_ENABLED, true); - // this.getIntent().putExtra(CallActivity.EXTRA_VIDEO_CALL, false); - - SharedPreferences sharedPreferences = - PreferenceManager.getDefaultSharedPreferences(this /* Activity context */); - serverUrl = sharedPreferences.getString(getString(R.string.serverAddress), SettingsActivity.DEFAULT_WEBSOCKET_URL); - - String roomId = sharedPreferences.getString(getString(R.string.roomId), SettingsActivity.DEFAULT_ROOM_NAME); - String streamId = null; //"stream1"; - conferenceManager = new ConferenceManager( - this, - this, - getIntent(), - serverUrl, - roomId, - publishViewRenderer, - playViewRenderers, - streamId, - this - ); - - conferenceManager.setPlayOnlyMode(false); - conferenceManager.setOpenFrontCamera(true); - } - public void joinConference(View v) { - incrementIdle(); - if (!conferenceManager.isJoined()) { - Log.w(getClass().getSimpleName(), "Joining Conference"); - ((Button)v).setText("Leave"); - conferenceManager.joinTheConference(); - } - else { - ((Button)v).setText("Join"); - conferenceManager.leaveFromConference(); - stoppedStream = true; - } - } - - - @Override - public void onPlayStarted(String streamId) { - Log.w(getClass().getSimpleName(), "onPlayStarted"); - Toast.makeText(this, "Play started", Toast.LENGTH_SHORT).show(); - decrementIdle(); - } - - @Override - public void onPublishStarted(String streamId) { - Log.w(getClass().getSimpleName(), "onPublishStarted"); - Toast.makeText(this, "Publish started", Toast.LENGTH_SHORT).show(); - broadcastingView.setText("Publishing"); - broadcastingView.setVisibility(View.VISIBLE); - decrementIdle(); - - } - - @Override - public void onPublishFinished(String streamId) { - Log.w(getClass().getSimpleName(), "onPublishFinished"); - Toast.makeText(this, "Publish finished", Toast.LENGTH_SHORT).show(); - broadcastingView.setVisibility(View.GONE); - decrementIdle(); - } - - @Override - protected void onStop() { - super.onStop(); - audioButton.setText("Disable Audio"); - videoButton.setText("Disable Video"); - stoppedStream = true; - } - - @Override - public void onSignalChannelClosed(WebSocket.WebSocketConnectionObserver.WebSocketCloseNotification code, String streamId) { - Toast.makeText(this, "Signal channel closed with code " + code, Toast.LENGTH_LONG).show(); - } - - @Override - public void onDisconnected(String streamId) { - Log.w(getClass().getSimpleName(), "disconnected"); - Toast.makeText(this, "Disconnected", Toast.LENGTH_SHORT).show(); - audioButton.setText("Disable Audio"); - videoButton.setText("Disable Video"); - } - - @Override - public void onMessage(DataChannel.Buffer buffer, String dataChannelLabel) { - ByteBuffer data = buffer.data; - String strDataJson = new String(data.array(), StandardCharsets.UTF_8); - - try { - JSONObject json = new JSONObject(strDataJson); - String eventType = json.getString("eventType"); - String streamId = json.getString("streamId"); - Toast.makeText(this, eventType + " : " + streamId, Toast.LENGTH_LONG).show(); - } catch (Exception e) { - Log.e(getClass().getSimpleName(), e.getMessage()); - } - } - - @Override - public void onMessageSent(DataChannel.Buffer buffer, boolean successful) { - ByteBuffer data = buffer.data; - String strDataJson = new String(data.array(), StandardCharsets.UTF_8); - - Log.e(getClass().getSimpleName(), "SentEvent: " + strDataJson); - } - - public void controlAudio(View view) { - if (conferenceManager.isPublisherAudioOn()) { - conferenceManager.disableAudio(); - audioButton.setText("Enable Audio"); - } else { - conferenceManager.enableAudio(); - audioButton.setText("Disable Audio"); - } - } - - public void controlVideo(View view) { - if (conferenceManager.isPublisherVideoOn()) { - conferenceManager.disableVideo(); - videoButton.setText("Enable Video"); - - } else { - conferenceManager.enableVideo(); - videoButton.setText("Disable Video"); - } - } - - public void switchCamera(View view) { - conferenceManager.switchCamera(); - } - - @Override - public void onNewVideoTrack(VideoTrack track) { - } - -} - diff --git a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/CustomFrameActivity.java b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/CustomFrameActivity.java deleted file mode 100644 index f4ec4101..00000000 --- a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/CustomFrameActivity.java +++ /dev/null @@ -1,237 +0,0 @@ -package io.antmedia.webrtc_android_sample_app; - -import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_CAPTURETOTEXTURE_ENABLED; -import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_DATA_CHANNEL_ENABLED; -import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_VIDEO_BITRATE; -import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_VIDEO_FPS; - -import android.app.Activity; -import android.content.DialogInterface; -import android.content.SharedPreferences; -import android.content.pm.PackageManager; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.os.Build; -import android.os.Bundle; -import android.os.Handler; -import android.os.SystemClock; -import android.preference.PreferenceManager; -import android.util.Log; -import android.view.View; -import android.view.Window; -import android.view.WindowManager; -import android.widget.Button; -import android.widget.EditText; -import android.widget.Spinner; -import android.widget.TextView; -import android.widget.Toast; - -import androidx.annotation.RequiresApi; -import androidx.appcompat.app.AlertDialog; -import androidx.test.espresso.IdlingResource; -import androidx.test.espresso.idling.CountingIdlingResource; - -import org.apache.commons.lang3.RandomStringUtils; -import org.webrtc.DataChannel; -import org.webrtc.JavaI420Buffer; -import org.webrtc.SurfaceViewRenderer; -import org.webrtc.VideoFrame; -import org.webrtc.VideoTrack; - -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Timer; -import java.util.TimerTask; -import java.util.concurrent.TimeUnit; - -import de.tavendo.autobahn.WebSocket; -import io.antmedia.webrtcandroidframework.CustomVideoCapturer; -import io.antmedia.webrtcandroidframework.IDataChannelObserver; -import io.antmedia.webrtcandroidframework.IWebRTCClient; -import io.antmedia.webrtcandroidframework.IWebRTCListener; -import io.antmedia.webrtcandroidframework.StreamInfo; -import io.antmedia.webrtcandroidframework.WebRTCClient; -import io.antmedia.webrtcandroidframework.apprtc.CallActivity; -import io.github.crow_misia.libyuv.AbgrBuffer; -import io.github.crow_misia.libyuv.I420Buffer; - -public class CustomFrameActivity extends AbstractSampleSDKActivity { - - private boolean enableDataChannel = true; - - - private WebRTCClient webRTCClient; - - private Button startStreamingButton; - private String operationName = ""; - private String serverUrl; - private String restUrl; - - private SurfaceViewRenderer cameraViewRenderer; - private SurfaceViewRenderer pipViewRenderer; - private Spinner streamInfoListSpinner; - private TextView broadcastingView; - private EditText streamIdEditText; - private Bitmap bitmapImage; - private Timer frameFeedTimer; - - @RequiresApi(api = Build.VERSION_CODES.M) - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - // Set window styles for fullscreen-window size. Needs to be done before - // adding content. - requestWindowFeature(Window.FEATURE_NO_TITLE); - getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON - | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED - | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON); - //getWindow().getDecorView().setSystemUiVisibility(getSystemUiVisibility()); - - setContentView(R.layout.activity_main); - - cameraViewRenderer = findViewById(R.id.camera_view_renderer); - pipViewRenderer = findViewById(R.id.pip_view_renderer); - - broadcastingView = findViewById(R.id.broadcasting_text_view); - - streamIdEditText = findViewById(R.id.stream_id_edittext); - streamIdEditText.setText("streamId" + RandomStringUtils.randomNumeric(5)); - - startStreamingButton = findViewById(R.id.start_streaming_button); - - SharedPreferences sharedPreferences = - PreferenceManager.getDefaultSharedPreferences(this /* Activity context */); - String serverUrl = sharedPreferences.getString(getString(R.string.serverAddress), SettingsActivity.DEFAULT_WEBSOCKET_URL); - - startStreamingButton.setText("Start Publishing"); - operationName = "Publishing"; - - - this.getIntent().putExtra(EXTRA_VIDEO_FPS, 30); - this.getIntent().putExtra(EXTRA_VIDEO_BITRATE, 1500); - this.getIntent().putExtra(EXTRA_CAPTURETOTEXTURE_ENABLED, true); - this.getIntent().putExtra(EXTRA_DATA_CHANNEL_ENABLED, enableDataChannel); - - webRTCClient = new WebRTCClient( this,this); - - //webRTCClient.setOpenFrontCamera(false); - - String tokenId = "tokenId"; - webRTCClient.setVideoRenderers(null, null); - webRTCClient.setCustomCapturerEnabled(true); - - // this.getIntent().putExtra(CallActivity.EXTRA_VIDEO_FPS, 24); - webRTCClient.init(serverUrl, streamIdEditText.getText().toString(), IWebRTCClient.MODE_PUBLISH, tokenId, this.getIntent()); - webRTCClient.setDataChannelObserver(this); - - } - - public void startStreaming(View v) { - //update stream id if it is changed - webRTCClient.setStreamId("stream2");//streamIdEditText.getText().toString()); - incrementIdle(); - if (!webRTCClient.isStreaming()) { - ((Button) v).setText("Stop " + operationName); - Log.i(getClass().getSimpleName(), "Calling startStream"); - - webRTCClient.startStream(); - - bitmapImage = BitmapFactory.decodeResource(getResources(), R.drawable.test); - bitmapImage = Bitmap.createScaledBitmap(bitmapImage, 360, 640, false); - - frameFeedTimer = new Timer(); - TimerTask tt = new TimerTask() { - @Override - public void run() { - VideoFrame videoFrame = getNextFrame(); - ((CustomVideoCapturer)webRTCClient.getVideoCapturer()).writeFrame(videoFrame); - } - }; - - frameFeedTimer.schedule(tt, 0, 50); - - } - else { - ((Button)startStreamingButton).setText("Start " + operationName); - Log.i(getClass().getSimpleName(), "Calling stopStream"); - webRTCClient.stopStream(); - frameFeedTimer.cancel(); - } - - } - - @Override - public void onPublishStarted(String streamId) { - Log.w(getClass().getSimpleName(), "onPublishStarted"); - Toast.makeText(this, "Publish started", Toast.LENGTH_SHORT).show(); - broadcastingView.setVisibility(View.VISIBLE); - decrementIdle(); - } - - @Override - public void onPublishFinished(String streamId) { - Log.w(getClass().getSimpleName(), "onPublishFinished"); - Toast.makeText(this, "Publish finished", Toast.LENGTH_SHORT).show(); - broadcastingView.setVisibility(View.GONE); - decrementIdle(); - } - - @Override - protected void onStop() { - super.onStop(); - if (webRTCClient != null) { - Log.i(getClass().getSimpleName(), "onStop and calling stopStream"); - webRTCClient.stopStream(); - } - } - - @Override - public void onDisconnected(String streamId) { - - Log.w(getClass().getSimpleName(), "disconnected"); - Toast.makeText(this, "Disconnected", Toast.LENGTH_SHORT).show(); - broadcastingView.setVisibility(View.GONE); - decrementIdle(); - startStreamingButton.setText("Start " + operationName); - } - - @Override - public void onIceConnected(String streamId) { - //it is called when connected to ice - startStreamingButton.setText("Stop " + operationName); - } - - @Override - public void onBitrateMeasurement(String streamId, int targetBitrate, int videoBitrate, int audioBitrate) { - Log.e(getClass().getSimpleName(), "st:"+streamId+" tb:"+targetBitrate+" vb:"+videoBitrate+" ab:"+audioBitrate); - if(targetBitrate < (videoBitrate+audioBitrate)) { - Toast.makeText(this, "low bandwidth", Toast.LENGTH_SHORT).show(); - } - } - - public VideoFrame getNextFrame() { - final long captureTimeNs = TimeUnit.MILLISECONDS.toNanos(SystemClock.elapsedRealtime()); - - int frameWidth = bitmapImage.getWidth(); - int frameHeight = bitmapImage.getHeight(); - AbgrBuffer originalBuffer = AbgrBuffer.Factory.allocate(frameWidth, frameHeight); - I420Buffer i420Buffer = I420Buffer.Factory.allocate(frameWidth, frameHeight); - - bitmapImage.copyPixelsToBuffer(originalBuffer.asBuffer()); - originalBuffer.convertTo(i420Buffer); - - int ySize = frameWidth * frameHeight; - int uvSize = ySize / 4; - - final JavaI420Buffer buffer = JavaI420Buffer.wrap(frameWidth, frameHeight, - i420Buffer.getPlaneY().getBuffer(), i420Buffer.getPlaneY().getRowStride(), - i420Buffer.getPlaneU().getBuffer(), i420Buffer.getPlaneU().getRowStride(), - i420Buffer.getPlaneV().getBuffer(), i420Buffer.getPlaneV().getRowStride(), - null); - - originalBuffer.close(); - return new VideoFrame(buffer, 0 /* rotation */, captureTimeNs); - } -} diff --git a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/CustomHWFrameActivity.java b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/CustomHWFrameActivity.java deleted file mode 100644 index a0cf5c97..00000000 --- a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/CustomHWFrameActivity.java +++ /dev/null @@ -1,222 +0,0 @@ -package io.antmedia.webrtc_android_sample_app; - -import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_CAPTURETOTEXTURE_ENABLED; -import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_DATA_CHANNEL_ENABLED; -import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_VIDEO_BITRATE; -import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_VIDEO_FPS; - -import android.content.SharedPreferences; -import android.content.pm.PackageManager; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.Canvas; -import android.graphics.SurfaceTexture; -import android.os.Build; -import android.os.Bundle; -import android.preference.PreferenceManager; -import android.util.Log; -import android.view.Surface; -import android.view.View; -import android.view.Window; -import android.view.WindowManager; -import android.widget.Button; -import android.widget.EditText; -import android.widget.Spinner; -import android.widget.TextView; -import android.widget.Toast; - -import androidx.annotation.RequiresApi; -import androidx.test.espresso.idling.CountingIdlingResource; - -import org.apache.commons.lang3.RandomStringUtils; -import org.webrtc.SurfaceViewRenderer; - -import java.util.Timer; -import java.util.TimerTask; - -import io.antmedia.webrtcandroidframework.CustomVideoCapturer; -import io.antmedia.webrtcandroidframework.IWebRTCClient; -import io.antmedia.webrtcandroidframework.WebRTCClient; -import io.antmedia.webrtcandroidframework.apprtc.CallActivity; - -public class CustomHWFrameActivity extends AbstractSampleSDKActivity { - - private boolean enableDataChannel = true; - - private WebRTCClient webRTCClient; - - private Button startStreamingButton; - private String operationName = ""; - private String serverUrl; - private String restUrl; - - private SurfaceViewRenderer cameraViewRenderer; - private SurfaceViewRenderer pipViewRenderer; - private Spinner streamInfoListSpinner; - - public CountingIdlingResource idlingResource = new CountingIdlingResource("Load", true); - private TextView broadcastingView; - private EditText streamIdEditText; - private Timer frameFeedTimer; - private Surface surface; - private Bitmap bitmapImage; - - @RequiresApi(api = Build.VERSION_CODES.M) - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - // Set window styles for fullscreen-window size. Needs to be done before - // adding content. - requestWindowFeature(Window.FEATURE_NO_TITLE); - getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON - | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED - | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON); - //getWindow().getDecorView().setSystemUiVisibility(getSystemUiVisibility()); - - setContentView(R.layout.activity_main); - - cameraViewRenderer = findViewById(R.id.camera_view_renderer); - pipViewRenderer = findViewById(R.id.pip_view_renderer); - - broadcastingView = findViewById(R.id.broadcasting_text_view); - - streamIdEditText = findViewById(R.id.stream_id_edittext); - streamIdEditText.setText("streamId" + RandomStringUtils.randomNumeric(5)); - - startStreamingButton = findViewById(R.id.start_streaming_button); - - SharedPreferences sharedPreferences = - PreferenceManager.getDefaultSharedPreferences(this /* Activity context */); - String serverUrl = sharedPreferences.getString(getString(R.string.serverAddress), SettingsActivity.DEFAULT_WEBSOCKET_URL); - - startStreamingButton.setText("Start Publishing"); - operationName = "Publishing"; - - this.getIntent().putExtra(EXTRA_VIDEO_FPS, 30); - this.getIntent().putExtra(EXTRA_VIDEO_BITRATE, 1500); - this.getIntent().putExtra(EXTRA_CAPTURETOTEXTURE_ENABLED, true); - this.getIntent().putExtra(EXTRA_DATA_CHANNEL_ENABLED, enableDataChannel); - - webRTCClient = new WebRTCClient( this,this); - - //webRTCClient.setOpenFrontCamera(false); - - String tokenId = "tokenId"; - webRTCClient.setVideoRenderers(null, null); - webRTCClient.setCustomCapturerEnabled(true); - - // this.getIntent().putExtra(CallActivity.EXTRA_VIDEO_FPS, 24); - webRTCClient.init(serverUrl, streamIdEditText.getText().toString(), IWebRTCClient.MODE_PUBLISH, tokenId, this.getIntent()); - webRTCClient.setDataChannelObserver(this); - - } - - public void startStreaming(View v) { - //update stream id if it is changed - webRTCClient.setStreamId(streamIdEditText.getText().toString()); - incrementIdle(); - if (!webRTCClient.isStreaming()) { - ((Button) v).setText("Stop " + operationName); - Log.i(getClass().getSimpleName(), "Calling startStream"); - - webRTCClient.startStream(); - - frameFeedTimer = new Timer(); - - if(surface == null) { - SurfaceTexture surfaceTexture = ((CustomVideoCapturer) webRTCClient.getVideoCapturer()).getSurfaceTextureHelper().getSurfaceTexture(); - surface = new Surface(surfaceTexture); - } - - bitmapImage = BitmapFactory.decodeResource(getResources(), R.drawable.test); - bitmapImage = Bitmap.createScaledBitmap(bitmapImage, 720, 1280, false); - - - TimerTask tt = new TimerTask() { - @RequiresApi(api = Build.VERSION_CODES.M) - @Override - public void run() { - - Canvas canvas = surface.lockHardwareCanvas(); - Log.d("CustomHWFrameActivity", "canvas: " + canvas.getWidth() + " " + canvas.getHeight()); - if (canvas != null) { - canvas.drawBitmap(bitmapImage, 0, 0, null); - surface.unlockCanvasAndPost(canvas); - } - - } - }; - - frameFeedTimer.schedule(tt, 0, 50); - - } - else { - ((Button)startStreamingButton).setText("Start " + operationName); - Log.i(getClass().getSimpleName(), "Calling stopStream"); - webRTCClient.stopStream(); - frameFeedTimer.cancel(); - } - - } - - @Override - public void onPublishStarted(String streamId) { - Log.w(getClass().getSimpleName(), "onPublishStarted"); - Toast.makeText(this, "Publish started", Toast.LENGTH_SHORT).show(); - broadcastingView.setVisibility(View.VISIBLE); - } - - @Override - public void onPublishFinished(String streamId) { - Log.w(getClass().getSimpleName(), "onPublishFinished"); - Toast.makeText(this, "Publish finished", Toast.LENGTH_SHORT).show(); - broadcastingView.setVisibility(View.GONE); - } - - @Override - public void onPlayFinished(String streamId) { - Log.w(getClass().getSimpleName(), "onPlayFinished"); - Toast.makeText(this, "Play finished", Toast.LENGTH_SHORT).show(); - broadcastingView.setVisibility(View.GONE); - } - - @Override - public void noStreamExistsToPlay(String streamId) { - Log.w(getClass().getSimpleName(), "noStreamExistsToPlay for stream:" + streamId); - Toast.makeText(this, "No stream exist to play", Toast.LENGTH_LONG).show(); - finish(); - } - - @Override - protected void onStop() { - super.onStop(); - if (webRTCClient != null) { - Log.i(getClass().getSimpleName(), "onStop and calling stopStream"); - webRTCClient.stopStream(); - } - } - - @Override - public void onDisconnected(String streamId) { - - Log.w(getClass().getSimpleName(), "disconnected"); - Toast.makeText(this, "Disconnected", Toast.LENGTH_SHORT).show(); - broadcastingView.setVisibility(View.GONE); - startStreamingButton.setText("Start " + operationName); - } - - @Override - public void onIceConnected(String streamId) { - //it is called when connected to ice - startStreamingButton.setText("Stop " + operationName); - } - - @Override - public void onBitrateMeasurement(String streamId, int targetBitrate, int videoBitrate, int audioBitrate) { - Log.e(getClass().getSimpleName(), "st:"+streamId+" tb:"+targetBitrate+" vb:"+videoBitrate+" ab:"+audioBitrate); - if(targetBitrate < (videoBitrate+audioBitrate)) { - Toast.makeText(this, "low bandwidth", Toast.LENGTH_SHORT).show(); - } - } -} diff --git a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/DataChannelActivity.java b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/DataChannelActivity.java deleted file mode 100644 index 226a9494..00000000 --- a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/DataChannelActivity.java +++ /dev/null @@ -1,541 +0,0 @@ -package io.antmedia.webrtc_android_sample_app; - -import android.app.Activity; -import android.content.Intent; -import android.content.SharedPreferences; -import android.content.pm.PackageManager; -import android.database.Cursor; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.net.Uri; -import android.os.Build; -import android.os.Bundle; -import android.preference.PreferenceManager; -import android.provider.MediaStore; -import android.util.Log; -import android.view.KeyEvent; -import android.view.View; -import android.view.Window; -import android.view.WindowManager; -import android.view.inputmethod.EditorInfo; -import android.widget.Button; -import android.widget.EditText; -import android.widget.ListView; -import android.widget.TextView; -import android.widget.Toast; - -import androidx.annotation.RequiresApi; - -import org.webrtc.DataChannel; -import org.webrtc.RendererCommon; -import org.webrtc.SurfaceViewRenderer; -import org.webrtc.VideoTrack; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Date; -import java.util.UUID; - -import de.tavendo.autobahn.WebSocket; -import io.antmedia.webrtc_android_sample_app.chat.ImageMessage; -import io.antmedia.webrtc_android_sample_app.chat.Message; -import io.antmedia.webrtc_android_sample_app.chat.MessageAdapter; -import io.antmedia.webrtc_android_sample_app.chat.SettingsActivity; -import io.antmedia.webrtc_android_sample_app.chat.TextMessage; -import io.antmedia.webrtcandroidframework.IDataChannelObserver; -import io.antmedia.webrtcandroidframework.IWebRTCClient; -import io.antmedia.webrtcandroidframework.IWebRTCListener; -import io.antmedia.webrtcandroidframework.StreamInfo; -import io.antmedia.webrtcandroidframework.WebRTCClient; -import io.antmedia.webrtcandroidframework.apprtc.CallActivity; - -import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_CAPTURETOTEXTURE_ENABLED; -import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_DATA_CHANNEL_ENABLED; - -/******************** - * Take a look at the blog posts - * https://antmedia.io/webrtc-chat-and-file-transfer-2/ - * https://antmedia.io/webrtc-chat-and-file-transfer/ - ********************/ -public class DataChannelActivity extends AbstractSampleSDKActivity implements TextView.OnEditorActionListener { - - - private WebRTCClient webRTCClient; - private String webRTCMode; - private Button startStreamingButton; - private String operationName = ""; - private EditText messageInput; - private MessageAdapter messageAdapter; - private ListView messagesView; - private Button settingsButton; - private Button sendImageButton; - private SurfaceViewRenderer cameraViewRenderer; - private SurfaceViewRenderer pipViewRenderer; - private static int REQUEST_GET_IMAGE = 1; - private BinaryDataSender imageSender = new BinaryDataSender(); - private BinaryDataReceiver imageReceiver = new BinaryDataReceiver(); - private String uniqueID = UUID.randomUUID().toString(); - private int lastSentMessageNum = 0; - - private String tokenId; - private String serverURL; - private EditText streamIdEditText; - - @RequiresApi(api = Build.VERSION_CODES.M) - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - // Set window styles for fullscreen-window size. Needs to be done before - // adding content. - requestWindowFeature(Window.FEATURE_NO_TITLE); - getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON - | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED - | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON); - //getWindow().getDecorView().setSystemUiVisibility(getSystemUiVisibility()); - - setContentView(R.layout.activity_data); - - messageInput = findViewById(R.id.message_text_input); - messageInput.setOnEditorActionListener(this); - messageInput.setEnabled(false); - - sendImageButton = findViewById(R.id.send_image_button); - sendImageButton.setEnabled(false); - - messageAdapter = new MessageAdapter(this); - messagesView = findViewById(R.id.messages_view); - messagesView.setAdapter(messageAdapter); - - streamIdEditText = findViewById(R.id.stream_id_edittext); - streamIdEditText.setText("streamId" + (int)(Math.random()*9999)); - - - webRTCClient = new WebRTCClient( this,this); - - SharedPreferences sharedPreferences = - PreferenceManager.getDefaultSharedPreferences(this /* Activity context */); - serverURL = sharedPreferences.getString(getString(R.string.serverAddress), io.antmedia.webrtc_android_sample_app.SettingsActivity.DEFAULT_WEBSOCKET_URL); - tokenId = "tokenId"; - webRTCMode = sharedPreferences.getString(getString(R.string.stream_mode), IWebRTCClient.MODE_JOIN); - - cameraViewRenderer = findViewById(R.id.camera_view_renderer); - - pipViewRenderer = findViewById(R.id.pip_view_renderer); - - startStreamingButton = findViewById(R.id.start_streaming_button); - settingsButton = findViewById(R.id.settings); - cameraViewRenderer.setZOrderOnTop(true); - webRTCClient.setVideoRenderers(cameraViewRenderer, pipViewRenderer); - - this.getIntent().putExtra(EXTRA_CAPTURETOTEXTURE_ENABLED, true); - this.getIntent().putExtra(EXTRA_DATA_CHANNEL_ENABLED, true); - - - if (webRTCMode.equals(IWebRTCClient.MODE_PUBLISH)) { - startStreamingButton.setText("Start Publishing"); - operationName = "Publishing"; - } - else if (webRTCMode.equals(IWebRTCClient.MODE_PLAY)) { - startStreamingButton.setText("Start Playing"); - operationName = "Playing"; - } - else if (webRTCMode.equals(IWebRTCClient.MODE_JOIN)) { - startStreamingButton.setText("Start P2P"); - operationName = "P2P"; - } - webRTCClient.setDataChannelObserver(this); - - } - - private void initStream() { - cameraViewRenderer = findViewById(R.id.camera_view_renderer); - pipViewRenderer = findViewById(R.id.pip_view_renderer); - webRTCClient.setVideoRenderers(cameraViewRenderer, pipViewRenderer); - webRTCClient.init(serverURL, streamIdEditText.getText().toString(), webRTCMode, tokenId, this.getIntent()); - } - - @Override - protected void onStart() { - super.onStart(); - - initStream(); - } - - - public void startStreaming(View v) { - - if (!webRTCClient.isStreaming()) { - ((Button)v).setText("Stop " + operationName); - webRTCClient.startStream(); - } - else { - ((Button)v).setText("Start " + operationName); - webRTCClient.stopStream(); - } - } - - public void sendTextMessage() { - String messageToSend = messageInput.getText().toString(); - - String messageToSendJson = Message.createJsonTextMessage(computeMessageId(), new Date(), messageToSend); - - final ByteBuffer buffer = ByteBuffer.wrap(messageToSendJson.getBytes(StandardCharsets.UTF_8)); - DataChannel.Buffer buf= new DataChannel.Buffer(buffer,false); - webRTCClient.sendMessageViaDataChannel(buf); - } - - - private String computeMessageId() { - return uniqueID+lastSentMessageNum; - } - - private void increaseMessageId() { - lastSentMessageNum++; - } - - public void sendImage(View view) { - if(messageInput.isEnabled()) { - Intent i = new Intent( - Intent.ACTION_PICK, - android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI); - - startActivityForResult(i, REQUEST_GET_IMAGE); - } - } - - @Override - protected void onDestroy() { - super.onDestroy(); - webRTCClient.setDataChannelObserver(null); - webRTCClient = null; - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - - if (requestCode == REQUEST_GET_IMAGE && resultCode == RESULT_OK && null != data) { - Uri selectedImage = data.getData(); - String[] filePathColumn = {MediaStore.Images.Media.DATA}; - - Cursor cursor = getContentResolver().query(selectedImage, - filePathColumn, null, null, null); - cursor.moveToFirst(); - - int columnIndex = cursor.getColumnIndex(filePathColumn[0]); - String picturePath = cursor.getString(columnIndex); - cursor.close(); - - File imageFile = new File(picturePath); - int size = (int) imageFile.length(); - byte[] imageBytes = new byte[size]; - try { - FileInputStream inputStream = new FileInputStream(imageFile); - inputStream.read(imageBytes, 0, imageBytes.length); - inputStream.close(); - String imageHeaderInJson = Message.createJsonMessage(computeMessageId(), new Date()); - imageSender.startSending(imageBytes, imageHeaderInJson); - } catch (FileNotFoundException e) { - Log.e(getClass().getSimpleName(), e.getMessage()); - } catch (IOException e) { - Log.e(getClass().getSimpleName(), e.getMessage()); - } - } - } - - @Override - public void onPlayStarted(String streamId) { - Log.w(getClass().getSimpleName(), "onPlayStarted"); - Toast.makeText(this, "Play started", Toast.LENGTH_LONG).show(); - webRTCClient.switchVideoScaling(RendererCommon.ScalingType.SCALE_ASPECT_FIT); - messageInput.setEnabled(true); - sendImageButton.setEnabled(true); - } - - @Override - protected void onStop() { - super.onStop(); - //webRTCClient.stopStream(); - //messageInput.setEnabled(false); - //settingsButton.setEnabled(true); - imageReceiver.clear(); - imageSender.clear(); - } - - @Override - public void onDisconnected(String streamId) { - sendImageButton.setEnabled(false); - messageInput.setEnabled(false); - settingsButton.setEnabled(true); - Log.w(getClass().getSimpleName(), "disconnected"); - Toast.makeText(this, "Disconnected", Toast.LENGTH_LONG).show(); - startStreamingButton.setText("Start " + operationName); - //finish(); - webRTCClient.stopStream(); - imageReceiver.clear(); - imageSender.clear(); - initStream(); - } - - @Override - public void onIceConnected(String streamId) { - //it is called when connected to ice - messageInput.setEnabled(true); - sendImageButton.setEnabled(true); - settingsButton.setEnabled(false); - } - - - public void onOffVideo(View view) { - if (webRTCClient.isVideoOn()) { - webRTCClient.disableVideo(); - } - else { - webRTCClient.enableVideo(); - } - } - - public void onOffAudio(View view) { - if (webRTCClient.isAudioOn()) { - webRTCClient.disableAudio(); - } - else { - webRTCClient.enableAudio(); - } - } - - @Override - public void onMessage(final DataChannel.Buffer buffer, String dataChannelLabel) { - if (buffer.binary) { - Log.d(DataChannelActivity.class.getName(), "Received binary msg over " ); - - imageReceiver.receiveDataChunk(buffer.data); - - if(imageReceiver.isAllDataReceived()) { - Bitmap bmp=BitmapFactory.decodeByteArray(imageReceiver.receivedData.array(),0,imageReceiver.receivedData.capacity()); - final ImageMessage message = new ImageMessage(); - message.parseJson(imageReceiver.header.text); - message.setImageBitmap(bmp); - - messageAdapter.add(message); - // scroll the ListView to the last added element - messagesView.setSelection(messagesView.getCount() - 1); - - imageReceiver.clear(); - } - - } else { - ByteBuffer data = buffer.data; - String strDataJson = new String(data.array(), StandardCharsets.UTF_8); - - final Message message = new TextMessage(); - message.parseJson(strDataJson); - - messageAdapter.add(message); - // scroll the ListView to the last added element - messagesView.setSelection(messagesView.getCount() - 1); - } - } - - @Override - public void onMessageSent(DataChannel.Buffer buffer, boolean successful) { - if(successful) { - increaseMessageId(); - if (!buffer.binary) { - ByteBuffer data = buffer.data; - final byte[] bytes = new byte[data.capacity()]; - data.get(bytes); - String strDataJson = new String(bytes, StandardCharsets.UTF_8); - - final Message message = new TextMessage(); - message.parseJson(strDataJson); - message.setBelongsToCurrentUser(true); - - messageAdapter.add(message); - // scroll the ListView to the last added element - messagesView.setSelection(messagesView.getCount() - 1); - } else { - imageSender.updateLastChunkSent(); - if(!imageSender.isDataSendingComplete()) { - imageSender.sendLastChunk(); - } else { - Bitmap bmp=BitmapFactory.decodeByteArray(imageSender.dataBytes,0,imageSender.dataBytes.length); - - final ImageMessage message = new ImageMessage(); - message.setBelongsToCurrentUser(true); - message.parseJson(imageSender.header.text); - message.setImageBitmap(bmp); - - messageAdapter.add(message); - // scroll the ListView to the last added element - messagesView.setSelection(messagesView.getCount() - 1); - - imageSender.clear(); - } - } - } else { - if(!buffer.binary) { - Toast.makeText(this, "Could not send the text message", Toast.LENGTH_LONG).show(); - } else { - Toast.makeText(this, "Could not send the image", Toast.LENGTH_LONG).show(); - imageSender.clear(); - } - } - } - - @Override - public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { - boolean handled = false; - if (actionId == EditorInfo.IME_ACTION_SEND) { - sendTextMessage(); - handled = true; - messageInput.setText(""); - } - return handled; - } - - public void sendMessage(View view) { - if (messageInput.isEnabled()) { - sendTextMessage(); - messageInput.setText(""); - } - } - - public void goToSettings(View view) { - Intent settingsIntent = new Intent(getApplicationContext(), SettingsActivity.class); - startActivity(settingsIntent); - } - - private class BinaryMessageHeader { - int length; - String text; - } - - private class BinaryDataSender { - - boolean sending = false; - int endChunk = 0; - int lastOffset = 0; - byte[] dataBytes = null; - BinaryMessageHeader header; - - final static int CHUNK_SIZE_IN_BYTES = 10000; - - boolean isDataSendingComplete() { - return lastOffset >= dataBytes.length; - } - - void updateLastChunkSent() { - lastOffset = endChunk; - } - - void clear() { - sending= false; - lastOffset = 0; - endChunk = 0; - dataBytes = null; - header = null; - } - - void startSending(final byte[] imageBytes, String messageHeader ) { - sending = true; - lastOffset = 0; - this.dataBytes = imageBytes; - - sendFirstChunk(messageHeader); - } - - void sendFirstChunk(String messageHeader) { - header = new BinaryMessageHeader(); - header.text = messageHeader; - - byte[] messageHeaderBytes = messageHeader.getBytes(Charset.defaultCharset()); - header.length = messageHeaderBytes.length; - int totalMessageHeaderLength = header.length +8; - - int remainingChunkSize = CHUNK_SIZE_IN_BYTES-(totalMessageHeaderLength); - endChunk = (lastOffset+remainingChunkSize)> dataBytes.length? dataBytes.length: (lastOffset+remainingChunkSize); - - // put length how much data will be sent in total - ByteBuffer imageChunkData = ByteBuffer.allocate(totalMessageHeaderLength+endChunk); - imageChunkData.putInt(dataBytes.length); - imageChunkData.putInt(header.length); - imageChunkData.put(messageHeaderBytes); - - byte[] chunkBytes = Arrays.copyOfRange(dataBytes,lastOffset, endChunk); - imageChunkData.put(chunkBytes); - imageChunkData.rewind(); - - DataChannel.Buffer buf = new DataChannel.Buffer(imageChunkData, true); - - webRTCClient.sendMessageViaDataChannel(buf); - } - - void sendLastChunk() { - - endChunk = (lastOffset+CHUNK_SIZE_IN_BYTES)> dataBytes.length? dataBytes.length: (lastOffset+CHUNK_SIZE_IN_BYTES); - byte[] chunkBytes = Arrays.copyOfRange(dataBytes,lastOffset, endChunk); - - ByteBuffer imageChunkData = ByteBuffer.wrap(chunkBytes); - DataChannel.Buffer buf = new DataChannel.Buffer(imageChunkData, true); - - webRTCClient.sendMessageViaDataChannel(buf); - - } - } - - private class BinaryDataReceiver { - ByteBuffer receivedData = null; - int toBeReceivedBinaryDataLength = 0; - BinaryMessageHeader header = null; - - private void clear() { - receivedData = null; - toBeReceivedBinaryDataLength = 0; - header = null; - } - - private boolean isFirstPart() { - return receivedData == null; - } - - private boolean isAllDataReceived() { - return receivedData.capacity()>= toBeReceivedBinaryDataLength; - } - - private void receiveDataChunk(ByteBuffer dataChunk) { - if(!isFirstPart()) { - // append two buffers if not first chunk - receivedData = ByteBuffer.allocate(receivedData.capacity()+dataChunk.capacity()).put(receivedData); - receivedData.put(dataChunk); - receivedData.rewind(); - } else { - header = new BinaryMessageHeader(); - dataChunk.order(ByteOrder.LITTLE_ENDIAN); - toBeReceivedBinaryDataLength = dataChunk.getInt(); - header.length = dataChunk.getInt(); - int totalHeaderLength = header.length+8; - - byte[] headerMessageByte = new byte[header.length]; - dataChunk.get(headerMessageByte); - header.text = new String(headerMessageByte, StandardCharsets.UTF_8); - - Log.w(getClass().getSimpleName(), "Total Data to receive "+ toBeReceivedBinaryDataLength); - receivedData = ByteBuffer.allocate(dataChunk.capacity()-totalHeaderLength); - receivedData.put(dataChunk); - receivedData.rewind(); - } - } - } -} - - diff --git a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/DataChannelOnlyActivity.java b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/DataChannelOnlyActivity.java deleted file mode 100644 index 5304abb7..00000000 --- a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/DataChannelOnlyActivity.java +++ /dev/null @@ -1,237 +0,0 @@ -package io.antmedia.webrtc_android_sample_app; - -import android.app.Activity; -import android.content.SharedPreferences; -import android.content.pm.PackageManager; -import android.os.Build; -import android.os.Bundle; -import android.os.Handler; -import android.preference.PreferenceManager; -import android.util.Log; -import android.view.View; -import android.view.Window; -import android.view.WindowManager; -import android.widget.Button; -import android.widget.EditText; -import android.widget.TextView; -import android.widget.Toast; - -import org.webrtc.DataChannel; -import org.webrtc.SurfaceViewRenderer; -import org.webrtc.VideoTrack; - -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; - -import androidx.annotation.RequiresApi; -import androidx.test.espresso.IdlingResource; -import androidx.test.espresso.idling.CountingIdlingResource; - -import de.tavendo.autobahn.WebSocket; -import io.antmedia.webrtcandroidframework.IDataChannelObserver; -import io.antmedia.webrtcandroidframework.IWebRTCClient; -import io.antmedia.webrtcandroidframework.IWebRTCListener; -import io.antmedia.webrtcandroidframework.StreamInfo; -import io.antmedia.webrtcandroidframework.WebRTCClient; -import io.antmedia.webrtcandroidframework.apprtc.CallActivity; - -import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_DATA_CHANNEL_ENABLED; - -/** - * This Activity is for demonstrating the data channel usage without video and audio - * Steps: - * set dataChannelOnly parameter of WebRTCClient - * start WebRTC Cilent with play mode - * if no stream exist is called start it in publish mode - */ -public class DataChannelOnlyActivity extends AbstractSampleSDKActivity { - - private boolean enableDataChannel = true; - - private WebRTCClient webRTCClient; - - private Button startStreamingButton; - private String operationName = ""; - String tokenId = "tokenId"; - String serverUrl; - - private SurfaceViewRenderer cameraViewRenderer; - private SurfaceViewRenderer pipViewRenderer; - - // variables for handling reconnection attempts after disconnected - final int RECONNECTION_PERIOD_MLS = 100; - private boolean stoppedStream = false; - Handler reconnectionHandler = new Handler(); - private EditText messageInput; - private TextView messages; - private EditText streamIdEditText; - private View broadcastView; - - @RequiresApi(api = Build.VERSION_CODES.M) - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - // Set window styles for fullscreen-window size. Needs to be done before - // adding content. - requestWindowFeature(Window.FEATURE_NO_TITLE); - getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON - | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED - | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON); - //getWindow().getDecorView().setSystemUiVisibility(getSystemUiVisibility()); - - setContentView(R.layout.activity_data_channel_only); - - startStreamingButton = findViewById(R.id.start_streaming_button); - messageInput = findViewById(R.id.message_text_input); - messages = findViewById(R.id.messages_view); - - broadcastView = findViewById(R.id.broadcasting_text_view); - streamIdEditText = findViewById(R.id.stream_id_edittext); - streamIdEditText.setText("streamId" + (int)(Math.random()*9999)); - - SharedPreferences sharedPreferences = - PreferenceManager.getDefaultSharedPreferences(this /* Activity context */); - serverUrl = sharedPreferences.getString(getString(R.string.serverAddress), io.antmedia.webrtc_android_sample_app.SettingsActivity.DEFAULT_WEBSOCKET_URL); - - operationName = "DataChannel"; - - this.getIntent().putExtra(EXTRA_DATA_CHANNEL_ENABLED, enableDataChannel); - - webRTCClient = new WebRTCClient( this,this); - webRTCClient.setDataChannelOnly(true); - webRTCClient.setDataChannelObserver(this); - webRTCClient.setVideoRenderers(pipViewRenderer, cameraViewRenderer); - webRTCClient.init(serverUrl, streamIdEditText.getText().toString(), IWebRTCClient.MODE_PLAY, tokenId, this.getIntent()); - } - - public void startStreaming(View v) { - incrementIdle(); - //update stream id if it is changed - webRTCClient.setStreamId(streamIdEditText.getText().toString()); - if (!webRTCClient.isStreaming()) { - ((Button) v).setText("Stop"); - webRTCClient.startStream(); - } - else { - ((Button)v).setText("Start"); - webRTCClient.stopStream(); - stoppedStream = true; - } - } - - public void sendMessage(View v) { - String messageToSend = messageInput.getText().toString(); - messageInput.setText(""); - - final ByteBuffer buffer = ByteBuffer.wrap(messageToSend.getBytes(StandardCharsets.UTF_8)); - DataChannel.Buffer buf= new DataChannel.Buffer(buffer,false); - webRTCClient.sendMessageViaDataChannel(buf); - } - - @Override - public void onPlayStarted(String streamId) { - Log.w(getClass().getSimpleName(), "onPlayStarted"); - Toast.makeText(this, "Play started", Toast.LENGTH_LONG).show(); - broadcastView.setVisibility(View.VISIBLE); - } - - @Override - public void onPublishStarted(String streamId) { - Log.w(getClass().getSimpleName(), "onPublishStarted"); - Toast.makeText(this, "Publish started", Toast.LENGTH_LONG).show(); - broadcastView.setVisibility(View.VISIBLE); - decrementIdle(); - } - - @Override - public void onPublishFinished(String streamId) { - Log.w(getClass().getSimpleName(), "onPublishFinished"); - Toast.makeText(this, "Publish finished", Toast.LENGTH_LONG).show(); - broadcastView.setVisibility(View.GONE); - decrementIdle(); - } - - @Override - public void onPlayFinished(String streamId) { - Log.w(getClass().getSimpleName(), "onPlayFinished"); - Toast.makeText(this, "Play finished", Toast.LENGTH_LONG).show(); - broadcastView.setVisibility(View.GONE); - decrementIdle(); - } - - @Override - public void noStreamExistsToPlay(String streamId) { - //Log.w(getClass().getSimpleName(), "noStreamExistsToPlay"); - //Toast.makeText(this, "No stream exist to play", Toast.LENGTH_LONG).show(); - decrementIdle(); - webRTCClient.stopStream(); - - webRTCClient = new WebRTCClient( this,this); - webRTCClient.setDataChannelOnly(true); - webRTCClient.setDataChannelObserver(this); - webRTCClient.setVideoRenderers(pipViewRenderer, cameraViewRenderer); - webRTCClient.init(serverUrl, streamId, IWebRTCClient.MODE_PUBLISH, tokenId, this.getIntent()); - - startStreaming(startStreamingButton); - } - - @Override - protected void onStop() { - super.onStop(); - webRTCClient.stopStream(); - } - - @Override - public void onIceConnected(String streamId) { - //it is called when connected to ice - startStreamingButton.setText("Stop"); - } - - @Override - public void onIceDisconnected(String streamId) { - //it's called when ice is disconnected - } - - public void onOffVideo(View view) { - if (webRTCClient.isVideoOn()) { - webRTCClient.disableVideo(); - } - else { - webRTCClient.enableVideo(); - } - } - - public void onOffAudio(View view) { - if (webRTCClient.isAudioOn()) { - webRTCClient.disableAudio(); - } - else { - webRTCClient.enableAudio(); - } - } - - @Override - public void onMessage(DataChannel.Buffer buffer, String dataChannelLabel) { - ByteBuffer data = buffer.data; - String messageText = new String(data.array(), StandardCharsets.UTF_8); - messages.append("received:"+messageText+"\n"); - Toast.makeText(this, "New Message: " + messageText, Toast.LENGTH_LONG).show(); - } - - @Override - public void onMessageSent(DataChannel.Buffer buffer, boolean successful) { - if (successful) { - ByteBuffer data = buffer.data; - final byte[] bytes = new byte[data.capacity()]; - data.get(bytes); - String messageText = new String(bytes, StandardCharsets.UTF_8); - messages.append("sent:"+messageText+"\n"); - Toast.makeText(this, "Message is sent", Toast.LENGTH_SHORT).show(); - } else { - Toast.makeText(this, "Could not send the text message", Toast.LENGTH_LONG).show(); - } - } - -} diff --git a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/HomeActivity.java b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/HomeActivity.java deleted file mode 100644 index 9964803a..00000000 --- a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/HomeActivity.java +++ /dev/null @@ -1,71 +0,0 @@ -package io.antmedia.webrtc_android_sample_app; - -import android.content.Intent; -import android.os.Build; -import android.os.Bundle; -import android.view.View; -import android.widget.AdapterView; -import android.widget.GridView; - -import androidx.annotation.RequiresApi; -import androidx.appcompat.app.AppCompatActivity; - -import java.util.ArrayList; -import java.util.List; - -import io.antmedia.webrtcandroidframework.IWebRTCClient; - -public class HomeActivity extends AppCompatActivity implements AdapterView.OnItemClickListener { - - - private List activities; - private GridView list; - - @RequiresApi(api = Build.VERSION_CODES.M) - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_home); - - list = findViewById(R.id.list); - createList(); - setListAdapter(activities); - } - - private void createList() { - activities = new ArrayList<>(); - activities.add(new ActivityLink(new Intent(this, MainActivity.class), - "Default Camera")); - activities.add(new ActivityLink(new Intent(this, MainActivity.class).putExtra(MainActivity.WEBRTC_MODE, IWebRTCClient.MODE_PLAY), - "Simple Play")); - activities.add(new ActivityLink(new Intent(this, MainActivity.class).putExtra(MainActivity.WEBRTC_MODE, IWebRTCClient.MODE_JOIN), - "P2P")); - activities.add(new ActivityLink(new Intent(this, MultiTrackPlayActivity.class), - "Multi Track Play")); - activities.add(new ActivityLink(new Intent(this, DataChannelOnlyActivity.class), - "Data Channel Only Activity")); - activities.add(new ActivityLink(new Intent(this, ConferenceActivity.class), - "Conference")); - activities.add(new ActivityLink(new Intent(this, ScreenCaptureActivity.class), - "Screen Capture")); - activities.add(new ActivityLink(new Intent(this, USBCameraActivity.class), - "USB Camera")); - activities.add(new ActivityLink(new Intent(this, SettingsActivity.class), - "Settings")); - activities.add(new ActivityLink(new Intent(this, TrackBasedConferenceActivity.class), - "Multitrack Conference")); - activities.add(new ActivityLink(new Intent(this, PublishActivity.class), - "Publish")); - } - - private void setListAdapter(List activities) { - list.setAdapter(new ButtonAdapter(activities)); - list.setOnItemClickListener(this); - } - - @Override - public void onItemClick(AdapterView adapterView, View view, int i, long l) { - ActivityLink link = activities.get(i); - startActivity(link.getIntent()); - } -} diff --git a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/MP3PublishActivity.java b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/MP3PublishActivity.java deleted file mode 100644 index 0a7db398..00000000 --- a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/MP3PublishActivity.java +++ /dev/null @@ -1,182 +0,0 @@ -package io.antmedia.webrtc_android_sample_app; - -import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_CAPTURETOTEXTURE_ENABLED; -import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_DATA_CHANNEL_ENABLED; -import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_VIDEO_BITRATE; -import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_VIDEO_FPS; - -import android.app.Activity; -import android.content.DialogInterface; -import android.content.SharedPreferences; -import android.content.pm.PackageManager; -import android.os.Build; -import android.os.Bundle; -import android.os.Environment; -import android.os.Handler; -import android.preference.PreferenceManager; -import android.util.Log; -import android.view.View; -import android.view.Window; -import android.view.WindowManager; -import android.widget.Button; -import android.widget.EditText; -import android.widget.Spinner; -import android.widget.TextView; -import android.widget.Toast; - -import androidx.annotation.RequiresApi; -import androidx.appcompat.app.AlertDialog; -import androidx.test.espresso.IdlingResource; -import androidx.test.espresso.idling.CountingIdlingResource; - -import org.webrtc.DataChannel; -import org.webrtc.SurfaceViewRenderer; -import org.webrtc.VideoTrack; - -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; - -import de.tavendo.autobahn.WebSocket; -import io.antmedia.webrtcandroidframework.IDataChannelObserver; -import io.antmedia.webrtcandroidframework.IWebRTCClient; -import io.antmedia.webrtcandroidframework.IWebRTCListener; -import io.antmedia.webrtcandroidframework.MP3Publisher; -import io.antmedia.webrtcandroidframework.StreamInfo; -import io.antmedia.webrtcandroidframework.WebRTCClient; -import io.antmedia.webrtcandroidframework.apprtc.CallActivity; - -public class MP3PublishActivity extends AbstractSampleSDKActivity { - - private static final int DESIRED_SAMPLE_RATE = 48000; - /** - * Mode can Publish, Play or P2P - */ - private String webRTCMode = IWebRTCClient.MODE_PUBLISH; - - private boolean enableDataChannel = true; - - - private WebRTCClient webRTCClient; - - private Button startStreamingButton; - private String operationName = ""; - private String serverUrl; - - private SurfaceViewRenderer cameraViewRenderer; - private SurfaceViewRenderer pipViewRenderer; - private Spinner streamInfoListSpinner; - public static final String WEBRTC_MODE = "WebRTC_MODE"; - private TextView broadcastingView; - private EditText streamIdEditText; - private boolean audioPushingEnabled = false; - private String TAG = MP3PublishActivity.class.getSimpleName(); - public MP3Publisher mp3Publisher; - - @RequiresApi(api = Build.VERSION_CODES.M) - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setContentView(R.layout.activity_main); - - cameraViewRenderer = findViewById(R.id.camera_view_renderer); - pipViewRenderer = findViewById(R.id.pip_view_renderer); - - broadcastingView = findViewById(R.id.broadcasting_text_view); - - streamIdEditText = findViewById(R.id.stream_id_edittext); - streamIdEditText.setText("streamId" + (int)(Math.random()*9999)); - - startStreamingButton = findViewById(R.id.start_streaming_button); - - streamInfoListSpinner = findViewById(R.id.stream_info_list); - - SharedPreferences sharedPreferences = - PreferenceManager.getDefaultSharedPreferences(this /* Activity context */); - serverUrl = sharedPreferences.getString(getString(R.string.serverAddress), SettingsActivity.DEFAULT_WEBSOCKET_URL); - - String mode = this.getIntent().getStringExtra(WEBRTC_MODE); - if (mode != null) { - webRTCMode = mode; - } - - startStreamingButton.setText("Start Publishing"); - operationName = "Publishing"; - - this.getIntent().putExtra(EXTRA_VIDEO_FPS, 30); - this.getIntent().putExtra(EXTRA_VIDEO_BITRATE, 1500); - this.getIntent().putExtra(EXTRA_CAPTURETOTEXTURE_ENABLED, true); - this.getIntent().putExtra(EXTRA_DATA_CHANNEL_ENABLED, enableDataChannel); - - webRTCClient = new WebRTCClient( this,this); - - //webRTCClient.setOpenFrontCamera(false); - - String tokenId = "tokenId"; - webRTCClient.setVideoRenderers(pipViewRenderer, cameraViewRenderer); - - String path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath() + "/sample_44100_stereo.mp3"; - mp3Publisher = new MP3Publisher(webRTCClient, this, path); - - // this.getIntent().putExtra(CallActivity.EXTRA_VIDEO_FPS, 24); - webRTCClient.init(serverUrl, streamIdEditText.getText().toString(), webRTCMode, tokenId, this.getIntent()); - webRTCClient.setDataChannelObserver(this); - - - } - - public void startStreaming(View v) { - //update stream id if it is changed - webRTCClient.setStreamId(streamIdEditText.getText().toString()); - incrementIdle(); - if (!webRTCClient.isStreaming()) { - ((Button) v).setText("Stop " + operationName); - Log.i(getClass().getSimpleName(), "Calling startStream"); - - mp3Publisher.startStreaming(); - webRTCClient.startStream(); - - } - else { - ((Button)v).setText("Start " + operationName); - Log.i(getClass().getSimpleName(), "Calling stopStream"); - webRTCClient.stopStream(); - mp3Publisher.stopStreaming(); - } - - } - - - @Override - public void onPublishStarted(String streamId) { - Log.w(getClass().getSimpleName(), "onPublishStarted"); - Toast.makeText(this, "Publish started", Toast.LENGTH_SHORT).show(); - broadcastingView.setVisibility(View.VISIBLE); - decrementIdle(); - } - - @Override - public void onPublishFinished(String streamId) { - Log.w(getClass().getSimpleName(), "onPublishFinished"); - Toast.makeText(this, "Publish finished", Toast.LENGTH_SHORT).show(); - broadcastingView.setVisibility(View.GONE); - decrementIdle(); - } - - - @Override - protected void onStop() { - super.onStop(); - if (webRTCClient != null) { - Log.i(getClass().getSimpleName(), "onStop and calling stopStream"); - webRTCClient.stopStream(); - } - } - - @Override - public void onIceConnected(String streamId) { - //it is called when connected to ice - startStreamingButton.setText("Stop " + operationName); - } -} diff --git a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/MP4PublishActivity.java b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/MP4PublishActivity.java deleted file mode 100644 index 2fe25288..00000000 --- a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/MP4PublishActivity.java +++ /dev/null @@ -1,307 +0,0 @@ -package io.antmedia.webrtc_android_sample_app; - -import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_CAPTURETOTEXTURE_ENABLED; -import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_DATA_CHANNEL_ENABLED; -import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_VIDEO_BITRATE; -import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_VIDEO_FPS; - -import android.content.SharedPreferences; -import android.content.pm.PackageManager; -import android.content.res.Resources; -import android.graphics.Canvas; -import android.graphics.SurfaceTexture; -import android.media.Image; -import android.media.MediaCodec; -import android.media.MediaExtractor; -import android.media.MediaFormat; -import android.os.Build; -import android.os.Bundle; -import android.os.Handler; -import android.os.SystemClock; -import android.preference.PreferenceManager; -import android.util.Log; -import android.view.Surface; -import android.view.View; -import android.view.Window; -import android.view.WindowManager; -import android.widget.Button; -import android.widget.EditText; -import android.widget.Spinner; -import android.widget.TextView; -import android.widget.Toast; - -import androidx.annotation.RequiresApi; - -import org.apache.commons.lang3.RandomStringUtils; -import org.webrtc.JavaI420Buffer; -import org.webrtc.SurfaceTextureHelper; -import org.webrtc.SurfaceViewRenderer; -import org.webrtc.VideoFrame; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.util.concurrent.TimeUnit; - -import io.antmedia.webrtcandroidframework.CustomVideoCapturer; -import io.antmedia.webrtcandroidframework.IWebRTCClient; -import io.antmedia.webrtcandroidframework.WebRTCClient; -import io.antmedia.webrtcandroidframework.apprtc.CallActivity; - -public class MP4PublishActivity extends AbstractSampleSDKActivity { - - private boolean enableDataChannel = true; - - - private WebRTCClient webRTCClient; - - private Button startStreamingButton; - private String operationName = ""; - private String serverUrl; - private String restUrl; - - private SurfaceViewRenderer cameraViewRenderer; - private SurfaceViewRenderer pipViewRenderer; - private Spinner streamInfoListSpinner; - private TextView broadcastingView; - private EditText streamIdEditText; - - Handler handler = new Handler(); - - @RequiresApi(api = Build.VERSION_CODES.M) - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - // Set window styles for fullscreen-window size. Needs to be done before - // adding content. - requestWindowFeature(Window.FEATURE_NO_TITLE); - getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON - | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED - | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON); - //getWindow().getDecorView().setSystemUiVisibility(getSystemUiVisibility()); - - setContentView(R.layout.activity_main); - - cameraViewRenderer = findViewById(R.id.camera_view_renderer); - pipViewRenderer = findViewById(R.id.pip_view_renderer); - - broadcastingView = findViewById(R.id.broadcasting_text_view); - - streamIdEditText = findViewById(R.id.stream_id_edittext); - streamIdEditText.setText("streamId" + RandomStringUtils.randomNumeric(5)); - - startStreamingButton = findViewById(R.id.start_streaming_button); - - SharedPreferences sharedPreferences = - PreferenceManager.getDefaultSharedPreferences(this /* Activity context */); - String serverUrl = sharedPreferences.getString(getString(R.string.serverAddress), SettingsActivity.DEFAULT_WEBSOCKET_URL); - - - startStreamingButton.setText("Start Publishing"); - operationName = "Publishing"; - - - this.getIntent().putExtra(EXTRA_VIDEO_FPS, 30); - this.getIntent().putExtra(EXTRA_VIDEO_BITRATE, 1500); - this.getIntent().putExtra(EXTRA_CAPTURETOTEXTURE_ENABLED, true); - this.getIntent().putExtra(EXTRA_DATA_CHANNEL_ENABLED, enableDataChannel); - - webRTCClient = new WebRTCClient( this,this); - - //webRTCClient.setOpenFrontCamera(false); - - String tokenId = "tokenId"; - webRTCClient.setVideoRenderers(null, cameraViewRenderer); - webRTCClient.setCustomCapturerEnabled(true); - - // this.getIntent().putExtra(CallActivity.EXTRA_VIDEO_FPS, 24); - webRTCClient.init(serverUrl, streamIdEditText.getText().toString(), IWebRTCClient.MODE_PUBLISH, tokenId, this.getIntent()); - webRTCClient.setDataChannelObserver(this); - - - - } - - public void startStreaming(View v) { - //update stream id if it is changed - webRTCClient.setStreamId("stream2");//streamIdEditText.getText().toString()); - incrementIdle(); - if (!webRTCClient.isStreaming()) { - ((Button) v).setText("Stop " + operationName); - Log.i(getClass().getSimpleName(), "Calling startStream"); - - webRTCClient.startStream(); - - Thread t = new Thread() { - @RequiresApi(api = Build.VERSION_CODES.N) - @Override - public void run() { - extractFrames(); - } - }; - t.start(); - - } - else { - ((Button)startStreamingButton).setText("Start " + operationName); - Log.i(getClass().getSimpleName(), "Calling stopStream"); - webRTCClient.stopStream(); - } - - } - - @Override - public void onPublishStarted(String streamId) { - Log.w(getClass().getSimpleName(), "onPublishStarted"); - Toast.makeText(this, "Publish started", Toast.LENGTH_SHORT).show(); - broadcastingView.setVisibility(View.VISIBLE); - decrementIdle(); - } - - @Override - public void onPublishFinished(String streamId) { - Log.w(getClass().getSimpleName(), "onPublishFinished"); - Toast.makeText(this, "Publish finished", Toast.LENGTH_SHORT).show(); - broadcastingView.setVisibility(View.GONE); - decrementIdle(); - } - - @Override - protected void onStop() { - super.onStop(); - if (webRTCClient != null) { - Log.i(getClass().getSimpleName(), "onStop and calling stopStream"); - webRTCClient.stopStream(); - } - } - - @Override - public void onDisconnected(String streamId) { - - Log.w(getClass().getSimpleName(), "disconnected"); - Toast.makeText(this, "Disconnected", Toast.LENGTH_SHORT).show(); - broadcastingView.setVisibility(View.GONE); - decrementIdle(); - startStreamingButton.setText("Start " + operationName); - } - - @Override - public void onIceConnected(String streamId) { - //it is called when connected to ice - startStreamingButton.setText("Stop " + operationName); - } - - @Override - public void onBitrateMeasurement(String streamId, int targetBitrate, int videoBitrate, int audioBitrate) { - Log.e(getClass().getSimpleName(), "st:"+streamId+" tb:"+targetBitrate+" vb:"+videoBitrate+" ab:"+audioBitrate); - if(targetBitrate < (videoBitrate+audioBitrate)) { - Toast.makeText(this, "low bandwidth", Toast.LENGTH_SHORT).show(); - } - } - - private void sendFrame(Image yuvImage) { - int frameHeight = yuvImage.getHeight(); - int frameWidth = yuvImage.getWidth(); - - ByteBuffer yData = (ByteBuffer) yuvImage.getPlanes()[0].getBuffer().rewind(); - ByteBuffer uData = (ByteBuffer) yuvImage.getPlanes()[1].getBuffer().rewind(); - ByteBuffer vData = (ByteBuffer) yuvImage.getPlanes()[2].getBuffer().rewind(); - - final JavaI420Buffer buffer = JavaI420Buffer.wrap(frameWidth, frameHeight, - yData, yuvImage.getPlanes()[0].getRowStride(), - uData, yuvImage.getPlanes()[1].getRowStride(), - vData, yuvImage.getPlanes()[2].getRowStride(), - null); - - final long captureTimeNs = TimeUnit.MILLISECONDS.toNanos(SystemClock.elapsedRealtime()); - - VideoFrame videoFrame = new VideoFrame(buffer, 0 /* rotation */, captureTimeNs); - ((CustomVideoCapturer)webRTCClient.getVideoCapturer()).writeFrame(videoFrame); - - } - - @RequiresApi(api = Build.VERSION_CODES.N) - public void extractFrames() { - - MediaExtractor extractor = new MediaExtractor(); - try { - Resources res = this.getResources(); - extractor.setDataSource(res.openRawResourceFd(R.raw.test)); - } catch (IOException e) { - e.printStackTrace(); - return; - } - - - int trackCount = extractor.getTrackCount(); - int videoTrackIndex = -1; - MediaFormat videoFormat = null; - - for (int i = 0; i < trackCount; i++) { - MediaFormat format = extractor.getTrackFormat(i); - String mime = format.getString(MediaFormat.KEY_MIME); - if (mime != null && mime.startsWith("video/")) { - videoTrackIndex = i; - videoFormat = format; - break; - } - } - - if (videoTrackIndex == -1 || videoFormat == null) { - Log.e("MP4Publish", "No video track found."); - return; - } - - extractor.selectTrack(videoTrackIndex); - - try { - MediaCodec decoder = MediaCodec.createDecoderByType(videoFormat.getString(MediaFormat.KEY_MIME)); - - decoder.configure(videoFormat, null, null, 0); - //Surface surface = decoder.createInputSurface(); - decoder.start(); - - MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo(); - boolean isEOS = false; - - while (!Thread.interrupted() && !isEOS) { - int inputIndex = decoder.dequeueInputBuffer(10000); - if (inputIndex >= 0) { - ByteBuffer inputBuffer = decoder.getInputBuffer(inputIndex); - int sampleSize = extractor.readSampleData(inputBuffer, 0); - if (sampleSize < 0) { - isEOS = true; - decoder.queueInputBuffer(inputIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM); - } else { - decoder.queueInputBuffer(inputIndex, 0, sampleSize, extractor.getSampleTime(), 0); - extractor.advance(); - } - } - - int outputIndex = decoder.dequeueOutputBuffer(bufferInfo, 10000); - if (outputIndex >= 0) { - Image yuvImage = decoder.getOutputImage(outputIndex); - - sendFrame(yuvImage); - - yuvImage.close(); - decoder.releaseOutputBuffer(outputIndex, true); - } else if (outputIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { - // Handle format change if needed - } - - Thread.sleep(50); - } - - decoder.stop(); - decoder.release(); - extractor.release(); - } catch (IOException e) { - e.printStackTrace(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - } - - -} diff --git a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/MP4PublishWithSurfaceActivity.java b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/MP4PublishWithSurfaceActivity.java deleted file mode 100644 index 0ab0e224..00000000 --- a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/MP4PublishWithSurfaceActivity.java +++ /dev/null @@ -1,322 +0,0 @@ -package io.antmedia.webrtc_android_sample_app; - -import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_CAPTURETOTEXTURE_ENABLED; -import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_DATA_CHANNEL_ENABLED; -import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_VIDEO_BITRATE; -import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_VIDEO_FPS; -import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_VIDEO_HEIGHT; -import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_VIDEO_WIDTH; - -import android.content.SharedPreferences; -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.Canvas; -import android.graphics.SurfaceTexture; -import android.media.Image; -import android.media.MediaCodec; -import android.media.MediaExtractor; -import android.media.MediaFormat; -import android.os.Build; -import android.os.Bundle; -import android.os.Handler; -import android.preference.PreferenceManager; -import android.util.Log; -import android.view.Surface; -import android.view.View; -import android.view.Window; -import android.view.WindowManager; -import android.widget.Button; -import android.widget.EditText; -import android.widget.Spinner; -import android.widget.TextView; -import android.widget.Toast; - -import androidx.annotation.RequiresApi; - -import org.apache.commons.lang3.RandomStringUtils; -import org.webrtc.SurfaceViewRenderer; - -import java.io.IOException; -import java.nio.ByteBuffer; - -import io.antmedia.webrtcandroidframework.CustomVideoCapturer; -import io.antmedia.webrtcandroidframework.IWebRTCClient; -import io.antmedia.webrtcandroidframework.WebRTCClient; -import io.github.crow_misia.libyuv.AbgrBuffer; -import io.github.crow_misia.libyuv.I420Buffer; -import io.github.crow_misia.libyuv.Plane; -import io.github.crow_misia.libyuv.PlanePrimitive; - -public class MP4PublishWithSurfaceActivity extends AbstractSampleSDKActivity { - - private boolean enableDataChannel = true; - - - private WebRTCClient webRTCClient; - - private Button startStreamingButton; - private String operationName = ""; - private String serverUrl; - private String restUrl; - - private SurfaceViewRenderer cameraViewRenderer; - private SurfaceViewRenderer pipViewRenderer; - private Spinner streamInfoListSpinner; - private TextView broadcastingView; - private EditText streamIdEditText; - - Handler handler = new Handler(); - private Surface surface; - - @RequiresApi(api = Build.VERSION_CODES.M) - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - // Set window styles for fullscreen-window size. Needs to be done before - // adding content. - requestWindowFeature(Window.FEATURE_NO_TITLE); - getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON - | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED - | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON); - //getWindow().getDecorView().setSystemUiVisibility(getSystemUiVisibility()); - - setContentView(R.layout.activity_main); - - cameraViewRenderer = findViewById(R.id.camera_view_renderer); - pipViewRenderer = findViewById(R.id.pip_view_renderer); - - broadcastingView = findViewById(R.id.broadcasting_text_view); - - streamIdEditText = findViewById(R.id.stream_id_edittext); - streamIdEditText.setText("streamId" + RandomStringUtils.randomNumeric(5)); - - startStreamingButton = findViewById(R.id.start_streaming_button); - - SharedPreferences sharedPreferences = - PreferenceManager.getDefaultSharedPreferences(this /* Activity context */); - String serverUrl = sharedPreferences.getString(getString(R.string.serverAddress), SettingsActivity.DEFAULT_WEBSOCKET_URL); - - - startStreamingButton.setText("Start Publishing"); - operationName = "Publishing"; - - - this.getIntent().putExtra(EXTRA_VIDEO_FPS, 20); - this.getIntent().putExtra(EXTRA_VIDEO_BITRATE, 1500); - this.getIntent().putExtra(EXTRA_CAPTURETOTEXTURE_ENABLED, true); - this.getIntent().putExtra(EXTRA_DATA_CHANNEL_ENABLED, enableDataChannel); - this.getIntent().putExtra(EXTRA_VIDEO_WIDTH, 360); - this.getIntent().putExtra(EXTRA_VIDEO_HEIGHT, 640); - - webRTCClient = new WebRTCClient( this,this); - - //webRTCClient.setOpenFrontCamera(false); - - String tokenId = "tokenId"; - webRTCClient.setVideoRenderers(null, cameraViewRenderer); - webRTCClient.setCustomCapturerEnabled(true); - - // this.getIntent().putExtra(CallActivity.EXTRA_VIDEO_FPS, 24); - webRTCClient.init(serverUrl, streamIdEditText.getText().toString(), IWebRTCClient.MODE_PUBLISH, tokenId, this.getIntent()); - webRTCClient.setDataChannelObserver(this); - - - - } - - public void startStreaming(View v) { - //update stream id if it is changed - webRTCClient.setStreamId("stream2");//streamIdEditText.getText().toString()); - incrementIdle(); - if (!webRTCClient.isStreaming()) { - ((Button) v).setText("Stop " + operationName); - Log.i(getClass().getSimpleName(), "Calling startStream"); - - webRTCClient.startStream(); - - if(surface == null) { - SurfaceTexture surfaceTexture = ((CustomVideoCapturer) webRTCClient.getVideoCapturer()).getSurfaceTextureHelper().getSurfaceTexture(); - surface = new Surface(surfaceTexture); - } - - Thread t = new Thread() { - @RequiresApi(api = Build.VERSION_CODES.N) - @Override - public void run() { - extractFrames(); - } - }; - t.start(); - - } - else { - ((Button)startStreamingButton).setText("Start " + operationName); - Log.i(getClass().getSimpleName(), "Calling stopStream"); - webRTCClient.stopStream(); - } - - } - - @Override - public void onPublishStarted(String streamId) { - Log.w(getClass().getSimpleName(), "onPublishStarted"); - Toast.makeText(this, "Publish started", Toast.LENGTH_SHORT).show(); - broadcastingView.setVisibility(View.VISIBLE); - decrementIdle(); - } - - @Override - public void onPublishFinished(String streamId) { - Log.w(getClass().getSimpleName(), "onPublishFinished"); - Toast.makeText(this, "Publish finished", Toast.LENGTH_SHORT).show(); - broadcastingView.setVisibility(View.GONE); - decrementIdle(); - } - - @Override - protected void onStop() { - super.onStop(); - if (webRTCClient != null) { - Log.i(getClass().getSimpleName(), "onStop and calling stopStream"); - webRTCClient.stopStream(); - } - } - - @Override - public void onDisconnected(String streamId) { - - Log.w(getClass().getSimpleName(), "disconnected"); - Toast.makeText(this, "Disconnected", Toast.LENGTH_SHORT).show(); - broadcastingView.setVisibility(View.GONE); - decrementIdle(); - startStreamingButton.setText("Start " + operationName); - } - - @Override - public void onIceConnected(String streamId) { - //it is called when connected to ice - startStreamingButton.setText("Stop " + operationName); - } - - @Override - public void onBitrateMeasurement(String streamId, int targetBitrate, int videoBitrate, int audioBitrate) { - Log.e(getClass().getSimpleName(), "st:"+streamId+" tb:"+targetBitrate+" vb:"+videoBitrate+" ab:"+audioBitrate); - if(targetBitrate < (videoBitrate+audioBitrate)) { - Toast.makeText(this, "low bandwidth", Toast.LENGTH_SHORT).show(); - } - } - - private void sendFrame(Image image) { - byte[] data = new byte[image.getHeight() * image.getWidth() * 3 / 2]; - ByteBuffer bufferY = image.getPlanes()[0].getBuffer(); - ByteBuffer bufferU = image.getPlanes()[1].getBuffer(); - ByteBuffer bufferV = image.getPlanes()[2].getBuffer(); - - int strideY = image.getPlanes()[0].getRowStride(); - int strideU = image.getPlanes()[1].getRowStride(); - int strideV = image.getPlanes()[2].getRowStride(); - - //drawYUVonSurface(data); - - - PlanePrimitive planeY = new PlanePrimitive(strideY, bufferY); - PlanePrimitive planeU = new PlanePrimitive(strideU, bufferU); - PlanePrimitive planeV = new PlanePrimitive(strideV, bufferV); - - I420Buffer yuvBuffer = I420Buffer.Factory.wrap(planeY, planeU, planeV, 640, 360); - AbgrBuffer rgbBuffer = AbgrBuffer.Factory.allocate(640, 360); - - yuvBuffer.convertTo(rgbBuffer); - - Canvas canvas = surface.lockCanvas(null); - canvas.drawBitmap(rgbBuffer.asBitmap(), 0, 0, null); - surface.unlockCanvasAndPost(canvas); - } - - @RequiresApi(api = Build.VERSION_CODES.N) - public void extractFrames() { - - MediaExtractor extractor = new MediaExtractor(); - try { - Resources res = this.getResources(); - extractor.setDataSource(res.openRawResourceFd(R.raw.test)); - } catch (IOException e) { - e.printStackTrace(); - return; - } - - - int trackCount = extractor.getTrackCount(); - int videoTrackIndex = -1; - MediaFormat videoFormat = null; - - for (int i = 0; i < trackCount; i++) { - MediaFormat format = extractor.getTrackFormat(i); - String mime = format.getString(MediaFormat.KEY_MIME); - if (mime != null && mime.startsWith("video/")) { - videoTrackIndex = i; - videoFormat = format; - break; - } - } - - if (videoTrackIndex == -1 || videoFormat == null) { - Log.e("MP4Publish", "No video track found."); - return; - } - - extractor.selectTrack(videoTrackIndex); - - try { - MediaCodec decoder = MediaCodec.createDecoderByType(videoFormat.getString(MediaFormat.KEY_MIME)); - - decoder.configure(videoFormat, null, null, 0); - //Surface surface = decoder.createInputSurface(); - decoder.start(); - - MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo(); - boolean isEOS = false; - - while (!Thread.interrupted() && !isEOS) { - int inputIndex = decoder.dequeueInputBuffer(10000); - if (inputIndex >= 0) { - ByteBuffer inputBuffer = decoder.getInputBuffer(inputIndex); - int sampleSize = extractor.readSampleData(inputBuffer, 0); - if (sampleSize < 0) { - isEOS = true; - decoder.queueInputBuffer(inputIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM); - } else { - decoder.queueInputBuffer(inputIndex, 0, sampleSize, extractor.getSampleTime(), 0); - extractor.advance(); - } - } - - int outputIndex = decoder.dequeueOutputBuffer(bufferInfo, 10000); - if (outputIndex >= 0) { - Image yuvImage = decoder.getOutputImage(outputIndex); - - sendFrame(yuvImage); - - yuvImage.close(); - decoder.releaseOutputBuffer(outputIndex, true); - } else if (outputIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { - // Handle format change if needed - } - - Thread.sleep(50); - } - - decoder.stop(); - decoder.release(); - extractor.release(); - } catch (IOException e) { - e.printStackTrace(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - } - - -} diff --git a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/MainActivity.java b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/MainActivity.java index 9afc6552..89a13e9a 100644 --- a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/MainActivity.java +++ b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/MainActivity.java @@ -1,422 +1,142 @@ package io.antmedia.webrtc_android_sample_app; -import android.app.Activity; -import android.content.DialogInterface; -import android.content.SharedPreferences; -import android.content.pm.PackageManager; +import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; +import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; + +import android.content.Intent; +import android.content.res.Resources; import android.os.Build; import android.os.Bundle; -import android.os.Handler; -import android.preference.PreferenceManager; -import android.util.Log; +import android.view.Gravity; import android.view.View; -import android.view.Window; -import android.view.WindowManager; +import android.view.ViewGroup; import android.widget.AdapterView; -import android.widget.ArrayAdapter; -import android.widget.Button; -import android.widget.EditText; -import android.widget.Spinner; +import android.widget.BaseAdapter; +import android.widget.GridView; import android.widget.TextView; -import android.widget.Toast; import androidx.annotation.RequiresApi; -import androidx.appcompat.app.AlertDialog; -import androidx.test.espresso.IdlingResource; -import androidx.test.espresso.idling.CountingIdlingResource; - -import org.webrtc.DataChannel; -import org.webrtc.RendererCommon; -import org.webrtc.SurfaceViewRenderer; -import org.webrtc.VideoTrack; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.content.res.ResourcesCompat; -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; - -import de.tavendo.autobahn.WebSocket; -import io.antmedia.webrtcandroidframework.IDataChannelObserver; -import io.antmedia.webrtcandroidframework.IWebRTCClient; -import io.antmedia.webrtcandroidframework.IWebRTCListener; -import io.antmedia.webrtcandroidframework.StreamInfo; -import io.antmedia.webrtcandroidframework.WebRTCClient; -import io.antmedia.webrtcandroidframework.apprtc.CallActivity; - -import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_CAPTURETOTEXTURE_ENABLED; -import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_DATA_CHANNEL_ENABLED; -import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_VIDEO_BITRATE; -import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_VIDEO_FPS; - -public class MainActivity extends AbstractSampleSDKActivity { - - /** - * Mode can Publish, Play or P2P - */ - private String webRTCMode = IWebRTCClient.MODE_PUBLISH; - - private boolean enableDataChannel = true; - - - private WebRTCClient webRTCClient; - - private Button startStreamingButton; - private String operationName = ""; - private String serverUrl; - - private SurfaceViewRenderer cameraViewRenderer; - private SurfaceViewRenderer pipViewRenderer; - private Spinner streamInfoListSpinner; - public static final String WEBRTC_MODE = "WebRTC_MODE"; - - private boolean stoppedStream = false; - private TextView broadcastingView; - private EditText streamIdEditText; +import java.util.List; + +import io.antmedia.webrtc_android_sample_app.advanced.MP3PublishActivity; +import io.antmedia.webrtc_android_sample_app.advanced.MP4PublishActivity; +import io.antmedia.webrtc_android_sample_app.advanced.MP4PublishWithSurfaceActivity; +import io.antmedia.webrtc_android_sample_app.advanced.MultiTrackPlayActivity; +import io.antmedia.webrtc_android_sample_app.advanced.USBCameraActivity; +import io.antmedia.webrtc_android_sample_app.basic.ConferenceActivity; +import io.antmedia.webrtc_android_sample_app.basic.DataChannelOnlyActivity; +import io.antmedia.webrtc_android_sample_app.basic.PeerActivity; +import io.antmedia.webrtc_android_sample_app.basic.PlayActivity; +import io.antmedia.webrtc_android_sample_app.basic.PublishActivity; +import io.antmedia.webrtc_android_sample_app.basic.ScreenCaptureActivity; +import io.antmedia.webrtc_android_sample_app.basic.SettingsActivity; + +public class MainActivity extends AppCompatActivity implements AdapterView.OnItemClickListener { + final private List activities = new ArrayList<>(); + private GridView list; @RequiresApi(api = Build.VERSION_CODES.M) @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - - // Set window styles for fullscreen-window size. Needs to be done before - // adding content. - requestWindowFeature(Window.FEATURE_NO_TITLE); - getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON - | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED - | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON); - //getWindow().getDecorView().setSystemUiVisibility(getSystemUiVisibility()); - setContentView(R.layout.activity_main); - cameraViewRenderer = findViewById(R.id.camera_view_renderer); - pipViewRenderer = findViewById(R.id.pip_view_renderer); - - broadcastingView = findViewById(R.id.broadcasting_text_view); - - streamIdEditText = findViewById(R.id.stream_id_edittext); - streamIdEditText.setText("streamId" + (int)(Math.random()*9999)); - - startStreamingButton = findViewById(R.id.start_streaming_button); - - streamInfoListSpinner = findViewById(R.id.stream_info_list); - - SharedPreferences sharedPreferences = - PreferenceManager.getDefaultSharedPreferences(this /* Activity context */); - serverUrl = sharedPreferences.getString(getString(R.string.serverAddress), SettingsActivity.DEFAULT_WEBSOCKET_URL); - if(!webRTCMode.equals(IWebRTCClient.MODE_PLAY)) { - streamInfoListSpinner.setVisibility(View.INVISIBLE); - } - else { - - streamInfoListSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { - boolean firstCall = true; - @Override - public void onItemSelected(AdapterView adapterView, View view, int i, long l) { - //for some reason in android onItemSelected is called automatically at first. - //there are some discussions about it in stackoverflow - //so we just have simple check - if (firstCall) { - firstCall = false; - return; - } - webRTCClient.forceStreamQuality(Integer.parseInt((String) adapterView.getSelectedItem())); - Log.i("MainActivity", "Spinner onItemSelected"); - } - - @Override - public void onNothingSelected(AdapterView adapterView) { - - } - }); - } - - String mode = this.getIntent().getStringExtra(WEBRTC_MODE); - if (mode != null) { - webRTCMode = mode; - } - - if (webRTCMode.equals(IWebRTCClient.MODE_PUBLISH)) { - startStreamingButton.setText("Start Publishing"); - operationName = "Publishing"; - } - else if (webRTCMode.equals(IWebRTCClient.MODE_PLAY)) { - startStreamingButton.setText("Start Playing"); - operationName = "Playing"; - } - else if (webRTCMode.equals(IWebRTCClient.MODE_JOIN)) { - startStreamingButton.setText("Start P2P"); - operationName = "P2P"; - } - - this.getIntent().putExtra(EXTRA_VIDEO_FPS, 30); - this.getIntent().putExtra(EXTRA_VIDEO_BITRATE, 1500); - this.getIntent().putExtra(EXTRA_CAPTURETOTEXTURE_ENABLED, true); - this.getIntent().putExtra(EXTRA_DATA_CHANNEL_ENABLED, enableDataChannel); - - webRTCClient = new WebRTCClient( this,this); - - //webRTCClient.setOpenFrontCamera(false); - - String tokenId = "tokenId"; - webRTCClient.setVideoRenderers(pipViewRenderer, cameraViewRenderer); - - // this.getIntent().putExtra(CallActivity.EXTRA_VIDEO_FPS, 24); - webRTCClient.init(serverUrl, streamIdEditText.getText().toString(), webRTCMode, tokenId, this.getIntent()); - webRTCClient.setDataChannelObserver(this); - - } - - public void startStreaming(View v) { - //update stream id if it is changed - webRTCClient.setStreamId(streamIdEditText.getText().toString()); - incrementIdle(); - if (!webRTCClient.isStreaming()) { - ((Button) v).setText("Stop " + operationName); - Log.i(getClass().getSimpleName(), "Calling startStream"); - - webRTCClient.startStream(); - if (webRTCMode == IWebRTCClient.MODE_JOIN) { - pipViewRenderer.setZOrderOnTop(true); - } - stoppedStream = false; - } - else { - ((Button)v).setText("Start " + operationName); - Log.i(getClass().getSimpleName(), "Calling stopStream"); - webRTCClient.stopStream(); - stoppedStream = true; - } + list = findViewById(R.id.list); + createList(); + setListAdapter(activities); } - @Override - public void onPlayStarted(String streamId) { - Log.w(getClass().getSimpleName(), "onPlayStarted"); - Toast.makeText(this, "Play started", Toast.LENGTH_SHORT).show(); - webRTCClient.switchVideoScaling(RendererCommon.ScalingType.SCALE_ASPECT_FIT); - webRTCClient.getStreamInfoList(); + private void createList() { + addActivity(PublishActivity.class, "Publish"); + addActivity(PlayActivity.class, "Play"); + addActivity(PeerActivity.class, "Peer"); + addActivity(ConferenceActivity.class, "Conference"); + addActivity(ScreenCaptureActivity.class, "Screen Share"); + addActivity(DataChannelOnlyActivity.class, "DC Only"); + addActivity(MP3PublishActivity.class, "mp3"); + addActivity(MP4PublishActivity.class, "mp4"); + addActivity(MP4PublishWithSurfaceActivity.class, "mp4 s"); + addActivity(USBCameraActivity.class, "USB Camera"); + addActivity(MultiTrackPlayActivity.class, "Multi Track"); + addActivity(SettingsActivity.class, "Settings"); - broadcastingView.setText(R.string.playing); - broadcastingView.setVisibility(View.VISIBLE); - decrementIdle(); } - @Override - public void onPublishStarted(String streamId) { - Log.w(getClass().getSimpleName(), "onPublishStarted"); - Toast.makeText(this, "Publish started", Toast.LENGTH_SHORT).show(); - broadcastingView.setVisibility(View.VISIBLE); - decrementIdle(); + private void addActivity(Class cls, String label) { + activities.add(new ActivityLink(new Intent(this, cls), label)); } - @Override - public void onPublishFinished(String streamId) { - Log.w(getClass().getSimpleName(), "onPublishFinished"); - Toast.makeText(this, "Publish finished", Toast.LENGTH_SHORT).show(); - broadcastingView.setVisibility(View.GONE); - decrementIdle(); - + private void setListAdapter(List activities) { + list.setAdapter(new ButtonAdapter(activities)); + list.setOnItemClickListener(this); } @Override - public void onPlayFinished(String streamId) { - Log.w(getClass().getSimpleName(), "onPlayFinished"); - Toast.makeText(this, "Play finished", Toast.LENGTH_SHORT).show(); - broadcastingView.setVisibility(View.GONE); - decrementIdle(); + public void onItemClick(AdapterView adapterView, View view, int i, long l) { + ActivityLink link = activities.get(i); + startActivity(link.getIntent()); } - @Override - public void noStreamExistsToPlay(String streamId) { - Log.w(getClass().getSimpleName(), "noStreamExistsToPlay for stream:" + streamId); - Toast.makeText(this, "No stream exist to play", Toast.LENGTH_LONG).show(); - decrementIdle(); - finish(); - } + public class ActivityLink { + private final String label; + private final Intent intent; - @Override - public void streamIdInUse(String streamId) { - Log.w(getClass().getSimpleName(), "streamIdInUse"); - Toast.makeText(this, "Stream id is already in use.", Toast.LENGTH_LONG).show(); - if (webRTCClient.isReconnectionInProgress()) { - webRTCClient.stopStream(streamId); - webRTCClient.startStream(streamId); + public ActivityLink(Intent intent, String label) { + this.intent = intent; + this.label = label; } - decrementIdle(); - } - - @Override - public void onError(String description, String streamId) { - Log.w(getClass().getSimpleName(), "onError:" + description); - Toast.makeText(this, "Error: " +description , Toast.LENGTH_LONG).show(); - decrementIdle(); - } - @Override - protected void onStop() { - super.onStop(); - if (webRTCClient != null) { - Log.i(getClass().getSimpleName(), "onStop and calling stopStream"); - webRTCClient.stopStream(); + public String getLabel() { + return label; } - stoppedStream = true; - } - - @Override - public void onSignalChannelClosed(WebSocket.WebSocketConnectionObserver.WebSocketCloseNotification code, String streamId) { - Toast.makeText(this, "Signal channel closed with code " + code, Toast.LENGTH_LONG).show(); - } - - @Override - public void onDisconnected(String streamId) { - Log.w(getClass().getSimpleName(), "disconnected"); - Toast.makeText(this, "Disconnected", Toast.LENGTH_SHORT).show(); - broadcastingView.setVisibility(View.GONE); - decrementIdle(); - startStreamingButton.setText("Start " + operationName); - // handle reconnection attempt - if (!stoppedStream) { - Log.i(getClass().getSimpleName(),"Disconnected. Trying to reconnect"); - Toast.makeText(this, "Disconnected.Trying to reconnect", Toast.LENGTH_LONG).show(); - } else { - Toast.makeText(this, "Stopped the stream", Toast.LENGTH_LONG).show(); + public Intent getIntent() { + return intent; } - } - @Override - public void onIceConnected(String streamId) { - //it is called when connected to ice - startStreamingButton.setText("Stop " + operationName); } - @Override - public void onIceDisconnected(String streamId) { - //it's called when ice is disconnected - } + public class ButtonAdapter extends BaseAdapter { - public void onOffVideo(View view) { - if (webRTCClient.isVideoOn()) { - webRTCClient.disableVideo(); - } - else { - webRTCClient.enableVideo(); - } - } + private List links; - public void onOffAudio(View view) { - if (webRTCClient.isAudioOn()) { - webRTCClient.disableAudio(); + public ButtonAdapter(List links) { + this.links = links; } - else { - webRTCClient.enableAudio(); - } - } - - @Override - public void onTrackList(String[] tracks) { - } - - @Override - public void onBitrateMeasurement(String streamId, int targetBitrate, int videoBitrate, int audioBitrate) { - Log.e(getClass().getSimpleName(), "st:"+streamId+" tb:"+targetBitrate+" vb:"+videoBitrate+" ab:"+audioBitrate); - if(targetBitrate < (videoBitrate+audioBitrate)) { - Toast.makeText(this, "low bandwidth", Toast.LENGTH_SHORT).show(); + public int getCount() { + return links.size(); } - } - @Override - public void onStreamInfoList(String streamId, ArrayList streamInfoList) { - String[] stringArray = new String[streamInfoList.size()]; - int i = 0; - for (StreamInfo si : streamInfoList) { - stringArray[i++] = si.getHeight()+""; + public ActivityLink getItem(int position) { + return links.get(position); } - ArrayAdapter modeAdapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, android.R.id.text1, stringArray); - streamInfoListSpinner.setAdapter(modeAdapter); - } - @Override - public void onNewVideoTrack(VideoTrack track) { - - } - - @Override - public void onVideoTrackEnded(VideoTrack track) { - - } - - @Override - public void onReconnectionAttempt(String streamId) { - - } - - @Override - public void onBufferedAmountChange(long previousAmount, String dataChannelLabel) { - Log.d(MainActivity.class.getName(), "Data channel buffered amount changed: "); - } - - @Override - public void onStateChange(DataChannel.State state, String dataChannelLabel) { - Log.d(MainActivity.class.getName(), "Data channel state changed: "); - } - - @Override - public void onMessage(DataChannel.Buffer buffer, String dataChannelLabel) { - ByteBuffer data = buffer.data; - String messageText = new String(data.array(), StandardCharsets.UTF_8); - Toast.makeText(this, "New Message: " + messageText, Toast.LENGTH_LONG).show(); - } - - @Override - public void onMessageSent(DataChannel.Buffer buffer, boolean successful) { - if (successful) { - ByteBuffer data = buffer.data; - final byte[] bytes = new byte[data.capacity()]; - data.get(bytes); - String messageText = new String(bytes, StandardCharsets.UTF_8); - - Toast.makeText(this, "Message is sent", Toast.LENGTH_SHORT).show(); - } else { - Toast.makeText(this, "Could not send the text message", Toast.LENGTH_LONG).show(); + public long getItemId(int position) { + return position; } - } - - public void sendTextMessage(String messageToSend) { - final ByteBuffer buffer = ByteBuffer.wrap(messageToSend.getBytes(StandardCharsets.UTF_8)); - DataChannel.Buffer buf = new DataChannel.Buffer(buffer, false); - webRTCClient.sendMessageViaDataChannel(buf); - } - public void showSendDataChannelMessageDialog(View view) { - if (webRTCClient != null && webRTCClient.isDataChannelEnabled()) { - // create an alert builder - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setTitle("Send Message via Data Channel"); - // set the custom layout - final View customLayout = getLayoutInflater().inflate(R.layout.send_message_data_channel, null); - builder.setView(customLayout); - // add a button - builder.setPositiveButton("OK", new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - // send data from the AlertDialog to the Activity - EditText editText = customLayout.findViewById(R.id.message_text_input); - sendTextMessage(editText.getText().toString()); - // sendDialogDataToActivity(editText.getText().toString()); - } - }); - builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - - } - }); - // create and show the alert dialog - AlertDialog dialog = builder.create(); - dialog.show(); - } - else { - Toast.makeText(this, R.string.data_channel_not_available, Toast.LENGTH_LONG).show(); + @Override + public View getView(int position, View convertView, ViewGroup parent) { + TextView button; + Resources resources = parent.getResources(); + if (convertView == null) { + button = new TextView(parent.getContext()); + button.setLayoutParams(new GridView.LayoutParams(MATCH_PARENT, WRAP_CONTENT)); + button.setGravity(Gravity.CENTER); + button.setPadding(8, 48, 8, 48); + button.setTextColor(ResourcesCompat.getColor(resources, R.color.textColor, null)); + button.setBackgroundColor(ResourcesCompat.getColor(resources, R.color.colorPrimary, null)); + convertView = button; + } else { + button = (TextView) convertView; + } + button.setText(links.get(position).getLabel()); + return convertView; } } } diff --git a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/MultiTrackPlayActivity.java b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/MultiTrackPlayActivity.java deleted file mode 100644 index 027dc75a..00000000 --- a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/MultiTrackPlayActivity.java +++ /dev/null @@ -1,187 +0,0 @@ -package io.antmedia.webrtc_android_sample_app; - -import android.content.SharedPreferences; -import android.content.pm.PackageManager; -import android.os.Build; -import android.os.Bundle; -import android.preference.PreferenceManager; -import android.util.Log; -import android.view.View; -import android.view.Window; -import android.view.WindowManager; -import android.widget.Button; -import android.widget.CompoundButton; -import android.widget.EditText; -import android.widget.Toast; -import android.widget.ToggleButton; - -import androidx.annotation.RequiresApi; - -import org.webrtc.RendererCommon; -import org.webrtc.SurfaceViewRenderer; - -import java.util.ArrayList; -import java.util.List; - -import io.antmedia.webrtcandroidframework.IWebRTCClient; -import io.antmedia.webrtcandroidframework.WebRTCClient; -import io.antmedia.webrtcandroidframework.apprtc.CallActivity; - -import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_CAPTURETOTEXTURE_ENABLED; - -public class MultiTrackPlayActivity extends AbstractSampleSDKActivity { - private WebRTCClient webRTCClient; - private String webRTCMode; - private Button startStreamingButton; - private String operationName = ""; - private String tokenId; - private ToggleButton track1Button; - private ToggleButton track2Button; - private String[] allTracks; - private String serverUrl; - private EditText streamIdEditText; - - @RequiresApi(api = Build.VERSION_CODES.M) - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - // Set window styles for fullscreen-window size. Needs to be done before - // adding content. - requestWindowFeature(Window.FEATURE_NO_TITLE); - getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON - | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED - | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON); - //getWindow().getDecorView().setSystemUiVisibility(getSystemUiVisibility()); - - setContentView(R.layout.activity_multitrack); - - - webRTCClient = new WebRTCClient( this,this); - - //webRTCClient.setOpenFrontCamera(false); - streamIdEditText = findViewById(R.id.stream_id_edittext); - streamIdEditText.setText("streamId" + (int)(Math.random()*9999)); - - SharedPreferences sharedPreferences = - PreferenceManager.getDefaultSharedPreferences(this /* Activity context */); - //String streamId = "stream" + (int)(Math.random() * 999); - //streamId = "stream_multi_track"; - tokenId = "tokenId"; - - serverUrl = sharedPreferences.getString(getString(R.string.serverAddress), SettingsActivity.DEFAULT_WEBSOCKET_URL); - - SurfaceViewRenderer cameraViewRenderer = findViewById(R.id.player1); - - SurfaceViewRenderer pipViewRenderer = findViewById(R.id.player2); - - List rendererList = new ArrayList<>(); - rendererList.add(cameraViewRenderer); - rendererList.add(pipViewRenderer); - - startStreamingButton = findViewById(R.id.start_streaming_button); - - track1Button = findViewById(R.id.track_1_button); - track2Button = findViewById(R.id.track_2_button); - - track1Button.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - webRTCClient.enableTrack(webRTCClient.getStreamId(), allTracks[0], isChecked); - } - }); - - track2Button.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - webRTCClient.enableTrack(webRTCClient.getStreamId(), allTracks[1], isChecked); - } - }); - - //webRTCClient.setVideoRenderers(pipViewRenderer, cameraViewRenderer); - - webRTCClient.setRemoteRendererList(rendererList); - - this.getIntent().putExtra(EXTRA_CAPTURETOTEXTURE_ENABLED, true); - - webRTCMode = IWebRTCClient.MODE_MULTI_TRACK_PLAY; - - // this.getIntent().putExtra(CallActivity.EXTRA_VIDEO_FPS, 24); - webRTCClient.init(serverUrl, streamIdEditText.getText().toString(), webRTCMode, tokenId, this.getIntent()); - - } - - public void startStreaming(View v) { - - if (!webRTCClient.isStreaming()) { - ((Button)v).setText("Stop " + operationName); - webRTCClient.startStream(); - } - else { - ((Button)v).setText("Start " + operationName); - webRTCClient.stopStream(); - } - } - - - @Override - public void onPlayStarted(String streamId) { - Log.w(getClass().getSimpleName(), "onPlayStarted"); - Toast.makeText(this, "Play started", Toast.LENGTH_LONG).show(); - webRTCClient.switchVideoScaling(RendererCommon.ScalingType.SCALE_ASPECT_FIT); - } - - @Override - public void noStreamExistsToPlay(String streamId) { - Log.w(getClass().getSimpleName(), "noStreamExistsToPlay"); - Toast.makeText(this, "No stream exist to play", Toast.LENGTH_LONG).show(); - finish(); - } - - @Override - protected void onStop() { - super.onStop(); - webRTCClient.stopStream(); - - } - - @Override - public void onDisconnected(String streamId) { - - Log.w(getClass().getSimpleName(), "disconnected"); - Toast.makeText(this, "Disconnected", Toast.LENGTH_LONG).show(); - - finish(); - } - - public void onOffVideo(View view) { - if (webRTCClient.isVideoOn()) { - webRTCClient.disableVideo(); - } - else { - webRTCClient.enableVideo(); - } - } - - public void onOffAudio(View view) { - if (webRTCClient.isAudioOn()) { - webRTCClient.disableAudio(); - } - else { - webRTCClient.enableAudio(); - } - } - - @Override - public void onTrackList(String[] tracks) { - allTracks = new String[tracks.length+1]; - - allTracks[0] = webRTCClient.getStreamId(); - for (int i = 0; i < tracks.length; i++) { - allTracks[i+1] = tracks[i]; - Log.i(getClass().getSimpleName(), "track id: " + tracks[i]); - } - - webRTCClient.play(webRTCClient.getStreamId(), tokenId, allTracks); - } -} diff --git a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/MultitrackConferenceActivity.java b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/MultitrackConferenceActivity.java deleted file mode 100644 index 76df1bb5..00000000 --- a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/MultitrackConferenceActivity.java +++ /dev/null @@ -1,192 +0,0 @@ -package io.antmedia.webrtc_android_sample_app; - -import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_CAPTURETOTEXTURE_ENABLED; - -import android.content.Context; -import android.content.SharedPreferences; -import android.content.pm.PackageManager; -import android.net.wifi.WifiManager; -import android.os.Bundle; -import android.preference.PreferenceManager; -import android.util.Log; -import android.view.View; -import android.view.Window; -import android.view.WindowManager; -import android.widget.Button; -import android.widget.Switch; -import android.widget.TextView; -import android.widget.Toast; - -import org.webrtc.SurfaceViewRenderer; -import org.webrtc.VideoTrack; - -import java.util.ArrayList; - -import io.antmedia.webrtcandroidframework.MultitrackConferenceManager; -import io.antmedia.webrtcandroidframework.apprtc.CallActivity; - -/* - * This activity is a sample activity shows how to implement Track Based Conference Solution - * https://antmedia.io/reveal-the-secrets-of-3-types-of-video-conference-solutions/ - * - * @deprecated use {@link TrackBasedConferenceActivity} instead - */ -@Deprecated -public class MultitrackConferenceActivity extends AbstractSampleSDKActivity { - - private MultitrackConferenceManager conferenceManager; - private Button audioButton; - private Button videoButton; - - final int RECONNECTION_PERIOD_MLS = 1000; - private boolean stoppedStream = false; - private TextView broadcastingView; - private ArrayList playViewRenderers; - private int rendererIndex = 0; - private Switch playOnlySwitch; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - // Set window styles for fullscreen-window size. Needs to be done before - // adding content. - requestWindowFeature(Window.FEATURE_NO_TITLE); - getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON - | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED - | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON); - //getWindow().getDecorView().setSystemUiVisibility(getSystemUiVisibility()); - - setContentView(R.layout.activity_multitrack_conference); - - broadcastingView = findViewById(R.id.broadcasting_text_view); - - SurfaceViewRenderer publishViewRenderer = findViewById(R.id.publish_view_renderer); - - playViewRenderers = new ArrayList<>(); - - playViewRenderers.add(findViewById(R.id.play_view_renderer1)); - playViewRenderers.add(findViewById(R.id.play_view_renderer2)); - playViewRenderers.add(findViewById(R.id.play_view_renderer3)); - playViewRenderers.add(findViewById(R.id.play_view_renderer4)); - - playOnlySwitch = findViewById(R.id.play_only_switch); - playOnlySwitch.setOnCheckedChangeListener((compoundButton, b) -> { - conferenceManager.setPlayOnlyMode(b); - publishViewRenderer.setVisibility(b ? View.GONE : View.VISIBLE); - }); - - audioButton = findViewById(R.id.control_audio_button); - audioButton.setOnClickListener((view) -> controlAudio(view)); - - videoButton = findViewById(R.id.control_video_button); - videoButton.setOnClickListener((view) -> controlVideo(view)); - - this.getIntent().putExtra(EXTRA_CAPTURETOTEXTURE_ENABLED, true); - // this.getIntent().putExtra(CallActivity.EXTRA_VIDEO_CALL, false); - - SharedPreferences sharedPreferences = - PreferenceManager.getDefaultSharedPreferences(this /* Activity context */); - String serverUrl = sharedPreferences.getString(getString(R.string.serverAddress), SettingsActivity.DEFAULT_WEBSOCKET_URL); - - String roomId = sharedPreferences.getString(getString(R.string.roomId), SettingsActivity.DEFAULT_ROOM_NAME); - String streamId = null; - - conferenceManager = new MultitrackConferenceManager( - this, - this, - getIntent(), - serverUrl, - roomId, - publishViewRenderer, - playViewRenderers, //new ArrayList<>(),// - streamId, - this - ); - - conferenceManager.init(); - conferenceManager.setPlayOnlyMode(false); - conferenceManager.setOpenFrontCamera(true); - conferenceManager.setReconnectionEnabled(true); - } - - public void joinConference(View v) { - - if (!conferenceManager.isJoined()) { - Log.w(getClass().getSimpleName(), "Joining Conference"); - ((Button) v).setText("Leave"); - conferenceManager.joinTheConference(); - } else { - ((Button) v).setText("Join"); - conferenceManager.leaveFromConference(); - stoppedStream = true; - } - } - - @Override - public void onPublishStarted(String streamId) { - Log.w(getClass().getSimpleName(), "onPublishStarted"); - Toast.makeText(this, "Publish started", Toast.LENGTH_SHORT).show(); - - broadcastingView.setText("Publishing"); - broadcastingView.setVisibility(View.VISIBLE); - } - - @Override - public void onPublishFinished(String streamId) { - Log.w(getClass().getSimpleName(), "onPublishFinished"); - Toast.makeText(this, "Publish finished", Toast.LENGTH_SHORT).show(); - broadcastingView.setVisibility(View.GONE); - } - - @Override - protected void onStop() { - super.onStop(); - audioButton.setText("Disable Audio"); - videoButton.setText("Disable Video"); - stoppedStream = true; - } - - @Override - public void onDisconnected(String streamId) { - Log.w(getClass().getSimpleName(), "disconnected"); - Toast.makeText(this, "Disconnected", Toast.LENGTH_SHORT).show(); - audioButton.setText("Disable Audio"); - videoButton.setText("Disable Video"); - } - - @Override - public void onNewVideoTrack(VideoTrack track) { - if(false && !track.id().contains(conferenceManager.getStreamId())) { - SurfaceViewRenderer renderer = playViewRenderers.get(rendererIndex++%4); - conferenceManager.addTrackToRenderer(track, renderer); - } - } - - public void controlAudio(View view) { - if (conferenceManager.isPublisherAudioOn()) { - conferenceManager.disableAudio(); - audioButton.setText("Enable Audio"); - } else { - conferenceManager.enableAudio(); - audioButton.setText("Disable Audio"); - } - } - - public void controlVideo(View view) { - if (conferenceManager.isPublisherVideoOn()) { - conferenceManager.disableVideo(); - videoButton.setText("Enable Video"); - - } else { - conferenceManager.enableVideo(); - videoButton.setText("Disable Video"); - } - } - - public void changeWifiState(boolean state) { - WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE); - wifiManager.setWifiEnabled(state); - } -} - diff --git a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/PublishActivity.java b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/PublishActivity.java deleted file mode 100644 index 175bf334..00000000 --- a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/PublishActivity.java +++ /dev/null @@ -1,70 +0,0 @@ -package io.antmedia.webrtc_android_sample_app; - -import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_CAPTURETOTEXTURE_ENABLED; -import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_DATA_CHANNEL_ENABLED; -import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_VIDEO_BITRATE; -import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_VIDEO_FPS; - -import android.app.Activity; -import android.content.DialogInterface; -import android.content.SharedPreferences; -import android.os.Build; -import android.os.Bundle; -import android.preference.PreferenceManager; -import android.util.Log; -import android.view.View; -import android.view.Window; -import android.view.WindowManager; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; -import android.widget.Button; -import android.widget.EditText; -import android.widget.Spinner; -import android.widget.TextView; -import android.widget.Toast; - -import androidx.annotation.RequiresApi; -import androidx.appcompat.app.AlertDialog; - -import org.webrtc.DataChannel; -import org.webrtc.RendererCommon; -import org.webrtc.SurfaceViewRenderer; -import org.webrtc.VideoTrack; - -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; - -import de.tavendo.autobahn.WebSocket; -import io.antmedia.webrtcandroidframework.DefaultWebRTCListener; -import io.antmedia.webrtcandroidframework.IWebRTCClient; -import io.antmedia.webrtcandroidframework.StreamInfo; -import io.antmedia.webrtcandroidframework.WebRTCClient; - -public class PublishActivity extends Activity { - @RequiresApi(api = Build.VERSION_CODES.M) - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_main); - - View cameraViewRenderer = findViewById(R.id.camera_view_renderer); - View pipViewRenderer = findViewById(R.id.pip_view_renderer); - - IWebRTCClient webRTCClient = IWebRTCClient.builder() - .setPipRenderer((SurfaceViewRenderer) pipViewRenderer) - .setFullScreenRenderer((SurfaceViewRenderer) cameraViewRenderer) - .setServerUrl("wss://test.antmedia.io:5443/LiveApp/websocket") - .setContext(getApplicationContext()) - .setWebRTCListener(new DefaultWebRTCListener(this)) - .build(); - - View startStreamingButton = findViewById(R.id.start_streaming_button); - startStreamingButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - webRTCClient.publish("stream1"); - } - }); - } -} diff --git a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/PushToTalkActivity.java b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/PushToTalkActivity.java deleted file mode 100644 index 8875a7a2..00000000 --- a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/PushToTalkActivity.java +++ /dev/null @@ -1,139 +0,0 @@ -package io.antmedia.webrtc_android_sample_app; - -import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_CAPTURETOTEXTURE_ENABLED; -import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_VIDEO_CALL; - -import android.annotation.SuppressLint; -import android.app.Activity; -import android.content.SharedPreferences; -import android.content.pm.PackageManager; -import android.graphics.Color; -import android.os.Bundle; -import android.preference.PreferenceManager; -import android.util.Log; -import android.view.MotionEvent; -import android.view.View; -import android.view.Window; -import android.view.WindowManager; -import android.widget.Button; -import android.widget.Toast; - -import androidx.test.espresso.IdlingResource; -import androidx.test.espresso.idling.CountingIdlingResource; - -import org.json.JSONObject; -import org.webrtc.DataChannel; -import org.webrtc.SurfaceViewRenderer; -import org.webrtc.VideoTrack; - -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; - -import de.tavendo.autobahn.WebSocket; -import io.antmedia.webrtcandroidframework.IDataChannelObserver; -import io.antmedia.webrtcandroidframework.IWebRTCListener; -import io.antmedia.webrtcandroidframework.MultitrackConferenceManager; -import io.antmedia.webrtcandroidframework.StreamInfo; -import io.antmedia.webrtcandroidframework.apprtc.CallActivity; - -public class PushToTalkActivity extends AbstractSampleSDKActivity { - - private MultitrackConferenceManager conferenceManager; - - @SuppressLint("WrongViewCast") - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - // Set window styles for fullscreen-window size. Needs to be done before - // adding content. - requestWindowFeature(Window.FEATURE_NO_TITLE); - getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON - | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED - | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON); - //getWindow().getDecorView().setSystemUiVisibility(getSystemUiVisibility()); - - this.getIntent().putExtra(EXTRA_VIDEO_CALL, false); - - setContentView(R.layout.activity_ptt); - - ArrayList playViewRenderers = new ArrayList<>(); - - // playViewRenderers.add(findViewById(R.id.play_view_renderer1)); - - View talkButton = findViewById(R.id.talkButton); - - talkButton.setOnTouchListener(new View.OnTouchListener() { - @Override - public boolean onTouch(View view, MotionEvent motionEvent) { - if(motionEvent.getAction() == MotionEvent.ACTION_UP) { - controlAudio(false); - view.setBackgroundColor(Color.RED); - return true; - } - if(motionEvent.getAction() == MotionEvent.ACTION_DOWN) { - controlAudio(true); - view.setBackgroundColor(Color.GREEN); - return true; - } - return false; - } - }); - - this.getIntent().putExtra(EXTRA_CAPTURETOTEXTURE_ENABLED, true); - // this.getIntent().putExtra(CallActivity.EXTRA_VIDEO_CALL, false); - - String streamId = "stream"+ (int)(Math.random()*9999); - String roomId = "room1"; - SharedPreferences sharedPreferences = - PreferenceManager.getDefaultSharedPreferences(this /* Activity context */); - String serverUrl = sharedPreferences.getString(getString(R.string.serverAddress), SettingsActivity.DEFAULT_WEBSOCKET_URL); - conferenceManager = new MultitrackConferenceManager( - this, - this, - getIntent(), - serverUrl, - roomId, - null, - playViewRenderers, - streamId, - this - ); - - conferenceManager.init(); - conferenceManager.setPlayOnlyMode(false); - conferenceManager.setOpenFrontCamera(true); - } - - public void joinConference(View v) { - - if (!conferenceManager.isJoined()) { - Log.w(getClass().getSimpleName(), "Joining Conference"); - ((Button)v).setText("Leave"); - conferenceManager.joinTheConference(); - } - else { - ((Button)v).setText("Join"); - conferenceManager.leaveFromConference(); - } - } - @Override - public void onPublishStarted(String streamId) { - Log.w(getClass().getSimpleName(), "onPublishStarted"); - Toast.makeText(this, "Publish started", Toast.LENGTH_SHORT).show(); - - runOnUiThread(() -> controlAudio(false)); - } - - public void controlAudio(boolean enable) { - if (enable) { - conferenceManager.enableAudio(); - conferenceManager.updateAudioLevel(10); - } else { - conferenceManager.disableAudio(); - conferenceManager.updateAudioLevel(0); - } - } -} - diff --git a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/ScreenCaptureActivity.java b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/ScreenCaptureActivity.java deleted file mode 100644 index ac7fc9df..00000000 --- a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/ScreenCaptureActivity.java +++ /dev/null @@ -1,254 +0,0 @@ -package io.antmedia.webrtc_android_sample_app; - -import static io.antmedia.webrtc_android_sample_app.MediaProjectionService.EXTRA_MEDIA_PROJECTION_DATA; - -import android.app.Activity; -import android.content.Intent; -import android.content.SharedPreferences; -import android.content.pm.PackageManager; -import android.hardware.SensorManager; -import android.media.projection.MediaProjection; -import android.media.projection.MediaProjectionManager; -import android.os.Build; -import android.os.Bundle; -import android.preference.PreferenceManager; -import android.util.DisplayMetrics; -import android.util.Log; -import android.view.OrientationEventListener; -import android.view.View; -import android.view.Window; -import android.view.WindowManager; -import android.widget.Button; -import android.widget.EditText; -import android.widget.RadioGroup; -import android.widget.Toast; - -import org.webrtc.RendererCommon; -import org.webrtc.ScreenCapturerAndroid; -import org.webrtc.SurfaceViewRenderer; -import org.webrtc.VideoTrack; - -import java.util.ArrayList; - -import androidx.test.espresso.IdlingResource; -import androidx.test.espresso.idling.CountingIdlingResource; - -import de.tavendo.autobahn.WebSocket; -import io.antmedia.webrtcandroidframework.IWebRTCClient; -import io.antmedia.webrtcandroidframework.IWebRTCListener; -import io.antmedia.webrtcandroidframework.StreamInfo; -import io.antmedia.webrtcandroidframework.WebRTCClient; -import io.antmedia.webrtcandroidframework.apprtc.CallActivity; - -public class ScreenCaptureActivity extends AbstractSampleSDKActivity { - - private WebRTCClient webRTCClient; - private RadioGroup bg; - private String tokenId = "tokenId"; - private String serverUrl; - private EditText streamIdEditText; - private int videoWidth ,videoHeight = 0; - - private static final String TAG = ScreenCaptureActivity.class.getSimpleName(); - private View broadcastingView; - - private OrientationEventListener orientationEventListener; - - - /* - ATTENTION: Android refresh rate changes according to the screen changes. - In order to have consistent behavior in all cases you need to set the Refresh rate to Standard. - Check how to set refresh rate in android - One Scenario: Tap Display. Tap Advanced. Tap Smooth Display. - Toggle the switch off or on to enable or disable the higher refresh rate - For samsung devices: Motion smoothness - - */ - - //TODO: I think opening the camera at first does not make sense. I mean expectation is to open the screen. @mekya. - //TODO: Try to provide a better experience - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - // Set window styles for fullscreen-window size. Needs to be done before - // adding content. - requestWindowFeature(Window.FEATURE_NO_TITLE); - getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON - | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED - | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON); - //getWindow().getDecorView().setSystemUiVisibility(getSystemUiVisibility()); - - setContentView(R.layout.activity_screenshare); - - - webRTCClient = new WebRTCClient(this, this); - - streamIdEditText = findViewById(R.id.stream_id_edittext); - streamIdEditText.setText("streamId" + (int)(Math.random()*9999)); - //webRTCClient.setOpenFrontCamera(false); - - broadcastingView = findViewById(R.id.broadcasting_text_view); - - SurfaceViewRenderer cameraViewRenderer = findViewById(R.id.camera_view_renderer); - - SurfaceViewRenderer pipViewRenderer = findViewById(R.id.pip_view_renderer); - - - webRTCClient.setVideoRenderers(pipViewRenderer, cameraViewRenderer); - - // fix:- to remove the black boarder issue - DisplayMetrics displayMetrics = webRTCClient.getDisplayMetrics(); - videoWidth = displayMetrics.widthPixels; - videoHeight = displayMetrics.heightPixels; - - this.getIntent().putExtra(CallActivity.EXTRA_CAPTURETOTEXTURE_ENABLED, true); - this.getIntent().putExtra(CallActivity.EXTRA_VIDEO_BITRATE, 2500); - this.getIntent().putExtra(CallActivity.EXTRA_VIDEO_WIDTH, videoWidth); - this.getIntent().putExtra(CallActivity.EXTRA_VIDEO_HEIGHT, videoHeight); - //this.getIntent().putExtra(CallActivity.EXTRA_SCREENCAPTURE, true); - this.getIntent().putExtra(CallActivity.EXTRA_VIDEO_FPS, 30); - - bg = findViewById(R.id.rbGroup); - bg.check(R.id.rbFront); - bg.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(RadioGroup group, int checkedId) { - String newSource = ""; - if(checkedId == R.id.rbScreen) { - newSource = WebRTCClient.SOURCE_SCREEN; - getIntent().putExtra(CallActivity.EXTRA_SCREENCAPTURE, true); - } - else if(checkedId == R.id.rbFront) { - newSource = WebRTCClient.SOURCE_FRONT; - getIntent().putExtra(CallActivity.EXTRA_SCREENCAPTURE, false); - } - else if(checkedId == R.id.rbRear) { - newSource = WebRTCClient.SOURCE_REAR; - getIntent().putExtra(CallActivity.EXTRA_SCREENCAPTURE, false); - } - idlingResource.increment(); - webRTCClient.changeVideoSource(newSource); - decrementIdle(); - } - }); - - SharedPreferences sharedPreferences = - PreferenceManager.getDefaultSharedPreferences(this /* Activity context */); - serverUrl = sharedPreferences.getString(getString(R.string.serverAddress), SettingsActivity.DEFAULT_WEBSOCKET_URL); - webRTCClient.init(serverUrl, streamIdEditText.getText().toString(), IWebRTCClient.MODE_PUBLISH, tokenId, this.getIntent()); - - orientationEventListener - = new OrientationEventListener(this, SensorManager.SENSOR_DELAY_NORMAL){ - - @Override - public void onOrientationChanged(int rotationDegree) { - if (webRTCClient.getVideoCapturer() instanceof ScreenCapturerAndroid) { - ((ScreenCapturerAndroid) webRTCClient.getVideoCapturer()).rotateScreen(rotationDegree); - } - }}; - - if (orientationEventListener.canDetectOrientation()){ - orientationEventListener.enable(); - } - else{ - orientationEventListener.disable(); - } - } - - @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) - { - // If the device version is v29 or higher, screen sharing will work service due to media projection policy. - // Otherwise media projection will work without service - if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q){ - - MediaProjectionService.setListener(mediaProjection -> { - webRTCClient.setMediaProjection(mediaProjection); - webRTCClient.onActivityResult(requestCode, resultCode, data); - }); - - Intent serviceIntent = new Intent(this, MediaProjectionService.class); - serviceIntent.putExtra(EXTRA_MEDIA_PROJECTION_DATA, data); - startForegroundService(serviceIntent); - } - else{ - webRTCClient.onActivityResult(requestCode, resultCode, data); - } - - decrementIdle(); - } - - public void startStreaming(View v) { - - webRTCClient.setStreamId(streamIdEditText.getText().toString()); - incrementIdle(); - //focus edit text to make the system update the frames - streamIdEditText.requestFocus(); - if (!webRTCClient.isStreaming()) { - ((Button)v).setText("Stop Streaming"); - Log.i(TAG, "Starting streaming"); - webRTCClient.startStream(); - } - else { - ((Button)v).setText("Start Streaming"); - webRTCClient.stopStream(); - } - } - public void switchCamera(View v) { - webRTCClient.switchCamera(); - } - - @Override - public void onPlayStarted(String streamId) { - Log.w(getClass().getSimpleName(), "onPlayStarted"); - Toast.makeText(this, "Play started", Toast.LENGTH_LONG).show(); - webRTCClient.switchVideoScaling(RendererCommon.ScalingType.SCALE_ASPECT_FIT); - } - - @Override - public void onPublishStarted(String streamId) { - Log.w(getClass().getSimpleName(), "onPublishStarted"); - Toast.makeText(this, "Publish started", Toast.LENGTH_LONG).show(); - broadcastingView.setVisibility(View.VISIBLE); - decrementIdle(); - } - - @Override - public void onPublishFinished(String streamId) { - Log.w(getClass().getSimpleName(), "onPublishFinished"); - Toast.makeText(this, "Publish finished", Toast.LENGTH_LONG).show(); - broadcastingView.setVisibility(View.GONE); - decrementIdle(); - } - - - @Override - public void streamIdInUse(String streamId) { - Log.w(getClass().getSimpleName(), "streamIdInUse"); - Toast.makeText(this, "Stream id is already in use.", Toast.LENGTH_LONG).show(); - decrementIdle(); - } - - @Override - public void onError(String description, String streamId) { - Log.w(getClass().getSimpleName(), "onError:" + description); - Toast.makeText(this, "Error: " +description , Toast.LENGTH_LONG).show(); - decrementIdle(); - } - - @Override - protected void onDestroy() { - super.onDestroy(); - webRTCClient.stopStream(); - } - - @Override - public void onDisconnected(String streamId) { - Log.w(getClass().getSimpleName(), "disconnected"); - Toast.makeText(this, "Disconnected", Toast.LENGTH_LONG).show(); - broadcastingView.setVisibility(View.GONE); - decrementIdle(); - } -} - diff --git a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/TestableActivity.java b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/TestableActivity.java new file mode 100644 index 00000000..eec1beee --- /dev/null +++ b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/TestableActivity.java @@ -0,0 +1,47 @@ +package io.antmedia.webrtc_android_sample_app; + +import android.app.Activity; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.view.Window; +import android.view.WindowManager; + +import androidx.annotation.Nullable; +import androidx.preference.PreferenceManager; +import androidx.test.espresso.IdlingResource; +import androidx.test.espresso.idling.CountingIdlingResource; + +public abstract class TestableActivity extends Activity { + public CountingIdlingResource idlingResource = new CountingIdlingResource("Load", true); + protected SharedPreferences sharedPreferences; + + public void incrementIdle() { + idlingResource.increment(); + } + public void decrementIdle() { + if (!idlingResource.isIdleNow()) { + idlingResource.decrement(); + } + } + + public IdlingResource getIdlingResource() { + return idlingResource; + } + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + //window styles for fullscreen-window size. Needs to be done before + // adding content. + requestWindowFeature(Window.FEATURE_NO_TITLE); + getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON + | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED + | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON); + //getWindow().getDecorView().setSystemUiVisibility(getSystemUiVisibility()); + + sharedPreferences = + PreferenceManager.getDefaultSharedPreferences(this /* Activity context */); + + } +} + diff --git a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/TrackBasedConferenceActivity.java b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/TrackBasedConferenceActivity.java deleted file mode 100644 index 2273204f..00000000 --- a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/TrackBasedConferenceActivity.java +++ /dev/null @@ -1,410 +0,0 @@ -package io.antmedia.webrtc_android_sample_app; - -import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_CAPTURETOTEXTURE_ENABLED; - -import android.content.Context; -import android.content.SharedPreferences; -import android.content.pm.PackageManager; -import android.net.wifi.WifiManager; -import android.os.Build; -import android.os.Bundle; -import android.os.Handler; -import android.preference.PreferenceManager; -import android.util.Log; -import android.view.View; -import android.view.Window; -import android.view.WindowManager; -import android.widget.Button; -import android.widget.Switch; -import android.widget.TextView; -import android.widget.Toast; - -import org.json.JSONException; -import org.json.JSONObject; -import org.webrtc.DataChannel; -import org.webrtc.SurfaceViewRenderer; -import org.webrtc.VideoTrack; - -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Random; - -import io.antmedia.webrtcandroidframework.WebRTCClient; -import io.antmedia.webrtcandroidframework.apprtc.CallActivity; - -public class TrackBasedConferenceActivity extends AbstractSampleSDKActivity { - - public static final String MIC_UNMUTED = "MIC_UNMUTED"; - public static final String MIC_MUTED = "MIC_MUTED"; - public static final String CAM_TURNED_ON = "CAM_TURNED_ON"; - public static final String CAM_TURNED_OFF = "CAM_TURNED_OFF"; - public static final String EVENT_TYPE = "eventType"; - public static final String STREAM_ID = "streamId"; - public static final String UPDATE_STATUS = "UPDATE_STATUS"; - public static final String MIC_STATUS = "mic"; - public static final String CAMERA_STATUS = "camera"; - - private int STATUS_SEND_PERIOD_MILLIS = 3000; - - private WebRTCClient webRTCClient; - private Button audioButton; - private Button videoButton; - private boolean stoppedStream = false; - private TextView broadcastingView; - private ArrayList playViewRenderers; - private int rendererIndex = 0; - private boolean playOnlyMode = false; - private boolean joined = false; - private String roomId; - private String streamId; - - private Handler handler = new Handler(); - - private int ROOM_INFO_POLLING_MILLIS = 5000; - private Runnable getRoomInfoRunnable = new Runnable() { - @Override - public void run() { - getRoomInfo(); - handler.postDelayed(this, ROOM_INFO_POLLING_MILLIS); - } - }; - private String token; - private String subscriberId; - private String subscriberCode; - private boolean videoCallEnabled = true; - private boolean audioCallEnabled = true; - private String streamName; - private boolean playMessageSent; - private String viewerInfo; - - private Runnable sendStatusRunnable = new Runnable() { - @Override - public void run() { - sendStatusMessage(); - handler.postDelayed(this, STATUS_SEND_PERIOD_MILLIS); - } - }; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - // Set window styles for fullscreen-window size. Needs to be done before - // adding content. - requestWindowFeature(Window.FEATURE_NO_TITLE); - getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON - | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED - | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON); - //getWindow().getDecorView().setSystemUiVisibility(getSystemUiVisibility()); - - setContentView(R.layout.activity_multitrack_conference); - - broadcastingView = findViewById(R.id.broadcasting_text_view); - - SurfaceViewRenderer publishViewRenderer = findViewById(R.id.publish_view_renderer); - - playViewRenderers = new ArrayList<>(); - - playViewRenderers.add(findViewById(R.id.play_view_renderer1)); - playViewRenderers.add(findViewById(R.id.play_view_renderer2)); - playViewRenderers.add(findViewById(R.id.play_view_renderer3)); - playViewRenderers.add(findViewById(R.id.play_view_renderer4)); - - Switch playOnlySwitch = findViewById(R.id.play_only_switch); - playOnlySwitch.setOnCheckedChangeListener((compoundButton, b) -> { - //conferenceManager.setPlayOnlyMode(b); - playOnlyMode = b; - publishViewRenderer.setVisibility(b ? View.GONE : View.VISIBLE); - }); - - audioButton = findViewById(R.id.control_audio_button); - audioButton.setOnClickListener((view) -> controlAudio(view)); - - videoButton = findViewById(R.id.control_video_button); - videoButton.setOnClickListener((view) -> controlVideo(view)); - - this.getIntent().putExtra(EXTRA_CAPTURETOTEXTURE_ENABLED, true); - // this.getIntent().putExtra(CallActivity.EXTRA_VIDEO_CALL, false); - - SharedPreferences sharedPreferences = - PreferenceManager.getDefaultSharedPreferences(this /* Activity context */); - String serverUrl = sharedPreferences.getString(getString(R.string.serverAddress), SettingsActivity.DEFAULT_WEBSOCKET_URL); - - roomId = sharedPreferences.getString(getString(R.string.roomId), SettingsActivity.DEFAULT_ROOM_NAME); - streamId = streamId == null ? "stream"+new Random().nextInt(99999) : streamId; - - - webRTCClient = new WebRTCClient(this, this); - webRTCClient.setMainTrackId(roomId); - webRTCClient.setSelfStreamId(streamId); - webRTCClient.setVideoRenderers(null, publishViewRenderer); - webRTCClient.setRemoteRendererList(playViewRenderers); - - - webRTCClient.init(serverUrl, streamId, WebRTCClient.MODE_TRACK_BASED_CONFERENCE, "", this.getIntent()); - webRTCClient.setDataChannelEnabled(true); - webRTCClient.setDataChannelObserver(this); - webRTCClient.setOpenFrontCamera(true); - webRTCClient.setReconnectionEnabled(true); - } - - private void scheduleGetRoomInfo() { - handler.postDelayed(getRoomInfoRunnable, ROOM_INFO_POLLING_MILLIS); - } - - private void clearGetRoomInfoSchedule() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - if (handler.hasCallbacks(getRoomInfoRunnable)) { - handler.removeCallbacks(getRoomInfoRunnable); - } - } else { - handler.removeCallbacks(getRoomInfoRunnable); - } - } - - private void getRoomInfo() { - webRTCClient.getRoomInfo(roomId, streamId); - } - - public void joinConference(View v) { - - if (!joined) { - Log.w(getClass().getSimpleName(), "Joining Conference"); - ((Button) v).setText("Leave"); - webRTCClient.joinToConferenceRoom(roomId, streamId); - } else { - ((Button) v).setText("Join"); - webRTCClient.leaveFromConference(roomId); - stoppedStream = true; - } - } - - @Override - public void onPublishStarted(String streamId) { - Log.w(getClass().getSimpleName(), "onPublishStarted"); - Toast.makeText(this, "Publish started", Toast.LENGTH_SHORT).show(); - - broadcastingView.setText("Publishing"); - broadcastingView.setVisibility(View.VISIBLE); - - scheduleSendStatusTimer(); - decrementIdle(); - } - - @Override - public void onPublishFinished(String streamId) { - Log.w(getClass().getSimpleName(), "onPublishFinished"); - Toast.makeText(this, "Publish finished", Toast.LENGTH_SHORT).show(); - broadcastingView.setVisibility(View.GONE); - } - - @Override - protected void onStop() { - super.onStop(); - audioButton.setText("Disable Audio"); - videoButton.setText("Disable Video"); - stoppedStream = true; - } - - @Override - public void onDisconnected(String streamId) { - Log.w(getClass().getSimpleName(), "disconnected"); - Toast.makeText(this, "Disconnected", Toast.LENGTH_SHORT).show(); - audioButton.setText("Disable Audio"); - videoButton.setText("Disable Video"); - } - - @Override - public void onNewVideoTrack(VideoTrack track) { - if(false && !track.id().contains(webRTCClient.getStreamId())) { - SurfaceViewRenderer renderer = playViewRenderers.get(rendererIndex++%4); - webRTCClient.addTrackToRenderer(track, renderer); - } - } - - @Override - public void onJoinedTheRoom(String streamId, String[] streams) { - Log.w(this.getClass().getSimpleName(), "On Joined the Room "); - - if(!webRTCClient.isReconnectionInProgress() && !playOnlyMode) { - publishStream(streamId); - } - - if(playOnlyMode) { - startPlaying(streams); - } - - joined = true; - // start periodic polling of room info - scheduleGetRoomInfo(); - if(streams.length > 0) { - //on track list triggers start playing - onTrackList(streams); - } - } - - @Override - public void onLeftTheRoom(String roomId) { - clearGetRoomInfoSchedule(); - joined = false; - playMessageSent = false; - clearSendStatusSchedule(); - } - - public void publishStream(String streamId) { - incrementIdle(); - if (!this.playOnlyMode) { - webRTCClient.publish(streamId, token, videoCallEnabled, audioCallEnabled, subscriberId, subscriberCode, streamName, roomId); - } - else { - Log.i(getClass().getSimpleName(), "Play only mode. No publishing"); - } - } - - private void startPlaying(String[] streams) { - if(!playMessageSent) { - webRTCClient.play(roomId, token, streams, subscriberId, subscriberCode, viewerInfo); - playMessageSent = true; - } - } - - @Override - public void onRoomInformation(String[] streams) { - if (webRTCClient != null) { - startPlaying(streams); - } - } - - public void controlAudio(View view) { - if (webRTCClient.isAudioOn()) { - webRTCClient.disableAudio(); - audioButton.setText("Enable Audio"); - sendNotificationEvent(MIC_MUTED, null); - } else { - webRTCClient.enableAudio(); - audioButton.setText("Disable Audio"); - sendNotificationEvent(MIC_UNMUTED, null); - } - } - - public void controlVideo(View view) { - if (webRTCClient.isVideoOn()) { - webRTCClient.disableVideo(); - videoButton.setText("Enable Video"); - sendNotificationEvent(CAM_TURNED_OFF, null); - - } else { - webRTCClient.enableVideo(); - videoButton.setText("Disable Video"); - sendNotificationEvent(CAM_TURNED_ON, null); - } - } - - public void changeWifiState(boolean state) { - WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE); - wifiManager.setWifiEnabled(state); - } - - - private void scheduleSendStatusTimer() { - handler.postDelayed(sendStatusRunnable, STATUS_SEND_PERIOD_MILLIS); - } - - private void clearSendStatusSchedule() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - if (handler.hasCallbacks(sendStatusRunnable)) { - handler.removeCallbacks(sendStatusRunnable); - } - } else { - handler.removeCallbacks(sendStatusRunnable); - } - } - - public void sendStatusMessage() { - try { - JSONObject jsonObject = new JSONObject(); - jsonObject.put(MIC_STATUS, webRTCClient.isAudioOn()); - jsonObject.put(CAMERA_STATUS, webRTCClient.isVideoOn()); - sendNotificationEvent(UPDATE_STATUS, jsonObject); - } catch (JSONException e) { - throw new RuntimeException(e); - } - } - - public void sendNotificationEvent(String eventType, JSONObject data) { - JSONObject jsonObject = new JSONObject(); - try { - jsonObject.put("streamId", streamId); - jsonObject.put("eventType", eventType); - jsonObject.put(STREAM_ID, streamId); - jsonObject.put(EVENT_TYPE, eventType); - if(data != null) { - jsonObject.put("info", data); - } - - String notificationEventText = jsonObject.toString(); - - final ByteBuffer buffer = ByteBuffer.wrap(notificationEventText.getBytes(StandardCharsets.UTF_8)); - DataChannel.Buffer buf = new DataChannel.Buffer(buffer, false); - webRTCClient.sendMessageViaDataChannel(streamId, buf); - } catch (JSONException e) { - Log.e(this.getClass().getSimpleName(), "JSON write error when creating notification event"); - } - } - - public void onMessage(final DataChannel.Buffer buffer, String dataChannelLabel) { - if (!buffer.binary) { - ByteBuffer data = buffer.data; - try { - String strDataJson = new String(data.array(), StandardCharsets.UTF_8); - JSONObject jsonObject = new JSONObject(strDataJson); - String eventType = jsonObject.getString(EVENT_TYPE); - String streamId = jsonObject.getString(STREAM_ID); - if(eventType.equals(MIC_MUTED)) { - String messageText = "Microphone is muted for " + streamId; - Log.d(AbstractSampleSDKActivity.class.getName(), messageText); - Toast.makeText(this, messageText, Toast.LENGTH_LONG).show(); - } - else if(eventType.equals(MIC_UNMUTED)) { - String messageText = "Microphone is unmuted for " + streamId; - Log.d(AbstractSampleSDKActivity.class.getName(), messageText); - Toast.makeText(this, messageText, Toast.LENGTH_LONG).show(); - } - else if(eventType.equals(CAM_TURNED_ON)) { - String messageText = "Camera is turned on for " + streamId; - Log.d(AbstractSampleSDKActivity.class.getName(), messageText); - Toast.makeText(this, messageText, Toast.LENGTH_LONG).show(); - - } - else if(eventType.equals(CAM_TURNED_OFF)) { - String messageText = "Camera is turned off for " + streamId; - Log.d(AbstractSampleSDKActivity.class.getName(), messageText); - Toast.makeText(this, messageText, Toast.LENGTH_LONG).show(); - } - else if(eventType.equals(UPDATE_STATUS)) { - JSONObject info = jsonObject.getJSONObject("info"); - boolean micStatus = info.getBoolean(MIC_STATUS); - boolean cameraStatus = info.getBoolean(CAMERA_STATUS); - String messageText = "Status update for " + streamId + " mic: " + micStatus + " camera: " + cameraStatus; - Log.d(AbstractSampleSDKActivity.class.getName(), messageText); - Toast.makeText(this, messageText, Toast.LENGTH_LONG).show(); - } - } catch (JSONException e) { - throw new RuntimeException(e); - } - } - } - - // This method is added for unit testing purposes - public void setWebRTCClient(WebRTCClient webRTCClient) { - this.webRTCClient = webRTCClient; - } - - // This method is added for unit testing purposes - public void setAudioButton(Button audioButton) { - this.audioButton = audioButton; - } - -} - diff --git a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/advanced/MP3PublishActivity.java b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/advanced/MP3PublishActivity.java new file mode 100644 index 00000000..482aea33 --- /dev/null +++ b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/advanced/MP3PublishActivity.java @@ -0,0 +1,123 @@ +package io.antmedia.webrtc_android_sample_app.advanced; + +import android.os.Build; +import android.os.Bundle; +import android.os.Environment; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.annotation.RequiresApi; + +import org.webrtc.SurfaceViewRenderer; + +import io.antmedia.webrtc_android_sample_app.R; +import io.antmedia.webrtc_android_sample_app.TestableActivity; +import io.antmedia.webrtc_android_sample_app.basic.SettingsActivity; +import io.antmedia.webrtcandroidframework.api.DefaultDataChannelObserver; +import io.antmedia.webrtcandroidframework.api.DefaultWebRTCListener; +import io.antmedia.webrtcandroidframework.api.IDataChannelObserver; +import io.antmedia.webrtcandroidframework.api.IWebRTCClient; +import io.antmedia.webrtcandroidframework.api.IWebRTCListener; +import io.antmedia.webrtcandroidframework.core.MediaFileReader; + +public class MP3PublishActivity extends TestableActivity { + private View broadcastingView; + private View startStreamingButton; + private String streamId; + private IWebRTCClient webRTCClient; + private MediaFileReader mediaFileReader; + + @RequiresApi(api = Build.VERSION_CODES.N) + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_publish); + + SurfaceViewRenderer fullScreenRenderer = findViewById(R.id.full_screen_renderer); + broadcastingView = findViewById(R.id.broadcasting_text_view); + startStreamingButton = findViewById(R.id.start_streaming_button); + TextView streamIdEditText = findViewById(R.id.stream_id_edittext); + + String serverUrl = sharedPreferences.getString(getString(R.string.serverAddress), SettingsActivity.DEFAULT_WEBSOCKET_URL); + streamId = "streamId" + (int)(Math.random()*9999); + streamIdEditText.setText(streamId); + + webRTCClient = IWebRTCClient.builder() + .setLocalVideoRenderer(fullScreenRenderer) + .setServerUrl(serverUrl) + .setActivity(this) + .setCustomAudioFeed(true) + .setWebRTCListener(createWebRTCListener()) + .setDataChannelObserver(createDatachannelObserver()) + .build(); + + + String path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath() + "/sample_44100_stereo.mp3"; + + mediaFileReader = MediaFileReader.fromResources(getResources(), R.raw.sample_44100_stereo) + .withFrameType(MediaFileReader.FrameType.audio) + .withAudioFrameListener(audioData -> onAudioData(audioData)); + + View startStreamingButton = findViewById(R.id.start_streaming_button); + startStreamingButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + startStopStream(v); + } + }); + } + + private void onAudioData(byte[] data) { + webRTCClient.getAudioInput().pushAudio(data, data.length); + + } + + public void startStopStream(View v) { + incrementIdle(); + if (!webRTCClient.isStreaming(streamId)) { + ((Button) v).setText("Stop"); + Log.i(getClass().getSimpleName(), "Calling publish start"); + + webRTCClient.publish(streamId); + mediaFileReader.start(); + } + else { + ((Button) v).setText("Start"); + Log.i(getClass().getSimpleName(), "Calling publish start"); + + mediaFileReader.stop(); + webRTCClient.stop(streamId); + } + } + + private IDataChannelObserver createDatachannelObserver() { + return new DefaultDataChannelObserver() { + @Override + public void textMessageReceived(String messageText) { + super.textMessageReceived(messageText); + Toast.makeText(MP3PublishActivity.this, "Message received: " + messageText, Toast.LENGTH_SHORT).show(); + } + }; + } + + private IWebRTCListener createWebRTCListener() { + return new DefaultWebRTCListener() { + @Override + public void onPublishStarted(String streamId) { + super.onPublishStarted(streamId); + broadcastingView.setVisibility(View.VISIBLE); + decrementIdle(); + } + + @Override + public void onPublishFinished(String streamId) { + super.onPublishFinished(streamId); + broadcastingView.setVisibility(View.GONE); + decrementIdle(); + } + }; + } +} diff --git a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/advanced/MP4PublishActivity.java b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/advanced/MP4PublishActivity.java new file mode 100644 index 00000000..65d9c226 --- /dev/null +++ b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/advanced/MP4PublishActivity.java @@ -0,0 +1,148 @@ +package io.antmedia.webrtc_android_sample_app.advanced; + +import android.media.Image; +import android.os.Build; +import android.os.Bundle; +import android.os.SystemClock; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.annotation.RequiresApi; + +import org.webrtc.JavaI420Buffer; +import org.webrtc.SurfaceViewRenderer; +import org.webrtc.VideoFrame; + +import java.nio.ByteBuffer; +import java.util.concurrent.TimeUnit; + +import io.antmedia.webrtc_android_sample_app.R; +import io.antmedia.webrtc_android_sample_app.TestableActivity; +import io.antmedia.webrtc_android_sample_app.basic.SettingsActivity; +import io.antmedia.webrtcandroidframework.core.CustomVideoCapturer; +import io.antmedia.webrtcandroidframework.api.DefaultDataChannelObserver; +import io.antmedia.webrtcandroidframework.api.DefaultWebRTCListener; +import io.antmedia.webrtcandroidframework.api.IDataChannelObserver; +import io.antmedia.webrtcandroidframework.api.IWebRTCClient; +import io.antmedia.webrtcandroidframework.api.IWebRTCListener; +import io.antmedia.webrtcandroidframework.core.MediaFileReader; + +public class MP4PublishActivity extends TestableActivity { + + private View broadcastingView; + private View startStreamingButton; + private String streamId; + private IWebRTCClient webRTCClient; + private MediaFileReader mediaFileReader; + + @RequiresApi(api = Build.VERSION_CODES.N) + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_publish); + + SurfaceViewRenderer fullScreenRenderer = findViewById(R.id.full_screen_renderer); + broadcastingView = findViewById(R.id.broadcasting_text_view); + startStreamingButton = findViewById(R.id.start_streaming_button); + TextView streamIdEditText = findViewById(R.id.stream_id_edittext); + + String serverUrl = sharedPreferences.getString(getString(R.string.serverAddress), SettingsActivity.DEFAULT_WEBSOCKET_URL); + streamId = "streamId" + (int)(Math.random()*9999); + streamIdEditText.setText(streamId); + + webRTCClient = IWebRTCClient.builder() + .setLocalVideoRenderer(fullScreenRenderer) + .setServerUrl(serverUrl) + .setActivity(this) + .setCustomVideoCapturerEnabled(true) + .setWebRTCListener(createWebRTCListener()) + .setDataChannelObserver(createDatachannelObserver()) + .setInitiateBeforeStream(true) + .build(); + + + mediaFileReader = MediaFileReader.fromResources(getResources(), R.raw.test) + .withFrameType(MediaFileReader.FrameType.video) + .withVideoFrameListener(yuvImage -> onYuvImage(yuvImage)); + + mediaFileReader.start(); + + + View startStreamingButton = findViewById(R.id.start_streaming_button); + startStreamingButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + startStopStream(v); + } + }); + } + + private void onYuvImage(Image yuvImage) { + int frameHeight = yuvImage.getHeight(); + int frameWidth = yuvImage.getWidth(); + + ByteBuffer yData = (ByteBuffer) yuvImage.getPlanes()[0].getBuffer().rewind(); + ByteBuffer uData = (ByteBuffer) yuvImage.getPlanes()[1].getBuffer().rewind(); + ByteBuffer vData = (ByteBuffer) yuvImage.getPlanes()[2].getBuffer().rewind(); + + final JavaI420Buffer buffer = JavaI420Buffer.wrap(frameWidth, frameHeight, + yData, yuvImage.getPlanes()[0].getRowStride(), + uData, yuvImage.getPlanes()[1].getRowStride(), + vData, yuvImage.getPlanes()[2].getRowStride(), + null); + + final long captureTimeNs = TimeUnit.MILLISECONDS.toNanos(SystemClock.elapsedRealtime()); + + VideoFrame videoFrame = new VideoFrame(buffer, 0 /* rotation */, captureTimeNs); + ((CustomVideoCapturer)webRTCClient.getVideoCapturer()).writeFrame(videoFrame); + } + + public void startStopStream(View v) { + incrementIdle(); + + if (!webRTCClient.isStreaming(streamId)) { + ((Button) v).setText("Stop"); + Log.i(getClass().getSimpleName(), "Calling publish start"); + + webRTCClient.publish(streamId); + } + else { + ((Button) v).setText("Start"); + Log.i(getClass().getSimpleName(), "Calling publish start"); + + mediaFileReader.stop(); + webRTCClient.stop(streamId); + } + } + + private IDataChannelObserver createDatachannelObserver() { + return new DefaultDataChannelObserver() { + @Override + public void textMessageReceived(String messageText) { + super.textMessageReceived(messageText); + Toast.makeText(MP4PublishActivity.this, "Message received: " + messageText, Toast.LENGTH_SHORT).show(); + } + }; + } + + private IWebRTCListener createWebRTCListener() { + return new DefaultWebRTCListener() { + @Override + public void onPublishStarted(String streamId) { + super.onPublishStarted(streamId); + broadcastingView.setVisibility(View.VISIBLE); + decrementIdle(); + } + + @Override + public void onPublishFinished(String streamId) { + super.onPublishFinished(streamId); + broadcastingView.setVisibility(View.GONE); + decrementIdle(); + } + }; + } +} diff --git a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/advanced/MP4PublishWithSurfaceActivity.java b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/advanced/MP4PublishWithSurfaceActivity.java new file mode 100644 index 00000000..90c3d446 --- /dev/null +++ b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/advanced/MP4PublishWithSurfaceActivity.java @@ -0,0 +1,161 @@ +package io.antmedia.webrtc_android_sample_app.advanced; + +import android.graphics.SurfaceTexture; +import android.media.Image; +import android.os.Build; +import android.os.Bundle; +import android.util.Log; +import android.view.Surface; +import android.view.View; +import android.widget.Button; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.annotation.RequiresApi; + +import org.webrtc.SurfaceViewRenderer; + +import io.antmedia.webrtc_android_sample_app.R; +import io.antmedia.webrtc_android_sample_app.TestableActivity; +import io.antmedia.webrtc_android_sample_app.basic.SettingsActivity; +import io.antmedia.webrtcandroidframework.core.CustomVideoCapturer; +import io.antmedia.webrtcandroidframework.api.DefaultDataChannelObserver; +import io.antmedia.webrtcandroidframework.api.DefaultWebRTCListener; +import io.antmedia.webrtcandroidframework.api.IDataChannelObserver; +import io.antmedia.webrtcandroidframework.api.IWebRTCClient; +import io.antmedia.webrtcandroidframework.api.IWebRTCListener; +import io.antmedia.webrtcandroidframework.core.MediaFileReader; +//import io.github.crow_misia.libyuv.AbgrBuffer; +//import io.github.crow_misia.libyuv.I420Buffer; +//import io.github.crow_misia.libyuv.PlanePrimitive; + +public class MP4PublishWithSurfaceActivity extends TestableActivity { + + + private Surface surface; + + private View broadcastingView; + private View startStreamingButton; + private String streamId; + private IWebRTCClient webRTCClient; + private MediaFileReader mediaFileReader; + + @RequiresApi(api = Build.VERSION_CODES.N) + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_publish); + + SurfaceViewRenderer fullScreenRenderer = findViewById(R.id.full_screen_renderer); + broadcastingView = findViewById(R.id.broadcasting_text_view); + startStreamingButton = findViewById(R.id.start_streaming_button); + TextView streamIdEditText = findViewById(R.id.stream_id_edittext); + + String serverUrl = sharedPreferences.getString(getString(R.string.serverAddress), SettingsActivity.DEFAULT_WEBSOCKET_URL); + streamId = "streamId" + (int)(Math.random()*9999); + streamIdEditText.setText(streamId); + + webRTCClient = IWebRTCClient.builder() + .setLocalVideoRenderer(fullScreenRenderer) + .setServerUrl(serverUrl) + .setActivity(this) + .setCustomVideoCapturerEnabled(true) + .setWebRTCListener(createWebRTCListener()) + .setDataChannelObserver(createDatachannelObserver()) + .build(); + + + mediaFileReader = MediaFileReader.fromResources(getResources(), R.raw.test) + .withFrameType(MediaFileReader.FrameType.video) + .withVideoFrameListener(yuvImage->onYuvImage(yuvImage)); + + View startStreamingButton = findViewById(R.id.start_streaming_button); + startStreamingButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + startStopStream(v); + } + }); + } + + + //We can use surface with rgb so we convert yuv to rgb first + private void onYuvImage(Image image) { + /* + ByteBuffer bufferY = image.getPlanes()[0].getBuffer(); + ByteBuffer bufferU = image.getPlanes()[1].getBuffer(); + ByteBuffer bufferV = image.getPlanes()[2].getBuffer(); + + int strideY = image.getPlanes()[0].getRowStride(); + int strideU = image.getPlanes()[1].getRowStride(); + int strideV = image.getPlanes()[2].getRowStride(); + + PlanePrimitive planeY = new PlanePrimitive(strideY, bufferY); + PlanePrimitive planeU = new PlanePrimitive(strideU, bufferU); + PlanePrimitive planeV = new PlanePrimitive(strideV, bufferV); + + I420Buffer yuvBuffer = I420Buffer.Factory.wrap(planeY, planeU, planeV, 640, 360); + AbgrBuffer rgbBuffer = AbgrBuffer.Factory.allocate(640, 360); + + yuvBuffer.convertTo(rgbBuffer); + + Canvas canvas = surface.lockCanvas(null); + canvas.drawBitmap(rgbBuffer.asBitmap(), 0, 0, null); + surface.unlockCanvasAndPost(canvas); + */ + } + + public void startStopStream(View v) { + incrementIdle(); + + if (!webRTCClient.isStreaming(streamId)) { + ((Button) v).setText("Stop"); + Log.i(getClass().getSimpleName(), "Calling publish start"); + + webRTCClient.publish(streamId); + + if(surface == null) { + SurfaceTexture surfaceTexture = ((CustomVideoCapturer) webRTCClient.getVideoCapturer()) + .getSurfaceTextureHelper().getSurfaceTexture(); + surface = new Surface(surfaceTexture); + } + + mediaFileReader.start(); + } + else { + ((Button) v).setText("Start"); + Log.i(getClass().getSimpleName(), "Calling publish start"); + + mediaFileReader.stop(); + webRTCClient.stop(streamId); + } + } + + private IDataChannelObserver createDatachannelObserver() { + return new DefaultDataChannelObserver() { + @Override + public void textMessageReceived(String messageText) { + super.textMessageReceived(messageText); + Toast.makeText(MP4PublishWithSurfaceActivity.this, "Message received: " + messageText, Toast.LENGTH_SHORT).show(); + } + }; + } + + private IWebRTCListener createWebRTCListener() { + return new DefaultWebRTCListener() { + @Override + public void onPublishStarted(String streamId) { + super.onPublishStarted(streamId); + broadcastingView.setVisibility(View.VISIBLE); + decrementIdle(); + } + + @Override + public void onPublishFinished(String streamId) { + super.onPublishFinished(streamId); + broadcastingView.setVisibility(View.GONE); + decrementIdle(); + } + }; + } +} diff --git a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/advanced/MultiTrackPlayActivity.java b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/advanced/MultiTrackPlayActivity.java new file mode 100644 index 00000000..23f306f2 --- /dev/null +++ b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/advanced/MultiTrackPlayActivity.java @@ -0,0 +1,143 @@ +package io.antmedia.webrtc_android_sample_app.advanced; + +import android.os.Build; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.CheckBox; +import android.widget.EditText; +import android.widget.LinearLayout; +import android.widget.Toast; + +import androidx.annotation.RequiresApi; + +import org.webrtc.SurfaceViewRenderer; +import org.webrtc.VideoTrack; + +import io.antmedia.webrtc_android_sample_app.R; +import io.antmedia.webrtc_android_sample_app.TestableActivity; +import io.antmedia.webrtc_android_sample_app.basic.SettingsActivity; +import io.antmedia.webrtcandroidframework.api.DefaultDataChannelObserver; +import io.antmedia.webrtcandroidframework.api.DefaultWebRTCListener; +import io.antmedia.webrtcandroidframework.api.IDataChannelObserver; +import io.antmedia.webrtcandroidframework.api.IWebRTCClient; +import io.antmedia.webrtcandroidframework.api.IWebRTCListener; + +public class MultiTrackPlayActivity extends TestableActivity { + private IWebRTCClient webRTCClient; + private String[] allTracks; + private EditText streamIdEditText; + private String streamId; + private Button startStreamingButton; + private String selecetedTrack; + private LinearLayout playersLayout; + private LinearLayout checkboxesLayout; + + @RequiresApi(api = Build.VERSION_CODES.M) + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_multitrack); + + playersLayout = findViewById(R.id.players); + checkboxesLayout = findViewById(R.id.checkboxes); + + startStreamingButton = findViewById(R.id.start_streaming_button); + Button tracksButton = findViewById(R.id.tracks_button); + streamIdEditText = findViewById(R.id.stream_id_edittext); + + String serverUrl = sharedPreferences.getString(getString(R.string.serverAddress), SettingsActivity.DEFAULT_WEBSOCKET_URL); + streamIdEditText.setText("streamId"); + + webRTCClient = IWebRTCClient.builder() + .setServerUrl(serverUrl) + .setActivity(this) + .setWebRTCListener(createWebRTCListener()) + .setDataChannelObserver(createDatachannelObserver()) + .setVideoCallEnabled(false) + //.setVideoCodec("H264") + .build(); + + View startStreamingButton = findViewById(R.id.start_streaming_button); + startStreamingButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + startStopStream(v); + } + }); + + tracksButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + webRTCClient.getTrackList(streamIdEditText.getText().toString(), ""); + } + }); + } + + public void startStopStream(View v) { + incrementIdle(); + streamId = streamIdEditText.getText().toString(); + if (!webRTCClient.isStreaming(streamId)) { + ((Button) v).setText("Stop"); + Log.i(getClass().getSimpleName(), "Calling play start"); + + webRTCClient.play(streamId); + } + else { + ((Button) v).setText("Start"); + Log.i(getClass().getSimpleName(), "Calling play start"); + + webRTCClient.stop(streamId); + } + } + + private IDataChannelObserver createDatachannelObserver() { + return new DefaultDataChannelObserver() { + @Override + public void textMessageReceived(String messageText) { + super.textMessageReceived(messageText); + Toast.makeText(MultiTrackPlayActivity.this, "Message received: " + messageText, Toast.LENGTH_SHORT).show(); + } + }; + } + + private IWebRTCListener createWebRTCListener() { + return new DefaultWebRTCListener() { + @Override + public void onPlayStarted(String streamId) { + super.onPlayStarted(streamId); + decrementIdle(); + } + + @Override + public void onPlayFinished(String streamId) { + super.onPlayFinished(streamId); + decrementIdle(); + } + + @Override + public void onNewVideoTrack(VideoTrack track) { + runOnUiThread(() -> { + SurfaceViewRenderer renderer = new SurfaceViewRenderer(MultiTrackPlayActivity.this); + renderer.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, + LinearLayout.LayoutParams.WRAP_CONTENT)); + playersLayout.addView(renderer); + webRTCClient.setRendererForVideoTrack(renderer, track); + }); + } + + @Override + public void onTrackList(String[] tracks) { + for (String track : tracks) { + CheckBox checkBox = new CheckBox(MultiTrackPlayActivity.this); + checkBox.setText(track); + + checkBox.setOnCheckedChangeListener((buttonView, isChecked) -> + webRTCClient.enableTrack(streamId, track, isChecked)); + checkboxesLayout.addView(checkBox); + } + } + }; + } +} diff --git a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/USBCameraActivity.java b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/advanced/USBCameraActivity.java similarity index 72% rename from webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/USBCameraActivity.java rename to webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/advanced/USBCameraActivity.java index 5fcdcad3..adf95dd0 100644 --- a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/USBCameraActivity.java +++ b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/advanced/USBCameraActivity.java @@ -1,13 +1,6 @@ -package io.antmedia.webrtc_android_sample_app; - -import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_CAPTURETOTEXTURE_ENABLED; -import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_DATA_CHANNEL_ENABLED; -import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_VIDEO_BITRATE; -import static io.antmedia.webrtcandroidframework.apprtc.CallActivity.EXTRA_VIDEO_FPS; +package io.antmedia.webrtc_android_sample_app.advanced; import android.Manifest; -import android.content.Context; -import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.graphics.SurfaceTexture; import android.hardware.usb.UsbDevice; @@ -16,76 +9,62 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.Message; -import android.preference.PreferenceManager; import android.util.Log; import android.view.Surface; import android.view.View; import android.widget.Button; -import android.widget.EditText; -import android.widget.LinearLayout; import android.widget.RadioGroup; -import android.widget.Spinner; import android.widget.TextView; import android.widget.Toast; + import androidx.annotation.NonNull; +import androidx.annotation.RequiresApi; import androidx.core.app.ActivityCompat; -import androidx.test.espresso.idling.CountingIdlingResource; import com.hsj.camera.Size; import com.hsj.camera.USBMonitor; import com.hsj.camera.UVCCamera; -import org.apache.commons.lang3.RandomStringUtils; import org.webrtc.SurfaceViewRenderer; import java.util.List; -import java.util.Timer; -import io.antmedia.webrtcandroidframework.CustomVideoCapturer; -import io.antmedia.webrtcandroidframework.IWebRTCClient; -import io.antmedia.webrtcandroidframework.WebRTCClient; -import io.antmedia.webrtcandroidframework.apprtc.CallActivity; +import io.antmedia.webrtc_android_sample_app.R; +import io.antmedia.webrtc_android_sample_app.TestableActivity; +import io.antmedia.webrtc_android_sample_app.basic.SettingsActivity; +import io.antmedia.webrtcandroidframework.core.CustomVideoCapturer; +import io.antmedia.webrtcandroidframework.api.DefaultDataChannelObserver; +import io.antmedia.webrtcandroidframework.api.DefaultWebRTCListener; +import io.antmedia.webrtcandroidframework.api.IDataChannelObserver; +import io.antmedia.webrtcandroidframework.api.IWebRTCClient; +import io.antmedia.webrtcandroidframework.api.IWebRTCListener; -public final class USBCameraActivity extends AbstractSampleSDKActivity implements Handler.Callback { +public final class USBCameraActivity extends TestableActivity implements Handler.Callback { private static final String TAG = "USBCameraActivity"; - //TODO Set your usb camera display width and height private static int PREVIEW_WIDTH = 640; private static int PREVIEW_HEIGHT = 480; - private static final int CAMERA_CREATE = 1; private static final int CAMERA_PREVIEW = 2; private static final int CAMERA_START = 3; private static final int CAMERA_STOP = 4; private static final int CAMERA_DESTROY = 5; - - private Context context; private USBMonitor mUSBMonitor; private Handler cameraHandler; private HandlerThread cameraThread; - - private boolean enableDataChannel = true; - - private WebRTCClient webRTCClient; - - private Button startStreamingButton; - private String operationName = ""; - - private SurfaceViewRenderer cameraViewRenderer; - private SurfaceViewRenderer pipViewRenderer; - - private TextView broadcastingView; - private EditText streamIdEditText; private Surface surface; - private RadioGroup bg; + private View broadcastingView; + private View startStreamingButton; + private String streamId; + private IWebRTCClient webRTCClient; + @RequiresApi(api = Build.VERSION_CODES.M) @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_cam); - this.context = getApplicationContext(); this.cameraThread = new HandlerThread("thread_uvc_camera"); this.cameraThread.start(); this.cameraHandler = new Handler(cameraThread.getLooper(), this); @@ -94,68 +73,58 @@ protected void onCreate(Bundle savedInstanceState) { createUsbMonitor(); } - cameraViewRenderer = findViewById(R.id.camera_view_renderer); - pipViewRenderer = findViewById(R.id.pip_view_renderer); - + SurfaceViewRenderer fullScreenRenderer = findViewById(R.id.full_screen_renderer); broadcastingView = findViewById(R.id.broadcasting_text_view); - - streamIdEditText = findViewById(R.id.stream_id_edittext); - streamIdEditText.setText("streamId" + RandomStringUtils.randomNumeric(5)); - startStreamingButton = findViewById(R.id.start_streaming_button); + TextView streamIdEditText = findViewById(R.id.stream_id_edittext); - SharedPreferences sharedPreferences = - PreferenceManager.getDefaultSharedPreferences(this /* Activity context */); String serverUrl = sharedPreferences.getString(getString(R.string.serverAddress), SettingsActivity.DEFAULT_WEBSOCKET_URL); - - startStreamingButton.setText("Start Publishing"); - operationName = "Publishing"; - - this.getIntent().putExtra(EXTRA_VIDEO_FPS, 30); - this.getIntent().putExtra(EXTRA_VIDEO_BITRATE, 1500); - this.getIntent().putExtra(EXTRA_CAPTURETOTEXTURE_ENABLED, true); - this.getIntent().putExtra(EXTRA_DATA_CHANNEL_ENABLED, enableDataChannel); + streamId = "streamId" + (int)(Math.random()*9999); + streamIdEditText.setText(streamId); + + webRTCClient = IWebRTCClient.builder() + .setLocalVideoRenderer(fullScreenRenderer) + .setServerUrl(serverUrl) + .setActivity(this) + .setWebRTCListener(createWebRTCListener()) + .setDataChannelObserver(createDatachannelObserver()) + .setInitiateBeforeStream(true) + .build(); bg = findViewById(R.id.rbGroup); bg.check(R.id.rbFront); bg.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() { @Override public void onCheckedChanged(RadioGroup group, int checkedId) { - String newSource = ""; + IWebRTCClient.StreamSource newSource; if(checkedId == R.id.rbFront) { - newSource = WebRTCClient.SOURCE_FRONT; - getIntent().putExtra(CallActivity.EXTRA_SCREENCAPTURE, false); - webRTCClient.setCustomCapturerEnabled(false); + newSource = IWebRTCClient.StreamSource.FRONT_CAMERA; + webRTCClient.getConfig().customVideoCapturerEnabled = false; cameraHandler.obtainMessage(CAMERA_STOP).sendToTarget(); webRTCClient.changeVideoSource(newSource); - } else if(checkedId == R.id.rbRear) { - newSource = WebRTCClient.SOURCE_REAR; - getIntent().putExtra(CallActivity.EXTRA_SCREENCAPTURE, false); - webRTCClient.setCustomCapturerEnabled(false); + newSource = IWebRTCClient.StreamSource.REAR_CAMERA; + webRTCClient.getConfig().customVideoCapturerEnabled = false; cameraHandler.obtainMessage(CAMERA_STOP).sendToTarget(); webRTCClient.changeVideoSource(newSource); - } else if(checkedId == R.id.rbUsb) { - newSource = WebRTCClient.SOURCE_CUSTOM; - getIntent().putExtra(CallActivity.EXTRA_SCREENCAPTURE, false); - webRTCClient.setCustomCapturerEnabled(true); + newSource = IWebRTCClient.StreamSource.CUSTOM; + webRTCClient.getConfig().customVideoCapturerEnabled = true; webRTCClient.changeVideoSource(newSource); startUSBCamera(); } - } }); - webRTCClient = new WebRTCClient( this,this); - - String tokenId = "tokenId"; - webRTCClient.setVideoRenderers(pipViewRenderer, cameraViewRenderer); - - webRTCClient.init(serverUrl, streamIdEditText.getText().toString(), IWebRTCClient.MODE_PUBLISH, tokenId, this.getIntent()); - webRTCClient.setDataChannelObserver(this); + View startStreamingButton = findViewById(R.id.start_streaming_button); + startStreamingButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + startStopStream(v); + } + }); } private void startUSBCamera() { @@ -166,25 +135,52 @@ private void startUSBCamera() { cameraHandler.obtainMessage(CAMERA_START).sendToTarget(); } - public void startStreaming(View v) { - //update stream id if it is changed - webRTCClient.setStreamId(streamIdEditText.getText().toString()); + public void startStopStream(View v) { incrementIdle(); - if (!webRTCClient.isStreaming()) { - ((Button) v).setText("Stop " + operationName); - Log.i(getClass().getSimpleName(), "Calling startStream"); + if (!webRTCClient.isStreaming(streamId)) { + ((Button) v).setText("Stop"); + Log.i(getClass().getSimpleName(), "Calling publish start"); - webRTCClient.startStream(); + webRTCClient.publish(streamId); } else { - ((Button)startStreamingButton).setText("Start " + operationName); - Log.i(getClass().getSimpleName(), "Calling stopStream"); - webRTCClient.stopStream(); + ((Button) v).setText("Start"); + Log.i(getClass().getSimpleName(), "Calling publish start"); + + webRTCClient.stop(streamId); cameraHandler.obtainMessage(CAMERA_STOP).sendToTarget(); } + } + private IDataChannelObserver createDatachannelObserver() { + return new DefaultDataChannelObserver() { + @Override + public void textMessageReceived(String messageText) { + super.textMessageReceived(messageText); + Toast.makeText(USBCameraActivity.this, "Message received: " + messageText, Toast.LENGTH_SHORT).show(); + } + }; } + private IWebRTCListener createWebRTCListener() { + return new DefaultWebRTCListener() { + @Override + public void onPublishStarted(String streamId) { + super.onPublishStarted(streamId); + broadcastingView.setVisibility(View.VISIBLE); + decrementIdle(); + } + + @Override + public void onPublishFinished(String streamId) { + super.onPublishFinished(streamId); + broadcastingView.setVisibility(View.GONE); + decrementIdle(); + } + }; + } + + @Override protected void onDestroy() { if (mUSBMonitor != null) { @@ -218,7 +214,7 @@ public void onRequestPermissionsResult(int requestCode, } private void createUsbMonitor() { - this.mUSBMonitor = new USBMonitor(context, dcl); + this.mUSBMonitor = new USBMonitor(this, dcl); this.mUSBMonitor.register(); } diff --git a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/basic/ConferenceActivity.java b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/basic/ConferenceActivity.java new file mode 100644 index 00000000..f4f9c707 --- /dev/null +++ b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/basic/ConferenceActivity.java @@ -0,0 +1,133 @@ +package io.antmedia.webrtc_android_sample_app.basic; + +import android.os.Build; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.Switch; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.annotation.RequiresApi; + +import org.webrtc.SurfaceViewRenderer; + +import io.antmedia.webrtc_android_sample_app.R; +import io.antmedia.webrtc_android_sample_app.TestableActivity; +import io.antmedia.webrtcandroidframework.api.DefaultConferenceWebRTCListener; +import io.antmedia.webrtcandroidframework.api.DefaultDataChannelObserver; +import io.antmedia.webrtcandroidframework.api.IDataChannelObserver; +import io.antmedia.webrtcandroidframework.api.IWebRTCClient; + +public class ConferenceActivity extends TestableActivity { + private TextView broadcastingView; + private View joinButton; + private View streamInfoListSpinner; + private String streamId; + private IWebRTCClient webRTCClient; + private String roomId; + + private Button audioButton; + private Button videoButton; + + + + + @RequiresApi(api = Build.VERSION_CODES.M) + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_conference); + + SurfaceViewRenderer publisherRenderer = findViewById(R.id.publish_view_renderer); + SurfaceViewRenderer player1Renderer = findViewById(R.id.play_view_renderer1); + SurfaceViewRenderer player2Renderer = findViewById(R.id.play_view_renderer2); + SurfaceViewRenderer player3Renderer = findViewById(R.id.play_view_renderer3); + SurfaceViewRenderer player4Renderer = findViewById(R.id.play_view_renderer4); + + broadcastingView = findViewById(R.id.broadcasting_text_view); + joinButton = findViewById(R.id.join_conference_button); + + audioButton = findViewById(R.id.control_audio_button); + videoButton = findViewById(R.id.control_video_button); + + String serverUrl = sharedPreferences.getString(getString(R.string.serverAddress), SettingsActivity.DEFAULT_WEBSOCKET_URL); + roomId = sharedPreferences.getString(getString(R.string.roomId), SettingsActivity.DEFAULT_ROOM_NAME); + streamId = "streamId" + (int)(Math.random()*9999); + + DefaultConferenceWebRTCListener defaultConferenceListener = createWebRTCListener(roomId, streamId); + + Switch playOnlySwitch = findViewById(R.id.play_only_switch); + playOnlySwitch.setOnCheckedChangeListener((compoundButton, b) -> { + defaultConferenceListener.setPlayOnly(b); + publisherRenderer.setVisibility(b ? View.GONE : View.VISIBLE); + }); + + webRTCClient = IWebRTCClient.builder() + .addRemoteVideoRenderer(player1Renderer, player2Renderer, player3Renderer, player4Renderer) + .setLocalVideoRenderer(publisherRenderer) + .setServerUrl(serverUrl) + .setActivity(this) + .setWebRTCListener(defaultConferenceListener) + .setDataChannelObserver(createDatachannelObserver()) + .build(); + + joinButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + joinLeaveRoom(v); + } + }); + } + + public void joinLeaveRoom(View v) { + incrementIdle(); + if (!webRTCClient.isStreaming(streamId)) { + ((Button) v).setText("Leave"); + Log.i(getClass().getSimpleName(), "Calling join"); + + webRTCClient.joinToConferenceRoom(roomId, streamId); + } + else { + ((Button) v).setText("Join"); + Log.i(getClass().getSimpleName(), "Calling leave"); + + webRTCClient.leaveFromConference(roomId); + } + } + + private IDataChannelObserver createDatachannelObserver() { + return new DefaultDataChannelObserver() { + @Override + public void textMessageReceived(String messageText) { + super.textMessageReceived(messageText); + Toast.makeText(ConferenceActivity.this, "Message received: " + messageText, Toast.LENGTH_SHORT).show(); + } + }; + } + + private DefaultConferenceWebRTCListener createWebRTCListener(String roomId, String streamId) { + return new DefaultConferenceWebRTCListener(roomId, streamId); + } + + public void controlAudio(View view) { + if (webRTCClient.getConfig().audioCallEnabled) { + webRTCClient.setAudioEnabled(false); + audioButton.setText("Enable Audio"); + } else { + webRTCClient.setAudioEnabled(true); + audioButton.setText("Disable Audio"); + } + } + + public void controlVideo(View view) { + if (webRTCClient.getConfig().videoCallEnabled) { + webRTCClient.setVideoEnabled(false); + videoButton.setText("Enable Video"); + } else { + webRTCClient.setVideoEnabled(true); + videoButton.setText("Disable Video"); + } + } +} diff --git a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/basic/DataChannelOnlyActivity.java b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/basic/DataChannelOnlyActivity.java new file mode 100644 index 00000000..831d144b --- /dev/null +++ b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/basic/DataChannelOnlyActivity.java @@ -0,0 +1,156 @@ +package io.antmedia.webrtc_android_sample_app.basic; + +import android.os.Build; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.EditText; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.annotation.RequiresApi; + +import org.webrtc.DataChannel; + +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; + +import io.antmedia.webrtc_android_sample_app.R; +import io.antmedia.webrtc_android_sample_app.TestableActivity; +import io.antmedia.webrtcandroidframework.api.DefaultDataChannelObserver; +import io.antmedia.webrtcandroidframework.api.DefaultWebRTCListener; +import io.antmedia.webrtcandroidframework.api.IDataChannelObserver; +import io.antmedia.webrtcandroidframework.api.IWebRTCClient; +import io.antmedia.webrtcandroidframework.api.IWebRTCListener; +import io.antmedia.webrtcandroidframework.core.WebRTCClient; + +/** + * This Activity is for demonstrating the data channel usage without video and audio + * Steps: + * set dataChannelOnly parameter of WebRTCClient + * start WebRTC Cilent with play mode + * if no stream exist is called start it in publish mode + */ +public class DataChannelOnlyActivity extends TestableActivity { + private WebRTCClient webRTCClient; + private View broadcastingView; + private Button startStreamingButton; + private String operationName = ""; + private EditText messageInput; + private TextView messages; + private View broadcastView; + private String streamId; + private TextView streamIdEditText; + + @RequiresApi(api = Build.VERSION_CODES.M) + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_data_channel_only); + + broadcastingView = findViewById(R.id.broadcasting_text_view); + startStreamingButton = findViewById(R.id.start_streaming_button); + streamIdEditText = findViewById(R.id.stream_id_edittext); + + setContentView(R.layout.activity_data_channel_only); + + startStreamingButton = findViewById(R.id.start_streaming_button); + messageInput = findViewById(R.id.message_text_input); + messages = findViewById(R.id.messages_view); + + broadcastView = findViewById(R.id.broadcasting_text_view); + streamIdEditText = findViewById(R.id.stream_id_edittext); + streamIdEditText.setText("streamId" + (int)(Math.random()*9999)); + + String serverUrl = sharedPreferences.getString(getString(R.string.serverAddress), SettingsActivity.DEFAULT_WEBSOCKET_URL); + streamId = "streamId" + (int)(Math.random()*9999); + streamIdEditText.setText(streamId); + + webRTCClient = IWebRTCClient.builder() + .setServerUrl(serverUrl) + .setActivity(this) + .setWebRTCListener(createWebRTCListener()) + .setDataChannelObserver(createDatachannelObserver()) + .setVideoCallEnabled(false) + .setAudioCallEnabled(false) + .build(); + + View startStreamingButton = findViewById(R.id.start_streaming_button); + startStreamingButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + startStopStream(v); + } + }); + } + + public void startStopStream(View v) { + incrementIdle(); + streamId = streamIdEditText.getText().toString(); + if (!webRTCClient.isStreaming(streamId)) { + ((Button) v).setText("Stop"); + Log.i(getClass().getSimpleName(), "Calling publish start"); + + webRTCClient.play(streamId); + } + else { + ((Button) v).setText("Start"); + Log.i(getClass().getSimpleName(), "Calling publish start"); + + webRTCClient.stop(streamId); + } + } + + public void sendMessage(View v) { + String messageToSend = messageInput.getText().toString(); + messageInput.setText(""); + + final ByteBuffer buffer = ByteBuffer.wrap(messageToSend.getBytes(StandardCharsets.UTF_8)); + DataChannel.Buffer buf= new DataChannel.Buffer(buffer,false); + webRTCClient.sendMessageViaDataChannel(streamId, buf); + } + + private IDataChannelObserver createDatachannelObserver() { + return new DefaultDataChannelObserver() { + @Override + public void onMessage(DataChannel.Buffer buffer, String dataChannelLabel) { + String messageText = toTextMessage(buffer); + Toast.makeText(DataChannelOnlyActivity.this, "Message received: " + messageText, Toast.LENGTH_SHORT).show(); + messages.append("received:"+messageText+"\n"); + } + + @Override + public void onMessageSent(DataChannel.Buffer buffer, boolean successful) { + if (successful) { + String messageText = toTextMessage(buffer); + Log.i(DefaultDataChannelObserver.class.getSimpleName(), "Message is sent"); + Toast.makeText(DataChannelOnlyActivity.this, "Message is sent", Toast.LENGTH_SHORT).show(); + messages.append("sent:"+messageText+"\n"); + } else { + Toast.makeText(DataChannelOnlyActivity.this, "Could not send the text message", Toast.LENGTH_LONG).show(); + Log.e(DefaultDataChannelObserver.class.getSimpleName(),"Could not send the text message"); + } + } + }; + } + + private IWebRTCListener createWebRTCListener() { + return new DefaultWebRTCListener() { + @Override + public void onPlayStarted(String streamId) { + super.onPlayStarted(streamId); + broadcastingView.setVisibility(View.VISIBLE); + decrementIdle(); + } + + @Override + public void onPlayFinished(String streamId) { + super.onPlayFinished(streamId); + broadcastingView.setVisibility(View.GONE); + decrementIdle(); + } + }; + } + +} diff --git a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/MediaProjectionService.java b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/basic/MediaProjectionService.java similarity index 97% rename from webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/MediaProjectionService.java rename to webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/basic/MediaProjectionService.java index abdf29e0..656a0f64 100644 --- a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/MediaProjectionService.java +++ b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/basic/MediaProjectionService.java @@ -1,4 +1,4 @@ -package io.antmedia.webrtc_android_sample_app; +package io.antmedia.webrtc_android_sample_app.basic; import android.app.Activity; import android.app.Notification; @@ -14,10 +14,11 @@ import android.media.projection.MediaProjectionManager; import android.os.Build; import android.os.IBinder; -import android.util.Log; import androidx.annotation.RequiresApi; +import io.antmedia.webrtc_android_sample_app.R; + public class MediaProjectionService extends Service { private static MediaProjectionServiceListener listener; diff --git a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/basic/PeerActivity.java b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/basic/PeerActivity.java new file mode 100644 index 00000000..877ed804 --- /dev/null +++ b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/basic/PeerActivity.java @@ -0,0 +1,148 @@ +package io.antmedia.webrtc_android_sample_app.basic; + +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.os.Build; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.EditText; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.annotation.RequiresApi; + +import org.webrtc.DataChannel; +import org.webrtc.SurfaceViewRenderer; + +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; + +import io.antmedia.webrtc_android_sample_app.R; +import io.antmedia.webrtc_android_sample_app.TestableActivity; +import io.antmedia.webrtcandroidframework.api.DefaultDataChannelObserver; +import io.antmedia.webrtcandroidframework.api.DefaultWebRTCListener; +import io.antmedia.webrtcandroidframework.api.IDataChannelObserver; +import io.antmedia.webrtcandroidframework.api.IWebRTCClient; +import io.antmedia.webrtcandroidframework.api.IWebRTCListener; + +public class PeerActivity extends TestableActivity { + private View broadcastingView; + private View startStreamingButton; + private String streamId; + private IWebRTCClient webRTCClient; + private TextView streamIdEditText; + + @RequiresApi(api = Build.VERSION_CODES.M) + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_peer); + + SurfaceViewRenderer fullScreenRenderer = findViewById(R.id.full_screen_renderer); + SurfaceViewRenderer pipRenderer = findViewById(R.id.pip_view_renderer); + + broadcastingView = findViewById(R.id.broadcasting_text_view); + startStreamingButton = findViewById(R.id.start_streaming_button); + streamIdEditText = findViewById(R.id.stream_id_edittext); + + String serverUrl = sharedPreferences.getString(getString(R.string.serverAddress), SettingsActivity.DEFAULT_WEBSOCKET_URL); + streamIdEditText.setText("streamId"); + + webRTCClient = IWebRTCClient.builder() + .setLocalVideoRenderer(pipRenderer) + .addRemoteVideoRenderer(fullScreenRenderer) + .setServerUrl(serverUrl) + .setActivity(this) + .setWebRTCListener(createWebRTCListener()) + .setDataChannelObserver(createDatachannelObserver()) + .build(); + + View startStreamingButton = findViewById(R.id.start_streaming_button); + startStreamingButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + startStopStream(v); + } + }); + } + + public void startStopStream(View v) { + incrementIdle(); + streamId = streamIdEditText.getText().toString(); + if (!webRTCClient.isStreaming(streamId)) { + ((Button) v).setText("Stop"); + Log.i(getClass().getSimpleName(), "Calling play start"); + + webRTCClient.join(streamId); + } + else { + ((Button) v).setText("Start"); + Log.i(getClass().getSimpleName(), "Calling play start"); + + webRTCClient.stop(streamId); + } + } + + private IDataChannelObserver createDatachannelObserver() { + return new DefaultDataChannelObserver() { + @Override + public void textMessageReceived(String messageText) { + super.textMessageReceived(messageText); + Toast.makeText(PeerActivity.this, "Message received: " + messageText, Toast.LENGTH_SHORT).show(); + } + }; + } + + private IWebRTCListener createWebRTCListener() { + return new DefaultWebRTCListener() { + @Override + public void onPlayStarted(String streamId) { + super.onPlayStarted(streamId); + broadcastingView.setVisibility(View.VISIBLE); + decrementIdle(); + } + + @Override + public void onPlayFinished(String streamId) { + super.onPlayFinished(streamId); + broadcastingView.setVisibility(View.GONE); + decrementIdle(); + } + }; + } + + public void sendTextMessage(String messageToSend) { + final ByteBuffer buffer = ByteBuffer.wrap(messageToSend.getBytes(StandardCharsets.UTF_8)); + DataChannel.Buffer buf = new DataChannel.Buffer(buffer, false); + webRTCClient.sendMessageViaDataChannel(streamId, buf); + } + + public void showSendDataChannelMessageDialog(View view) { + if (webRTCClient != null && webRTCClient.isDataChannelEnabled()) { + // create an alert builder + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle("Send Message via Data Channel"); + // set the custom layout + final View customLayout = getLayoutInflater().inflate(R.layout.send_message_data_channel, null); + builder.setView(customLayout); + // add a button + builder.setPositiveButton("OK", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + // send data from the AlertDialog to the Activity + EditText editText = customLayout.findViewById(R.id.message_text_input); + sendTextMessage(editText.getText().toString()); + } + }); + // create and show the alert dialog + AlertDialog dialog = builder.create(); + dialog.show(); + } + else { + Toast.makeText(this, R.string.data_channel_not_available, Toast.LENGTH_LONG).show(); + } + } + +} diff --git a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/basic/PlayActivity.java b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/basic/PlayActivity.java new file mode 100644 index 00000000..ad429fdd --- /dev/null +++ b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/basic/PlayActivity.java @@ -0,0 +1,147 @@ +package io.antmedia.webrtc_android_sample_app.basic; + +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.os.Build; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.EditText; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.annotation.RequiresApi; + +import org.webrtc.DataChannel; +import org.webrtc.SurfaceViewRenderer; + +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; + +import io.antmedia.webrtc_android_sample_app.R; +import io.antmedia.webrtc_android_sample_app.TestableActivity; +import io.antmedia.webrtcandroidframework.api.DefaultDataChannelObserver; +import io.antmedia.webrtcandroidframework.api.DefaultWebRTCListener; +import io.antmedia.webrtcandroidframework.api.IDataChannelObserver; +import io.antmedia.webrtcandroidframework.api.IWebRTCClient; +import io.antmedia.webrtcandroidframework.api.IWebRTCListener; + +public class PlayActivity extends TestableActivity { + private View broadcastingView; + private View startStreamingButton; + private View streamInfoListSpinner; + private String streamId; + private IWebRTCClient webRTCClient; + private TextView streamIdEditText; + + @RequiresApi(api = Build.VERSION_CODES.M) + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_play); + + SurfaceViewRenderer fullScreenRenderer = findViewById(R.id.full_screen_renderer); + broadcastingView = findViewById(R.id.broadcasting_text_view); + startStreamingButton = findViewById(R.id.start_streaming_button); + streamInfoListSpinner = findViewById(R.id.stream_info_list); + streamIdEditText = findViewById(R.id.stream_id_edittext); + + String serverUrl = sharedPreferences.getString(getString(R.string.serverAddress), SettingsActivity.DEFAULT_WEBSOCKET_URL); + streamIdEditText.setText("streamId"); + + webRTCClient = IWebRTCClient.builder() + .addRemoteVideoRenderer(fullScreenRenderer) + .setServerUrl(serverUrl) + .setActivity(this) + .setWebRTCListener(createWebRTCListener()) + .setDataChannelObserver(createDatachannelObserver()) + .build(); + + View startStreamingButton = findViewById(R.id.start_streaming_button); + startStreamingButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + startStopStream(v); + } + }); + } + + public void startStopStream(View v) { + incrementIdle(); + streamId = streamIdEditText.getText().toString(); + if (!webRTCClient.isStreaming(streamId)) { + ((Button) v).setText("Stop"); + Log.i(getClass().getSimpleName(), "Calling play start"); + + webRTCClient.play(streamId); + } + else { + ((Button) v).setText("Start"); + Log.i(getClass().getSimpleName(), "Calling play start"); + + webRTCClient.stop(streamId); + } + } + + private IDataChannelObserver createDatachannelObserver() { + return new DefaultDataChannelObserver() { + @Override + public void textMessageReceived(String messageText) { + super.textMessageReceived(messageText); + Toast.makeText(PlayActivity.this, "Message received: " + messageText, Toast.LENGTH_SHORT).show(); + } + }; + } + + private IWebRTCListener createWebRTCListener() { + return new DefaultWebRTCListener() { + @Override + public void onPlayStarted(String streamId) { + super.onPlayStarted(streamId); + broadcastingView.setVisibility(View.VISIBLE); + decrementIdle(); + } + + @Override + public void onPlayFinished(String streamId) { + super.onPlayFinished(streamId); + broadcastingView.setVisibility(View.GONE); + decrementIdle(); + } + }; + } + + public void sendTextMessage(String messageToSend) { + final ByteBuffer buffer = ByteBuffer.wrap(messageToSend.getBytes(StandardCharsets.UTF_8)); + DataChannel.Buffer buf = new DataChannel.Buffer(buffer, false); + webRTCClient.sendMessageViaDataChannel(streamId, buf); + } + + public void showSendDataChannelMessageDialog(View view) { + if (webRTCClient != null && webRTCClient.isDataChannelEnabled()) { + // create an alert builder + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle("Send Message via Data Channel"); + // set the custom layout + final View customLayout = getLayoutInflater().inflate(R.layout.send_message_data_channel, null); + builder.setView(customLayout); + // add a button + builder.setPositiveButton("OK", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + // send data from the AlertDialog to the Activity + EditText editText = customLayout.findViewById(R.id.message_text_input); + sendTextMessage(editText.getText().toString()); + } + }); + // create and show the alert dialog + AlertDialog dialog = builder.create(); + dialog.show(); + } + else { + Toast.makeText(this, R.string.data_channel_not_available, Toast.LENGTH_LONG).show(); + } + } + +} diff --git a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/basic/PublishActivity.java b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/basic/PublishActivity.java new file mode 100644 index 00000000..5ce68ec9 --- /dev/null +++ b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/basic/PublishActivity.java @@ -0,0 +1,144 @@ +package io.antmedia.webrtc_android_sample_app.basic; + +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.os.Build; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.EditText; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.annotation.RequiresApi; + +import org.webrtc.DataChannel; +import org.webrtc.SurfaceViewRenderer; + +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; + +import io.antmedia.webrtc_android_sample_app.R; +import io.antmedia.webrtc_android_sample_app.TestableActivity; +import io.antmedia.webrtcandroidframework.api.DefaultDataChannelObserver; +import io.antmedia.webrtcandroidframework.api.DefaultWebRTCListener; +import io.antmedia.webrtcandroidframework.api.IDataChannelObserver; +import io.antmedia.webrtcandroidframework.api.IWebRTCClient; +import io.antmedia.webrtcandroidframework.api.IWebRTCListener; + +public class PublishActivity extends TestableActivity { + private View broadcastingView; + private View startStreamingButton; + private String streamId; + private IWebRTCClient webRTCClient; + + @RequiresApi(api = Build.VERSION_CODES.M) + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_publish); + + SurfaceViewRenderer fullScreenRenderer = findViewById(R.id.full_screen_renderer); + broadcastingView = findViewById(R.id.broadcasting_text_view); + startStreamingButton = findViewById(R.id.start_streaming_button); + TextView streamIdEditText = findViewById(R.id.stream_id_edittext); + + String serverUrl = sharedPreferences.getString(getString(R.string.serverAddress), SettingsActivity.DEFAULT_WEBSOCKET_URL); + streamId = "streamId" + (int)(Math.random()*9999); + streamIdEditText.setText(streamId); + + webRTCClient = IWebRTCClient.builder() + .setLocalVideoRenderer(fullScreenRenderer) + .setServerUrl(serverUrl) + .setActivity(this) + .setWebRTCListener(createWebRTCListener()) + .setDataChannelObserver(createDatachannelObserver()) + .build(); + + View startStreamingButton = findViewById(R.id.start_streaming_button); + startStreamingButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + startStopStream(v); + } + }); + } + + public void startStopStream(View v) { + incrementIdle(); + if (!webRTCClient.isStreaming(streamId)) { + ((Button) v).setText("Stop"); + Log.i(getClass().getSimpleName(), "Calling publish start"); + + webRTCClient.publish(streamId); + } + else { + ((Button) v).setText("Start"); + Log.i(getClass().getSimpleName(), "Calling publish start"); + + webRTCClient.stop(streamId); + } + } + + private IDataChannelObserver createDatachannelObserver() { + return new DefaultDataChannelObserver() { + @Override + public void textMessageReceived(String messageText) { + super.textMessageReceived(messageText); + Toast.makeText(PublishActivity.this, "Message received: " + messageText, Toast.LENGTH_SHORT).show(); + } + }; + } + + private IWebRTCListener createWebRTCListener() { + return new DefaultWebRTCListener() { + @Override + public void onPublishStarted(String streamId) { + super.onPublishStarted(streamId); + broadcastingView.setVisibility(View.VISIBLE); + decrementIdle(); + } + + @Override + public void onPublishFinished(String streamId) { + super.onPublishFinished(streamId); + broadcastingView.setVisibility(View.GONE); + decrementIdle(); + } + }; + } + + public void sendTextMessage(String messageToSend) { + final ByteBuffer buffer = ByteBuffer.wrap(messageToSend.getBytes(StandardCharsets.UTF_8)); + DataChannel.Buffer buf = new DataChannel.Buffer(buffer, false); + webRTCClient.sendMessageViaDataChannel(streamId, buf); + } + + public void showSendDataChannelMessageDialog(View view) { + if (webRTCClient != null && webRTCClient.isDataChannelEnabled()) { + // create an alert builder + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle("Send Message via Data Channel"); + // set the custom layout + final View customLayout = getLayoutInflater().inflate(R.layout.send_message_data_channel, null); + builder.setView(customLayout); + // add a button + builder.setPositiveButton("OK", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + // send data from the AlertDialog to the Activity + EditText editText = customLayout.findViewById(R.id.message_text_input); + sendTextMessage(editText.getText().toString()); + } + }); + // create and show the alert dialog + AlertDialog dialog = builder.create(); + dialog.show(); + } + else { + Toast.makeText(this, R.string.data_channel_not_available, Toast.LENGTH_LONG).show(); + } + } + +} diff --git a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/basic/ScreenCaptureActivity.java b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/basic/ScreenCaptureActivity.java new file mode 100644 index 00000000..151b69ac --- /dev/null +++ b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/basic/ScreenCaptureActivity.java @@ -0,0 +1,178 @@ +package io.antmedia.webrtc_android_sample_app.basic; + +import static io.antmedia.webrtc_android_sample_app.basic.MediaProjectionService.EXTRA_MEDIA_PROJECTION_DATA; + +import android.annotation.TargetApi; +import android.content.Intent; +import android.os.Build; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.RadioGroup; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.annotation.RequiresApi; + +import org.webrtc.SurfaceViewRenderer; + +import io.antmedia.webrtc_android_sample_app.R; +import io.antmedia.webrtc_android_sample_app.TestableActivity; +import io.antmedia.webrtcandroidframework.api.DefaultDataChannelObserver; +import io.antmedia.webrtcandroidframework.api.DefaultWebRTCListener; +import io.antmedia.webrtcandroidframework.api.IDataChannelObserver; +import io.antmedia.webrtcandroidframework.api.IWebRTCClient; +import io.antmedia.webrtcandroidframework.api.IWebRTCListener; +import io.antmedia.webrtcandroidframework.core.WebRTCClientConfig; + +import android.media.projection.MediaProjection; +import android.media.projection.MediaProjectionManager; + +public class ScreenCaptureActivity extends TestableActivity { + private View broadcastingView; + private View startStreamingButton; + private String streamId; + private IWebRTCClient webRTCClient; + private RadioGroup bg; + + @RequiresApi(api = Build.VERSION_CODES.M) + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_screenshare); + + SurfaceViewRenderer fullScreenRenderer = findViewById(R.id.full_screen_renderer); + broadcastingView = findViewById(R.id.broadcasting_text_view); + startStreamingButton = findViewById(R.id.start_streaming_button); + TextView streamIdEditText = findViewById(R.id.stream_id_edittext); + + String serverUrl = sharedPreferences.getString(getString(R.string.serverAddress), SettingsActivity.DEFAULT_WEBSOCKET_URL); + streamId = "streamId" + (int)(Math.random()*9999); + streamIdEditText.setText(streamId); + + bg = findViewById(R.id.rbGroup); + bg.check(R.id.rbFront); + bg.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(RadioGroup group, int checkedId) { + IWebRTCClient.StreamSource newSource = IWebRTCClient.StreamSource.FRONT_CAMERA; + if(checkedId == R.id.rbScreen) { + requestScreenCapture(); + return; + } + else if(checkedId == R.id.rbFront) { + newSource = IWebRTCClient.StreamSource.FRONT_CAMERA; + } + else if(checkedId == R.id.rbRear) { + newSource = IWebRTCClient.StreamSource.REAR_CAMERA; + } + idlingResource.increment(); + webRTCClient.changeVideoSource(newSource); + decrementIdle(); + } + }); + + webRTCClient = IWebRTCClient.builder() + .setLocalVideoRenderer(fullScreenRenderer) + .setServerUrl(serverUrl) + .setActivity(this) + .setWebRTCListener(createWebRTCListener()) + .setDataChannelObserver(createDatachannelObserver()) + .build(); + + View startStreamingButton = findViewById(R.id.start_streaming_button); + startStreamingButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + startStopStream(v); + } + }); + } + + public void startStopStream(View v) { + incrementIdle(); + if (!webRTCClient.isStreaming(streamId)) { + ((Button) v).setText("Stop"); + Log.i(getClass().getSimpleName(), "Calling publish start"); + + webRTCClient.publish(streamId); + } + else { + ((Button) v).setText("Start"); + Log.i(getClass().getSimpleName(), "Calling publish start"); + + webRTCClient.stop(streamId); + } + } + + private IDataChannelObserver createDatachannelObserver() { + return new DefaultDataChannelObserver() { + @Override + public void textMessageReceived(String messageText) { + super.textMessageReceived(messageText); + Toast.makeText(ScreenCaptureActivity.this, "Message received: " + messageText, Toast.LENGTH_SHORT).show(); + } + }; + } + + private IWebRTCListener createWebRTCListener() { + return new DefaultWebRTCListener() { + @Override + public void onPublishStarted(String streamId) { + super.onPublishStarted(streamId); + broadcastingView.setVisibility(View.VISIBLE); + decrementIdle(); + } + + @Override + public void onPublishFinished(String streamId) { + super.onPublishFinished(streamId); + broadcastingView.setVisibility(View.GONE); + decrementIdle(); + } + }; + } + + + + public static final int CAPTURE_PERMISSION_REQUEST_CODE = 1234; + public MediaProjectionManager mediaProjectionManager; + + @TargetApi(21) + public void requestScreenCapture() { + mediaProjectionManager = (MediaProjectionManager) getSystemService(MEDIA_PROJECTION_SERVICE); + startActivityForResult(mediaProjectionManager.createScreenCaptureIntent(), CAPTURE_PERMISSION_REQUEST_CODE); + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) + { + if (requestCode != CAPTURE_PERMISSION_REQUEST_CODE) + return; + + // If the device version is v29 or higher, screen sharing will work service due to media projection policy. + // Otherwise media projection will work without service + if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q){ + + MediaProjectionService.setListener(mediaProjection -> { + startScreenCapturer(mediaProjection); + }); + + Intent serviceIntent = new Intent(this, MediaProjectionService.class); + serviceIntent.putExtra(EXTRA_MEDIA_PROJECTION_DATA, data); + startForegroundService(serviceIntent); + } + else{ + MediaProjection mediaProjection = mediaProjectionManager.getMediaProjection(resultCode, data); + startScreenCapturer(mediaProjection); + } + } + + private void startScreenCapturer(MediaProjection mediaProjection) { + WebRTCClientConfig config = webRTCClient.getConfig(); + config.mediaProjection = mediaProjection; + webRTCClient.changeVideoSource(IWebRTCClient.StreamSource.SCREEN); + decrementIdle(); + } +} diff --git a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/SettingsActivity.java b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/basic/SettingsActivity.java similarity index 96% rename from webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/SettingsActivity.java rename to webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/basic/SettingsActivity.java index 0dfa701b..3c97e648 100644 --- a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/SettingsActivity.java +++ b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/basic/SettingsActivity.java @@ -1,4 +1,4 @@ -package io.antmedia.webrtc_android_sample_app; +package io.antmedia.webrtc_android_sample_app.basic; import android.app.Activity; import android.content.Context; @@ -13,6 +13,8 @@ import androidx.preference.PreferenceManager; +import io.antmedia.webrtc_android_sample_app.R; + public class SettingsActivity extends Activity { public static final String DEFAULT_WEBSOCKET_URL = "wss://test.antmedia.io:5443/LiveApp/websocket"; diff --git a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/chat/ImageMessage.java b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/basic/chat/ImageMessage.java similarity index 91% rename from webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/chat/ImageMessage.java rename to webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/basic/chat/ImageMessage.java index 271e7a9e..e6b361d8 100644 --- a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/chat/ImageMessage.java +++ b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/basic/chat/ImageMessage.java @@ -1,4 +1,4 @@ -package io.antmedia.webrtc_android_sample_app.chat; +package io.antmedia.webrtc_android_sample_app.basic.chat; import android.graphics.Bitmap; import android.util.Log; diff --git a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/chat/Message.java b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/basic/chat/Message.java similarity index 97% rename from webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/chat/Message.java rename to webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/basic/chat/Message.java index 0413b1f2..749a9435 100644 --- a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/chat/Message.java +++ b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/basic/chat/Message.java @@ -1,4 +1,4 @@ -package io.antmedia.webrtc_android_sample_app.chat; +package io.antmedia.webrtc_android_sample_app.basic.chat; import android.util.Log; diff --git a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/chat/MessageAdapter.java b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/basic/chat/MessageAdapter.java similarity index 98% rename from webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/chat/MessageAdapter.java rename to webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/basic/chat/MessageAdapter.java index c89cee9e..c5fc1e28 100644 --- a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/chat/MessageAdapter.java +++ b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/basic/chat/MessageAdapter.java @@ -1,4 +1,4 @@ -package io.antmedia.webrtc_android_sample_app.chat; +package io.antmedia.webrtc_android_sample_app.basic.chat; import android.app.Activity; import android.content.Context; diff --git a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/chat/SettingsActivity.java b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/basic/chat/SettingsActivity.java similarity index 94% rename from webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/chat/SettingsActivity.java rename to webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/basic/chat/SettingsActivity.java index 2fe334c9..718fcfbf 100644 --- a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/chat/SettingsActivity.java +++ b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/basic/chat/SettingsActivity.java @@ -1,4 +1,4 @@ -package io.antmedia.webrtc_android_sample_app.chat; +package io.antmedia.webrtc_android_sample_app.basic.chat; import android.os.Bundle; diff --git a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/chat/TextMessage.java b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/basic/chat/TextMessage.java similarity index 90% rename from webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/chat/TextMessage.java rename to webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/basic/chat/TextMessage.java index 17b763c5..a72de6c7 100644 --- a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/chat/TextMessage.java +++ b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/basic/chat/TextMessage.java @@ -1,4 +1,4 @@ -package io.antmedia.webrtc_android_sample_app.chat; +package io.antmedia.webrtc_android_sample_app.basic.chat; import android.util.Log; diff --git a/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/minimal/SimplePublishActivity.java b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/minimal/SimplePublishActivity.java new file mode 100644 index 00000000..25cb5eac --- /dev/null +++ b/webrtc-android-sample-app/src/main/java/io/antmedia/webrtc_android_sample_app/minimal/SimplePublishActivity.java @@ -0,0 +1,27 @@ +package io.antmedia.webrtc_android_sample_app.minimal; + +import android.app.Activity; +import android.os.Bundle; + +import org.webrtc.SurfaceViewRenderer; + +import io.antmedia.webrtc_android_sample_app.R; +import io.antmedia.webrtcandroidframework.api.IWebRTCClient; + +public class SimplePublishActivity extends Activity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_simple_publish); + + SurfaceViewRenderer fullScreenRenderer = findViewById(R.id.full_screen_renderer); + + IWebRTCClient webRTCClient = IWebRTCClient.builder() + .setActivity(this) + .setLocalVideoRenderer(fullScreenRenderer) + .setServerUrl("wss://test.antmedia.io:5443/LiveApp/websocket") + .build(); + + webRTCClient.publish("stream1"); + } +} diff --git a/webrtc-android-sample-app/src/main/res/layout/activity_cam.xml b/webrtc-android-sample-app/src/main/res/layout/activity_cam.xml index 84989194..23b30f75 100644 --- a/webrtc-android-sample-app/src/main/res/layout/activity_cam.xml +++ b/webrtc-android-sample-app/src/main/res/layout/activity_cam.xml @@ -13,19 +13,10 @@ android:layout_alignParentStart="true"> - - + tools:context=".basic.ConferenceActivity"> + + + + + + android:layout_height="match_parent" > + + \ No newline at end of file diff --git a/webrtc-android-sample-app/src/main/res/layout/activity_data_channel_only.xml b/webrtc-android-sample-app/src/main/res/layout/activity_data_channel_only.xml index f5cf0802..91519d20 100644 --- a/webrtc-android-sample-app/src/main/res/layout/activity_data_channel_only.xml +++ b/webrtc-android-sample-app/src/main/res/layout/activity_data_channel_only.xml @@ -4,7 +4,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" - tools:context=".DataChannelOnlyActivity"> + tools:context=".basic.DataChannelOnlyActivity"> - - - - - \ No newline at end of file diff --git a/webrtc-android-sample-app/src/main/res/layout/activity_main.xml b/webrtc-android-sample-app/src/main/res/layout/activity_main.xml index 1407d91d..81b36617 100644 --- a/webrtc-android-sample-app/src/main/res/layout/activity_main.xml +++ b/webrtc-android-sample-app/src/main/res/layout/activity_main.xml @@ -1,79 +1,23 @@ - - tools:context=".MainActivity"> - - - - - - - - - - - - - - -