我们在做Android平台轻量级RTSP服务和内网RTSP网关的时候,遇到过这样的问题,如何同时发布多路RTSP流出去?
回答这个问题,实际上不难,大牛直播SDK在设计这个模块的时候,考虑到了一个Service带多路流的情况,以下是大概的技术实现:
启动RTSP服务:
启动RTSP服务的时候,你可以注意到OpenRtspServer()会返回rtsp_handle_,这个句柄对应一个RTSP Service。
- 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拉流URL)?
实际上,只要确保,每个发布的RTSP流,对应一个publisherHandle,音视频数据投递,也是传递这个publisherHandle。
- private boolean StartRtspStream(long publisherHandle, String rtsp_stream_name)
- {
- if (isRTSPPublisherRunning)
- return false;
-
- libPublisher.SetRtspStreamName(publisherHandle, rtsp_stream_name);
- libPublisher.AddRtspStreamServer(publisherHandle, rtsp_handle_, 0);
-
- if (libPublisher.StartRtspStream(publisherHandle, 0) != 0)
- {
- Log.e(TAG, "调用发布rtsp流接口失败!");
-
- if (!isPushing)
- {
- libPublisher.SmartPublisherClose(publisherHandle);
- publisherHandle = 0;
- }
-
- return false;
- }
-
- isRTSPPublisherRunning = true;
- return true;
- }
停止发布RTSP流:
- //停止发布RTSP流
- private void stopRtspPublisher(long publisherHandle)
- {
- if(!isRTSPPublisherRunning)
- return;
-
- isRTSPPublisherRunning = false;
-
- if (null == libPublisher || 0 == publisherHandle)
- return;
-
- libPublisher.StopRtspStream(publisherHandle);
-
- if (!isPushing && !isRTSPServiceRunning)
- {
- releasePublisherHandle();
- }
- }
每一路发布的RTSP流,都对应一个event回调,回上来外部可以拉流的RTSP URL:
- class EventHandlePublisherV2 implements NTSmartEventCallbackV2 {
- @Override
- public void onNTSmartEventCallbackV2(long handle, int id, long param1, long param2, String param3, String param4, Object param5) {
-
- Log.i(TAG, "EventHandlePublisherV2: handle=" + handle + " id:" + id);
-
- String publisher_event = "";
-
- switch (id) {
- ...
- case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_RTSP_URL:
- publisher_event = "RTSP服务URL: " + param3;
- break;
- ...
- }
- }
- }
如果需要获取单个Service的会话链接数:
- //获取RTSP会话数
- class ButtonGetRtspSessionNumbersListener implements OnClickListener {
- public void onClick(View v) {
- if (libPublisher != null && rtsp_handle_ != 0) {
- int session_numbers = libPublisher.GetRtspServerClientSessionNumbers(rtsp_handle_);
-
- Log.i(TAG, "GetRtspSessionNumbers: " + session_numbers);
-
- PopRtspSessionNumberDialog(session_numbers);
- }
- }
- };
以上是大概的设计思路,感兴趣的开发者可以参考。