Android平台实现Unity3D下RTMP推送

像Unity3D下的RTMP或RTSP播放器一样,好多开发者苦于在Unity环境下,如何高效率低延迟的把数据采集并编码实时推送到流媒体服务器,实现Unity场景下的低延迟推拉流方案。

关于屏幕采集,有两种方案:

1. 直接封装Android原生的屏幕采集工程,在unity提供接口,拿到屏幕权限后,获取屏幕数据并推送;

具体可以参考 https://blog.csdn.net/renhui1112/article/details/106750891

2. 如果只需要拿到Unity的窗体或摄像机数据推出去,可在Unity下获取到需要推送的原始数据,然后封装原生的RTMP推流接口,调用原生SDK实现数据推送,这种做法的好处是,可以自定义需要采集的数据内容,只要按照原生SDK提供的接口,完成数据对接即可,具体实现参看本文。

Android平台Unity3D的RTSP或RTMP播放器,可以参考

https://blog.csdn.net/renhui1112/article/details/104154788

本文以Android平台为例,介绍下Unity环境下的Android平台RTMP推流,数据采集在Unity完成,数据编码推送,调用大牛直播SDK(官方)Android平台RTMP直播推送SDK原生库对外二次封装的接口,高效率的实现RTMP推送。废话多说,先上图看效果。

下图系Android平台Unity环境下采集屏幕,编码推送到RTMP服务器,然后Windows平台播放器拉取RTMP流播放,为了方便看到延迟效果,特地在Android端的Unity窗口显示了当前时间,可以看到,整体延迟在毫秒级:

数据采集推送

unity数据采集相对简单,可以很轻松的拿到RGB24的数据:

texture_ = new Texture2D(video_width_, video_height_, TextureFormat.RGB24, false);texture_.ReadPixels(new Rect(0, 0, video_width_, video_height_), 0, 0, false);texture_.Apply();

然后通过调用texture_.GetRawTextureData<sbyte>(); 获取到数据即可。

拿到数据后,调用原生SDK封装的NT_PB_U3D_OnCaptureVideoRGB24PtrData()接口,完成数据投递。

简单调用流程

    private void Start(){game_object_ = this.gameObject.name;AndroidJavaClass android_class = new AndroidJavaClass("com.unity3d.player.UnityPlayer");java_obj_cur_activity_ = android_class.GetStatic<AndroidJavaObject>("currentActivity");pusher_obj_ = new AndroidJavaObject("com.daniulive.smartpublisher.SmartPublisherUnity3d");NT_PB_U3D_Init();//NT_U3D_SetSDKClientKey("", "", 0);btn_encode_mode_.onClick.AddListener(OnEncodeModeBtnClicked);btn_pusher_.onClick.AddListener(OnPusherBtnClicked);btn_mute_.onClick.AddListener(OnMuteBtnClicked);}

完成接口初始化后,调用Push()接口

    public void Push(){if (is_running){Debug.Log("已推送..");   return;}if (texture_ != null){UnityEngine.Object.Destroy(texture_);texture_ = null;}video_width_ = Screen.width;video_height_ = Screen.height;scale_width_ = (video_width_ + 1) / 2;scale_height_ = (video_height_ + 1) / 2;if (scale_width_ % 2 != 0){scale_width_ = scale_width_ + 1;}if (scale_height_ % 2 != 0){scale_height_ = scale_height_ + 1;}texture_ = new Texture2D(video_width_, video_height_, TextureFormat.RGB24, false);//获取输入框的urlstring url = input_url_.text.Trim();if (!url.StartsWith("rtmp://")){push_url_ = "rtmp://192.168.0.199:1935/hls/stream1";}else{push_url_ = url;}OpenPusher();if (pusher_handle_ == 0)return;NT_PB_U3D_Set_Game_Object(pusher_handle_, game_object_);/* ++ 推送前参数配置可加在此处 ++ */InitAndSetConfig();NT_PB_U3D_SetPushUrl(pusher_handle_, push_url_);/* -- 推送前参数配置可加在此处 -- */int flag = NT_PB_U3D_StartPublisher(pusher_handle_);if (flag  == DANIULIVE_RETURN_OK){Debug.Log("推送成功..");}else{Debug.LogError("推送失败..");}is_running = true;}

调用OpenPusher()

    private void OpenPusher(){if ( java_obj_cur_activity_ == null ){Debug.LogError("getApplicationContext is null");return;}int audio_opt = 1;int video_opt = 1;pusher_handle_ = NT_PB_U3D_Open(audio_opt, video_opt, video_width_, video_height_);if (pusher_handle_ != 0)Debug.Log("NT_PB_U3D_Open success");elseDebug.LogError("NT_PB_U3D_Open fail");}

InitAndSetConfig()

    private void InitAndSetConfig(){if (is_hw_encode_){int h264HWKbps = setHardwareEncoderKbps(true, video_width_, video_height_);Debug.Log("h264HWKbps: " + h264HWKbps);int isSupportH264HWEncoder = NT_PB_U3D_SetVideoHWEncoder(pusher_handle_, h264HWKbps);if (isSupportH264HWEncoder == 0){Debug.Log("Great, it supports h.264 hardware encoder!");}}else {if (is_sw_vbr_mode_) //H.264 software encoder{int is_enable_vbr = 1;int video_quality = CalVideoQuality(video_width_, video_height_, true);int vbr_max_bitrate = CalVbrMaxKBitRate(video_width_, video_height_);NT_PB_U3D_SetSwVBRMode(pusher_handle_, is_enable_vbr, video_quality, vbr_max_bitrate);//NT_PB_U3D_SetSWVideoEncoderSpeed(pusher_handle_, 2);}}NT_PB_U3D_SetAudioCodecType(pusher_handle_, 1);NT_PB_U3D_SetFPS(pusher_handle_, 25);NT_PB_U3D_SetGopInterval(pusher_handle_, 25*2);//NT_PB_U3D_SetSWVideoBitRate(pusher_handle_, 600, 1200);}

ClosePusher()

    private void ClosePusher(){if (texture_ != null){UnityEngine.Object.Destroy(texture_);texture_ = null;}int flag = NT_PB_U3D_StopPublisher(pusher_handle_);if (flag == DANIULIVE_RETURN_OK){Debug.Log("停止成功..");}else{Debug.LogError("停止失败..");}flag = NT_PB_U3D_Close(pusher_handle_);if (flag == DANIULIVE_RETURN_OK){Debug.Log("关闭成功..");}else{Debug.LogError("关闭失败..");}pusher_handle_ = 0;NT_PB_U3D_UnInit();is_running = false;}

为了便于测试,Update()刷新下当前时间:

    private void Update(){//获取当前时间hour = DateTime.Now.Hour;minute = DateTime.Now.Minute;millisecond = DateTime.Now.Millisecond;second = DateTime.Now.Second;year = DateTime.Now.Year;month = DateTime.Now.Month;day = DateTime.Now.Day;GameObject.Find("Canvas/Panel/LableText").GetComponent<Text>().text = string.Format("{0:D2}:{1:D2}:{2:D2}:{3:D2} " + "{4:D4}/{5:D2}/{6:D2}", hour, minute, second, millisecond, year, month, day);}

相关Event处理

 public void onNTSmartEvent(string param){if (!param.Contains(",")){Debug.Log("[onNTSmartEvent] android传递参数错误");return;}string[] strs = param.Split(',');string player_handle =strs[0];string code = strs[1];string param1 = strs[2];string param2 = strs[3];string param3 = strs[4];string param4 = strs[5];Debug.Log("[onNTSmartEvent] code: 0x" + Convert.ToString(Convert.ToInt32(code), 16));String publisher_event = "";switch (Convert.ToInt32(code)){case EVENTID.EVENT_DANIULIVE_ERC_PUBLISHER_STARTED:publisher_event = "开始..";break;case EVENTID.EVENT_DANIULIVE_ERC_PUBLISHER_CONNECTING:publisher_event = "连接中..";break;case EVENTID.EVENT_DANIULIVE_ERC_PUBLISHER_CONNECTION_FAILED:publisher_event = "连接失败..";break;case EVENTID.EVENT_DANIULIVE_ERC_PUBLISHER_CONNECTED:publisher_event = "连接成功..";break;case EVENTID.EVENT_DANIULIVE_ERC_PUBLISHER_DISCONNECTED:publisher_event = "连接断开..";break;case EVENTID.EVENT_DANIULIVE_ERC_PUBLISHER_STOP:publisher_event = "关闭..";break;case EVENTID.EVENT_DANIULIVE_ERC_PUBLISHER_RECORDER_START_NEW_FILE:publisher_event = "开始一个新的录像文件 : " + param3;break;case EVENTID.EVENT_DANIULIVE_ERC_PUBLISHER_ONE_RECORDER_FILE_FINISHED:publisher_event = "已生成一个录像文件 : " + param3;break;case EVENTID.EVENT_DANIULIVE_ERC_PUBLISHER_SEND_DELAY:publisher_event = "发送时延: " + param1 + " 帧数:" + param2;break;case EVENTID.EVENT_DANIULIVE_ERC_PUBLISHER_CAPTURE_IMAGE:publisher_event = "快照: " + param1 + " 路径:" + param3;if (Convert.ToInt32(param1) == 0){publisher_event = publisher_event + "截取快照成功..";}else{publisher_event = publisher_event + "截取快照失败..";}break;case EVENTID.EVENT_DANIULIVE_ERC_PUBLISHER_RTSP_URL:publisher_event = "RTSP服务URL: " + param3;break;case EVENTID.EVENT_DANIULIVE_ERC_PUSH_RTSP_SERVER_RESPONSE_STATUS_CODE:publisher_event = "RTSP status code received, codeID: " + param1 + ", RTSP URL: " + param3;break;case EVENTID.EVENT_DANIULIVE_ERC_PUSH_RTSP_SERVER_NOT_SUPPORT:publisher_event = "服务器不支持RTSP推送, 推送的RTSP URL: " + param3;break;}Debug.Log(publisher_event);}

总结

通过以上流程,可以实现Unity环境下屏幕或摄像机数据,毫秒级体验的RTMP推送和播放,感兴趣的开发者可酌情参考。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/553046.shtml

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

相关文章

Windows平台实现Unity下窗体|摄像头|屏幕采集推送

技术背景 随着Unity3D的应用范围越来越广&#xff0c;越来越多的行业开始基于Unity3D开发产品&#xff0c;如传统行业中虚拟仿真教育、航空工业、室内设计、城市规划、工业仿真等领域。 基于此&#xff0c;好多开发者苦于在Unity环境下&#xff0c;没有低延迟的推拉流解决方案…

钉钉调岗申请单怎么写 钉钉申请调岗的教程

钉钉中想要申请调岗申请单&#xff0c;调岗申请单在哪&#xff0c;该怎么填写呢?下面我们就来看看详细的教程。 1、首先打开钉钉客户端&#xff0c;点击下方的工作。 钉钉调岗申请单怎么写? 钉钉申请调岗的教程 2、向下滑动找到并打开调岗申请单应用。 钉钉调岗申请单怎么…

Android平台RTMP推送端实现外部数据对接推送和录像

背景 好多开发者在做Android平台RTMP推送对接的同时&#xff0c;除了编码前的数据外&#xff0c;还有些外部编码数据推送诉求&#xff0c;他们希望外部的编码音视频数据不止可以实现RTMP推送&#xff0c;还可以同时在推送端实时录制下来&#xff0c;本文以我们&#xff08;官方…

excel怎么删除浏览记录 方法介绍

Excel 2013工作表删除浏览记录的步骤&#xff1a; 1、鼠标左键双击计算机桌面Excel2013程序图标&#xff0c;将其打开运行。在打开的Excel2013程序窗口&#xff0c;点击“空白工作薄”选项&#xff0c;新建一个空白Excel工作薄。如图所示; excel怎么删除浏览记录&#xff1f;…

爱奇艺怎么开启数据流量自动播放

1、打开手机&#xff0c;点击爱奇艺 爱奇艺怎么开启数据流量自动播放 2、进入爱奇艺&#xff0c;点击我的 爱奇艺怎么开启数据流量自动播放 3、然后在我的界面&#xff0c;点击设置 爱奇艺怎么开启数据流量自动播放 4、进入设置界面&#xff0c;点击播放与下载 爱奇艺怎…

Win7系统账户被禁用的解决方法

我们都知道Win7系统中可以建立多个账户来进行不同的登录使用&#xff0c;十分的方便&#xff0c;但是有的用户建立了多个账户&#xff0c;却发现所有的账号都被禁用&#xff0c;那遇到这个问题应该怎么解决呢&#xff0c;下面就为小伙伴们带来 了Win7系统账户被禁用的解决方法&…

Win11系统如何隐藏快速搜索

近日网络中已经出现了很多Windows 11的泄露版本&#xff0c;意味着Win11版本正式确定&#xff0c;现在只是等待微软正式发布了。Windows系统中菜单栏都会默认放一个快速搜索的图标&#xff0c;快速搜索一直是Windows系统中的一个重要功能&#xff0c;那么在Win11系统中该如何隐…

网易邮箱广告标签怎么关 163邮箱去广告标签的教程

1、打开百度输入网易邮箱 网易邮箱广告标签怎么关? 163邮箱去广告标签的教程 网易邮箱广告标签怎么关? 163邮箱去广告标签的教程 2、输入账号和密码&#xff0c;进入邮箱主页&#xff0c;在标签栏看到广告标签 网易邮箱广告标签怎么关? 163邮箱去广告标签的教程 3、选中…

Linux|麒麟操作系统实现多路RTMP|RTSP播放

技术背景 无论是Windows平台还是Linux&#xff0c;多路播放诉求非常普遍&#xff0c;比如针对智慧工地、展馆、教育等宏观场景下的摄像头展示&#xff0c;关于RTSP或RTMP直播播放器开发需要注意的点&#xff0c;可参考之前博客&#xff0c;总的来说有以下一些点&#xff1a; …

TIM提示“个人文件夹被占用,请稍候再登录”怎么解决

在登录TIM的时候&#xff0c;弹出提示“个人文件夹被占用&#xff0c;请稍后再登录”的提示&#xff0c;无法正常登录TIM TIM提示“个人文件夹被占用&#xff0c;请稍候再登录”怎么解决&#xff1f; 这个时候&#xff0c;我们可以直接关闭上面的登录窗口&#xff0c;然后过一…

数据推送选择GB28181、RTSP还是RTMP?

GB/T28181 国标GB/T28181协议全称《安全防范视频监控联网系统信息传输、交换、控制技术要求》&#xff0c;是一个定义视频联网传输和设备控制标准的白皮书&#xff0c;由公安部科技信息化局提出&#xff0c;该标准规定了城市监控报警联网系统中信息传输、交换、控制的互联结构…

Windows平台RTMP推送摄像头对接介绍

背景 好多开发者在对接大牛直播SDK&#xff08;官方&#xff09;的Windows平台RTMP推送时&#xff0c;不熟悉摄像头调用&#xff0c;实际上&#xff0c;摄像头调用逻辑并不复杂&#xff0c;以下是大概流程&#xff1a; 首先调用我们sdk接口获取摄像头个数&#xff0c;调用接口…

Outlook2016未读邮件怎么设置字体颜色

Outlook2016中想要设置未读邮件的字体颜色&#xff0c;该怎么设置呢?下面我们就来看看详细的教程。 Outlook2016未读邮件怎么设置字体颜色? 1、下载安装outlook软件。 Outlook2016未读邮件怎么设置字体颜色? 2、双击打开outlook软件&#xff0c;登入邮箱账户。 Outlook…

Android平台RTMP推送模块如何对接NV21、YV12、RGB、YUV等编码前数据

前言 我们在对接Android平台摄像头或者屏幕采集、编码打包推送场景的时候&#xff0c;随着采集设备的不同&#xff0c;出来的数据也是多样化的&#xff0c;比如NV21、YV12、RGB、YUV等&#xff0c;更有图像数据甚至是翻转或者倒置的&#xff0c;如果上层处理&#xff0c;效率低…

庖丁解牛之-Android平台RTSP|RTMP播放器设计

背景 我们在做Android平台RTSP或者RTMP播放器开发的时候&#xff0c;需要注意的点非常多&#xff0c;以下&#xff0c;以大牛直播SDK(官方)的接口为例&#xff0c;大概介绍下相关接口设计&#xff1a; 接口设计 1. Open() 接口 Open接口的目的&#xff0c;主要是创建实例&a…

如何在Android端实现轻量级RTSP服务(类似于IPC)

为什么要设计轻量级RTSP服务 首先声明一点&#xff0c;本blog提到的轻量级RTSP服务&#xff0c;类似于网络摄像头&#xff08;IPC&#xff09;&#xff0c;而非传统意义的接受外部推流的RTSP服务器。 轻量级RTSP服务解决的核心痛点&#xff1a;避免用户单独部署RTSP或者RTMP服…

Android平台基于RTMP或RTSP的一对一音视频互动技术方案探讨

背景 随着智能门禁等物联网产品的普及&#xff0c;越来越多的开发者对音视频互动体验提出了更高的要求。目前市面上大多一对一互动都是基于WebRTC&#xff0c;优点不再赘述&#xff0c;我们这里先说说可能需要面临的问题&#xff1a;WebRTC的服务器部署非常复杂&#xff0c;可…

微信公众号怎么发送模板消息 微信公众平台模板消息免费发送的技巧

想要发免费的模板消息&#xff0c;该怎么发布?下面我们就来看看详细的教程。 1、首先我们需要在微信公众号里面开通模板消息功能&#xff0c;没有开通的需要去申请&#xff0c;不然就用不了&#xff0c;如下图所示。 微信公众号怎么发送模板消息?微信公众平台模板消息免费发…

如何理解面向过程和面向对象?

一句话理解面向对象 有人说&#xff1a;“如果上帝是程序员&#xff0c;他怎么创造世界上的所有动物。”&#xff0c;理解这个问题就理解了面向对像。 面向过程和面向对象区别&#xff1f; 面向过程的思路&#xff1a;什么事都自己做&#xff1b;分析解决问题所需的步骤&…

如何实现Android平台GB28181前端设备接入

技术背景 在实现Android平台GB28181前端设备接入之前&#xff0c;我们几年前就有了非常成熟的RTMP推送、RTSP推送和轻量级RTSP服务等模块&#xff0c;特别是RTMP推送&#xff0c;行业内应用非常广泛&#xff0c;好多开发者可能会问&#xff0c;既然有了以上模块&#xff0c;干…