Kurento 实战之四:应用开发指南
按照上述方式将代码做好拆分,划定边界,不论是阅读官方 demo 还是自己开发应用,都能条理清晰的应对,接下来一起学习官方的 hello-world 源码,看看一个完整的 Kurento 应用是如何开发出来的
WebSocket 相关
最简单的逻辑应该是通用的 WebSocket 处理了,咱们先看这部分,复杂的稍后再说,Handler 类中和 WebSockert 相关的逻辑如下:
继承自 TextWebSocketHandler(只处理 text 类型的数据,对于二进制数据直接关闭会话);
重写 afterConnectionEstablished:WebSocket 连接建立的回调,只打了一行日志;
重写 handleTransportError:WebSocket 发生异常时候的回调,仅关闭 WebSocketSession;
重写 afterConnectionClosed:不论 WebSocket 是正常关闭还是发生异常,此方法都会执行,逻辑也很简单,就是调用 stop 方法,这个方法是用来释放 KMS 资源的,有好几处都会调用,我们留到稍后和其他处理 KMS 的地方一起讲;
WebSockert 部分最重要的代码是 handleTextMessage 方法,里面是收到前端数据时的处理逻辑:先把数据转为 JsonObject 对象,此对象的 messageId 字段有四种值,每一种 id 及其对应的处理方法如下表格所示:
| messageId | 处理方法 | 说明 |
| --- | --- | --- |
| PROCESS_SDP_OFFER | handleProcessSdpOffer | 收到前端 SDPOffer 数据后的处理逻辑 |
| ADD_ICE_CANDIDATE | handleAddIceCandidate | 收到前端 ICE 数据后的处理逻辑 |
| STOP | handleStop | HashMap 删除用户数据,再远程调用 MediaPipeline.release |
| ERROR | handleError | HashMap 删除用户数据,再远程调用 MediaPipeline.release |
并不是所有的应用都需要重写上诉全部代码,还是以实际需求出发决定是否要重写,以 kurento-one2one-call 项目为例,只重写了 handleTextMessage 和 afterConnectionClosed,其他的使用父类的即可,如下图:
还有一个发送消息到浏览器侧的 sendMessage 方法,以及发送错误信息的 sendError 方法;
信令相关
kurento-hello-world 应用的功能是和 KMS 实现实时音视频通信,因此 WebRTC 标准的信令处理是必不可少的,可惜 Kurento 官方并没有对信令处理做太多封装(也可能是信令和不同的业务处理逻辑都不一样,导致不好抽象),结果就是一堆信令处理的代码散落在业务代码中;
就算业务和信令的处理代码同时出现在 Handler 类中,只要熟悉 WebRTC 的信令处理流程,也很容易读懂代码,下图结合了 WebRTC 标准的信令处理流程,对前端和服务端的代码串联在一起就行分析,左边是浏览器上执行的 js 代码,右边是服务端,这些代码都用红色箭头标识了处于 WebRTC 信令处理流程的具体位置,至此,整个流程都清晰的展现出来:
如果您在电脑或手机上看上图觉得模糊,请下载原始文件,用 draw.io 打开,文件所在目录是:https://github.com/zq2599/blog_demos/tree/master/files ,文件名为 helloworld-flow.drawio
上图列出了信令相关的所有代码,等到看完这些,剩下的就是业务代码了,也就是图中紫色部分的 handleProcessSdpOffer 方法;
业务相关
kurento-hello-world 应用是把本地摄像头和麦克风数据传到 KMS,再从 KMS 取得这些数据在页面展示,先看看官方是如何描述 KMS pipeline 的:
从上图可见 pipeline 逻辑非常简单:只有一个 WebRtcEn
dpoint,把自己的 Src 和 Sink 接上就完成了,咱们来看看对应的代码,在方法 handleProcessSdpOffer 中:
// 创建 pipeline
final MediaPipeline pipeline = kurento.createMediaPipeline();
user.setMediaPipeline(pipeline);
// 创建 webRtcEndpoint
final WebRtcEndpoint webRtcEp =
new WebRtcEndpoint.Builder(pipeline).build();
user.setWebRtcEndpoint(webRtcEp);
// 自己的 sink 连接上自己的 src
webRtcEp.connect(webRtcEp);
// ---- Endpoint configuration
String sdpOffer = jsonMessage.get("sdpOffer").getAsString();
// 注册各类监听,例如媒体资源状态变化、ICE 变化等
// 通过 websocket 回复 SDP Offer
initWebRtcEndpoint(session, webRtcEp, sdpOffer);
log.info("[Handler::handleStart] New WebRtcEndpoint: {}",
webRtcEp.getName());
// ---- Endpoint startup
// 取得 ICE 信息
startWebRtcEndpoint(webRtcEp);
再来看看停止 WebRtc 的 stop 方法,其实就是向 KMS 发送了 release 指令:
private void stop(final WebSocketSession session) {
// Remove the user session and release all resources
final UserSession user = users.remove(session.getId());
if (user != null) {
MediaPipeline mediaPipeline = user.getMediaPipeline();
if (mediaPipeline != null) {
log.info("[Handler::stop] Release the Media Pipeline");
mediaPipeline.release();
}
}
}
评论