From f26c38444b95f10273cb491e02ab31a00883f92e Mon Sep 17 00:00:00 2001 From: Mario Danic Date: Wed, 8 Nov 2017 20:06:33 +0100 Subject: [PATCH] Some work Signed-off-by: Mario Danic --- .../talk/activities/CallActivity.java | 274 ++++++++++++++++-- 1 file changed, 251 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java b/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java index 5a83d3ec1..6f7b3aed8 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java +++ b/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java @@ -20,21 +20,39 @@ package com.nextcloud.talk.activities; +import android.Manifest; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; -import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; -import com.bluelinelabs.conductor.Conductor; -import com.bluelinelabs.conductor.Router; -import com.bluelinelabs.conductor.RouterTransaction; -import com.bluelinelabs.conductor.changehandler.SimpleSwapChangeHandler; +import com.gun0912.tedpermission.PermissionListener; +import com.gun0912.tedpermission.TedPermission; import com.nextcloud.talk.R; import com.nextcloud.talk.application.NextcloudTalkApplication; -import com.nextcloud.talk.controllers.CallController; -import com.nextcloud.talk.utils.bundle.BundleBuilder; +import com.nextcloud.talk.webrtc.MagicPeerConnectionObserver; +import com.nextcloud.talk.webrtc.MagicSdpObserver; + +import org.webrtc.AudioSource; +import org.webrtc.AudioTrack; +import org.webrtc.Camera1Enumerator; +import org.webrtc.CameraEnumerator; +import org.webrtc.IceCandidate; +import org.webrtc.Logging; +import org.webrtc.MediaConstraints; +import org.webrtc.MediaStream; +import org.webrtc.PeerConnection; +import org.webrtc.PeerConnectionFactory; +import org.webrtc.SessionDescription; +import org.webrtc.SurfaceViewRenderer; +import org.webrtc.VideoCapturer; +import org.webrtc.VideoRenderer; +import org.webrtc.VideoSource; +import org.webrtc.VideoTrack; + +import java.util.ArrayList; +import java.util.List; import autodagger.AutoInjector; import butterknife.BindView; @@ -44,14 +62,29 @@ import butterknife.ButterKnife; public class CallActivity extends AppCompatActivity { private static final String TAG = "CallActivity"; - @BindView(R.id.controller_container) - ViewGroup container; + @BindView(R.id.pip_video_view) + SurfaceViewRenderer pipVideoView; - private Router router; + @BindView(R.id.fullscreen_video_view) + SurfaceViewRenderer fullScreenVideoView; private String roomToken; private String userDisplayName; + PeerConnectionFactory peerConnectionFactory; + MediaConstraints audioConstraints; + MediaConstraints videoConstraints; + MediaConstraints sdpConstraints; + VideoSource videoSource; + VideoTrack localVideoTrack; + AudioSource audioSource; + AudioTrack localAudioTrack; + + VideoRenderer localRenderer; + VideoRenderer remoteRenderer; + + PeerConnection localPeer, remotePeer; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -70,26 +103,221 @@ public class CallActivity extends AppCompatActivity { roomToken = getIntent().getExtras().getString("roomToken", ""); userDisplayName = getIntent().getExtras().getString("userDisplayName", ""); - router = Conductor.attachRouter(this, container, savedInstanceState); - if (!router.hasRootController()) { - BundleBuilder bundleBuilder = new BundleBuilder(new Bundle()); - bundleBuilder.putString("roomToken", roomToken); - bundleBuilder.putString("userDisplayName", userDisplayName); + PermissionListener permissionlistener = new PermissionListener() { + @Override + public void onPermissionGranted() { + start(); + call(); + } - router.setRoot(RouterTransaction.with(new CallController(bundleBuilder.build())) - .popChangeHandler(new SimpleSwapChangeHandler()) - .pushChangeHandler((new SimpleSwapChangeHandler()))); + @Override + public void onPermissionDenied(ArrayList deniedPermissions) { + } + }; + + TedPermission.with(this) + .setPermissionListener(permissionlistener) + .setDeniedMessage("If you reject permission,you can not use this service\n\nPlease turn on permissions at [Setting] > [Permission]") + .setPermissions(android.Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO, + Manifest.permission.MODIFY_AUDIO_SETTINGS, Manifest.permission.ACCESS_NETWORK_STATE, + Manifest.permission.ACCESS_WIFI_STATE, Manifest.permission.INTERNET) + .check(); + } + + + + + private VideoCapturer createVideoCapturer() { + VideoCapturer videoCapturer; + videoCapturer = createCameraCapturer(new Camera1Enumerator(false)); + return videoCapturer; + } + + private VideoCapturer createCameraCapturer(CameraEnumerator enumerator) { + final String[] deviceNames = enumerator.getDeviceNames(); + + // First, try to find front facing camera + Logging.d(TAG, "Looking for front facing cameras."); + for (String deviceName : deviceNames) { + if (enumerator.isFrontFacing(deviceName)) { + Logging.d(TAG, "Creating front facing camera capturer."); + VideoCapturer videoCapturer = enumerator.createCapturer(deviceName, null); + + if (videoCapturer != null) { + return videoCapturer; + } + } + } + + // Front facing camera not found, try something else + Logging.d(TAG, "Looking for other cameras."); + for (String deviceName : deviceNames) { + if (!enumerator.isFrontFacing(deviceName)) { + Logging.d(TAG, "Creating other camera capturer."); + VideoCapturer videoCapturer = enumerator.createCapturer(deviceName, null); + + if (videoCapturer != null) { + return videoCapturer; + } + } + } + + return null; + } + + public void start() { + //Initialize PeerConnectionFactory globals. + //Params are context, initAudio,initVideo and videoCodecHwAcceleration + PeerConnectionFactory.initializeAndroidGlobals(this, true, true, + false); + + //Create a new PeerConnectionFactory instance. + PeerConnectionFactory.Options options = new PeerConnectionFactory.Options(); + peerConnectionFactory = new PeerConnectionFactory(options); + + //Now create a VideoCapturer instance. Callback methods are there if you want to do something! Duh! + VideoCapturer videoCapturerAndroid = createVideoCapturer(); + + //Create MediaConstraints - Will be useful for specifying video and audio constraints. + audioConstraints = new MediaConstraints(); + videoConstraints = new MediaConstraints(); + + //Create a VideoSource instance + videoSource = peerConnectionFactory.createVideoSource(videoCapturerAndroid); + localVideoTrack = peerConnectionFactory.createVideoTrack("100", videoSource); + + //create an AudioSource instance + audioSource = peerConnectionFactory.createAudioSource(audioConstraints); + localAudioTrack = peerConnectionFactory.createAudioTrack("101", audioSource); + + //create a videoRenderer based on SurfaceViewRenderer instance + localRenderer = new VideoRenderer(pipVideoView); + // And finally, with our VideoRenderer ready, we + // can add our renderer to the VideoTrack. + localVideoTrack.addRenderer(localRenderer); + + } + + + private void call() { + //we already have video and audio tracks. Now create peerconnections + List iceServers = new ArrayList<>(); + iceServers.add(new PeerConnection.IceServer("stun:stun.nextcloud.com:443")); + + //create sdpConstraints + sdpConstraints = new MediaConstraints(); + sdpConstraints.mandatory.add(new MediaConstraints.KeyValuePair("offerToReceiveAudio", "true")); + sdpConstraints.mandatory.add(new MediaConstraints.KeyValuePair("offerToReceiveVideo", "true")); + sdpConstraints.optional.add(new MediaConstraints.KeyValuePair("internalSctpDataChannels", "true")); + sdpConstraints.optional.add(new MediaConstraints.KeyValuePair("DtlsSrtpKeyAgreement", "true")); + + //creating localPeer + localPeer = peerConnectionFactory.createPeerConnection(iceServers, sdpConstraints, + new MagicPeerConnectionObserver() { + @Override + public void onIceCandidate(IceCandidate iceCandidate) { + super.onIceCandidate(iceCandidate); + onIceCandidateReceived(localPeer, iceCandidate); + } + }); + + //creating remotePeer + remotePeer = peerConnectionFactory.createPeerConnection(iceServers, sdpConstraints, + new MagicPeerConnectionObserver() { + + @Override + public void onIceCandidate(IceCandidate iceCandidate) { + super.onIceCandidate(iceCandidate); + onIceCandidateReceived(remotePeer, iceCandidate); + } + + public void onAddStream(MediaStream mediaStream) { + super.onAddStream(mediaStream); + gotRemoteStream(mediaStream); + } + + @Override + public void onIceGatheringChange(PeerConnection.IceGatheringState iceGatheringState) { + super.onIceGatheringChange(iceGatheringState); + + } + }); + + //creating local mediastream + MediaStream stream = peerConnectionFactory.createLocalMediaStream("102"); + stream.addTrack(localAudioTrack); + stream.addTrack(localVideoTrack); + localPeer.addStream(stream); + + //creating Offer + localPeer.createOffer(new MagicSdpObserver(){ + @Override + public void onCreateSuccess(SessionDescription sessionDescription) { + //we have localOffer. Set it as local desc for localpeer and remote desc for remote peer. + //try to create answer from the remote peer. + super.onCreateSuccess(sessionDescription); + localPeer.setLocalDescription(new MagicSdpObserver(), sessionDescription); + remotePeer.setRemoteDescription(new MagicSdpObserver(), sessionDescription); + remotePeer.createAnswer(new MagicSdpObserver() { + @Override + public void onCreateSuccess(SessionDescription sessionDescription) { + //remote answer generated. Now set it as local desc for remote peer and remote desc for local peer. + super.onCreateSuccess(sessionDescription); + remotePeer.setLocalDescription(new MagicSdpObserver(), sessionDescription); + localPeer.setRemoteDescription(new MagicSdpObserver(), sessionDescription); + + } + },new MediaConstraints()); + } + },sdpConstraints); + } + + + private void hangup() { + if (localPeer != null) { + localPeer.close(); + localPeer = null; + } + + if (remotePeer != null) { + remotePeer.close(); + remotePeer = null; + } + } + + private void gotRemoteStream(MediaStream stream) { + //we have remote video stream. add to the renderer. + final VideoTrack videoTrack = stream.videoTracks.getFirst(); + AudioTrack audioTrack = stream.audioTracks.getFirst(); + runOnUiThread(new Runnable() { + @Override + public void run() { + try { + remoteRenderer = new VideoRenderer(fullScreenVideoView); + videoTrack.addRenderer(remoteRenderer); + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + + } + + public void onIceCandidateReceived(PeerConnection peer, IceCandidate iceCandidate) { + //we have received ice candidate. We can set it to the other peer. + if (peer == localPeer) { + remotePeer.addIceCandidate(iceCandidate); + } else { + localPeer.addIceCandidate(iceCandidate); } } @Override - public void onBackPressed() { - if (!router.handleBack()) { - super.onBackPressed(); - } + public void onDestroy() { + super.onDestroy(); + hangup(); } - private static int getSystemUiVisibility() { int flags = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN; flags |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;