好多开发者在做虚拟仿真、VR教育等场景的时候,遇到个问题,想把头显里面的画面在内网环境下低延迟的同步出来,又不想单独部署流媒体服务器。为此,我们在Unity下,添加了轻量级RTSP服务模块,通过头显端启动个轻量级RTSP服务,把采集到的音视频数据,通过对外提供RTSP拉流URL的形式,供内网其他终端调用,废话不多说,先上图看效果:
上图展示的是,Android的Unity下Camera场景获取到texture数据编码后,注入RTSP服务和RTMP推送模块。二者可以单独使用,也可同时使用,相互不影响。其中轻量级RTSP服务,可实时查看链接的RTSP会话数。
由于我们原生Android平台轻量级RTSP服务已经有多年积累,本次只是把相关的接口,同步过来。
首先来看RTSP Server相关的接口设计:
代码语言:javascript复制/*
* SmartPublisherAndroidMono.cs
*
* WebSite: http://daniusdk.com
* Github: https://github.com/daniulive/SmarterStreaming
*/
/* SmartRTSPServerSDK */
/// <summary>
/// Init rtsp server(和UnInitRtspServer配对使用,即便是启动多个RTSP服务,也只需调用一次InitRtspServer,请确保在OpenRtspServer之前调用)
/// </summary>
public int NT_PB_U3D_InitRtspServer()
{
return pusher_obj_.Call<int>("InitRtspServer");
}
/// <summary>
/// 创建一个rtsp server
/// </summary>
public long NT_PB_U3D_OpenRtspServer(int reserve)
{
return pusher_obj_.Call<long>("OpenRtspServer", reserve);
}
/// <summary>
/// 设置rtsp server 监听端口, 在StartRtspServer之前必须要设置端口
/// @param rtsp_server_handle: rtsp server 句柄
/// @param port: 端口号,可以设置为554,或者是1024到65535之间,其他值返回失败
/// </summary>
public int NT_PB_U3D_SetRtspServerPort(long rtsp_server_handle, int port)
{
return pusher_obj_.Call<int>("SetRtspServerPort", rtsp_server_handle, port);
}
/// <summary>
/// 设置rtsp server 鉴权用户名和密码, 这个可以不设置,只有需要鉴权的再设置
/// @param rtsp_server_handle: rtsp server 句柄
/// @param user_name: 用户名(必须是英文), password:密码(必须是英文)
/// </summary>
public int NT_PB_U3D_SetRtspServerUserNamePassword(long rtsp_server_handle, String user_name, String password)
{
return pusher_obj_.Call<int>("SetRtspServerUserNamePassword", rtsp_server_handle, user_name, password);
}
/// <summary>
/// 设置rtsp server 组播, 如果server设置成组播就不能单播,组播和单播只能选一个, 一般来说单播网络设备支持的好,wifi组播很多路由器不支持
/// @param rtsp_server_handle: rtsp server 句柄
/// @param is_multicast: 是否组播, 1为组播, 0为单播, 其他值接口返回错误, 默认是单播
/// </summary>
public int NT_PB_U3D_SetRtspServerMulticast(long rtsp_server_handle, int is_multicast)
{
return pusher_obj_.Call<int>("SetRtspServerMulticast", rtsp_server_handle, is_multicast);
}
/// <summary>
/// 设置rtsp server 组播组播地址
/// @param rtsp_server_handle: rtsp server 句柄
/// 如果设置的不是组播地址, 将返回错误
/// 组播地址范围说明: [224.0.0.0, 224.0.0.255] 为组播预留地址, 不能设置. 可设置范围为[224.0.1.0, 239.255.255.255], 其中SSM地址范围为[232.0.0.0, 232.255.255.255]
/// </summary>
public int NT_PB_U3D_SetRtspServerMulticastAddress(long rtsp_server_handle, String multicast_address)
{
return pusher_obj_.Call<int>("SetRtspServerMulticastAddress", rtsp_server_handle, multicast_address);
}
/// <summary>
/// 获取rtsp server当前的客户会话数, 这个接口必须在StartRtspServer之后再调用
/// @param rtsp_server_handle: rtsp server 句柄
/// @return {当前rtsp server会话数}
/// </summary>
public int NT_PB_U3D_GetRtspServerClientSessionNumbers(long rtsp_server_handle)
{
return pusher_obj_.Call<int>("GetRtspServerClientSessionNumbers", rtsp_server_handle);
}
/// <summary>
/// 启动rtsp server
/// @param rtsp_server_handle: rtsp server 句柄
/// @param reserve: 保留参数传0
/// </summary>
public int NT_PB_U3D_StartRtspServer(long rtsp_server_handle, int reserve)
{
return pusher_obj_.Call<int>("StartRtspServer", rtsp_server_handle, reserve);
}
/// <summary>
/// 停止rtsp server
/// @param rtsp_server_handle: rtsp server 句柄
/// </summary>
public int NT_PB_U3D_StopRtspServer(long rtsp_server_handle)
{
return pusher_obj_.Call<int>("StopRtspServer", rtsp_server_handle);
}
/// <summary>
/// 关闭rtsp server
/// @param rtsp_server_handle: rtsp server 句柄
/// NOTE: 调用这个接口之后rtsp_server_handle失效,
/// </summary>
public int NT_PB_U3D_CloseRtspServer(long rtsp_server_handle)
{
return pusher_obj_.Call<int>("CloseRtspServer", rtsp_server_handle);
}
/// <summary>
/// UnInit rtsp server(和InitRtspServer配对使用,即便是启动多个RTSP服务,也只需调用一次UnInitRtspServer)
/// </summary>
public int NT_PB_U3D_UnInitRtspServer()
{
return pusher_obj_.Call<int>("UnInitRtspServer");
}
/*---------------SmartRTSPServerSDK---------------*/
其次是,用于publisher实例操作的接口,把publisher实例和rtsp实例串起来:
代码语言:javascript复制/* SmartRTSPServerSDK供Publisher调用的接口 */
/// <summary>
/// 设置rtsp的流名称
/// @param handle: 推送实例句柄
/// @param stream_name: 流程名称,不能为空字符串,必须是英文
/// 这个作用是: 比如rtsp的url是:rtsp://192.168.0.111/test, test就是设置下去的stream_name
/// </summary>
public int NT_PB_U3D_SetRtspStreamName(long handle, String stream_name)
{
return pusher_obj_.Call<int>("SetRtspStreamName", handle, stream_name);
}
/// <summary>
/// 给要发布的rtsp流设置rtsp server, 一个流可以发布到多个rtsp server上,rtsp server的创建启动请参考OpenRtspServer和StartRtspServer接口
/// @param handle: 推送实例句柄
/// @param rtsp_server_handle:rtsp server句柄
/// @param reserve:保留参数,传0
/// </summary>
public int NT_PB_U3D_AddRtspStreamServer(long handle, long rtsp_server_handle, int reserve)
{
return pusher_obj_.Call<int>("AddRtspStreamServer", handle, rtsp_server_handle, reserve);
}
/// <summary>
/// 清除设置的rtsp server
/// @param handle: 推送实例句柄
/// </summary>
public int NT_PB_U3D_ClearRtspStreamServer(long handle)
{
return pusher_obj_.Call<int>("ClearRtspStreamServer", handle);
}
/// <summary>
/// 启动rtsp流
/// @param handle: 推送实例句柄
/// @param reserve: 保留参数,传0
/// </summary>
public int NT_PB_U3D_StartRtspStream(long handle, int reserve)
{
return pusher_obj_.Call<int>("StartRtspStream", handle, reserve);
}
/// <summary>
/// 停止rtsp流
/// @param handle: 推送实例句柄
/// </summary>
public int NT_PB_U3D_StopRtspStream(long handle)
{
return pusher_obj_.Call<int>("StopRtspStream", handle);
}
/*---------------SmartRTSPServerSDK供Publisher调用的接口---------------*/
Unity调用demo如下:
启动、停止轻量级服务:
代码语言:javascript复制private void OnRtspServiceBtnClicked()
{
if (is_rtsp_service_running_) {
StopRtspService();
btn_rtsp_service_.GetComponentInChildren<Text>().text = "启动RTSP服务";
btn_rtsp_publisher_.GetComponent<Button>().interactable = false;
return;
}
rtsp_handle_ = NT_PB_U3D_OpenRtspServer(0);
if (rtsp_handle_ == 0) {
Debug.LogError("创建rtsp server实例失败! 请检查SDK有效性");
} else {
int port = 8554;
if (NT_PB_U3D_SetRtspServerPort(rtsp_handle_, port) != 0) {
NT_PB_U3D_CloseRtspServer(rtsp_handle_);
rtsp_handle_ = 0;
Debug.LogError("创建rtsp server端口失败! 请检查端口是否重复或者端口不在范围内!");
}
if (NT_PB_U3D_StartRtspServer(rtsp_handle_, 0) == DANIULIVE_RETURN_OK) {
is_rtsp_service_running_ = true;
Debug.Log("启动rtsp server 成功!");
} else {
NT_PB_U3D_CloseRtspServer(rtsp_handle_);
rtsp_handle_ = 0;
Debug.LogError("启动rtsp server失败! 请检查设置的端口是否被占用!");
}
btn_rtsp_service_.GetComponentInChildren<Text>().text = "停止RTSP服务";
btn_rtsp_publisher_.GetComponent<Button>().interactable = true;
}
}
服务启动后,可以发布或停止RTSP流:
代码语言:javascript复制private void OnRtspPublisherBtnClicked()
{
if (is_rtsp_publisher_running_)
{
StopRtspPublisher();
if (!is_pushing_rtmp_)
{
StopCaptureAvData();
if (coroutine_ != null) {
StopCoroutine(coroutine_);
coroutine_ = null;
}
}
btn_rtsp_publisher_.GetComponentInChildren<Text>().text = "发布RTSP流";
btn_get_rtsp_session_numbers_.GetComponentInChildren<Text>().text = "RTSP会话数";
btn_get_rtsp_session_numbers_.GetComponent<Button>().interactable = false;
btn_rtsp_service_.GetComponent<Button>().interactable = true;
input_url_.GetComponentInChildren<Text>().text = "";
}
else
{
bool is_started = StartRtspPublisher();
if(is_started)
{
btn_rtsp_publisher_.GetComponentInChildren<Text>().text = "停止RTSP流";
btn_get_rtsp_session_numbers_.GetComponent<Button>().interactable = true;
btn_rtsp_service_.GetComponent<Button>().interactable = false;
if(!is_pushing_rtmp_)
{
StartCaptureAvData();
coroutine_ = StartCoroutine(OnPostVideo());
}
}
}
}
发布RTSP流:
代码语言:javascript复制private bool StartRtspPublisher()
{
if (is_rtsp_publisher_running_)
{
Debug.Log("已推送..");
return false;
}
if (!is_pushing_rtmp_)
{
InitAndSetConfig();
}
if (pusher_handle_ == 0) {
Debug.LogError("Start rtsp publisher, pusher handle is null..");
return false;
}
String rtsp_stream_name = "stream1";
NT_PB_U3D_SetRtspStreamName(pusher_handle_, rtsp_stream_name);
NT_PB_U3D_ClearRtspStreamServer(pusher_handle_);
NT_PB_U3D_AddRtspStreamServer(pusher_handle_, rtsp_handle_, 0);
int is_suc = NT_PB_U3D_StartRtspStream(pusher_handle_, 0);
if (is_suc == DANIULIVE_RETURN_OK)
{
Debug.Log("StartRtspStream success..");
is_rtsp_publisher_running_ = true;
}
else
{
Debug.LogError("StartRtspStream failed!");
return false;
}
return true;
}
停止发布RTSP流:
代码语言:javascript复制//停止发布RTSP流
private void StopRtspPublisher() {
if(!is_rtsp_publisher_running_)
return;
NT_PB_U3D_StopRtspStream(pusher_handle_);
if(!is_pushing_rtmp_)
{
NT_PB_U3D_Close(pusher_handle_);
pusher_handle_ = 0;
NT_PB_U3D_UnInit();
}
is_rtsp_publisher_running_ = false;
}
发布RTSP流后,我们可以实时获取到链接的RTSP会话数:
代码语言:javascript复制private void OnGetRtspSessionNumbersBtnClicked()
{
if (rtsp_handle_ != 0)
{
int session_numbers = NT_PB_U3D_GetRtspServerClientSessionNumbers(rtsp_handle_);
Debug.Log("GetRtspSessionNumbers: " session_numbers);
btn_get_rtsp_session_numbers_.GetComponentInChildren<Text>().text = "RTSP会话数:"
轻量级RTSP服务,由于不需要单独部署流媒体服务器,在内网小并发场景下,使用起来非常方便,感兴趣的开发者可酌情参考。