Skip to content

Instantly share code, notes, and snippets.

@vivek1794
Last active February 4, 2018 17:35
Show Gist options
  • Save vivek1794/5ce080ac6fb2662a05a9f89990c6301e to your computer and use it in GitHub Desktop.
Save vivek1794/5ce080ac6fb2662a05a9f89990c6301e to your computer and use it in GitHub Desktop.
public class MainActivity extends AppCompatActivity implements View.OnClickListener, SignallingClient.SignalingInterface {
PeerConnectionFactory peerConnectionFactory;
MediaConstraints audioConstraints;
MediaConstraints videoConstraints;
MediaConstraints sdpConstraints;
VideoSource videoSource;
VideoTrack localVideoTrack;
AudioSource audioSource;
AudioTrack localAudioTrack;
SurfaceViewRenderer localVideoView;
SurfaceViewRenderer remoteVideoView;
VideoRenderer localRenderer;
VideoRenderer remoteRenderer;
Button hangup;
PeerConnection localPeer;
List<IceServer> iceServers;
EglBase rootEglBase;
boolean gotUserMedia;
List<PeerConnection.IceServer> peerIceServers = new ArrayList<>();
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initViews();
initVideos();
getIceServers();
SignallingClient.getInstance().init(this);
start();
}
private void initViews() {
hangup = findViewById(R.id.end_call);
localVideoView = findViewById(R.id.local_gl_surface_view);
remoteVideoView = findViewById(R.id.remote_gl_surface_view);
hangup.setOnClickListener(this);
}
private void initVideos() {
rootEglBase = EglBase.create();
localVideoView.init(rootEglBase.getEglBaseContext(), null);
remoteVideoView.init(rootEglBase.getEglBaseContext(), null);
localVideoView.setZOrderMediaOverlay(true);
remoteVideoView.setZOrderMediaOverlay(true);
}
private void getIceServers() {
//get Ice servers using xirsys
Utils.getInstance().getRetrofitInstance().getIceCandidates().enqueue(new Callback<TurnServerPojo>() {
@Override
public void onResponse(@NonNull Call<TurnServerPojo> call, @NonNull Response<TurnServerPojo> response) {
TurnServerPojo body = response.body();
if (body != null) {
iceServers = body.iceServerList.iceServers;
}
for (IceServer iceServer : iceServers) {
if (iceServer.credential == null) {
PeerConnection.IceServer peerIceServer = PeerConnection.IceServer.builder(iceServer.url).createIceServer();
peerIceServers.add(peerIceServer);
} else {
PeerConnection.IceServer peerIceServer = PeerConnection.IceServer.builder(iceServer.url)
.setUsername(iceServer.username)
.setPassword(iceServer.credential)
.createIceServer();
peerIceServers.add(peerIceServer);
}
}
Log.d("onApiResponse", "IceServers\n" + iceServers.toString());
}
@Override
public void onFailure(@NonNull Call<TurnServerPojo> call, @NonNull Throwable t) {
t.printStackTrace();
}
});
}
public void start() {
//Initialize PeerConnectionFactory globals.
PeerConnectionFactory.InitializationOptions initializationOptions =
PeerConnectionFactory.InitializationOptions.builder(this)
.setEnableVideoHwAcceleration(true)
.createInitializationOptions();
PeerConnectionFactory.initialize(initializationOptions);
//Create a new PeerConnectionFactory instance - using Hardware encoder and decoder.
PeerConnectionFactory.Options options = new PeerConnectionFactory.Options();
DefaultVideoEncoderFactory defaultVideoEncoderFactory = new DefaultVideoEncoderFactory(
rootEglBase.getEglBaseContext(), /* enableIntelVp8Encoder */true, /* enableH264HighProfile */true);
DefaultVideoDecoderFactory defaultVideoDecoderFactory = new DefaultVideoDecoderFactory(rootEglBase.getEglBaseContext());
peerConnectionFactory = new PeerConnectionFactory(options, defaultVideoEncoderFactory, defaultVideoDecoderFactory);
//Now create a VideoCapturer instance.
VideoCapturer videoCapturerAndroid;
videoCapturerAndroid = createCameraCapturer(new Camera1Enumerator(false));
//Create MediaConstraints - Will be useful for specifying video and audio constraints.
audioConstraints = new MediaConstraints();
videoConstraints = new MediaConstraints();
//Create a VideoSource instance
if (videoCapturerAndroid != null) {
videoSource = peerConnectionFactory.createVideoSource(videoCapturerAndroid);
}
localVideoTrack = peerConnectionFactory.createVideoTrack("100", videoSource);
//create an AudioSource instance
audioSource = peerConnectionFactory.createAudioSource(audioConstraints);
localAudioTrack = peerConnectionFactory.createAudioTrack("101", audioSource);
if (videoCapturerAndroid != null) {
videoCapturerAndroid.startCapture(1024, 720, 30);
}
localVideoView.setVisibility(View.VISIBLE);
//create a videoRenderer based on SurfaceViewRenderer instance
localRenderer = new VideoRenderer(localVideoView);
// And finally, with our VideoRenderer ready, we
// can add our renderer to the VideoTrack.
localVideoTrack.addRenderer(localRenderer);
localVideoView.setMirror(true);
remoteVideoView.setMirror(true);
gotUserMedia = true;
if (SignallingClient.getInstance().isInitiator) {
onTryToStart();
}
}
/**
* This method will be called directly by the app when it is the initiator and has got the local media
* or when the remote peer sends a message through socket that it is ready to transmit AV data
*/
@Override
public void onTryToStart() {
runOnUiThread(() -> {
if (!SignallingClient.getInstance().isStarted && localVideoTrack != null && SignallingClient.getInstance().isChannelReady) {
createPeerConnection();
SignallingClient.getInstance().isStarted = true;
if (SignallingClient.getInstance().isInitiator) {
doCall();
}
}
});
}
/**
* Creating the local peerconnection instance
*/
private void createPeerConnection() {
PeerConnection.RTCConfiguration rtcConfig =
new PeerConnection.RTCConfiguration(peerIceServers);
// TCP candidates are only useful when connecting to a server that supports
// ICE-TCP.
rtcConfig.tcpCandidatePolicy = PeerConnection.TcpCandidatePolicy.DISABLED;
rtcConfig.bundlePolicy = PeerConnection.BundlePolicy.MAXBUNDLE;
rtcConfig.rtcpMuxPolicy = PeerConnection.RtcpMuxPolicy.REQUIRE;
rtcConfig.continualGatheringPolicy = PeerConnection.ContinualGatheringPolicy.GATHER_CONTINUALLY;
// Use ECDSA encryption.
rtcConfig.keyType = PeerConnection.KeyType.ECDSA;
localPeer = peerConnectionFactory.createPeerConnection(rtcConfig, new CustomPeerConnectionObserver("localPeerCreation") {
@Override
public void onIceCandidate(IceCandidate iceCandidate) {
super.onIceCandidate(iceCandidate);
onIceCandidateReceived(iceCandidate);
}
@Override
public void onAddStream(MediaStream mediaStream) {
showToast("Received Remote stream");
super.onAddStream(mediaStream);
gotRemoteStream(mediaStream);
}
});
addStreamToLocalPeer();
}
/**
* Adding the stream to the localpeer
*/
private void addStreamToLocalPeer() {
//creating local mediastream
MediaStream stream = peerConnectionFactory.createLocalMediaStream("102");
stream.addTrack(localAudioTrack);
stream.addTrack(localVideoTrack);
localPeer.addStream(stream);
}
/**
* This method is called when the app is initiator - We generate the offer and send it over through socket
* to remote peer
*/
private void doCall() {
localPeer.createOffer(new CustomSdpObserver("localCreateOffer") {
@Override
public void onCreateSuccess(SessionDescription sessionDescription) {
super.onCreateSuccess(sessionDescription);
localPeer.setLocalDescription(new CustomSdpObserver("localSetLocalDesc"), sessionDescription);
Log.d("onCreateSuccess", "SignallingClient emit ");
SignallingClient.getInstance().emitMessage(sessionDescription);
}
}, sdpConstraints);
}
/**
* Received remote peer's media stream. we will get the first video track and render it
*/
private void gotRemoteStream(MediaStream stream) {
//we have remote video stream. add to the renderer.
final VideoTrack videoTrack = stream.videoTracks.get(0);
runOnUiThread(() -> {
try {
remoteRenderer = new VideoRenderer(remoteVideoView);
remoteVideoView.setVisibility(View.VISIBLE);
videoTrack.addRenderer(remoteRenderer);
} catch (Exception e) {
e.printStackTrace();
}
});
}
/**
* Received local ice candidate. Send it to remote peer through signalling for negotiation
*/
public void onIceCandidateReceived(IceCandidate iceCandidate) {
//we have received ice candidate. We can set it to the other peer.
SignallingClient.getInstance().emitIceCandidate(iceCandidate);
}
/**
* SignallingCallback - called when the room is created - i.e. you are the initiator
*/
@Override
public void onCreatedRoom() {
showToast("You created the room " + gotUserMedia);
if (gotUserMedia) {
SignallingClient.getInstance().emitMessage("got user media");
}
}
/**
* SignallingCallback - called when you join the room - you are a participant
*/
@Override
public void onJoinedRoom() {
showToast("You joined the room " + gotUserMedia);
if (gotUserMedia) {
SignallingClient.getInstance().emitMessage("got user media");
}
}
@Override
public void onNewPeerJoined() {
showToast("Remote Peer Joined");
}
@Override
public void onRemoteHangUp(String msg) {
showToast("Remote Peer hungup");
runOnUiThread(this::hangup);
}
/**
* SignallingCallback - Called when remote peer sends offer
*/
@Override
public void onOfferReceived(final JSONObject data) {
showToast("Received Offer");
runOnUiThread(() -> {
if (!SignallingClient.getInstance().isInitiator && !SignallingClient.getInstance().isStarted) {
onTryToStart();
}
try {
localPeer.setRemoteDescription(new CustomSdpObserver("localSetRemote"), new SessionDescription(SessionDescription.Type.OFFER, data.getString("sdp")));
doAnswer();
updateVideoViews(true);
} catch (JSONException e) {
e.printStackTrace();
}
});
}
private void doAnswer() {
localPeer.createAnswer(new CustomSdpObserver("localCreateAns") {
@Override
public void onCreateSuccess(SessionDescription sessionDescription) {
super.onCreateSuccess(sessionDescription);
localPeer.setLocalDescription(new CustomSdpObserver("localSetLocal"), sessionDescription);
SignallingClient.getInstance().emitMessage(sessionDescription);
}
}, new MediaConstraints());
}
/**
* SignallingCallback - Called when remote peer sends answer to your offer
*/
@Override
public void onAnswerReceived(JSONObject data) {
showToast("Received Answer");
try {
localPeer.setRemoteDescription(new CustomSdpObserver("localSetRemote"), new SessionDescription(SessionDescription.Type.fromCanonicalForm(data.getString("type").toLowerCase()), data.getString("sdp")));
updateVideoViews(true);
} catch (JSONException e) {
e.printStackTrace();
}
}
/**
* Remote IceCandidate received
*/
@Override
public void onIceCandidateReceived(JSONObject data) {
try {
localPeer.addIceCandidate(new IceCandidate(data.getString("id"), data.getInt("label"), data.getString("candidate")));
} catch (JSONException e) {
e.printStackTrace();
}
}
private void updateVideoViews(final boolean remoteVisible) {
runOnUiThread(() -> {
ViewGroup.LayoutParams params = localVideoView.getLayoutParams();
if (remoteVisible) {
params.height = dpToPx(100);
params.width = dpToPx(100);
} else {
params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
}
localVideoView.setLayoutParams(params);
});
}
/**
* Closing up - normal hangup and app destroye
*/
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.end_call: {
hangup();
break;
}
}
}
private void hangup() {
try {
localPeer.close();
localPeer = null;
SignallingClient.getInstance().close();
updateVideoViews(false);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
protected void onDestroy() {
SignallingClient.getInstance().close();
super.onDestroy();
}
/**
* Util Methods
*/
public int dpToPx(int dp) {
DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
return Math.round(dp * (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT));
}
public void showToast(final String msg) {
runOnUiThread(() -> Toast.makeText(MainActivity.this, msg, Toast.LENGTH_SHORT).show());
}
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;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment