我们在做Android平台轻量级RTSP服务和内网RTSP网关的时候,遇到过这样的问题,如何同时发布多路RTSP流出去?
回答这个问题,实际上不难,大牛直播SDK在设计这个模块的时候,考虑到了一个Service带多路流的情况,以下是大概的技术实现:
启动RTSP服务:
启动RTSP服务的时候,你可以注意到OpenRtspServer()会返回rtsp_handle_,这个句柄对应一个RTSP Service。
classButtonRtspServiceListenerimplementsOnClickListener { publicvoidonClick(Viewv) { 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 { intport=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流classButtonRtspPublisherListenerimplementsOnClickListener { publicvoidonClick(Viewv) { if (isRTSPPublisherRunning) { stopRtspPublisher(); btnRtspPublisher.setText("发布RTSP流"); btnGetRtspSessionNumbers.setEnabled(false); btnRtspService.setEnabled(true); } else { Log.i(TAG, "onClick start rtsp publisher.."); booleanstartRet=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。
privatebooleanStartRtspStream(longpublisherHandle, Stringrtsp_stream_name) { if (isRTSPPublisherRunning) returnfalse; 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; } returnfalse; } isRTSPPublisherRunning=true; returntrue; }
停止发布RTSP流:
//停止发布RTSP流privatevoidstopRtspPublisher(longpublisherHandle) { if(!isRTSPPublisherRunning) return; isRTSPPublisherRunning=false; if (null==libPublisher||0==publisherHandle) return; libPublisher.StopRtspStream(publisherHandle); if (!isPushing&&!isRTSPServiceRunning) { releasePublisherHandle(); } }
每一路发布的RTSP流,都对应一个event回调,回上来外部可以拉流的RTSP URL:
classEventHandlePublisherV2implementsNTSmartEventCallbackV2 { publicvoidonNTSmartEventCallbackV2(longhandle, intid, longparam1, longparam2, Stringparam3, Stringparam4, Objectparam5) { Log.i(TAG, "EventHandlePublisherV2: handle="+handle+" id:"+id); Stringpublisher_event=""; switch (id) { ... caseNTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_RTSP_URL: publisher_event="RTSP服务URL: "+param3; break; ... } } }
如果需要获取单个Service的会话链接数:
//获取RTSP会话数classButtonGetRtspSessionNumbersListenerimplementsOnClickListener { publicvoidonClick(Viewv) { if (libPublisher!=null&&rtsp_handle_!=0) { intsession_numbers=libPublisher.GetRtspServerClientSessionNumbers(rtsp_handle_); Log.i(TAG, "GetRtspSessionNumbers: "+session_numbers); PopRtspSessionNumberDialog(session_numbers); } } };
以上是大概的设计思路,感兴趣的开发者可以参考。