企业网站建设的困难和问题,太原搜索引擎优化招聘信息,如何跟客户沟通网站建设,潍坊作风建设年网站随着VR类、工业仿真、智慧城市等场景的快速发展#xff0c;开发者对Unity3d低延迟的直播需求量越来越大#xff0c;前两年#xff0c;大牛直播SDK发布了Windows平台、Android平台和iOS平台的Unity3d RTMP和RTSP的播放#xff0c;好多公司用起来体验都非常好#xff0c;以下…随着VR类、工业仿真、智慧城市等场景的快速发展开发者对Unity3d低延迟的直播需求量越来越大前两年大牛直播SDK发布了Windows平台、Android平台和iOS平台的Unity3d RTMP和RTSP的播放好多公司用起来体验都非常好以下介绍大概实现流程。
本文以Android平台为例我们的实现基于大牛直播SDK现有非常成熟的native RTMP和RTSP播放模块回调解码后的原始数据传递给Unity3d实现相应的绘制即可对应demo可以参考 Github。
具体步骤如下
1. Native RTSP或RTSP直播播放SDK回调RGB/YUV420/NV12等其中的一种未压缩的图像格式
2. Unity3D创建相应的RGB/YUV420等Shader
3.Unity3D从各个平台获取图像数据来填充纹理即可。
以Android平台为例我们在原有接口基础上做了桥接接口供Unity平台使用 /// summary/// Init/// /summarypublic int NT_U3D_Init(){return DANIULIVE_RETURN_OK;}/// summary/// 开始/// 返回播放句柄/// /summarypublic long NT_U3D_Open(){if ( 0 ! player_obj_.Callint(Init, java_obj_cur_activity_) ){return 0;}return player_obj_.Calllong(Open);}/// summary/// Register Game Object,用于消息传递/// /summarypublic int NT_U3D_Set_Game_Object(long handle, string gameObjectName){return player_obj_.Callint(SetGameObject, handle, gameObjectName);}/// summary/// 设置H.264解码方式 false 软件解码 true 硬件解码 默认为false/// /summary/// param nameisHwDecoder/parampublic int NT_U3D_SetVideoDecoderMode(long handle, int isHwDecoder){return player_obj_.Callint(SetPlayerVideoHWDecoder, handle, isHwDecoder);}/// summary/// 设置H.265 解码方式 false 软件解码 true 硬件解码 默认为false/// /summary/// param nameisHevcHwDecoder/parampublic int NT_U3D_SetVideoHevcDecoderMode(long handle, int isHevcHwDecoder){return player_obj_.Callint(SetPlayerVideoHevcHWDecoder, handle, isHevcHwDecoder);}/// summary/// 设置音频输出模式: if 0: 自动选择; if with 1: audiotrack模式/// /summary/// param nameuse_audiotrack/parampublic int NT_U3D_SetAudioOutputType(long handle, int use_audiotrack){return player_obj_.Callint(SetAudioOutputType, handle, use_audiotrack);}/// summary/// 设置播放端缓存大小, 默认200毫秒/// /summary/// param namebuffer/parampublic int NT_U3D_SetBuffer(long handle, int buffer){return player_obj_.Callint(SetBuffer, handle, buffer);}/// summary/// 接口可实时调用设置是否实时静音1静音; 0: 取消静音/// /summary/// param nameis_mute/parampublic int NT_U3D_SetMute(long handle, int is_mute){return player_obj_.Callint(SetMute, handle, is_mute);}/// summary/// 设置RTSP TCP模式, 1: TCP; 0: UDP/// /summary/// param nameis_using_tcp/parampublic int NT_U3D_SetRTSPTcpMode(long handle, int is_using_tcp){return player_obj_.Callint(SetRTSPTcpMode, handle, is_using_tcp);}/// summary/// 设置RTSP超时时间, timeout单位为秒必须大于0/// /summary/// param nametimeout/parampublic int NT_U3D_SetRTSPTimeout(long handle, int timeout){return player_obj_.Callint(SetRTSPTimeout, handle, timeout);}/// summary/// 设置RTSP TCP/UDP自动切换/// NOTE: 对于RTSP来说有些可能支持rtp over udp方式有些可能支持使用rtp over tcp方式./// 为了方便使用有些场景下可以开启自动尝试切换开关, 打开后如果udp无法播放sdk会自动尝试tcp, 如果tcp方式播放不了,sdk会自动尝试udp./// /summary/// param nametimeout/param/// timeout如果设置1的话, sdk将在tcp和udp之间尝试切换播放如果设置为0则不尝试切换.public int NT_U3D_SetRTSPAutoSwitchTcpUdp(long handle, int is_auto_switch_tcp_udp){return player_obj_.Callint(SetRTSPAutoSwitchTcpUdp, handle, is_auto_switch_tcp_udp);}/// summary/// 设置快速启动该模式/// /summary/// param nameis_fast_startup/parampublic int NT_U3D_SetFastStartup(long handle, int is_fast_startup){return player_obj_.Callint(SetFastStartup, handle, is_fast_startup);}/// summary/// 设置超低延迟模式 false不开启 true开启 默认false/// /summary/// param namemode/parampublic int NT_U3D_SetPlayerLowLatencyMode(long handle, int mode){return player_obj_.Callint(SetPlayerLowLatencyMode, handle, mode);}/// summary/// 设置视频垂直反转/// is_flip 0: 不反转, 1: 反转/// /summary/// param nameis_flip/parampublic int NT_U3D_SetFlipVertical(long handle, int is_flip){return player_obj_.Callint(SetFlipVertical, handle, is_flip);}/// summary/// 设置视频水平反转/// is_flip 0: 不反转, 1: 反转/// /summary/// param nameis_flip/parampublic int NT_U3D_SetFlipHorizontal(long handle, int is_flip){return player_obj_.Callint(SetFlipHorizontal, handle, is_flip);}/// summary/// 设置顺时针旋转, 注意除了0度之外 其他角度都会额外消耗性能/// degress 当前支持 0度90度, 180度, 270度 旋转/// /summary/// param namedegress/parampublic int NT_U3D_SetRotation(long handle, int degress){return player_obj_.Callint(SetRotation, handle, degress);}/// summary/// 设置是否回调下载速度/// is_report: if 1: 上报下载速度, 0: 不上报./// report_interval: 上报间隔以秒为单位0./// /summary/// param nameis_report/param/// param namereport_interval/parampublic int NT_U3D_SetReportDownloadSpeed(long handle, int is_report, int report_interval){return player_obj_.Callint(SetReportDownloadSpeed, handle, is_report, report_interval);}/// summary/// 设置是否需要在播放或录像过程中快照/// /summary/// param nameis_save_image/parampublic int NT_U3D_SetSaveImageFlag(long handle, int is_save_image){return player_obj_.Callint(SetSaveImageFlag, handle, is_save_image);}/// summary/// 播放或录像过程中快照/// /summary/// param nameimageName/parampublic int NT_U3D_SaveCurImage(long handle, string imageName){return player_obj_.Callint(SaveCurImage, handle, imageName);}/// summary/// 播放或录像过程中快速切换url/// /summary/// param nameuri/parampublic int NT_U3D_SwitchPlaybackUrl(long handle, string uri){return player_obj_.Callint(SwitchPlaybackUrl, handle, uri);}/// summary/// 创建录像存储路径/// /summary/// param namepath/parampublic int NT_U3D_CreateFileDirectory(string path){return player_obj_.Callint(CreateFileDirectory, path);}/// summary/// 设置录像存储路径/// /summary/// param namepath/parampublic int NT_U3D_SetRecorderDirectory(long handle, string path){return player_obj_.Callint(SetRecorderDirectory, handle, path);}/// summary/// 设置单个录像文件大小/// /summary/// param namesize/parampublic int NT_U3D_SetRecorderFileMaxSize(long handle, int size){return player_obj_.Callint(SetRecorderFileMaxSize, handle, size);}/// summary/// 设置录像时音频转AAC编码的开关/// aac比较通用sdk增加其他音频编码(比如speex, pcmu, pcma等)转aac的功能./// 注意: 转码会增加性能消耗/// /summary/// param nameis_transcode/param/// is_transcode设置为1的话如果音频编码不是aac则转成aac如果是aac则不做转换. 设置为0的话则不做任何转换. 默认是0.public int NT_U3D_SetRecorderAudioTranscodeAAC(long handle, int is_transcode){return player_obj_.Callint(SetRecorderAudioTranscodeAAC, handle, is_transcode);}/// summary/// 设置播放路径/// /summarypublic int NT_U3D_SetUrl(long handle, string url){return player_obj_.Callint(SetUrl, handle, url);}/// summary/// 开始播放/// /summarypublic int NT_U3D_StartPlay(long handle){return player_obj_.Callint(StartPlay, handle);}/// summary/// 获取YUV数据/// /summarypublic AndroidJavaObject NT_U3D_GetVideoFrame(long handle){return player_obj_.CallAndroidJavaObject(GetVideoFrame, handle);}/// summary/// 停止播放/// /summarypublic int NT_U3D_StopPlay(long handle){return player_obj_.Callint(StopPlay, handle);}/// summary/// 开始录像/// /summarypublic int NT_U3D_StartRecorder(long handle){return player_obj_.Callint(StartRecorder, handle);}/// summary/// 停止录像/// /summarypublic int NT_U3D_StopRecorder(long handle){return player_obj_.Callint(StopRecorder, handle);}/// summary/// 关闭播放/// /summarypublic int NT_U3D_Close(long handle){return player_obj_.Callint(Close, handle);}/// summary/// UnInit Player/// /summarypublic int NT_U3D_UnInit(){return DANIULIVE_RETURN_OK;}Event传递我们在unity3d里面解析相应的字串即可 /// summary/// android 传递过来 code/// /summary/// param namecode/parampublic 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));switch (Convert.ToInt32(code)){case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_STARTED:Debug.Log(开始。。);break;case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_CONNECTING:Debug.Log(连接中。。);break;case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_CONNECTION_FAILED:Debug.Log(连接失败。。);break;case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_CONNECTED:Debug.Log(连接成功。。);break;case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_DISCONNECTED:Debug.Log(连接断开。。);break;case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_STOP:Debug.Log(停止播放。。);break;case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_RESOLUTION_INFO:Debug.Log(分辨率信息: width: Convert.ToInt32(param1) , height: Convert.ToInt32(param2));break;case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_NO_MEDIADATA_RECEIVED:Debug.Log(收不到媒体数据可能是url错误。。);break;case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_SWITCH_URL:Debug.Log(切换播放URL。。);break;case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_CAPTURE_IMAGE:Debug.Log(快照: param1 路径 param3);if (Convert.ToInt32(param1) 0){Debug.Log(截取快照成功。.);}else{Debug.Log(截取快照失败。.);}break;case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_RECORDER_START_NEW_FILE:Debug.Log([record]开始一个新的录像文件 : param3);break;case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_ONE_RECORDER_FILE_FINISHED:Debug.Log([record]已生成一个录像文件 : param3);break;case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_START_BUFFERING:Debug.Log(Start_Buffering);break;case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_BUFFERING:Debug.Log(Buffering: Convert.ToInt32(param1));break;case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_STOP_BUFFERING:Debug.Log(Stop_Buffering);break;case EVENTID.EVENT_DANIULIVE_ERC_PLAYER_DOWNLOAD_SPEED:Debug.Log(download_speed: param1 Byte/s , (Convert.ToInt32(param1) * 8 / 1000) kbps , (Convert.ToInt32(param1) / 1024) KB/s);break;}}
开始播放 public void Play(){if (is_running){Debug.Log(已经在播放。。); return;}//获取输入框的urlstring url input_url_.text.Trim();if (!url.StartsWith(rtmp://) !url.StartsWith(rtsp://)){videoUrl rtmp://202.69.69.180:443/webcast/bshdlive-pc;}else{videoUrl url;}OpenPlayer();if ( player_handle_ 0 )return;NT_U3D_Set_Game_Object(player_handle_, game_object_);NT_U3D_SetUrl(player_handle_, videoUrl);/* 播放前参数配置可加在此处 */int is_using_tcp 0; //TCP/UDP模式设置NT_U3D_SetRTSPTcpMode(player_handle_, is_using_tcp);int is_report 0;int report_interval 1;NT_U3D_SetReportDownloadSpeed(player_handle_, is_report, report_interval); //下载速度回调NT_U3D_SetBuffer(player_handle_, play_buffer_time_); //设置buffer timeNT_U3D_SetPlayerLowLatencyMode(player_handle_, is_low_latency_ ? 1 : 0); //设置是否启用低延迟模式NT_U3D_SetMute(player_handle_, is_mute_ ? 1 : 0); //是否启动播放的时候静音NT_U3D_SetVideoDecoderMode(player_handle_, is_hw_decode_ ? 1 : 0); //设置H.264软硬解模式NT_U3D_SetVideoHevcDecoderMode(player_handle_, is_hw_decode_ ? 1 : 0); //设置H.265软硬解模式int is_fast_startup 1;NT_U3D_SetFastStartup(player_handle_, is_fast_startup); //设置快速启动模式int rtsp_timeout 10;NT_U3D_SetRTSPTimeout(player_handle_, rtsp_timeout); //设置RTSP超时时间int is_auto_switch_tcp_udp 1;NT_U3D_SetRTSPAutoSwitchTcpUdp(player_handle_, is_auto_switch_tcp_udp); //设置TCP/UDP模式自动切换/* -- 播放前参数配置可加在此处 -- */int flag NT_U3D_StartPlay(player_handle_);if (flag DANIULIVE_RETURN_OK){is_need_get_frame_ true;Debug.Log(播放成功);}else{is_need_get_frame_ false;Debug.LogError(播放失败);}is_running true; }
开始播放后native模块回调yuv/rgb数据unity3d模块做相应的绘制处理即可。 private void Update(){if (!is_need_get_frame_)return;if (player_handle_ 0)return;AndroidJavaObject u3d_video_frame_obj NT_U3D_GetVideoFrame(player_handle_);if (u3d_video_frame_obj null)return;VideoFrame converted_video_frame ConvertToVideoFrame(u3d_video_frame_obj);if (converted_video_frame null)return;if ( !is_need_init_texture_){if (converted_video_frame.width_ ! video_width_|| converted_video_frame.height_ ! video_height_|| converted_video_frame.y_stride_ ! y_row_bytes_|| converted_video_frame.u_stride_ ! u_row_bytes_|| converted_video_frame.v_stride_ ! v_row_bytes_){is_need_init_texture_ true;}}if (is_need_init_texture_){if (InitYUVTexture(converted_video_frame)){is_need_init_texture_ false;}}UpdateYUVTexture(converted_video_frame);converted_video_frame null;}
关闭播放 private void ClosePlayer(){is_need_get_frame_ false;is_need_init_texture_ false;int flag NT_U3D_StopPlay(player_handle_);if (flag DANIULIVE_RETURN_OK){Debug.Log(停止成功);}else{Debug.LogError(停止失败);}flag NT_U3D_Close(player_handle_);if (flag DANIULIVE_RETURN_OK){Debug.Log(关闭成功);}else{Debug.LogError(关闭失败);}player_handle_ 0;NT_U3D_UnInit();is_running false;}
以上是大概的实现流程Unity环境下实际测试时延和 native直播模块相差无几都是毫秒级延迟。Windows平台和iOS平台也是如此。