4

如何在Unity下采集音视频实现轻量级RTSP服务(类似于IPC)

 1 year ago
source link: https://blog.51cto.com/daniusdk/5692039
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

如何在Unity下采集音视频实现轻量级RTSP服务(类似于IPC)

精选 原创

好多开发者在做虚拟仿真、VR教育等场景的时候,遇到个问题,想把头显里面的画面在内网环境下低延迟的同步出来,又不想单独部署流媒体服务器。为此,我们在Unity下,添加了轻量级RTSP服务模块,通过头显端启动个轻量级RTSP服务,把采集到的音视频数据,通过对外提供RTSP拉流URL的形式,供内网其他终端调用,废话不多说,先上图看效果:

如何在Unity下采集音视频实现轻量级RTSP服务(类似于IPC)_Unity RTSP

上图展示的是,Android的Unity下Camera场景获取到texture数据编码后,注入RTSP服务和RTMP推送模块。二者可以单独使用,也可同时使用,相互不影响。其中轻量级RTSP服务,可实时查看链接的RTSP会话数。

由于我们原生Android平台轻量级RTSP服务已经有多年积累,本次只是把相关的接口,同步过来。

首先来看RTSP Server相关的接口设计:

/*
* 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实例串起来:

/*+++++++++++++++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如下:

启动、停止轻量级服务:

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流:

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流:

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流:

//停止发布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会话数:

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服务,由于不需要单独部署流媒体服务器,在内网小并发场景下,使用起来非常方便,感兴趣的开发者可酌情参考。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK