我有一个
Android应用程序作为运行在Node.js服务器上的WebRTC服务器的客户端运行.
该应用程序的当前状态是我可以进行视频通话,但无法在DataChannel上发送消息.
该应用程序的当前状态是我可以进行视频通话,但无法在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,100,ScalingType.SCALE_ASPECT_FIT,false); local_view = VideoRendererGui.create(0,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,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,true); VideoRendererGui.update(remote_view,200,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")); } 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约束.