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

news2024/11/13 10:21:23

技术背景

随着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.coloradmin.cn/o/378090.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

华为OD机试题,用 Java 解【内存资源分配】问题

最近更新的博客 华为OD机试题,用 Java 解【停车场车辆统计】问题华为OD机试题,用 Java 解【字符串变换最小字符串】问题华为OD机试题,用 Java 解【计算最大乘积】问题华为OD机试题,用 Java 解【DNA 序列】问题华为OD机试 - 组成最大数(Java) | 机试题算法思路 【2023】使…

蓝蓝算法二期工程day3,一万年太久,只争朝夕

思路&#xff1a; 最好想的是用hashmap&#xff0c;当然用c的话也可以用两个数组&#xff0c;一个数组用于存放字符串&#xff0c;自动对应ACSII码&#xff0c;一个将对应ACSII码的数字对应其下标&#xff0c;当然这也是用的映射的思想。 import java.util.*;public class Cac…

【蓝桥杯选拔赛真题38】python目标值判断 青少年组蓝桥杯python 选拔赛STEMA比赛真题解析

目录 python目标值判断 一、题目要求 1、编程实现 2、输入输出 二、解题思路

47个SQL性能优化技巧,看到就是赚到

1、先了解MySQL的执行过程 了解了MySQL的执行过程&#xff0c;我们才知道如何进行sql优化。 &#xff08;1&#xff09;客户端发送一条查询语句到服务器&#xff1b; &#xff08;2&#xff09;服务器先查询缓存&#xff0c;如果命中缓存&#xff0c;则立即返回存储在缓存中的…

Linux 内核 container_of 宏详解

目录 前言 1、container_of 宏介绍 2、container_of 宏的使用示例 3、container_of 宏实现原理分析 3.1 结构体在内存中的存储 3.2 计算成员变量在结构体内的偏移 3.3 container_of 宏的原理实现 4、总结 前言 本章内容会涉及到的基础知识有 typeof关键字 和 语句表达…

django-博客(一)

一、 1、环境&#xff1a;pycharm&#xff0c;python3.6&#xff0c;django3&#xff0c;mysql8.0 2、创建项目 3、把html和css样式那些导入到文件夹中&#xff0c;​​​​​​然后配置这些文件夹的路径&#xff0c;再添加首页视图。 改成反向解析 python manage.py runserv…

ElasticSearch 学习笔记总结(三)

文章目录一、ES 相关名词 专业介绍二、ES 系统架构三、ES 创建分片副本 和 elasticsearch-head插件四、ES 故障转移五、ES 应对故障六、ES 路由计算 和 分片控制七、ES集群 数据写流程八、ES集群 数据读流程九、ES集群 更新流程 和 批量操作十、ES 相关重要 概念 和 名词十一、…

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

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

分析设备故障时间和次数,打破生产瓶颈?包在虹科身上

前言 生产设备的稳定性和可靠性是保证企业正常生产的重要条件之一&#xff0c;设备故障的频发严重影响企业的正常生产&#xff0c;那么如何分析设备故障时间和次数&#xff0c;查找设备故障原因&#xff0c;协助企业打破生产瓶颈&#xff0c;有效地实现生产目标呢&#xff1f;…

面试总结——react生命周期

react生命周期总结 生命周期主要分为以下几个阶段&#xff1a; Mounting:创建虚拟DOM&#xff0c;渲染UI(初始化)Updating&#xff1a;更新虚拟DOM&#xff0c;重新渲染UI&#xff1b;(更新)UnMounting&#xff1a;删除虚拟DOM&#xff0c;移除UI&#xff1b;(销毁) 生命周期…

docker-compose安装kafka和php简单测试

docker-compose.yml内容&#xff1a; version: 3.1 services: zookeeper: container_name: zookeeper image: zookeeper:3.6 ports: - 2181:2181 kafka: image: wurstmeister/kafka container_name: kafka depends_on: - zookeeper …

java基础系列(八) synchronized关键字

一. 认识synchronized 先看一下如下Demo public class Test {public static void main(String[] args) {Count obj new Count();//only one objectMyThread1 t1 new MyThread1(obj);MyThread2 t2 new MyThread2(obj);t1.start();t2.start();} }class MyThread2 extends Th…

面试题1-JAVA的执行流程

JAVA的执行流程 分两步,1.翻译 2执行 Java属于两种类型: 编译型和解释型 编译型 ​ 概念: 把源代码翻译成.class文件 >意思是: 把程序员的源代码翻译成虚拟电脑能看得懂的代码 解释型 ​ 概念: 把.class文件进行解析执行 >把翻译后的代码交给虚拟电脑执行指令 执行流程…

揭开Salesforce Accredited Professional证书神秘面纱,到底含金量有多高?

自从Salesforce宣布Accredited Professional计划以来&#xff0c;已经过去了将近两年。这些认证旨在证明备考者在Salesforce平台特定领域的广泛知识&#xff0c;并且仅供Salesforce合作伙伴使用。Accredited Professional中有近40项Salesforce认证&#xff0c;涵盖平台、销售、…

学校机房4大安全隐患,赶紧自查

无论是企业、医院、政府机构还是学校&#xff0c;都有机房。传统机房出现事故时无法及时发现和处理&#xff0c;导致影响范围大&#xff0c;损失严重。 随着信息技术的不断发展&#xff0c;各行业信息化程度不断提升&#xff0c;配套的数据中心机房日益增多&#xff0c;为业务信…

LeetCode 147. 对链表进行插入排序 | C/C++版

LeetCode 147. 对链表进行插入排序 | C语言版LeetCode 147. 对链表进行插入排序题目描述解题思路思路一&#xff1a;使用栈代码实现运行结果参考文章&#xff1a;思路二&#xff1a;减少遍历节点数代码实现运行结果参考文章&#xff1a;[]()LeetCode 147. 对链表进行插入排序 …

Sharding Sphere学习

一、基本概念 1.什么是Sharding Sphere 2.分库分表3.分库分表的方式 4.分库分表应用和问题 5.功能 5.1数据分片 —核心概念 —使用限制 5.2分布式事务 —核心概念 —使用限制 5.3读写分离 —核心概念 —使用限制 5.4高可用 —核心概念 —使用限制 5.5数据库网关 —核心概念…

shield分析

本文仅供学习交流&#xff0c;只提供关键思路不会给出完整代码&#xff0c;严禁用于非法用途&#xff0c;若有侵权请联系我删除&#xff01;技术交流合作请私信&#xff01; 熟练打开Fiddler设置好手机代理&#xff0c;摆弄半天一直抓不到包&#xff0c;应该是小红书监测到了F…

对 Dom 树的理解

什么是 DOM 从网络传给渲染引擎的 HTML 文件字节流是无法直接被渲染引擎理解的&#xff0c;所以要将其转化为渲染引擎能够理解的内部结构&#xff0c;这个结构就是 DOM。 DOM 提供了对 HTML 文档结构化的表述。 在渲染引擎中&#xff0c;DOM 有三个层面的作用&#xff1a; …

攻略 | 6步帮助中小微企业开拓东盟机电产品市场

如何帮助中小微外贸企业在东盟市场拓展机电产品一般贸易&#xff1f;随着全球化的发展&#xff0c;越来越多的中小微外贸企业开始涉足国际贸易。对于机电产品行业而言&#xff0c;东盟市场是一个非常重要的出口目的地。本文将为您介绍如何帮助中小微外贸企业在东盟市场拓展机电…