org.webrtc.PeerConnection

Here are the examples of the java api org.webrtc.PeerConnection taken from open source projects. By voting up you can indicate which examples are most useful and appropriate.

65 Examples 7

19 Source : PeerConnectionWrapper.java
with GNU General Public License v3.0
from XecureIT

public clreplaced PeerConnectionWrapper {

    private static final String TAG = PeerConnectionWrapper.clreplaced.getSimpleName();

    private static final PeerConnection.IceServer STUN_SERVER = new PeerConnection.IceServer("stun:stun1.l.google.com:19302");

    @NonNull
    private final PeerConnection peerConnection;

    @NonNull
    private final AudioTrack audioTrack;

    @NonNull
    private final AudioSource audioSource;

    @Nullable
    private final VideoCapturer videoCapturer;

    @Nullable
    private final VideoSource videoSource;

    @Nullable
    private final VideoTrack videoTrack;

    public PeerConnectionWrapper(@NonNull Context context, @NonNull PeerConnectionFactory factory, @NonNull PeerConnection.Observer observer, @NonNull VideoRenderer.Callbacks localRenderer, @NonNull List<PeerConnection.IceServer> turnServers, boolean hideIp) {
        List<PeerConnection.IceServer> iceServers = new LinkedList<>();
        iceServers.add(STUN_SERVER);
        iceServers.addAll(turnServers);
        MediaConstraints constraints = new MediaConstraints();
        MediaConstraints audioConstraints = new MediaConstraints();
        PeerConnection.RTCConfiguration configuration = new PeerConnection.RTCConfiguration(iceServers);
        configuration.bundlePolicy = PeerConnection.BundlePolicy.MAXBUNDLE;
        configuration.rtcpMuxPolicy = PeerConnection.RtcpMuxPolicy.REQUIRE;
        if (hideIp) {
            configuration.iceTransportsType = PeerConnection.IceTransportsType.RELAY;
        }
        constraints.optional.add(new MediaConstraints.KeyValuePair("DtlsSrtpKeyAgreement", "true"));
        audioConstraints.optional.add(new MediaConstraints.KeyValuePair("DtlsSrtpKeyAgreement", "true"));
        this.peerConnection = factory.createPeerConnection(configuration, constraints, observer);
        this.videoCapturer = createVideoCapturer(context);
        MediaStream mediaStream = factory.createLocalMediaStream("ARDAMS");
        this.audioSource = factory.createAudioSource(audioConstraints);
        this.audioTrack = factory.createAudioTrack("ARDAMSa0", audioSource);
        this.audioTrack.setEnabled(false);
        mediaStream.addTrack(audioTrack);
        if (videoCapturer != null) {
            this.videoSource = factory.createVideoSource(videoCapturer);
            this.videoTrack = factory.createVideoTrack("ARDAMSv0", videoSource);
            this.videoTrack.addRenderer(new VideoRenderer(localRenderer));
            this.videoTrack.setEnabled(false);
            mediaStream.addTrack(videoTrack);
        } else {
            this.videoSource = null;
            this.videoTrack = null;
        }
        this.peerConnection.addStream(mediaStream);
    }

    public void setVideoEnabled(boolean enabled) {
        if (this.videoTrack != null) {
            this.videoTrack.setEnabled(enabled);
        }
        if (this.videoCapturer != null) {
            try {
                if (enabled)
                    this.videoCapturer.startCapture(1280, 720, 30);
                else
                    this.videoCapturer.stopCapture();
            } catch (InterruptedException e) {
                Log.w(TAG, e);
            }
        }
    }

    public void setAudioEnabled(boolean enabled) {
        this.audioTrack.setEnabled(enabled);
    }

    public DataChannel createDataChannel(String name) {
        DataChannel.Init dataChannelConfiguration = new DataChannel.Init();
        dataChannelConfiguration.ordered = true;
        return this.peerConnection.createDataChannel(name, dataChannelConfiguration);
    }

    public SessionDescription createOffer(MediaConstraints mediaConstraints) throws PeerConnectionException {
        final SettableFuture<SessionDescription> future = new SettableFuture<>();
        peerConnection.createOffer(new SdpObserver() {

            @Override
            public void onCreateSuccess(SessionDescription sdp) {
                future.set(sdp);
            }

            @Override
            public void onCreateFailure(String error) {
                future.setException(new PeerConnectionException(error));
            }

            @Override
            public void onSetSuccess() {
                throw new replacedertionError();
            }

            @Override
            public void onSetFailure(String error) {
                throw new replacedertionError();
            }
        }, mediaConstraints);
        try {
            return correctSessionDescription(future.get());
        } catch (InterruptedException e) {
            throw new replacedertionError(e);
        } catch (ExecutionException e) {
            throw new PeerConnectionException(e);
        }
    }

    public SessionDescription createAnswer(MediaConstraints mediaConstraints) throws PeerConnectionException {
        final SettableFuture<SessionDescription> future = new SettableFuture<>();
        peerConnection.createAnswer(new SdpObserver() {

            @Override
            public void onCreateSuccess(SessionDescription sdp) {
                future.set(sdp);
            }

            @Override
            public void onCreateFailure(String error) {
                future.setException(new PeerConnectionException(error));
            }

            @Override
            public void onSetSuccess() {
                throw new replacedertionError();
            }

            @Override
            public void onSetFailure(String error) {
                throw new replacedertionError();
            }
        }, mediaConstraints);
        try {
            return correctSessionDescription(future.get());
        } catch (InterruptedException e) {
            throw new replacedertionError(e);
        } catch (ExecutionException e) {
            throw new PeerConnectionException(e);
        }
    }

    public void setRemoteDescription(SessionDescription sdp) throws PeerConnectionException {
        final SettableFuture<Boolean> future = new SettableFuture<>();
        peerConnection.setRemoteDescription(new SdpObserver() {

            @Override
            public void onCreateSuccess(SessionDescription sdp) {
            }

            @Override
            public void onCreateFailure(String error) {
            }

            @Override
            public void onSetSuccess() {
                future.set(true);
            }

            @Override
            public void onSetFailure(String error) {
                future.setException(new PeerConnectionException(error));
            }
        }, sdp);
        try {
            future.get();
        } catch (InterruptedException e) {
            throw new replacedertionError(e);
        } catch (ExecutionException e) {
            throw new PeerConnectionException(e);
        }
    }

    public void setLocalDescription(SessionDescription sdp) throws PeerConnectionException {
        final SettableFuture<Boolean> future = new SettableFuture<>();
        peerConnection.setLocalDescription(new SdpObserver() {

            @Override
            public void onCreateSuccess(SessionDescription sdp) {
                throw new replacedertionError();
            }

            @Override
            public void onCreateFailure(String error) {
                throw new replacedertionError();
            }

            @Override
            public void onSetSuccess() {
                future.set(true);
            }

            @Override
            public void onSetFailure(String error) {
                future.setException(new PeerConnectionException(error));
            }
        }, sdp);
        try {
            future.get();
        } catch (InterruptedException e) {
            throw new replacedertionError(e);
        } catch (ExecutionException e) {
            throw new PeerConnectionException(e);
        }
    }

    public void dispose() {
        if (this.videoCapturer != null) {
            try {
                this.videoCapturer.stopCapture();
            } catch (InterruptedException e) {
                Log.w(TAG, e);
            }
            this.videoCapturer.dispose();
        }
        if (this.videoSource != null) {
            this.videoSource.dispose();
        }
        this.audioSource.dispose();
        this.peerConnection.close();
        this.peerConnection.dispose();
    }

    public boolean addIceCandidate(IceCandidate candidate) {
        return this.peerConnection.addIceCandidate(candidate);
    }

    @Nullable
    private CameraVideoCapturer createVideoCapturer(@NonNull Context context) {
        boolean camera2EnumeratorIsSupported = false;
        try {
            camera2EnumeratorIsSupported = Camera2Enumerator.isSupported(context);
        } catch (final Throwable throwable) {
            Log.w(TAG, "Camera2Enumator.isSupport() threw.", throwable);
        }
        Log.w(TAG, "Camera2 enumerator supported: " + camera2EnumeratorIsSupported);
        CameraEnumerator enumerator;
        if (camera2EnumeratorIsSupported)
            enumerator = new Camera2Enumerator(context);
        else
            enumerator = new Camera1Enumerator(true);
        String[] deviceNames = enumerator.getDeviceNames();
        for (String deviceName : deviceNames) {
            if (enumerator.isFrontFacing(deviceName)) {
                Log.w(TAG, "Creating front facing camera capturer.");
                final CameraVideoCapturer videoCapturer = enumerator.createCapturer(deviceName, null);
                if (videoCapturer != null) {
                    Log.w(TAG, "Found front facing capturer: " + deviceName);
                    return videoCapturer;
                }
            }
        }
        for (String deviceName : deviceNames) {
            if (!enumerator.isFrontFacing(deviceName)) {
                Log.w(TAG, "Creating other camera capturer.");
                final CameraVideoCapturer videoCapturer = enumerator.createCapturer(deviceName, null);
                if (videoCapturer != null) {
                    Log.w(TAG, "Found other facing capturer: " + deviceName);
                    return videoCapturer;
                }
            }
        }
        Log.w(TAG, "Video capture not supported!");
        return null;
    }

    private SessionDescription correctSessionDescription(SessionDescription sessionDescription) {
        String updatedSdp = sessionDescription.description.replaceAll("(a=fmtp:111 ((?!cbr=).)*)\r?\n", "$1;cbr=1\r\n");
        updatedSdp = updatedSdp.replaceAll(".+urn:ietf:params:rtp-hdrext:ssrc-audio-level.*\r?\n", "");
        return new SessionDescription(sessionDescription.type, updatedSdp);
    }

    public static clreplaced PeerConnectionException extends Exception {

        public PeerConnectionException(String error) {
            super(error);
        }

        public PeerConnectionException(Throwable throwable) {
            super(throwable);
        }
    }
}

19 Source : FancyWebRTC.java
with MIT License
from triniwiz

/**
 * Created by Osei Fortune on 8/15/18
 */
public clreplaced FancyWebRTC {

    private PeerConnectionFactory factory;

    private PeerConnection connection;

    private Map<String, MediaStream> localMediaStreams;

    private Map<String, MediaStream> remoteMediaStreams;

    private MediaConstraints defaultConstraints;

    private ArrayList<IceCandidate> remoteIceCandidates;

    private FancyWebRTCListener listener;

    private WeakReference<FancyWebRTC> ref;

    private boolean videoEnabled = true;

    private boolean audioEnabled = true;

    public static String[] WEBRTC_PERMISSIONS = new String[] { android.Manifest.permission.CAMERA, android.Manifest.permission.RECORD_AUDIO };

    public static int WEBRTC_PERMISSIONS_REQUEST_CODE = 12345;

    private PeerConnection.RTCConfiguration configuration;

    static final ExecutorService executor = Executors.newSingleThreadExecutor();

    private final Map<String, MediaData> tracks = new HashMap<>();

    private final Map<String, DataChannel> dataChannels = new HashMap<>();

    private final Map<String, FancyWebRTCListener.GetUserMediaListener> getUserMediaListenerMap = new HashMap<>();

    private final Context appContext;

    public static String Tag = "co.fitcom.fancywebrtc.logger";

    clreplaced MediaData {

        public final MediaSource mediaSource;

        public final MediaStreamTrack track;

        public final FancyWebRTCCapturer capturer;

        public MediaData(MediaSource mediaSource, MediaStreamTrack track, FancyWebRTCCapturer capturer) {
            this.mediaSource = mediaSource;
            this.track = track;
            this.capturer = capturer;
        }
    }

    public static void init(Context context) {
        executor.execute(() -> {
            FancyWebRTCEglUtils.getRootEglBase();
            PeerConnectionFactory.InitializationOptions.Builder builder = PeerConnectionFactory.InitializationOptions.builder(context);
            builder.setEnableInternalTracer(true);
            PeerConnectionFactory.initialize(builder.createInitializationOptions());
        });
    }

    public FancyWebRTC(Context context, boolean videoEnabled, boolean audioEnabled, ArrayList<PeerConnection.IceServer> iceServers) {
        initialize(videoEnabled, audioEnabled, iceServers);
        this.appContext = context;
    }

    private void initialize(final boolean videoEnabled, final boolean audioEnabled, @Nullable final ArrayList<PeerConnection.IceServer> iceServers) {
        ref = new WeakReference<>(this);
        this.videoEnabled = videoEnabled;
        this.audioEnabled = audioEnabled;
        executor.execute(new Runnable() {

            @Override
            public void run() {
                PeerConnectionFactory.Builder builder = PeerConnectionFactory.builder();
                PeerConnectionFactory.Options options = new PeerConnectionFactory.Options();
                builder.setOptions(options);
                factory = builder.createPeerConnectionFactory();
                remoteIceCandidates = new ArrayList<>();
                localMediaStreams = new HashMap<>();
                remoteMediaStreams = new HashMap<>();
                defaultConstraints = new MediaConstraints();
                defaultConstraints.mandatory.add(new MediaConstraints.KeyValuePair("OfferToReceiveAudio", "true"));
                defaultConstraints.mandatory.add(new MediaConstraints.KeyValuePair("OfferToReceiveVideo", "true"));
                configuration = new PeerConnection.RTCConfiguration(iceServers);
                VideoEncoderFactory encoderFactory;
                VideoDecoderFactory decoderFactory;
                if (FancyWebRTCEglUtils.getRootEglBaseContext() != null) {
                    encoderFactory = new DefaultVideoEncoderFactory(FancyWebRTCEglUtils.getRootEglBaseContext(), false, false);
                    decoderFactory = new DefaultVideoDecoderFactory(FancyWebRTCEglUtils.getRootEglBaseContext());
                } else {
                    encoderFactory = new SoftwareVideoEncoderFactory();
                    decoderFactory = new SoftwareVideoDecoderFactory();
                }
                builder.setVideoDecoderFactory(decoderFactory);
                builder.setVideoEncoderFactory(encoderFactory);
                configuration.enableDtlsSrtp = true;
                configuration.enableRtpDataChannel = true;
                configuration.tcpCandidatePolicy = PeerConnection.TcpCandidatePolicy.DISABLED;
                configuration.bundlePolicy = PeerConnection.BundlePolicy.MAXBUNDLE;
                configuration.rtcpMuxPolicy = PeerConnection.RtcpMuxPolicy.REQUIRE;
                configuration.continualGatheringPolicy = PeerConnection.ContinualGatheringPolicy.GATHER_CONTINUALLY;
                configuration.keyType = PeerConnection.KeyType.ECDSA;
            }
        });
    }

    public enum DataChannelState {

        CONNECTING, OPEN, CLOSING, CLOSED
    }

    public void toggleSpeaker() {
        AudioManager manager = (AudioManager) appContext.getSystemService(Context.AUDIO_SERVICE);
        if (manager != null) {
            if (manager.isSpeakerphoneOn()) {
                manager.setSpeakerphoneOn(false);
            } else {
                manager.setSpeakerphoneOn(true);
            }
        }
    }

    public void toggleMic() {
        AudioManager manager = (AudioManager) appContext.getSystemService(Context.AUDIO_SERVICE);
        if (manager != null) {
            if (manager.isMicrophoneMute()) {
                manager.setMicrophoneMute(false);
            } else {
                manager.setMicrophoneMute(true);
            }
        }
    }

    public void speakerEnabled(boolean enabled) {
        AudioManager manager = (AudioManager) appContext.getSystemService(Context.AUDIO_SERVICE);
        if (manager != null) {
            manager.setSpeakerphoneOn(enabled);
        }
    }

    public void micEnabled(boolean enabled) {
        AudioManager manager = (AudioManager) appContext.getSystemService(Context.AUDIO_SERVICE);
        if (manager != null) {
            manager.setMicrophoneMute(enabled);
        }
    }

    public enum DataChannelMessageType {

        TEXT, BINARY
    }

    public void setListener(FancyWebRTCListener listener) {
        this.listener = listener;
    }

    public void connect() {
        if (connection != null)
            return;
        executor.execute(new Runnable() {

            @Override
            public void run() {
                connection = factory.createPeerConnection(configuration, new PeerConnection.Observer() {

                    @Override
                    public void onSignalingChange(PeerConnection.SignalingState signalingState) {
                        if (listener != null) {
                            listener.webRTCClientOnSignalingChange(ref.get(), signalingState);
                        }
                    }

                    @Override
                    public void onIceConnectionChange(PeerConnection.IceConnectionState iceConnectionState) {
                        if (listener != null) {
                            listener.webRTCClientOnIceConnectionChange(ref.get(), iceConnectionState);
                        }
                    }

                    @Override
                    public void onIceConnectionReceivingChange(boolean b) {
                        if (listener != null) {
                            listener.webRTCClientOnIceConnectionReceivingChange(ref.get(), b);
                        }
                    }

                    @Override
                    public void onIceGatheringChange(PeerConnection.IceGatheringState iceGatheringState) {
                        if (listener != null) {
                            listener.webRTCClientOnIceGatheringChange(ref.get(), iceGatheringState);
                        }
                    }

                    @Override
                    public void onIceCandidate(IceCandidate iceCandidate) {
                        if (listener != null) {
                            listener.webRTCClientDidGenerateIceCandidate(ref.get(), iceCandidate);
                        }
                    }

                    @Override
                    public void onIceCandidatesRemoved(IceCandidate[] iceCandidates) {
                        if (listener != null) {
                            listener.webRTCClientOnIceCandidatesRemoved(ref.get(), iceCandidates);
                        }
                    }

                    @Override
                    public void onAddStream(MediaStream mediaStream) {
                        if (listener != null) {
                            if (mediaStream.videoTracks.size() > 0) {
                                listener.webRTCClientDidReceiveStream(ref.get(), mediaStream);
                            }
                        }
                    }

                    @Override
                    public void onRemoveStream(MediaStream mediaStream) {
                        if (listener != null) {
                            listener.webRTCClientOnRemoveStream(ref.get(), mediaStream);
                        }
                    }

                    @Override
                    public void onDataChannel(DataChannel dataChannel) {
                        dataChannels.put(dataChannel.label(), dataChannel);
                        registerDataChannelObserver(dataChannel.label());
                    }

                    @Override
                    public void onRenegotiationNeeded() {
                        if (listener != null) {
                            listener.webRTCClientOnRenegotiationNeeded(ref.get());
                        }
                    }

                    @Override
                    public void onAddTrack(RtpReceiver rtpReceiver, MediaStream[] mediaStreams) {
                    }

                    @Override
                    public void onTrack(RtpTransceiver rtpTransceiver) {
                    }
                });
            }
        });
    }

    private void registerDataChannelObserver(String name) {
        final DataChannel dataChannel = dataChannels.get(name);
        executor.execute(new Runnable() {

            @Override
            public void run() {
                if (dataChannel != null) {
                    dataChannel.registerObserver(new DataChannel.Observer() {

                        @Override
                        public void onBufferedAmountChange(long l) {
                        }

                        @Override
                        public void onStateChange() {
                            if (listener != null) {
                                listener.webRTCClientDataChannelStateChanged(ref.get(), dataChannel.label(), dataChannel.state());
                            }
                        }

                        @Override
                        public void onMessage(DataChannel.Buffer buffer) {
                            byte[] bytes;
                            if (buffer.data.hasArray()) {
                                bytes = buffer.data.array();
                            } else {
                                bytes = new byte[buffer.data.remaining()];
                                buffer.data.get(bytes);
                            }
                            DataChannelMessageType type;
                            String data;
                            if (buffer.binary) {
                                type = DataChannelMessageType.BINARY;
                                data = Base64.encodeToString(bytes, Base64.NO_WRAP);
                            } else {
                                type = DataChannelMessageType.TEXT;
                                data = new String(bytes, Charset.forName("UTF-8"));
                            }
                            if (listener != null) {
                                listener.webRTCClientDataChannelMessageType(ref.get(), dataChannel.label(), data, type);
                            }
                        }
                    });
                }
            }
        });
    }

    public void enableTrack(String id, boolean enabled) {
        MediaData mediaData = tracks.get(id);
        if (mediaData != null) {
            if (mediaData.capturer != null) {
                if (enabled) {
                    mediaData.track.setEnabled(true);
                    mediaData.capturer.startVideo(appContext);
                } else {
                    mediaData.track.setEnabled(false);
                    mediaData.capturer.stopVideo();
                }
            } else {
                mediaData.track.setEnabled(enabled);
            }
        }
    }

    public void switchCamera(String id) {
        final MediaData mediaData = tracks.get(id);
        executor.execute(new Runnable() {

            @Override
            public void run() {
                if (mediaData != null) {
                    if (mediaData.capturer != null) {
                        mediaData.capturer.toggleCamera();
                    }
                }
            }
        });
    }

    public FancyWebRTCListener getListener() {
        return listener;
    }

    public void disconnect() {
        executor.execute(new Runnable() {

            @Override
            public void run() {
                if (connection == null)
                    return;
                for (MediaStream stream : localMediaStreams.values()) {
                    stream.dispose();
                }
                connection.close();
                connection.dispose();
            }
        });
    }

    public void dataChannelSend(String name, final String data, final DataChannelMessageType type) {
        final DataChannel channel = dataChannels.get(name);
        executor.execute(new Runnable() {

            @Override
            public void run() {
                if (channel != null) {
                    byte[] dataArray = null;
                    switch(type) {
                        case BINARY:
                            dataArray = Base64.decode(data, Base64.NO_WRAP);
                            break;
                        case TEXT:
                            try {
                                dataArray = data.getBytes("UTF-8");
                            } catch (UnsupportedEncodingException e) {
                                return;
                            }
                            break;
                    }
                    ByteBuffer byteBuffer = ByteBuffer.wrap(dataArray);
                    DataChannel.Buffer buffer = new DataChannel.Buffer(byteBuffer, type == DataChannelMessageType.BINARY);
                    channel.send(buffer);
                }
            }
        });
    }

    public void dataChannelClose(String name) {
        final DataChannel channel = dataChannels.get(name);
        executor.execute(new Runnable() {

            @Override
            public void run() {
                if (channel != null) {
                    channel.close();
                }
            }
        });
    }

    public void dataChannelCreate(final String name) {
        final DataChannel.Init initData = new DataChannel.Init();
        executor.execute(new Runnable() {

            @Override
            public void run() {
                DataChannel channel = connection.createDataChannel(name, initData);
                dataChannels.put(name, channel);
                registerDataChannelObserver(name);
            }
        });
    }

    public void handleAnswerReceived(final SessionDescription sdp) {
        executor.execute(new Runnable() {

            @Override
            public void run() {
                if (connection == null || sdp == null)
                    return;
                SessionDescription newSdp = new SessionDescription(SessionDescription.Type.ANSWER, sdp.description);
                connection.setRemoteDescription(new SdpObserver() {

                    @Override
                    public void onCreateSuccess(SessionDescription sessionDescription) {
                    }

                    @Override
                    public void onSetSuccess() {
                    }

                    @Override
                    public void onCreateFailure(String s) {
                    }

                    @Override
                    public void onSetFailure(String s) {
                        if (listener != null) {
                            listener.webRTCClientDidReceiveError(ref.get(), s);
                        }
                    }
                }, newSdp);
            }
        });
    }

    public void createAnswerForOfferReceived(final SessionDescription remoteSdp, MediaConstraints constraints) {
        executor.execute(new Runnable() {

            @Override
            public void run() {
                if (connection == null || remoteSdp == null)
                    return;
                if (connection.getRemoteDescription() != null && (connection.getRemoteDescription().type == SessionDescription.Type.ANSWER && remoteSdp.type == SessionDescription.Type.ANSWER))
                    return;
                connection.setRemoteDescription(new SdpObserver() {

                    @Override
                    public void onCreateSuccess(SessionDescription sessionDescription) {
                    }

                    @Override
                    public void onSetSuccess() {
                        handleRemoteDescriptionSet();
                        connection.createAnswer(new SdpObserver() {

                            @Override
                            public void onCreateSuccess(SessionDescription sessionDescription) {
                                handleSdpGenerated(sessionDescription);
                                if (listener != null) {
                                    listener.webRTCClientStartCallWithSdp(ref.get(), sessionDescription);
                                }
                            }

                            @Override
                            public void onSetSuccess() {
                            }

                            @Override
                            public void onCreateFailure(String s) {
                            }

                            @Override
                            public void onSetFailure(String s) {
                                if (listener != null) {
                                    listener.webRTCClientDidReceiveError(ref.get(), s);
                                }
                            }
                        }, defaultConstraints);
                    }

                    @Override
                    public void onCreateFailure(String s) {
                    }

                    @Override
                    public void onSetFailure(String s) {
                        if (listener != null) {
                            listener.webRTCClientDidReceiveError(ref.get(), s);
                        }
                    }
                }, remoteSdp);
            }
        });
    }

    public void addIceCandidate(final IceCandidate iceCandidate) {
        executor.execute(new Runnable() {

            @Override
            public void run() {
                if (connection != null && connection.getRemoteDescription() != null) {
                    connection.addIceCandidate(iceCandidate);
                } else {
                    remoteIceCandidates.add(iceCandidate);
                }
            }
        });
    }

    public MediaStream getLocalMediaStream(String id) {
        return localMediaStreams.get(id);
    }

    public MediaStream getRemoteMediaStream(String id) {
        return remoteMediaStreams.get(id);
    }

    public void addLocalStream(final MediaStream stream) {
        executor.execute(new Runnable() {

            @Override
            public void run() {
                localMediaStreams.put(stream.getId(), stream);
                connection.addStream(stream);
            }
        });
    }

    public void addRemoteStream(MediaStream stream) {
        remoteMediaStreams.put(stream.getId(), stream);
    }

    public void makeOffer(MediaConstraints constraints) {
        executor.execute(new Runnable() {

            @Override
            public void run() {
                if (connection != null) {
                    connection.createOffer(new SdpObserver() {

                        @Override
                        public void onCreateSuccess(SessionDescription sessionDescription) {
                            handleSdpGenerated(sessionDescription);
                        }

                        @Override
                        public void onSetSuccess() {
                        }

                        @Override
                        public void onCreateFailure(String s) {
                            if (listener != null) {
                                listener.webRTCClientDidReceiveError(ref.get(), s);
                            }
                        }

                        @Override
                        public void onSetFailure(String s) {
                        }
                    }, defaultConstraints);
                }
            }
        });
    }

    public void getUserMedia(final Quality quality, final FancyWebRTCListener.GetUserMediaListener getUserMediaListener) {
        final String streamId = randomId();
        if (!FancyWebRTC.hasPermissions(appContext)) {
            boolean videoPermission = ContextCompat.checkSelfPermission(appContext, android.Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED;
            boolean audioPermission = ContextCompat.checkSelfPermission(appContext, android.Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED;
            String error = "";
            if (!videoPermission && !audioPermission) {
                error = "Camera and Record Audio Permission needed.";
            } else if (!videoPermission) {
                error = "Camera Permission needed.";
            } else if (!audioPermission) {
                error = "Record Audio Permission needed.";
            }
            getUserMediaListener.webRTCClientOnGetUserMediaDidReceiveError(ref.get(), error);
            return;
        }
        executor.execute(new Runnable() {

            @Override
            public void run() {
                MediaStream stream = factory.createLocalMediaStream(streamId);
                FancyWebRTCCapturer capturer = createCapturer(quality);
                VideoSource videoSource = factory.createVideoSource(false);
                String videoTrackId = randomId();
                VideoTrack videoTrack = factory.createVideoTrack(videoTrackId, videoSource);
                if (videoEnabled && ContextCompat.checkSelfPermission(appContext, android.Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
                    videoTrack.setEnabled(true);
                } else {
                    videoTrack.setEnabled(false);
                }
                capturer.setVideoSource(videoSource);
                capturer.startVideo(appContext);
                tracks.put(videoTrackId, new MediaData(videoSource, videoTrack, capturer));
                stream.addTrack(videoTrack);
                AudioSource audioSource = factory.createAudioSource(new MediaConstraints());
                String audioTrackId = randomId();
                AudioTrack audioTrack = factory.createAudioTrack(audioTrackId, audioSource);
                if (audioEnabled && (ContextCompat.checkSelfPermission(appContext, android.Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED)) {
                    audioTrack.setEnabled(true);
                } else {
                    audioTrack.setEnabled(false);
                }
                tracks.put(audioTrack.id(), new MediaData(audioSource, audioTrack, null));
                stream.addTrack(audioTrack);
                getUserMediaListener.webRTCClientOnGetUserMedia(ref.get(), stream);
            }
        });
    }

    private void handleRemoteDescriptionSet() {
        executor.execute(new Runnable() {

            @Override
            public void run() {
                for (IceCandidate iceCandidate : remoteIceCandidates) {
                    connection.addIceCandidate(iceCandidate);
                }
                remoteIceCandidates.clear();
            }
        });
    }

    private void handleSdpGenerated(final SessionDescription sdp) {
        executor.execute(new Runnable() {

            @Override
            public void run() {
                if (connection == null)
                    return;
                if (connection.getLocalDescription() != null && (connection.getLocalDescription().type == SessionDescription.Type.ANSWER && sdp.type == SessionDescription.Type.ANSWER))
                    return;
                connection.setLocalDescription(new SdpObserver() {

                    @Override
                    public void onCreateSuccess(SessionDescription sessionDescription) {
                    }

                    @Override
                    public void onSetSuccess() {
                        if (listener != null) {
                            listener.webRTCClientStartCallWithSdp(ref.get(), sdp);
                        }
                    }

                    @Override
                    public void onCreateFailure(String s) {
                    }

                    @Override
                    public void onSetFailure(String s) {
                        if (listener != null) {
                            listener.webRTCClientDidReceiveError(ref.get(), s);
                        }
                    }
                }, sdp);
            }
        });
    }

    public FancyWebRTCCapturer createCapturer(Quality quality) {
        CameraEnumerator enumerator;
        if (Camera2Enumerator.isSupported(appContext)) {
            enumerator = new Camera2Enumerator(appContext);
        } else {
            enumerator = new Camera1Enumerator(false);
        }
        String[] deviceNames = enumerator.getDeviceNames();
        FancyWebRTCCapturer videoCapturer = null;
        for (String deviceName : deviceNames) {
            List<CameraEnumerationAndroid.CaptureFormat> formatList = enumerator.getSupportedFormats(deviceName);
            // Default to lowest
            CameraEnumerationAndroid.CaptureFormat selectedFormat = formatList.get(formatList.size() - 1);
            for (CameraEnumerationAndroid.CaptureFormat format : enumerator.getSupportedFormats(deviceName)) {
                if (quality == Quality.LOWEST) {
                    selectedFormat = formatList.get(formatList.size() - 1);
                    break;
                } else if (quality == Quality.HIGHEST) {
                    selectedFormat = formatList.get(0);
                    break;
                } else if (quality == Quality.MAX_480P && format.height == 480) {
                    selectedFormat = format;
                    break;
                } else if (quality == Quality.MAX_720P && format.height == 720) {
                    selectedFormat = format;
                    break;
                } else if (quality == Quality.MAX_1080P && format.height == 1080) {
                    selectedFormat = format;
                    break;
                } else if (quality == Quality.MAX_2160P && format.height == 2160) {
                    selectedFormat = format;
                    break;
                }
            }
            if (enumerator.isFrontFacing(deviceName)) {
                CameraVideoCapturer capturer = enumerator.createCapturer(deviceName, null);
                if (capturer != null) {
                    FancyWebRTCCapturer fancyWebRTCCapturer = new FancyWebRTCCapturer(ref.get(), capturer);
                    fancyWebRTCCapturer.setFormat(selectedFormat);
                    return fancyWebRTCCapturer;
                }
            }
        }
        for (String deviceName : deviceNames) {
            if (!enumerator.isFrontFacing(deviceName)) {
                List<CameraEnumerationAndroid.CaptureFormat> formatList = enumerator.getSupportedFormats(deviceName);
                // Default to lowest
                CameraEnumerationAndroid.CaptureFormat selectedFormat = formatList.get(formatList.size() - 1);
                for (CameraEnumerationAndroid.CaptureFormat format : enumerator.getSupportedFormats(deviceName)) {
                    if (quality == Quality.LOWEST) {
                        selectedFormat = formatList.get(formatList.size() - 1);
                        break;
                    } else if (quality == Quality.HIGHEST) {
                        selectedFormat = formatList.get(0);
                        break;
                    } else if (quality == Quality.MAX_480P && format.height == 480) {
                        selectedFormat = format;
                        break;
                    } else if (quality == Quality.MAX_720P && format.height == 720) {
                        selectedFormat = format;
                        break;
                    } else if (quality == Quality.MAX_1080P && format.height == 1080) {
                        selectedFormat = format;
                        break;
                    } else if (quality == Quality.MAX_2160P && format.height == 2160) {
                        selectedFormat = format;
                        break;
                    }
                }
                CameraVideoCapturer capturer = enumerator.createCapturer(deviceName, null);
                if (capturer != null) {
                    FancyWebRTCCapturer fancyWebRTCCapturer = new FancyWebRTCCapturer(ref.get(), capturer);
                    fancyWebRTCCapturer.setFormat(selectedFormat);
                    return fancyWebRTCCapturer;
                }
            }
        }
        return videoCapturer;
    }

    private String randomId() {
        return UUID.randomUUID().toString();
    }

    public static boolean hasPermissions(Context context) {
        return ContextCompat.checkSelfPermission(context, android.Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED && ContextCompat.checkSelfPermission(context, android.Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED;
    }

    public static void requestPermissions(Context context) {
        androidx.core.app.ActivityCompat.requestPermissions((Activity) context, WEBRTC_PERMISSIONS, WEBRTC_PERMISSIONS_REQUEST_CODE);
    }

    public enum Quality {

        MAX_480P,
        MAX_720P,
        MAX_1080P,
        MAX_2160P,
        HIGHEST,
        LOWEST
    }
}

19 Source : FancyRTCPeerConnection.java
with MIT License
from triniwiz

/**
 * Created by triniwiz on 1/7/19
 */
public clreplaced FancyRTCPeerConnection {

    private FancyRTCConfiguration configuration;

    private PeerConnection connection;

    static PeerConnectionFactory factory;

    private Context context;

    static final ExecutorService executor = Executors.newSingleThreadExecutor();

    private FancyOnConnectionStateChangeListener onConnectionStateChangeListener;

    private FancyOnTrackListener onTrackListener;

    private FancyOnRemoveTrackListener onRemoveTrackListener;

    private FancyOnIceGatheringStateChangeListener onIceGatheringStateChangeListener;

    private FancyOnNegotiationNeededListener onNegotiationNeededListener;

    private FancyOnSignalingStateChangeListener onSignalingStateChangeListener;

    private FancyOnIceCandidateListener onIceCandidateListener;

    private FancyOnDataChannelListener onDataChannelListener;

    private FancyOnAddStreamListener onAddStreamListener;

    private FancyOnRemoveStreamListener onRemoveStreamListener;

    private FancyOnIceConnectionChangeListener onIceConnectionChangeListener;

    private Map<String, RtpReceiver> remoteReceivers = new HashMap<>();

    private Map<String, RtpTransceiver> remoteTransceivers = new HashMap<>();

    static {
        initFactory();
    }

    private static void initFactory() {
        executor.execute(() -> {
            PeerConnectionFactory.Builder builder = PeerConnectionFactory.builder();
            PeerConnectionFactory.Options options = new PeerConnectionFactory.Options();
            builder.setOptions(options);
            VideoEncoderFactory encoderFactory;
            VideoDecoderFactory decoderFactory;
            if (FancyWebRTCEglUtils.getRootEglBaseContext() != null) {
                encoderFactory = new DefaultVideoEncoderFactory(FancyWebRTCEglUtils.getRootEglBaseContext(), true, true);
                decoderFactory = new DefaultVideoDecoderFactory(FancyWebRTCEglUtils.getRootEglBaseContext());
            } else {
                encoderFactory = new SoftwareVideoEncoderFactory();
                decoderFactory = new SoftwareVideoDecoderFactory();
            }
            builder.setVideoDecoderFactory(decoderFactory);
            builder.setVideoEncoderFactory(encoderFactory);
            factory = builder.createPeerConnectionFactory();
        });
    }

    private void init() {
        executor.execute(() -> connection = factory.createPeerConnection(configuration.getConfiguration(), new PeerConnection.Observer() {

            @Override
            public void onConnectionChange(PeerConnection.PeerConnectionState newState) {
                if (onConnectionStateChangeListener != null) {
                    onConnectionStateChangeListener.onChange();
                }
            }

            @Override
            public void onSignalingChange(PeerConnection.SignalingState signalingState) {
                if (onSignalingStateChangeListener != null) {
                    onSignalingStateChangeListener.onSignalingStateChange();
                }
            }

            @Override
            public void onIceConnectionChange(PeerConnection.IceConnectionState iceConnectionState) {
                if (onIceConnectionChangeListener != null) {
                    onIceConnectionChangeListener.onChange();
                }
            }

            @Override
            public void onIceConnectionReceivingChange(boolean b) {
            }

            @Override
            public void onIceGatheringChange(PeerConnection.IceGatheringState iceGatheringState) {
                if (onIceGatheringStateChangeListener != null) {
                    onIceGatheringStateChangeListener.onIceGatheringStateChange();
                }
            }

            @Override
            public void onIceCandidate(IceCandidate iceCandidate) {
                if (onIceCandidateListener != null) {
                    onIceCandidateListener.onIceCandidate(new FancyRTCIceCandidate(iceCandidate));
                }
            }

            @Override
            public void onIceCandidatesRemoved(IceCandidate[] iceCandidates) {
            }

            @Override
            public void onAddStream(MediaStream mediaStream) {
                if (onAddStreamListener != null) {
                    onAddStreamListener.onAddStream(new FancyRTCMediaStream(mediaStream));
                }
            }

            @Override
            public void onRemoveStream(MediaStream mediaStream) {
                if (onRemoveStreamListener != null) {
                    onRemoveStreamListener.onRemoveStream(new FancyRTCMediaStream(mediaStream));
                }
            }

            @Override
            public void onDataChannel(DataChannel dataChannel) {
                if (onDataChannelListener != null) {
                    onDataChannelListener.onDataChannel(new FancyRTCDataChannelEvent(new FancyRTCDataChannel(dataChannel)));
                }
            }

            @Override
            public void onRenegotiationNeeded() {
                if (onNegotiationNeededListener != null) {
                    onNegotiationNeededListener.onNegotiationNeeded();
                }
            }

            @Override
            public void onAddTrack(RtpReceiver rtpReceiver, MediaStream[] mediaStreams) {
                remoteReceivers.put(FancyUtils.getUUID(), rtpReceiver);
                if (onTrackListener != null) {
                    if (configuration.getSdpSemantics() == FancyRTCSdpSemantics.PLAN_B) {
                        List<FancyRTCMediaStream> list = new ArrayList<>();
                        for (MediaStream stream : mediaStreams) {
                            list.add(new FancyRTCMediaStream(stream));
                        }
                        FancyRTCTrackEvent event = new FancyRTCTrackEvent(new FancyRTCRtpReceiver(rtpReceiver), list, new FancyRTCMediaStreamTrack(rtpReceiver.track()), null);
                        onTrackListener.onTrack(event);
                    }
                }
            }

            @Override
            public void onTrack(RtpTransceiver rtpTransceiver) {
                remoteTransceivers.put(FancyUtils.getUUID(), rtpTransceiver);
                if (onTrackListener != null) {
                    FancyRTCTrackEvent event = new FancyRTCTrackEvent(new FancyRTCRtpReceiver(rtpTransceiver.getReceiver()), null, new FancyRTCMediaStreamTrack(rtpTransceiver.getReceiver().track()), new FancyRTCRtpTransceiver(rtpTransceiver));
                    onTrackListener.onTrack(event);
                }
            }
        }));
    }

    public FancyRTCPeerConnection(Context context) {
        this.context = context;
        configuration = new FancyRTCConfiguration();
        init();
    }

    public FancyRTCPeerConnection(Context context, FancyRTCConfiguration configuration) {
        this.context = context;
        this.configuration = configuration;
        init();
    }

    public FancyRTCSessionDescription getLocalDescription() {
        if (connection == null)
            return null;
        SessionDescription sdp = connection.getLocalDescription();
        if (sdp == null)
            return null;
        return FancyRTCSessionDescription.fromRTCSessionDescription(sdp);
    }

    public FancyRTCSessionDescription getRemoteDescription() {
        if (connection == null)
            return null;
        SessionDescription sdp = connection.getRemoteDescription();
        if (sdp == null)
            return null;
        return FancyRTCSessionDescription.fromRTCSessionDescription(sdp);
    }

    public FancyRTCPeerConnectionState getConnectionState() {
        if (connection != null) {
            switch(connection.iceConnectionState()) {
                case NEW:
                    return FancyRTCPeerConnectionState.NEW;
                case CLOSED:
                    return FancyRTCPeerConnectionState.CLOSED;
                case FAILED:
                    return FancyRTCPeerConnectionState.FAILED;
                case CHECKING:
                case COMPLETED:
                case CONNECTED:
                    return FancyRTCPeerConnectionState.CONNECTED;
                case DISCONNECTED:
                    return FancyRTCPeerConnectionState.DISCONNECTED;
            }
        }
        return FancyRTCPeerConnectionState.NEW;
    }

    public static interface FancyOnIceConnectionChangeListener {

        public void onChange();
    }

    public void setOnIceConnectionChange(FancyOnIceConnectionChangeListener listener) {
        onIceConnectionChangeListener = listener;
    }

    public static interface FancyOnConnectionStateChangeListener {

        public void onChange();
    }

    public void setOnConnectionStateChange(FancyOnConnectionStateChangeListener listener) {
        onConnectionStateChangeListener = listener;
    }

    public static interface FancyOnTrackListener {

        public void onTrack(FancyRTCTrackEvent event);
    }

    public void setOnTrackListener(FancyOnTrackListener listener) {
        onTrackListener = listener;
    }

    public static interface FancyOnRemoveTrackListener {

        public void onRemoveTrack();
    }

    public void setOnRemoveTrackListener(FancyOnRemoveTrackListener listener) {
        onRemoveTrackListener = listener;
    }

    public static interface FancyOnRemoveStreamListener {

        public void onRemoveStream(FancyRTCMediaStream stream);
    }

    public void setOnRemoveStreamListener(FancyOnRemoveStreamListener listener) {
        onRemoveStreamListener = listener;
    }

    public static interface FancyOnIceGatheringStateChangeListener {

        public void onIceGatheringStateChange();
    }

    public void setOnIceGatheringStateChangeListener(FancyOnIceGatheringStateChangeListener listener) {
        onIceGatheringStateChangeListener = listener;
    }

    public static interface FancyOnAddStreamListener {

        public void onAddStream(FancyRTCMediaStream stream);
    }

    public void setOnAddStreamListener(FancyOnAddStreamListener listener) {
        onAddStreamListener = listener;
    }

    public static interface FancyOnNegotiationNeededListener {

        public void onNegotiationNeeded();
    }

    public void setOnNegotiationNeededListener(FancyOnNegotiationNeededListener listener) {
        onNegotiationNeededListener = listener;
    }

    public static interface FancyOnSignalingStateChangeListener {

        public void onSignalingStateChange();
    }

    public void setOnSignalingStateChangeListener(FancyOnSignalingStateChangeListener listener) {
        onSignalingStateChangeListener = listener;
    }

    public static interface FancyOnIceCandidateListener {

        public void onIceCandidate(FancyRTCIceCandidate candidate);
    }

    public void setOnIceCandidateListener(FancyOnIceCandidateListener listener) {
        onIceCandidateListener = listener;
    }

    public static interface FancyOnDataChannelListener {

        public void onDataChannel(FancyRTCDataChannelEvent event);
    }

    public void setOnDataChannelListener(FancyOnDataChannelListener listener) {
        onDataChannelListener = listener;
    }

    public List<FancyRTCIceServer> getDefaultIceServers() {
        List<FancyRTCIceServer> list = new ArrayList<>();
        String[] defaultIceServers = new String[] { "stun:stun.l.google.com:19302", "stun:stun1.l.google.com:19302", "stun:stun2.l.google.com:19302", "stun:stun3.l.google.com:19302", "stun:stun4.l.google.com:19302" };
        for (String server : defaultIceServers) {
            list.add(new FancyRTCIceServer(server));
        }
        return list;
    }

    public void addIceCandidate(FancyRTCIceCandidate candidate) {
        executor.execute(() -> {
            if (connection != null) {
                connection.addIceCandidate(candidate.getIceCandidate());
            }
        });
    }

    public void addTrack(FancyRTCMediaStreamTrack track, List<String> streamIds) {
        executor.execute(() -> {
            if (connection != null) {
                connection.addTrack(track.getMediaStreamTrack(), streamIds);
            }
        });
    }

    public void close() {
        executor.execute(() -> {
            if (connection != null) {
                connection.close();
            }
        });
    }

    public static interface SdpCreateListener {

        public void onSuccess(FancyRTCSessionDescription description);

        public void onError(String error);
    }

    public static interface SdpSetListener {

        public void onSuccess();

        public void onError(String error);
    }

    public FancyRTCDataChannel createDataChannel(String label, FancyRTCDataChannelInit init) {
        if (connection != null) {
            DataChannel channel = connection.createDataChannel(label, init.getInit());
            return new FancyRTCDataChannel(channel);
        }
        return null;
    }

    public void dispose() {
        executor.execute(() -> {
            if (connection != null) {
                connection.dispose();
            }
        });
    }

    public void createAnswer(FancyRTCMediaConstraints mediaConstraints, SdpCreateListener listener) {
        executor.execute(() -> {
            if (connection != null) {
                if (!mediaConstraints.mandatory.contains(new FancyRTCMediaConstraints.KeyValue("OfferToReceiveVideo", "true")) || !mediaConstraints.mandatory.contains(new FancyRTCMediaConstraints.KeyValue("OfferToReceiveVideo", "false"))) {
                    mediaConstraints.mandatory.add(new FancyRTCMediaConstraints.KeyValue("OfferToReceiveVideo", "true"));
                }
                if (!mediaConstraints.mandatory.contains(new FancyRTCMediaConstraints.KeyValue("OfferToReceiveAudio", "true")) || !mediaConstraints.mandatory.contains(new FancyRTCMediaConstraints.KeyValue("OfferToReceiveAudio", "false"))) {
                    mediaConstraints.mandatory.add(new FancyRTCMediaConstraints.KeyValue("OfferToReceiveAudio", "true"));
                }
                connection.createAnswer(new SdpObserver() {

                    @Override
                    public void onCreateSuccess(SessionDescription sessionDescription) {
                        listener.onSuccess(FancyRTCSessionDescription.fromRTCSessionDescription(sessionDescription));
                    }

                    @Override
                    public void onSetSuccess() {
                    }

                    @Override
                    public void onCreateFailure(String s) {
                        listener.onError(s);
                    }

                    @Override
                    public void onSetFailure(String s) {
                    }
                }, mediaConstraints.getMediaConstraints());
            }
        });
    }

    public void createOffer(FancyRTCMediaConstraints mediaConstraints, SdpCreateListener listener) {
        executor.execute(() -> {
            if (connection != null) {
                if (!mediaConstraints.mandatory.contains(new FancyRTCMediaConstraints.KeyValue("OfferToReceiveVideo", "true")) || !mediaConstraints.mandatory.contains(new FancyRTCMediaConstraints.KeyValue("OfferToReceiveVideo", "false"))) {
                    mediaConstraints.mandatory.add(new FancyRTCMediaConstraints.KeyValue("OfferToReceiveVideo", "true"));
                }
                if (!mediaConstraints.mandatory.contains(new FancyRTCMediaConstraints.KeyValue("OfferToReceiveAudio", "true")) || !mediaConstraints.mandatory.contains(new FancyRTCMediaConstraints.KeyValue("OfferToReceiveAudio", "false"))) {
                    mediaConstraints.mandatory.add(new FancyRTCMediaConstraints.KeyValue("OfferToReceiveAudio", "true"));
                }
                connection.createOffer(new SdpObserver() {

                    @Override
                    public void onCreateSuccess(SessionDescription sessionDescription) {
                        listener.onSuccess(FancyRTCSessionDescription.fromRTCSessionDescription(sessionDescription));
                    }

                    @Override
                    public void onSetSuccess() {
                    }

                    @Override
                    public void onCreateFailure(String s) {
                        listener.onError(s);
                    }

                    @Override
                    public void onSetFailure(String s) {
                    }
                }, mediaConstraints.getMediaConstraints());
            }
        });
    }

    public void setLocalDescription(FancyRTCSessionDescription sdp, SdpSetListener listener) {
        executor.execute(() -> {
            if (connection != null) {
                connection.setLocalDescription(new SdpObserver() {

                    @Override
                    public void onCreateSuccess(SessionDescription sessionDescription) {
                    }

                    @Override
                    public void onSetSuccess() {
                        listener.onSuccess();
                    }

                    @Override
                    public void onCreateFailure(String s) {
                    }

                    @Override
                    public void onSetFailure(String s) {
                        listener.onError(s);
                    }
                }, sdp.getSessionDescription());
            }
        });
    }

    public void setRemoteDescription(FancyRTCSessionDescription sdp, SdpSetListener listener) {
        executor.execute(() -> {
            if (connection != null) {
                connection.setRemoteDescription(new SdpObserver() {

                    @Override
                    public void onCreateSuccess(SessionDescription sessionDescription) {
                    }

                    @Override
                    public void onSetSuccess() {
                        listener.onSuccess();
                    }

                    @Override
                    public void onCreateFailure(String s) {
                    }

                    @Override
                    public void onSetFailure(String s) {
                        listener.onError(s);
                    }
                }, sdp.getSessionDescription());
            }
        });
    }

    public PeerConnection getConnection() {
        return connection;
    }

    public List<FancyRTCRtpSender> getSenders() {
        List<FancyRTCRtpSender> senders = new ArrayList<>();
        for (RtpSender sender : connection.getSenders()) {
            senders.add(new FancyRTCRtpSender(sender));
        }
        return senders;
    }

    AudioDeviceModule createJavaAudioDevice() {
        return JavaAudioDeviceModule.builder(context).setSamplesReadyCallback(new JavaAudioDeviceModule.SamplesReadyCallback() {

            @Override
            public void onWebRtcAudioRecordSamplesReady(JavaAudioDeviceModule.AudioSamples audioSamples) {
            }
        }).setUseHardwareAcousticEchoCanceler(true).setUseHardwareNoiseSuppressor(true).setAudioRecordErrorCallback(new JavaAudioDeviceModule.AudioRecordErrorCallback() {

            @Override
            public void onWebRtcAudioRecordInitError(String s) {
            }

            @Override
            public void onWebRtcAudioRecordStartError(JavaAudioDeviceModule.AudioRecordStartErrorCode audioRecordStartErrorCode, String s) {
            }

            @Override
            public void onWebRtcAudioRecordError(String s) {
            }
        }).setAudioTrackErrorCallback(new JavaAudioDeviceModule.AudioTrackErrorCallback() {

            @Override
            public void onWebRtcAudioTrackInitError(String s) {
            }

            @Override
            public void onWebRtcAudioTrackStartError(JavaAudioDeviceModule.AudioTrackStartErrorCode audioTrackStartErrorCode, String s) {
            }

            @Override
            public void onWebRtcAudioTrackError(String s) {
            }
        }).createAudioDeviceModule();
    }
}

19 Source : PeerConnectionWrapper.java
with GNU Affero General Public License v3.0
from threema-ch

/**
 * Wrapper around the WebRTC PeerConnection.
 *
 * This handles everything from creating the peer connection
 * to destroying it afterwards.
 */
@SameThread
public clreplaced PeerConnectionWrapper {

    private static final String THREEMA_DC_LABEL = "THREEMA";

    // Logger
    private final Logger logger = LoggerFactory.getLogger(PeerConnectionWrapper.clreplaced);

    // Worker thread handler
    @NonNull
    private final HandlerExecutor handler;

    // WebRTC / SaltyRTC
    @NonNull
    private final PeerConnectionFactory factory;

    @NonNull
    private final org.webrtc.PeerConnection pc;

    @NonNull
    private final WebRTCTask task;

    private final boolean allowIpv6;

    // State
    @NonNull
    private CompletableFuture<Void> readyToSetRemoteDescription = new CompletableFuture<>();

    @NonNull
    private CompletableFuture<Void> readyToAddRemoteCandidates = new CompletableFuture<>();

    @NonNull
    private CompletableFuture<Void> readyToSendLocalCandidates = new CompletableFuture<>();

    @NonNull
    private PeerConnectionState state = PeerConnectionState.NEW;

    private boolean disposed = false;

    // Listener
    @NonNull
    private final PeerConnectionListener listener;

    /**
     * Return a PeerConnectionFactory instance used for Threema Web.
     */
    public static PeerConnectionFactory getPeerConnectionFactory() {
        return PeerConnectionFactory.builder().createPeerConnectionFactory();
    }

    /**
     * Return the RTCConfiguration used for Threema Web.
     */
    public static PeerConnection.RTCConfiguration getRTCConfiguration(@NonNull final Logger logger) throws Exception {
        // Set ICE servers
        final List<org.webrtc.PeerConnection.IceServer> iceServers = new ArrayList<>();
        final APIConnector.TurnServerInfo turnServerInfo = Config.getTurnServerCache().getTurnServers();
        final List<String> turnServers = Arrays.asList(turnServerInfo.turnUrls);
        StreamSupport.stream(turnServers).map(server -> PeerConnection.IceServer.builder(server).setUsername(turnServerInfo.turnUsername).setPreplacedword(turnServerInfo.turnPreplacedword).createIceServer()).forEach(iceServers::add);
        logger.debug("Using ICE servers: {}", turnServers);
        // Set up RTC configuration
        final PeerConnection.RTCConfiguration rtcConfig = new PeerConnection.RTCConfiguration(iceServers);
        rtcConfig.continualGatheringPolicy = PeerConnection.ContinualGatheringPolicy.GATHER_ONCE;
        rtcConfig.sdpSemantics = PeerConnection.SdpSemantics.UNIFIED_PLAN;
        return rtcConfig;
    }

    /**
     * Initialize a peer connection.
     */
    public PeerConnectionWrapper(@NonNull final String logPrefix, @NonNull final Context appContext, @NonNull final HandlerExecutor handler, @NonNull final WebRTCTask task, @NonNull final TemporaryTaskEventHandler temporaryTaskEventHandler, final boolean allowIpv6, @NonNull final PeerConnectionListener listener) throws Exception {
        // Set logger prefix
        if (logger instanceof ThreemaLogger) {
            ((ThreemaLogger) logger).setPrefix(logPrefix);
        }
        logger.info("Initialize WebRTC PeerConnection");
        // Initialise WebRTC for Android
        WebRTCUtil.initializeAndroidGlobals(appContext);
        this.factory = getPeerConnectionFactory();
        // Store handler, listener, task and set message handler
        this.handler = handler;
        this.listener = listener;
        this.task = task;
        temporaryTaskEventHandler.replace(this.task, new TaskMessageHandler());
        this.allowIpv6 = allowIpv6;
        // Create peer connection
        final PeerConnection peerConnection = factory.createPeerConnection(getRTCConfiguration(logger), new PeerConnectionObserver());
        if (peerConnection == null) {
            throw new RuntimeException("Could not create peer connection: createPeerConnection returned null");
        }
        this.pc = peerConnection;
    }

    /**
     * If the instance is disposed, throw an exception.
     */
    private void ensureNotDisposed() {
        if (this.disposed) {
            throw new IllegalStateException("PeerConnection is disposed");
        }
    }

    /**
     * Handler for incoming task messages.
     */
    @AnyThread
    private clreplaced TaskMessageHandler implements org.saltyrtc.tasks.webrtc.events.MessageHandler {

        @Override
        public void onOffer(@NonNull final Offer offer) {
            PeerConnectionWrapper.this.readyToSetRemoteDescription.thenRunAsync(new Runnable() {

                @Override
                @WorkerThread
                public void run() {
                    if (PeerConnectionWrapper.this.disposed) {
                        logger.warn("Ignoring offer, peer connection already disposed");
                    } else {
                        PeerConnectionWrapper.this.onOfferReceived(offer);
                    }
                }
            }, PeerConnectionWrapper.this.handler.getExecutor());
        }

        @Override
        public void onAnswer(@NonNull final Answer answer) {
            logger.warn("Ignoring answer");
        }

        @Override
        public void onCandidates(@NonNull final Candidate[] candidates) {
            PeerConnectionWrapper.this.readyToAddRemoteCandidates.thenRunAsync(new Runnable() {

                @Override
                @WorkerThread
                public void run() {
                    if (PeerConnectionWrapper.this.disposed) {
                        logger.warn("Ignoring candidates, peer connection already disposed");
                    } else {
                        PeerConnectionWrapper.this.onIceCandidatesReceived(candidates);
                    }
                }
            }, PeerConnectionWrapper.this.handler.getExecutor());
        }
    }

    /**
     * A WebRTC offer was received. Set the remote description.
     */
    @AnyThread
    private void onOfferReceived(@NonNull final Offer offer) {
        logger.info("Offer received, applying as remote description");
        this.pc.setRemoteDescription(new SdpObserver() {

            @Override
            @AnyThread
            public void onCreateSuccess(@NonNull final SessionDescription description) {
            // Unreachable
            }

            @Override
            @AnyThread
            public void onCreateFailure(@NonNull final String error) {
            // Unreachable
            }

            @Override
            @AnyThread
            public void onSetSuccess() {
                PeerConnectionWrapper.this.onRemoteDescriptionSet();
            }

            @Override
            @AnyThread
            public void onSetFailure(@NonNull final String error) {
                logger.error("Could not apply remote description: {}", error);
            }
        }, new SessionDescription(SessionDescription.Type.OFFER, offer.getSdp()));
    }

    /**
     * The remote description was set. Create and send an answer.
     */
    @AnyThread
    private void onRemoteDescriptionSet() {
        logger.info("Remote description applied successfully, creating answer");
        this.pc.createAnswer(new SdpObserver() {

            @Nullable
            private SessionDescription description;

            @Override
            @AnyThread
            public synchronized void onCreateSuccess(@NonNull final SessionDescription description) {
                logger.info("Created answer");
                this.description = description;
                PeerConnectionWrapper.this.pc.setLocalDescription(this, description);
            }

            @Override
            @AnyThread
            public void onCreateFailure(@NonNull final String error) {
                logger.error("Could not create answer: {}", error);
            }

            @Override
            @AnyThread
            public synchronized void onSetSuccess() {
                logger.info("Local description applied successfully, sending answer");
                final Answer answer = new Answer(Objects.requireNonNull(this.description).description);
                PeerConnectionWrapper.this.handler.post(new Runnable() {

                    @Override
                    @WorkerThread
                    public void run() {
                        logger.debug("Sending answer");
                        try {
                            // Send the answer
                            PeerConnectionWrapper.this.task.sendAnswer(answer);
                            // Signal that local ICE candidates may be sent now
                            PeerConnectionWrapper.this.readyToSendLocalCandidates.complete(null);
                        } catch (ConnectionException error) {
                            logger.error("Could not send answer", error);
                        }
                    }
                });
            }

            @Override
            @AnyThread
            public void onSetFailure(@NonNull final String error) {
                logger.error("Could not set local description: {}", error);
            }
        }, new MediaConstraints());
        // Signal that remote ICE candidates may be added now (delayed to rule
        // out weird state bugs in libwebrtc)
        PeerConnectionWrapper.this.handler.post(new Runnable() {

            @Override
            @WorkerThread
            public void run() {
                PeerConnectionWrapper.this.readyToAddRemoteCandidates.complete(null);
            }
        });
    }

    /**
     * One or more ICE candidates were received. Add them.
     */
    @AnyThread
    private void onIceCandidatesReceived(@NonNull final Candidate[] candidates) {
        int added = 0;
        for (Candidate candidate : candidates) {
            // Ignore without m-line
            if (candidate.getSdpMLineIndex() == null) {
                logger.warn("Received candidate without SdpMLineIndex, ignoring: {}", candidate.getSdp());
                continue;
            }
            // Ignore candidates with empty SDP
            if (candidate.getSdp() == null || candidate.getSdp().trim().equals("")) {
                logger.warn("Received candidate with empty SDP, ignoring");
                continue;
            }
            // Ignore IPv6 (if requested)
            if (!this.allowIpv6 && SdpUtil.isIpv6Candidate(candidate.getSdp())) {
                logger.info("Ignoring IPv6 candidate due to settings: {}", candidate.getSdp());
                continue;
            }
            // Add candidate
            logger.info("Adding peer ICE candidate: {}", candidate.getSdp());
            this.pc.addIceCandidate(new IceCandidate(candidate.getSdpMid(), candidate.getSdpMLineIndex(), candidate.getSdp()));
            added++;
        }
        logger.info("Added {} ICE candidate(s) from peer", added);
        if (added < candidates.length) {
            logger.info("Ignored {} remote candidate(s) from peer", candidates.length - added);
        }
    }

    /**
     * Return the wrapped PeerConnection.
     */
    public org.webrtc.PeerConnection getPeerConnection() {
        this.ensureNotDisposed();
        return this.pc;
    }

    /**
     * Return the peer connection state.
     */
    @NonNull
    public synchronized PeerConnectionState getState() {
        return this.state;
    }

    /**
     * Set the peer connection state and notify listeners.
     */
    @AnyThread
    private synchronized void setState(@NonNull final PeerConnectionState state) {
        final PeerConnectionState current = this.state;
        if (this.disposed) {
            logger.warn("PeerConnection is disposed, ignoring state change from {} to {}", current, state);
            return;
        }
        // Update state
        this.state = state;
        logger.info("PeerConnectionState changed to {}", state);
        // Fire state event
        this.listener.onStateChanged(current, state);
    }

    /**
     * Close the peer connection and dispose allocated resources.
     *
     * This results in a terminal state. After calling this method,
     * the instance MUST not be used anymore.
     */
    public void dispose() {
        logger.info("dispose()");
        if (this.disposed) {
            logger.warn("Not disposing: Already disposed");
            return;
        }
        synchronized (this) {
            // Mark this instance as disposed
            this.disposed = true;
        }
        // Close and dispose peer connection.
        // (The `dispose()` method implicitly calls `close()`)
        logger.trace("Closing peer connection");
        pc.close();
        logger.trace("Disposing peer connection");
        pc.dispose();
        logger.trace("Disposed peer connection");
        // Dispose the peer connection factory.
        logger.trace("Disposing factory");
        factory.dispose();
        logger.trace("Disposed factory");
        logger.info("All native resources disposed");
        synchronized (this) {
            // Set state to CLOSED
            this.state = PeerConnectionState.CLOSED;
            // Fire state event
            this.listener.onStateChanged(this.state, PeerConnectionState.CLOSED);
        }
    }

    private clreplaced PeerConnectionObserver implements org.webrtc.PeerConnection.Observer {

        @Override
        @AnyThread
        public void onSignalingChange(@NonNull final org.webrtc.PeerConnection.SignalingState state) {
            logger.info("Signaling state change to {}", state.name());
        }

        @Override
        @AnyThread
        public void onIceConnectionChange(@NonNull final IceConnectionState state) {
            logger.info("ICE connection state change to {}", state.name());
            switch(state) {
                case NEW:
                    PeerConnectionWrapper.this.setState(PeerConnectionState.NEW);
                    break;
                case CHECKING:
                case DISCONNECTED:
                    PeerConnectionWrapper.this.setState(PeerConnectionState.CONNECTING);
                    break;
                case CONNECTED:
                case COMPLETED:
                    PeerConnectionWrapper.this.setState(PeerConnectionState.CONNECTED);
                    break;
                case FAILED:
                    PeerConnectionWrapper.this.setState(PeerConnectionState.FAILED);
                    PeerConnectionWrapper.this.logStatus();
                    break;
                case CLOSED:
                    PeerConnectionWrapper.this.setState(PeerConnectionState.CLOSED);
                    break;
                default:
                    logger.error("Unknown ICE connection state: {}", state);
            }
        }

        @Override
        @AnyThread
        public void onIceConnectionReceivingChange(final boolean noIdeaWhatThisIs) {
        }

        @Override
        @AnyThread
        public void onIceGatheringChange(@NonNull final IceGatheringState state) {
            logger.info("ICE gathering state change to {}", state.name());
        }

        /**
         * A new ICE candidate was generated. Send it to the peer.
         */
        @Override
        @AnyThread
        public void onIceCandidate(@NonNull final IceCandidate candidate) {
            logger.info("New local ICE candidate: {}", candidate.sdp);
            // Check if loopback
            if (SdpUtil.isLoopbackCandidate(candidate.sdp)) {
                logger.info("Ignored local loopback candidate");
                return;
            }
            // Check if IPv6
            if (!allowIpv6 && SdpUtil.isIpv6Candidate(candidate.sdp)) {
                logger.info("Ignored local IPv6 candidate (disabled via preferences)");
                return;
            }
            // Send candidate when ready
            PeerConnectionWrapper.this.readyToSendLocalCandidates.thenRunAsync(new Runnable() {

                @Override
                @WorkerThread
                public void run() {
                    logger.debug("Sending ICE candidate");
                    try {
                        final Candidate[] candidates = new Candidate[] { new Candidate(candidate.sdp, candidate.sdpMid, candidate.sdpMLineIndex) };
                        PeerConnectionWrapper.this.task.sendCandidates(candidates);
                    } catch (ConnectionException error) {
                        logger.error("Could not send ICE candidate", error);
                    }
                }
            }, PeerConnectionWrapper.this.handler.getExecutor());
        }

        @Override
        @AnyThread
        public void onIceCandidatesRemoved(@NonNull final IceCandidate[] iceCandidates) {
            // Legacy nonsense
            if (logger.isInfoEnabled()) {
                logger.info("Ignoring removed candidates: {}", Arrays.toString(iceCandidates));
            }
        }

        @Override
        @AnyThread
        public void onRenegotiationNeeded() {
            logger.info("Negotiation needed");
            PeerConnectionWrapper.this.setState(PeerConnectionState.CONNECTING);
            // Signal that a remote description may now be safely set (delayed to rule
            // out weird state bugs in libwebrtc)
            PeerConnectionWrapper.this.handler.post(new Runnable() {

                @Override
                @WorkerThread
                public void run() {
                    PeerConnectionWrapper.this.readyToSetRemoteDescription.complete(null);
                }
            });
        }

        @Override
        @AnyThread
        public void onAddTrack(@NonNull final RtpReceiver rtpReceiver, @NonNull final MediaStream[] mediaStreams) {
            logger.error("onAddTrack (in web client)");
        }

        @Override
        @AnyThread
        public void onAddStream(@NonNull final MediaStream mediaStream) {
            logger.error("onAddStream (in web client)");
        }

        @Override
        @AnyThread
        public void onRemoveStream(@NonNull final MediaStream mediaStream) {
            logger.error("onRemoveStream (in web client)");
        }

        @Override
        @AnyThread
        public void onDataChannel(@NonNull final DataChannel dc) {
            final String label = dc.label();
            logger.info("New data channel: {}", label);
            if (!THREEMA_DC_LABEL.equals(label)) {
                logger.warn("Ignoring new data channel (wrong label).");
                return;
            }
            // Fire data channel event
            PeerConnectionWrapper.this.listener.onDataChannel(dc);
        }
    }

    @AnyThread
    public long getMaxMessageSize() {
        // Sigh... still not supported by libwebrtc, so fallback to a
        // well-known (and, frankly, terribly small) value.
        return 64 * 1024;
    }

    /**
     * Log connection status info to the Android log.
     */
    @AnyThread
    private synchronized void logStatus() {
        logger.debug("*** CONNECTION STATUS");
        logger.debug("Aggregated state: {}", this.state);
        logger.debug("ICE connection state: {}", this.pc.iceConnectionState());
        logger.debug("ICE gathering state: {}", this.pc.iceGatheringState());
        logger.debug("Signaling state: {}", this.pc.signalingState());
        logger.debug("*** END CONNECTION STATUS");
    }
}

19 Source : WebDiagnosticsActivity.java
with GNU Affero General Public License v3.0
from threema-ch

@SuppressWarnings("FieldCanBeLocal")
@UiThread
public clreplaced WebDiagnosticsActivity extends ThreemaToolbarActivity implements TextEntryDialog.TextEntryDialogClickListener {

    private static final Logger logger = LoggerFactory.getLogger(WebDiagnosticsActivity.clreplaced);

    private static final String DIALOG_TAG_SEND_VOIP_DEBUG = "svd";

    // Config
    private static final String WS_HOST = "saltyrtc-ee.threema.ch";

    private static final String WS_BASE_URL = "wss://" + WS_HOST;

    private static final String WS_PATH = "ffffffffffffffff000000000000eeeeeeee000000000000ffffffffffffffff";

    private static final String WS_PROTOCOL = "v1.saltyrtc.org";

    private static final int WS_CONNECT_TIMEOUT_MS = 10000;

    private static final int WS_TEST_TIMEOUT_MS = WS_CONNECT_TIMEOUT_MS + 3000;

    private static final int RTC_TEST_TIMEOUT_MS = 12000;

    // Threema services
    @Nullable
    private ContactService contactService;

    // Views
    @Nullable
    private ProgressBar progressBar;

    @Nullable
    private TextView introText;

    @Nullable
    private TextView doneText;

    @Nullable
    private Button copyButton;

    @Nullable
    private Button sendButton;

    @Nullable
    private View footerButtons;

    // String that will be copied to clipboard
    @Nullable
    private String clipboardString;

    // Event logging
    @NonNull
    private final List<String> eventLog = new ArrayList<>();

    @Nullable
    private ArrayAdapter<String> adapter;

    private long startTime = 0;

    // Websocket
    @Nullable
    private WebSocket ws;

    private boolean wsDone = false;

    // WebRTC
    @Nullable
    private PeerConnection pc;

    @Nullable
    private PeerConnectionFactory pcFactory;

    private final AtomicInteger candidateCount = new AtomicInteger(0);

    private boolean rtcDone = false;

    // Executor service that should be used for running creation / destruction
    // of the peer connection and related objects.
    @Nullable
    private ScheduledExecutorService webrtcExecutor;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        logger.trace("onCreate");
        super.onCreate(savedInstanceState);
        final ActionBar actionBar = getSupportActionBar();
        if (actionBar != null) {
            actionBar.setDisplayHomeAsUpEnabled(true);
            actionBar.setreplacedle(R.string.webclient_diagnostics);
        }
    }

    @Override
    protected boolean initActivity(Bundle savedInstanceState) {
        logger.trace("initActivity");
        if (!super.initActivity(savedInstanceState)) {
            return false;
        }
        // Initialize services
        try {
            this.contactService = this.serviceManager.getContactService();
        } catch (MasterKeyLockedException | FileSystemNotPresentException e) {
            logger.error("Could not initialize services", e);
        }
        // Get view references
        this.progressBar = findViewById(R.id.webclient_diagnostics_loading);
        this.introText = findViewById(R.id.webclient_diagnostics_intro);
        this.doneText = findViewById(R.id.webclient_diagnostics_done);
        this.copyButton = findViewById(R.id.webclient_diagnostics_copy_button);
        this.sendButton = findViewById(R.id.webclient_diagnostics_send_button);
        this.footerButtons = findViewById(R.id.webclient_diagnostics_footer_buttons);
        // Wire up start button
        final Button startButton = findViewById(R.id.webclient_diagnostics_start);
        startButton.setOnClickListener(view -> {
            startButton.setVisibility(View.GONE);
            WebDiagnosticsActivity.this.startTests();
        });
        // Wire up copy button
        replacedert this.copyButton != null;
        this.copyButton.setOnClickListener(view -> {
            if (!TestUtil.empty(this.clipboardString)) {
                WebDiagnosticsActivity.this.copyToClipboard(this.clipboardString);
            }
        });
        // Wire up send button
        replacedert this.sendButton != null;
        this.sendButton.setOnClickListener(view -> {
            if (!TestUtil.empty(this.clipboardString)) {
                WebDiagnosticsActivity.this.prepareSendToSupport();
            }
        });
        // Initialize event log
        final ListView eventLog = findViewById(R.id.webclient_diagnostics_event_log);
        this.adapter = new ArrayAdapter<>(this, R.layout.item_webrtc_debug_list, this.eventLog);
        eventLog.setAdapter(this.adapter);
        return true;
    }

    @Override
    public int getLayoutResource() {
        return R.layout.activity_webclient_debug;
    }

    @Override
    protected void onStart() {
        logger.trace("onStart");
        this.webrtcExecutor = Executors.newSingleThreadScheduledExecutor();
        super.onStart();
    }

    @Override
    protected void onStop() {
        logger.trace("onStop");
        this.cleanup();
        super.onStop();
    }

    @AnyThread
    private void resetStartTime() {
        this.startTime = System.nanoTime();
    }

    @AnyThread
    private void addLogSeparator() {
        this.addToLog("----------------", false);
    }

    @AnyThread
    private void addToLog(final String value, boolean timestamp) {
        final long elapsedNs = System.nanoTime() - this.startTime;
        final String logLine = timestamp ? String.format("+%sms %s", elapsedNs / 1000 / 1000, value) : value;
        this.clipboardString += logLine + "\n";
        RuntimeUtil.runOnUiThread(() -> {
            synchronized (WebDiagnosticsActivity.this.eventLog) {
                logger.info(logLine);
                WebDiagnosticsActivity.this.eventLog.add(logLine);
                if (WebDiagnosticsActivity.this.adapter != null) {
                    WebDiagnosticsActivity.this.adapter.notifyDataSetChanged();
                }
            }
        });
    }

    @AnyThread
    private void addToLog(final String value) {
        this.addToLog(value, true);
    }

    @UiThread
    private void copyToClipboard(@NonNull String text) {
        final ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
        if (clipboard != null) {
            final String label = getString(R.string.webclient_diagnostics);
            final ClipData clip = ClipData.newPlainText(label, text);
            clipboard.setPrimaryClip(clip);
            Toast.makeText(getApplicationContext(), getString(R.string.voip_webrtc_debug_copied), Toast.LENGTH_LONG).show();
        }
    }

    private void prepareSendToSupport() {
        TextEntryDialog dialog = TextEntryDialog.newInstance(R.string.send_to_support, R.string.enter_description, R.string.send, R.string.cancel, 5, 3000, 1);
        dialog.show(getSupportFragmentManager(), DIALOG_TAG_SEND_VOIP_DEBUG);
    }

    @SuppressLint("StaticFieldLeak")
    private void sendToSupport(@NonNull String caption) {
        final MessageService messageService;
        try {
            messageService = serviceManager.getMessageService();
        } catch (ThreemaException e) {
            logger.error("Exception", e);
            return;
        }
        if (this.contactService == null || messageService == null) {
            return;
        }
        new AsyncTask<Void, Void, ContactMessageReceiver>() {

            @Override
            protected ContactMessageReceiver doInBackground(Void... voids) {
                try {
                    final ContactModel contactModel = contactService.getOrCreateByIdenreplacedy(THREEMA_SUPPORT_IDENreplacedY, true);
                    return contactService.createReceiver(contactModel);
                } catch (Exception e) {
                    return null;
                }
            }

            @Override
            protected void onPostExecute(ContactMessageReceiver messageReceiver) {
                try {
                    messageService.sendText(clipboardString + "\n---\n" + caption + "\n---\n" + ConfigUtils.getDeviceInfo(WebDiagnosticsActivity.this, false) + "\n" + "Threema " + ConfigUtils.getFullAppVersion(WebDiagnosticsActivity.this) + "\n" + getMyIdenreplacedy(), messageReceiver);
                    Toast.makeText(getApplicationContext(), R.string.message_sent, Toast.LENGTH_LONG).show();
                    finish();
                    return;
                } catch (Exception e1) {
                    logger.error("Exception", e1);
                }
                Toast.makeText(getApplicationContext(), R.string.an_error_occurred, Toast.LENGTH_LONG).show();
            }
        }.execute();
    }

    @UiThread
    private void startTests() {
        logger.info("*** Starting Threema Web Diagnostics Test");
        this.eventLog.clear();
        this.clipboardString = "";
        this.resetStartTime();
        this.addToLog("Starting Threema Web Diagnostics...", false);
        // Update UI visibility
        replacedert this.progressBar != null;
        this.progressBar.setVisibility(View.VISIBLE);
        replacedert this.introText != null;
        this.introText.setVisibility(View.GONE);
        replacedert this.doneText != null;
        this.doneText.setVisibility(View.GONE);
        replacedert this.footerButtons != null;
        this.footerButtons.setVisibility(View.GONE);
        // Print connectivity info
        this.queryConnectivityInfo();
        // Start with WebSocket test
        this.startWsTest();
    }

    @UiThread
    private void queryConnectivityInfo() {
        final Context appContext = getApplicationContext();
        final ConnectivityManager connectivityManager = (ConnectivityManager) appContext.getSystemService(Context.CONNECTIVITY_SERVICE);
        this.addLogSeparator();
        if (Build.VERSION.SDK_INT >= 21) {
            // Ignore Android 4
            // Add available networks
            final Network[] networks = connectivityManager.getAllNetworks();
            this.addToLog("Networks (" + networks.length + "):", false);
            for (Network network : networks) {
                final NetworkInfo info = connectivityManager.getNetworkInfo(network);
                final String typeName = info.getTypeName();
                final String fullType = info.getSubtypeName().isEmpty() ? typeName : typeName + "/" + info.getSubtypeName();
                final String detailedState = info.getDetailedState().toString();
                final String failover = "failover=" + info.isFailover();
                final String available = "available=" + info.isAvailable();
                final String roaming = "roaming=" + info.isRoaming();
                this.addToLog("- " + fullType + ", " + detailedState + ", " + failover + ", " + available + ", " + roaming, false);
            }
        } else {
            this.addToLog("API level " + Build.VERSION.SDK_INT + ", ignoring network info");
        }
        this.addLogSeparator();
        try {
            final List<String> addresses = new ArrayList<>();
            for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements(); ) {
                final NetworkInterface intf = en.nextElement();
                for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements(); ) {
                    final InetAddress inetAddress = enumIpAddr.nextElement();
                    if (!inetAddress.isLoopbackAddress()) {
                        final String addr = inetAddress.getHostAddress();
                        if (inetAddress.isLinkLocalAddress()) {
                            addresses.add(addr + " [link-local]");
                        } else {
                            addresses.add(addr);
                        }
                    }
                }
            }
            Collections.sort(addresses);
            this.addToLog("Non-loopback interfaces (" + addresses.size() + "):", false);
            for (String addr : addresses) {
                this.addToLog("- " + addr, false);
            }
        } catch (SocketException e) {
            this.addToLog("Socket exception when enumerating network interfaces: " + e.toString());
        }
    }

    /**
     * Start the WebSocket test.
     */
    @UiThread
    private synchronized void startWsTest() {
        this.wsDone = false;
        final Handler handler = new Handler();
        handler.postDelayed(() -> {
            if (!wsDone) {
                WebDiagnosticsActivity.this.failWs("WS test timed out");
            }
        }, WS_TEST_TIMEOUT_MS);
        RuntimeUtil.runInAsyncTask(() -> {
            final boolean success = WebDiagnosticsActivity.this.testWebsocket();
            if (!success) {
                addToLog("Initializing WebSocket test failed.");
            }
        });
    }

    /**
     * Start the WebRTC test.
     */
    @UiThread
    private synchronized void startRtcTest() {
        this.rtcDone = false;
        this.candidateCount.set(0);
        final Handler handler = new Handler();
        handler.postDelayed(() -> {
            if (!rtcDone) {
                WebDiagnosticsActivity.this.addToLog("WebRTC test timed out");
                WebDiagnosticsActivity.this.onRtcComplete(this.candidateCount.get() > 0);
            }
        }, RTC_TEST_TIMEOUT_MS);
        RuntimeUtil.runInAsyncTask(() -> {
            final boolean success = WebDiagnosticsActivity.this.testWebRTC();
            if (!success) {
                addToLog("Initializing WebRTC test failed.");
            }
        });
    }

    /**
     * Initialize the WebSocket tests.
     *
     * If something during the initialization fails, return false.
     */
    @AnyThread
    private boolean testWebsocket() {
        this.addLogSeparator();
        this.resetStartTime();
        this.addToLog("Starting WS tests");
        // Get configuration
        // Note: Below needs to be kept in sync with how dual stack mode is applied to the
        // SaltyRTC WebSocket code.
        replacedert this.preferenceService != null;
        DualStackMode dualStackMode = DualStackMode.BOTH;
        if (!this.preferenceService.allowWebrtcIpv6()) {
            dualStackMode = DualStackMode.IPV4_ONLY;
        }
        this.addToLog("Setting: dualStackMode=" + dualStackMode.name());
        // Create WebSocket
        final String url = WS_BASE_URL + "/" + WS_PATH;
        logger.info("Connecting to " + url);
        try {
            this.ws = new WebSocketFactory().setConnectionTimeout(WS_CONNECT_TIMEOUT_MS).setSSLSocketFactory(ConfigUtils.getSSLSocketFactory(WS_HOST)).setVerifyHostname(true).setDualStackMode(dualStackMode).createSocket(url).addProtocol(WS_PROTOCOL).addListener(this.wsListener);
        } catch (IOException e) {
            this.failWs("IOException when creating WebSocket: " + e.getMessage(), e);
            return false;
        }
        // Connect
        try {
            this.addToLog("Connecting to WebSocket");
            replacedert this.ws != null;
            this.ws.connect();
        } catch (WebSocketException e) {
            this.failWs("WebSocketException when connecting: " + e.getMessage(), e);
            return false;
        }
        return true;
    }

    /**
     * Initialize the WebRTC tests.
     *
     * If something during the initialization fails, return false.
     */
    @AnyThread
    private boolean testWebRTC() {
        this.addLogSeparator();
        this.resetStartTime();
        this.addToLog("Starting WebRTC tests");
        // Get configuration
        replacedert this.preferenceService != null;
        final boolean allowIpv6 = this.preferenceService.allowWebrtcIpv6();
        this.addToLog("Setting: allowWebrtcIpv6=" + allowIpv6);
        // Set up peer connection
        replacedert this.webrtcExecutor != null;
        this.webrtcExecutor.execute(() -> {
            WebRTCUtil.initializeAndroidGlobals(this.getApplicationContext());
            final PeerConnection.RTCConfiguration rtcConfig;
            try {
                rtcConfig = PeerConnectionWrapper.getRTCConfiguration(logger);
            } catch (Exception e) {
                this.addToLog("Could not get RTC configuration: " + e.getMessage());
                return;
            }
            rtcConfig.continualGatheringPolicy = PeerConnection.ContinualGatheringPolicy.GATHER_ONCE;
            this.addToLog("Using " + rtcConfig.iceServers.size() + " ICE servers:");
            for (PeerConnection.IceServer server : rtcConfig.iceServers) {
                this.addToLog("- " + server.urls.toString());
            }
            // Instantiate peer connection
            this.pcFactory = PeerConnectionWrapper.getPeerConnectionFactory();
            this.pc = this.pcFactory.createPeerConnection(rtcConfig, this.pcObserver);
            if (this.pc == null) {
                this.addToLog("Could not create peer connection");
                return;
            }
            // Create a data channel and a offer to kick off ICE gathering
            this.pc.createDataChannel("trigger-ice-gathering", new DataChannel.Init());
            this.pc.createOffer(this.sdpObserver, new MediaConstraints());
        });
        return true;
    }

    private final WebSocketListener wsListener = new DefaultNoopWebSocketListener() {

        // State changes
        @Override
        public void onStateChanged(WebSocket websocket, WebSocketState newState) {
            addToLog("WS state changed to " + newState.name());
        }

        @Override
        public void onConnected(WebSocket websocket, Map<String, List<String>> headers) {
            final Socket socket;
            try {
                socket = websocket.getConnectedSocket();
            } catch (WebSocketException e) {
                addToLog("Unable to retrieve connected socket: " + e.toString());
                return;
            }
            final String local = socket.getLocalAddress().getHostAddress() + ":" + socket.getLocalPort();
            final String remote = socket.getInetAddress().getHostAddress() + ":" + socket.getPort();
            addToLog("WS connected (" + local + " -> " + remote + ")");
        }

        @Override
        public void onDisconnected(WebSocket websocket, WebSocketFrame serverCloseFrame, WebSocketFrame clientCloseFrame, boolean closedByServer) {
            if (closedByServer) {
                int code = serverCloseFrame.getCloseCode();
                addToLog("WS closed by server with code " + code + " (" + CloseCode.explain(code) + ")");
            } else {
                int code = clientCloseFrame.getCloseCode();
                addToLog("WS closed by us with code " + code + " (" + CloseCode.explain(code) + ")");
            }
            final boolean success = !closedByServer && clientCloseFrame.getCloseCode() == 1000;
            WebDiagnosticsActivity.this.onWsComplete(success);
        }

        // Data
        @Override
        public void onTextMessage(WebSocket websocket, String text) {
            addToLog("WS received text message, aborting");
            websocket.disconnect();
        }

        @Override
        public void onTextMessage(WebSocket websocket, byte[] data) {
            addToLog("WS received text message, aborting");
            websocket.disconnect();
        }

        @Override
        public void onBinaryMessage(WebSocket websocket, byte[] binary) {
            addToLog("WS received " + binary.length + " bytes");
            // This should be the server-hello message
            // Validate length
            if (binary.length < 81) {
                addToLog("Invalid message length: " + binary.length);
                websocket.disconnect(1000);
                return;
            }
            // Wrap message
            final Box box = new Box(ByteBuffer.wrap(binary), SignalingChannelNonce.TOTAL_LENGTH);
            // Validate nonce
            final SignalingChannelNonce nonce = new SignalingChannelNonce(ByteBuffer.wrap(box.getNonce()));
            if (nonce.getSource() != 0) {
                addToLog("Invalid nonce source: " + nonce.getSource());
                websocket.disconnect(1000);
                return;
            }
            if (nonce.getDestination() != 0) {
                addToLog("Invalid nonce destination: " + nonce.getDestination());
                websocket.disconnect(1000);
                return;
            }
            if (nonce.getOverflow() != 0) {
                addToLog("Invalid nonce overflow: " + nonce.getOverflow());
                websocket.disconnect(1000);
                return;
            }
            // Validate data
            // Data should start with 0x82 (fixmap with 2 entries) followed by a string
            // with either the value "type" or "key".
            final byte[] data = box.getData();
            short byte1 = UnsignedHelper.readUnsignedByte(data[0]);
            short byte2 = UnsignedHelper.readUnsignedByte(data[1]);
            short byte3 = UnsignedHelper.readUnsignedByte(data[2]);
            short byte4 = UnsignedHelper.readUnsignedByte(data[3]);
            short byte5 = UnsignedHelper.readUnsignedByte(data[4]);
            short byte6 = UnsignedHelper.readUnsignedByte(data[5]);
            if (byte1 != 0x82) {
                addToLog("Invalid data (does not start with 0x82)");
                websocket.disconnect(1000);
                return;
            }
            if (byte2 == 0xa3 && byte3 == 'k' && byte4 == 'e' && byte5 == 'y') {
                addToLog("Received server-hello message!");
            } else if (byte2 == 0xa4 && byte3 == 't' && byte4 == 'y' && byte5 == 'p' && byte6 == 'e') {
                addToLog("Received server-hello message!");
            } else {
                addToLog("Received invalid message (bad data)");
            }
            websocket.disconnect(1000);
        }

        // Errors
        @Override
        public void onConnectError(WebSocket websocket, WebSocketException cause) {
            WebDiagnosticsActivity.this.failWs("WS connect error: " + cause.toString());
        }

        @Override
        public void onError(WebSocket websocket, WebSocketException cause) {
            WebDiagnosticsActivity.this.failWs("WS error: " + cause.toString());
        }

        @Override
        public void onFrameError(WebSocket websocket, WebSocketException cause, WebSocketFrame frame) {
            WebDiagnosticsActivity.this.failWs("WS frame error: " + cause.toString());
        }

        @Override
        public void onMessageError(WebSocket websocket, WebSocketException cause, List<WebSocketFrame> frames) {
            WebDiagnosticsActivity.this.failWs("WS message error: " + cause.toString());
        }

        @Override
        public void onSendError(WebSocket websocket, WebSocketException cause, WebSocketFrame frame) {
            WebDiagnosticsActivity.this.failWs("WS send error: " + cause.toString());
        }
    };

    private final PeerConnection.Observer pcObserver = new DefaultNoopPeerConnectionObserver() {

        @Override
        public void onSignalingChange(PeerConnection.SignalingState signalingState) {
            if (WebDiagnosticsActivity.this.pc == null) {
                return;
            }
            WebDiagnosticsActivity.this.addToLog("PC signaling state change to " + signalingState.name());
        }

        @Override
        public void onIceConnectionChange(PeerConnection.IceConnectionState iceConnectionState) {
            if (WebDiagnosticsActivity.this.pc == null) {
                return;
            }
            WebDiagnosticsActivity.this.addToLog("ICE connection state change to " + iceConnectionState.name());
            switch(iceConnectionState) {
                case NEW:
                case CHECKING:
                case CONNECTED:
                case COMPLETED:
                case DISCONNECTED:
                case CLOSED:
                    break;
                case FAILED:
                    WebDiagnosticsActivity.this.failRtc("ICE failed");
                    break;
            }
        }

        @Override
        public void onIceConnectionReceivingChange(boolean b) {
            if (WebDiagnosticsActivity.this.pc == null) {
                return;
            }
            WebDiagnosticsActivity.this.addToLog("ICE connection receiving: " + b);
        }

        @Override
        public void onIceGatheringChange(PeerConnection.IceGatheringState iceGatheringState) {
            if (WebDiagnosticsActivity.this.pc == null) {
                return;
            }
            WebDiagnosticsActivity.this.addToLog("ICE gathering state change to " + iceGatheringState.name());
            switch(iceGatheringState) {
                case NEW:
                case GATHERING:
                    break;
                case COMPLETE:
                    WebDiagnosticsActivity.this.onRtcComplete(true);
                    break;
            }
        }

        @Override
        public void onIceCandidate(IceCandidate candidate) {
            if (WebDiagnosticsActivity.this.pc == null) {
                return;
            }
            WebDiagnosticsActivity.this.addToLog(WebRTCUtil.iceCandidateToString(candidate));
            if (candidate == null) {
                WebDiagnosticsActivity.this.onRtcComplete(true);
            } else {
                WebDiagnosticsActivity.this.candidateCount.incrementAndGet();
                WebDiagnosticsActivity.this.addToLog(WebRTCUtil.iceCandidateToString(candidate));
            }
        }

        @Override
        public void onIceCandidatesRemoved(IceCandidate[] iceCandidates) {
            if (WebDiagnosticsActivity.this.pc == null) {
                return;
            }
            for (IceCandidate candidate : iceCandidates) {
                WebDiagnosticsActivity.this.addToLog("Removed: " + WebRTCUtil.iceCandidateToString(candidate));
            }
        }

        @Override
        public void onRenegotiationNeeded() {
            if (WebDiagnosticsActivity.this.pc == null) {
                return;
            }
            WebDiagnosticsActivity.this.addToLog("ICE renegotiation needed");
        }
    };

    private final SdpObserver sdpObserver = new SdpObserver() {

        @Override
        public void onCreateSuccess(SessionDescription sessionDescription) {
            WebDiagnosticsActivity.this.addToLog("SDP create success");
            replacedert WebDiagnosticsActivity.this.webrtcExecutor != null;
            WebDiagnosticsActivity.this.webrtcExecutor.execute(() -> {
                if (WebDiagnosticsActivity.this.pc != null) {
                    WebDiagnosticsActivity.this.pc.setLocalDescription(this, sessionDescription);
                } else {
                    WebDiagnosticsActivity.this.failRtc("Could not set local description: Peer connection is null");
                }
            });
        }

        @Override
        public void onSetSuccess() {
            WebDiagnosticsActivity.this.addToLog("SDP set success");
        }

        @Override
        public void onCreateFailure(String s) {
            WebDiagnosticsActivity.this.addToLog("SDP create failure");
            WebDiagnosticsActivity.this.failRtc("Could not create SDP: " + s);
        }

        @Override
        public void onSetFailure(String s) {
            WebDiagnosticsActivity.this.addToLog("SDP set failure");
            WebDiagnosticsActivity.this.failRtc("Could not set SDP: " + s);
        }
    };

    @AnyThread
    private void failWs(@NonNull String message) {
        this.failWs(message, null);
    }

    @AnyThread
    private void failWs(@NonNull String message, @Nullable Exception e) {
        if (e != null) {
            logger.error("WS Exception", e);
        }
        this.addToLog(message);
        this.onWsComplete(false);
    }

    @AnyThread
    private void failRtc(@NonNull String message) {
        this.addToLog(message);
        this.onRtcComplete(false);
    }

    /**
     * Test is complete.
     */
    @AnyThread
    private void onWsComplete(boolean success) {
        this.addToLog("WS tests complete (success=" + success + ")");
        this.cleanupWs();
        this.wsDone = true;
        if (success) {
            this.runOnUiThread(this::startRtcTest);
        } else {
            RuntimeUtil.runOnUiThread(this::onComplete);
        }
    }

    /**
     * Test is complete.
     */
    @AnyThread
    private void onRtcComplete(boolean success) {
        this.addToLog("WebRTC tests complete (success=" + success + ")");
        this.cleanupRtc();
        this.rtcDone = true;
        RuntimeUtil.runOnUiThread(this::onComplete);
    }

    @UiThread
    private void onComplete() {
        final Handler handler = new Handler();
        handler.postDelayed(() -> {
            logger.info("*** Finished Threema Web Diagnostics Test");
            this.addLogSeparator();
            this.addToLog("Done.", false);
            RuntimeUtil.runOnUiThread(() -> {
                replacedert progressBar != null;
                progressBar.setVisibility(View.GONE);
                replacedert introText != null;
                introText.setVisibility(View.GONE);
                replacedert doneText != null;
                doneText.setVisibility(View.VISIBLE);
                replacedert footerButtons != null;
                footerButtons.setVisibility(View.VISIBLE);
            });
        }, 200);
    }

    @AnyThread
    private synchronized void cleanupWs() {
        logger.trace("cleanupWs");
        if (this.ws != null) {
            this.ws.clearListeners();
            this.ws.disconnect();
            this.ws = null;
        }
    }

    @AnyThread
    private synchronized void cleanupRtc() {
        logger.trace("cleanupRtc");
        if (this.pc != null) {
            replacedert this.webrtcExecutor != null;
            this.webrtcExecutor.execute(this.pc::dispose);
            this.pc = null;
        }
        if (this.pcFactory != null) {
            replacedert this.webrtcExecutor != null;
            this.webrtcExecutor.execute(this.pcFactory::dispose);
            this.pcFactory = null;
        }
    }

    @AnyThread
    private synchronized void cleanup() {
        logger.info("Cleaning up resources");
        this.cleanupWs();
        this.cleanupRtc();
        if (this.webrtcExecutor != null) {
            this.webrtcExecutor.shutdown();
            try {
                if (!this.webrtcExecutor.awaitTermination(800, TimeUnit.MILLISECONDS)) {
                    this.webrtcExecutor.shutdownNow();
                }
            } catch (InterruptedException e) {
                this.webrtcExecutor.shutdownNow();
            }
            this.webrtcExecutor = null;
        }
    }

    @Override
    public void onYes(String tag, String text) {
        if (DIALOG_TAG_SEND_VOIP_DEBUG.equals(tag)) {
            sendToSupport(text);
        }
    }

    @Override
    public void onNo(String tag) {
    }

    @Override
    public void onNeutral(String tag) {
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // noinspection SwitchStatementWithTooFewBranches
        switch(item.gereplacedemId()) {
            case android.R.id.home:
                finish();
                return true;
        }
        return super.onOptionsItemSelected(item);
    }
}

19 Source : WebRTCWrapper.java
with GNU General Public License v3.0
from snikket-im

public clreplaced WebRTCWrapper {

    private static final String EXTENDED_LOGGING_TAG = WebRTCWrapper.clreplaced.getSimpleName();

    // we should probably keep this in sync with: https://github.com/signalapp/Signal-Android/blob/master/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java#L296
    private static final Set<String> HARDWARE_AEC_BLACKLIST = new ImmutableSet.Builder<String>().add("Pixel").add("Pixel XL").add("Moto G5").add("Moto G (5S) Plus").add("Moto G4").add("TA-1053").add("Mi A1").add("Mi A2").add(// Sony z5 compact
    "E5823").add("Redmi Note 5").add(// Fairphone FP2
    "FP2").add("MI 5").build();

    private static final int CAPTURING_RESOLUTION = 1920;

    private static final int CAPTURING_MAX_FRAME_RATE = 30;

    private final EventCallback eventCallback;

    private final AppRTCAudioManager.AudioManagerEvents audioManagerEvents = new AppRTCAudioManager.AudioManagerEvents() {

        @Override
        public void onAudioDeviceChanged(AppRTCAudioManager.AudioDevice selectedAudioDevice, Set<AppRTCAudioManager.AudioDevice> availableAudioDevices) {
            eventCallback.onAudioDeviceChanged(selectedAudioDevice, availableAudioDevices);
        }
    };

    private final Handler mainHandler = new Handler(Looper.getMainLooper());

    private VideoTrack localVideoTrack = null;

    private VideoTrack remoteVideoTrack = null;

    private final PeerConnection.Observer peerConnectionObserver = new PeerConnection.Observer() {

        @Override
        public void onSignalingChange(PeerConnection.SignalingState signalingState) {
            Log.d(EXTENDED_LOGGING_TAG, "onSignalingChange(" + signalingState + ")");
        // this is called after removeTrack or addTrack
        // and should then trigger a content-add or content-remove or something
        // https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/removeTrack
        }

        @Override
        public void onConnectionChange(PeerConnection.PeerConnectionState newState) {
            eventCallback.onConnectionChange(newState);
        }

        @Override
        public void onIceConnectionChange(PeerConnection.IceConnectionState iceConnectionState) {
        }

        @Override
        public void onSelectedCandidatePairChanged(CandidatePairChangeEvent event) {
            Log.d(Config.LOGTAG, "remote candidate selected: " + event.remote);
            Log.d(Config.LOGTAG, "local candidate selected: " + event.local);
        }

        @Override
        public void onIceConnectionReceivingChange(boolean b) {
        }

        @Override
        public void onIceGatheringChange(PeerConnection.IceGatheringState iceGatheringState) {
            Log.d(EXTENDED_LOGGING_TAG, "onIceGatheringChange(" + iceGatheringState + ")");
        }

        @Override
        public void onIceCandidate(IceCandidate iceCandidate) {
            eventCallback.onIceCandidate(iceCandidate);
        }

        @Override
        public void onIceCandidatesRemoved(IceCandidate[] iceCandidates) {
        }

        @Override
        public void onAddStream(MediaStream mediaStream) {
            Log.d(EXTENDED_LOGGING_TAG, "onAddStream(numAudioTracks=" + mediaStream.audioTracks.size() + ",numVideoTracks=" + mediaStream.videoTracks.size() + ")");
            final List<VideoTrack> videoTracks = mediaStream.videoTracks;
            if (videoTracks.size() > 0) {
                remoteVideoTrack = videoTracks.get(0);
                Log.d(Config.LOGTAG, "remote video track enabled?=" + remoteVideoTrack.enabled());
            } else {
                Log.d(Config.LOGTAG, "no remote video tracks found");
            }
        }

        @Override
        public void onRemoveStream(MediaStream mediaStream) {
        }

        @Override
        public void onDataChannel(DataChannel dataChannel) {
        }

        @Override
        public void onRenegotiationNeeded() {
        }

        @Override
        public void onAddTrack(RtpReceiver rtpReceiver, MediaStream[] mediaStreams) {
            final MediaStreamTrack track = rtpReceiver.track();
            Log.d(EXTENDED_LOGGING_TAG, "onAddTrack(kind=" + (track == null ? "null" : track.kind()) + ",numMediaStreams=" + mediaStreams.length + ")");
        }

        @Override
        public void onTrack(RtpTransceiver transceiver) {
            Log.d(EXTENDED_LOGGING_TAG, "onTrack(mid=" + transceiver.getMid() + ",media=" + transceiver.getMediaType() + ")");
        }
    };

    @Nullable
    private PeerConnection peerConnection = null;

    private AudioTrack localAudioTrack = null;

    private AppRTCAudioManager appRTCAudioManager = null;

    private ToneManager toneManager = null;

    private Context context = null;

    private EglBase eglBase = null;

    private CapturerChoice capturerChoice;

    WebRTCWrapper(final EventCallback eventCallback) {
        this.eventCallback = eventCallback;
    }

    private static void dispose(final PeerConnection peerConnection) {
        try {
            peerConnection.dispose();
        } catch (final IllegalStateException e) {
            Log.e(Config.LOGTAG, "unable to dispose of peer connection", e);
        }
    }

    @Nullable
    private static CapturerChoice of(CameraEnumerator enumerator, final String deviceName, Set<String> availableCameras) {
        final CameraVideoCapturer capturer = enumerator.createCapturer(deviceName, null);
        if (capturer == null) {
            return null;
        }
        final ArrayList<CameraEnumerationAndroid.CaptureFormat> choices = new ArrayList<>(enumerator.getSupportedFormats(deviceName));
        Collections.sort(choices, (a, b) -> b.width - a.width);
        for (final CameraEnumerationAndroid.CaptureFormat captureFormat : choices) {
            if (captureFormat.width <= CAPTURING_RESOLUTION) {
                return new CapturerChoice(capturer, captureFormat, availableCameras);
            }
        }
        return null;
    }

    public void setup(final XmppConnectionService service, final AppRTCAudioManager.SpeakerPhonePreference speakerPhonePreference) throws InitializationException {
        try {
            PeerConnectionFactory.initialize(PeerConnectionFactory.InitializationOptions.builder(service).createInitializationOptions());
        } catch (final UnsatisfiedLinkError e) {
            throw new InitializationException("Unable to initialize PeerConnectionFactory", e);
        }
        try {
            this.eglBase = EglBase.create();
        } catch (final RuntimeException e) {
            throw new InitializationException("Unable to create EGL base", e);
        }
        this.context = service;
        this.toneManager = service.getJingleConnectionManager().toneManager;
        mainHandler.post(() -> {
            appRTCAudioManager = AppRTCAudioManager.create(service, speakerPhonePreference);
            toneManager.setAppRtcAudioManagerHasControl(true);
            appRTCAudioManager.start(audioManagerEvents);
            eventCallback.onAudioDeviceChanged(appRTCAudioManager.getSelectedAudioDevice(), appRTCAudioManager.getAudioDevices());
        });
    }

    synchronized void initializePeerConnection(final Set<Media> media, final List<PeerConnection.IceServer> iceServers) throws InitializationException {
        Preconditions.checkState(this.eglBase != null);
        Preconditions.checkNotNull(media);
        Preconditions.checkArgument(media.size() > 0, "media can not be empty when initializing peer connection");
        final boolean setUseHardwareAcousticEchoCanceler = WebRtcAudioEffects.creplacedeAcousticEchoCanceler() && !HARDWARE_AEC_BLACKLIST.contains(Build.MODEL);
        Log.d(Config.LOGTAG, String.format("setUseHardwareAcousticEchoCanceler(%s) model=%s", setUseHardwareAcousticEchoCanceler, Build.MODEL));
        PeerConnectionFactory peerConnectionFactory = PeerConnectionFactory.builder().setVideoDecoderFactory(new DefaultVideoDecoderFactory(eglBase.getEglBaseContext())).setVideoEncoderFactory(new DefaultVideoEncoderFactory(eglBase.getEglBaseContext(), true, true)).setAudioDeviceModule(JavaAudioDeviceModule.builder(context).setUseHardwareAcousticEchoCanceler(setUseHardwareAcousticEchoCanceler).createAudioDeviceModule()).createPeerConnectionFactory();
        final MediaStream stream = peerConnectionFactory.createLocalMediaStream("my-media-stream");
        final Optional<CapturerChoice> optionalCapturerChoice = media.contains(Media.VIDEO) ? getVideoCapturer() : Optional.absent();
        if (optionalCapturerChoice.isPresent()) {
            this.capturerChoice = optionalCapturerChoice.get();
            final CameraVideoCapturer capturer = this.capturerChoice.cameraVideoCapturer;
            final VideoSource videoSource = peerConnectionFactory.createVideoSource(false);
            SurfaceTextureHelper surfaceTextureHelper = SurfaceTextureHelper.create("webrtc", eglBase.getEglBaseContext());
            capturer.initialize(surfaceTextureHelper, requireContext(), videoSource.getCapturerObserver());
            Log.d(Config.LOGTAG, String.format("start capturing at %dx%d@%d", capturerChoice.captureFormat.width, capturerChoice.captureFormat.height, capturerChoice.getFrameRate()));
            capturer.startCapture(capturerChoice.captureFormat.width, capturerChoice.captureFormat.height, capturerChoice.getFrameRate());
            this.localVideoTrack = peerConnectionFactory.createVideoTrack("my-video-track", videoSource);
            stream.addTrack(this.localVideoTrack);
        }
        if (media.contains(Media.AUDIO)) {
            // set up audio track
            final AudioSource audioSource = peerConnectionFactory.createAudioSource(new MediaConstraints());
            this.localAudioTrack = peerConnectionFactory.createAudioTrack("my-audio-track", audioSource);
            stream.addTrack(this.localAudioTrack);
        }
        final PeerConnection.RTCConfiguration rtcConfig = new PeerConnection.RTCConfiguration(iceServers);
        // XEP-0176 doesn't support tcp
        rtcConfig.tcpCandidatePolicy = PeerConnection.TcpCandidatePolicy.DISABLED;
        rtcConfig.continualGatheringPolicy = PeerConnection.ContinualGatheringPolicy.GATHER_CONTINUALLY;
        final PeerConnection peerConnection = peerConnectionFactory.createPeerConnection(rtcConfig, peerConnectionObserver);
        if (peerConnection == null) {
            throw new InitializationException("Unable to create PeerConnection");
        }
        peerConnection.addStream(stream);
        peerConnection.setAudioPlayout(true);
        peerConnection.setAudioRecording(true);
        this.peerConnection = peerConnection;
    }

    synchronized void close() {
        final PeerConnection peerConnection = this.peerConnection;
        final CapturerChoice capturerChoice = this.capturerChoice;
        final AppRTCAudioManager audioManager = this.appRTCAudioManager;
        final EglBase eglBase = this.eglBase;
        if (peerConnection != null) {
            dispose(peerConnection);
            this.peerConnection = null;
        }
        if (audioManager != null) {
            toneManager.setAppRtcAudioManagerHasControl(false);
            mainHandler.post(audioManager::stop);
        }
        this.localVideoTrack = null;
        this.remoteVideoTrack = null;
        if (capturerChoice != null) {
            try {
                capturerChoice.cameraVideoCapturer.stopCapture();
            } catch (InterruptedException e) {
                Log.e(Config.LOGTAG, "unable to stop capturing");
            }
        }
        if (eglBase != null) {
            eglBase.release();
            this.eglBase = null;
        }
    }

    synchronized void verifyClosed() {
        if (this.peerConnection != null || this.eglBase != null || this.localVideoTrack != null || this.remoteVideoTrack != null) {
            final IllegalStateException e = new IllegalStateException("WebRTCWrapper hasn't been closed properly");
            Log.e(Config.LOGTAG, "verifyClosed() failed. Going to throw", e);
            throw e;
        }
    }

    boolean isCameraSwitchable() {
        final CapturerChoice capturerChoice = this.capturerChoice;
        return capturerChoice != null && capturerChoice.availableCameras.size() > 1;
    }

    boolean isFrontCamera() {
        final CapturerChoice capturerChoice = this.capturerChoice;
        return capturerChoice == null || capturerChoice.isFrontCamera;
    }

    ListenableFuture<Boolean> switchCamera() {
        final CapturerChoice capturerChoice = this.capturerChoice;
        if (capturerChoice == null) {
            return Futures.immediateFailedFuture(new IllegalStateException("CameraCapturer has not been initialized"));
        }
        final SettableFuture<Boolean> future = SettableFuture.create();
        capturerChoice.cameraVideoCapturer.switchCamera(new CameraVideoCapturer.CameraSwitchHandler() {

            @Override
            public void onCameraSwitchDone(boolean isFrontCamera) {
                capturerChoice.isFrontCamera = isFrontCamera;
                future.set(isFrontCamera);
            }

            @Override
            public void onCameraSwitchError(final String message) {
                future.setException(new IllegalStateException(String.format("Unable to switch camera %s", message)));
            }
        });
        return future;
    }

    boolean isMicrophoneEnabled() {
        final AudioTrack audioTrack = this.localAudioTrack;
        if (audioTrack == null) {
            throw new IllegalStateException("Local audio track does not exist (yet)");
        }
        try {
            return audioTrack.enabled();
        } catch (final IllegalStateException e) {
            // sometimes UI might still be rendering the buttons when a background thread has already ended the call
            return false;
        }
    }

    boolean setMicrophoneEnabled(final boolean enabled) {
        final AudioTrack audioTrack = this.localAudioTrack;
        if (audioTrack == null) {
            throw new IllegalStateException("Local audio track does not exist (yet)");
        }
        try {
            audioTrack.setEnabled(enabled);
            return true;
        } catch (final IllegalStateException e) {
            Log.d(Config.LOGTAG, "unable to toggle microphone", e);
            // ignoring race condition in case MediaStreamTrack has been disposed
            return false;
        }
    }

    boolean isVideoEnabled() {
        final VideoTrack videoTrack = this.localVideoTrack;
        if (videoTrack == null) {
            throw new IllegalStateException("Local video track does not exist");
        }
        return videoTrack.enabled();
    }

    void setVideoEnabled(final boolean enabled) {
        final VideoTrack videoTrack = this.localVideoTrack;
        if (videoTrack == null) {
            throw new IllegalStateException("Local video track does not exist");
        }
        videoTrack.setEnabled(enabled);
    }

    ListenableFuture<SessionDescription> createOffer() {
        return Futures.transformAsync(getPeerConnectionFuture(), peerConnection -> {
            final SettableFuture<SessionDescription> future = SettableFuture.create();
            peerConnection.createOffer(new CreateSdpObserver() {

                @Override
                public void onCreateSuccess(SessionDescription sessionDescription) {
                    future.set(sessionDescription);
                }

                @Override
                public void onCreateFailure(String s) {
                    future.setException(new IllegalStateException("Unable to create offer: " + s));
                }
            }, new MediaConstraints());
            return future;
        }, MoreExecutors.directExecutor());
    }

    ListenableFuture<SessionDescription> createAnswer() {
        return Futures.transformAsync(getPeerConnectionFuture(), peerConnection -> {
            final SettableFuture<SessionDescription> future = SettableFuture.create();
            peerConnection.createAnswer(new CreateSdpObserver() {

                @Override
                public void onCreateSuccess(SessionDescription sessionDescription) {
                    future.set(sessionDescription);
                }

                @Override
                public void onCreateFailure(String s) {
                    future.setException(new IllegalStateException("Unable to create answer: " + s));
                }
            }, new MediaConstraints());
            return future;
        }, MoreExecutors.directExecutor());
    }

    ListenableFuture<Void> setLocalDescription(final SessionDescription sessionDescription) {
        Log.d(EXTENDED_LOGGING_TAG, "setting local description:");
        for (final String line : sessionDescription.description.split(eu.siacs.conversations.xmpp.jingle.SessionDescription.LINE_DIVIDER)) {
            Log.d(EXTENDED_LOGGING_TAG, line);
        }
        return Futures.transformAsync(getPeerConnectionFuture(), peerConnection -> {
            final SettableFuture<Void> future = SettableFuture.create();
            peerConnection.setLocalDescription(new SetSdpObserver() {

                @Override
                public void onSetSuccess() {
                    future.set(null);
                }

                @Override
                public void onSetFailure(final String s) {
                    future.setException(new IllegalArgumentException("unable to set local session description: " + s));
                }
            }, sessionDescription);
            return future;
        }, MoreExecutors.directExecutor());
    }

    ListenableFuture<Void> setRemoteDescription(final SessionDescription sessionDescription) {
        Log.d(EXTENDED_LOGGING_TAG, "setting remote description:");
        for (final String line : sessionDescription.description.split(eu.siacs.conversations.xmpp.jingle.SessionDescription.LINE_DIVIDER)) {
            Log.d(EXTENDED_LOGGING_TAG, line);
        }
        return Futures.transformAsync(getPeerConnectionFuture(), peerConnection -> {
            final SettableFuture<Void> future = SettableFuture.create();
            peerConnection.setRemoteDescription(new SetSdpObserver() {

                @Override
                public void onSetSuccess() {
                    future.set(null);
                }

                @Override
                public void onSetFailure(String s) {
                    future.setException(new IllegalArgumentException("unable to set remote session description: " + s));
                }
            }, sessionDescription);
            return future;
        }, MoreExecutors.directExecutor());
    }

    @Nonnull
    private ListenableFuture<PeerConnection> getPeerConnectionFuture() {
        final PeerConnection peerConnection = this.peerConnection;
        if (peerConnection == null) {
            return Futures.immediateFailedFuture(new IllegalStateException("initialize PeerConnection first"));
        } else {
            return Futures.immediateFuture(peerConnection);
        }
    }

    void addIceCandidate(IceCandidate iceCandidate) {
        requirePeerConnection().addIceCandidate(iceCandidate);
    }

    private CameraEnumerator getCameraEnumerator() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            return new Camera2Enumerator(requireContext());
        } else {
            return new Camera1Enumerator();
        }
    }

    private Optional<CapturerChoice> getVideoCapturer() {
        final CameraEnumerator enumerator = getCameraEnumerator();
        final Set<String> deviceNames = ImmutableSet.copyOf(enumerator.getDeviceNames());
        for (final String deviceName : deviceNames) {
            if (isFrontFacing(enumerator, deviceName)) {
                final CapturerChoice capturerChoice = of(enumerator, deviceName, deviceNames);
                if (capturerChoice == null) {
                    return Optional.absent();
                }
                capturerChoice.isFrontCamera = true;
                return Optional.of(capturerChoice);
            }
        }
        if (deviceNames.size() == 0) {
            return Optional.absent();
        } else {
            return Optional.fromNullable(of(enumerator, Iterables.get(deviceNames, 0), deviceNames));
        }
    }

    private static boolean isFrontFacing(final CameraEnumerator cameraEnumerator, final String deviceName) {
        try {
            return cameraEnumerator.isFrontFacing(deviceName);
        } catch (final NullPointerException e) {
            return false;
        }
    }

    public PeerConnection.PeerConnectionState getState() {
        return requirePeerConnection().connectionState();
    }

    EglBase.Context getEglBaseContext() {
        return this.eglBase.getEglBaseContext();
    }

    Optional<VideoTrack> getLocalVideoTrack() {
        return Optional.fromNullable(this.localVideoTrack);
    }

    Optional<VideoTrack> getRemoteVideoTrack() {
        return Optional.fromNullable(this.remoteVideoTrack);
    }

    private PeerConnection requirePeerConnection() {
        final PeerConnection peerConnection = this.peerConnection;
        if (peerConnection == null) {
            throw new PeerConnectionNotInitialized();
        }
        return peerConnection;
    }

    private Context requireContext() {
        final Context context = this.context;
        if (context == null) {
            throw new IllegalStateException("call setup first");
        }
        return context;
    }

    AppRTCAudioManager getAudioManager() {
        return appRTCAudioManager;
    }

    public interface EventCallback {

        void onIceCandidate(IceCandidate iceCandidate);

        void onConnectionChange(PeerConnection.PeerConnectionState newState);

        void onAudioDeviceChanged(AppRTCAudioManager.AudioDevice selectedAudioDevice, Set<AppRTCAudioManager.AudioDevice> availableAudioDevices);
    }

    private static abstract clreplaced SetSdpObserver implements SdpObserver {

        @Override
        public void onCreateSuccess(org.webrtc.SessionDescription sessionDescription) {
            throw new IllegalStateException("Not able to use SetSdpObserver");
        }

        @Override
        public void onCreateFailure(String s) {
            throw new IllegalStateException("Not able to use SetSdpObserver");
        }
    }

    private static abstract clreplaced CreateSdpObserver implements SdpObserver {

        @Override
        public void onSetSuccess() {
            throw new IllegalStateException("Not able to use CreateSdpObserver");
        }

        @Override
        public void onSetFailure(String s) {
            throw new IllegalStateException("Not able to use CreateSdpObserver");
        }
    }

    static clreplaced InitializationException extends Exception {

        private InitializationException(final String message, final Throwable throwable) {
            super(message, throwable);
        }

        private InitializationException(final String message) {
            super(message);
        }
    }

    public static clreplaced PeerConnectionNotInitialized extends IllegalStateException {

        private PeerConnectionNotInitialized() {
            super("initialize PeerConnection first");
        }
    }

    private static clreplaced CapturerChoice {

        private final CameraVideoCapturer cameraVideoCapturer;

        private final CameraEnumerationAndroid.CaptureFormat captureFormat;

        private final Set<String> availableCameras;

        private boolean isFrontCamera = false;

        CapturerChoice(CameraVideoCapturer cameraVideoCapturer, CameraEnumerationAndroid.CaptureFormat captureFormat, Set<String> cameras) {
            this.cameraVideoCapturer = cameraVideoCapturer;
            this.captureFormat = captureFormat;
            this.availableCameras = cameras;
        }

        int getFrameRate() {
            return Math.max(captureFormat.framerate.min, Math.min(CAPTURING_MAX_FRAME_RATE, captureFormat.framerate.max));
        }
    }
}

19 Source : WebRTCWrapper.java
with GNU General Public License v3.0
from snikket-im

private PeerConnection requirePeerConnection() {
    final PeerConnection peerConnection = this.peerConnection;
    if (peerConnection == null) {
        throw new PeerConnectionNotInitialized();
    }
    return peerConnection;
}

19 Source : RemoteParticipant.java
with Apache License 2.0
from sergiopaniego

public void setPeerConnection(PeerConnection peerConnection) {
    this.peerConnection = peerConnection;
}

19 Source : PeersManager.java
with Apache License 2.0
from sergiopaniego

/**
 * Created by sergiopaniegoblanco on 18/02/2018.
 */
public clreplaced PeersManager {

    private PeerConnection localPeer;

    private PeerConnectionFactory peerConnectionFactory;

    private CustomWebSocketListener webSocketAdapter;

    private WebSocket webSocket;

    private LinearLayout views_container;

    private AudioTrack localAudioTrack;

    private VideoTrack localVideoTrack;

    private VideoRenderer localRenderer;

    private SurfaceViewRenderer localVideoView;

    private VideoCapturer videoGrabberAndroid;

    private VideoConferenceActivity activity;

    public PeersManager(VideoConferenceActivity activity, LinearLayout views_container, SurfaceViewRenderer localVideoView) {
        this.views_container = views_container;
        this.localVideoView = localVideoView;
        this.activity = activity;
    }

    public PeerConnection getLocalPeer() {
        return localPeer;
    }

    public AudioTrack getLocalAudioTrack() {
        return localAudioTrack;
    }

    public VideoTrack getLocalVideoTrack() {
        return localVideoTrack;
    }

    public PeerConnectionFactory getPeerConnectionFactory() {
        return peerConnectionFactory;
    }

    public void setWebSocket(WebSocket webSocket) {
        this.webSocket = webSocket;
    }

    public CustomWebSocketListener getWebSocketAdapter() {
        return webSocketAdapter;
    }

    public void setWebSocketAdapter(CustomWebSocketListener webSocketAdapter) {
        this.webSocketAdapter = webSocketAdapter;
    }

    public WebSocket getWebSocket() {
        return webSocket;
    }

    public void start() {
        PeerConnectionFactory.initializeAndroidGlobals(activity, true);
        PeerConnectionFactory.Options options = new PeerConnectionFactory.Options();
        peerConnectionFactory = new PeerConnectionFactory(options);
        videoGrabberAndroid = createVideoGrabber();
        MediaConstraints constraints = new MediaConstraints();
        VideoSource videoSource = peerConnectionFactory.createVideoSource(videoGrabberAndroid);
        localVideoTrack = peerConnectionFactory.createVideoTrack("100", videoSource);
        AudioSource audioSource = peerConnectionFactory.createAudioSource(constraints);
        localAudioTrack = peerConnectionFactory.createAudioTrack("101", audioSource);
        if (videoGrabberAndroid != null) {
            videoGrabberAndroid.startCapture(1000, 1000, 30);
        }
        localRenderer = new VideoRenderer(localVideoView);
        localVideoTrack.addRenderer(localRenderer);
        MediaConstraints sdpConstraints = new MediaConstraints();
        sdpConstraints.mandatory.add(new MediaConstraints.KeyValuePair("offerToReceiveAudio", "true"));
        sdpConstraints.mandatory.add(new MediaConstraints.KeyValuePair("offerToReceiveVideo", "true"));
        createLocalPeerConnection(sdpConstraints);
    }

    private VideoCapturer createVideoGrabber() {
        VideoCapturer videoCapturer;
        videoCapturer = createCameraGrabber(new Camera1Enumerator(false));
        return videoCapturer;
    }

    private VideoCapturer createCameraGrabber(CameraEnumerator enumerator) {
        final String[] deviceNames = enumerator.getDeviceNames();
        for (String deviceName : deviceNames) {
            if (enumerator.isFrontFacing(deviceName)) {
                VideoCapturer videoCapturer = enumerator.createCapturer(deviceName, null);
                if (videoCapturer != null) {
                    return videoCapturer;
                }
            }
        }
        for (String deviceName : deviceNames) {
            if (!enumerator.isFrontFacing(deviceName)) {
                VideoCapturer videoCapturer = enumerator.createCapturer(deviceName, null);
                if (videoCapturer != null) {
                    return videoCapturer;
                }
            }
        }
        return null;
    }

    private void createLocalPeerConnection(MediaConstraints sdpConstraints) {
        final List<PeerConnection.IceServer> iceServers = new ArrayList<>();
        PeerConnection.IceServer iceServer = new PeerConnection.IceServer("stun:stun.l.google.com:19302");
        iceServers.add(iceServer);
        localPeer = peerConnectionFactory.createPeerConnection(iceServers, sdpConstraints, new CustomPeerConnectionObserver("localPeerCreation") {

            @Override
            public void onIceCandidate(IceCandidate iceCandidate) {
                super.onIceCandidate(iceCandidate);
                Map<String, String> iceCandidateParams = new HashMap<>();
                iceCandidateParams.put("sdpMid", iceCandidate.sdpMid);
                iceCandidateParams.put("sdpMLineIndex", Integer.toString(iceCandidate.sdpMLineIndex));
                iceCandidateParams.put("candidate", iceCandidate.sdp);
                if (webSocketAdapter.getUserId() != null) {
                    iceCandidateParams.put("endpointName", webSocketAdapter.getUserId());
                    webSocketAdapter.sendJson(webSocket, "onIceCandidate", iceCandidateParams);
                } else {
                    webSocketAdapter.addIceCandidate(iceCandidateParams);
                }
            }
        });
    }

    public void createLocalOffer(MediaConstraints sdpConstraints) {
        localPeer.createOffer(new CustomSdpObserver("localCreateOffer") {

            @Override
            public void onCreateSuccess(SessionDescription sessionDescription) {
                super.onCreateSuccess(sessionDescription);
                localPeer.setLocalDescription(new CustomSdpObserver("localSetLocalDesc"), sessionDescription);
                Map<String, String> localOfferParams = new HashMap<>();
                localOfferParams.put("audioActive", "true");
                localOfferParams.put("videoActive", "true");
                localOfferParams.put("doLoopback", "false");
                localOfferParams.put("frameRate", "30");
                localOfferParams.put("typeOfVideo", "CAMERA");
                localOfferParams.put("sdpOffer", sessionDescription.description);
                if (webSocketAdapter.getId() > 1) {
                    webSocketAdapter.sendJson(webSocket, "publishVideo", localOfferParams);
                } else {
                    webSocketAdapter.setLocalOfferParams(localOfferParams);
                }
            }
        }, sdpConstraints);
    }

    public void createRemotePeerConnection(RemoteParticipant remoteParticipant) {
        final List<PeerConnection.IceServer> iceServers = new ArrayList<>();
        PeerConnection.IceServer iceServer = new PeerConnection.IceServer("stun:stun.l.google.com:19302");
        iceServers.add(iceServer);
        MediaConstraints sdpConstraints = new MediaConstraints();
        sdpConstraints.mandatory.add(new MediaConstraints.KeyValuePair("offerToReceiveAudio", "true"));
        sdpConstraints.mandatory.add(new MediaConstraints.KeyValuePair("offerToReceiveVideo", "true"));
        PeerConnection remotePeer = peerConnectionFactory.createPeerConnection(iceServers, sdpConstraints, new CustomPeerConnectionObserver("remotePeerCreation", remoteParticipant) {

            @Override
            public void onIceCandidate(IceCandidate iceCandidate) {
                super.onIceCandidate(iceCandidate);
                Map<String, String> iceCandidateParams = new HashMap<>();
                iceCandidateParams.put("sdpMid", iceCandidate.sdpMid);
                iceCandidateParams.put("sdpMLineIndex", Integer.toString(iceCandidate.sdpMLineIndex));
                iceCandidateParams.put("candidate", iceCandidate.sdp);
                iceCandidateParams.put("endpointName", getRemoteParticipant().getId());
                webSocketAdapter.sendJson(webSocket, "onIceCandidate", iceCandidateParams);
            }

            @Override
            public void onAddStream(MediaStream mediaStream) {
                super.onAddStream(mediaStream);
                activity.gotRemoteStream(mediaStream, getRemoteParticipant());
            }
        });
        MediaStream mediaStream = peerConnectionFactory.createLocalMediaStream("105");
        mediaStream.addTrack(localAudioTrack);
        mediaStream.addTrack(localVideoTrack);
        remotePeer.addStream(mediaStream);
        remoteParticipant.setPeerConnection(remotePeer);
    }

    public void hangup() {
        if (webSocketAdapter != null && localPeer != null) {
            webSocketAdapter.sendJson(webSocket, "leaveRoom", new HashMap<String, String>());
            webSocket.disconnect();
            localPeer.close();
            Map<String, RemoteParticipant> participants = webSocketAdapter.getParticipants();
            for (RemoteParticipant remoteParticipant : participants.values()) {
                remoteParticipant.getPeerConnection().close();
                views_container.removeView(remoteParticipant.getView());
            }
        }
        if (localVideoTrack != null) {
            localVideoTrack.removeRenderer(localRenderer);
            localVideoView.clearImage();
            videoGrabberAndroid.dispose();
        }
    }
}

19 Source : WebRTC.java
with GNU Affero General Public License v3.0
from RooyeKhat-Media

public clreplaced WebRTC {

    private static final String VIDEO_TRACK_ID = "ARDAMSv0";

    private static final int VIDEO_RESOLUTION_WIDTH = 720;

    private static final int VIDEO_RESOLUTION_HEIGHT = 480;

    private static final int FPS = 30;

    private PeerConnection peerConnection;

    private PeerConnectionFactory peerConnectionFactory;

    private MediaStream mediaStream;

    private String offerSdp;

    private MediaConstraints mediaConstraints;

    private MediaConstraints audioConstraints;

    private VideoCapturer videoCapturer;

    private VideoTrack videoTrackFromCamera;

    VideoSource videoSource;

    private ProtoSignalingOffer.SignalingOffer.Type callTYpe;

    private static WebRTC webRTCInstance;

    public static WebRTC getInstance() {
        if (webRTCInstance == null) {
            webRTCInstance = new WebRTC();
        }
        return webRTCInstance;
    }

    public void muteSound() {
        if (mediaStream == null) {
            return;
        }
        for (AudioTrack audioTrack : mediaStream.audioTracks) {
            audioTrack.setEnabled(false);
        }
    }

    public void switchCamera() {
        if (Camera.getNumberOfCameras() > 1) {
            if (videoCapturer instanceof CameraVideoCapturer) {
                ((CameraVideoCapturer) videoCapturer).switchCamera(null);
            }
        }
    }

    public void unMuteSound() {
        if (mediaStream == null) {
            return;
        }
        for (AudioTrack audioTrack : mediaStream.audioTracks) {
            audioTrack.setEnabled(true);
        }
    }

    private void addAudioTrack(MediaStream mediaStream) {
        AudioSource audioSource = peerConnectionFactoryInstance().createAudioSource(audioConstraintsGetInstance());
        AudioTrack audioTrack = peerConnectionFactoryInstance().createAudioTrack("ARDAMSa0", audioSource);
        audioTrack.setEnabled(true);
        mediaStream.addTrack(audioTrack);
    }

    public void setCallType(ProtoSignalingOffer.SignalingOffer.Type callTYpe) {
        this.callTYpe = callTYpe;
    }

    private void addVideoTrack(MediaStream mediaStream) {
        if (callTYpe == ProtoSignalingOffer.SignalingOffer.Type.VIDEO_CALLING) {
            videoCapturer = createCameraCapturer(new Camera1Enumerator(false));
            videoSource = peerConnectionFactoryInstance().createVideoSource(videoCapturer);
            videoCapturer.startCapture(VIDEO_RESOLUTION_WIDTH, VIDEO_RESOLUTION_HEIGHT, FPS);
            videoTrackFromCamera = peerConnectionFactoryInstance().createVideoTrack(VIDEO_TRACK_ID, videoSource);
            videoTrackFromCamera.setEnabled(true);
            videoTrackFromCamera.addSink(new VideoSink() {

                @Override
                public void onFrame(VideoFrame videoFrame) {
                    if (G.onVideoCallFrame != null) {
                        G.onVideoCallFrame.onPeerFrame(videoFrame);
                    }
                }
            });
            mediaStream.addTrack(videoTrackFromCamera);
        }
    }

    private VideoCapturer createCameraCapturer(CameraEnumerator enumerator) {
        final String[] deviceNames = enumerator.getDeviceNames();
        // First, try to find front facing camera
        for (String deviceName : deviceNames) {
            if (enumerator.isFrontFacing(deviceName)) {
                VideoCapturer videoCapturer = enumerator.createCapturer(deviceName, null);
                if (videoCapturer != null) {
                    return videoCapturer;
                }
            }
        }
        // Front facing camera not found, try something else
        for (String deviceName : deviceNames) {
            if (!enumerator.isFrontFacing(deviceName)) {
                VideoCapturer videoCapturer = enumerator.createCapturer(deviceName, null);
                if (videoCapturer != null) {
                    return videoCapturer;
                }
            }
        }
        return null;
    }

    public void pauseVideoCapture() {
        if (videoCapturer != null) {
            try {
                videoCapturer.stopCapture();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public void startVideoCapture() {
        if (videoCapturer != null) {
            try {
                videoCapturer.startCapture(VIDEO_RESOLUTION_WIDTH, VIDEO_RESOLUTION_HEIGHT, FPS);
            } catch (RuntimeException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * First, we initiate the PeerConnectionFactory with our application context and some options.
     */
    private PeerConnectionFactory peerConnectionFactoryInstance() {
        if (peerConnectionFactory == null) {
            Set<String> HARDWARE_AEC_WHITELIST = new HashSet<String>() {

                {
                    add("D5803");
                    add("FP1");
                    add("SM-A500FU");
                    add("XT1092");
                }
            };
            Set<String> OPEN_SL_ES_WHITELIST = new HashSet<String>() {

                {
                }
            };
            if (Build.VERSION.SDK_INT >= 11) {
                if (HARDWARE_AEC_WHITELIST.contains(Build.MODEL)) {
                    WebRtcAudioUtils.setWebRtcBasedAcousticEchoCanceler(false);
                } else {
                    WebRtcAudioUtils.setWebRtcBasedAcousticEchoCanceler(true);
                }
                if (OPEN_SL_ES_WHITELIST.contains(Build.MODEL)) {
                    WebRtcAudioManager.setBlacklistDeviceForOpenSLESUsage(false);
                } else {
                    WebRtcAudioManager.setBlacklistDeviceForOpenSLESUsage(true);
                }
            }
            PeerConnectionFactory.initialize(PeerConnectionFactory.InitializationOptions.builder(G.context).createInitializationOptions());
            peerConnectionFactory = PeerConnectionFactory.builder().createPeerConnectionFactory();
        }
        return peerConnectionFactory;
    }

    PeerConnection peerConnectionInstance() {
        if (peerConnection == null) {
            List<PeerConnection.IceServer> iceServers = new ArrayList<>();
            Realm realm = Realm.getDefaultInstance();
            RealmCallConfig realmCallConfig = realm.where(RealmCallConfig.clreplaced).findFirst();
            for (RealmIceServer ice : realmCallConfig.getIceServer()) {
                iceServers.add(new PeerConnection.IceServer(ice.getUrl(), ice.getUsername(), ice.getCredential()));
            }
            realm.close();
            PeerConnection.RTCConfiguration configuration = new PeerConnection.RTCConfiguration(iceServers);
            configuration.bundlePolicy = PeerConnection.BundlePolicy.MAXBUNDLE;
            configuration.rtcpMuxPolicy = PeerConnection.RtcpMuxPolicy.REQUIRE;
            configuration.iceTransportsType = PeerConnection.IceTransportsType.RELAY;
            PeerConnection.Observer observer = new PeerConnectionObserver();
            MediaConstraints mediaConstraints = mediaConstraintsGetInstance();
            peerConnection = peerConnectionFactoryInstance().createPeerConnection(iceServers, mediaConstraints, observer);
            mediaStream = peerConnectionFactoryInstance().createLocalMediaStream("ARDAMS");
            addAudioTrack(mediaStream);
            addVideoTrack(mediaStream);
            peerConnection.addStream(mediaStream);
        }
        return peerConnection;
    }

    public void createOffer(final long userIdCallee) {
        peerConnectionInstance().createOffer(new SdpObserver() {

            @Override
            public void onCreateSuccess(SessionDescription sessionDescription) {
                offerSdp = sessionDescription.description;
                new RequestSignalingOffer().signalingOffer(userIdCallee, callTYpe, sessionDescription.description);
            }

            @Override
            public void onSetSuccess() {
            }

            @Override
            public void onCreateFailure(String s) {
            }

            @Override
            public void onSetFailure(String s) {
            }
        }, mediaConstraintsGetInstance());
    }

    public void setOfferLocalDescription() {
        setLocalDescription(SessionDescription.Type.OFFER, offerSdp);
    }

    public void createAnswer() {
        peerConnectionInstance().createAnswer(new SdpObserver() {

            @Override
            public void onCreateSuccess(SessionDescription sessionDescription) {
                setLocalDescription(SessionDescription.Type.ANSWER, sessionDescription.description);
                Log.i("WWW", "onCreateSuccess sessionDescription.description : " + sessionDescription.description);
                Log.i("WWW", "onCreateSuccess sessionDescription.type : " + sessionDescription.type);
                acceptCall(sessionDescription.description);
            }

            @Override
            public void onSetSuccess() {
            }

            @Override
            public void onCreateFailure(String s) {
            }

            @Override
            public void onSetFailure(String s) {
            }
        }, mediaConstraintsGetInstance());
    }

    private void setLocalDescription(final SessionDescription.Type type, String sdp) {
        peerConnectionInstance().setLocalDescription(new SdpObserver() {

            @Override
            public void onCreateSuccess(SessionDescription sessionDescription) {
            }

            @Override
            public void onSetSuccess() {
                Log.i("WWW", "onSetSuccess");
            }

            @Override
            public void onCreateFailure(String s) {
            }

            @Override
            public void onSetFailure(String s) {
            }
        }, new SessionDescription(type, sdp));
    }

    private MediaConstraints mediaConstraintsGetInstance() {
        if (mediaConstraints == null) {
            mediaConstraints = new MediaConstraints();
            mediaConstraints.optional.add(new MediaConstraints.KeyValuePair("DtlsSrtpKeyAgreement", "true"));
        }
        return mediaConstraints;
    }

    private MediaConstraints audioConstraintsGetInstance() {
        if (audioConstraints == null) {
            audioConstraints = new MediaConstraints();
            audioConstraints.optional.add(new MediaConstraints.KeyValuePair("DtlsSrtpKeyAgreement", "true"));
        }
        return audioConstraints;
    }

    private void acceptCall(String sdp) {
        new RequestSignalingAccept().signalingAccept(sdp);
    }

    public void leaveCall() {
        // don't need for close/dispose here, this action will be doing in onLeave callback
        // close();
        // dispose();
        /**
         * set peer connection null for try again
         */
        // clearConnection();
        new RequestSignalingLeave().signalingLeave();
    }

    public void close() {
        if (peerConnection != null) {
            peerConnection.close();
        }
    }

    void dispose() {
        try {
            if (peerConnection != null) {
                peerConnection.dispose();
            }
            if (peerConnectionFactory != null) {
                peerConnectionFactory.dispose();
            }
            if (videoCapturer != null) {
                videoCapturer.stopCapture();
                videoCapturer = null;
            }
        } catch (RuntimeException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    void clearConnection() {
        peerConnectionFactory = null;
        peerConnection = null;
        webRTCInstance = null;
    }
}

19 Source : MainActivity.java
with Apache License 2.0
from rome753

public clreplaced MainActivity extends AppCompatActivity implements SignalingClient.Callback {

    PeerConnectionFactory peerConnectionFactory;

    PeerConnection peerConnection;

    SurfaceViewRenderer localView;

    SurfaceViewRenderer remoteView;

    MediaStream mediaStream;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        EglBase.Context eglBaseContext = EglBase.create().getEglBaseContext();
        // create PeerConnectionFactory
        PeerConnectionFactory.initialize(PeerConnectionFactory.InitializationOptions.builder(this).createInitializationOptions());
        PeerConnectionFactory.Options options = new PeerConnectionFactory.Options();
        DefaultVideoEncoderFactory defaultVideoEncoderFactory = new DefaultVideoEncoderFactory(eglBaseContext, true, true);
        DefaultVideoDecoderFactory defaultVideoDecoderFactory = new DefaultVideoDecoderFactory(eglBaseContext);
        peerConnectionFactory = PeerConnectionFactory.builder().setOptions(options).setVideoEncoderFactory(defaultVideoEncoderFactory).setVideoDecoderFactory(defaultVideoDecoderFactory).createPeerConnectionFactory();
        SurfaceTextureHelper surfaceTextureHelper = SurfaceTextureHelper.create("CaptureThread", eglBaseContext);
        // create VideoCapturer
        VideoCapturer videoCapturer = createCameraCapturer(true);
        VideoSource videoSource = peerConnectionFactory.createVideoSource(videoCapturer.isScreencast());
        videoCapturer.initialize(surfaceTextureHelper, getApplicationContext(), videoSource.getCapturerObserver());
        videoCapturer.startCapture(480, 640, 30);
        localView = findViewById(R.id.localView);
        localView.setMirror(true);
        localView.init(eglBaseContext, null);
        // create VideoTrack
        VideoTrack videoTrack = peerConnectionFactory.createVideoTrack("100", videoSource);
        // // display in localView
        videoTrack.addSink(localView);
        remoteView = findViewById(R.id.remoteView);
        remoteView.setMirror(false);
        remoteView.init(eglBaseContext, null);
        AudioSource audioSource = peerConnectionFactory.createAudioSource(new MediaConstraints());
        AudioTrack audioTrack = peerConnectionFactory.createAudioTrack("101", audioSource);
        mediaStream = peerConnectionFactory.createLocalMediaStream("mediaStream");
        mediaStream.addTrack(videoTrack);
        mediaStream.addTrack(audioTrack);
        SignalingClient.get().setCallback(this);
        call();
    }

    private void call() {
        List<PeerConnection.IceServer> iceServers = new ArrayList<>();
        iceServers.add(PeerConnection.IceServer.builder("stun:stun.l.google.com:19302").createIceServer());
        peerConnection = peerConnectionFactory.createPeerConnection(iceServers, new PeerConnectionAdapter("localconnection") {

            @Override
            public void onIceCandidate(IceCandidate iceCandidate) {
                super.onIceCandidate(iceCandidate);
                SignalingClient.get().sendIceCandidate(iceCandidate);
            }

            @Override
            public void onAddStream(MediaStream mediaStream) {
                super.onAddStream(mediaStream);
                VideoTrack remoteVideoTrack = mediaStream.videoTracks.get(0);
                runOnUiThread(() -> {
                    remoteVideoTrack.addSink(remoteView);
                });
            }
        });
        peerConnection.addStream(mediaStream);
    }

    private VideoCapturer createCameraCapturer(boolean isFront) {
        Camera1Enumerator enumerator = new Camera1Enumerator(false);
        final String[] deviceNames = enumerator.getDeviceNames();
        // First, try to find front facing camera
        for (String deviceName : deviceNames) {
            if (isFront ? enumerator.isFrontFacing(deviceName) : enumerator.isBackFacing(deviceName)) {
                VideoCapturer videoCapturer = enumerator.createCapturer(deviceName, null);
                if (videoCapturer != null) {
                    return videoCapturer;
                }
            }
        }
        return null;
    }

    @Override
    public void onCreateRoom() {
    }

    @Override
    public void onPeerJoined() {
    }

    @Override
    public void onSelfJoined() {
        peerConnection.createOffer(new SdpAdapter("local offer sdp") {

            @Override
            public void onCreateSuccess(SessionDescription sessionDescription) {
                super.onCreateSuccess(sessionDescription);
                peerConnection.setLocalDescription(new SdpAdapter("local set local"), sessionDescription);
                SignalingClient.get().sendSessionDescription(sessionDescription);
            }
        }, new MediaConstraints());
    }

    @Override
    public void onPeerLeave(String msg) {
    }

    @Override
    public void onOfferReceived(JSONObject data) {
        runOnUiThread(() -> {
            peerConnection.setRemoteDescription(new SdpAdapter("localSetRemote"), new SessionDescription(SessionDescription.Type.OFFER, data.optString("sdp")));
            peerConnection.createAnswer(new SdpAdapter("localAnswerSdp") {

                @Override
                public void onCreateSuccess(SessionDescription sdp) {
                    super.onCreateSuccess(sdp);
                    peerConnection.setLocalDescription(new SdpAdapter("localSetLocal"), sdp);
                    SignalingClient.get().sendSessionDescription(sdp);
                }
            }, new MediaConstraints());
        });
    }

    @Override
    public void onAnswerReceived(JSONObject data) {
        peerConnection.setRemoteDescription(new SdpAdapter("localSetRemote"), new SessionDescription(SessionDescription.Type.ANSWER, data.optString("sdp")));
    }

    @Override
    public void onIceCandidateReceived(JSONObject data) {
        peerConnection.addIceCandidate(new IceCandidate(data.optString("id"), data.optInt("label"), data.optString("candidate")));
    }
}

19 Source : MainActivity.java
with Apache License 2.0
from rome753

public clreplaced MainActivity extends AppCompatActivity {

    PeerConnectionFactory peerConnectionFactory;

    PeerConnection peerConnectionLocal;

    PeerConnection peerConnectionRemote;

    SurfaceViewRenderer localView;

    SurfaceViewRenderer remoteView;

    MediaStream mediaStreamLocal;

    MediaStream mediaStreamRemote;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        EglBase.Context eglBaseContext = EglBase.create().getEglBaseContext();
        // create PeerConnectionFactory
        PeerConnectionFactory.initialize(PeerConnectionFactory.InitializationOptions.builder(this).createInitializationOptions());
        PeerConnectionFactory.Options options = new PeerConnectionFactory.Options();
        DefaultVideoEncoderFactory defaultVideoEncoderFactory = new DefaultVideoEncoderFactory(eglBaseContext, true, true);
        DefaultVideoDecoderFactory defaultVideoDecoderFactory = new DefaultVideoDecoderFactory(eglBaseContext);
        peerConnectionFactory = PeerConnectionFactory.builder().setOptions(options).setVideoEncoderFactory(defaultVideoEncoderFactory).setVideoDecoderFactory(defaultVideoDecoderFactory).createPeerConnectionFactory();
        SurfaceTextureHelper surfaceTextureHelper = SurfaceTextureHelper.create("CaptureThread", eglBaseContext);
        // create VideoCapturer
        VideoCapturer videoCapturer = createCameraCapturer(true);
        VideoSource videoSource = peerConnectionFactory.createVideoSource(videoCapturer.isScreencast());
        videoCapturer.initialize(surfaceTextureHelper, getApplicationContext(), videoSource.getCapturerObserver());
        videoCapturer.startCapture(480, 640, 30);
        localView = findViewById(R.id.localView);
        localView.setMirror(true);
        localView.init(eglBaseContext, null);
        // create VideoTrack
        VideoTrack videoTrack = peerConnectionFactory.createVideoTrack("100", videoSource);
        // // display in localView
        // videoTrack.addSink(localView);
        SurfaceTextureHelper remoteSurfaceTextureHelper = SurfaceTextureHelper.create("RemoteCaptureThread", eglBaseContext);
        // create VideoCapturer
        VideoCapturer remoteVideoCapturer = createCameraCapturer(false);
        VideoSource remoteVideoSource = peerConnectionFactory.createVideoSource(remoteVideoCapturer.isScreencast());
        remoteVideoCapturer.initialize(remoteSurfaceTextureHelper, getApplicationContext(), remoteVideoSource.getCapturerObserver());
        remoteVideoCapturer.startCapture(480, 640, 30);
        remoteView = findViewById(R.id.remoteView);
        remoteView.setMirror(false);
        remoteView.init(eglBaseContext, null);
        // create VideoTrack
        VideoTrack remoteVideoTrack = peerConnectionFactory.createVideoTrack("102", remoteVideoSource);
        // // display in remoteView
        // remoteVideoTrack.addSink(remoteView);
        mediaStreamLocal = peerConnectionFactory.createLocalMediaStream("mediaStreamLocal");
        mediaStreamLocal.addTrack(videoTrack);
        mediaStreamRemote = peerConnectionFactory.createLocalMediaStream("mediaStreamRemote");
        mediaStreamRemote.addTrack(remoteVideoTrack);
        call(mediaStreamLocal, mediaStreamRemote);
    }

    private void call(MediaStream localMediaStream, MediaStream remoteMediaStream) {
        List<PeerConnection.IceServer> iceServers = new ArrayList<>();
        peerConnectionLocal = peerConnectionFactory.createPeerConnection(iceServers, new PeerConnectionAdapter("localconnection") {

            @Override
            public void onIceCandidate(IceCandidate iceCandidate) {
                super.onIceCandidate(iceCandidate);
                peerConnectionRemote.addIceCandidate(iceCandidate);
            }

            @Override
            public void onAddStream(MediaStream mediaStream) {
                super.onAddStream(mediaStream);
                VideoTrack remoteVideoTrack = mediaStream.videoTracks.get(0);
                runOnUiThread(() -> {
                    remoteVideoTrack.addSink(localView);
                });
            }
        });
        peerConnectionRemote = peerConnectionFactory.createPeerConnection(iceServers, new PeerConnectionAdapter("remoteconnection") {

            @Override
            public void onIceCandidate(IceCandidate iceCandidate) {
                super.onIceCandidate(iceCandidate);
                peerConnectionLocal.addIceCandidate(iceCandidate);
            }

            @Override
            public void onAddStream(MediaStream mediaStream) {
                super.onAddStream(mediaStream);
                VideoTrack localVideoTrack = mediaStream.videoTracks.get(0);
                runOnUiThread(() -> {
                    localVideoTrack.addSink(remoteView);
                });
            }
        });
        peerConnectionLocal.addStream(localMediaStream);
        peerConnectionLocal.createOffer(new SdpAdapter("local offer sdp") {

            @Override
            public void onCreateSuccess(SessionDescription sessionDescription) {
                super.onCreateSuccess(sessionDescription);
                // todo crashed here
                peerConnectionLocal.setLocalDescription(new SdpAdapter("local set local"), sessionDescription);
                peerConnectionRemote.addStream(remoteMediaStream);
                peerConnectionRemote.setRemoteDescription(new SdpAdapter("remote set remote"), sessionDescription);
                peerConnectionRemote.createAnswer(new SdpAdapter("remote answer sdp") {

                    @Override
                    public void onCreateSuccess(SessionDescription sdp) {
                        super.onCreateSuccess(sdp);
                        peerConnectionRemote.setLocalDescription(new SdpAdapter("remote set local"), sdp);
                        peerConnectionLocal.setRemoteDescription(new SdpAdapter("local set remote"), sdp);
                    }
                }, new MediaConstraints());
            }
        }, new MediaConstraints());
    }

    private VideoCapturer createCameraCapturer(boolean isFront) {
        Camera1Enumerator enumerator = new Camera1Enumerator(false);
        final String[] deviceNames = enumerator.getDeviceNames();
        // First, try to find front facing camera
        for (String deviceName : deviceNames) {
            if (isFront ? enumerator.isFrontFacing(deviceName) : enumerator.isBackFacing(deviceName)) {
                VideoCapturer videoCapturer = enumerator.createCapturer(deviceName, null);
                if (videoCapturer != null) {
                    return videoCapturer;
                }
            }
        }
        return null;
    }
}

19 Source : Stream.java
with MIT License
from qunarcorp

/**
 * Created by xinbo.wang on 2017-04-05.
 */
public clreplaced Stream {

    protected PeerConnection peerConnection;

    protected boolean localStream;

    protected IConferenceView view;

    protected boolean isDispose;

    public Stream(boolean isLocal, IConferenceView view) {
        this.localStream = isLocal;
        this.view = view;
    }

    public void updateMainSpeaker() {
    }

    public void dispose() {
        peerConnection.dispose();
        isDispose = true;
    }

    public boolean isDispose() {
        return isDispose;
    }

    public void subscribe() {
    }
}

19 Source : RtcEventLog.java
with MIT License
from pcgpcgpcg

public clreplaced RtcEventLog {

    private static final String TAG = "RtcEventLog";

    private static final int OUTPUT_FILE_MAX_BYTES = 10_000_000;

    private final PeerConnection peerConnection;

    private RtcEventLogState state = RtcEventLogState.INACTIVE;

    enum RtcEventLogState {

        INACTIVE, STARTED, STOPPED
    }

    public RtcEventLog(PeerConnection peerConnection) {
        if (peerConnection == null) {
            throw new NullPointerException("The peer connection is null.");
        }
        this.peerConnection = peerConnection;
    }

    public void start(final File outputFile) {
        if (state == RtcEventLogState.STARTED) {
            Log.e(TAG, "RtcEventLog has already started.");
            return;
        }
        final ParcelFileDescriptor fileDescriptor;
        try {
            fileDescriptor = ParcelFileDescriptor.open(outputFile, ParcelFileDescriptor.MODE_READ_WRITE | ParcelFileDescriptor.MODE_CREATE | ParcelFileDescriptor.MODE_TRUNCATE);
        } catch (IOException e) {
            Log.e(TAG, "Failed to create a new file", e);
            return;
        }
        // Preplacedes ownership of the file to WebRTC.
        boolean success = peerConnection.startRtcEventLog(fileDescriptor.detachFd(), OUTPUT_FILE_MAX_BYTES);
        if (!success) {
            Log.e(TAG, "Failed to start RTC event log.");
            return;
        }
        state = RtcEventLogState.STARTED;
        Log.d(TAG, "RtcEventLog started.");
    }

    public void stop() {
        if (state != RtcEventLogState.STARTED) {
            Log.e(TAG, "RtcEventLog was not started.");
            return;
        }
        peerConnection.stopRtcEventLog();
        state = RtcEventLogState.STOPPED;
        Log.d(TAG, "RtcEventLog stopped.");
    }
}

19 Source : JanusConnection2.java
with MIT License
from pcgpcgpcg

public clreplaced JreplacedConnection2 {

    public BigInteger handleId;

    public PeerConnection peerConnection;

    public PeerConnectionClient2.SDPObserver sdpObserver;

    public VideoTrack videoTrack;

    public boolean type;
}

19 Source : JanusConnection.java
with MIT License
from pcgpcgpcg

public clreplaced JreplacedConnection {

    public BigInteger handleId;

    public PeerConnection peerConnection;

    public PeerConnectionClient.SDPObserver sdpObserver;

    public VideoTrack videoTrack;

    public SurfaceViewRenderer videoRender;

    public boolean type;
}

19 Source : PeerConnectionChannel.java
with Apache License 2.0
from open-webrtc-toolkit

// /@cond
public abstract clreplaced PeerConnectionChannel implements PeerConnection.Observer, SdpObserver, DataChannel.Observer {

    // For P2P, key is peer id, for conference, key is Publication/Subscription id.
    public final String key;

    protected final PeerConnectionChannelObserver observer;

    protected final ExecutorService callbackExecutor = Executors.newSingleThreadExecutor();

    private final ExecutorService pcExecutor = Executors.newSingleThreadExecutor();

    private final List<IceCandidate> queuedRemoteCandidates;

    private final Object remoteIceLock = new Object();

    private final Object disposeLock = new Object();

    protected PeerConnection peerConnection;

    protected PeerConnection.SignalingState signalingState;

    protected PeerConnection.IceConnectionState iceConnectionState;

    protected DataChannel localDataChannel;

    protected List<VideoCodec> videoCodecs;

    protected List<AudioCodec> audioCodecs;

    protected Integer videoMaxBitrate = null, audioMaxBitrate = null;

    protected ArrayList<String> queuedMessage;

    private MediaConstraints sdpConstraints;

    private SessionDescription localSdp;

    private boolean disposed = false;

    protected boolean onError = false;

    // <MediaStream id, RtpSender>
    private ConcurrentHashMap<String, RtpSender> videoRtpSenders, audioRtpSenders;

    protected PeerConnectionChannel(String key, PeerConnection.RTCConfiguration configuration, boolean receiveVideo, boolean receiveAudio, PeerConnectionChannelObserver observer) {
        this.key = key;
        this.observer = observer;
        videoRtpSenders = new ConcurrentHashMap<>();
        audioRtpSenders = new ConcurrentHashMap<>();
        queuedRemoteCandidates = new LinkedList<>();
        queuedMessage = new ArrayList<>();
        sdpConstraints = new MediaConstraints();
        sdpConstraints.mandatory.add(new KeyValuePair("OfferToReceiveAudio", String.valueOf(receiveAudio)));
        sdpConstraints.mandatory.add(new KeyValuePair("OfferToReceiveVideo", String.valueOf(receiveVideo)));
        peerConnection = PCFactoryProxy.instance().createPeerConnection(configuration, this);
        RCHECK(peerConnection);
        signalingState = peerConnection.signalingState();
    }

    // Utility method for getting the mediastream as it is package privileged.
    protected static MediaStream GetMediaStream(Stream localStream) {
        return localStream.mediaStream;
    }

    public boolean disposed() {
        synchronized (disposeLock) {
            return disposed;
        }
    }

    protected void createOffer() {
        DCHECK(pcExecutor);
        pcExecutor.execute(() -> {
            if (disposed()) {
                return;
            }
            Log.d(LOG_TAG, "create offer");
            peerConnection.createOffer(PeerConnectionChannel.this, sdpConstraints);
        });
    }

    protected void createAnswer() {
        DCHECK(pcExecutor);
        pcExecutor.execute(() -> {
            if (disposed()) {
                return;
            }
            Log.d(LOG_TAG, "creating answer");
            peerConnection.createAnswer(PeerConnectionChannel.this, sdpConstraints);
        });
    }

    public void processSignalingMessage(JSONObject data) throws JSONException {
        String signalingType = data.getString("type");
        if (signalingType.equals("candidates")) {
            IceCandidate candidate = new IceCandidate(data.getString("sdpMid"), data.getInt("sdpMLineIndex"), data.getString("candidate"));
            addOrQueueCandidate(candidate);
        } else if (signalingType.equals("offer") || signalingType.equals("answer")) {
            // When gets an SDP during onError, it means that i'm waiting for peer to re-configure
            // itself to keep compatibility with me.
            if (onError) {
                // Ignore the SDP only once.
                onError = false;
                return;
            }
            String sdpString = data.getString("sdp");
            SessionDescription remoteSdp = new SessionDescription(SessionDescription.Type.fromCanonicalForm(signalingType), sdpString);
            setRemoteDescription(remoteSdp);
        }
    }

    private void addOrQueueCandidate(final IceCandidate iceCandidate) {
        if (disposed()) {
            return;
        }
        DCHECK(pcExecutor);
        DCHECK(iceCandidate);
        pcExecutor.execute(() -> {
            if (disposed()) {
                return;
            }
            if (peerConnection.signalingState() == PeerConnection.SignalingState.STABLE) {
                Log.d(LOG_TAG, "add ice candidate");
                peerConnection.addIceCandidate(iceCandidate);
            } else {
                synchronized (remoteIceLock) {
                    Log.d(LOG_TAG, "queue ice candidate");
                    queuedRemoteCandidates.add(iceCandidate);
                }
            }
        });
    }

    protected void drainRemoteCandidates() {
        DCHECK(pcExecutor);
        DCHECK(queuedRemoteCandidates);
        synchronized (remoteIceLock) {
            for (final IceCandidate candidate : queuedRemoteCandidates) {
                pcExecutor.execute(() -> {
                    if (disposed()) {
                        return;
                    }
                    Log.d(LOG_TAG, "add ice candidate");
                    peerConnection.addIceCandidate(candidate);
                    queuedRemoteCandidates.remove(candidate);
                });
            }
        }
    }

    private void setRemoteDescription(final SessionDescription remoteDescription) {
        pcExecutor.execute(() -> {
            if (disposed()) {
                return;
            }
            peerConnection.setRemoteDescription(PeerConnectionChannel.this, remoteDescription);
        });
    }

    protected void addStream(final MediaStream mediaStream) {
        DCHECK(mediaStream);
        DCHECK(pcExecutor);
        pcExecutor.execute(() -> {
            if (disposed()) {
                return;
            }
            ArrayList<String> streamIds = new ArrayList<>();
            streamIds.add(mediaStream.getId());
            for (AudioTrack audioTrack : mediaStream.audioTracks) {
                RtpSender audioSender = peerConnection.addTrack(audioTrack, streamIds);
                audioRtpSenders.put(mediaStream.getId(), audioSender);
            }
            for (VideoTrack videoTrack : mediaStream.videoTracks) {
                RtpSender videoSender = peerConnection.addTrack(videoTrack, streamIds);
                videoRtpSenders.put(mediaStream.getId(), videoSender);
            }
        });
    }

    protected void removeStream(String mediaStreamId) {
        DCHECK(pcExecutor);
        pcExecutor.execute(() -> {
            if (disposed()) {
                return;
            }
            Log.d(LOG_TAG, "remove stream");
            if (audioRtpSenders.get(mediaStreamId) != null) {
                peerConnection.removeTrack(audioRtpSenders.get(mediaStreamId));
            }
            if (videoRtpSenders.get(mediaStreamId) != null) {
                peerConnection.removeTrack(videoRtpSenders.get(mediaStreamId));
            }
        });
    }

    protected void createDataChannel() {
        DCHECK(pcExecutor);
        DCHECK(localDataChannel == null);
        pcExecutor.execute(() -> {
            if (disposed()) {
                return;
            }
            DataChannel.Init init = new DataChannel.Init();
            localDataChannel = peerConnection.createDataChannel("message", init);
            localDataChannel.registerObserver(PeerConnectionChannel.this);
        });
    }

    public void getConnectionStats(final ActionCallback<RTCStatsReport> callback) {
        DCHECK(pcExecutor);
        pcExecutor.execute(() -> {
            if (disposed()) {
                callback.onFailure(new OwtError("Invalid stats."));
                return;
            }
            peerConnection.getStats(callback::onSuccess);
        });
    }

    private SessionDescription preferCodecs(SessionDescription sdp, boolean video) {
        LinkedHashSet<String> preferredCodecs = new LinkedHashSet<>();
        if (video) {
            for (VideoCodec codec : this.videoCodecs) {
                preferredCodecs.add(codec.name);
            }
            preferredCodecs.add("red");
            preferredCodecs.add("ulpfec");
        } else {
            for (AudioCodec codec : audioCodecs) {
                preferredCodecs.add(codec.name);
            }
            preferredCodecs.add("CN");
            preferredCodecs.add("telephone-event");
        }
        return preferCodec(sdp, preferredCodecs, video);
    }

    private SessionDescription preferCodec(SessionDescription originalSdp, LinkedHashSet<String> preferredCodecs, boolean video) {
        String[] lines = originalSdp.description.split("(\r\n|\n)");
        ArrayList<String> newLines = new ArrayList<>();
        int audioMLineIndex = -1;
        int videoMLineIndex = -1;
        // <codecName, payloadType>
        HashMap<String, ArrayList<String>> preferredPayloadTypes = new HashMap<>();
        // skipped all video payload types when dealing with audio codecs, and vice versa.
        HashSet<String> misMatchedPayloadTypes = new HashSet<>();
        for (int i = 0; i < lines.length; i++) {
            String line = lines[i];
            if (line.startsWith("a=rtpmap:")) {
                String payloadType = line.split(" ")[0].split(":")[1];
                String codecName = line.split(" ")[1].split("/")[0];
                boolean typeMismatched = video ? VideoCodec.get(codecName) == VideoCodec.INVALID : AudioCodec.get(codecName) == AudioCodec.INVALID;
                boolean codecPreferred = preferredCodecs.contains(codecName);
                boolean rtxPreferred = codecName.equals("rtx") && containsValue(preferredPayloadTypes, lines[i + 1].split("apt=")[1]);
                if (codecPreferred || rtxPreferred) {
                    putEntry(preferredPayloadTypes, codecName, payloadType);
                } else if (typeMismatched && !codecName.equals("rtx")) {
                    misMatchedPayloadTypes.add(payloadType);
                } else {
                    continue;
                }
            } else if (line.startsWith("a=rtcp-fb:") || line.startsWith("a=fmtp:")) {
                String payloadType = line.split(" ")[0].split(":")[1];
                if (!misMatchedPayloadTypes.contains(payloadType) && !containsValue(preferredPayloadTypes, payloadType)) {
                    continue;
                }
            } else if (line.startsWith("m=audio")) {
                audioMLineIndex = newLines.size();
            } else if (line.startsWith("m=video")) {
                videoMLineIndex = newLines.size();
            }
            newLines.add(line);
        }
        if (!video && audioMLineIndex != -1) {
            newLines.set(audioMLineIndex, changeMLine(newLines.get(audioMLineIndex), preferredCodecs, preferredPayloadTypes));
        }
        if (video && videoMLineIndex != -1) {
            newLines.set(videoMLineIndex, changeMLine(newLines.get(videoMLineIndex), preferredCodecs, preferredPayloadTypes));
        }
        String newSdp = joinString(newLines, "\r\n", true);
        return new SessionDescription(originalSdp.type, newSdp);
    }

    private boolean containsValue(HashMap<String, ArrayList<String>> payloadTypes, String value) {
        for (ArrayList<String> v : payloadTypes.values()) {
            for (String s : v) {
                if (s.equals(value)) {
                    return true;
                }
            }
        }
        return false;
    }

    private void putEntry(HashMap<String, ArrayList<String>> payloadTypes, String key, String value) {
        if (payloadTypes.containsKey(key)) {
            payloadTypes.get(key).add(value);
        } else {
            ArrayList<String> valueList = new ArrayList<>();
            valueList.add(value);
            payloadTypes.put(key, valueList);
        }
    }

    private String changeMLine(String mLine, LinkedHashSet<String> preferredCodecs, HashMap<String, ArrayList<String>> preferredPayloadTypes) {
        List<String> oldMLineParts = Arrays.asList(mLine.split(" "));
        List<String> mLineHeader = oldMLineParts.subList(0, 3);
        ArrayList<String> newMLineParts = new ArrayList<>(mLineHeader);
        for (String preferredCodec : preferredCodecs) {
            if (preferredPayloadTypes.containsKey(preferredCodec)) {
                newMLineParts.addAll(preferredPayloadTypes.get(preferredCodec));
            }
        }
        if (preferredPayloadTypes.containsKey("rtx")) {
            newMLineParts.addAll(preferredPayloadTypes.get("rtx"));
        }
        return joinString(newMLineParts, " ", false);
    }

    private String joinString(ArrayList<String> strings, String delimiter, boolean delimiterAtEnd) {
        Iterator<String> iter = strings.iterator();
        if (!iter.hasNext()) {
            return "";
        }
        StringBuilder buffer = new StringBuilder(iter.next());
        while (iter.hasNext()) {
            buffer.append(delimiter).append(iter.next());
        }
        if (delimiterAtEnd) {
            buffer.append(delimiter);
        }
        return buffer.toString();
    }

    private void setMaxBitrate(RtpSender sender, Integer bitrate) {
        if (sender == null || bitrate == null || bitrate.intValue() <= 0) {
            return;
        }
        RtpParameters rtpParameters = sender.getParameters();
        if (rtpParameters == null) {
            Log.e(LOG_TAG, "Null rtp paramters");
            return;
        }
        for (RtpParameters.Encoding encoding : rtpParameters.encodings) {
            encoding.maxBitrateBps = bitrate * 1000;
        }
        if (!sender.setParameters(rtpParameters)) {
            Log.e(LOG_TAG, "Failed to configure max audio/video bitrate");
        }
    }

    protected void setMaxBitrate(String mediaStreamId) {
        DCHECK(peerConnection);
        if (videoRtpSenders.get(mediaStreamId) != null) {
            setMaxBitrate(videoRtpSenders.get(mediaStreamId), videoMaxBitrate);
        }
        if (audioRtpSenders.get(mediaStreamId) != null) {
            setMaxBitrate(audioRtpSenders.get(mediaStreamId), audioMaxBitrate);
        }
    }

    protected void dispose() {
        pcExecutor.execute(() -> {
            synchronized (disposeLock) {
                disposed = true;
                if (peerConnection != null) {
                    peerConnection.dispose();
                }
                peerConnection = null;
            }
        });
    }

    // SdpObserver
    @Override
    public void onCreateSuccess(final SessionDescription sessionDescription) {
        localSdp = sessionDescription;
        if (audioCodecs != null) {
            localSdp = preferCodecs(localSdp, false);
        }
        if (videoCodecs != null) {
            localSdp = preferCodecs(localSdp, true);
        }
        callbackExecutor.execute(() -> {
            if (disposed) {
                return;
            }
            observer.onLocalDescription(key, localSdp);
        });
        pcExecutor.execute(() -> {
            if (disposed) {
                return;
            }
            peerConnection.setLocalDescription(PeerConnectionChannel.this, localSdp);
        });
    }

    @Override
    abstract public void onSetSuccess();

    @Override
    abstract public void onCreateFailure(final String error);

    @Override
    abstract public void onSetFailure(final String error);

    // PeerConnection.Observer interface
    @Override
    abstract public void onSignalingChange(PeerConnection.SignalingState signalingState);

    @Override
    abstract public void onIceConnectionChange(PeerConnection.IceConnectionState iceConnectionState);

    @Override
    public void onIceConnectionReceivingChange(boolean b) {
    }

    @Override
    public void onIceGatheringChange(PeerConnection.IceGatheringState iceGatheringState) {
    }

    @Override
    abstract public void onIceCandidate(IceCandidate iceCandidate);

    @Override
    abstract public void onIceCandidatesRemoved(IceCandidate[] iceCandidates);

    @Override
    abstract public void onAddStream(MediaStream mediaStream);

    @Override
    abstract public void onRemoveStream(final MediaStream mediaStream);

    @Override
    public void onDataChannel(final DataChannel dataChannel) {
        if (disposed) {
            return;
        }
        callbackExecutor.execute(() -> {
            localDataChannel = dataChannel;
            localDataChannel.registerObserver(PeerConnectionChannel.this);
        });
    }

    @Override
    abstract public void onRenegotiationNeeded();

    @Override
    public void onAddTrack(RtpReceiver rtpReceiver, MediaStream[] mediaStreams) {
    }

    // DataChannel.Observer interface
    @Override
    public void onBufferedAmountChange(long l) {
    }

    @Override
    public void onStateChange() {
        if (disposed) {
            return;
        }
        callbackExecutor.execute(() -> {
            if (localDataChannel.state() == DataChannel.State.OPEN) {
                for (int i = 0; i < queuedMessage.size(); i++) {
                    ByteBuffer byteBuffer = ByteBuffer.wrap(queuedMessage.get(i).getBytes(Charset.forName("UTF-8")));
                    DataChannel.Buffer buffer = new DataChannel.Buffer(byteBuffer, false);
                    localDataChannel.send(buffer);
                }
                queuedMessage.clear();
            }
        });
    }

    @Override
    public void onMessage(final DataChannel.Buffer buffer) {
        if (disposed) {
            return;
        }
        callbackExecutor.execute(() -> {
            ByteBuffer data = buffer.data;
            final byte[] bytes = new byte[data.capacity()];
            data.get(bytes);
            String message = new String(bytes);
            observer.onDataChannelMessage(key, message);
        });
    }

    public interface PeerConnectionChannelObserver {

        void onIceCandidate(String key, IceCandidate candidate);

        void onIceCandidatesRemoved(String key, IceCandidate[] candidates);

        void onLocalDescription(String key, SessionDescription localSdp);

        /**
         * @param recoverable true means SDK gotta reconfigure the peerconnection instead of
         * triggering failure actions.
         */
        void onError(String key, String errorMsg, boolean recoverable);

        void onEnded(String key);

        void onAddStream(String key, RemoteStream remoteStream);

        void onDataChannelMessage(String key, String message);

        void onRenegotiationRequest(String key);
    }
}

19 Source : MainActivity.java
with MIT License
from Nitrillo

public clreplaced MainActivity extends AppCompatActivity {

    private static final String SIGNALING_URI = "http://your.ip.here:7000";

    private static final String VIDEO_TRACK_ID = "video1";

    private static final String AUDIO_TRACK_ID = "audio1";

    private static final String LOCAL_STREAM_ID = "stream1";

    private static final String SDP_MID = "sdpMid";

    private static final String SDP_M_LINE_INDEX = "sdpMLineIndex";

    private static final String SDP = "sdp";

    private static final String CREATEOFFER = "createoffer";

    private static final String OFFER = "offer";

    private static final String ANSWER = "answer";

    private static final String CANDIDATE = "candidate";

    private PeerConnectionFactory peerConnectionFactory;

    private VideoSource localVideoSource;

    private PeerConnection peerConnection;

    private MediaStream localMediaStream;

    private VideoRenderer otherPeerRenderer;

    private Socket socket;

    private boolean createOffer = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        AudioManager audioManager = (AudioManager) this.getSystemService(Context.AUDIO_SERVICE);
        audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
        audioManager.setSpeakerphoneOn(true);
        PeerConnectionFactory.initializeAndroidGlobals(// Context
        this, // Audio Enabled
        true, // Video Enabled
        true, // Hardware Acceleration Enabled
        true, // Render EGL Context
        null);
        peerConnectionFactory = new PeerConnectionFactory();
        VideoCapturerAndroid vc = VideoCapturerAndroid.create(VideoCapturerAndroid.getNameOfFrontFacingDevice(), null);
        localVideoSource = peerConnectionFactory.createVideoSource(vc, new MediaConstraints());
        VideoTrack localVideoTrack = peerConnectionFactory.createVideoTrack(VIDEO_TRACK_ID, localVideoSource);
        localVideoTrack.setEnabled(true);
        AudioSource audioSource = peerConnectionFactory.createAudioSource(new MediaConstraints());
        AudioTrack localAudioTrack = peerConnectionFactory.createAudioTrack(AUDIO_TRACK_ID, audioSource);
        localAudioTrack.setEnabled(true);
        localMediaStream = peerConnectionFactory.createLocalMediaStream(LOCAL_STREAM_ID);
        localMediaStream.addTrack(localVideoTrack);
        localMediaStream.addTrack(localAudioTrack);
        GLSurfaceView videoView = (GLSurfaceView) findViewById(R.id.glview_call);
        VideoRendererGui.setView(videoView, null);
        try {
            otherPeerRenderer = VideoRendererGui.createGui(0, 0, 100, 100, VideoRendererGui.ScalingType.SCALE_ASPECT_FILL, true);
            VideoRenderer renderer = VideoRendererGui.createGui(50, 50, 50, 50, VideoRendererGui.ScalingType.SCALE_ASPECT_FILL, true);
            localVideoTrack.addRenderer(renderer);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void onConnect(View button) {
        if (peerConnection != null)
            return;
        ArrayList<PeerConnection.IceServer> iceServers = new ArrayList<>();
        iceServers.add(new PeerConnection.IceServer("stun:stun.l.google.com:19302"));
        peerConnection = peerConnectionFactory.createPeerConnection(iceServers, new MediaConstraints(), peerConnectionObserver);
        peerConnection.addStream(localMediaStream);
        try {
            socket = IO.socket(SIGNALING_URI);
            socket.on(CREATEOFFER, new Emitter.Listener() {

                @Override
                public void call(Object... args) {
                    createOffer = true;
                    peerConnection.createOffer(sdpObserver, new MediaConstraints());
                }
            }).on(OFFER, new Emitter.Listener() {

                @Override
                public void call(Object... args) {
                    try {
                        JSONObject obj = (JSONObject) args[0];
                        SessionDescription sdp = new SessionDescription(SessionDescription.Type.OFFER, obj.getString(SDP));
                        peerConnection.setRemoteDescription(sdpObserver, sdp);
                        peerConnection.createAnswer(sdpObserver, new MediaConstraints());
                    } catch (JSONException e) {
                        e.printStackTrace();
                    }
                }
            }).on(ANSWER, new Emitter.Listener() {

                @Override
                public void call(Object... args) {
                    try {
                        JSONObject obj = (JSONObject) args[0];
                        SessionDescription sdp = new SessionDescription(SessionDescription.Type.ANSWER, obj.getString(SDP));
                        peerConnection.setRemoteDescription(sdpObserver, sdp);
                    } catch (JSONException e) {
                        e.printStackTrace();
                    }
                }
            }).on(CANDIDATE, new Emitter.Listener() {

                @Override
                public void call(Object... args) {
                    try {
                        JSONObject obj = (JSONObject) args[0];
                        peerConnection.addIceCandidate(new IceCandidate(obj.getString(SDP_MID), obj.getInt(SDP_M_LINE_INDEX), obj.getString(SDP)));
                    } catch (JSONException e) {
                        e.printStackTrace();
                    }
                }
            });
            socket.connect();
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
    }

    SdpObserver sdpObserver = new SdpObserver() {

        @Override
        public void onCreateSuccess(SessionDescription sessionDescription) {
            peerConnection.setLocalDescription(sdpObserver, sessionDescription);
            try {
                JSONObject obj = new JSONObject();
                obj.put(SDP, sessionDescription.description);
                if (createOffer) {
                    socket.emit(OFFER, obj);
                } else {
                    socket.emit(ANSWER, obj);
                }
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onSetSuccess() {
        }

        @Override
        public void onCreateFailure(String s) {
        }

        @Override
        public void onSetFailure(String s) {
        }
    };

    PeerConnection.Observer peerConnectionObserver = new PeerConnection.Observer() {

        @Override
        public void onSignalingChange(PeerConnection.SignalingState signalingState) {
            Log.d("RTCAPP", "onSignalingChange:" + signalingState.toString());
        }

        @Override
        public void onIceConnectionChange(PeerConnection.IceConnectionState iceConnectionState) {
            Log.d("RTCAPP", "onIceConnectionChange:" + iceConnectionState.toString());
        }

        @Override
        public void onIceConnectionReceivingChange(boolean b) {
        }

        @Override
        public void onIceGatheringChange(PeerConnection.IceGatheringState iceGatheringState) {
        }

        @Override
        public void onIceCandidate(IceCandidate iceCandidate) {
            try {
                JSONObject obj = new JSONObject();
                obj.put(SDP_MID, iceCandidate.sdpMid);
                obj.put(SDP_M_LINE_INDEX, iceCandidate.sdpMLineIndex);
                obj.put(SDP, iceCandidate.sdp);
                socket.emit(CANDIDATE, obj);
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onAddStream(MediaStream mediaStream) {
            mediaStream.videoTracks.getFirst().addRenderer(otherPeerRenderer);
        }

        @Override
        public void onRemoveStream(MediaStream mediaStream) {
        }

        @Override
        public void onDataChannel(DataChannel dataChannel) {
        }

        @Override
        public void onRenegotiationNeeded() {
        }
    };
}

19 Source : RTCCall.java
with GNU General Public License v3.0
from meshenger-app

public clreplaced RTCCall implements DataChannel.Observer {

    enum CallState {

        CONNECTING,
        RINGING,
        CONNECTED,
        DISMISSED,
        ENDED,
        ERROR
    }

    private final String StateChangeMessage = "StateChange";

    private final String CameraDisabledMessage = "CameraDisabled";

    private final String CameraEnabledMessage = "CameraEnabled";

    private PeerConnectionFactory factory;

    private PeerConnection connection;

    private MediaConstraints constraints;

    private String offer;

    private SurfaceViewRenderer remoteRenderer;

    private SurfaceViewRenderer localRenderer;

    private EglBase.Context sharedContext;

    private CameraVideoCapturer capturer;

    private MediaStream upStream;

    private DataChannel dataChannel;

    private boolean videoEnabled;

    private Context context;

    private Contact contact;

    private byte[] ownPublicKey;

    private byte[] ownSecretKey;

    private List<PeerConnection.IceServer> iceServers;

    private OnStateChangeListener listener;

    private MainService.MainBinder binder;

    public CallState state;

    public Socket commSocket;

    static public RTCCall startCall(Context context, MainService.MainBinder binder, Contact contact, OnStateChangeListener listener) {
        return new RTCCall(context, binder, contact, listener);
    }

    // called for incoming calls
    public RTCCall(Context context, MainService.MainBinder binder, Contact contact, Socket commSocket, String offer) {
        this.context = context;
        this.contact = contact;
        this.commSocket = commSocket;
        this.listener = null;
        this.binder = binder;
        this.ownPublicKey = binder.getSettings().getPublicKey();
        this.ownSecretKey = binder.getSettings().getSecretKey();
        this.offer = offer;
        // usually empty
        this.iceServers = new ArrayList<>();
        for (String server : this.binder.getSettings().getIceServers()) {
            this.iceServers.add(PeerConnection.IceServer.builder(server).createIceServer());
        }
        initRTC(context);
    }

    // called for outgoing calls
    private RTCCall(Context context, MainService.MainBinder binder, Contact contact, OnStateChangeListener listener) {
        this.context = context;
        this.contact = contact;
        this.commSocket = null;
        this.listener = listener;
        this.binder = binder;
        this.ownPublicKey = binder.getSettings().getPublicKey();
        this.ownSecretKey = binder.getSettings().getSecretKey();
        log("RTCCall created");
        // usually empty
        this.iceServers = new ArrayList<>();
        for (String server : binder.getSettings().getIceServers()) {
            this.iceServers.add(PeerConnection.IceServer.builder(server).createIceServer());
        }
        initRTC(context);
        if (AppCompatDelegate.getDefaultNightMode() == AppCompatDelegate.MODE_NIGHT_YES) {
            context.setTheme(R.style.AppTheme_Dark);
        } else {
            context.setTheme(R.style.AppTheme_Light);
        }
        new Thread(() -> {
            connection = factory.createPeerConnection(Collections.emptyList(), new DefaultObserver() {

                @Override
                public void onIceGatheringChange(PeerConnection.IceGatheringState iceGatheringState) {
                    super.onIceGatheringChange(iceGatheringState);
                    byte[] otherPublicKey = new byte[Sodium.crypto_sign_publickeybytes()];
                    if (iceGatheringState == PeerConnection.IceGatheringState.COMPLETE) {
                        log("transferring offer...");
                        try {
                            commSocket = contact.createSocket();
                            if (commSocket == null) {
                                log("cannot establish connection");
                                reportStateChange(CallState.ERROR);
                                // RTCCall.this.binder.addCallEvent(contact, CallEvent.Type.OUTGOING_ERROR);
                                return;
                            }
                            InetSocketAddress remote_address = (InetSocketAddress) commSocket.getRemoteSocketAddress();
                            log("outgoing call from remote address: " + remote_address);
                            // remember latest working address
                            contact.setLastWorkingAddress(new InetSocketAddress(remote_address.getAddress(), MainService.serverPort));
                            log("connect..");
                            PacketReader pr = new PacketReader(commSocket);
                            reportStateChange(CallState.CONNECTING);
                            {
                                JSONObject obj = new JSONObject();
                                obj.put("action", "call");
                                obj.put("offer", connection.getLocalDescription().description);
                                byte[] encrypted = Crypto.encryptMessage(obj.toString(), contact.getPublicKey(), ownPublicKey, ownSecretKey);
                                if (encrypted == null) {
                                    log("encryption failed");
                                    closeCommSocket();
                                    reportStateChange(CallState.ERROR);
                                    // RTCCall.this.binder.addCallEvent(contact, CallEvent.Type.OUTGOING_ERROR);
                                    return;
                                }
                                PacketWriter pw = new PacketWriter(commSocket);
                                pw.writeMessage(encrypted);
                            }
                            {
                                byte[] response = pr.readMessage();
                                String decrypted = Crypto.decryptMessage(response, otherPublicKey, ownPublicKey, ownSecretKey);
                                if (decrypted == null || !Arrays.equals(contact.getPublicKey(), otherPublicKey)) {
                                    log("decryption failed");
                                    closeCommSocket();
                                    reportStateChange(CallState.ERROR);
                                    // RTCCall.this.binder.addCallEvent(contact, CallEvent.Type.OUTGOING_ERROR);
                                    return;
                                }
                                JSONObject obj = new JSONObject(decrypted);
                                if (!obj.optString("action", "").equals("ringing")) {
                                    closeCommSocket();
                                    reportStateChange(CallState.ERROR);
                                    // RTCCall.this.binder.addCallEvent(contact, CallEvent.Type.OUTGOING_ERROR);
                                    return;
                                }
                                log("ringing...");
                                reportStateChange(CallState.RINGING);
                            }
                            {
                                byte[] response = pr.readMessage();
                                String decrypted = Crypto.decryptMessage(response, otherPublicKey, ownPublicKey, ownSecretKey);
                                if (decrypted == null || !Arrays.equals(contact.getPublicKey(), otherPublicKey)) {
                                    closeCommSocket();
                                    reportStateChange(CallState.ERROR);
                                    return;
                                }
                                JSONObject obj = new JSONObject(decrypted);
                                String action = obj.getString("action");
                                if (action.equals("connected")) {
                                    reportStateChange(CallState.CONNECTED);
                                    handleAnswer(obj.getString("answer"));
                                // contact accepted receiving call
                                // RTCCall.this.binder.addCallEvent(contact, CallEvent.Type.OUTGOING_ACCEPTED);
                                } else if (action.equals("dismissed")) {
                                    closeCommSocket();
                                    reportStateChange(CallState.DISMISSED);
                                // contact declined receiving call
                                // RTCCall.this.binder.addCallEvent(contact, CallEvent.Type.OUTGOING_DECLINED);
                                } else {
                                    log("unknown action reply: " + action);
                                    closeCommSocket();
                                    reportStateChange(CallState.ERROR);
                                // RTCCall.this.binder.addCallEvent(contact, CallEvent.Type.OUTGOING_ERROR);
                                }
                            }
                        } catch (Exception e) {
                            closeCommSocket();
                            e.printStackTrace();
                            reportStateChange(CallState.ERROR);
                        // RTCCall.this.binder.addCallEvent(contact, CallEvent.Type.OUTGOING_ERROR);
                        }
                    }
                }

                @Override
                public void onIceConnectionChange(PeerConnection.IceConnectionState iceConnectionState) {
                    log("onIceGatheringChange.onIceConnectionChange " + iceConnectionState.name());
                    super.onIceConnectionChange(iceConnectionState);
                    if (iceConnectionState == PeerConnection.IceConnectionState.DISCONNECTED) {
                        reportStateChange(CallState.ENDED);
                    }
                }

                @Override
                public void onAddStream(MediaStream mediaStream) {
                    super.onAddStream(mediaStream);
                    handleMediaStream(mediaStream);
                }

                @Override
                public void onDataChannel(DataChannel dataChannel) {
                    super.onDataChannel(dataChannel);
                    RTCCall.this.dataChannel = dataChannel;
                    dataChannel.registerObserver(RTCCall.this);
                }
            });
            connection.addStream(createStream());
            this.dataChannel = connection.createDataChannel("data", new DataChannel.Init());
            this.dataChannel.registerObserver(this);
            connection.createOffer(new DefaultSdpObserver() {

                @Override
                public void onCreateSuccess(SessionDescription sessionDescription) {
                    super.onCreateSuccess(sessionDescription);
                    connection.setLocalDescription(new DefaultSdpObserver(), sessionDescription);
                }
            }, constraints);
        }).start();
    }

    private void closeCommSocket() {
        log("closeCommSocket");
        if (this.commSocket != null) {
            try {
                this.commSocket.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
            this.commSocket = null;
        }
    }

    private void closePeerConnection() {
        log("closePeerConnection");
        if (this.connection != null) {
            try {
                this.connection.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
            this.connection = null;
        }
    }

    public void setRemoteRenderer(SurfaceViewRenderer remoteRenderer) {
        this.remoteRenderer = remoteRenderer;
    }

    public void switchFrontFacing() {
        if (this.capturer != null) {
            this.capturer.switchCamera(null);
        }
    }

    @Override
    public void onBufferedAmountChange(long l) {
    // nothing to do
    }

    @Override
    public void onStateChange() {
    // nothing to do
    }

    @Override
    public void onMessage(DataChannel.Buffer buffer) {
        byte[] data = new byte[buffer.data.remaining()];
        buffer.data.get(data);
        String s = new String(data);
        JSONObject object = null;
        try {
            log("onMessage: " + s);
            object = new JSONObject(s);
            if (object.has(StateChangeMessage)) {
                String state = object.getString(StateChangeMessage);
                switch(state) {
                    case CameraEnabledMessage:
                    case CameraDisabledMessage:
                        {
                            setRemoteVideoEnabled(state.equals(CameraEnabledMessage));
                            break;
                        }
                }
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }
    }

    private void setRemoteVideoEnabled(boolean enabled) {
        new Handler(Looper.getMainLooper()).post(() -> {
            if (enabled) {
                this.remoteRenderer.setBackgroundColor(Color.TRANSPARENT);
            } else {
                TypedValue typedValue = new TypedValue();
                Resources.Theme theme = this.context.getTheme();
                theme.resolveAttribute(R.attr.backgroundCardColor, typedValue, true);
                @ColorInt
                int color = typedValue.data;
                this.remoteRenderer.setBackgroundColor(color);
            }
        });
    }

    public boolean isVideoEnabled() {
        return this.videoEnabled;
    }

    public void setVideoEnabled(boolean enabled) {
        this.videoEnabled = enabled;
        try {
            if (enabled) {
                this.capturer.startCapture(500, 500, 30);
            } else {
                this.capturer.stopCapture();
            }
            JSONObject object = new JSONObject();
            object.put(StateChangeMessage, enabled ? CameraEnabledMessage : CameraDisabledMessage);
            log("setVideoEnabled: " + object);
            dataChannel.send(new DataChannel.Buffer(ByteBuffer.wrap(object.toString().getBytes()), false));
        } catch (JSONException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /*private void initLocalRenderer() {
        if (this.localRenderer != null) {
            log("really initng " + (this.sharedContext == null));
            this.localRenderer.init(this.sharedContext, null);
            this.localCameraTrack.addSink(localRenderer);
            this.capturer.startCapture(500, 500, 30);
        }
    }*/
    /*private void initVideoTrack() {
        this.sharedContext = EglBase.create().getEglBaseContext();
        this.capturer = createCapturer(true);
        this.localCameraTrack = factory.createVideoTrack("video1", factory.createVideoSource(capturer));
    }*/
    private CameraVideoCapturer createCapturer() {
        CameraEnumerator enumerator = new Camera1Enumerator();
        for (String name : enumerator.getDeviceNames()) {
            if (enumerator.isFrontFacing(name)) {
                return enumerator.createCapturer(name, null);
            }
        }
        return null;
    }

    public void releaseCamera() {
        if (this.capturer != null) {
            try {
                this.capturer.stopCapture();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        if (this.remoteRenderer != null) {
            this.remoteRenderer.release();
        }
        if (this.localRenderer != null) {
            this.localRenderer.release();
        }
    }

    private void handleMediaStream(MediaStream stream) {
        log("handleMediaStream");
        if (this.remoteRenderer == null || stream.videoTracks.size() == 0) {
            return;
        }
        new Handler(Looper.getMainLooper()).post(() -> {
            // remoteRenderer.setBackgroundColor(Color.TRANSPARENT);
            remoteRenderer.init(this.sharedContext, null);
            stream.videoTracks.get(0).addSink(remoteRenderer);
        });
    }

    private MediaStream createStream() {
        upStream = factory.createLocalMediaStream("stream1");
        AudioTrack audio = factory.createAudioTrack("audio1", factory.createAudioSource(new MediaConstraints()));
        upStream.addTrack(audio);
        upStream.addTrack(getVideoTrack());
        // this.capturer.startCapture(500, 500, 30);
        return upStream;
    }

    private VideoTrack getVideoTrack() {
        this.capturer = createCapturer();
        return factory.createVideoTrack("video1", factory.createVideoSource(this.capturer.isScreencast()));
    }

    private void initRTC(Context c) {
        PeerConnectionFactory.initialize(PeerConnectionFactory.InitializationOptions.builder(c).createInitializationOptions());
        factory = PeerConnectionFactory.builder().createPeerConnectionFactory();
        constraints = new MediaConstraints();
        constraints.optional.add(new MediaConstraints.KeyValuePair("offerToReceiveAudio", "true"));
        constraints.optional.add(new MediaConstraints.KeyValuePair("offerToReceiveVideo", "false"));
        constraints.optional.add(new MediaConstraints.KeyValuePair("DtlsSrtpKeyAgreement", "true"));
    // initVideoTrack();
    }

    private void handleAnswer(String remoteDesc) {
        connection.setRemoteDescription(new DefaultSdpObserver() {

            @Override
            public void onSetSuccess() {
                super.onSetSuccess();
                log("onSetSuccess");
            }

            @Override
            public void onSetFailure(String s) {
                super.onSetFailure(s);
                log("onSetFailure: " + s);
            }
        }, new SessionDescription(SessionDescription.Type.ANSWER, remoteDesc));
    }

    private void reportStateChange(CallState state) {
        this.state = state;
        if (this.listener != null) {
            this.listener.OnStateChange(state);
        }
    }

    public void accept(OnStateChangeListener listener) {
        this.listener = listener;
        new Thread(() -> {
            connection = factory.createPeerConnection(this.iceServers, new DefaultObserver() {

                @Override
                public void onIceGatheringChange(PeerConnection.IceGatheringState iceGatheringState) {
                    super.onIceGatheringChange(iceGatheringState);
                    if (iceGatheringState == PeerConnection.IceGatheringState.COMPLETE) {
                        log("onIceGatheringChange");
                        try {
                            PacketWriter pw = new PacketWriter(commSocket);
                            JSONObject obj = new JSONObject();
                            obj.put("action", "connected");
                            obj.put("answer", connection.getLocalDescription().description);
                            byte[] encrypted = Crypto.encryptMessage(obj.toString(), contact.getPublicKey(), ownPublicKey, ownSecretKey);
                            if (encrypted != null) {
                                pw.writeMessage(encrypted);
                                reportStateChange(CallState.CONNECTED);
                            } else {
                                reportStateChange(CallState.ERROR);
                            }
                        // new Thread(new SpeakerRunnable(commSocket)).start();
                        } catch (Exception e) {
                            e.printStackTrace();
                            reportStateChange(CallState.ERROR);
                        }
                    }
                }

                @Override
                public void onIceConnectionChange(PeerConnection.IceConnectionState iceConnectionState) {
                    log("accept.onIceConnectionChange " + iceConnectionState.name());
                    super.onIceConnectionChange(iceConnectionState);
                    if (iceConnectionState == PeerConnection.IceConnectionState.DISCONNECTED) {
                        reportStateChange(CallState.ENDED);
                    }
                }

                @Override
                public void onAddStream(MediaStream mediaStream) {
                    log("onAddStream");
                    super.onAddStream(mediaStream);
                    handleMediaStream(mediaStream);
                }

                @Override
                public void onDataChannel(DataChannel dataChannel) {
                    super.onDataChannel(dataChannel);
                    RTCCall.this.dataChannel = dataChannel;
                    dataChannel.registerObserver(RTCCall.this);
                }
            });
            connection.addStream(createStream());
            // this.dataChannel = connection.createDataChannel("data", new DataChannel.Init());
            log("setting remote description");
            connection.setRemoteDescription(new DefaultSdpObserver() {

                @Override
                public void onSetSuccess() {
                    super.onSetSuccess();
                    log("creating answer...");
                    connection.createAnswer(new DefaultSdpObserver() {

                        @Override
                        public void onCreateSuccess(SessionDescription sessionDescription) {
                            log("onCreateSuccess");
                            super.onCreateSuccess(sessionDescription);
                            connection.setLocalDescription(new DefaultSdpObserver(), sessionDescription);
                        }

                        @Override
                        public void onCreateFailure(String s) {
                            super.onCreateFailure(s);
                            log("onCreateFailure: " + s);
                        }
                    }, constraints);
                }
            }, new SessionDescription(SessionDescription.Type.OFFER, offer));
        }).start();
    }

    public void decline() {
        new Thread(() -> {
            try {
                log("declining...");
                if (this.commSocket != null) {
                    PacketWriter pw = new PacketWriter(commSocket);
                    byte[] encrypted = Crypto.encryptMessage("{\"action\":\"dismissed\"}", this.contact.getPublicKey(), this.ownPublicKey, this.ownSecretKey);
                    pw.writeMessage(encrypted);
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                cleanup();
            }
        }).start();
    }

    public void cleanup() {
        closeCommSocket();
        if (this.upStream != null && state == CallState.CONNECTED) {
            /*for(AudioTrack track : this.upStream.audioTracks){
                track.setEnabled(false);
                track.dispose();
            }
            for(VideoTrack track : this.upStream.videoTracks) track.dispose();*/
            closePeerConnection();
        // factory.dispose();
        }
    }

    public void hangUp() {
        new Thread(() -> {
            try {
                if (this.commSocket != null) {
                    PacketWriter pw = new PacketWriter(this.commSocket);
                    byte[] encrypted = Crypto.encryptMessage("{\"action\":\"dismissed\"}", this.contact.getPublicKey(), this.ownPublicKey, this.ownSecretKey);
                    pw.writeMessage(encrypted);
                }
                closeCommSocket();
                closePeerConnection();
                reportStateChange(CallState.ENDED);
            } catch (IOException e) {
                e.printStackTrace();
                reportStateChange(CallState.ERROR);
            }
        }).start();
    }

    public interface OnStateChangeListener {

        void OnStateChange(CallState state);
    }

    private void log(String s) {
        Log.d(this, s);
    }
}

19 Source : WebRTC.java
with GNU Affero General Public License v3.0
from KianIranian-STDG

public clreplaced WebRTC {

    private static String TAG = "iGapCall WebRTC ";

    private static final String VIDEO_TRACK_ID = "ARDAMSv0";

    private static final int VIDEO_RESOLUTION_WIDTH = 1920;

    private static final int VIDEO_RESOLUTION_HEIGHT = 1080;

    private static final int FPS = 30;

    private PeerConnection peerConnection;

    private PeerConnectionFactory peerConnectionFactory;

    private MediaStream mediaStream;

    private VideoFrameListener frameListener;

    // this is filled for offer
    private String localSDP;

    private String remoteSDP;

    private MediaConstraints mediaConstraints;

    private MediaConstraints audioConstraints;

    private VideoCapturer videoCapturer;

    private ProtoSignalingOffer.SignalingOffer.Type callTYpe;

    private EglBase.Context eglBaseContext = null;

    private static volatile WebRTC instance = null;

    public static WebRTC getInstance() {
        WebRTC localInstance = instance;
        if (localInstance == null) {
            synchronized (WebRTC.clreplaced) {
                localInstance = instance;
                if (localInstance == null) {
                    instance = localInstance = new WebRTC();
                }
            }
        }
        return localInstance;
    }

    public static boolean isAlive() {
        return instance != null;
    }

    public void toggleSound(boolean isEnable) {
        if (mediaStream == null) {
            return;
        }
        for (AudioTrack audioTrack : mediaStream.audioTracks) {
            audioTrack.setEnabled(isEnable);
        }
    }

    @Deprecated
    public void muteSound() {
        if (mediaStream == null) {
            return;
        }
        for (AudioTrack audioTrack : mediaStream.audioTracks) {
            audioTrack.setEnabled(false);
        }
    }

    public void switchCamera() {
        if (Camera.getNumberOfCameras() > 1) {
            if (videoCapturer instanceof CameraVideoCapturer) {
                ((CameraVideoCapturer) videoCapturer).switchCamera(null);
            }
        }
    }

    @Deprecated
    public void unMuteSound() {
        if (mediaStream == null) {
            return;
        }
        for (AudioTrack audioTrack : mediaStream.audioTracks) {
            audioTrack.setEnabled(true);
        }
    }

    private void addAudioTrack(MediaStream mediaStream) {
        AudioSource audioSource = peerConnectionFactoryInstance().createAudioSource(audioConstraintsGetInstance());
        AudioTrack audioTrack = peerConnectionFactoryInstance().createAudioTrack("ARDAMSa0", audioSource);
        audioTrack.setEnabled(true);
        mediaStream.addTrack(audioTrack);
    }

    @Deprecated
    public void setCallType(ProtoSignalingOffer.SignalingOffer.Type callTYpe) {
        this.callTYpe = callTYpe;
    }

    public EglBase.Context getEglBaseContext() {
        if (eglBaseContext == null)
            eglBaseContext = EglBase.create().getEglBaseContext();
        return eglBaseContext;
    }

    private void addVideoTrack(MediaStream mediaStream) {
        if (CallManager.getInstance().getCallType() == ProtoSignalingOffer.SignalingOffer.Type.VIDEO_CALLING) {
            videoCapturer = createCameraCapturer(new Camera1Enumerator(false));
            VideoSource videoSource = peerConnectionFactoryInstance().createVideoSource(videoCapturer.isScreencast());
            SurfaceTextureHelper surfaceTextureHelper = SurfaceTextureHelper.create("CaptureThread", getEglBaseContext());
            videoCapturer.initialize(surfaceTextureHelper, G.context, videoSource.getCapturerObserver());
            videoCapturer.startCapture(VIDEO_RESOLUTION_WIDTH, VIDEO_RESOLUTION_HEIGHT, FPS);
            VideoTrack videoTrackFromCamera = peerConnectionFactoryInstance().createVideoTrack(VIDEO_TRACK_ID, videoSource);
            videoTrackFromCamera.setEnabled(true);
            videoTrackFromCamera.addSink(videoFrame -> {
                if (frameListener != null)
                    frameListener.onLocalFrame(videoFrame);
            // if (G.onVideoCallFrame != null) {
            // G.onVideoCallFrame.onPeerFrame(videoFrame);
            // }
            });
            mediaStream.addTrack(videoTrackFromCamera);
        }
    }

    private VideoCapturer createCameraCapturer(CameraEnumerator enumerator) {
        final String[] deviceNames = enumerator.getDeviceNames();
        // First, try to find front facing camera
        for (String deviceName : deviceNames) {
            if (enumerator.isFrontFacing(deviceName)) {
                VideoCapturer videoCapturer = enumerator.createCapturer(deviceName, null);
                if (videoCapturer != null) {
                    return videoCapturer;
                }
            }
        }
        // Front facing camera not found, try something else
        for (String deviceName : deviceNames) {
            if (!enumerator.isFrontFacing(deviceName)) {
                VideoCapturer videoCapturer = enumerator.createCapturer(deviceName, null);
                if (videoCapturer != null) {
                    return videoCapturer;
                }
            }
        }
        return null;
    }

    public void pauseVideoCapture() {
        if (videoCapturer != null) {
            try {
                videoCapturer.stopCapture();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public void startVideoCapture() {
        if (videoCapturer != null) {
            try {
                videoCapturer.startCapture(VIDEO_RESOLUTION_WIDTH, VIDEO_RESOLUTION_HEIGHT, FPS);
            } catch (RuntimeException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * First, we initiate the PeerConnectionFactory with our application context and some options.
     */
    private PeerConnectionFactory peerConnectionFactoryInstance() {
        if (peerConnectionFactory == null) {
            initializePeerConnectionFactory();
            PeerConnectionFactory.initialize(PeerConnectionFactory.InitializationOptions.builder(G.context).createInitializationOptions());
            PeerConnectionFactory.Options options = new PeerConnectionFactory.Options();
            DefaultVideoEncoderFactory defaultVideoEncoderFactory = new DefaultVideoEncoderFactory(eglBaseContext, true, true);
            DefaultVideoDecoderFactory defaultVideoDecoderFactory = new DefaultVideoDecoderFactory(eglBaseContext);
            peerConnectionFactory = PeerConnectionFactory.builder().setOptions(options).setVideoEncoderFactory(defaultVideoEncoderFactory).setVideoDecoderFactory(defaultVideoDecoderFactory).createPeerConnectionFactory();
            WebRtcAudioTrack.setErrorCallback(new WebRtcAudioTrack.ErrorCallback() {

                @Override
                public void onWebRtcAudioTrackInitError(String s) {
                }

                @Override
                public void onWebRtcAudioTrackStartError(WebRtcAudioTrack.AudioTrackStartErrorCode audioTrackStartErrorCode, String s) {
                }

                @Override
                public void onWebRtcAudioTrackError(String s) {
                }
            });
            WebRtcAudioRecord.setErrorCallback(new WebRtcAudioRecord.WebRtcAudioRecordErrorCallback() {

                @Override
                public void onWebRtcAudioRecordInitError(String s) {
                }

                @Override
                public void onWebRtcAudioRecordStartError(WebRtcAudioRecord.AudioRecordStartErrorCode audioRecordStartErrorCode, String s) {
                }

                @Override
                public void onWebRtcAudioRecordError(String s) {
                }
            });
        }
        return peerConnectionFactory;
    }

    private void initializePeerConnectionFactory() {
        try {
            Set<String> HARDWARE_AEC_BLACKLIST = new HashSet<String>() {

                {
                    add("Pixel");
                    add("Pixel XL");
                    add("Moto G5");
                    add("Moto G (5S) Plus");
                    add("Moto G4");
                    add("TA-1053");
                    add("Mi A1");
                    // Sony z5 compact
                    add("E5823");
                }
            };
            Set<String> OPEN_SL_ES_WHITELIST = new HashSet<String>() {

                {
                    add("Pixel");
                    add("Pixel XL");
                }
            };
            // PeerConnectionFactory.startInternalTracingCapture(
            // Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "webrtc-trace.txt");
            if (WebRtcAudioUtils.isAcousticEchoCancelerSupported()) {
                WebRtcAudioUtils.setWebRtcBasedAcousticEchoCanceler(true);
            }
            if (WebRtcAudioUtils.isAutomaticGainControlSupported()) {
                WebRtcAudioUtils.setWebRtcBasedAutomaticGainControl(true);
            }
            if (WebRtcAudioUtils.isNoiseSuppressorSupported()) {
                WebRtcAudioUtils.setWebRtcBasedNoiseSuppressor(true);
            }
            // if (HARDWARE_AEC_BLACKLIST.contains(Build.MODEL)) {
            // WebRtcAudioUtils.setWebRtcBasedAcousticEchoCanceler(true);
            // }
            if (!OPEN_SL_ES_WHITELIST.contains(Build.MODEL)) {
                WebRtcAudioManager.setBlacklistDeviceForOpenSLESUsage(true);
            }
        } catch (UnsatisfiedLinkError e) {
            e.printStackTrace();
        }
    }

    public PeerConnection peerConnectionInstance() {
        if (peerConnection == null) {
            List<PeerConnection.IceServer> iceServers = new ArrayList<>();
            DbManager.getInstance().doRealmTask(realm -> {
                RealmCallConfig realmCallConfig = realm.where(RealmCallConfig.clreplaced).findFirst();
                for (RealmIceServer ice : realmCallConfig.getIceServer()) {
                    iceServers.add(new PeerConnection.IceServer(ice.getUrl(), ice.getUsername(), ice.getCredential()));
                }
            });
            PeerConnection.RTCConfiguration configuration = new PeerConnection.RTCConfiguration(iceServers);
            configuration.bundlePolicy = PeerConnection.BundlePolicy.MAXBUNDLE;
            configuration.rtcpMuxPolicy = PeerConnection.RtcpMuxPolicy.REQUIRE;
            configuration.iceTransportsType = PeerConnection.IceTransportsType.RELAY;
            PeerConnection.Observer observer = new PeerConnectionObserver();
            MediaConstraints mediaConstraints = mediaConstraintsGetInstance();
            peerConnection = peerConnectionFactoryInstance().createPeerConnection(iceServers, mediaConstraints, observer);
            mediaStream = peerConnectionFactoryInstance().createLocalMediaStream("ARDAMS");
            addAudioTrack(mediaStream);
            addVideoTrack(mediaStream);
            peerConnection.addStream(mediaStream);
        }
        return peerConnection;
    }

    public void createOffer(final long userIdCallee) {
        peerConnectionInstance().createOffer(new SdpObserver() {

            @Override
            public void onCreateSuccess(SessionDescription sessionDescription) {
                if (localSDP != null) {
                    return;
                }
                localSDP = sessionDescription.description;
                CallManager.getInstance().makeOffer(userIdCallee, localSDP);
            // new RequestSignalingOffer().signalingOffer(userIdCallee, callTYpe, localSDP);
            }

            @Override
            public void onSetSuccess() {
            }

            @Override
            public void onCreateFailure(String s) {
            }

            @Override
            public void onSetFailure(String s) {
            }
        }, mediaConstraintsGetInstance());
    }

    public void setOfferLocalDescription() {
        setLocalDescription(SessionDescription.Type.OFFER, localSDP);
    }

    public void createAnswer() {
        peerConnectionInstance().createAnswer(new SdpObserver() {

            @Override
            public void onCreateSuccess(SessionDescription sessionDescription) {
                localSDP = sessionDescription.description;
                setLocalDescription(SessionDescription.Type.ANSWER, localSDP);
                CallManager.getInstance().makeAccept(sessionDescription.description);
            }

            @Override
            public void onSetSuccess() {
            }

            @Override
            public void onCreateFailure(String s) {
            }

            @Override
            public void onSetFailure(String s) {
            }
        }, mediaConstraintsGetInstance());
    }

    private void setLocalDescription(final SessionDescription.Type type, String sdp) {
        peerConnectionInstance().setLocalDescription(new SdpObserver() {

            @Override
            public void onCreateSuccess(SessionDescription sessionDescription) {
            }

            @Override
            public void onSetSuccess() {
            }

            @Override
            public void onCreateFailure(String s) {
            }

            @Override
            public void onSetFailure(String s) {
            }
        }, new SessionDescription(type, sdp));
    }

    private MediaConstraints mediaConstraintsGetInstance() {
        if (mediaConstraints == null) {
            mediaConstraints = new MediaConstraints();
            mediaConstraints.optional.add(new MediaConstraints.KeyValuePair("DtlsSrtpKeyAgreement", "true"));
        }
        return mediaConstraints;
    }

    private MediaConstraints audioConstraintsGetInstance() {
        if (audioConstraints == null) {
            audioConstraints = new MediaConstraints();
            audioConstraints.optional.add(new MediaConstraints.KeyValuePair("DtlsSrtpKeyAgreement", "true"));
        }
        return audioConstraints;
    }

    @Deprecated
    private void acceptCall(String sdp) {
        new RequestSignalingAccept().signalingAccept(sdp);
    }

    @Deprecated
    public void leaveCall() {
    }

    public void close() {
        try {
            if (videoCapturer != null) {
                videoCapturer.stopCapture();
                videoCapturer.dispose();
                videoCapturer = null;
            }
            if (peerConnection != null) {
                peerConnection.close();
                peerConnection.dispose();
                peerConnection = null;
            }
            if (peerConnectionFactory != null) {
                peerConnectionFactory.dispose();
                peerConnectionFactory = null;
            }
            instance = null;
        } catch (RuntimeException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    // My new code just changing the place
    public void setRemoteDesc(SessionDescription sdp) {
        peerConnectionInstance().setRemoteDescription(new SdpObserver() {

            @Override
            public void onCreateSuccess(SessionDescription sessionDescription) {
            }

            @Override
            public void onSetSuccess() {
                CallManager.getInstance().onSdpSuccess();
            }

            @Override
            public void onCreateFailure(String s) {
            }

            @Override
            public void onSetFailure(String s) {
            }
        }, sdp);
    }

    public void holdVideoCall(boolean callHold) {
        if (callHold) {
            pauseVideoCapture();
        } else {
            startVideoCapture();
        }
    }

    public interface VideoFrameListener {

        void onLocalFrame(VideoFrame frame);

        void onRemoteFrame(VideoFrame frame);
    }

    public void setFrameListener(VideoFrameListener frameListener) {
        this.frameListener = frameListener;
    }

    public VideoFrameListener getFrameListener() {
        return frameListener;
    }
}

19 Source : CallActivity.java
with GNU General Public License v3.0
from Jhuster

public clreplaced CallActivity extends AppCompatActivity {

    private static final int VIDEO_RESOLUTION_WIDTH = 1280;

    private static final int VIDEO_RESOLUTION_HEIGHT = 720;

    private static final int VIDEO_FPS = 30;

    private TextView mLogcatView;

    private Button mStartCallBtn;

    private Button mEndCallBtn;

    private static final String TAG = "CallActivity";

    public static final String VIDEO_TRACK_ID = "ARDAMSv0";

    public static final String AUDIO_TRACK_ID = "ARDAMSa0";

    private EglBase mRootEglBase;

    private PeerConnection mPeerConnection;

    private PeerConnectionFactory mPeerConnectionFactory;

    private SurfaceTextureHelper mSurfaceTextureHelper;

    private SurfaceViewRenderer mLocalSurfaceView;

    private SurfaceViewRenderer mRemoteSurfaceView;

    private VideoTrack mVideoTrack;

    private AudioTrack mAudioTrack;

    private VideoCapturer mVideoCapturer;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_call);
        mLogcatView = findViewById(R.id.LogcatView);
        mStartCallBtn = findViewById(R.id.StartCallButton);
        mEndCallBtn = findViewById(R.id.EndCallButton);
        RTCSignalClient.getInstance().setSignalEventListener(mOnSignalEventListener);
        String serverAddr = getIntent().getStringExtra("ServerAddr");
        String roomName = getIntent().getStringExtra("RoomName");
        RTCSignalClient.getInstance().joinRoom(serverAddr, UUID.randomUUID().toString(), roomName);
        mRootEglBase = EglBase.create();
        mLocalSurfaceView = findViewById(R.id.LocalSurfaceView);
        mRemoteSurfaceView = findViewById(R.id.RemoteSurfaceView);
        mLocalSurfaceView.init(mRootEglBase.getEglBaseContext(), null);
        mLocalSurfaceView.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FILL);
        mLocalSurfaceView.setMirror(true);
        mLocalSurfaceView.setEnableHardwareScaler(false);
        mRemoteSurfaceView.init(mRootEglBase.getEglBaseContext(), null);
        mRemoteSurfaceView.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FILL);
        mRemoteSurfaceView.setMirror(true);
        mRemoteSurfaceView.setEnableHardwareScaler(true);
        mRemoteSurfaceView.setZOrderMediaOverlay(true);
        ProxyVideoSink videoSink = new ProxyVideoSink();
        videoSink.setTarget(mLocalSurfaceView);
        mPeerConnectionFactory = createPeerConnectionFactory(this);
        // NOTE: this _must_ happen while PeerConnectionFactory is alive!
        Logging.enableLogToDebugOutput(Logging.Severity.LS_VERBOSE);
        mVideoCapturer = createVideoCapturer();
        mSurfaceTextureHelper = SurfaceTextureHelper.create("CaptureThread", mRootEglBase.getEglBaseContext());
        VideoSource videoSource = mPeerConnectionFactory.createVideoSource(false);
        mVideoCapturer.initialize(mSurfaceTextureHelper, getApplicationContext(), videoSource.getCapturerObserver());
        mVideoTrack = mPeerConnectionFactory.createVideoTrack(VIDEO_TRACK_ID, videoSource);
        mVideoTrack.setEnabled(true);
        mVideoTrack.addSink(videoSink);
        AudioSource audioSource = mPeerConnectionFactory.createAudioSource(new MediaConstraints());
        mAudioTrack = mPeerConnectionFactory.createAudioTrack(AUDIO_TRACK_ID, audioSource);
        mAudioTrack.setEnabled(true);
    }

    @Override
    protected void onResume() {
        super.onResume();
        mVideoCapturer.startCapture(VIDEO_RESOLUTION_WIDTH, VIDEO_RESOLUTION_HEIGHT, VIDEO_FPS);
    }

    @Override
    protected void onPause() {
        super.onPause();
        try {
            mVideoCapturer.stopCapture();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        doEndCall();
        mLocalSurfaceView.release();
        mRemoteSurfaceView.release();
        mVideoCapturer.dispose();
        mSurfaceTextureHelper.dispose();
        PeerConnectionFactory.stopInternalTracingCapture();
        PeerConnectionFactory.shutdownInternalTracer();
        RTCSignalClient.getInstance().leaveRoom();
    }

    public static clreplaced ProxyVideoSink implements VideoSink {

        private VideoSink mTarget;

        @Override
        synchronized public void onFrame(VideoFrame frame) {
            if (mTarget == null) {
                Log.d(TAG, "Dropping frame in proxy because target is null.");
                return;
            }
            mTarget.onFrame(frame);
        }

        synchronized void setTarget(VideoSink target) {
            this.mTarget = target;
        }
    }

    public static clreplaced SimpleSdpObserver implements SdpObserver {

        @Override
        public void onCreateSuccess(SessionDescription sessionDescription) {
            Log.i(TAG, "SdpObserver: onCreateSuccess !");
        }

        @Override
        public void onSetSuccess() {
            Log.i(TAG, "SdpObserver: onSetSuccess");
        }

        @Override
        public void onCreateFailure(String msg) {
            Log.e(TAG, "SdpObserver onCreateFailure: " + msg);
        }

        @Override
        public void onSetFailure(String msg) {
            Log.e(TAG, "SdpObserver onSetFailure: " + msg);
        }
    }

    public void onClickStartCallButton(View v) {
        doStartCall();
    }

    public void onClickEndCallButton(View v) {
        doEndCall();
    }

    private void updateCallState(boolean idle) {
        runOnUiThread(new Runnable() {

            @Override
            public void run() {
                if (idle) {
                    mStartCallBtn.setVisibility(View.VISIBLE);
                    mEndCallBtn.setVisibility(View.GONE);
                    mRemoteSurfaceView.setVisibility(View.GONE);
                } else {
                    mStartCallBtn.setVisibility(View.GONE);
                    mEndCallBtn.setVisibility(View.VISIBLE);
                    mRemoteSurfaceView.setVisibility(View.VISIBLE);
                }
            }
        });
    }

    public void doStartCall() {
        logcatOnUI("Start Call, Wait ...");
        if (mPeerConnection == null) {
            mPeerConnection = createPeerConnection();
        }
        MediaConstraints mediaConstraints = new MediaConstraints();
        mediaConstraints.mandatory.add(new MediaConstraints.KeyValuePair("OfferToReceiveAudio", "true"));
        mediaConstraints.mandatory.add(new MediaConstraints.KeyValuePair("OfferToReceiveVideo", "true"));
        mediaConstraints.optional.add(new MediaConstraints.KeyValuePair("DtlsSrtpKeyAgreement", "true"));
        mPeerConnection.createOffer(new SimpleSdpObserver() {

            @Override
            public void onCreateSuccess(SessionDescription sessionDescription) {
                Log.i(TAG, "Create local offer success: \n" + sessionDescription.description);
                mPeerConnection.setLocalDescription(new SimpleSdpObserver(), sessionDescription);
                JSONObject message = new JSONObject();
                try {
                    message.put("userId", RTCSignalClient.getInstance().getUserId());
                    message.put("msgType", RTCSignalClient.MESSAGE_TYPE_OFFER);
                    message.put("sdp", sessionDescription.description);
                    RTCSignalClient.getInstance().sendMessage(message);
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            }
        }, mediaConstraints);
    }

    public void doEndCall() {
        logcatOnUI("End Call, Wait ...");
        hanup();
        JSONObject message = new JSONObject();
        try {
            message.put("userId", RTCSignalClient.getInstance().getUserId());
            message.put("msgType", RTCSignalClient.MESSAGE_TYPE_HANGUP);
            RTCSignalClient.getInstance().sendMessage(message);
        } catch (JSONException e) {
            e.printStackTrace();
        }
    }

    public void doAnswerCall() {
        logcatOnUI("Answer Call, Wait ...");
        if (mPeerConnection == null) {
            mPeerConnection = createPeerConnection();
        }
        MediaConstraints sdpMediaConstraints = new MediaConstraints();
        Log.i(TAG, "Create answer ...");
        mPeerConnection.createAnswer(new SimpleSdpObserver() {

            @Override
            public void onCreateSuccess(SessionDescription sessionDescription) {
                Log.i(TAG, "Create answer success !");
                mPeerConnection.setLocalDescription(new SimpleSdpObserver(), sessionDescription);
                JSONObject message = new JSONObject();
                try {
                    message.put("userId", RTCSignalClient.getInstance().getUserId());
                    message.put("msgType", RTCSignalClient.MESSAGE_TYPE_ANSWER);
                    message.put("sdp", sessionDescription.description);
                    RTCSignalClient.getInstance().sendMessage(message);
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            }
        }, sdpMediaConstraints);
        updateCallState(false);
    }

    private void hanup() {
        logcatOnUI("Hanup Call, Wait ...");
        if (mPeerConnection == null) {
            return;
        }
        mPeerConnection.close();
        mPeerConnection = null;
        logcatOnUI("Hanup Done.");
        updateCallState(true);
    }

    public PeerConnection createPeerConnection() {
        Log.i(TAG, "Create PeerConnection ...");
        PeerConnection.RTCConfiguration configuration = new PeerConnection.RTCConfiguration(new ArrayList<>());
        PeerConnection connection = mPeerConnectionFactory.createPeerConnection(configuration, mPeerConnectionObserver);
        if (connection == null) {
            Log.e(TAG, "Failed to createPeerConnection !");
            return null;
        }
        connection.addTrack(mVideoTrack);
        connection.addTrack(mAudioTrack);
        return connection;
    }

    public PeerConnectionFactory createPeerConnectionFactory(Context context) {
        final VideoEncoderFactory encoderFactory;
        final VideoDecoderFactory decoderFactory;
        encoderFactory = new DefaultVideoEncoderFactory(mRootEglBase.getEglBaseContext(), false, /* enableIntelVp8Encoder */
        true);
        decoderFactory = new DefaultVideoDecoderFactory(mRootEglBase.getEglBaseContext());
        PeerConnectionFactory.initialize(PeerConnectionFactory.InitializationOptions.builder(context).setEnableInternalTracer(true).createInitializationOptions());
        PeerConnectionFactory.Builder builder = PeerConnectionFactory.builder().setVideoEncoderFactory(encoderFactory).setVideoDecoderFactory(decoderFactory);
        builder.setOptions(null);
        return builder.createPeerConnectionFactory();
    }

    /*
     * Read more about Camera2 here
     * https://developer.android.com/reference/android/hardware/camera2/package-summary.html
     **/
    private VideoCapturer createVideoCapturer() {
        if (Camera2Enumerator.isSupported(this)) {
            return createCameraCapturer(new Camera2Enumerator(this));
        } else {
            return createCameraCapturer(new Camera1Enumerator(true));
        }
    }

    private VideoCapturer createCameraCapturer(CameraEnumerator enumerator) {
        final String[] deviceNames = enumerator.getDeviceNames();
        // First, try to find front facing camera
        Log.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
        Log.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;
    }

    private PeerConnection.Observer mPeerConnectionObserver = new PeerConnection.Observer() {

        @Override
        public void onSignalingChange(PeerConnection.SignalingState signalingState) {
            Log.i(TAG, "onSignalingChange: " + signalingState);
        }

        @Override
        public void onIceConnectionChange(PeerConnection.IceConnectionState iceConnectionState) {
            Log.i(TAG, "onIceConnectionChange: " + iceConnectionState);
        }

        @Override
        public void onIceConnectionReceivingChange(boolean b) {
            Log.i(TAG, "onIceConnectionChange: " + b);
        }

        @Override
        public void onIceGatheringChange(PeerConnection.IceGatheringState iceGatheringState) {
            Log.i(TAG, "onIceGatheringChange: " + iceGatheringState);
        }

        @Override
        public void onIceCandidate(IceCandidate iceCandidate) {
            Log.i(TAG, "onIceCandidate: " + iceCandidate);
            try {
                JSONObject message = new JSONObject();
                message.put("userId", RTCSignalClient.getInstance().getUserId());
                message.put("msgType", RTCSignalClient.MESSAGE_TYPE_CANDIDATE);
                message.put("label", iceCandidate.sdpMLineIndex);
                message.put("id", iceCandidate.sdpMid);
                message.put("candidate", iceCandidate.sdp);
                RTCSignalClient.getInstance().sendMessage(message);
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onIceCandidatesRemoved(IceCandidate[] iceCandidates) {
            for (int i = 0; i < iceCandidates.length; i++) {
                Log.i(TAG, "onIceCandidatesRemoved: " + iceCandidates[i]);
            }
            mPeerConnection.removeIceCandidates(iceCandidates);
        }

        @Override
        public void onAddStream(MediaStream mediaStream) {
            Log.i(TAG, "onAddStream: " + mediaStream.videoTracks.size());
        }

        @Override
        public void onRemoveStream(MediaStream mediaStream) {
            Log.i(TAG, "onRemoveStream");
        }

        @Override
        public void onDataChannel(DataChannel dataChannel) {
            Log.i(TAG, "onDataChannel");
        }

        @Override
        public void onRenegotiationNeeded() {
            Log.i(TAG, "onRenegotiationNeeded");
        }

        @Override
        public void onAddTrack(RtpReceiver rtpReceiver, MediaStream[] mediaStreams) {
            MediaStreamTrack track = rtpReceiver.track();
            if (track instanceof VideoTrack) {
                Log.i(TAG, "onAddVideoTrack");
                VideoTrack remoteVideoTrack = (VideoTrack) track;
                remoteVideoTrack.setEnabled(true);
                ProxyVideoSink videoSink = new ProxyVideoSink();
                videoSink.setTarget(mRemoteSurfaceView);
                remoteVideoTrack.addSink(videoSink);
            }
        }
    };

    private RTCSignalClient.OnSignalEventListener mOnSignalEventListener = new RTCSignalClient.OnSignalEventListener() {

        @Override
        public void onConnected() {
            logcatOnUI("Signal Server Connected !");
        }

        @Override
        public void onConnecting() {
            logcatOnUI("Signal Server Connecting !");
        }

        @Override
        public void onDisconnected() {
            logcatOnUI("Signal Server Connecting !");
        }

        @Override
        public void onRemoteUserJoined(String userId) {
            logcatOnUI("Remote User Joined: " + userId);
        }

        @Override
        public void onRemoteUserLeft(String userId) {
            logcatOnUI("Remote User Leaved: " + userId);
        }

        @Override
        public void onBroadcastReceived(JSONObject message) {
            Log.i(TAG, "onBroadcastReceived: " + message);
            try {
                String userId = message.getString("userId");
                int type = message.getInt("msgType");
                switch(type) {
                    case RTCSignalClient.MESSAGE_TYPE_OFFER:
                        onRemoteOfferReceived(userId, message);
                        break;
                    case RTCSignalClient.MESSAGE_TYPE_ANSWER:
                        onRemoteAnswerReceived(userId, message);
                        break;
                    case RTCSignalClient.MESSAGE_TYPE_CANDIDATE:
                        onRemoteCandidateReceived(userId, message);
                        break;
                    case RTCSignalClient.MESSAGE_TYPE_HANGUP:
                        onRemoteHangup(userId);
                        break;
                }
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }

        private void onRemoteOfferReceived(String userId, JSONObject message) {
            logcatOnUI("Receive Remote Call ...");
            if (mPeerConnection == null) {
                mPeerConnection = createPeerConnection();
            }
            try {
                String description = message.getString("sdp");
                mPeerConnection.setRemoteDescription(new SimpleSdpObserver(), new SessionDescription(SessionDescription.Type.OFFER, description));
                doAnswerCall();
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }

        private void onRemoteAnswerReceived(String userId, JSONObject message) {
            logcatOnUI("Receive Remote Answer ...");
            try {
                String description = message.getString("sdp");
                mPeerConnection.setRemoteDescription(new SimpleSdpObserver(), new SessionDescription(SessionDescription.Type.ANSWER, description));
            } catch (JSONException e) {
                e.printStackTrace();
            }
            updateCallState(false);
        }

        private void onRemoteCandidateReceived(String userId, JSONObject message) {
            logcatOnUI("Receive Remote Candidate ...");
            try {
                IceCandidate remoteIceCandidate = new IceCandidate(message.getString("id"), message.getInt("label"), message.getString("candidate"));
                mPeerConnection.addIceCandidate(remoteIceCandidate);
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }

        private void onRemoteHangup(String userId) {
            logcatOnUI("Receive Remote Hanup Event ...");
            hanup();
        }
    };

    private void logcatOnUI(String msg) {
        Log.i(TAG, msg);
        runOnUiThread(new Runnable() {

            @Override
            public void run() {
                String output = mLogcatView.getText() + "\n" + msg;
                mLogcatView.setText(output);
            }
        });
    }
}

19 Source : Peer.java
with MIT License
from ddssingsong

/**
 * Created by dds on 2020/3/11.
 * [email protected]
 */
public clreplaced Peer implements SdpObserver, PeerConnection.Observer {

    private final static String TAG = "dds_Peer";

    private final PeerConnection pc;

    private final String mUserId;

    private List<IceCandidate> queuedRemoteCandidates;

    private SessionDescription localSdp;

    private final PeerConnectionFactory mFactory;

    private final List<PeerConnection.IceServer> mIceLis;

    private final IPeerEvent mEvent;

    private boolean isOffer;

    public MediaStream _remoteStream;

    public SurfaceViewRenderer renderer;

    public ProxyVideoSink sink;

    public Peer(PeerConnectionFactory factory, List<PeerConnection.IceServer> list, String userId, IPeerEvent event) {
        mFactory = factory;
        mIceLis = list;
        mEvent = event;
        mUserId = userId;
        queuedRemoteCandidates = new ArrayList<>();
        this.pc = createPeerConnection();
        Log.d("dds_test", "create Peer:" + mUserId);
    }

    public PeerConnection createPeerConnection() {
        PeerConnection.RTCConfiguration rtcConfig = new PeerConnection.RTCConfiguration(mIceLis);
        if (mFactory != null) {
            return mFactory.createPeerConnection(rtcConfig, this);
        } else {
            return null;
        }
    }

    public void setOffer(boolean isOffer) {
        this.isOffer = isOffer;
    }

    // 创建offer
    public void createOffer() {
        if (pc == null)
            return;
        Log.d("dds_test", "createOffer");
        pc.createOffer(this, offerOrAnswerConstraint());
    }

    // 创建answer
    public void createAnswer() {
        if (pc == null)
            return;
        Log.d("dds_test", "createAnswer");
        pc.createAnswer(this, offerOrAnswerConstraint());
    }

    // 设置LocalDescription
    public void setLocalDescription(SessionDescription sdp) {
        Log.d("dds_test", "setLocalDescription");
        if (pc == null)
            return;
        pc.setLocalDescription(this, sdp);
    }

    // 设置RemoteDescription
    public void setRemoteDescription(SessionDescription sdp) {
        if (pc == null)
            return;
        Log.d("dds_test", "setRemoteDescription");
        pc.setRemoteDescription(this, sdp);
    }

    // 添加本地流
    public void addLocalStream(MediaStream stream) {
        if (pc == null)
            return;
        Log.d("dds_test", "addLocalStream" + mUserId);
        pc.addStream(stream);
    }

    // 添加RemoteIceCandidate
    public synchronized void addRemoteIceCandidate(final IceCandidate candidate) {
        Log.d("dds_test", "addRemoteIceCandidate");
        if (pc != null) {
            if (queuedRemoteCandidates != null) {
                Log.d("dds_test", "addRemoteIceCandidate  2222");
                synchronized (Peer.clreplaced) {
                    if (queuedRemoteCandidates != null) {
                        queuedRemoteCandidates.add(candidate);
                    }
                }
            } else {
                Log.d("dds_test", "addRemoteIceCandidate1111");
                pc.addIceCandidate(candidate);
            }
        }
    }

    // 移除RemoteIceCandidates
    public void removeRemoteIceCandidates(final IceCandidate[] candidates) {
        if (pc == null) {
            return;
        }
        drainCandidates();
        pc.removeIceCandidates(candidates);
    }

    public void createRender(EglBase mRootEglBase, Context context, boolean isOverlay) {
        renderer = new SurfaceViewRenderer(context);
        renderer.init(mRootEglBase.getEglBaseContext(), new RendererCommon.RendererEvents() {

            @Override
            public void onFirstFrameRendered() {
                Log.d(TAG, "createRender onFirstFrameRendered");
            }

            @Override
            public void onFrameResolutionChanged(int videoWidth, int videoHeight, int rotation) {
                Log.d(TAG, "createRender onFrameResolutionChanged");
            }
        });
        renderer.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FILL);
        renderer.setMirror(true);
        renderer.setZOrderMediaOverlay(isOverlay);
        sink = new ProxyVideoSink();
        sink.setTarget(renderer);
        if (_remoteStream != null && _remoteStream.videoTracks.size() > 0) {
            _remoteStream.videoTracks.get(0).addSink(sink);
        }
    }

    // 关闭Peer
    public void close() {
        if (renderer != null) {
            renderer.release();
            renderer = null;
        }
        if (sink != null) {
            sink.setTarget(null);
        }
        if (pc != null) {
            try {
                pc.close();
                pc.dispose();
            } catch (Exception e) {
            }
        }
    }

    // ------------------------------Observer-------------------------------------
    @Override
    public void onSignalingChange(PeerConnection.SignalingState signalingState) {
        Log.i(TAG, "onSignalingChange: " + signalingState);
    }

    @Override
    public void onIceConnectionChange(PeerConnection.IceConnectionState newState) {
        Log.i(TAG, "onIceConnectionChange: " + newState);
        if (newState == PeerConnection.IceConnectionState.DISCONNECTED || newState == PeerConnection.IceConnectionState.FAILED) {
            mEvent.onDisconnected(mUserId);
        }
    }

    @Override
    public void onIceConnectionReceivingChange(boolean receiving) {
        Log.i(TAG, "onIceConnectionReceivingChange:" + receiving);
    }

    @Override
    public void onIceGatheringChange(PeerConnection.IceGatheringState newState) {
        Log.i(TAG, "onIceGatheringChange:" + newState.toString());
    }

    @Override
    public void onIceCandidate(IceCandidate candidate) {
        // 发送IceCandidate
        mEvent.onSendIceCandidate(mUserId, candidate);
    }

    @Override
    public void onIceCandidatesRemoved(IceCandidate[] candidates) {
        Log.i(TAG, "onIceCandidatesRemoved:");
    }

    @Override
    public void onAddStream(MediaStream stream) {
        Log.i(TAG, "onAddStream:");
        stream.audioTracks.get(0).setEnabled(true);
        _remoteStream = stream;
        if (mEvent != null) {
            mEvent.onRemoteStream(mUserId, stream);
        }
    }

    @Override
    public void onRemoveStream(MediaStream stream) {
        Log.i(TAG, "onRemoveStream:");
        if (mEvent != null) {
            mEvent.onRemoveStream(mUserId, stream);
        }
    }

    @Override
    public void onDataChannel(DataChannel dataChannel) {
        Log.i(TAG, "onDataChannel:");
    }

    @Override
    public void onRenegotiationNeeded() {
        Log.i(TAG, "onRenegotiationNeeded:");
    }

    @Override
    public void onAddTrack(RtpReceiver receiver, MediaStream[] mediaStreams) {
        Log.i(TAG, "onAddTrack:" + mediaStreams.length);
    }

    // -------------SdpObserver--------------------
    @Override
    public void onCreateSuccess(SessionDescription origSdp) {
        Log.d(TAG, "sdp创建成功       " + origSdp.type);
        String sdpString = origSdp.description;
        final SessionDescription sdp = new SessionDescription(origSdp.type, sdpString);
        localSdp = sdp;
        setLocalDescription(sdp);
    }

    @Override
    public void onSetSuccess() {
        Log.d(TAG, "sdp连接成功   " + pc.signalingState().toString());
        if (pc == null)
            return;
        // 发送者
        if (isOffer) {
            if (pc.getRemoteDescription() == null) {
                Log.d(TAG, "Local SDP set succesfully");
                if (!isOffer) {
                    // 接收者,发送Answer
                    mEvent.onSendAnswer(mUserId, localSdp);
                } else {
                    // 发送者,发送自己的offer
                    mEvent.onSendOffer(mUserId, localSdp);
                }
            } else {
                Log.d(TAG, "Remote SDP set succesfully");
                drainCandidates();
            }
        } else {
            if (pc.getLocalDescription() != null) {
                Log.d(TAG, "Local SDP set succesfully");
                if (!isOffer) {
                    // 接收者,发送Answer
                    mEvent.onSendAnswer(mUserId, localSdp);
                } else {
                    // 发送者,发送自己的offer
                    mEvent.onSendOffer(mUserId, localSdp);
                }
                drainCandidates();
            } else {
                Log.d(TAG, "Remote SDP set succesfully");
            }
        }
    }

    @Override
    public void onCreateFailure(String error) {
        Log.i(TAG, " SdpObserver onCreateFailure:" + error);
    }

    @Override
    public void onSetFailure(String error) {
        Log.i(TAG, "SdpObserver onSetFailure:" + error);
    }

    private void drainCandidates() {
        Log.i("dds_test", "drainCandidates");
        synchronized (Peer.clreplaced) {
            if (queuedRemoteCandidates != null) {
                Log.d(TAG, "Add " + queuedRemoteCandidates.size() + " remote candidates");
                for (IceCandidate candidate : queuedRemoteCandidates) {
                    pc.addIceCandidate(candidate);
                }
                queuedRemoteCandidates = null;
            }
        }
    }

    private MediaConstraints offerOrAnswerConstraint() {
        MediaConstraints mediaConstraints = new MediaConstraints();
        ArrayList<MediaConstraints.KeyValuePair> keyValuePairs = new ArrayList<>();
        keyValuePairs.add(new MediaConstraints.KeyValuePair("OfferToReceiveAudio", "true"));
        keyValuePairs.add(new MediaConstraints.KeyValuePair("OfferToReceiveVideo", "true"));
        mediaConstraints.mandatory.addAll(keyValuePairs);
        return mediaConstraints;
    }

    // ----------------------------回调-----------------------------------
    public interface IPeerEvent {

        void onSendIceCandidate(String userId, IceCandidate candidate);

        void onSendOffer(String userId, SessionDescription description);

        void onSendAnswer(String userId, SessionDescription description);

        void onRemoteStream(String userId, MediaStream stream);

        void onRemoveStream(String userId, MediaStream stream);

        void onDisconnected(String userId);
    }
}

19 Source : RTCCall.java
with GNU General Public License v3.0
from dakhnod

public clreplaced RTCCall implements DataChannel.Observer {

    enum CallState {

        CONNECTING,
        RINGING,
        CONNECTED,
        DISMISSED,
        ENDED,
        ERROR
    }

    private final String StateChangeMessage = "StateChange";

    private final String CameraDisabledMessage = "CameraDisabled";

    private final String CameraEnabledMessage = "CameraEnabled";

    private OnStateChangeListener listener;

    Socket commSocket;

    private PeerConnectionFactory factory;

    private PeerConnection connection;

    private MediaConstraints constraints;

    private String offer;

    private SurfaceViewRenderer remoteRenderer;

    private SurfaceViewRenderer localRenderer;

    private EglBase.Context sharedContext;

    private CameraVideoCapturer capturer;

    private MediaStream upStream;

    private DataChannel dataChannel;

    private boolean videoEnabled;

    private Context context;

    public CallState state;

    public void setRemoteRenderer(SurfaceViewRenderer remoteRenderer) {
        this.remoteRenderer = remoteRenderer;
    }

    private RTCCall(Contact target, String username, String identifier, OnStateChangeListener listener, Context context) {
        log("starting call to " + target.getAddress());
        initRTC(context);
        this.context = context;
        context.setTheme(AppCompatDelegate.getDefaultNightMode() == AppCompatDelegate.MODE_NIGHT_YES ? R.style.AppTheme_Dark : R.style.AppTheme_Light);
        log("init RTC done");
        this.listener = listener;
        new Thread(() -> {
            log("creating PeerConnection");
            connection = factory.createPeerConnection(Collections.emptyList(), new DefaultObserver() {

                @Override
                public void onIceGatheringChange(PeerConnection.IceGatheringState iceGatheringState) {
                    super.onIceGatheringChange(iceGatheringState);
                    if (iceGatheringState == PeerConnection.IceGatheringState.COMPLETE) {
                        log("transferring offer...");
                        try {
                            commSocket = new Socket(target.getAddress().replace("%zone", "%wlan0"), MainService.serverPort);
                            OutputStream os = commSocket.getOutputStream();
                            reportStateChange(CallState.CONNECTING);
                            JSONObject object = new JSONObject();
                            object.put("action", "call");
                            object.put("username", username);
                            object.put("identifier", identifier);
                            object.put("offer", connection.getLocalDescription().description);
                            os.write((object.toString() + "\n").getBytes());
                            BufferedReader reader = new BufferedReader(new InputStreamReader(commSocket.getInputStream()));
                            String response = reader.readLine();
                            JSONObject responseObject = new JSONObject(response);
                            if (!responseObject.getString("action").equals("ringing")) {
                                commSocket.close();
                                reportStateChange(CallState.ERROR);
                                return;
                            }
                            log("ringing...");
                            reportStateChange(CallState.RINGING);
                            response = reader.readLine();
                            responseObject = new JSONObject(response);
                            if (responseObject.getString("action").equals("connected")) {
                                log("connected");
                                reportStateChange(CallState.CONNECTED);
                                log("answer: " + responseObject.getString("answer"));
                                handleAnswer(responseObject.getString("answer"));
                            } else if (responseObject.getString("action").equals("dismissed")) {
                                reportStateChange(CallState.DISMISSED);
                                commSocket.close();
                            } else {
                                reportStateChange(CallState.ERROR);
                                commSocket.close();
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                            reportStateChange(CallState.ERROR);
                        }
                    }
                }

                @Override
                public void onIceConnectionChange(PeerConnection.IceConnectionState iceConnectionState) {
                    log("change " + iceConnectionState.name());
                    super.onIceConnectionChange(iceConnectionState);
                    if (iceConnectionState == PeerConnection.IceConnectionState.DISCONNECTED) {
                        reportStateChange(CallState.ENDED);
                    }
                }

                @Override
                public void onAddStream(MediaStream mediaStream) {
                    super.onAddStream(mediaStream);
                    handleMediaStream(mediaStream);
                }

                @Override
                public void onDataChannel(DataChannel dataChannel) {
                    super.onDataChannel(dataChannel);
                    RTCCall.this.dataChannel = dataChannel;
                    dataChannel.registerObserver(RTCCall.this);
                }
            });
            log("PeerConnection created");
            connection.addStream(createStream());
            this.dataChannel = connection.createDataChannel("data", new DataChannel.Init());
            this.dataChannel.registerObserver(this);
            connection.createOffer(new DefaultSdpObserver() {

                @Override
                public void onCreateSuccess(SessionDescription sessionDescription) {
                    super.onCreateSuccess(sessionDescription);
                    connection.setLocalDescription(new DefaultSdpObserver(), sessionDescription);
                }
            }, constraints);
        }).start();
    }

    public void switchFrontFacing() {
        if (this.capturer == null)
            return;
        this.capturer.switchCamera(null);
    }

    @Override
    public void onBufferedAmountChange(long l) {
    }

    @Override
    public void onStateChange() {
    }

    @Override
    public void onMessage(DataChannel.Buffer buffer) {
        byte[] data = new byte[buffer.data.remaining()];
        buffer.data.get(data);
        String s = new String(data);
        JSONObject object = null;
        try {
            object = new JSONObject(s);
            if (object.has(StateChangeMessage)) {
                String state = object.getString(StateChangeMessage);
                switch(state) {
                    case CameraEnabledMessage:
                    case CameraDisabledMessage:
                        {
                            setRemoteVideoEnabled(state.equals(CameraEnabledMessage));
                            break;
                        }
                }
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }
    }

    private void setRemoteVideoEnabled(boolean enabled) {
        log("setting remote video enabled: " + enabled);
        new Handler(Looper.getMainLooper()).post(() -> {
            if (enabled) {
                this.remoteRenderer.setBackgroundColor(Color.TRANSPARENT);
            } else {
                TypedValue typedValue = new TypedValue();
                Resources.Theme theme = this.context.getTheme();
                theme.resolveAttribute(R.attr.backgroundCardColor, typedValue, true);
                @ColorInt
                int color = typedValue.data;
                this.remoteRenderer.setBackgroundColor(color);
            }
        });
    }

    public boolean isVideoEnabled() {
        return this.videoEnabled;
    }

    public void setVideoEnabled(boolean enabled) {
        this.videoEnabled = enabled;
        try {
            if (enabled) {
                this.capturer.startCapture(500, 500, 30);
            } else {
                this.capturer.stopCapture();
            }
            JSONObject object = new JSONObject();
            object.put(StateChangeMessage, enabled ? CameraEnabledMessage : CameraDisabledMessage);
            dataChannel.send(new DataChannel.Buffer(ByteBuffer.wrap(object.toString().getBytes()), false));
        } catch (JSONException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /*private void initLocalRenderer() {
        if (this.localRenderer == null) return;
        log("really initng " + (this.sharedContext == null));
        this.localRenderer.init(this.sharedContext, null);
        this.localCameraTrack.addSink(localRenderer);
        this.capturer.startCapture(500, 500, 30);
    }*/
    /*private void initVideoTrack() {
        this.sharedContext = EglBase.create().getEglBaseContext();
        this.capturer = createCapturer(true);
        this.localCameraTrack = factory.createVideoTrack("video1", factory.createVideoSource(capturer));
    }*/
    private CameraVideoCapturer createCapturer() {
        CameraEnumerator enumerator = new Camera1Enumerator();
        for (String name : enumerator.getDeviceNames()) {
            if (enumerator.isFrontFacing(name)) {
                return enumerator.createCapturer(name, null);
            }
        }
        return null;
    }

    public void releaseCamera() {
        if (this.capturer != null) {
            try {
                this.capturer.stopCapture();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        if (this.remoteRenderer != null)
            this.remoteRenderer.release();
        if (this.localRenderer != null)
            this.localRenderer.release();
    }

    private void handleMediaStream(MediaStream stream) {
        log("handling video stream");
        if (this.remoteRenderer == null || stream.videoTracks.size() == 0)
            return;
        new Handler(Looper.getMainLooper()).post(() -> {
            // remoteRenderer.setBackgroundColor(Color.TRANSPARENT);
            remoteRenderer.init(this.sharedContext, null);
            stream.videoTracks.get(0).addSink(remoteRenderer);
        });
    }

    private MediaStream createStream() {
        upStream = factory.createLocalMediaStream("stream1");
        AudioTrack audio = factory.createAudioTrack("audio1", factory.createAudioSource(new MediaConstraints()));
        upStream.addTrack(audio);
        upStream.addTrack(getVideoTrack());
        // this.capturer.startCapture(500, 500, 30);
        return upStream;
    }

    private VideoTrack getVideoTrack() {
        this.capturer = createCapturer();
        return factory.createVideoTrack("video1", factory.createVideoSource(this.capturer));
    }

    private void initRTC(Context c) {
        log("initializing");
        PeerConnectionFactory.initialize(PeerConnectionFactory.InitializationOptions.builder(c).createInitializationOptions());
        log("initialized");
        factory = PeerConnectionFactory.builder().createPeerConnectionFactory();
        log("created");
        constraints = new MediaConstraints();
        constraints.optional.add(new MediaConstraints.KeyValuePair("offerToReceiveAudio", "true"));
        constraints.optional.add(new MediaConstraints.KeyValuePair("offerToReceiveVideo", "false"));
        constraints.optional.add(new MediaConstraints.KeyValuePair("DtlsSrtpKeyAgreement", "true"));
    // initVideoTrack();
    }

    static public RTCCall startCall(Contact target, String username, String identifier, OnStateChangeListener listener, Context context) {
        return new RTCCall(target, username, identifier, listener, context);
    }

    public RTCCall(Socket commSocket, Context context, String offer) {
        this.commSocket = commSocket;
        initRTC(context);
        this.offer = offer;
    }

    private void handleAnswer(String remoteDesc) {
        log("setting remote desc");
        connection.setRemoteDescription(new DefaultSdpObserver() {

            @Override
            public void onSetSuccess() {
                super.onSetSuccess();
                log("success");
            }

            @Override
            public void onSetFailure(String s) {
                super.onSetFailure(s);
                log("failure: " + s);
            }
        }, new SessionDescription(SessionDescription.Type.ANSWER, remoteDesc));
    }

    private void reportStateChange(CallState state) {
        this.state = state;
        if (this.listener != null) {
            this.listener.OnStateChange(state);
        }
    }

    public void accept(OnStateChangeListener listener) {
        this.listener = listener;
        new Thread(() -> {
            connection = factory.createPeerConnection(Collections.emptyList(), new DefaultObserver() {

                @Override
                public void onIceGatheringChange(PeerConnection.IceGatheringState iceGatheringState) {
                    super.onIceGatheringChange(iceGatheringState);
                    if (iceGatheringState == PeerConnection.IceGatheringState.COMPLETE) {
                        log("transferring answer");
                        try {
                            JSONObject o = new JSONObject();
                            o.put("action", "connected");
                            o.put("answer", connection.getLocalDescription().description);
                            commSocket.getOutputStream().write((o.toString() + "\n").getBytes());
                            reportStateChange(CallState.CONNECTED);
                        // new Thread(new SpeakerRunnable(commSocket)).start();
                        } catch (Exception e) {
                            e.printStackTrace();
                            reportStateChange(CallState.ERROR);
                        }
                    }
                }

                @Override
                public void onIceConnectionChange(PeerConnection.IceConnectionState iceConnectionState) {
                    log("change");
                    super.onIceConnectionChange(iceConnectionState);
                    if (iceConnectionState == PeerConnection.IceConnectionState.DISCONNECTED) {
                        reportStateChange(CallState.ENDED);
                    }
                }

                @Override
                public void onAddStream(MediaStream mediaStream) {
                    log("onAddStream");
                    super.onAddStream(mediaStream);
                    handleMediaStream(mediaStream);
                }

                @Override
                public void onDataChannel(DataChannel dataChannel) {
                    super.onDataChannel(dataChannel);
                    RTCCall.this.dataChannel = dataChannel;
                    dataChannel.registerObserver(RTCCall.this);
                }
            });
            connection.addStream(createStream());
            // this.dataChannel = connection.createDataChannel("data", new DataChannel.Init());
            log("setting remote description");
            connection.setRemoteDescription(new DefaultSdpObserver() {

                @Override
                public void onSetSuccess() {
                    super.onSetSuccess();
                    log("creating answer...");
                    connection.createAnswer(new DefaultSdpObserver() {

                        @Override
                        public void onCreateSuccess(SessionDescription sessionDescription) {
                            log("success");
                            super.onCreateSuccess(sessionDescription);
                            connection.setLocalDescription(new DefaultSdpObserver(), sessionDescription);
                        }

                        @Override
                        public void onCreateFailure(String s) {
                            super.onCreateFailure(s);
                            log("failure: " + s);
                        }
                    }, constraints);
                }
            }, new SessionDescription(SessionDescription.Type.OFFER, offer));
        }).start();
    }

    public void decline() {
        new Thread(() -> {
            try {
                log("declining...");
                commSocket.getOutputStream().write("{\"action\":\"dismissed\"}\n".getBytes());
                commSocket.getOutputStream().flush();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }).start();
    }

    public void cleanup() {
        if (this.upStream != null && state == CallState.CONNECTED) {
            /*for(AudioTrack track : this.upStream.audioTracks){
                track.setEnabled(false);
                track.dispose();
            }
            for(VideoTrack track : this.upStream.videoTracks) track.dispose();*/
            if (this.connection != null)
                this.connection.close();
        // factory.dispose();
        }
        if (commSocket != null) {
            try {
                commSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public void hangUp() {
        new Thread(() -> {
            try {
                if (commSocket != null) {
                    commSocket.getOutputStream().write("{\"action\":\"dismissed\"}\n".getBytes());
                    commSocket.close();
                }
                if (connection != null)
                    connection.close();
                reportStateChange(CallState.ENDED);
            } catch (IOException e) {
                e.printStackTrace();
                reportStateChange(CallState.ERROR);
            }
        }).start();
    }

    public interface OnStateChangeListener {

        void OnStateChange(CallState state);
    }

    private void log(String s) {
        Log.d(RTCCall.clreplaced.getSimpleName(), s);
    }
}

19 Source : PeerConnectionWrapper.java
with GNU General Public License v3.0
from bcmapp

public clreplaced PeerConnectionWrapper {

    private static final String TAG = PeerConnectionWrapper.clreplaced.getSimpleName();

    private static final PeerConnection.IceServer STUN_SERVER = new PeerConnection.IceServer("stun:stun1.l.google.com:19302");

    @NonNull
    private final PeerConnection peerConnection;

    @NonNull
    private AudioTrack audioTrack;

    @NonNull
    private AudioSource audioSource;

    @NonNull
    private final Camera camera;

    @Nullable
    private final VideoSource videoSource;

    @Nullable
    private final VideoTrack videoTrack;

    @Nullable
    private MediaStream mediaStream;

    @NonNull
    private List<PeerConnection.IceServer> iceServers;

    public PeerConnectionWrapper(@NonNull Context context, @NonNull PeerConnectionFactory factory, @NonNull PeerConnection.Observer observer, @NonNull VideoSink localRenderer, @NonNull List<PeerConnection.IceServer> turnServers, @NonNull CameraEventListener cameraEventListener, @NonNull EglBase eglBase, boolean hideIp) {
        List<PeerConnection.IceServer> iceServers = new LinkedList<>();
        iceServers.add(STUN_SERVER);
        iceServers.addAll(turnServers);
        this.iceServers = iceServers;
        MediaConstraints constraints = new MediaConstraints();
        MediaConstraints audioConstraints = new MediaConstraints();
        PeerConnection.RTCConfiguration configuration = new PeerConnection.RTCConfiguration(iceServers);
        configuration.bundlePolicy = PeerConnection.BundlePolicy.MAXBUNDLE;
        configuration.rtcpMuxPolicy = PeerConnection.RtcpMuxPolicy.REQUIRE;
        if (hideIp) {
            configuration.iceTransportsType = PeerConnection.IceTransportsType.RELAY;
        }
        constraints.optional.add(new MediaConstraints.KeyValuePair("DtlsSrtpKeyAgreement", "true"));
        audioConstraints.optional.add(new MediaConstraints.KeyValuePair("DtlsSrtpKeyAgreement", "true"));
        this.peerConnection = factory.createPeerConnection(configuration, constraints, observer);
        this.peerConnection.setAudioPlayout(false);
        this.peerConnection.setAudioRecording(false);
        this.mediaStream = factory.createLocalMediaStream("ARDAMS");
        this.audioSource = factory.createAudioSource(audioConstraints);
        this.audioTrack = factory.createAudioTrack("ARDAMSa0", audioSource);
        this.audioTrack.setEnabled(false);
        mediaStream.addTrack(audioTrack);
        this.camera = new Camera(context, cameraEventListener);
        if (camera.capturer != null) {
            this.videoSource = factory.createVideoSource(false);
            this.videoTrack = factory.createVideoTrack("ARDAMSv0", videoSource);
            camera.capturer.initialize(SurfaceTextureHelper.create("WebRTC-SurfaceTextureHelper", eglBase.getEglBaseContext()), context, videoSource.getCapturerObserver());
            this.videoTrack.addSink(localRenderer);
            this.videoTrack.setEnabled(false);
            mediaStream.addTrack(videoTrack);
        } else {
            this.videoSource = null;
            this.videoTrack = null;
        }
        this.peerConnection.addStream(mediaStream);
    }

    public void reInitAudioTrack(@NonNull PeerConnectionFactory factory) {
        this.peerConnection.removeStream(this.mediaStream);
        this.mediaStream.removeTrack(audioTrack);
        MediaConstraints audioConstraints = new MediaConstraints();
        audioConstraints.optional.add(new MediaConstraints.KeyValuePair("DtlsSrtpKeyAgreement", "true"));
        this.audioSource = factory.createAudioSource(audioConstraints);
        this.audioTrack = factory.createAudioTrack("ARDAMSa0", audioSource);
        this.audioTrack.setEnabled(false);
        this.mediaStream.addTrack(audioTrack);
        this.peerConnection.addStream(this.mediaStream);
    }

    public void setVideoEnabled(boolean enabled) {
        if (this.videoTrack != null) {
            this.videoTrack.setEnabled(enabled);
        }
        camera.setEnabled(enabled);
    }

    public void flipCamera() {
        camera.flip();
    }

    public CameraState getCameraState() {
        return new CameraState(camera.getActiveDirection(), camera.getCount());
    }

    public void setCommunicationMode() {
        this.peerConnection.setAudioPlayout(true);
        this.peerConnection.setAudioRecording(true);
    }

    public void setAudioEnabled(boolean enabled) {
        this.audioTrack.setEnabled(enabled);
    }

    public DataChannel createDataChannel(String name) {
        DataChannel.Init dataChannelConfiguration = new DataChannel.Init();
        dataChannelConfiguration.ordered = true;
        return this.peerConnection.createDataChannel(name, dataChannelConfiguration);
    }

    public List<PeerConnection.IceServer> getServerList() {
        return iceServers;
    }

    public SessionDescription createOffer(MediaConstraints mediaConstraints) throws PeerConnectionException {
        final SettableFuture<SessionDescription> future = new SettableFuture<>();
        peerConnection.createOffer(new SdpObserver() {

            @Override
            public void onCreateSuccess(SessionDescription sdp) {
                future.set(sdp);
            }

            @Override
            public void onCreateFailure(String error) {
                future.setException(new PeerConnectionException(error));
            }

            @Override
            public void onSetSuccess() {
                throw new replacedertionError();
            }

            @Override
            public void onSetFailure(String error) {
                throw new replacedertionError();
            }
        }, mediaConstraints);
        try {
            return correctSessionDescription(future.get());
        } catch (InterruptedException e) {
            throw new replacedertionError(e);
        } catch (ExecutionException e) {
            throw new PeerConnectionException(e);
        }
    }

    public SessionDescription createAnswer(MediaConstraints mediaConstraints) throws PeerConnectionException {
        final SettableFuture<SessionDescription> future = new SettableFuture<>();
        peerConnection.createAnswer(new SdpObserver() {

            @Override
            public void onCreateSuccess(SessionDescription sdp) {
                future.set(sdp);
            }

            @Override
            public void onCreateFailure(String error) {
                future.setException(new PeerConnectionException(error));
            }

            @Override
            public void onSetSuccess() {
                throw new replacedertionError();
            }

            @Override
            public void onSetFailure(String error) {
                throw new replacedertionError();
            }
        }, mediaConstraints);
        try {
            return correctSessionDescription(future.get());
        } catch (InterruptedException e) {
            throw new replacedertionError(e);
        } catch (ExecutionException e) {
            throw new PeerConnectionException(e);
        }
    }

    public void setRemoteDescription(SessionDescription sdp) throws PeerConnectionException {
        final SettableFuture<Boolean> future = new SettableFuture<>();
        peerConnection.setRemoteDescription(new SdpObserver() {

            @Override
            public void onCreateSuccess(SessionDescription sdp) {
            }

            @Override
            public void onCreateFailure(String error) {
            }

            @Override
            public void onSetSuccess() {
                future.set(true);
            }

            @Override
            public void onSetFailure(String error) {
                future.setException(new PeerConnectionException(error));
            }
        }, sdp);
        try {
            future.get();
        } catch (InterruptedException e) {
            throw new replacedertionError(e);
        } catch (ExecutionException e) {
            throw new PeerConnectionException(e);
        }
    }

    public void setLocalDescription(SessionDescription sdp) throws PeerConnectionException {
        final SettableFuture<Boolean> future = new SettableFuture<>();
        peerConnection.setLocalDescription(new SdpObserver() {

            @Override
            public void onCreateSuccess(SessionDescription sdp) {
                throw new replacedertionError();
            }

            @Override
            public void onCreateFailure(String error) {
                throw new replacedertionError();
            }

            @Override
            public void onSetSuccess() {
                future.set(true);
            }

            @Override
            public void onSetFailure(String error) {
                future.setException(new PeerConnectionException(error));
            }
        }, sdp);
        try {
            future.get();
        } catch (InterruptedException e) {
            throw new replacedertionError(e);
        } catch (ExecutionException e) {
            throw new PeerConnectionException(e);
        }
    }

    public void dispose() {
        this.camera.dispose();
        if (this.videoSource != null) {
            this.videoSource.dispose();
        }
        this.audioSource.dispose();
        this.peerConnection.close();
        this.peerConnection.dispose();
    }

    public boolean addIceCandidate(IceCandidate candidate) {
        return this.peerConnection.addIceCandidate(candidate);
    }

    private SessionDescription correctSessionDescription(SessionDescription sessionDescription) {
        String updatedSdp = sessionDescription.description.replaceAll("(a=fmtp:111 ((?!cbr=).)*)\r?\n", "$1;cbr=1\r\n");
        updatedSdp = updatedSdp.replaceAll(".+urn:ietf:params:rtp-hdrext:ssrc-audio-level.*\r?\n", "");
        return new SessionDescription(sessionDescription.type, updatedSdp);
    }

    public static clreplaced PeerConnectionException extends Exception {

        public PeerConnectionException(String error) {
            super(error);
        }

        public PeerConnectionException(Throwable throwable) {
            super(throwable);
        }
    }

    private static clreplaced Camera implements CameraVideoCapturer.CameraSwitchHandler {

        @Nullable
        private final CameraVideoCapturer capturer;

        private final CameraEventListener cameraEventListener;

        private final int cameraCount;

        private CameraState.Direction activeDirection;

        private boolean enabled;

        Camera(@NonNull Context context, @NonNull CameraEventListener cameraEventListener) {
            this.cameraEventListener = cameraEventListener;
            CameraEnumerator enumerator = getCameraEnumerator(context);
            cameraCount = enumerator.getDeviceNames().length;
            CameraVideoCapturer cameraVideoCapturer = createVideoCapturer(enumerator, FRONT);
            if (cameraVideoCapturer != null) {
                activeDirection = FRONT;
            } else {
                cameraVideoCapturer = createVideoCapturer(enumerator, BACK);
                if (cameraVideoCapturer != null) {
                    activeDirection = BACK;
                } else {
                    activeDirection = NONE;
                }
            }
            capturer = cameraVideoCapturer;
        }

        void flip() {
            if (capturer == null || cameraCount < 2) {
                ALog.w(TAG, "Only have " + cameraCount + " cameras in this device.");
                return;
            }
            activeDirection = PENDING;
            capturer.switchCamera(this);
        }

        void setEnabled(boolean enabled) {
            this.enabled = enabled;
            if (capturer == null) {
                return;
            }
            try {
                if (enabled) {
                    capturer.startCapture(1280, 720, 30);
                } else {
                    capturer.stopCapture();
                }
            } catch (InterruptedException e) {
                ALog.w(TAG, "Get interrupted while trying to stop video capture. " + e.getMessage());
            }
        }

        void dispose() {
            if (capturer != null) {
                capturer.dispose();
            }
        }

        int getCount() {
            return cameraCount;
        }

        @NonNull
        CameraState.Direction getActiveDirection() {
            return enabled ? activeDirection : NONE;
        }

        @Nullable
        CameraVideoCapturer getCapturer() {
            return capturer;
        }

        @Nullable
        private CameraVideoCapturer createVideoCapturer(@NonNull CameraEnumerator enumerator, @NonNull CameraState.Direction direction) {
            String[] deviceNames = enumerator.getDeviceNames();
            for (String deviceName : deviceNames) {
                if ((direction == FRONT && enumerator.isFrontFacing(deviceName)) || (direction == BACK && enumerator.isBackFacing(deviceName))) {
                    return enumerator.createCapturer(deviceName, null);
                }
            }
            return null;
        }

        @NonNull
        private CameraEnumerator getCameraEnumerator(@NonNull Context context) {
            boolean camera2EnumeratorIsSupported = false;
            try {
                camera2EnumeratorIsSupported = Camera2Enumerator.isSupported(context);
            } catch (final Throwable throwable) {
                ALog.w(TAG, "Camera2Enumator.isSupport() threw. " + throwable.getMessage());
            }
            ALog.i(TAG, "Camera2 enumerator supported: " + camera2EnumeratorIsSupported);
            return camera2EnumeratorIsSupported ? new Camera2Enumerator(context) : new Camera1Enumerator(true);
        }

        @Override
        public void onCameraSwitchDone(boolean isFrontFacing) {
            activeDirection = isFrontFacing ? FRONT : BACK;
            cameraEventListener.onCameraSwitchCompleted(new CameraState(getActiveDirection(), getCount()));
        }

        @Override
        public void onCameraSwitchError(String errorMessage) {
            ALog.e(TAG, "onCameraSwitchError: " + errorMessage);
            cameraEventListener.onCameraSwitchCompleted(new CameraState(getActiveDirection(), getCount()));
        }
    }

    public interface CameraEventListener {

        void onCameraSwitchCompleted(@NonNull CameraState newCameraState);
    }
}

19 Source : WebRtcActivity.java
with Apache License 2.0
from awslabs

public clreplaced WebRtcActivity extends AppCompatActivity {

    private static final String TAG = "KVSWebRtcActivity";

    private static final String AudioTrackID = "KvsAudioTrack";

    private static final String VideoTrackID = "KvsVideoTrack";

    private static final String LOCAL_MEDIA_STREAM_LABEL = "KvsLocalMediaStream";

    private static final int VIDEO_SIZE_WIDTH = 400;

    private static final int VIDEO_SIZE_HEIGHT = 300;

    private static final int VIDEO_FPS = 30;

    private static final String CHANNEL_ID = "WebRtcDataChannel";

    private static final boolean ENABLE_INTEL_VP8_ENCODER = true;

    private static final boolean ENABLE_H264_HIGH_PROFILE = true;

    private static volatile SignalingServiceWebSocketClient client;

    private PeerConnectionFactory peerConnectionFactory;

    private VideoSource videoSource;

    private VideoTrack localVideoTrack;

    private AudioManager audioManager;

    private int originalAudioMode;

    private boolean originalSpeakerphoneOn;

    private AudioTrack localAudioTrack;

    private SurfaceViewRenderer localView;

    private SurfaceViewRenderer remoteView;

    private PeerConnection localPeer;

    private EglBase rootEglBase = null;

    private VideoCapturer videoCapturer;

    private final List<IceServer> peerIceServers = new ArrayList<>();

    private boolean gotException = false;

    private String recipientClientId;

    private int mNotificationId = 0;

    private boolean master = true;

    private boolean isAudioSent = false;

    private EditText dataChannelText = null;

    private Button sendDataChannelButton = null;

    private String mChannelArn;

    private String mClientId;

    private String mWssEndpoint;

    private String mRegion;

    private boolean mCameraFacingFront = true;

    private AWSCredentials mCreds = null;

    // Map to keep track of established peer connections by IDs
    private HashMap<String, PeerConnection> peerConnectionFoundMap = new HashMap<String, PeerConnection>();

    // Map to keep track of ICE candidates received for a client ID before peer connection is established
    private HashMap<String, Queue<IceCandidate>> pendingIceCandidatesMap = new HashMap<String, Queue<IceCandidate>>();

    private void initWsConnection() {
        final String masterEndpoint = mWssEndpoint + "?X-Amz-ChannelARN=" + mChannelArn;
        final String viewerEndpoint = mWssEndpoint + "?X-Amz-ChannelARN=" + mChannelArn + "&X-Amz-ClientId=" + mClientId;
        URI signedUri;
        runOnUiThread(new Runnable() {

            @Override
            public void run() {
                mCreds = KinesisVideoWebRtcDemoApp.getCredentialsProvider().getCredentials();
            }
        });
        signedUri = getSignedUri(masterEndpoint, viewerEndpoint);
        if (master) {
            createLocalPeerConnection();
        }
        final String wsHost = signedUri.toString();
        final SignalingListener signalingListener = new SignalingListener() {

            @Override
            public void onSdpOffer(final Event offerEvent) {
                Log.d(TAG, "Received SDP Offer: Setting Remote Description ");
                final String sdp = Event.parseOfferEvent(offerEvent);
                localPeer.setRemoteDescription(new KinesisVideoSdpObserver(), new SessionDescription(SessionDescription.Type.OFFER, sdp));
                recipientClientId = offerEvent.getSenderClientId();
                Log.d(TAG, "Received SDP offer for client ID: " + recipientClientId + ".Creating answer");
                createSdpAnswer();
            }

            @Override
            public void onSdpAnswer(final Event answerEvent) {
                Log.d(TAG, "SDP answer received from signaling");
                final String sdp = Event.parseSdpEvent(answerEvent);
                final SessionDescription sdpAnswer = new SessionDescription(SessionDescription.Type.ANSWER, sdp);
                localPeer.setRemoteDescription(new KinesisVideoSdpObserver(), sdpAnswer);
                Log.d(TAG, "Answer Client ID: " + answerEvent.getSenderClientId());
                peerConnectionFoundMap.put(answerEvent.getSenderClientId(), localPeer);
                // Check if ICE candidates are available in the queue and add the candidate
                handlePendingIceCandidates(answerEvent.getSenderClientId());
            }

            @Override
            public void onIceCandidate(Event message) {
                Log.d(TAG, "Received IceCandidate from remote ");
                final IceCandidate iceCandidate = Event.parseIceCandidate(message);
                if (iceCandidate != null) {
                    checkAndAddIceCandidate(message, iceCandidate);
                } else {
                    Log.e(TAG, "Invalid Ice candidate");
                }
            }

            @Override
            public void onError(Event errorMessage) {
                Log.e(TAG, "Received error message" + errorMessage);
            }

            @Override
            public void onException(Exception e) {
                Log.e(TAG, "Signaling client returned exception " + e.getMessage());
                gotException = true;
            }
        };
        if (wsHost != null) {
            try {
                client = new SignalingServiceWebSocketClient(wsHost, signalingListener, Executors.newFixedThreadPool(10));
                Log.d(TAG, "Client connection " + (client.isOpen() ? "Successful" : "Failed"));
            } catch (Exception e) {
                gotException = true;
            }
            if (isValidClient()) {
                Log.d(TAG, "Client connected to Signaling service " + client.isOpen());
                if (!master) {
                    Log.d(TAG, "Signaling service is connected: " + // Viewer
                    "Sending offer as viewer to remote peer");
                    createSdpOffer();
                }
            } else {
                Log.e(TAG, "Error in connecting to signaling service");
                gotException = true;
            }
        }
    }

    private boolean isValidClient() {
        return client != null && client.isOpen();
    }

    private void handlePendingIceCandidates(String clientId) {
        // Add any pending ICE candidates from the queue for the client ID
        Log.d(TAG, "Pending ice candidates found? " + pendingIceCandidatesMap.get(clientId));
        Queue<IceCandidate> pendingIceCandidatesQueueByClientId = pendingIceCandidatesMap.get(clientId);
        while (pendingIceCandidatesQueueByClientId != null && !pendingIceCandidatesQueueByClientId.isEmpty()) {
            final IceCandidate iceCandidate = pendingIceCandidatesQueueByClientId.peek();
            final PeerConnection peer = peerConnectionFoundMap.get(clientId);
            final boolean addIce = peer.addIceCandidate(iceCandidate);
            Log.d(TAG, "Added ice candidate after SDP exchange " + iceCandidate + " " + (addIce ? "Successfully" : "Failed"));
            pendingIceCandidatesQueueByClientId.remove();
        }
        // After sending pending ICE candidates, the client ID's peer connection need not be tracked
        pendingIceCandidatesMap.remove(clientId);
    }

    private void checkAndAddIceCandidate(Event message, IceCandidate iceCandidate) {
        // if answer/offer is not received, it means peer connection is not found. Hold the received ICE candidates in the map.
        if (!peerConnectionFoundMap.containsKey(message.getSenderClientId())) {
            Log.d(TAG, "SDP exchange is not complete. Ice candidate " + iceCandidate + " + added to pending queue");
            // If the entry for the client ID already exists (in case of subsequent ICE candidates), update the queue
            if (pendingIceCandidatesMap.containsKey(message.getSenderClientId())) {
                Queue<IceCandidate> pendingIceCandidatesQueueByClientId = pendingIceCandidatesMap.get(message.getSenderClientId());
                pendingIceCandidatesQueueByClientId.add(iceCandidate);
                pendingIceCandidatesMap.put(message.getSenderClientId(), pendingIceCandidatesQueueByClientId);
            } else // If the first ICE candidate before peer connection is received, add entry to map and ICE candidate to a queue
            {
                Queue<IceCandidate> pendingIceCandidatesQueueByClientId = new LinkedList<>();
                pendingIceCandidatesQueueByClientId.add(iceCandidate);
                pendingIceCandidatesMap.put(message.getSenderClientId(), pendingIceCandidatesQueueByClientId);
            }
        } else // This is the case where peer connection is established and ICE candidates are received for the established
        // connection
        {
            Log.d(TAG, "Peer connection found already");
            // Remote sent us ICE candidates, add to local peer connection
            final PeerConnection peer = peerConnectionFoundMap.get(message.getSenderClientId());
            final boolean addIce = peer.addIceCandidate(iceCandidate);
            Log.d(TAG, "Added ice candidate " + iceCandidate + " " + (addIce ? "Successfully" : "Failed"));
        }
    }

    @Override
    protected void onDestroy() {
        Thread.setDefaultUncaughtExceptionHandler(null);
        audioManager.setMode(originalAudioMode);
        audioManager.setSpeakerphoneOn(originalSpeakerphoneOn);
        if (rootEglBase != null) {
            rootEglBase.release();
            rootEglBase = null;
        }
        if (remoteView != null) {
            remoteView.release();
            remoteView = null;
        }
        if (localPeer != null) {
            localPeer.dispose();
            localPeer = null;
        }
        if (videoSource != null) {
            videoSource.dispose();
            videoSource = null;
        }
        if (videoCapturer != null) {
            try {
                videoCapturer.stopCapture();
            } catch (InterruptedException e) {
                Log.e(TAG, "Failed to stop webrtc video capture. ", e);
            }
            videoCapturer = null;
        }
        if (localView != null) {
            localView.release();
            localView = null;
        }
        if (client != null) {
            client.disconnect();
            client = null;
        }
        peerConnectionFoundMap.clear();
        pendingIceCandidatesMap.clear();
        finish();
        super.onDestroy();
    }

    @Override
    protected void onPostCreate(@Nullable Bundle savedInstanceState) {
        super.onPostCreate(savedInstanceState);
        // Start websocket after adding local audio/video tracks
        initWsConnection();
        if (!gotException && isValidClient()) {
            Toast.makeText(this, "Signaling Connected", Toast.LENGTH_LONG).show();
        } else {
            notifySignalingConnectionFailed();
        }
    }

    private void notifySignalingConnectionFailed() {
        finish();
        Toast.makeText(this, "Connection error to signaling", Toast.LENGTH_LONG).show();
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
        Intent intent = getIntent();
        mChannelArn = intent.getStringExtra(KEY_CHANNEL_ARN);
        mWssEndpoint = intent.getStringExtra(KEY_WSS_ENDPOINT);
        mClientId = intent.getStringExtra(KEY_CLIENT_ID);
        if (mClientId == null || mClientId.isEmpty()) {
            mClientId = UUID.randomUUID().toString();
        }
        master = intent.getBooleanExtra(KEY_IS_MASTER, true);
        isAudioSent = intent.getBooleanExtra(KEY_SEND_AUDIO, false);
        ArrayList<String> mUserNames = intent.getStringArrayListExtra(KEY_ICE_SERVER_USER_NAME);
        ArrayList<String> mPreplacedwords = intent.getStringArrayListExtra(KEY_ICE_SERVER_PreplacedWORD);
        ArrayList<Integer> mTTLs = intent.getIntegerArrayListExtra(KEY_ICE_SERVER_TTL);
        ArrayList<List<String>> mUrisList = (ArrayList<List<String>>) intent.getSerializableExtra(KEY_ICE_SERVER_URI);
        mRegion = intent.getStringExtra(KEY_REGION);
        mCameraFacingFront = intent.getBooleanExtra(KEY_CAMERA_FRONT_FACING, true);
        rootEglBase = EglBase.create();
        // TODO: add ui to control TURN only option
        PeerConnection.IceServer stun = PeerConnection.IceServer.builder(String.format("stun:stun.kinesisvideo.%s.amazonaws.com:443", mRegion)).createIceServer();
        peerIceServers.add(stun);
        if (mUrisList != null) {
            for (int i = 0; i < mUrisList.size(); i++) {
                String turnServer = mUrisList.get(i).toString();
                if (turnServer != null) {
                    IceServer iceServer = IceServer.builder(turnServer.replace("[", "").replace("]", "")).setUsername(mUserNames.get(i)).setPreplacedword(mPreplacedwords.get(i)).createIceServer();
                    Log.d(TAG, "IceServer details (TURN) = " + iceServer.toString());
                    peerIceServers.add(iceServer);
                }
            }
        }
        setContentView(R.layout.activity_webrtc_main);
        PeerConnectionFactory.initialize(PeerConnectionFactory.InitializationOptions.builder(this).createInitializationOptions());
        peerConnectionFactory = PeerConnectionFactory.builder().setVideoDecoderFactory(new DefaultVideoDecoderFactory(rootEglBase.getEglBaseContext())).setVideoEncoderFactory(new DefaultVideoEncoderFactory(rootEglBase.getEglBaseContext(), ENABLE_INTEL_VP8_ENCODER, ENABLE_H264_HIGH_PROFILE)).createPeerConnectionFactory();
        videoCapturer = createVideoCapturer();
        // Local video view
        localView = findViewById(R.id.local_view);
        localView.init(rootEglBase.getEglBaseContext(), null);
        localView.setEnableHardwareScaler(true);
        videoSource = peerConnectionFactory.createVideoSource(false);
        SurfaceTextureHelper surfaceTextureHelper = SurfaceTextureHelper.create(Thread.currentThread().getName(), rootEglBase.getEglBaseContext());
        videoCapturer.initialize(surfaceTextureHelper, this.getApplicationContext(), videoSource.getCapturerObserver());
        localVideoTrack = peerConnectionFactory.createVideoTrack(VideoTrackID, videoSource);
        localVideoTrack.addSink(localView);
        if (isAudioSent) {
            AudioSource audioSource = peerConnectionFactory.createAudioSource(new MediaConstraints());
            localAudioTrack = peerConnectionFactory.createAudioTrack(AudioTrackID, audioSource);
            localAudioTrack.setEnabled(true);
        }
        audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
        originalAudioMode = audioManager.getMode();
        originalSpeakerphoneOn = audioManager.isSpeakerphoneOn();
        // Start capturing video
        videoCapturer.startCapture(VIDEO_SIZE_WIDTH, VIDEO_SIZE_HEIGHT, VIDEO_FPS);
        localVideoTrack.setEnabled(true);
        remoteView = findViewById(R.id.remote_view);
        remoteView.init(rootEglBase.getEglBaseContext(), null);
        dataChannelText = findViewById(R.id.data_channel_text);
        sendDataChannelButton = findViewById(R.id.send_data_channel_text);
        createNotificationChannel();
    }

    private VideoCapturer createVideoCapturer() {
        VideoCapturer videoCapturer;
        Logging.d(TAG, "Create camera");
        videoCapturer = createCameraCapturer(new Camera1Enumerator(false));
        return videoCapturer;
    }

    private VideoCapturer createCameraCapturer(CameraEnumerator enumerator) {
        final String[] deviceNames = enumerator.getDeviceNames();
        Logging.d(TAG, "Enumerating cameras");
        for (String deviceName : deviceNames) {
            if (mCameraFacingFront ? enumerator.isFrontFacing(deviceName) : enumerator.isBackFacing(deviceName)) {
                Logging.d(TAG, "Camera created");
                VideoCapturer videoCapturer = enumerator.createCapturer(deviceName, null);
                if (videoCapturer != null) {
                    return videoCapturer;
                }
            }
        }
        return null;
    }

    private void createLocalPeerConnection() {
        PeerConnection.RTCConfiguration rtcConfig = new PeerConnection.RTCConfiguration(peerIceServers);
        rtcConfig.bundlePolicy = PeerConnection.BundlePolicy.MAXBUNDLE;
        rtcConfig.sdpSemantics = PeerConnection.SdpSemantics.UNIFIED_PLAN;
        rtcConfig.continualGatheringPolicy = PeerConnection.ContinualGatheringPolicy.GATHER_CONTINUALLY;
        rtcConfig.keyType = PeerConnection.KeyType.ECDSA;
        rtcConfig.rtcpMuxPolicy = PeerConnection.RtcpMuxPolicy.REQUIRE;
        rtcConfig.tcpCandidatePolicy = PeerConnection.TcpCandidatePolicy.ENABLED;
        localPeer = peerConnectionFactory.createPeerConnection(rtcConfig, new KinesisVideoPeerConnection() {

            @Override
            public void onIceCandidate(IceCandidate iceCandidate) {
                super.onIceCandidate(iceCandidate);
                Message message = createIceCandidateMessage(iceCandidate);
                Log.d(TAG, "Sending IceCandidate to remote peer " + iceCandidate.toString());
                client.sendIceCandidate(message);
            /* Send to Peer */
            }

            @Override
            public void onAddStream(MediaStream mediaStream) {
                super.onAddStream(mediaStream);
                Log.d(TAG, "Adding remote video stream (and audio) to the view");
                addRemoteStreamToVideoView(mediaStream);
            }

            @Override
            public void onDataChannel(DataChannel dataChannel) {
                super.onDataChannel(dataChannel);
                dataChannel.registerObserver(new DataChannel.Observer() {

                    @Override
                    public void onBufferedAmountChange(long l) {
                    // no op on receiver side
                    }

                    @Override
                    public void onStateChange() {
                        Log.d(TAG, "Remote Data Channel onStateChange: state: " + dataChannel.state().toString());
                    }

                    @Override
                    public void onMessage(DataChannel.Buffer buffer) {
                        runOnUiThread(new Runnable() {

                            @Override
                            public void run() {
                                byte[] bytes;
                                if (buffer.data.hasArray()) {
                                    bytes = buffer.data.array();
                                } else {
                                    bytes = new byte[buffer.data.remaining()];
                                    buffer.data.get(bytes);
                                }
                                NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext(), CHANNEL_ID).setSmallIcon(R.mipmap.ic_launcher).setLargeIcon(BitmapFactory.decodeResource(getApplicationContext().getResources(), R.mipmap.ic_launcher)).setContentreplacedle("Message from Peer!").setContentText(new String(bytes, Charset.defaultCharset())).setPriority(NotificationCompat.PRIORITY_MAX).setAutoCancel(true);
                                NotificationManagerCompat notificationManager = NotificationManagerCompat.from(getApplicationContext());
                                // notificationId is a unique int for each notification that you must define
                                notificationManager.notify(mNotificationId++, builder.build());
                                Toast.makeText(getApplicationContext(), "New message from peer, check notification.", Toast.LENGTH_SHORT).show();
                            }
                        });
                    }
                });
            }
        });
        if (localPeer != null) {
            localPeer.getStats(new RTCStatsCollectorCallback() {

                @Override
                public void onStatsDelivered(RTCStatsReport rtcStatsReport) {
                    Map<String, RTCStats> statsMap = rtcStatsReport.getStatsMap();
                    Set<Map.Entry<String, RTCStats>> entries = statsMap.entrySet();
                    for (Map.Entry<String, RTCStats> entry : entries) {
                        Log.d(TAG, "Stats: " + entry.getKey() + " ," + entry.getValue());
                    }
                }
            });
        }
        addDataChannelToLocalPeer();
        addStreamToLocalPeer();
    }

    private Message createIceCandidateMessage(IceCandidate iceCandidate) {
        String sdpMid = iceCandidate.sdpMid;
        int sdpMLineIndex = iceCandidate.sdpMLineIndex;
        String sdp = iceCandidate.sdp;
        String messagePayload = "{\"candidate\":\"" + sdp + "\",\"sdpMid\":\"" + sdpMid + "\",\"sdpMLineIndex\":" + sdpMLineIndex + "}";
        String senderClientId = (master) ? "" : mClientId;
        return new Message("ICE_CANDIDATE", recipientClientId, senderClientId, new String(Base64.encode(messagePayload.getBytes(), Base64.URL_SAFE | Base64.NO_PADDING | Base64.NO_WRAP)));
    }

    private void addStreamToLocalPeer() {
        MediaStream stream = peerConnectionFactory.createLocalMediaStream(LOCAL_MEDIA_STREAM_LABEL);
        if (!stream.addTrack(localVideoTrack)) {
            Log.e(TAG, "Add video track failed");
        }
        localPeer.addTrack(stream.videoTracks.get(0), Collections.singletonList(stream.getId()));
        if (isAudioSent) {
            if (!stream.addTrack(localAudioTrack)) {
                Log.e(TAG, "Add audio track failed");
            }
            if (stream.audioTracks.size() > 0) {
                localPeer.addTrack(stream.audioTracks.get(0), Collections.singletonList(stream.getId()));
                Log.d(TAG, "Sending audio track ");
            }
        }
    }

    private void addDataChannelToLocalPeer() {
        Log.d(TAG, "Data channel addDataChannelToLocalPeer");
        DataChannel localDataChannel = localPeer.createDataChannel("data-channel-of-" + mClientId, new DataChannel.Init());
        localDataChannel.registerObserver(new DataChannel.Observer() {

            @Override
            public void onBufferedAmountChange(long l) {
                Log.d(TAG, "Local Data Channel onBufferedAmountChange called with amount " + l);
            }

            @Override
            public void onStateChange() {
                Log.d(TAG, "Local Data Channel onStateChange: state: " + localDataChannel.state().toString());
                if (sendDataChannelButton != null) {
                    runOnUiThread(() -> {
                        if (localDataChannel.state() == DataChannel.State.OPEN) {
                            sendDataChannelButton.setEnabled(true);
                        } else {
                            sendDataChannelButton.setEnabled(false);
                        }
                    });
                }
            }

            @Override
            public void onMessage(DataChannel.Buffer buffer) {
            // Send out data, no op on sender side
            }
        });
        sendDataChannelButton.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View view) {
                localDataChannel.send(new DataChannel.Buffer(ByteBuffer.wrap(dataChannelText.getText().toString().getBytes(Charset.defaultCharset())), false));
                dataChannelText.setText("");
            }
        });
    }

    // when mobile sdk is viewer
    private void createSdpOffer() {
        MediaConstraints sdpMediaConstraints = new MediaConstraints();
        sdpMediaConstraints.mandatory.add(new MediaConstraints.KeyValuePair("OfferToReceiveVideo", "true"));
        sdpMediaConstraints.mandatory.add(new MediaConstraints.KeyValuePair("OfferToReceiveAudio", "true"));
        if (localPeer == null) {
            createLocalPeerConnection();
        }
        localPeer.createOffer(new KinesisVideoSdpObserver() {

            @Override
            public void onCreateSuccess(SessionDescription sessionDescription) {
                super.onCreateSuccess(sessionDescription);
                localPeer.setLocalDescription(new KinesisVideoSdpObserver(), sessionDescription);
                Message sdpOfferMessage = Message.createOfferMessage(sessionDescription, mClientId);
                if (isValidClient()) {
                    client.sendSdpOffer(sdpOfferMessage);
                } else {
                    notifySignalingConnectionFailed();
                }
            }
        }, sdpMediaConstraints);
    }

    // when local is set to be the master
    private void createSdpAnswer() {
        localPeer.createAnswer(new KinesisVideoSdpObserver() {

            @Override
            public void onCreateSuccess(SessionDescription sessionDescription) {
                Log.d(TAG, "Creating answer : success");
                super.onCreateSuccess(sessionDescription);
                localPeer.setLocalDescription(new KinesisVideoSdpObserver(), sessionDescription);
                Message answer = Message.createAnswerMessage(sessionDescription, master, recipientClientId);
                client.sendSdpAnswer(answer);
                peerConnectionFoundMap.put(recipientClientId, localPeer);
                handlePendingIceCandidates(recipientClientId);
            }
        }, new MediaConstraints());
    }

    private void addRemoteStreamToVideoView(MediaStream stream) {
        final VideoTrack remoteVideoTrack = stream.videoTracks != null && stream.videoTracks.size() > 0 ? stream.videoTracks.get(0) : null;
        AudioTrack remoteAudioTrack = stream.audioTracks != null && stream.audioTracks.size() > 0 ? stream.audioTracks.get(0) : null;
        if (remoteAudioTrack != null) {
            remoteAudioTrack.setEnabled(true);
            Log.d(TAG, "remoteAudioTrack received: State=" + remoteAudioTrack.state().name());
            audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
            audioManager.setSpeakerphoneOn(true);
        }
        if (remoteVideoTrack != null) {
            runOnUiThread(new Runnable() {

                @Override
                public void run() {
                    try {
                        Log.d(TAG, "remoteVideoTrackId=" + remoteVideoTrack.id() + " videoTrackState=" + remoteVideoTrack.state());
                        resizeLocalView();
                        remoteVideoTrack.addSink(remoteView);
                        resizeRemoteView();
                    } catch (Exception e) {
                        Log.e(TAG, "Error in setting remote video view" + e);
                    }
                }
            });
        } else {
            Log.e(TAG, "Error in setting remote track");
        }
    }

    private URI getSignedUri(String masterEndpoint, String viewerEndpoint) {
        URI signedUri;
        if (master) {
            signedUri = AwsV4Signer.sign(URI.create(masterEndpoint), mCreds.getAWSAccessKeyId(), mCreds.getAWSSecretKey(), mCreds instanceof AWSSessionCredentials ? ((AWSSessionCredentials) mCreds).getSessionToken() : "", URI.create(mWssEndpoint), mRegion);
        } else {
            signedUri = AwsV4Signer.sign(URI.create(viewerEndpoint), mCreds.getAWSAccessKeyId(), mCreds.getAWSSecretKey(), mCreds instanceof AWSSessionCredentials ? ((AWSSessionCredentials) mCreds).getSessionToken() : "", URI.create(mWssEndpoint), mRegion);
        }
        return signedUri;
    }

    @SuppressLint("ClickableViewAccessibility")
    private void resizeLocalView() {
        DisplayMetrics displayMetrics = new DisplayMetrics();
        final ViewGroup.LayoutParams lp = localView.getLayoutParams();
        getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
        lp.height = (int) (displayMetrics.heightPixels * 0.25);
        lp.width = (int) (displayMetrics.widthPixels * 0.25);
        localView.setLayoutParams(lp);
        localView.setOnTouchListener(new View.OnTouchListener() {

            private final int mMarginRight = displayMetrics.widthPixels;

            private final int mMarginBottom = displayMetrics.heightPixels;

            private int deltaOfDownXAndMargin, deltaOfDownYAndMargin;

            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                final int X = (int) motionEvent.getRawX();
                final int Y = (int) motionEvent.getRawY();
                switch(motionEvent.getAction() & MotionEvent.ACTION_MASK) {
                    case MotionEvent.ACTION_DOWN:
                        FrameLayout.LayoutParams lParams = (FrameLayout.LayoutParams) lp;
                        deltaOfDownXAndMargin = X + lParams.rightMargin;
                        deltaOfDownYAndMargin = Y + lParams.bottomMargin;
                        return true;
                    case MotionEvent.ACTION_MOVE:
                        FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) lp;
                        layoutParams.rightMargin = deltaOfDownXAndMargin - X;
                        layoutParams.bottomMargin = deltaOfDownYAndMargin - Y;
                        // shouldn't be out of screen
                        if (layoutParams.rightMargin >= mMarginRight - lp.width) {
                            layoutParams.rightMargin = mMarginRight - lp.width;
                        }
                        if (layoutParams.bottomMargin >= mMarginBottom - lp.height) {
                            layoutParams.bottomMargin = mMarginBottom - lp.height;
                        }
                        if (layoutParams.rightMargin <= 0) {
                            layoutParams.rightMargin = 0;
                        }
                        if (layoutParams.bottomMargin <= 0) {
                            layoutParams.bottomMargin = 0;
                        }
                        localView.setLayoutParams(layoutParams);
                }
                return false;
            }
        });
    }

    private void resizeRemoteView() {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
            DisplayMetrics displayMetrics = new DisplayMetrics();
            final ViewGroup.LayoutParams lp = remoteView.getLayoutParams();
            getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
            lp.height = (int) (displayMetrics.heightPixels * 0.75);
            lp.width = (int) (displayMetrics.widthPixels * 0.75);
            remoteView.setLayoutParams(lp);
            localView.bringToFront();
        }
    }

    private void createNotificationChannel() {
        // Create the NotificationChannel, but only on API 26+ because
        // the NotificationChannel clreplaced is new and not in the support library
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            CharSequence name = getString(R.string.data_channel_notification);
            String description = getString(R.string.data_channel_notification_description);
            int importance = NotificationManager.IMPORTANCE_HIGH;
            NotificationChannel channel = new NotificationChannel(CHANNEL_ID, name, importance);
            channel.setDescription(description);
            // Register the channel with the system; you can't change the importance
            // or other notification behaviors after this
            NotificationManager notificationManager = getSystemService(NotificationManager.clreplaced);
            notificationManager.createNotificationChannel(channel);
        }
    }
}

18 Source : WebRTCWrapper.java
with GNU General Public License v3.0
from snikket-im

private static void dispose(final PeerConnection peerConnection) {
    try {
        peerConnection.dispose();
    } catch (final IllegalStateException e) {
        Log.e(Config.LOGTAG, "unable to dispose of peer connection", e);
    }
}

18 Source : WebSocketTask.java
with Apache License 2.0
from sergiopaniego

/**
 * Created by sergiopaniegoblanco on 18/02/2018.
 */
public clreplaced WebSocketTask extends AsyncTask<VideoConferenceActivity, Void, Void> {

    private static final String TAG = "WebSocketTask";

    private static final String TOKEN_URL = "https://demos.openvidu.io:4443/api/tokens";

    private static final String AUTH_TOKEN = "Basic T1BFTlZJRFVBUFA6TVlfU0VDUkVU";

    private VideoConferenceActivity activity;

    private PeerConnection localPeer;

    private String sessionName;

    private String participantName;

    private String socketAddress;

    private PeerConnectionFactory peerConnectionFactory;

    private AudioTrack localAudioTrack;

    private VideoTrack localVideoTrack;

    private PeersManager peersManager;

    private boolean isCancelled = false;

    private OkHttpClient client;

    private final TrustManager[] trustManagers = new TrustManager[] { new X509TrustManager() {

        @Override
        public X509Certificate[] getAcceptedIssuers() {
            return new X509Certificate[0];
        }

        @Override
        public void checkServerTrusted(final X509Certificate[] chain, final String authType) throws CertificateException {
            Log.i(TAG, ": authType: " + authType);
        }

        @Override
        public void checkClientTrusted(final X509Certificate[] chain, final String authType) throws CertificateException {
            Log.i(TAG, ": authType: " + authType);
        }
    } };

    public WebSocketTask(VideoConferenceActivity activity, PeersManager peersManager, String sessionName, String participantName, String socketAddress) {
        this.activity = activity;
        this.peersManager = peersManager;
        this.localPeer = peersManager.getLocalPeer();
        this.sessionName = sessionName;
        this.participantName = participantName;
        this.socketAddress = socketAddress;
        this.peerConnectionFactory = peersManager.getPeerConnectionFactory();
        this.localAudioTrack = peersManager.getLocalAudioTrack();
        this.localVideoTrack = peersManager.getLocalVideoTrack();
        this.client = new OkHttpClient();
    }

    public void setCancelled(boolean cancelled) {
        isCancelled = cancelled;
    }

    @Override
    protected Void doInBackground(VideoConferenceActivity... parameters) {
        try {
            String json = "{\"session\": \"SessionA\"}";
            RequestBody body = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), json);
            Request request = new Request.Builder().url(TOKEN_URL).header("Authorization", AUTH_TOKEN).post(body).build();
            Response response = client.newCall(request).execute();
            String responseString = response.body().string();
            String token = "";
            try {
                JSONObject jsonObject = new JSONObject(responseString);
                token = (String) jsonObject.get("token");
            } catch (JSONException e) {
                e.printStackTrace();
            }
            WebSocketFactory factory = new WebSocketFactory();
            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, trustManagers, new java.security.SecureRandom());
            factory.setSSLContext(sslContext);
            socketAddress = getSocketAddress();
            peersManager.setWebSocket(new WebSocketFactory().createSocket(socketAddress));
            peersManager.setWebSocketAdapter(new CustomWebSocketListener(parameters[0], peersManager, sessionName, participantName, activity.getViewsContainer(), socketAddress, token));
            peersManager.getWebSocket().addListener(peersManager.getWebSocketAdapter());
            if (!isCancelled) {
                peersManager.getWebSocket().connect();
            }
        } catch (IOException | KeyManagementException | WebSocketException | NoSuchAlgorithmException | IllegalArgumentException e) {
            Handler mainHandler = new Handler(activity.getMainLooper());
            Runnable myRunnable = new Runnable() {

                @Override
                public void run() {
                    Toast toast = Toast.makeText(activity, activity.getResources().getString(R.string.no_connection), Toast.LENGTH_LONG);
                    toast.show();
                    activity.hangup();
                }
            };
            mainHandler.post(myRunnable);
            isCancelled = true;
        }
        return null;
    }

    private String getSocketAddress() {
        String baseAddress = socketAddress;
        String secureWebSocketPrefix = "wss://";
        String insecureWebSocketPrefix = "ws://";
        if (baseAddress.split(secureWebSocketPrefix).length == 1 && baseAddress.split(insecureWebSocketPrefix).length == 1) {
            baseAddress = secureWebSocketPrefix.concat(baseAddress);
        }
        String portSuffix = ":4443";
        if (baseAddress.split(portSuffix).length == 1 && !baseAddress.regionMatches(true, baseAddress.length() - portSuffix.length(), portSuffix, 0, portSuffix.length())) {
            baseAddress = baseAddress.concat(portSuffix);
        }
        return baseAddress;
    }

    @Override
    protected void onProgressUpdate(Void... progress) {
        Log.i(TAG, "PROGRESS " + Arrays.toString(progress));
    }

    @Override
    protected void onPostExecute(Void results) {
        if (!isCancelled) {
            MediaConstraints sdpConstraints = new MediaConstraints();
            sdpConstraints.mandatory.add(new MediaConstraints.KeyValuePair("offerToReceiveAudio", "true"));
            sdpConstraints.mandatory.add(new MediaConstraints.KeyValuePair("offerToReceiveVideo", "true"));
            MediaStream stream = peerConnectionFactory.createLocalMediaStream("102");
            stream.addTrack(localAudioTrack);
            stream.addTrack(localVideoTrack);
            localPeer.addStream(stream);
            peersManager.createLocalOffer(sdpConstraints);
        } else {
            isCancelled = false;
        }
    }
}

18 Source : RemoteParticipant.java
with Apache License 2.0
from sergiopaniego

/**
 * Created by sergiopaniegoblanco on 08/02/2018.
 */
public clreplaced RemoteParticipant {

    private String id;

    private MediaStream mediaStream;

    private PeerConnection peerConnection;

    private AudioTrack audioTrack;

    private VideoTrack videoTrack;

    private SurfaceViewRenderer videoView;

    private View view;

    private TextView participantNameText;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public MediaStream getMediaStream() {
        return mediaStream;
    }

    public void setMediaStream(MediaStream mediaStream) {
        this.mediaStream = mediaStream;
    }

    public PeerConnection getPeerConnection() {
        return peerConnection;
    }

    public void setPeerConnection(PeerConnection peerConnection) {
        this.peerConnection = peerConnection;
    }

    public AudioTrack getAudioTrack() {
        return audioTrack;
    }

    public void setAudioTrack(AudioTrack audioTrack) {
        this.audioTrack = audioTrack;
    }

    public VideoTrack getVideoTrack() {
        return videoTrack;
    }

    public void setVideoTrack(VideoTrack videoTrack) {
        this.videoTrack = videoTrack;
    }

    public SurfaceViewRenderer getVideoView() {
        return videoView;
    }

    public void setVideoView(SurfaceViewRenderer videoView) {
        this.videoView = videoView;
    }

    public View getView() {
        return view;
    }

    public void setView(View view) {
        this.view = view;
    }

    public TextView getParticipantNameText() {
        return participantNameText;
    }

    public void setParticipantNameText(TextView participantNameText) {
        this.participantNameText = participantNameText;
    }
}

18 Source : CustomWebSocketListener.java
with Apache License 2.0
from sergiopaniego

/**
 * Created by sergiopaniegoblanco on 02/12/2017.
 */
public clreplaced CustomWebSocketListener implements WebSocketListener {

    private static final String TAG = "CustomWebSocketAdapter";

    private static final String JSON_RPCVERSION = "2.0";

    private static final int PING_MESSAGE_INTERVAL = 3;

    private VideoConferenceActivity videoConferenceActivity;

    private PeerConnection localPeer;

    private int id;

    private List<Map<String, String>> iceCandidatesParams;

    private Map<String, String> localOfferParams;

    private String userId;

    private String sessionName;

    private String participantName;

    private LinearLayout views_container;

    private Map<String, RemoteParticipant> participants;

    private String remoteParticipantId;

    private PeersManager peersManager;

    private String socketAddress;

    private String token;

    public CustomWebSocketListener(VideoConferenceActivity videoConferenceActivity, PeersManager peersManager, String sessionName, String participantName, LinearLayout views_container, String socketAddress, String token) {
        this.videoConferenceActivity = videoConferenceActivity;
        this.peersManager = peersManager;
        this.localPeer = peersManager.getLocalPeer();
        this.id = 0;
        this.sessionName = sessionName;
        this.participantName = participantName;
        this.views_container = views_container;
        this.socketAddress = socketAddress;
        this.iceCandidatesParams = new ArrayList<>();
        this.participants = new HashMap<>();
        this.token = token;
    }

    public Map<String, RemoteParticipant> getParticipants() {
        return participants;
    }

    public String getUserId() {
        return userId;
    }

    public int getId() {
        return id;
    }

    private void updateId() {
        id++;
    }

    @Override
    public void onStateChanged(WebSocket websocket, WebSocketState newState) throws Exception {
        Log.i(TAG, "State changed: " + newState.name());
    }

    @Override
    public void onConnected(WebSocket websocket, Map<String, List<String>> headers) throws Exception {
        Log.i(TAG, "Connected");
        pingMessageHandler(websocket);
        String regex = "(room)+";
        String baseAddress = socketAddress.split(regex)[0];
        Map<String, String> joinRoomParams = new HashMap<>();
        joinRoomParams.put(JSONConstants.METADATA, "{\"clientData\": \"" + participantName + "\"}");
        joinRoomParams.put("recorder", "false");
        joinRoomParams.put("secret", "MY_SECRET");
        joinRoomParams.put("session", sessionName);
        joinRoomParams.put("token", token);
        sendJson(websocket, "joinRoom", joinRoomParams);
        if (localOfferParams != null) {
            sendJson(websocket, "publishVideo", localOfferParams);
        }
    }

    private void pingMessageHandler(final WebSocket webSocket) {
        long initialDelay = 0L;
        ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1);
        executor.scheduleWithFixedDelay(new Runnable() {

            @Override
            public void run() {
                Map<String, String> pingParams = new HashMap<>();
                if (id == 0) {
                    pingParams.put("interval", "3000");
                }
                sendJson(webSocket, "ping", pingParams);
            }
        }, initialDelay, PING_MESSAGE_INTERVAL, TimeUnit.SECONDS);
    }

    @Override
    public void onConnectError(WebSocket websocket, WebSocketException cause) throws Exception {
        Log.i(TAG, "Connect error: " + cause);
    }

    @Override
    public void onDisconnected(WebSocket websocket, WebSocketFrame serverCloseFrame, WebSocketFrame clientCloseFrame, boolean closedByServer) throws Exception {
        Log.i(TAG, "Disconnected " + serverCloseFrame.getCloseReason() + " " + clientCloseFrame.getCloseReason() + " " + closedByServer);
    }

    @Override
    public void onFrame(WebSocket websocket, WebSocketFrame frame) throws Exception {
        Log.i(TAG, "Frame");
    }

    @Override
    public void onContinuationFrame(WebSocket websocket, WebSocketFrame frame) throws Exception {
        Log.i(TAG, "Continuation Frame");
    }

    @Override
    public void onTextFrame(WebSocket websocket, WebSocketFrame frame) throws Exception {
        Log.i(TAG, "Text Frame");
    }

    @Override
    public void onBinaryFrame(WebSocket websocket, WebSocketFrame frame) throws Exception {
        Log.i(TAG, "Binary Frame");
    }

    @Override
    public void onCloseFrame(WebSocket websocket, WebSocketFrame frame) throws Exception {
        Log.i(TAG, "Close Frame");
    }

    @Override
    public void onPingFrame(WebSocket websocket, WebSocketFrame frame) throws Exception {
        Log.i(TAG, "Ping Frame");
    }

    @Override
    public void onPongFrame(WebSocket websocket, WebSocketFrame frame) throws Exception {
        Log.i(TAG, "Pong Frame");
    }

    @Override
    public void onTextMessage(final WebSocket websocket, String text) throws Exception {
        Log.i(TAG, "Text Message " + text);
        JSONObject json = new JSONObject(text);
        if (json.has(JSONConstants.RESULT)) {
            handleResult(websocket, json);
        } else {
            handleMethod(websocket, json);
        }
    }

    private void handleResult(final WebSocket webSocket, JSONObject json) throws JSONException {
        JSONObject result = new JSONObject(json.getString(JSONConstants.RESULT));
        if (result.has(JSONConstants.SDP_ANSWER)) {
            saveAnswer(result);
        } else if (result.has(JSONConstants.SESSION_ID)) {
            if (result.has(JSONConstants.VALUE)) {
                if (result.getJSONArray(JSONConstants.VALUE).length() > 0) {
                    addParticipantsAlreadyInRoom(result, webSocket);
                }
                this.userId = result.getString(JSONConstants.ID);
                for (Map<String, String> iceCandidate : iceCandidatesParams) {
                    iceCandidate.put("endpointName", this.userId);
                    sendJson(webSocket, "onIceCandidate", iceCandidate);
                }
            }
        } else if (result.has(JSONConstants.VALUE)) {
            Log.i(TAG, "pong");
        } else {
            Log.e(TAG, "Unrecognized " + result);
        }
    }

    private void addParticipantsAlreadyInRoom(JSONObject result, final WebSocket webSocket) throws JSONException {
        for (int i = 0; i < result.getJSONArray(JSONConstants.VALUE).length(); i++) {
            remoteParticipantId = result.getJSONArray(JSONConstants.VALUE).getJSONObject(i).getString(JSONConstants.ID);
            final RemoteParticipant remoteParticipant = new RemoteParticipant();
            remoteParticipant.setId(remoteParticipantId);
            participants.put(remoteParticipantId, remoteParticipant);
            createVideoView(remoteParticipant);
            setRemoteParticipantName(new JSONObject(result.getJSONArray(JSONConstants.VALUE).getJSONObject(i).getString(JSONConstants.METADATA)).getString("clientData"), remoteParticipant);
            peersManager.createRemotePeerConnection(remoteParticipant);
            remoteParticipant.getPeerConnection().createOffer(new CustomSdpObserver("remoteCreateOffer") {

                @Override
                public void onCreateSuccess(SessionDescription sessionDescription) {
                    super.onCreateSuccess(sessionDescription);
                    remoteParticipant.getPeerConnection().setLocalDescription(new CustomSdpObserver("remoteSetLocalDesc"), sessionDescription);
                    Map<String, String> remoteOfferParams = new HashMap<>();
                    remoteOfferParams.put("sdpOffer", sessionDescription.description);
                    remoteOfferParams.put("sender", remoteParticipantId + "_CAMERA");
                    sendJson(webSocket, "receiveVideoFrom", remoteOfferParams);
                }
            }, new MediaConstraints());
        }
    }

    private void handleMethod(final WebSocket webSocket, JSONObject json) throws JSONException {
        if (!json.has(JSONConstants.PARAMS)) {
            Log.e(TAG, "No params");
        } else {
            final JSONObject params = new JSONObject(json.getString(JSONConstants.PARAMS));
            String method = json.getString(JSONConstants.METHOD);
            switch(method) {
                case JSONConstants.ICE_CANDIDATE:
                    iceCandidateMethod(params);
                    break;
                case JSONConstants.PARTICIPANT_JOINED:
                    participantJoinedMethod(params);
                    break;
                case JSONConstants.PARTICIPANT_PUBLISHED:
                    participantPublishedMethod(params, webSocket);
                    break;
                case JSONConstants.PARTICIPANT_LEFT:
                    participantLeftMethod(params);
                    break;
                default:
                    throw new JSONException("Can't understand method: " + method);
            }
        }
    }

    private void iceCandidateMethod(JSONObject params) throws JSONException {
        if (params.getString("endpointName").equals(userId)) {
            saveIceCandidate(params, null);
        } else {
            saveIceCandidate(params, params.getString("endpointName"));
        }
    }

    private void participantJoinedMethod(JSONObject params) throws JSONException {
        final RemoteParticipant remoteParticipant = new RemoteParticipant();
        remoteParticipant.setId(params.getString(JSONConstants.ID));
        participants.put(params.getString(JSONConstants.ID), remoteParticipant);
        createVideoView(remoteParticipant);
        setRemoteParticipantName(new JSONObject(params.getString(JSONConstants.METADATA)).getString("clientData"), remoteParticipant);
        peersManager.createRemotePeerConnection(remoteParticipant);
    }

    private void createVideoView(final RemoteParticipant remoteParticipant) {
        Handler mainHandler = new Handler(videoConferenceActivity.getMainLooper());
        Runnable myRunnable = new Runnable() {

            @Override
            public void run() {
                View rowView = videoConferenceActivity.getLayoutInflater().inflate(R.layout.peer_video, null);
                LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
                lp.setMargins(0, 0, 0, 20);
                rowView.setLayoutParams(lp);
                int rowId = View.generateViewId();
                rowView.setId(rowId);
                views_container.addView(rowView);
                SurfaceViewRenderer videoView = (SurfaceViewRenderer) ((ViewGroup) rowView).getChildAt(0);
                remoteParticipant.setVideoView(videoView);
                videoView.setMirror(false);
                EglBase rootEglBase = EglBase.create();
                videoView.init(rootEglBase.getEglBaseContext(), null);
                videoView.setZOrderMediaOverlay(true);
                View textView = ((ViewGroup) rowView).getChildAt(1);
                remoteParticipant.setParticipantNameText((TextView) textView);
                remoteParticipant.setView(rowView);
            }
        };
        mainHandler.post(myRunnable);
    }

    private void participantPublishedMethod(JSONObject params, final WebSocket webSocket) throws JSONException {
        remoteParticipantId = params.getString(JSONConstants.ID);
        RemoteParticipant remoteParticipantPublished = participants.get(remoteParticipantId);
        remoteParticipantPublished.getPeerConnection().createOffer(new CustomSdpObserver("remoteCreateOffer", remoteParticipantPublished) {

            @Override
            public void onCreateSuccess(SessionDescription sessionDescription) {
                super.onCreateSuccess(sessionDescription);
                getRemoteParticipant().getPeerConnection().setLocalDescription(new CustomSdpObserver("remoteSetLocalDesc"), sessionDescription);
                Map<String, String> remoteOfferParams = new HashMap<>();
                remoteOfferParams.put("sdpOffer", sessionDescription.description);
                remoteOfferParams.put("sender", getRemoteParticipant().getId() + "_webcam");
                sendJson(webSocket, "receiveVideoFrom", remoteOfferParams);
            }
        }, new MediaConstraints());
    }

    private void participantLeftMethod(JSONObject params) throws JSONException {
        final String participantId = params.getString("name");
        participants.get(participantId).getPeerConnection().close();
        Handler mainHandler = new Handler(videoConferenceActivity.getMainLooper());
        Runnable myRunnable = new Runnable() {

            @Override
            public void run() {
                views_container.removeView(participants.get(participantId).getView());
            }
        };
        mainHandler.post(myRunnable);
        RemoteParticipant remoteParticipantToDelete = participants.get(participantId);
        participants.remove(remoteParticipantToDelete);
    }

    private void setRemoteParticipantName(final String name, final RemoteParticipant participant) {
        Handler mainHandler = new Handler(videoConferenceActivity.getMainLooper());
        Runnable myRunnable = new Runnable() {

            @Override
            public void run() {
                videoConferenceActivity.setRemoteParticipantName(name, participant);
            }
        };
        mainHandler.post(myRunnable);
    }

    @Override
    public void onBinaryMessage(WebSocket websocket, byte[] binary) throws Exception {
        Log.i(TAG, "Binary Message");
    }

    @Override
    public void onSendingFrame(WebSocket websocket, WebSocketFrame frame) throws Exception {
        Log.i(TAG, "Sending Frame");
    }

    @Override
    public void onFrameSent(WebSocket websocket, WebSocketFrame frame) throws Exception {
        Log.i(TAG, "Frame sent");
    }

    @Override
    public void onFrameUnsent(WebSocket websocket, WebSocketFrame frame) throws Exception {
        Log.i(TAG, "Frame unsent");
    }

    @Override
    public void onThreadCreated(WebSocket websocket, ThreadType threadType, Thread thread) throws Exception {
        Log.i(TAG, "Thread created");
    }

    @Override
    public void onThreadStarted(WebSocket websocket, ThreadType threadType, Thread thread) throws Exception {
        Log.i(TAG, "Thread started");
    }

    @Override
    public void onThreadStopping(WebSocket websocket, ThreadType threadType, Thread thread) throws Exception {
        Log.i(TAG, "Thread stopping");
    }

    @Override
    public void onError(WebSocket websocket, WebSocketException cause) throws Exception {
        Log.i(TAG, "Error! " + cause);
    }

    @Override
    public void onFrameError(WebSocket websocket, WebSocketException cause, WebSocketFrame frame) throws Exception {
        Log.i(TAG, "Frame error");
    }

    @Override
    public void onMessageError(WebSocket websocket, WebSocketException cause, List<WebSocketFrame> frames) throws Exception {
        Log.i(TAG, "Message error! " + cause);
    }

    @Override
    public void onMessageDecompressionError(WebSocket websocket, WebSocketException cause, byte[] compressed) throws Exception {
        Log.i(TAG, "Message Decompression Error");
    }

    @Override
    public void onTextMessageError(WebSocket websocket, WebSocketException cause, byte[] data) throws Exception {
        Log.i(TAG, "Text Message Error! " + cause);
    }

    @Override
    public void onSendError(WebSocket websocket, WebSocketException cause, WebSocketFrame frame) throws Exception {
        Log.i(TAG, "Send Error! " + cause);
    }

    @Override
    public void onUnexpectedError(WebSocket websocket, WebSocketException cause) throws Exception {
        Log.i(TAG, "Unexpected error! " + cause);
    }

    @Override
    public void handleCallbackError(WebSocket websocket, Throwable cause) throws Exception {
        Log.i(TAG, "Handle callback error! " + cause);
    }

    @Override
    public void onSendingHandshake(WebSocket websocket, String requestLine, List<String[]> headers) throws Exception {
        Log.i(TAG, "Sending Handshake! Hello!");
    }

    private void saveIceCandidate(JSONObject json, String endPointName) throws JSONException {
        IceCandidate iceCandidate = new IceCandidate(json.getString("sdpMid"), Integer.parseInt(json.getString("sdpMLineIndex")), json.getString("candidate"));
        if (endPointName == null) {
            localPeer.addIceCandidate(iceCandidate);
        } else {
            participants.get(endPointName).getPeerConnection().addIceCandidate(iceCandidate);
        }
    }

    private void saveAnswer(JSONObject json) throws JSONException {
        SessionDescription sessionDescription = new SessionDescription(SessionDescription.Type.ANSWER, json.getString("sdpAnswer"));
        if (localPeer.getRemoteDescription() == null) {
            localPeer.setRemoteDescription(new CustomSdpObserver("localSetRemoteDesc"), sessionDescription);
        } else {
            participants.get(remoteParticipantId).getPeerConnection().setRemoteDescription(new CustomSdpObserver("remoteSetRemoteDesc"), sessionDescription);
        }
    }

    public void sendJson(WebSocket webSocket, String method, Map<String, String> params) {
        try {
            JSONObject paramsJson = new JSONObject();
            for (Map.Entry<String, String> param : params.entrySet()) {
                paramsJson.put(param.getKey(), param.getValue());
            }
            JSONObject jsonObject = new JSONObject();
            if (method.equals(JSONConstants.JOIN_ROOM)) {
                jsonObject.put(JSONConstants.ID, 1).put(JSONConstants.PARAMS, paramsJson);
            } else if (paramsJson.length() > 0) {
                jsonObject.put(JSONConstants.ID, getId()).put(JSONConstants.PARAMS, paramsJson);
            } else {
                jsonObject.put(JSONConstants.ID, getId());
            }
            jsonObject.put("jsonrpc", JSON_RPCVERSION).put(JSONConstants.METHOD, method);
            String jsonString = jsonObject.toString();
            updateId();
            webSocket.sendText(jsonString);
        } catch (JSONException e) {
            Log.i(TAG, "JSONException raised on sendJson");
        }
    }

    public void addIceCandidate(Map<String, String> iceCandidateParams) {
        iceCandidatesParams.add(iceCandidateParams);
    }

    public void setLocalOfferParams(Map<String, String> offerParams) {
        this.localOfferParams = offerParams;
    }
}

18 Source : MainActivity.java
with Apache License 2.0
from rome753

@Override
public void onPeerJoined(String socketId) {
    PeerConnection peerConnection = getOrCreatePeerConnection(socketId);
    peerConnection.createOffer(new SdpAdapter("createOfferSdp:" + socketId) {

        @Override
        public void onCreateSuccess(SessionDescription sessionDescription) {
            super.onCreateSuccess(sessionDescription);
            peerConnection.setLocalDescription(new SdpAdapter("setLocalSdp:" + socketId), sessionDescription);
            SignalingClient.get().sendSessionDescription(sessionDescription, socketId);
        }
    }, new MediaConstraints());
}

18 Source : PeerConnectionClient.java
with MIT License
from crossle

private void getStats(final BigInteger handleId) {
    PeerConnection peerConnection = peerConnectionMap.get(handleId).peerConnection;
    boolean success = peerConnection.getStats(new StatsObserver() {

        @Override
        public void onComplete(final StatsReport[] reports) {
            events.onPeerConnectionStatsReady(reports);
        }
    }, null);
    if (!success) {
        Log.e(TAG, "getStats() returns false!");
    }
}

17 Source : MainActivity.java
with Apache License 2.0
from rome753

private synchronized PeerConnection getOrCreatePeerConnection(String socketId) {
    PeerConnection peerConnection = peerConnectionMap.get(socketId);
    if (peerConnection != null) {
        return peerConnection;
    }
    peerConnection = peerConnectionFactory.createPeerConnection(iceServers, new PeerConnectionAdapter("PC:" + socketId) {

        @Override
        public void onIceCandidate(IceCandidate iceCandidate) {
            super.onIceCandidate(iceCandidate);
            SignalingClient.get().sendIceCandidate(iceCandidate, socketId);
        }

        @Override
        public void onAddStream(MediaStream mediaStream) {
            super.onAddStream(mediaStream);
            VideoTrack remoteVideoTrack = mediaStream.videoTracks.get(0);
            runOnUiThread(() -> {
                remoteVideoTrack.addSink(remoteViews[remoteViewsIndex++]);
            });
        }
    });
    peerConnection.addStream(mediaStream);
    peerConnectionMap.put(socketId, peerConnection);
    return peerConnection;
}

17 Source : MainActivity.java
with Apache License 2.0
from rome753

@Override
public void onAnswerReceived(JSONObject data) {
    String socketId = data.optString("from");
    PeerConnection peerConnection = getOrCreatePeerConnection(socketId);
    peerConnection.setRemoteDescription(new SdpAdapter("setRemoteSdp:" + socketId), new SessionDescription(SessionDescription.Type.ANSWER, data.optString("sdp")));
}

17 Source : MainActivity.java
with Apache License 2.0
from rome753

@Override
public void onOfferReceived(JSONObject data) {
    runOnUiThread(() -> {
        final String socketId = data.optString("from");
        PeerConnection peerConnection = getOrCreatePeerConnection(socketId);
        peerConnection.setRemoteDescription(new SdpAdapter("setRemoteSdp:" + socketId), new SessionDescription(SessionDescription.Type.OFFER, data.optString("sdp")));
        peerConnection.createAnswer(new SdpAdapter("localAnswerSdp") {

            @Override
            public void onCreateSuccess(SessionDescription sdp) {
                super.onCreateSuccess(sdp);
                peerConnectionMap.get(socketId).setLocalDescription(new SdpAdapter("setLocalSdp:" + socketId), sdp);
                SignalingClient.get().sendSessionDescription(sdp, socketId);
            }
        }, new MediaConstraints());
    });
}

17 Source : CallActivity.java
with GNU General Public License v3.0
from Jhuster

public PeerConnection createPeerConnection() {
    Log.i(TAG, "Create PeerConnection ...");
    PeerConnection.RTCConfiguration configuration = new PeerConnection.RTCConfiguration(new ArrayList<>());
    PeerConnection connection = mPeerConnectionFactory.createPeerConnection(configuration, mPeerConnectionObserver);
    if (connection == null) {
        Log.e(TAG, "Failed to createPeerConnection !");
        return null;
    }
    connection.addTrack(mVideoTrack);
    connection.addTrack(mAudioTrack);
    return connection;
}

17 Source : VideoChatHelper.java
with MIT License
from fengli12321

private String getKeyFromConnectionDic(PeerConnection peerConnection) {
    String socketId = null;
    for (Map.Entry<String, Peer> entry : peers.entrySet()) {
        if (peerConnection.equals(entry.getValue().pc)) {
            socketId = entry.getKey();
        }
    }
    if (socketId == null) {
        FLLog.i("错误:未找到相应的socketId");
    }
    return socketId;
}

17 Source : VideoChatHelper.java
with MIT License
from fengli12321

/*
    * 为所有连接添加流
    * */
private void addStream() {
    for (Map.Entry<String, Peer> entry : peers.entrySet()) {
        Peer peer = entry.getValue();
        PeerConnection connection = peer.pc;
        if (localMediaStream == null) {
            FLLog.i("添加本地流时,本地流为空");
        } else {
            connection.addStream(localMediaStream);
        }
    }
}

17 Source : VideoChatHelper.java
with MIT License
from fengli12321

/*
    * 创建点对点连接
    *
    * */
private PeerConnection createPeerConnection(String connectionId, Peer peer) {
    if (factory == null) {
        FLLog.i("工厂为空");
        return null;
    }
    if (ICEServers == null) {
        ICEServers = new LinkedList<>();
        ICEServers.add(defaultSTUNServer("stun:23.21.150.121"));
        ICEServers.add(defaultSTUNServer("stun:stun.l.google.com:19302"));
    }
    PeerConnection peerConnection = factory.createPeerConnection(ICEServers, peerConnectionConstraints(), peer);
    return peerConnection;
}

17 Source : VideoChatHelper.java
with MIT License
from fengli12321

/**
 * 为所有连接创建offer
 */
private void createOffers() {
    for (Map.Entry<String, Peer> entry : peers.entrySet()) {
        Peer peer = entry.getValue();
        PeerConnection connection = peer.pc;
        FLLog.i("createoffer调用");
        connection.createOffer(peer, offerOrAnswerConstraint());
    }
}

16 Source : WebRTCWrapper.java
with GNU General Public License v3.0
from snikket-im

@Nonnull
private ListenableFuture<PeerConnection> getPeerConnectionFuture() {
    final PeerConnection peerConnection = this.peerConnection;
    if (peerConnection == null) {
        return Futures.immediateFailedFuture(new IllegalStateException("initialize PeerConnection first"));
    } else {
        return Futures.immediateFuture(peerConnection);
    }
}

16 Source : MainActivity.java
with Apache License 2.0
from rome753

@Override
public void onIceCandidateReceived(JSONObject data) {
    String socketId = data.optString("from");
    PeerConnection peerConnection = getOrCreatePeerConnection(socketId);
    peerConnection.addIceCandidate(new IceCandidate(data.optString("id"), data.optInt("label"), data.optString("candidate")));
}

16 Source : WebRTCModule.java
with Apache License 2.0
from react-native-webrtc-kit

/**
 * peerConnectionClose(valueTag: ValueTag)
 */
@ReactMethod
public void peerConnectionClose(@NonNull String valueTag) {
    Log.d(getName(), "peerConnectionClose() - valueTag=" + valueTag);
    final PeerConnection peerConnection = repository.getPeerConnectionByValueTag(valueTag);
    if (peerConnection == null) {
        return;
    }
    repository.removePeerConnectionByValueTag(valueTag);
    peerConnection.dispose();
}

16 Source : VideoChatHelper.java
with MIT License
from fengli12321

/**
 * 关闭peerConnection
 *
 * @param connectionId 连接id
 */
private void closePeerConnection(String connectionId) {
    Peer peer = peers.get(connectionId);
    PeerConnection connection = peer.pc;
    if (connection != null) {
        connection.close();
    }
    peers.remove(connectionId);
    callBack.onCloseWithUserId(connectionId);
}

15 Source : WebRTCWrapper.java
with GNU General Public License v3.0
from snikket-im

synchronized void close() {
    final PeerConnection peerConnection = this.peerConnection;
    final CapturerChoice capturerChoice = this.capturerChoice;
    final AppRTCAudioManager audioManager = this.appRTCAudioManager;
    final EglBase eglBase = this.eglBase;
    if (peerConnection != null) {
        dispose(peerConnection);
        this.peerConnection = null;
    }
    if (audioManager != null) {
        toneManager.setAppRtcAudioManagerHasControl(false);
        mainHandler.post(audioManager::stop);
    }
    this.localVideoTrack = null;
    this.remoteVideoTrack = null;
    if (capturerChoice != null) {
        try {
            capturerChoice.cameraVideoCapturer.stopCapture();
        } catch (InterruptedException e) {
            Log.e(Config.LOGTAG, "unable to stop capturing");
        }
    }
    if (eglBase != null) {
        eglBase.release();
        this.eglBase = null;
    }
}

15 Source : WebRTCModule.java
with Apache License 2.0
from react-native-webrtc-kit

/**
 * peerConnectionSetConfiguration(valueTag: ValueTag, configuration: RTCConfiguration)
 */
@ReactMethod
public void peerConnectionSetConfiguration(@NonNull ReadableMap configurationJson, @NonNull String valueTag) {
    Log.d(getName(), "peerConnectionSetConfiguration()");
    final PeerConnection.RTCConfiguration configuration = rtcConfiguration(configurationJson);
    final PeerConnection peerConnection = repository.getPeerConnectionByValueTag(valueTag);
    if (peerConnection == null)
        return;
    peerConnection.setConfiguration(configuration);
}

14 Source : WebRTCModule.java
with Apache License 2.0
from react-native-webrtc-kit

/**
 * peerConnectionSetRemoteDescription(valueTag: ValueTag, sdp: RTCSessionDescription): Promise<void>
 */
@ReactMethod
public void peerConnectionSetRemoteDescription(@NonNull ReadableMap sdpJson, @NonNull String valueTag, @NonNull Promise promise) {
    Log.d(getName(), "peerConnectionSetRemoteDescription()");
    final PeerConnection peerConnection = repository.getPeerConnectionByValueTag(valueTag);
    if (peerConnection == null) {
        promise.reject("NotFoundError", "peer connection is not found");
        return;
    }
    final SdpObserver observer = new SdpObserver() {

        @Override
        public void onCreateSuccess(SessionDescription sessionDescription) {
            promise.reject("FatalError", "this must not be called");
        }

        @Override
        public void onSetSuccess() {
            promise.resolve(null);
        }

        @Override
        public void onCreateFailure(String s) {
            promise.reject("FatalError", "this must not be called");
        }

        @Override
        public void onSetFailure(String s) {
            promise.reject("SetRemoteDescriptionFailed", s);
        }
    };
    peerConnection.setRemoteDescription(observer, sessionDescription(sdpJson));
}

14 Source : WebRTCModule.java
with Apache License 2.0
from react-native-webrtc-kit

/**
 * peerConnectionInit(valueTag: ValueTag, configuration: RTCConfiguration, constraints: RTCMediaConstraints)
 * TODO: MediaConstraintsがdeprecated扱いになっているがどうするべきか?とりあえず現状はconstraints引数を無視するようにしているが・・・
 */
@ReactMethod
public void peerConnectionInit(@NonNull ReadableMap configurationJson, @Nullable ReadableMap constraintsJson, @NonNull String valueTag) {
    Log.d(getName(), "peerConnectionInit() - valueTag=" + valueTag);
    final PeerConnection.RTCConfiguration configuration = rtcConfiguration(configurationJson);
    final WebRTCPeerConnectionObserver observer = new WebRTCPeerConnectionObserver(reactContext);
    final PeerConnection peerConnection = peerConnectionFactory.createPeerConnection(configuration, observer);
    if (peerConnection == null) {
        throw new IllegalStateException("createPeerConnection failed");
    }
    final Pair<String, PeerConnection> peerConnectionPair = new Pair<>(valueTag, peerConnection);
    // observerもrepositoryに保存するようにして、close時に明示的にかつ確実に破棄するようにしたほうが良いかもしれないが、
    // Java側で明示的に破棄してもしなくても結局はlibwebrtcのobserverのラッパーでしかないため、
    // libwebrtc側できちんと破棄されればJava側もそのうち適切にGCされて消えることがわかったので、
    // ひとまずこの状態で大丈夫
    observer.peerConnectionPair = peerConnectionPair;
    repository.addPeerConnection(peerConnectionPair);
}

14 Source : PeerConnectionClient.java
with MIT License
from crossle

private void createPeerConnectionInternal(EglBase.Context renderEGLContext, BigInteger handleId) {
    if (factory == null || isError) {
        Log.e(TAG, "Peerconnection factory is not created");
        return;
    }
    Log.d(TAG, "PCConstraints: " + pcConstraints.toString());
    Log.d(TAG, "EGLContext: " + renderEGLContext);
    factory.setVideoHwAccelerationOptions(renderEGLContext, renderEGLContext);
    PeerConnection peerConnection = createPeerConnection(handleId, true);
    mediaStream = factory.createLocalMediaStream("ARDAMS");
    mediaStream.addTrack(createVideoTrack(videoCapturer));
    mediaStream.addTrack(createAudioTrack());
    peerConnection.addStream(mediaStream);
    findVideoSender(handleId);
}

14 Source : PeerConnectionClient.java
with MIT License
from crossle

private void findVideoSender(final BigInteger handleId) {
    PeerConnection peerConnection = peerConnectionMap.get(handleId).peerConnection;
    for (RtpSender sender : 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;
            }
        }
    }
}

13 Source : PeersManager.java
with Apache License 2.0
from sergiopaniego

public void createRemotePeerConnection(RemoteParticipant remoteParticipant) {
    final List<PeerConnection.IceServer> iceServers = new ArrayList<>();
    PeerConnection.IceServer iceServer = new PeerConnection.IceServer("stun:stun.l.google.com:19302");
    iceServers.add(iceServer);
    MediaConstraints sdpConstraints = new MediaConstraints();
    sdpConstraints.mandatory.add(new MediaConstraints.KeyValuePair("offerToReceiveAudio", "true"));
    sdpConstraints.mandatory.add(new MediaConstraints.KeyValuePair("offerToReceiveVideo", "true"));
    PeerConnection remotePeer = peerConnectionFactory.createPeerConnection(iceServers, sdpConstraints, new CustomPeerConnectionObserver("remotePeerCreation", remoteParticipant) {

        @Override
        public void onIceCandidate(IceCandidate iceCandidate) {
            super.onIceCandidate(iceCandidate);
            Map<String, String> iceCandidateParams = new HashMap<>();
            iceCandidateParams.put("sdpMid", iceCandidate.sdpMid);
            iceCandidateParams.put("sdpMLineIndex", Integer.toString(iceCandidate.sdpMLineIndex));
            iceCandidateParams.put("candidate", iceCandidate.sdp);
            iceCandidateParams.put("endpointName", getRemoteParticipant().getId());
            webSocketAdapter.sendJson(webSocket, "onIceCandidate", iceCandidateParams);
        }

        @Override
        public void onAddStream(MediaStream mediaStream) {
            super.onAddStream(mediaStream);
            activity.gotRemoteStream(mediaStream, getRemoteParticipant());
        }
    });
    MediaStream mediaStream = peerConnectionFactory.createLocalMediaStream("105");
    mediaStream.addTrack(localAudioTrack);
    mediaStream.addTrack(localVideoTrack);
    remotePeer.addStream(mediaStream);
    remoteParticipant.setPeerConnection(remotePeer);
}

See More Examples