1. 通信API

通信的过程是在封装好的jar包里面的,KurentoRoomAPI->(继承)KurentoAPI->(实现)JsonRpcWebSocketClient.WebSocketConnectionEvents。

public interface WebSocketConnectionEvents {

public void onOpen(ServerHandshake handshakedata);

public void onRequest(JsonRpcRequest request);

public void onResponse(JsonRpcResponse response);

public void onNotification(JsonRpcNotification notification);

public void onClose(int code, String reason, boolean remote);

public void onError(Exception e);

}

 

  1. 连接Url

KurentoAPI.connectWebSocket

    public void connectWebSocket() {
        try {
            if(isWebSocketConnected()){
                return;
            }
            URI uri = new URI(wsUri);
            client = new JsonRpcWebSocketClient(uri, this,executor);
            if (webSocketClientFactory != null) {
                client.setWebSocketFactory(webSocketClientFactory);
            }
            executor.execute(new Runnable() {
                public void run() {
                    client.connect();
                }
            });
        } catch (Exception exc){
            Log.e(LOG_TAG, "connectWebSocket", exc);
        }
    }

2)发送消息

KurentoAPI.send

    protected void send(String method, HashMap<String, Object> namedParameters, int id){

        try {
            final JsonRpcRequest request = new JsonRpcRequest();
            request.setMethod(method);
            if(namedParameters!=null) {
                request.setNamedParams(namedParameters);
            }
            if(id>=0) {
                request.setId(id);
            }
            executor.execute(new Runnable() {
                public void run() {
                    if(isWebSocketConnected()) {
                        client.sendRequest(request);
                    }
                }
            });
        } catch (Exception exc){
            Log.e(LOG_TAG, "send: "+method, exc);
        }
    }

KurentoRoomAPI中实现了 具体发送业务消息的代码 

如加入聊天室:

    public void sendJoinRoom(String userId, String roomId, boolean dataChannelsEnabled, int id){
        HashMap<String, Object> namedParameters = new HashMap<>();
        namedParameters.put("user", userId);
        namedParameters.put("room", roomId);
        namedParameters.put("dataChannels", dataChannelsEnabled);
        send("joinRoom", namedParameters, id);
    }

而在项目中自己写的类VideoClientAPI又继承自KurentoRoomAPI并实现获取WebRTC监控视频的功能。

服务端还是原来的基于原本的js-kurento-player的服务端,把需要的摄像头的rtsp地址用videourl传给服务端,接入原本的webrtc的过程。

VideoClientAPI.startPlay

   public void startPlay(String videourl, String sdpOffer, int id) {
        HashMap<String, Object> namedParameters = new HashMap<>();
        namedParameters.put("videourl", videourl);
        namedParameters.put("sdpOffer", sdpOffer);
        sendWithBuffer("start", namedParameters, id);
    }

具体的webrtc的过程暂时不讨论。

另外一个自定义的消息是获取视频图片

   public void getImage(int id) {
        HashMap<String, Object> namedParameters = new HashMap<>();
        sendWithBuffer("getImage", namedParameters, id);
    }

3)接受消息

javascript的websocket建立连接后获取到服务端消息后会进入到onMessage事件方法中,在里面根据不同的消息做不同的处理。

javascript里面的websocket对象,对应于前面KurentoAPI.client对象(JsonRpcWebSocketClient类)

相对应的也有onMessage

@Override
		public void onMessage(final String message) {
			executor.execute(new Runnable() {
				@Override
				public void run() {
					if (connectionState == WebSocketConnectionState.CONNECTED) {
						try {
							JSONRPC2Message msg = JSONRPC2Message.parse(message);

							if (msg instanceof JSONRPC2Request) {
								JsonRpcRequest request = new JsonRpcRequest();
								request.setId(((JSONRPC2Request) msg).getID());
								request.setMethod(((JSONRPC2Request) msg).getMethod());
								request.setNamedParams(((JSONRPC2Request) msg).getNamedParams());
								request.setPositionalParams(((JSONRPC2Request) msg).getPositionalParams());
								events.onRequest(request);
							} else if (msg instanceof JSONRPC2Notification) {
								JsonRpcNotification notification = new JsonRpcNotification();
								notification.setMethod(((JSONRPC2Notification) msg).getMethod());
								notification.setNamedParams(((JSONRPC2Notification) msg).getNamedParams());
								notification.setPositionalParams(((JSONRPC2Notification) msg).getPositionalParams());
								events.onNotification(notification);
							} else if (msg instanceof JSONRPC2Response) {
								JsonRpcResponse notification = new JsonRpcResponse(message);
								events.onResponse(notification);
							}
						} catch (JSONRPC2ParseException e) {
							// TODO: Handle exception
						}
					}
				}
			});
		}

这里接收到一个json字符串并解析,根据json解析后的类型不同,后续调用onRequest,onResponse或者onNotification,就是JsonRpcWebSocketClient.WebSocketConnectionEvents接口中定义的那几个方法,在KurentoRoomAPI中实现了具体的代码。

@Override
    public void onResponse(JsonRpcResponse response) {
        if(response.isSuccessful()){
            JSONObject jsonObject = (JSONObject)response.getResult();
            RoomResponse roomResponse = new RoomResponse(response.getId().toString(), jsonObject);

            synchronized (listeners) {
                for (RoomListener rl : listeners) {
                    rl.onRoomResponse(roomResponse);
                }
            }
        } else {
            RoomError roomError = new RoomError(response.getError());

            synchronized (listeners) {
                for (RoomListener rl : listeners) {
                    rl.onRoomError(roomError);
                }
            }
        }
    }

    /**
     * Callback method that relays the RoomNotification to the RoomListener interface.
     */
    @Override
    public void onNotification(JsonRpcNotification notification) {
        RoomNotification roomNotification = new RoomNotification(notification);

        synchronized (listeners) {
            for (RoomListener rl : listeners) {
                rl.onRoomNotification(roomNotification);
            }
        }
    }

这里onResponse封装了一下,把信息保存到RoomResponse里面,并通过RoomListener接口的onRoomResponse将消息发给前端代码。

onNotifaction也封装了一下,保持到RoomNotifacation里面,并通过RoomListener接口的onRoomNotification将消息发给前端代码。

前端业务逻辑代码需要实现RoomListener。

public interface RoomListener {

    public void onRoomResponse(RoomResponse response);

    public void onRoomError(RoomError error);

    public void onRoomNotification(RoomNotification notification);

    public void onRoomConnected();

    public void onRoomDisconnected();
}

具体前端代码:

public class VideoPlayer implements NBMWebRTCPeer.Observer, RoomListener {

具体的接收消息并处理的代码就在VideoPlayer的onRoomNotifacation里面了。

@Override
    public void onRoomNotification(RoomNotification notification) {
        //MyLog.i("RoomListener", "onRoomNotification:" + notification);
        String method = notification.getMethod();
        switch (method) {
            case "startResponse":
                startResponse(notification);
                break;
            case "error":
//                if (state == I_AM_STARTING) {
//                    setState(I_CAN_START);
//                }
//                onError('Error message from server: ' + parsedMessage.message);
                break;
            case "playEnd":
                //playEnd();
                break;
            case "videoInfo":
                //showVideoData(parsedMessage);
                break;
            case "iceCandidate":
                addIceCandidate(notification);
                break;
            case "seek":
                //console.log (parsedMessage.message);
                break;
            case "position":
                //document.getElementById("videoPosition").value = parsedMessage.position;
                break;
            case "imageInfo":
                getImageInfo(notification);
                break;
            default:
//                if (state == I_AM_STARTING) {
//                    setState(I_CAN_START);
//                }
//                onError('Unrecognized message', parsedMessage);
                break;
        }
    }

VideoPlayer也是封装了视频通信的一个类,具体到界面相关的交互还要到Activity里面。

public class VideoClientActivity
        extends AppCompatActivity
{

    protected VideoPlayer videoPlayer=new VideoPlayer(this);

在Activiy里面调用videoPlayer里面的方法,发送消息,如:

        findViewById(R.id.btnGenerate).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                VideoDebugActivity activity = VideoDebugActivity.this;
                videoPlayer.startPlay(cbSTUN.isChecked(), cbUseBuffer.isChecked(), true, cbLocalMedia.isChecked());
            }
        });

 

处理消息:

videoPlayer.addRtcListener(new RtcListener() {
            @Override
            public void addIceCandidate(RoomNotification notification) {
                if (isShowLocalCandidate == false) {
                    showRemoteCandidates();
                }
            }

            @Override
            public void onIceCandidate(IceCandidate iceCandidate, NBMPeerConnection connection) {
                if (isShowLocalCandidate)
                    showLocalCandidates();
            }

            @Override
            public void onIceGatheringDone() {
                Logger.w("OnIceGatheringDone");
                //todo:显示offer和localCandidate
            }

            @Override
            public void getImageInfo(String base64) {
                //final Bitmap bitmap = base64ToBitmap(base64);
                //todo:显示图片
//                runOnUiThread(new Runnable() {
//                    @Override
//                    public void run() {
//                        videoImage.setImageBitmap(bitmap);
//                    }
//                });

            }
        });

这里向videoPlayer添加RtcListener,从刚刚的onRoomNotification将消息触发到Activity进行相应处理。

这里用到java的接口的匿名实现的概念,对应于c#的事件(+=相应函数)。

这里的RtcListener是自定义的接口,用于做事件响应的过程。

public interface RtcListener  {
    void addIceCandidate(RoomNotification notification) ;
    void onIceCandidate(IceCandidate iceCandidate, NBMPeerConnection connection);
    void onIceGatheringDone();
    void getImageInfo(String base64) ;
}

需要相应其他的服务端消息时,这样也要相应的增加接口。

具体调用路径:VideoPlayer的onRoomNotification->getImageInfo->notifyGetImageInfo->Activity中定位的RtcListener匿名实现类

 

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐