DataChannel.state() 总是 returns 正在连接 webRTC Android

DataChannel.state() always returns CONNECTING webRTC Android

我在 Node.js 服务器上有一个 android 应用程序 运行 作为 WebRTC 服务器 运行 的客户端。 该应用程序的当前状态是我可以进行视频通话,但无法在 DataChannel 上发送消息。

这是 android 应用程序的完整代码。

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);
        }
    }
}

欢迎大家提出意见和建议;)

WebRTC 数据通道现在通过 SCTP 工作,因此您可以删除 RtpDataChannels 约束。