Windows平台Unity Camera场景实现轻量级RTSP服务和RTMP推送

技术背景

随着VR技术在医疗、军事、农业、学校、景区、消防、公共安全、研学机构、展厅展馆,商场等场所普及,开发者对Unity平台下的直播体验提出了更高的要求。

技术实现

Unity平台下的RTMP推流、RTMP、RTSP播放前几年已经覆盖了Windows、Linux、Android、iOS平台。本文主要介绍Windows平台Unity环境下的轻量级RTSP服务。通过对外提供RTSP拉流URL的形式,供内网其他终端调用。

RTMP的技术方案,我们之前有探讨过,这里先说轻量级RTSP服务,轻量级RTSP服务,我们的设计是,可以启动一个RTSP Service,然后发布多个RTSP流实例,这个在多实例的设计,非常有价值,简单来说,一个RTSP Service下面挂载多个RTSP Stream,对外提供RTSP拉流的URL,整体设计方案如下:

我们看看支持的音视频采集选项,其中视频这块,除了Unity下的Camera场景覆盖,还有Windows摄像头、屏幕数据,音频采集覆盖了Unity声音、扬声器、麦克风,还有混音数据。

音视频原始数据采集到后,编码注入RTSP服务和RTMP推送模块。二者可以单独使用,也可同时使用。其中轻量级RTSP服务,可实时查看链接的RTSP会话数。

首先看启动RTSP service封装:

/*
 * PublisherWrapper.cs
 * Author: daniusdk.com
 */
public bool StartRtspService()
{
    if (NTBaseCodeDefine.NT_ERC_OK != NTSmartPublisherSDK.NT_PB_OpenRtspServer(ref rtsp_handle_, 0))
    {
        Debug.LogError("创建rtsp server实例失败! 请检查sdk有效性.");
        return false;
    }

    if (IntPtr.Zero == rtsp_handle_)
    {
        Debug.LogError("创建rtsp server实例失败! 请检查sdk有效性.");
        return false;
    }

    int port = 28554;

    if (NTBaseCodeDefine.NT_ERC_OK != NTSmartPublisherSDK.NT_PB_SetRtspServerPort(rtsp_handle_, port))
    {
        NTSmartPublisherSDK.NT_PB_CloseRtspServer(rtsp_handle_);
        rtsp_handle_ = IntPtr.Zero;
        Debug.LogError("设置rtsp server端口失败,请检查端口是否重复或者端口不在范围内!");
        return false;
    }

    //String user_name = "admin";
    //String password = "123456";

    //NTSmartPublisherSDK.NT_PB_SetRtspServerUserNamePassword(rtsp_handle, user_name, password);

    if (NTBaseCodeDefine.NT_ERC_OK == NTSmartPublisherSDK.NT_PB_StartRtspServer(rtsp_handle_, 0))
    {
        Debug.Log("StartRtspServer suc..");
    }
    else
    {
        NTSmartPublisherSDK.NT_PB_CloseRtspServer(rtsp_handle_);
        rtsp_handle_ = IntPtr.Zero;
        Debug.LogError("启动rtsp server失败, 请检查设置的端口是否被占用!");
        return false;
    }

    is_rtsp_service_running_ = true;

    return true;
}

停止RTSP Service:

public void StopRtspService()
{
    if (is_rtsp_service_running_ == false) return;

    NTSmartPublisherSDK.NT_PB_StopRtspServer(rtsp_handle_);
    NTSmartPublisherSDK.NT_PB_CloseRtspServer(rtsp_handle_);
    rtsp_handle_ = IntPtr.Zero;

    is_rtsp_service_running_ = false;
}

服务启动后,可以发布或停止RTSP流:

public bool StartRtspStream()
{
    if (CheckPublisherHandleAvailable() == false) return false;

    if (publisher_handle_ == IntPtr.Zero)
    {
        return false;
    }

    if (publisher_handle_count_ < 1)
    {
        SetCommonOptionToPublisherSDK();
    }

    String rtsp_stream_name = "stream1";

    NTSmartPublisherSDK.NT_PB_SetRtspStreamName(publisher_handle_, rtsp_stream_name);

    NTSmartPublisherSDK.NT_PB_ClearRtspStreamServer(publisher_handle_);

    NTSmartPublisherSDK.NT_PB_AddRtspStreamServer(publisher_handle_, rtsp_handle_, 0);

    if (NTBaseCodeDefine.NT_ERC_OK != NTSmartPublisherSDK.NT_PB_StartRtspStream(publisher_handle_, 0))
    {
        if (0 == publisher_handle_count_)
        {
            NTSmartPublisherSDK.NT_PB_Close(publisher_handle_);
            publisher_handle_ = IntPtr.Zero;
        }

        Debug.LogError("调用发布rtsp流接口失败");

        return false;
    }

    publisher_handle_count_++;

    is_rtsp_publisher_running_ = true;

    return true;
}

停止RTSP流:

public void StopRtspStream()
{
    publisher_handle_count_--;

    NTSmartPublisherSDK.NT_PB_StopRtspStream(publisher_handle_);

    if (0 == publisher_handle_count_)
    {
        NTSmartPublisherSDK.NT_PB_Close(publisher_handle_);
        publisher_handle_ = IntPtr.Zero;
    }

    is_rtsp_publisher_running_ = false;
}

获取RTSP session连接数:

public int GetRtspSessionNumbers()
{
    int num = 0;
    if (rtsp_handle_!=IntPtr.Zero)
    {
        if (NTBaseCodeDefine.NT_ERC_OK != NTSmartPublisherSDK.NT_PB_GetRtspServerClientSessionNumbers(rtsp_handle_, ref num))
        {
            Debug.LogError("Call NT_PB_GetRtspServerClientSessionNumbers failed..");
        }
    }

    return num;
}

封装部分看过后,我们看看我们Unity下调用示例:

启动、停止RTSP服务:

public void btn_rtsp_service_Click()
{
    if (publisher_wrapper_.IsRtspServiceRunning())
    {
        publisher_wrapper_.StopRtspService();
        btn_rtsp_service_.GetComponentInChildren<Text>().text = "启动RTSP服务";

        btn_rtsp_publisher_.interactable = false;
        return;
    }

    if (!publisher_wrapper_.StartRtspService())
    {
        Debug.LogError("调用StartRtspService失败..");
        return;
    }

    btn_rtsp_publisher_.interactable = true;

    btn_rtsp_service_.GetComponentInChildren<Text>().text = "停止RTSP服务";
}

发布、停止RTSP流:

public void btn_rtsp_publisher_Click()
{
    if (publisher_wrapper_.IsRtspPublisherRunning())
    {
        publisher_wrapper_.StopRtspStream();

        if (!publisher_wrapper_.IsPreviewing() && !publisher_wrapper_.IsPushingRtmp())
        {
            StopCaptureAvData();

            if (coroutine_ != null)
            {
                StopCoroutine(coroutine_);
                coroutine_ = null;
            }
        }

        btn_rtsp_service_.interactable = true;

        btn_rtsp_publisher_.GetComponentInChildren<Text>().text = "发布RTSP";
    }
    else
    {
        if (!publisher_wrapper_.IsRtspServiceRunning())
        {
            Debug.LogError("RTSP service is not running..");
            return;
        }


        if (!publisher_wrapper_.IsPreviewing() && !publisher_wrapper_.IsPushingRtmp())
        {
            publisher_wrapper_.SetVideoPushType(video_push_type_);
            publisher_wrapper_.SetAudioPushType(audio_push_type_);
        }

        publisher_wrapper_.StartRtspStream();

        if (!publisher_wrapper_.IsPreviewing() && !publisher_wrapper_.IsPushingRtmp())
        {
            StartCaptureAvData();
            coroutine_ = StartCoroutine(OnPostVideo());
        }

        btn_rtsp_publisher_.GetComponentInChildren<Text>().text = "停止RTSP";

        btn_rtsp_service_.interactable = false;
    }
}

获取RTSP Session链接数:

public void btn_get_rtsp_session_numbers_Click()
{
    if (publisher_wrapper_.IsRtspServiceRunning())
    {
        btn_get_rtsp_session_numbers_.GetComponentInChildren<Text>().text = "RTSP会话数:" + publisher_wrapper_.GetRtspSessionNumbers();
    }
}

RTMP推送、停止推送:

public void btn_start_rtmp_pusher_Click()
{
    if (publisher_wrapper_.IsPushingRtmp())
    {
        StopPushRTMP();
        btn_rtmp_pusher_.GetComponentInChildren<Text>().text = "推送RTMP";

        return;
    }

    String url = rtmp_pusher_url_.text;

    if (url.Length < 8)
    {
        publisher_wrapper_.Close();

        Debug.LogError("请输入RTMP推送地址");
        return;
    }

    if (!publisher_wrapper_.IsPreviewing() && !publisher_wrapper_.IsRtspPublisherRunning())
    {
        publisher_wrapper_.SetVideoPushType(video_push_type_);
        publisher_wrapper_.SetAudioPushType(audio_push_type_);
    }

    if (!publisher_wrapper_.StartRtmpPusher(url))
    {
        Debug.LogError("调用StartPublisher失败..");
        return;
    }

    btn_rtmp_pusher_.GetComponentInChildren<Text>().text = "停止推送";

    if (!publisher_wrapper_.IsPreviewing() && !publisher_wrapper_.IsRtspPublisherRunning())
    {
        StartCaptureAvData();
        coroutine_ = StartCoroutine(OnPostVideo());
    }
}

总结

轻量级RTSP服务和RTMP推送的区别在于,轻量级RTSP服务不需要单独部署流媒体服务器(类似于网络摄像头),在内网小并发场景下,使用起来非常方便,如果需要上公网,还是需要用RTMP推送,感兴趣的开发者可酌情参考。


http://www.niftyadmin.cn/n/107859.html

相关文章

Android 9.0系统源码_通知服务(二)系统状态栏是如何监听通知服务的各种通知事件的

前言 上一篇文章简单讲述了系统通知服务NotificationManagerService的启动流程&#xff0c;本篇文章我们将会具体梳理一下SystemUI组件系统状态栏StatusBar是如何监听通知服务的各种通知事件的。 一、 1、我们在Android 9.0系统源码_SystemUI&#xff08;一&#xff09;Syst…

移动端异构运算技术 - GPU OpenCL 编程(基础篇)

一、前言 随着移动端芯片性能的不断提升&#xff0c;在移动端上实时进行计算机图形学、深度学习模型推理等计算密集型任务不再是一个奢望。在移动端设备上&#xff0c;GPU 凭借其优秀的浮点运算性能&#xff0c;以及良好的 API 兼容性&#xff0c;成为移动端异构计算中非常重要…

第14天-ElasticSearch环境配置,构建检索服务及商品上架到ES库

1.ElasticSearch概念 官网介绍&#xff1a;https://www.elastic.co/cn/what-is/elasticsearch/ 官网学习文档&#xff1a;https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html 1.1.ElasticSearch与MySQL的比较 MySQL有事务性,而ElasticSearch没有…

【Python实战】爬虫教程千千万,一到实战全完蛋?今天手把手教你一键采集某网站图书信息数据啦~排名第一的竟是...(爬虫+数据可视化)

前言 ​一本本书&#xff0c;是一扇扇窗&#xff0c;为追求知识的人打开认知世界的窗口 一本本书&#xff0c;是一双双翅膀&#xff0c;让追求理想的人张开翅膀翱翔 所有文章完整的素材源码都在&#x1f447;&#x1f447; 粉丝白嫖源码福利&#xff0c;请移步至CSDN社区或文末…

熵,线性规划,半监督自监督聚类打标签

1.熵 信息熵是消除不确定性所需信息量的度量。 信息熵就是信息的不确定程度&#xff0c;信息熵越小&#xff0c;信息越确定。 对象的信息熵是正比于它的概率的负对数的&#xff0c;也就是 I©−log(pc) 其中n为事件的所有可能性。 为什么使用交叉熵&#xff1f;在机器学习…

创建SpringBoot注意事项

作为一个java小白&#xff0c;你是否因为创建SpringBoot项目那些莫名其妙的错误搞得头皮发麻。不要慌张&#xff0c;这篇文章能帮你解决90%的问题【持续更新…】 本文结合创建SpringBoot项目的完整过程来讲 在idea中新建项目 虽然SpringBoot项目是由maven内核组成的&#xff0…

Maven的生命周期及常用命令

文章目录1、Maven生命周期1.1、clean生命周期1.2、default生命周期1.3、site生命周期2、Maven常用命令1、Maven生命周期 Maven有三套生命周期系统&#xff1a; 1、clean生命周期 2、default生命周期 3、site生命周期 三套生命周期之间相互独立&#xff0c;每套生命周期包含一…

GEBCO海洋数据下载

一、数据集简介 GEBCO&#xff08;General Bathymetric chart of the Oceans&#xff09;旨在为世界海洋提供最权威的、可公开获取的测深数据集。 目前的网格化测深数据集&#xff0c;即GEBCO_2022网格&#xff0c;是一个全球海洋和陆地的地形模型&#xff0c;在15角秒间隔的…