远程控制平台一之推拉流的实现

本文介绍了通过JeroMQ实现远程控制平台的推拉流技术。选择了JeroMQ作为框架,因其简单易用,避免了其他框架的延迟问题和复杂配置。推流通过“订阅-发布”模式实现,受控端发布,服务器订阅。拉流过程类似,服务器推流,主控端订阅。还讨论了身份认证和降低网络延迟的策略。
摘要由CSDN通过智能技术生成

确定框架

在选用推拉流框架的时候,有了解过nginx+rtmp/rtsp,Janus,以及其他开源的推拉流框架,要么是延迟严重(延迟一分多钟),要么配置复杂,而且这些框架对于只是转发远程画面这个简单需求来说,过于庞大了。机缘巧合之下,我了解到了一个简单易用的框架,就是ZeroMQ的Java版本–JeroMQ

然后就是具体实现了,前提是我们有一个属于自己的公网服务器,否则是不可能实现跨局域网推拉流的,整体流程是:
受控端推流 --> 服务器收流 --> 主控端拉流

推流实现

这里用到JeroMQ的“订阅-发布”模式,公网服务器订阅,受控端发布,就实现了推流。
①服务器端订阅

public void startReceiveService()  {
    // 收流服务器
    ZContext receiveContext = new ZContext();
    ZMQ.Socket receiveSocket = receiveContext.createSocket(SocketType.SUB);
    receiveSocket.bind("tcp://" + Constants.SERVER_IP + ":" + Constants.SERVER_PORT_RECEIVE);
    receiveSocket.subscribe("".getBytes(ZMQ.CHARSET));
    while (true) {
        // 在这里阻塞,直到有数据发过来
        byte[] reply = receiveSocket.recv(0);

        if (reply != null) {
            // 收流服务器收到消息后,用推流服务器推送收到的数据给其他客户端
            try {
                publishSocket.send(reply);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

②受控端推流

/**
 * 推流连接服务器
 */
fun startPublishStreamService() {
    publishStreamUrl = "tcp://${FollowManager.currentFollowBean.p}:${KeyValue.publishPortStream}"
    publishStreamExecutorService.submit(Runnable {
        if (publishStreamSocket != null) {
            return@Runnable
        }
        try {
            publishStreamContext = ZContext()
            publishStreamSocket = publishStreamContext?.createSocket(SocketType.PUB)
            publishStreamSocket?.connect(publishStreamUrl)
        } catch (e: Exception) {
            L.e(e.message)
            AppUtils.relaunchAppOnChildThread("推流服务启动失败")
        }
    })
}

受控端在获取到画面数据后,直接调用publishStreamSocket?.send(data)即可。

拉流实现

拉流过程是类似的,我们这里也是使用了JeroMQ的“订阅-发布”模式。

①主控端订阅

private fun startPreviewVideo() {
    mZContext = ZContext()
    mSocket = mZContext!!.createSocket(SocketType.SUB)
    mSocket.connect(serverUrl)
    mSocket.subscribe("".toByteArray())
    val paint = Paint()
    var widthScale = -1.0f
    var heightScale = -1.0f
    while (mSurfaceHolder != null && mZContext != null && !mZContext!!.isClosed && !Thread.currentThread().isInterrupted) {
        try {
            val receivedData = mSocket.recv()
            获取到数据后渲染...
        } catch (e:Exception) {
            e.printStackTrace()
        }
    }
}

②服务器端推流

private ZMQ.Socket publishSocket;
public void startPublishService() {
    // 推流服务器
    ZContext publishContext = new ZContext();
    publishSocket = publishContext.createSocket(SocketType.PUB);
    Application.enableStreamSetting(publishSocket);
    EncryptUtils.enableAuth(publishSocket);
    publishSocket.bind("tcp://" + Constants.SERVER_IP + ":" + Constants.SERVER_PORT_PUBLISH);
}

以上简单几行代码,就实现了最核心的推拉流难题。

身份认证

推拉流是通了,但这样还不够,因为我们的服务器是所有人都能访问的,如果服务器IP和以上2个端口都被泄露了的话,岂不是人人都可以窥探到我们设备的画面?于是需要新增认证设置。
①服务器端

public static final ZCert serverCert = new ZCert();
public static void enableAuth(ZMQ.Socket socket) {
    socket.setZapDomain("global".getBytes());
    socket.setCurveServer(true);
    socket.setCurvePublicKey(serverCert.getPublicKey());
    socket.setCurveSecretKey(serverCert.getSecretKey());
}

②客户端

private val clientCert = ZCert().apply {
    setMeta("name", "Client test certificate")
    setMeta("meta1/meta2/meta3", "Third level of meta")
}
fun ZMQ.Socket.enableCommonAuth() {
    curvePublicKey = clientCert.publicKey
    curveSecretKey = clientCert.secretKey
    curveServerKey = serverPublicKey
}

这里唯一要注意的点是,我们需要把服务器的serverPublicKey传给客户端,客户端在启动的时候需要用到这个serverPublicKey。怎么实现呢?首先在服务器启动的时候,把生成的serverPublicKey(byte数组)转成字符串发送给自己的邮箱,当然你也可以输出到本地文件,复制生成的字符串到客户端进行解码就可以了。

网络延迟

以上几乎已经是服务器端所有功能了,实际跑起来的时候,发现在网络不好的情况下,延迟还是比较大的,这是因为默认JeroMQ默认会缓存很长时间的画面,我们需要简单设置一下,尽量减少缓存,但太少又不行,会出现数据不推送,或者延迟没改善的问题,经过多次测试,以下设置可以把延迟控制在5秒内。

socket.setReceiveBufferSize(100*1024);
socket.setHWM(2);

在服务器和客户端都做同样的设置即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ithouse

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值