1.编译带ws接口的janus v0.7.6
官方文档地址 https://github.com/meetecho/janus-gateway
由于官方提供的andorid demo 是基于http c++的实现(https://github.com/meetecho/janus-mobile-sdk)对于android 过于复杂和有bug 所有使用ws 要简单的多
ws 安装后有以下日志会输出为成功
[WARN] libwebsockets has been built without IPv6 support, will bind to IPv4 only
libwebsockets logging: 0
WebSockets server started (port 8188)...
2.android studio 导入webrtc和websoket库
implementation 'org.webrtc:google-webrtc:1.0.26131' 可以用最新的 implementation 'com.github.dfqin:grantor:2.5' implementation "org.java-websocket:Java-WebSocket:1.3.8" 如果不熟悉android webrtc的开发的可以参考https://www.jianshu.com/p/8c10146afd6c
3.实现webscoket
主要对webscoket 的消息进行发送和接收 并把sdp信息给webrtc peerconntion
package com.liiln.janus5.echo; import android.util.Log; import com.liiln.janus5.janus.MyTools; import org.java_websocket.client.WebSocketClient; import org.java_websocket.drafts.Draft; import org.java_websocket.drafts.Draft_6455; import org.java_websocket.extensions.IExtension; import org.java_websocket.handshake.ServerHandshake; import org.java_websocket.protocols.IProtocol; import org.java_websocket.protocols.Protocol; import org.json.JSONException; import org.json.JSONObject; import org.webrtc.IceCandidate; import org.webrtc.SessionDescription; import java.math.BigInteger; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Collections; import java.util.concurrent.ConcurrentHashMap; public class EchoWebSocketChannel extends WebSocketClient { private ConcurrentHashMap<BigInteger, PlugHandle> attachedPlugins = new ConcurrentHashMap(); private ConcurrentHashMap<String, ITransaction> transactions = new ConcurrentHashMap(); private BigInteger sessionId = null; private Delegate _delegate = null; public EchoWebSocketChannel(URI serverUri, Draft protocolDraft) { super(serverUri, protocolDraft); } @Override public void onOpen(ServerHandshake handshakedata) { // createSession(); } @Override public void onMessage(String message) { Log.e("提示收到消息》》》",message); // try { JSONObject msg = new JSONObject(message); String janus = msg.getString("janus"); if(janus.equals("success")){ String tid = msg.getString("transaction"); ITransaction transaction = transactions.get(tid); if(transaction!=null){ transactions.remove(tid); transaction.onSuccess(msg); } }else if(janus.equals("error")){ String tid = msg.getString("transaction"); ITransaction transaction = transactions.get(tid); if(transaction!=null){ transactions.remove(tid); transaction.onError(); } }else if(janus.equals("ack")){ }else{ if(msg.has("sender")) { BigInteger sender = new BigInteger(msg.getString("sender")); PlugHandle handle = attachedPlugins.get(sender); if (handle != null) { if (janus.equals("event")) { JSONObject data = msg.getJSONObject("plugindata").getJSONObject("data"); JSONObject jsep = null; if(msg.has("jsep")){ jsep = msg.getJSONObject("jsep"); } _delegate.onMessage(sender, data, jsep); // } else if (janus.equals("detached")) { _delegate.onLeaving(sender);// } } } } } catch (Exception e) { e.printStackTrace(); } } @Override public void onClose(int code, String reason, boolean remote) { } @Override public void onError(Exception ex) { } public interface Delegate{ void onMessage(BigInteger handleId, JSONObject msg, JSONObject jsep); void onLeaving(BigInteger handleId); void onAttached(BigInteger handleId); } public void setDelegate(Delegate delegate){ _delegate =delegate; } public static EchoWebSocketChannel init(String url) { EchoWebSocketChannel websocket = null; try { ArrayList<IProtocol> protocols = new ArrayList<IProtocol>(); protocols.add(new Protocol("janus-protocol")); Draft_6455 proto_janus = new Draft_6455(Collections.<IExtension>emptyList(), protocols); websocket = new EchoWebSocketChannel(new URI(url),proto_janus); } catch (URISyntaxException e) { e.printStackTrace(); } websocket.connect(); return websocket; } public void disconnect(){ stopKeepAliveTimer(); super.close(); } public void sendmessage(String message){ Log.e("提示","send==>>"+message); send(message); } private volatile Thread keep_alive = null; boolean connected = false; private void startKeepAliveTimer(){ connected = true; keep_alive = new Thread(new Runnable() { @Override public void run() { Thread thisThread = Thread.currentThread(); while (keep_alive == thisThread) { try { thisThread.sleep(25000); } catch (InterruptedException ex) { } if (!connected) { return; } JSONObject obj = new JSONObject(); try { obj.put("janus", "keepalive"); obj.put("session_id", sessionId); obj.put("transaction", MyTools.randomString(12)); sendmessage(obj.toString()); } catch (JSONException e) { connected = false; return; } } } }, "KeepAlive"); Log.e("提示","socket end"); keep_alive.start(); } private void stopKeepAliveTimer(){ keep_alive = null; connected = false; } private void createSession(){ String tid = MyTools.randomString(12); transactions.put(tid, new ITransaction(tid) { @Override public void onSuccess(JSONObject msg) throws Exception{ JSONObject data = msg.getJSONObject("data"); sessionId = new BigInteger(data.getString("id")); Log.e("===>","收到sessionId"+sessionId); startKeepAliveTimer(); createHandle(); /** JSONObject objx = new JSONObject(); objx.put("janus", "info"); objx.put("transaction", stringGenerator.randomString(12)); sendmessage(objx.toString()); **/ } }); try{ JSONObject obj = new JSONObject(); obj.put("janus", "create"); obj.put("transaction", tid); sendmessage(obj.toString()); }catch (Exception e){ e.printStackTrace(); } } private void createHandle(){ String tid = MyTools.randomString(12); transactions.put(tid, new ITransaction(tid) { @Override public void onSuccess(JSONObject msg) throws Exception{ JSONObject data = msg.getJSONObject("data"); BigInteger handleId = new BigInteger(data.getString("id")); Log.e("===>","handleId="+handleId); _delegate.onAttached(handleId); PlugHandle handle = new PlugHandle(handleId); attachedPlugins.put(handleId,handle); //registerUsrname(handleId); } }); try{ JSONObject obj = new JSONObject(); obj.put("janus", "attach"); obj.put("transaction", tid); obj.put("plugin", "janus.plugin.echotest"); obj.put("session_id",sessionId); sendmessage(obj.toString()); }catch (Exception e){ e.printStackTrace(); } } public void publisherCreateOffer(final BigInteger handleId, final SessionDescription sdp) { JSONObject publish = new JSONObject(); JSONObject jsep = new JSONObject(); JSONObject message = new JSONObject(); try { publish.putOpt("request", "configure"); publish.putOpt("audio", true); publish.putOpt("video", true); jsep.putOpt("type", sdp.type); jsep.putOpt("sdp", sdp.description); message.putOpt("janus", "message"); message.putOpt("body", publish); message.putOpt("jsep", jsep); message.putOpt("transaction", MyTools.randomString(12) ); message.putOpt("session_id", sessionId); message.putOpt("handle_id", handleId); } catch (JSONException e) { e.printStackTrace(); } sendmessage(message.toString()); } public void trickleCandidate(final BigInteger handleId, final IceCandidate iceCandidate) { JSONObject candidate = new JSONObject(); JSONObject message = new JSONObject(); try { candidate.putOpt("candidate", iceCandidate.sdp); candidate.putOpt("sdpMid", iceCandidate.sdpMid); candidate.putOpt("sdpMLineIndex", iceCandidate.sdpMLineIndex); message.putOpt("janus", "trickle"); message.putOpt("candidate", candidate); message.putOpt("transaction", MyTools.randomString(12)); message.putOpt("session_id", sessionId); message.putOpt("handle_id", handleId); } catch (JSONException e) { e.printStackTrace(); } sendmessage(message.toString()); } public void trickleCandidateComplete(final BigInteger handleId) { JSONObject candidate = new JSONObject(); JSONObject message = new JSONObject(); try { candidate.putOpt("completed", true); message.putOpt("janus", "trickle"); message.putOpt("candidate", candidate); message.putOpt("transaction", MyTools.randomString(12)); message.putOpt("session_id", sessionId); message.putOpt("handle_id", handleId); } catch (JSONException e) { e.printStackTrace(); } sendmessage(message.toString()); } }
//额外类
public abstract class ITransaction { public String _tid = null; public ITransaction(String tid){ _tid = tid; } public void onError(){}; public void onSuccess(JSONObject data)throws Exception{}; }
public class PlugHandle { private BigInteger _handleId = null; public PlugHandle(BigInteger handleId){ _handleId = handleId; } }
3 、结合webrtc库使用 参考上面的android webrtc 连接
1.创建PeerConnectionFactory 、PeerConnection
2.peerConnection.addStream(mediaStream);
3.peerConnection.createOffer 成功后将sdp 发送
_websocket.publisherCreateOffer(handleId,sdp);
4.发送后webscoket会收到anwer的sdp信息设置给peerConnection
peerConnection.setRemoteDescription
5.设置后peerConnection 的onAddStream会返回 video数据 用webrtc.SurfaceViewRenderer显示就行了
VideoTrack remoteVideoTrack = mediaStream.videoTracks.get(0); runOnUiThread(() -> { if(remoteVideoTrack!=null) remoteVideoTrack.addSink(remoteView); });
6.ice的调用
trickleCandidate(handleId, candidate);和trickleCandidateComplete(handleId); 发送
4、测试
其他demo 比如会议、对话等可以参考博客列表
交流群261074724