自2017年我们发布跨平台的低延迟Unity下的RTSP|RTMP直播播放器后,Unity下的直播体验有了质的提升,特别是RTMP,从大家认知里面的几秒钟,直接缩减到100-300ms,满足了绝大多数场景下低延迟的技术诉求。今天就Unity下的RTSP|RTMP的低延迟播放,从以下几个维度,抛砖引玉,做个探讨:
Unity下的RTSP|RTMP低延迟播放,业内想到最多的是大牛直播SDK的SmartPlayer,支持 RTMP 和 RTSP 直播流播放,在资源占用、延迟等方面表现较好。以下是原生模块的功能支持,如不单独说明,系Windows、Linux(含x86_64|aarch64)、Android、iOS全平台支持。
以大牛直播SDK的Windows平台Unity下的RTSP|RTMP直播播放模块为例,开始播放实现如下,开始播放之前,可以选择用软解码还是硬解码:
/* SmartPlayerWinMono.cs
* Created by daniusdk.com on 2018/05/10.
* WeChat: xinsheng120
*/
public void StartPlayer(int sel)
{
Debug.Log("StartPlayer++, sel: " + sel);
if (videoctrl[sel].is_playing_)
{
Debug.Log("StartPlayer, already started.. sel: " + sel);
return;
}
lock (videoctrl[sel].frame_lock_)
{
videoctrl[sel].cur_video_frame_ = null;
}
if (!videoctrl[sel].is_recording_)
{
if (!OpenPlayerHandle(sel))
{
Debug.LogError("call OpenPlayerHandle failed, sel:" + sel);
return;
}
}
if (is_enable_hardware_decoder_)
{
NTSmartPlayerSDK.NT_SP_SetH264HardwareDecoder(videoctrl[sel].player_handle_, is_support_h264_hardware_decoder_ ? 1 : 0, 0);
NTSmartPlayerSDK.NT_SP_SetH265HardwareDecoder(videoctrl[sel].player_handle_, is_support_h265_hardware_decoder_ ? 1 : 0, 0);
}
else
{
NTSmartPlayerSDK.NT_SP_SetH264HardwareDecoder(videoctrl[sel].player_handle_, 0, 0);
NTSmartPlayerSDK.NT_SP_SetH265HardwareDecoder(videoctrl[sel].player_handle_, 0, 0);
}
//video frame callback (YUV/RGB)
videoctrl[sel].sdk_video_frame_call_back_ = new VideoControl.SetVideoFrameCallBack(SDKVideoFrameCallBack);
videoctrl[sel].video_frame_call_back_ = new SP_SDKVideoFrameCallBack(NT_SP_SetVideoFrameCallBack);
NTSmartPlayerSDK.NT_SP_SetVideoFrameCallBack(videoctrl[sel].player_handle_, (Int32)NT.NTSmartPlayerDefine.NT_SP_E_VIDEO_FRAME_FORMAT.NT_SP_E_VIDEO_FRAME_FROMAT_I420, window_handle_, videoctrl[sel].video_frame_call_back_);
UInt32 flag = NTSmartPlayerSDK.NT_SP_StartPlay(videoctrl[sel].player_handle_);
if (flag == DANIULIVE_RETURN_OK)
{
videoctrl[sel].is_need_get_frame_ = true;
Debug.Log("NT_SP_StartPlay succeed, sel:" + sel);
}
else
{
videoctrl[sel].is_need_get_frame_ = false;
Debug.LogError("NT_SP_StartPlay failed, sel:" + sel);
}
videoctrl[sel].is_playing_ = true;
}
对应的OpenPlayerHandle()实现如下,OpenPlayerHandle()主要完成播放实例的创建、事件回调设置、基础参数的设置(比如buffer time、RTSP tcp-udp模式设置、rtsp超时时间设置、低延迟模式设置等)。
private bool OpenPlayerHandle(int sel)
{
if (videoctrl[sel].player_handle_ != IntPtr.Zero)
return true;
window_handle_ = IntPtr.Zero;
if (videoctrl[sel].player_handle_ == IntPtr.Zero)
{
videoctrl[sel].player_handle_ = new IntPtr();
UInt32 ret_open = NTSmartPlayerSDK.NT_SP_Open(out videoctrl[sel].player_handle_, window_handle_, 0, IntPtr.Zero);
if (ret_open != 0)
{
videoctrl[sel].player_handle_ = IntPtr.Zero;
Debug.LogError("call NT_SP_Open failed, sel: " + sel);
return false;
}
}
videoctrl[sel].event_call_back_ = new SP_SDKEventCallBack(NT_SP_SDKEventCallBack);
NTSmartPlayerSDK.NT_SP_SetEventCallBack(videoctrl[sel].player_handle_, window_handle_, videoctrl[sel].event_call_back_);
videoctrl[sel].sdk_event_call_back_ = new VideoControl.SetEventCallBack(SDKEventCallBack);
if (IntPtr.Zero == videoctrl[sel].player_handle_)
return false;
/* ++ 播放前参数配置可加在此处 ++ */
int play_buffer_time_ = 100;
NTSmartPlayerSDK.NT_SP_SetBuffer(videoctrl[sel].player_handle_, play_buffer_time_); //设置buffer time
//int is_using_tcp = 1; //TCP模式
//NTSmartPlayerSDK.NT_SP_SetRTSPTcpMode(videoctrl[sel].player_handle_, is_using_tcp);
int timeout = 10;
NTSmartPlayerSDK.NT_SP_SetRtspTimeout(videoctrl[sel].player_handle_, timeout);
int is_auto_switch_tcp_udp = 1;
NTSmartPlayerSDK.NT_SP_SetRtspAutoSwitchTcpUdp(videoctrl[sel].player_handle_, is_auto_switch_tcp_udp);
Boolean is_mute_ = false;
NTSmartPlayerSDK.NT_SP_SetMute(videoctrl[sel].player_handle_, is_mute_ ? 1 : 0); //是否启动播放的时候静音
int is_fast_startup = 1;
NTSmartPlayerSDK.NT_SP_SetFastStartup(videoctrl[sel].player_handle_, is_fast_startup); //设置快速启动模式
Boolean is_low_latency_ = false;
NTSmartPlayerSDK.NT_SP_SetLowLatencyMode(videoctrl[sel].player_handle_, is_low_latency_ ? 1 : 0); //设置是否启用低延迟模式
//设置旋转角度(设置0, 90, 180, 270度有效,其他值无效)
int rotate_degrees = 0;
NTSmartPlayerSDK.NT_SP_SetRotation(videoctrl[sel].player_handle_, rotate_degrees);
int volume = 100;
NTSmartPlayerSDK.NT_SP_SetAudioVolume(videoctrl[sel].player_handle_, volume); //设置播放音量, 范围是[0, 100], 0是静音,100是最大音量, 默认是100
// 设置上传下载报速度
int is_report = 0;
int report_interval = 2;
NTSmartPlayerSDK.NT_SP_SetReportDownloadSpeed(videoctrl[sel].player_handle_, is_report, report_interval);
//设置播放URL
NTSmartPlayerSDK.NT_SP_SetURL(videoctrl[sel].player_handle_, videoctrl[sel].playback_url_);
/* -- 播放前参数配置可加在此处 -- */
return true;
}
停止播放
private void StopPlayer(int sel)
{
Debug.Log("StopPlayer++, sel: " + sel);
videoctrl[sel].is_need_get_frame_ = false;
videoctrl[sel].is_need_init_texture_ = false;
if (videoctrl[sel].player_handle_ == IntPtr.Zero)
{
return;
}
UInt32 flag = NTSmartPlayerSDK.NT_SP_StopPlay(videoctrl[sel].player_handle_);
if (flag == DANIULIVE_RETURN_OK)
{
Debug.Log("call NT_SP_StopPlay succeed, sel: " + sel);
}
else
{
Debug.LogError("call NT_SP_StopPlay failed, sel: " + sel);
}
if (!videoctrl[sel].is_recording_)
{
NTSmartPlayerSDK.NT_SP_Close(videoctrl[sel].player_handle_);
videoctrl[sel].player_handle_ = IntPtr.Zero;
}
videoctrl[sel].is_playing_ = false;
}
如果需要录像,实现如下:
private void StartRecorder(int sel)
{
Debug.Log("StartRecorder++, sel: " + sel);
if (videoctrl[sel].is_recording_)
{
Debug.Log("StartRecorder, already started.. sel: " + sel);
return;
}
if (!videoctrl[sel].is_playing_)
{
if (!OpenPlayerHandle(sel))
{
Debug.LogError("call OpenPlayerHandle failed..");
return;
}
}
bool is_rec_video = true;
bool is_rec_audio = true;
NTSmartPlayerSDK.NT_SP_SetRecorderVideo(videoctrl[sel].player_handle_, is_rec_video ? 1 : 0);
NTSmartPlayerSDK.NT_SP_SetRecorderAudio(videoctrl[sel].player_handle_, is_rec_audio ? 1 : 0);
String rec_dir = "D:\\Rec"; //录像目录可自行指定
String rec_name_file_prefix_= "daniu" + sel.ToString();
UInt32 max_file_size = 200 * 1024; // 单位是KByte, 默认200MB
bool is_append_date = true;
bool is_append_time = true;
bool is_audio_transcode_aac = true;
UInt32 ret = NTSmartPlayerSDK.NT_SP_SetRecorderDirectory(videoctrl[sel].player_handle_, rec_dir);
if (NT.NTBaseCodeDefine.NT_ERC_OK != ret)
{
Debug.LogError("设置录像目录失败,请确保目录存在且是英文目录");
return;
}
NTSmartPlayerSDK.NT_SP_SetRecorderFileMaxSize(videoctrl[sel].player_handle_, max_file_size);
NT_SP_RecorderFileNameRuler rec_name_ruler = new NT_SP_RecorderFileNameRuler();
rec_name_ruler.type_ = 0;
rec_name_ruler.file_name_prefix_ = rec_name_file_prefix_;
rec_name_ruler.append_date_ = is_append_date ? 1 : 0;
rec_name_ruler.append_time_ = is_append_time ? 1 : 0;
NTSmartPlayerSDK.NT_SP_SetRecorderFileNameRuler(videoctrl[sel].player_handle_, ref rec_name_ruler);
NTSmartPlayerSDK.NT_SP_SetRecorderAudioTranscodeAAC(videoctrl[sel].player_handle_, is_audio_transcode_aac ? 1 : 0);
videoctrl[sel].record_call_back_ = new SP_SDKRecorderCallBack(NT_SP_SDKRecorderCallBack);
NTSmartPlayerSDK.NT_SP_SetRecorderCallBack(videoctrl[sel].player_handle_, IntPtr.Zero, videoctrl[sel].record_call_back_);
videoctrl[sel].set_record_call_back_ = new VideoControl.SetRecordCallBack(RecordCallBack);
if (NT.NTBaseCodeDefine.NT_ERC_OK != NTSmartPlayerSDK.NT_SP_StartRecorder(videoctrl[sel].player_handle_))
{
Debug.LogError("call NT_SP_StartRecorder failed..");
return;
}
videoctrl[sel].is_recording_ = true;
}
private void StopRecorder(int sel)
{
Debug.Log("StopRecorder++, sel: " + sel);
if (videoctrl[sel].player_handle_ == IntPtr.Zero)
{
return;
}
NTSmartPlayerSDK.NT_SP_StopRecorder(videoctrl[sel].player_handle_);
videoctrl[sel].is_recording_ = false;
if (!videoctrl[sel].is_playing_)
{
NTSmartPlayerSDK.NT_SP_Close(videoctrl[sel].player_handle_);
videoctrl[sel].player_handle_ = IntPtr.Zero;
}
}
SmartPlayer同时播放4路海康2560*1440分辨率的RTSP流,资源占用如下:
Windows平台如果对延迟和资源占有等,要求非常高,可以选择合适的低延迟RTSP或RTMP播放插件、优化播放参数设置、优化网络环境、优化代码和渲染流程。流播放之前,做好参数配置,如缓冲设置、解码模式、低延迟模式、网络协议设置等播放参数。感兴趣的开发者,可以单独跟我沟通探讨。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。