技术背景
今天分享的是外部RTSP或RTMP流,拉取后注入到本地轻量级RTSP服务模块,供内网小并发场景下使用,这里我们叫做内网RTSP网关模块。
内网RTSP网关模块,系内置轻量级RTSP服务模块扩展,完成外部RTSP/RTMP数据拉取并注入到轻量级RTSP服务模块工作,多个内网客户端直接访问内网轻量级RTSP服务获取公网数据,无需部署单独的服务器,支持RTSP/RTMP H.265数据接入。
内置轻量级RTSP服务模块和内置RTSP网关模块共同点:
内置轻量级RTSP服务模块和内置RTSP网关模块,核心痛点是避免用户或者开发者单独部署RTSP或者RTMP服务,数据汇聚到内置RTSP服务,对外提供可供拉流的RTSP URL,适用于内网环境下,对并发要求不高的场景,支持H.264/H.265,支持RTSP鉴权、单播、组播模式,考虑到单个服务承载能力,我们支持同时创建多个RTSP服务,并支持获取当前RTSP服务会话连接数。
内置轻量级RTSP服务模块和内置RTSP网关模块不同点:数据来源不同
1. 内置轻量级RTSP服务模块,数据源来自摄像头、屏幕、麦克风等编码前数据,或者本地编码后的对接数据;
2. 内置RTSP网关模块,实际上是RTSP/RTMP拉流模块+内置轻量级RTSP服务模块组合出来的。数据源来自RTSP或RTMP网络流,拉流模块完成编码后的音视频数据回调,然后,汇聚到内置轻量级RTSP服务模块。
技术设计
本文以大牛直播SDK的转发demo基础设计为例,增加了内网RTSP网关模块,由于我们有非常稳定完善的RTSP、RTMP直播播放模块,内网RTSP网关模块,无非就是拉取到RTSP或RTMP流,把编码后的H.264/H.265数据回调上来,然后注入到轻量级RTSP服务模块即可,如下图所示:
先开始拉流,获取到拉流的RTSP或RTMP数据,实现如下:
//Author: daniusdk.com btnPullStream.setOnClickListener(new Button.OnClickListener() { // @Override public void onClick(View v) { if (isPulling) { if(isPushing || isRecording || isRTSPPublisherRunning) { Log.e(TAG, "please make sure pusher/recorder/rtsp server stopped first.."); return; } StopPull(); btnPullStream.setText("开始拉流"); btnPushStream.setEnabled(false); } else { Log.i(TAG, "onClick StartPull Stream.."); boolean is_pull_suc = StartPull(); if(!is_pull_suc) { Log.e(TAG, "call StartPull() failed!"); return; } btnPullStream.setText("停止拉流"); btnPushStream.setEnabled(true); } } });
启动RTSP服务:
//启动/停止RTSP服务 class ButtonRtspServiceListener implements OnClickListener { public void onClick(View v) { if (isRTSPServiceRunning) { stopRtspService(); btnRtspService.setText("启动RTSP服务"); btnRtspPublisher.setEnabled(false); isRTSPServiceRunning = false; return; } if(!OpenPushHandle()) { return; } Log.i(TAG, "onClick start rtsp service.."); rtsp_handle_ = libPublisher.OpenRtspServer(0); if (rtsp_handle_ == 0) { Log.e(TAG, "创建rtsp server实例失败! 请检查SDK有效性"); } else { int port = 8554; if (libPublisher.SetRtspServerPort(rtsp_handle_, port) != 0) { libPublisher.CloseRtspServer(rtsp_handle_); rtsp_handle_ = 0; Log.e(TAG, "创建rtsp server端口失败! 请检查端口是否重复或者端口不在范围内!"); } //String user_name = "admin"; //String password = "12345"; //libPublisher.SetRtspServerUserNamePassword(rtsp_handle_, user_name, password); if (libPublisher.StartRtspServer(rtsp_handle_, 0) == 0) { Log.i(TAG, "启动rtsp server 成功!"); } else { libPublisher.CloseRtspServer(rtsp_handle_); rtsp_handle_ = 0; Log.e(TAG, "启动rtsp server失败! 请检查设置的端口是否被占用!"); } btnRtspService.setText("停止RTSP服务"); btnRtspPublisher.setEnabled(true); isRTSPServiceRunning = true; } } }
发布RTSP流:
//发布/停止RTSP流 class ButtonRtspPublisherListener implements OnClickListener { public void onClick(View v) { if (isRTSPPublisherRunning) { stopRtspPublisher(); btnRtspPublisher.setText("发布RTSP流"); btnGetRtspSessionNumbers.setEnabled(false); btnRtspService.setEnabled(true); } else { Log.i(TAG, "onClick start rtsp publisher.."); boolean startRet = StartRtspStream(); if (!startRet) { Log.e(TAG, "Failed to call StartRtspStream()."); return; } btnRtspPublisher.setText("停止RTSP流"); btnGetRtspSessionNumbers.setEnabled(true); btnRtspService.setEnabled(false); } } };
如果需要获取到RTSP会话链接数:
//当前RTSP会话数弹出框 private void PopRtspSessionNumberDialog(int session_numbers) { final EditText inputUrlTxt = new EditText(this); inputUrlTxt.setFocusable(true); inputUrlTxt.setEnabled(false); String session_numbers_tag = "RTSP服务当前客户会话数: " + session_numbers; inputUrlTxt.setText(session_numbers_tag); AlertDialog.Builder builderUrl = new AlertDialog.Builder(this); builderUrl .setTitle("内置RTSP服务") .setView(inputUrlTxt).setNegativeButton("确定", null); builderUrl.show(); }
如果需要预览:
btnStartStopPlayback.setOnClickListener(new Button.OnClickListener() { // @Override public void onClick(View v) { if (isPlaying) { Log.i(TAG, "Stop playback stream++"); StopPlay(); btnStartStopPlayback.setText("开始播放 "); Log.i(TAG, "Stop playback stream--"); } else { Log.i(TAG, "Start playback stream++"); boolean startRet = StartPlay(); if (!startRet) { Log.e(TAG, "Failed to call StartPlay()."); return; } btnStartStopPlayback.setText("停止播放 "); Log.i(TAG, "Start playback stream--"); } } }); }
技术总结
内网RTSP网关,在小并发的环境下,不需要单独部署RTSP或RTMP服务,配合我们RTSP播放器,延迟毫秒级,优势非常明显,感兴趣的开发者可参考,如果需要测试,可测试看看,不止Android平台,Windows也有类似的实现。