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

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

技术背景

随着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,整体设计方案如下:

1cc44f3c31eba835635e8b2568ad47bd.png

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


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

ce8e02caaf4397c353508b7f70841d34.png

首先看启动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推送,感兴趣的开发者可酌情参考。

目录
打赏
0
0
0
0
70
分享
相关文章
Unity调用Windows弹出确认框
在 Unity 中调用 Windows 弹出确认框,可通过 Windows API 或 .NET 框架实现。使用 Windows API 的方式是通过 P/Invoke 技术调用 MessageBox 函数,创建模态对话框。代码示例展示了如何在应用退出时弹出确认框,用户选择“确定”则退出游戏。此方法也适用于 ALT+F4 触发的退出确认。
2025年1月9日更新Windows操作系统个人使用-禁用掉一下一些不必要的服务-关闭占用资源的进程-禁用服务提升系统运行速度-让电脑不再卡顿-优雅草央千澈-长期更新
2025年1月9日更新Windows操作系统个人使用-禁用掉一下一些不必要的服务-关闭占用资源的进程-禁用服务提升系统运行速度-让电脑不再卡顿-优雅草央千澈-长期更新
244 2
2025年1月9日更新Windows操作系统个人使用-禁用掉一下一些不必要的服务-关闭占用资源的进程-禁用服务提升系统运行速度-让电脑不再卡顿-优雅草央千澈-长期更新
Windows server 2012R2系统安装远程桌面服务后无法多用户同时登录是什么原因?
【11月更文挑战第15天】本文介绍了在Windows Server 2012 R2中遇到的多用户无法同时登录远程桌面的问题及其解决方法,包括许可模式限制、组策略配置问题、远程桌面服务配置错误以及网络和防火墙问题四个方面的原因分析及对应的解决方案。
330 4
|
8月前
|
超实用!深度解析Unity引擎,手把手教你从零开始构建精美的2D平面冒险游戏,涵盖资源导入、角色控制与动画、碰撞检测等核心技巧,打造沉浸式游戏体验完全指南
【8月更文挑战第31天】本文是 Unity 2D 游戏开发的全面指南,手把手教你从零开始构建精美的平面冒险游戏。首先,通过 Unity Hub 创建 2D 项目并导入游戏资源。接着,编写 `PlayerController` 脚本来实现角色移动,并添加动画以增强视觉效果。最后,通过 Collider 2D 组件实现碰撞检测等游戏机制。每一步均展示 Unity 在 2D 游戏开发中的强大功能。
441 6
掌握这五大绝招,让您的Unity游戏瞬间加载完毕,从此告别漫长等待,大幅提升玩家首次体验的满意度与留存率!
【8月更文挑战第31天】游戏的加载时间是影响玩家初次体验的关键因素,特别是在移动设备上。本文介绍了几种常见的Unity游戏加载优化方法,包括资源的预加载与异步加载、使用AssetBundles管理动态资源、纹理和模型优化、合理利用缓存系统以及脚本优化。通过具体示例代码展示了如何实现异步加载场景,并提出了针对不同资源的优化策略。综合运用这些技术可以显著缩短加载时间,提升玩家满意度。
780 5
掌握Unity调试与测试的终极指南:从内置调试工具到自动化测试框架,全方位保障游戏品质不踩坑,打造流畅游戏体验的必备技能大揭秘!
【9月更文挑战第1天】在开发游戏时,Unity 引擎让创意变为现实。但软件开发中难免遇到 Bug,若不解决,将严重影响用户体验。调试与测试成为确保游戏质量的最后一道防线。本文介绍如何利用 Unity 的调试工具高效排查问题,并通过 Profiler 分析性能瓶颈。此外,Unity Test Framework 支持自动化测试,提高开发效率。结合单元测试与集成测试,确保游戏逻辑正确无误。对于在线游戏,还需进行压力测试以验证服务器稳定性。总之,调试与测试贯穿游戏开发全流程,确保最终作品既好玩又稳定。
420 4
【独家揭秘】那些让你的游戏瞬间鲜活起来的Unity UI动画技巧:从零开始打造动态按钮,提升玩家交互体验的绝招大公开!
【9月更文挑战第1天】在游戏开发领域,Unity 是最受欢迎的游戏引擎之一,其强大的跨平台发布能力和丰富的功能集让开发者能够迅速打造出高质量的游戏。优秀的 UI 设计对于游戏至关重要,尤其是在手游市场,出色的 UI 能给玩家留下深刻的第一印象。Unity 的 UGUI 系统提供了一整套解决方案,包括 Canvas、Image 和 Button 等组件,支持添加各种动画效果。
409 3
深度解析Unity游戏开发:从零构建可扩展与可维护的游戏架构,让你的游戏项目在模块化设计、脚本对象运用及状态模式处理中焕发新生,实现高效迭代与团队协作的完美平衡之路
【9月更文挑战第1天】游戏开发中的架构设计是项目成功的关键。良好的架构能提升开发效率并确保项目的长期可维护性和可扩展性。在使用Unity引擎时,合理的架构尤为重要。本文探讨了如何在Unity中实现可扩展且易维护的游戏架构,包括模块化设计、使用脚本对象管理数据、应用设计模式(如状态模式)及采用MVC/MVVM架构模式。通过这些方法,可以显著提高开发效率和游戏质量。例如,模块化设计将游戏拆分为独立模块。
395 3
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等