androidnode.jssocket.iowebrtclibjingle

Stream from Android app to browser client app via socket.io-client and libjingle


So I'm trying to connect Android to Browser via webRTC via socket.io and libjingle and server is running on Node.js . Issue I'm facing is weired. When 1 client is at Android(native app) and other is at Ipad(native app), Everything works fine. When 1 client is at iPad(Native app) and other is WebApp, Everyting works fine. But When 1 Client is at Android(native app) and other is WebPage, everyting works fine except the audio and video is not streaming to that end.

Following are the two major classes i've used for the purpose:

PS. The method makeOffer(View v) called by the button.

MainActivity.java

public List<PeerConnection.IceServer> iceServers;
    private GLSurfaceView videoView;
    public static SocketIO socket;
    ArrayList<String> userIDs = new ArrayList<>();
    private static final String FIELD_TRIAL_VP9 = "WebRTC-SupportVP9/Enabled/";
    String RoomId = "";
    String sreverURL = "http://xx.xx.xx.xx:xxxx/";
    private EditText roomid;
    private VideoRenderer.Callbacks localRender;
    private VideoRenderer.Callbacks remoteRender;
    protected PeerConnectionFactory factory;
    PeerConnectionFactory.Options options = null;
    Events pc_events;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_home);
        videoView = (GLSurfaceView) findViewById(R.id.glview_call);
        VideoRendererGui.setView(videoView, new Runnable() {
            @Override
            public void run() {
                createPeerConnectionFactory();
            }
        });

        remoteRender = VideoRendererGui.create(0, 0, 100, 100, ScalingType.SCALE_ASPECT_FILL, false);
        localRender = VideoRendererGui.create(0, 0, 100, 100, ScalingType.SCALE_ASPECT_FILL, true);
        iceServers = new ArrayList<>();
        IceServer icc = new IceServer("stun:stun.l.google.com:19302", "", "");
        iceServers.add(icc);
        roomid = (EditText) findViewById(R.id.roomId);
        Random rand = new Random();
        roomid.setText("" + rand.nextInt(9999));
        pc_events = new peerEventHandler();
    }

    private void createPeerConnectionFactory() {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                PeerConnectionFactory.initializeFieldTrials(FIELD_TRIAL_VP9);
                PeerConnectionFactory.initializeAndroidGlobals(Home.this, true, true, true, VideoRendererGui.getEGLContext());
                try {
                    factory = new PeerConnectionFactory();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    public void ondail(View view) {

        try {

            try {
                SocketIO.setDefaultSSLSocketFactory(SSLContext.getDefault());
            } catch (NoSuchAlgorithmException e1) {
                e1.printStackTrace();
            }

            socket = new SocketIO();

            socket.connect(sreverURL, new IOCallback() {

                @Override
                public void onMessage(JSONObject json, IOAcknowledge ack) {
                    Log.e("json Object", json.toString());
                }
                @Override
                public void onMessage(String data, IOAcknowledge ack) {
                    Log.e("json Object", data.toString());
                }
                @Override
                public void onError(SocketIOException socketIOException) {
                    socketIOException.printStackTrace();
                    System.out.println(socketIOException.toString());
                }
                @Override
                public void onDisconnect() {
                }
                @Override
                public void onConnect() {
                    showToast("Connected to " + sreverURL);
                    startCall("");
                }
                @Override
                public void on(final String event, IOAcknowledge ack, final Object... args) {

                    Log.e("Socked.on", event + ", " + args);
                    switch (getEvent(event)) {

                        case LOG :
                            break;
                        case MESSAGE :
                            if (args instanceof Object[]) {
                                if (args[0].toString().contains("offer")) {
//                                  startCall("");
                                }
                                pc_events.setMessage(args[0].toString());
                            } else {
                                pc_events.setMessage(args.toString());
                            }
                            break;
                        case CREATED :
                            runOnUiThread(new Runnable() {
                                public void run() {
                                    showToast("Room Created " + args[0]);
                                }
                            });
                            break;
                        case BROADCAST :
                            Log.e("Socked.onBROADCAST", args.toString());
                            break;
                        case JOIN :
                            Log.e("Socked.onJOIN", args.toString());
                            break;

                        case EMIT :
                            Log.e("Socked.onEMIT", args.toString());
                            Log.e("Socked.onBROADCOST", args.toString());

                            break;

                        case ERROR :
                            Log.e("Socked.onERROR", args.toString());
                            break;

                        default :

                            break;
                    }
                }
            });

            try {
                RoomId = roomid.getText().toString();
            } catch (Exception e) {
            }

            socket.emit("create or join", RoomId);

        } catch (MalformedURLException e) {

            e.printStackTrace();
        }

    }

    public void oncancel(View view) {

    }

    public SocketEvent getEvent(String eventString) {

        SocketEvent eventType;

        try {

            if (eventString.contains("log")) {
                eventType = SocketEvent.LOG;

            } else if (eventString.contains("created")) {
                eventType = SocketEvent.CREATED;
            } else if (eventString.contains("emit():")) {
                eventType = SocketEvent.EMIT;
            }

            else if (eventString.contains("broadcast():")) {
                eventType = SocketEvent.BROADCAST;
            } else if (eventString.contains("message")) {
                eventType = SocketEvent.MESSAGE;
            } else if (eventString.toLowerCase().substring(0, 20).contains("join")) {
                eventType = SocketEvent.JOIN;
            } else {
                eventType = SocketEvent.ERROR;
            }

        } catch (Exception e) {
            eventType = SocketEvent.ERROR;
        }

        return eventType;

    }

    public static interface Events {

        public void peerConnectionEvent(String clintID, VideoRenderer.Callbacks localRender, VideoRenderer.Callbacks remoteRender);

        public void setFactory(PeerConnectionFactory factory);

        public void setMessage(String message);
        public void createOffer();

    }

    private void startCall(String clientID) {

        pc_events.setFactory(factory);

        pc_events.peerConnectionEvent(clientID, localRender, remoteRender);

    }

    public void showToast(final String message) {

        runOnUiThread(new Runnable() {
            public void run() {
                Toast.makeText(Home.this, message, Toast.LENGTH_SHORT).show();
            }
        });
    }


    public void makeOffer(View v) {
        pc_events.createOffer();
    }

}

PeerEventHandler.java

public class peerEventHandler implements Events {

    private PeerConnection peerConnection;
    private PeerConnectionFactory factory;
    PCObserver pcObserver = new PCObserver();
    public LooperExecutor executor;

    private MediaStream mediaStream;

    private VideoSource videoSource;

    public static final String VIDEO_TRACK_ID = "ARDAMSv0";
    public static final String AUDIO_TRACK_ID = "ARDAMSa0";

    private VideoCapturerAndroid videoCapturer;
    private VideoTrack localVideoTrack;
    private VideoTrack remoteVideoTrack;
    public boolean preferIsac = false;
    public boolean videoCallEnabled = true;
    public boolean preferH264 = true;

    private static final String VIDEO_CODEC_H264 = "VP8";
    private static final String AUDIO_CODEC_ISAC = "ISAC";

    private SessionDescription localSdp;

    private final SDPObserver sdpObserver = new SDPObserver();

    private LinkedList<IceCandidate> queuedRemoteCandidates;

    public boolean isInitiator = false;
    private MediaConstraints sdpMediaConstraints;

    private VideoRenderer.Callbacks localRender;
    private VideoRenderer.Callbacks remoteRender;

    @Override
    public void peerConnectionEvent(String clintID, Callbacks localRender, Callbacks remoteRender) {

        this.localRender = localRender;
        this.remoteRender = remoteRender;
        creatPeerConnection();

    }

    public void creatPeerConnection() {

        queuedRemoteCandidates = new LinkedList<IceCandidate>();
        executor = new LooperExecutor();
        executor.requestStart();

        MediaConstraints pcConstraints = new MediaConstraints();
        MediaConstraints videoConstraints = new MediaConstraints();
        MediaConstraints audioConstraints = new MediaConstraints();
        sdpMediaConstraints = new MediaConstraints();

        creatPcConstrains(pcConstraints);
        creatvideoConstraints(videoConstraints);
        creatsdpMediaConstraints(sdpMediaConstraints);

        List<PeerConnection.IceServer> iceServers = new ArrayList<PeerConnection.IceServer>();

        IceServer iceServer = new IceServer("stun:stun.l.google.com:19302", "", "");

        iceServers.add(iceServer);

        PeerConnection.RTCConfiguration rtcConfig = new PeerConnection.RTCConfiguration(iceServers);

        // TCP candidates are only useful when connecting to a server that
        // supports
        // ICE-TCP.
        rtcConfig.tcpCandidatePolicy = PeerConnection.TcpCandidatePolicy.DISABLED;
        rtcConfig.bundlePolicy = PeerConnection.BundlePolicy.BALANCED;
        rtcConfig.rtcpMuxPolicy = PeerConnection.RtcpMuxPolicy.NEGOTIATE;

        peerConnection = factory.createPeerConnection(rtcConfig, pcConstraints, pcObserver);

        Logging.enableTracing("logcat:", EnumSet.of(Logging.TraceLevel.TRACE_DEFAULT), Logging.Severity.LS_WARNING);

        mediaStream = factory.createLocalMediaStream("ARDAMS");

        String cameraDeviceName = CameraEnumerationAndroid.getDeviceName(0);
        String frontCameraDeviceName = CameraEnumerationAndroid.getNameOfFrontFacingDevice();

        cameraDeviceName = frontCameraDeviceName;

        videoCapturer = VideoCapturerAndroid.create(cameraDeviceName, null);

        videoSource = factory.createVideoSource(videoCapturer, videoConstraints);

        localVideoTrack = factory.createVideoTrack(VIDEO_TRACK_ID, videoSource);
        localVideoTrack.setEnabled(true);
        localVideoTrack.addRenderer(new VideoRenderer(localRender));
        mediaStream.addTrack(factory.createAudioTrack(AUDIO_TRACK_ID, factory.createAudioSource(audioConstraints)));
        mediaStream.addTrack(localVideoTrack);
        peerConnection.addStream(mediaStream);
    }

    @Override
    public void createOffer() {
        executor.execute(new Runnable() {
            @Override
            public void run() {
                if (peerConnection != null) {
                    isInitiator = true;
                    peerConnection.createOffer(sdpObserver, sdpMediaConstraints);
                }
            }
        });

    }

    public void createAnswer() {
        executor.execute(new Runnable() {
            @Override
            public void run() {
                if (peerConnection != null) {
                    isInitiator = false;
                    peerConnection.createAnswer(sdpObserver, sdpMediaConstraints);
                }
            }
        });
    }

    private class PCObserver implements PeerConnection.Observer {

        @Override
        public void onAddStream(final MediaStream stream) {

            Log.e("onAddStream", "onAddStream");
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    if (peerConnection == null) {
                        return;
                    }
                    if (stream.audioTracks.size() > 1 || stream.videoTracks.size() > 1) {
                        // /reportError("Weird-looking stream: " + stream);
                        return;
                    }
                    if (stream.videoTracks.size() == 1) {
                        remoteVideoTrack = stream.videoTracks.get(0);
                        remoteVideoTrack.setEnabled(true);
                        remoteVideoTrack.addRenderer(new VideoRenderer(remoteRender));
                    }
                }
            });

        }

        @Override
        public void onDataChannel(DataChannel arg0) {
            Log.e("On data channel", "Data Channel");

        }

        @Override
        public void onIceCandidate(IceCandidate candidate) {

            SocketIO socket = Home.socket;

            JSONObject json = new JSONObject();
            try {

                json.putOpt("type", "candidate");
                json.putOpt("label", candidate.sdpMLineIndex);
                json.putOpt("id", candidate.sdpMid);
                json.putOpt("candidate", candidate.sdp);

            } catch (JSONException e) {
            }

            socket.emit("message", json);
        }

        @Override
        public void onIceConnectionChange(IceConnectionState arg0) {
            Log.e("onIceConnectionChange", "onIceConnectionChange");
        }

        @Override
        public void onIceConnectionReceivingChange(boolean arg0) {
            Log.e("onIceConnectionReceivingChange", "onIceConnectionReceivingChange");
        }

        @Override
        public void onIceGatheringChange(IceGatheringState arg0) {
            Log.e("onIceGatheringChange", "onIceGatheringChange");
        }

        @Override
        public void onRemoveStream(MediaStream arg0) {
            Log.e("onRemoveStream", "onRemoveStream");
        }

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

        @Override
        public void onSignalingChange(SignalingState arg0) {
            Log.e("onSignalingChange", "onSignalingChange");
        }

    }

    public void creatPcConstrains(MediaConstraints pcConstraints) {
        pcConstraints.optional.add(new KeyValuePair("DtlsSrtpKeyAgreement", "true"));
        pcConstraints.optional.add(new KeyValuePair("RtpDataChannels", "true"));
    }
    public void creatvideoConstraints(MediaConstraints videoConstraints) {

        String MAX_VIDEO_WIDTH_CONSTRAINT = "maxWidth";
        String MIN_VIDEO_WIDTH_CONSTRAINT = "minWidth";
        String MAX_VIDEO_HEIGHT_CONSTRAINT = "maxHeight";
        String MIN_VIDEO_HEIGHT_CONSTRAINT = "minHeight";
        String MAX_VIDEO_FPS_CONSTRAINT = "maxFrameRate";
        String MIN_VIDEO_FPS_CONSTRAINT = "minFrameRate";

        int videoWidth = 0;
        int videoHeight = 0;

        // If VP8 HW video encoder is supported and video resolution is not
        // specified force it to HD.
        if ((videoWidth == 0 || videoHeight == 0) && true && MediaCodecVideoEncoder.isVp8HwSupported()) {
            videoWidth = 1280;
            videoHeight = 1280;
        }

        // Add video resolution constraints.
        if (videoWidth > 0 && videoHeight > 0) {
            videoWidth = Math.min(videoWidth, 1280);
            videoHeight = Math.min(videoHeight, 1280);
            videoConstraints.mandatory.add(new KeyValuePair(MIN_VIDEO_WIDTH_CONSTRAINT, Integer.toString(videoWidth)));
            videoConstraints.mandatory.add(new KeyValuePair(MAX_VIDEO_WIDTH_CONSTRAINT, Integer.toString(videoWidth)));
            videoConstraints.mandatory.add(new KeyValuePair(MIN_VIDEO_HEIGHT_CONSTRAINT, Integer.toString(videoHeight)));
            videoConstraints.mandatory.add(new KeyValuePair(MAX_VIDEO_HEIGHT_CONSTRAINT, Integer.toString(videoHeight)));
        }

        // Add fps constraints.
        int videoFps = 30;

        videoConstraints.mandatory.add(new KeyValuePair(MIN_VIDEO_FPS_CONSTRAINT, Integer.toString(videoFps)));
        videoConstraints.mandatory.add(new KeyValuePair(MAX_VIDEO_FPS_CONSTRAINT, Integer.toString(videoFps)));

    }
    public void creataudioConstraints(MediaConstraints pcConstraints) {
        pcConstraints.optional.add(new KeyValuePair("DtlsSrtpKeyAgreement", "true"));
        pcConstraints.optional.add(new KeyValuePair("RtpDataChannels", "true"));
    }
    public void creatsdpMediaConstraints(MediaConstraints sdpMediaConstraints) {

        sdpMediaConstraints.mandatory.add(new KeyValuePair("OfferToReceiveAudio", "true"));

        sdpMediaConstraints.mandatory.add(new KeyValuePair("OfferToReceiveVideo", "true"));

    }

    private class SDPObserver implements SdpObserver {

        @Override
        public void onCreateFailure(String arg0) {
            System.out.print(arg0);
        }

        @Override
        public void onCreateSuccess(SessionDescription origSdp) {
            if (localSdp != null) {
                return;
            }
            String sdpDescription = origSdp.description;
            if (preferIsac) {
                sdpDescription = preferCodec(sdpDescription, AUDIO_CODEC_ISAC, true);
            }
            if (videoCallEnabled && preferH264) {
                sdpDescription = preferCodec(sdpDescription, VIDEO_CODEC_H264, false);
            }
            final SessionDescription sdp = new SessionDescription(origSdp.type, sdpDescription);
            localSdp = sdp;
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    if (peerConnection != null) {
                        JSONObject json = new JSONObject();
                        try {
//                          peerConnection.setLocalDescription(null, localSdp);
                            json.putOpt("type", "offer");
                            json.putOpt("sdp", localSdp.description);
                        } catch (JSONException e) {
                            e.printStackTrace();
                        }

                        Home.socket.emit("message", json);
                    }
                }
            });
        }

        @Override
        public void onSetFailure(String arg0) {
            Log.e("onSetFailure", arg0);

        }

        @Override
        public void onSetSuccess() {

            executor.execute(new Runnable() {
                @Override
                public void run() {
                    if (peerConnection == null) {
                        return;
                    }
                    if (isInitiator) {
                        createOffer();

                    } else {
                         createAnswer();

                    }
                }
            });

        }

    }

    private static String preferCodec(String sdpDescription, String codec, boolean isAudio) {
        String[] lines = sdpDescription.split("\r\n");
        int mLineIndex = -1;
        String codecRtpMap = null;
        // a=rtpmap:<payload type> <encoding name>/<clock rate> [/<encoding
        // parameters>]
        String regex = "^a=rtpmap:(\\d+) " + codec + "(/\\d+)+[\r]?$";
        Pattern codecPattern = Pattern.compile(regex);
        String mediaDescription = "m=video ";
        if (isAudio) {
            mediaDescription = "m=audio ";
        }
        for (int i = 0; (i < lines.length) && (mLineIndex == -1 || codecRtpMap == null); i++) {
            if (lines[i].startsWith(mediaDescription)) {
                mLineIndex = i;
                continue;
            }
            Matcher codecMatcher = codecPattern.matcher(lines[i]);
            if (codecMatcher.matches()) {
                codecRtpMap = codecMatcher.group(1);
                continue;
            }
        }
        if (mLineIndex == -1) {
            // Log.w(TAG, "No " + mediaDescription + " line, so can't prefer " +
            // codec);
            return sdpDescription;
        }
        if (codecRtpMap == null) {
            // Log.w(TAG, "No rtpmap for " + codec);
            return sdpDescription;
        }
        // Log.d(TAG, "Found " + codec + " rtpmap " + codecRtpMap +
        // ", prefer at "
        // + lines[mLineIndex]);
        String[] origMLineParts = lines[mLineIndex].split(" ");
        if (origMLineParts.length > 3) {
            StringBuilder newMLine = new StringBuilder();
            int origPartIndex = 0;
            // Format is: m=<media> <port> <proto> <fmt> ...
            newMLine.append(origMLineParts[origPartIndex++]).append(" ");
            newMLine.append(origMLineParts[origPartIndex++]).append(" ");
            newMLine.append(origMLineParts[origPartIndex++]).append(" ");
            newMLine.append(codecRtpMap);
            for (; origPartIndex < origMLineParts.length; origPartIndex++) {
                if (!origMLineParts[origPartIndex].equals(codecRtpMap)) {
                    newMLine.append(" ").append(origMLineParts[origPartIndex]);
                }
            }
            lines[mLineIndex] = newMLine.toString();
            // Log.d(TAG, "Change media description: " + lines[mLineIndex]);
        } else {
            // Log.e(TAG, "Wrong SDP media description format: " +
            // lines[mLineIndex]);
        }
        StringBuilder newSdpDescription = new StringBuilder();
        for (String line : lines) {
            newSdpDescription.append(line).append("\r\n");
        }
        return newSdpDescription.toString();
    }

    private void drainCandidates() {
        if (queuedRemoteCandidates != null) {
            // Log.d(TAG, "Add " + queuedRemoteCandidates.size() +
            // " remote candidates");
            for (IceCandidate candidate : queuedRemoteCandidates) {
                // if(!candidate.sdpMid.contains("video"))
                peerConnection.addIceCandidate(candidate);
            }
            queuedRemoteCandidates = null;
        }
    }

    public void addRemoteIceCandidate(final IceCandidate candidate) {
        executor.execute(new Runnable() {
            @Override
            public void run() {
                if (peerConnection != null) {
                    if (queuedRemoteCandidates != null) {
                        queuedRemoteCandidates.add(candidate);
                    } else {
                        peerConnection.addIceCandidate(candidate);
                    }
                }
            }
        });
    }

    public void setRemoteDescription(final SessionDescription sdp) {
        executor.execute(new Runnable() {
            @Override
            public void run() {
                if (peerConnection == null) {
                    return;
                }
                String sdpDescription = sdp.description;
                if (preferIsac) {
                    sdpDescription = preferCodec(sdpDescription, AUDIO_CODEC_ISAC, true);
                }
                if (videoCallEnabled && preferH264) {
                    sdpDescription = preferCodec(sdpDescription, VIDEO_CODEC_H264, false);
                }

                SessionDescription sdpRemote = new SessionDescription(sdp.type, sdpDescription);
                peerConnection.setRemoteDescription(sdpObserver, sdpRemote);
            }
        });
    }

    @Override
    public void setFactory(PeerConnectionFactory factory) {
        this.factory = factory;
    }

    public void onWebSocketMessage(final String msg) {

        try {
            Log.e("onWebSocketMessage", msg);
            JSONObject json = new JSONObject(msg);
            json = new JSONObject(msg);
            String type = json.optString("type");
            if (type.equals("candidate")) {
                IceCandidate candidate = new IceCandidate(json.getString("id"), json.getInt("label"), json.getString("candidate"));
                addRemoteIceCandidate(candidate);
            } else if (type.equals("answer")) {
                if (isInitiator) {
                    SessionDescription sdp = new SessionDescription(SessionDescription.Type.fromCanonicalForm(type), json.getString("sdp"));
                    setRemoteDescription(sdp);
                } else {
                }
            } else if (type.equals("offer")) {
                if (!isInitiator) {
                    SessionDescription sdp = new SessionDescription(SessionDescription.Type.fromCanonicalForm(type), json.getString("sdp"));
                    setRemoteDescription(sdp);
                } else {

                }
            } else if (type.equals("bye")) {

            } else {

            }

        } catch (JSONException e) {

        }
    }

    @Override
    public void setMessage(String message) {
        if (message.toString().contains("got user media") || message.toString().contains("bye")) {

        } else
            onWebSocketMessage(message);

    }

}

Solution

  • I don't know why but I changed target=android-19 to target=android-21 and everything worked fine as desired (Except Datachannel).

    Final Working Code

    PS: You can use this code without any royalty and Guarantees :p

    Home.java

    public class Home extends Activity {
    
        public List<PeerConnection.IceServer> iceServers;
        private GLSurfaceView videoView;
        public static SocketIO socket;
        ArrayList<String> userIDs = new ArrayList<>();
        private static final String FIELD_TRIAL_VP9 = "WebRTC-SupportVP9/Enabled/";
        String RoomId = "";
        String sreverURL = "http://xx.xx.xx.xx:xxxx/";
        private EditText roomid;
        private VideoRenderer.Callbacks remote_view;
        private VideoRenderer.Callbacks local_view;
        protected PeerConnectionFactory factory;
        PeerConnectionFactory.Options options = null;
        Events pc_events;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_home);
            videoView = (GLSurfaceView) findViewById(R.id.glview_call_remote);
            VideoRendererGui.setView(videoView, new Runnable() {
                @Override
                public void run() {
                    createPeerConnectionFactory();
                }
            });
    
            remote_view = VideoRendererGui.create(0, 0, 100, 100, ScalingType.SCALE_ASPECT_FIT, false);
            local_view = VideoRendererGui.create(0, 0, 100, 100, ScalingType.SCALE_ASPECT_FILL, true);
            iceServers = new ArrayList<>();
            IceServer icc = new IceServer("stun:stun.l.google.com:19302", "", "");
            iceServers.add(icc);
            roomid = (EditText) findViewById(R.id.roomId);
            Random rand = new Random();
            roomid.setText("" + rand.nextInt(9999));
            pc_events = new peerEventHandler();
        }
    
        private void createPeerConnectionFactory() {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    PeerConnectionFactory.initializeFieldTrials(FIELD_TRIAL_VP9);
                    PeerConnectionFactory.initializeAndroidGlobals(Home.this, true, true, true, VideoRendererGui.getEGLContext());
                    try {
                        factory = new PeerConnectionFactory();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    
        public void ondail(View view) {
    
            try {
    
                try {
                    SocketIO.setDefaultSSLSocketFactory(SSLContext.getDefault());
                } catch (NoSuchAlgorithmException e1) {
                    e1.printStackTrace();
                }
    
                socket = new SocketIO();
    
                socket.connect(sreverURL, new IOCallback() {
    
                    @Override
                    public void onMessage(JSONObject json, IOAcknowledge ack) {
                    }
                    @Override
                    public void onMessage(String data, IOAcknowledge ack) {
                    }
                    @Override
                    public void onError(SocketIOException socketIOException) {
                        socketIOException.printStackTrace();
                    }
                    @Override
                    public void onDisconnect() {
                    }
                    @Override
                    public void onConnect() {
                        showToast("Connected to " + sreverURL);
                    }
                    @Override
                    public void on(final String event, IOAcknowledge ack, final Object... args) {
    
                        Log.e("Socked.on", event + ", " + args);
                        switch (getEvent(event)) {
    
                            case LOG :
                                break;
                            case MESSAGE :
                                if (args instanceof Object[]) {
                                    pc_events.setMessage(args[0].toString());
                                } else {
                                    pc_events.setMessage(args.toString());
                                }
                                break;
                            case CREATED :
                                runOnUiThread(new Runnable() {
                                    public void run() {
                                        showToast("Room Created " + args[0]);
                                    }
                                });
                                break;
                            case BROADCAST :
                                break;
                            case JOIN :
                                break;
                            case EMIT :
                                Log.e("Socked.onEMIT", args.toString());
                                startCall();
                                pc_events.createOffer();
                                break;
    
                            case ERROR :
                                Log.e("Socked.onERROR", args.toString());
                                break;
    
                            default :
    
                                break;
                        }
                    }
                });
    
                try {
                    RoomId = roomid.getText().toString();
                } catch (Exception e) {
                }
    
                socket.emit("create or join", RoomId);
    
            } catch (MalformedURLException e) {
    
                e.printStackTrace();
            }
    
        }
    
        public void oncancel(View view) {
    
        }
    
        public SocketEvent getEvent(String eventString) {
    
            SocketEvent eventType;
    
            try {
    
                if (eventString.contains("log")) {
                    eventType = SocketEvent.LOG;
                } else if (eventString.contains("created")) {
                    eventType = SocketEvent.CREATED;
                } else if (eventString.contains("emit():")) {
                    eventType = SocketEvent.EMIT;
                }
    
                else if (eventString.contains("broadcast():")) {
                    eventType = SocketEvent.BROADCAST;
                } else if (eventString.contains("message")) {
                    eventType = SocketEvent.MESSAGE;
                } else if (eventString.toLowerCase().substring(0, 20).contains("join")) {
                    eventType = SocketEvent.JOIN;
                } else {
                    eventType = SocketEvent.ERROR;
                }
    
            } catch (Exception e) {
                eventType = SocketEvent.ERROR;
            }
    
            return eventType;
    
        }
    
        public static interface Events {
    
            public void peerConnectionEvent(VideoRenderer.Callbacks localRender, VideoRenderer.Callbacks remoteRender);
    
            public void setFactory(PeerConnectionFactory factory);
    
            public void setMessage(String message);
            public void createOffer();
    
            public void sendMessage(String msg);
        }
    
        private void startCall() {
    
            pc_events.setFactory(factory);
    
            pc_events.peerConnectionEvent(remote_view, local_view);
    
        }
    
        public void showToast(final String message) {
    
            runOnUiThread(new Runnable() {
                public void run() {
                    Toast.makeText(Home.this, message, Toast.LENGTH_SHORT).show();
                }
            });
        }
    
        public void makeOffer(View v) {
            pc_events.sendMessage("Hello");
        }
    
    }
    

    peerEventHandler.java

    public class peerEventHandler implements Events {
    
        private PeerConnection peerConnection;
        private PeerConnectionFactory factory;
        PCObserver pcObserver = new PCObserver();
        public LooperExecutor executor;
    
        private MediaStream mediaStream;
    
        private VideoSource videoSource;
        private DcObserver dc_observer;
        public static final String VIDEO_TRACK_ID = "ARDAMSv0";
        public static final String AUDIO_TRACK_ID = "ARDAMSa0";
    
        private VideoCapturerAndroid videoCapturer;
        private VideoTrack localVideoTrack;
        private VideoTrack remoteVideoTrack;
        public boolean preferIsac = false;
        public boolean videoCallEnabled = true;
        public boolean preferH264 = false;
    
        private SessionDescription localSdp;
    
        private final SDPObserver sdpObserver = new SDPObserver();
    
        public boolean isInitiator = false;
        private MediaConstraints sdpMediaConstraints;
    
        private VideoRenderer.Callbacks remote_view;
        private VideoRenderer.Callbacks local_view;
        private DataChannel dataChannel;
    
        @Override
        public void peerConnectionEvent(Callbacks remoteRender, Callbacks localRender) {
    
            this.remote_view = remoteRender;
            this.local_view = localRender;
            creatPeerConnection();
    
        }
    
        public void creatPeerConnection() {
    
            executor = new LooperExecutor();
            executor.requestStart();
    
            MediaConstraints pcConstraints = new MediaConstraints();
            MediaConstraints videoConstraints = new MediaConstraints();
            MediaConstraints audioConstraints = new MediaConstraints();
            sdpMediaConstraints = new MediaConstraints();
    
            creatPcConstrains(pcConstraints);
            creatvideoConstraints(videoConstraints);
            creatsdpMediaConstraints(sdpMediaConstraints);
    
            List<PeerConnection.IceServer> iceServers = new ArrayList<PeerConnection.IceServer>();
    
            IceServer iceServer = new IceServer("stun:stun.l.google.com:19302", "", "");
    
            iceServers.add(iceServer);
    
            PeerConnection.RTCConfiguration rtcConfig = new PeerConnection.RTCConfiguration(iceServers);
    
            rtcConfig.tcpCandidatePolicy = PeerConnection.TcpCandidatePolicy.DISABLED;
            rtcConfig.bundlePolicy = PeerConnection.BundlePolicy.BALANCED;
            rtcConfig.rtcpMuxPolicy = PeerConnection.RtcpMuxPolicy.NEGOTIATE;
    
            peerConnection = factory.createPeerConnection(rtcConfig, pcConstraints, pcObserver);
    
            Logging.enableTracing("logcat:", EnumSet.of(Logging.TraceLevel.TRACE_DEFAULT), Logging.Severity.LS_WARNING);
    
            mediaStream = factory.createLocalMediaStream("ARDAMS");
    
            String cameraDeviceName = CameraEnumerationAndroid.getDeviceName(0);
            String frontCameraDeviceName = CameraEnumerationAndroid.getNameOfFrontFacingDevice();
    
            cameraDeviceName = frontCameraDeviceName;
    
            videoCapturer = VideoCapturerAndroid.create(cameraDeviceName, null);
    
            videoSource = factory.createVideoSource(videoCapturer, videoConstraints);
    
            localVideoTrack = factory.createVideoTrack(VIDEO_TRACK_ID, videoSource);
            localVideoTrack.setEnabled(true);
            localVideoTrack.addRenderer(new VideoRenderer(local_view));
            mediaStream.addTrack(factory.createAudioTrack(AUDIO_TRACK_ID, factory.createAudioSource(audioConstraints)));
            mediaStream.addTrack(localVideoTrack);
            peerConnection.addStream(mediaStream);
    
            dataChannel = peerConnection.createDataChannel("sendDataChannel", new DataChannel.Init());
            dc_observer = new DcObserver();
            dataChannel.registerObserver(dc_observer);
    
        }
    
        @Override
        public void createOffer() {
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    if (peerConnection != null) {
                        isInitiator = true;
                        peerConnection.createOffer(sdpObserver, sdpMediaConstraints);
                    }
                }
            });
    
        }
    
        public void createAnswer() {
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    if (peerConnection != null) {
                        isInitiator = false;
                        peerConnection.createAnswer(sdpObserver, sdpMediaConstraints);
                    }
                }
            });
        }
    
        private class PCObserver implements PeerConnection.Observer {
    
            @Override
            public void onAddStream(final MediaStream stream) {
    
                Log.e("onAddStream", "onAddStream");
                executor.execute(new Runnable() {
                    @Override
                    public void run() {
                        if (peerConnection == null) {
                            return;
                        }
                        if (stream.audioTracks.size() > 1 || stream.videoTracks.size() > 1) {
                            // /reportError("Weird-looking stream: " + stream);
                            return;
                        }
                        if (stream.videoTracks.size() == 1) {
                            remoteVideoTrack = stream.videoTracks.get(0);
                            remoteVideoTrack.setEnabled(true);
                            remoteVideoTrack.addRenderer(new VideoRenderer(remote_view));
                            VideoRendererGui.update(local_view, 75, 70, 60, 60, ScalingType.SCALE_ASPECT_FIT, true);
                            VideoRendererGui.update(remote_view, 0, 0, 200, 200, ScalingType.SCALE_ASPECT_FILL, false);
                        }
                    }
                });
    
    
    
            }
    
            @Override
            public void onDataChannel(final DataChannel dc) {
                executor.execute(new Runnable() {
                    @Override
                    public void run() {
                        dataChannel = dc;
                        String channelName = dataChannel.label();
                        dataChannel.registerObserver(new DcObserver());
                    }
                });
            }
    
            @Override
            public void onIceCandidate(IceCandidate candidate) {
    
                SocketIO socket = Home.socket;
    
                JSONObject json = new JSONObject();
                try {
    
                    json.putOpt("type", "candidate");
                    json.putOpt("label", candidate.sdpMLineIndex);
                    json.putOpt("id", candidate.sdpMid);
                    json.putOpt("candidate", candidate.sdp);
    
                } catch (JSONException e) {
                    e.printStackTrace();
                }
    
                socket.emit("message", json);
            }
    
            @Override
            public void onIceConnectionChange(IceConnectionState arg0) {
            }
    
            @Override
            public void onIceConnectionReceivingChange(boolean arg0) {
            }
    
            @Override
            public void onIceGatheringChange(IceGatheringState arg0) {
            }
    
            @Override
            public void onRemoveStream(MediaStream arg0) {
            }
    
            @Override
            public void onRenegotiationNeeded() {
            }
    
            @Override
            public void onSignalingChange(SignalingState arg0) {
            }
    
        }
    
        public void creatPcConstrains(MediaConstraints pcConstraints) {
            pcConstraints.optional.add(new KeyValuePair("DtlsSrtpKeyAgreement", "true"));
            pcConstraints.optional.add(new KeyValuePair("RtpDataChannels", "true"));
            pcConstraints.optional.add(new KeyValuePair("internalSctpDataChannels", "true"));
        }
        public void creatvideoConstraints(MediaConstraints videoConstraints) {
    
            String MAX_VIDEO_WIDTH_CONSTRAINT = "maxWidth";
            String MIN_VIDEO_WIDTH_CONSTRAINT = "minWidth";
            String MAX_VIDEO_HEIGHT_CONSTRAINT = "maxHeight";
            String MIN_VIDEO_HEIGHT_CONSTRAINT = "minHeight";
            String MAX_VIDEO_FPS_CONSTRAINT = "maxFrameRate";
            String MIN_VIDEO_FPS_CONSTRAINT = "minFrameRate";
    
            int videoWidth = 0;
            int videoHeight = 0;
    
            if ((videoWidth == 0 || videoHeight == 0) && true && MediaCodecVideoEncoder.isVp8HwSupported()) {
                videoWidth = 1280;
                videoHeight = 1280;
            }
    
            if (videoWidth > 0 && videoHeight > 0) {
                videoWidth = Math.min(videoWidth, 1280);
                videoHeight = Math.min(videoHeight, 1280);
                videoConstraints.mandatory.add(new KeyValuePair(MIN_VIDEO_WIDTH_CONSTRAINT, Integer.toString(videoWidth)));
                videoConstraints.mandatory.add(new KeyValuePair(MAX_VIDEO_WIDTH_CONSTRAINT, Integer.toString(videoWidth)));
                videoConstraints.mandatory.add(new KeyValuePair(MIN_VIDEO_HEIGHT_CONSTRAINT, Integer.toString(videoHeight)));
                videoConstraints.mandatory.add(new KeyValuePair(MAX_VIDEO_HEIGHT_CONSTRAINT, Integer.toString(videoHeight)));
            }
    
            int videoFps = 30;
    
            videoConstraints.mandatory.add(new KeyValuePair(MIN_VIDEO_FPS_CONSTRAINT, Integer.toString(videoFps)));
            videoConstraints.mandatory.add(new KeyValuePair(MAX_VIDEO_FPS_CONSTRAINT, Integer.toString(videoFps)));
    
        }
        public void creataudioConstraints(MediaConstraints pcConstraints) {
            pcConstraints.optional.add(new KeyValuePair("DtlsSrtpKeyAgreement", "true"));
            pcConstraints.optional.add(new KeyValuePair("RtpDataChannels", "true"));
            pcConstraints.optional.add(new KeyValuePair("internalSctpDataChannels", "true"));
        }
        public void creatsdpMediaConstraints(MediaConstraints sdpMediaConstraints) {
    
            sdpMediaConstraints.mandatory.add(new KeyValuePair("OfferToReceiveAudio", "true"));
    
            sdpMediaConstraints.mandatory.add(new KeyValuePair("OfferToReceiveVideo", "true"));
    
        }
    
        private class SDPObserver implements SdpObserver {
    
            @Override
            public void onCreateFailure(String arg0) {
                System.out.print(arg0);
            }
    
            @Override
            public void onCreateSuccess(SessionDescription origSdp) {
                if (localSdp != null) {
                    return;
                }
                localSdp = origSdp;
                setLocalDescription(origSdp);
            }
    
            @Override
            public void onSetFailure(String arg0) {
            }
    
            @Override
            public void onSetSuccess() {
    
                executor.execute(new Runnable() {
                    @Override
                    public void run() {
                        if (peerConnection == null) {
                            return;
                        }
                        if (isInitiator) {
                            if (peerConnection != null) {
                                JSONObject json = new JSONObject();
                                try {
                                    json.putOpt("type", localSdp.type.toString().toLowerCase());
                                    json.putOpt("sdp", localSdp.description);
                                } catch (JSONException e) {
                                    e.printStackTrace();
                                }
                                Home.socket.emit("message", json);
                            }
                        } else {
                            // createAnswer();
                        }
                    }
                });
    
            }
    
        }
        public void addRemoteIceCandidate(final IceCandidate candidate) {
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    peerConnection.addIceCandidate(candidate);
                }
            });
        }
    
        public void setLocalDescription(final SessionDescription sdp) {
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    if (peerConnection == null) {
                        return;
                    }
                    peerConnection.setLocalDescription(sdpObserver, sdp);
                }
            });
        }
    
        public void setRemoteDescription(final SessionDescription sdp) {
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    if (peerConnection == null) {
                        return;
                    }
    
                    peerConnection.setRemoteDescription(sdpObserver, sdp);
                }
            });
        }
    
        @Override
        public void setFactory(PeerConnectionFactory factory) {
            this.factory = factory;
        }
    
        public void onWebSocketMessage(final String msg) {
    
            try {
                Log.e("onWebSocketMessage", msg);
                JSONObject json = new JSONObject(msg);
                json = new JSONObject(msg);
                String type = json.optString("type");
                if (type.equals("candidate")) {
                    IceCandidate candidate = new IceCandidate(json.getString("id"), json.getInt("label"), json.getString("candidate"));
                    addRemoteIceCandidate(candidate);
                } else if (type.equals("answer")) {
                    isInitiator = false;
                    SessionDescription sdp = new SessionDescription(SessionDescription.Type.fromCanonicalForm(type), json.getString("sdp"));
                    setRemoteDescription(sdp);
                } else if (type.equals("offer")) {
                    SessionDescription sdp = new SessionDescription(SessionDescription.Type.fromCanonicalForm(type), json.getString("sdp"));
                    setRemoteDescription(sdp);
                } else if (type.equals("bye")) {
                } else {
                }
    
            } catch (JSONException e) {
    
            }
        }
    
        @Override
        public void setMessage(String message) {
            if (message.toString().contains("got user media") || message.toString().contains("bye")) {
    
            } else
                onWebSocketMessage(message);
    
        }
    
        private class DcObserver implements DataChannel.Observer {
    
            @Override
            public void onMessage(DataChannel.Buffer buffer) {
    
                ByteBuffer data = buffer.data;
                byte[] bytes = new byte[data.remaining()];
                data.get(bytes);
                String command = new String(bytes);
    
                Log.e("onMessage ", command);
    
            }
    
            @Override
            public void onStateChange() {
                Log.e("onStateChange ", "onStateChange");
            }
    
            @Override
            public void onBufferedAmountChange(long arg0) {
                Log.e("onMessage ", "" + arg0);
    
            }
        }
    
        @Override
        public void sendMessage(String msg) {
            ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes());
            boolean sent = dataChannel.send(new DataChannel.Buffer(buffer, false));
            if (sent) {
                Log.e("Message sent", "" + sent);
            }
        }
    }