我们在对接RTSP、RTMP推拉流播放的时候,开发者提到这样的技术诉求,他们在用于安检等场景的时候,采集分辨率甚至需要4K+,帧率需要达到50帧以上,码率也非常高,这就对推流和播放模块,提出了更高的要求。
以播放端为例,如果需要播放50帧以上高帧率高码率高分辨率的RTSP或RTMP流,以下是一些关键的步骤和考虑因素:
以大牛直播SDK的SmartPlayer为例,目前实现的功能如下,如不单独介绍,Windows、Linux、Android、iOS均支持,现场测试,超过1080p,50帧以上,依然可以达到150-300ms延迟:
对应Demo:
以Winodws平台为例,开始播放实现如下:
/*
* SmartPlayerForm.cs
* Author:daniusdk.com
* QQ 89030985
*/
private void btn_play_Click(object sender, EventArgs e)
{
if (btn_play.Text == "播放")
{
if (!is_recording_)
{
if (!InitCommonSDKParam())
{
MessageBox.Show("设置参数错误!");
return;
}
}
//video resolution callback
video_size_call_back_ = new SP_SDKVideoSizeCallBack(SP_SDKVideoSizeHandle);
NTSmartPlayerSDK.NT_SP_SetVideoSizeCallBack(player_handle_, IntPtr.Zero, video_size_call_back_);
bool is_support_d3d_render = false;
Int32 in_support_d3d_render = 0;
if (NT.NTBaseCodeDefine.NT_ERC_OK == NTSmartPlayerSDK.NT_SP_IsSupportD3DRender(player_handle_, playWnd.Handle, ref in_support_d3d_render))
{
if (1 == in_support_d3d_render)
{
is_support_d3d_render = true;
}
}
//is_support_d3d_render = false;
if (is_support_d3d_render)
{
is_gdi_render_ = false;
// 支持d3d绘制的话,就用D3D绘制
NTSmartPlayerSDK.NT_SP_SetRenderWindow(player_handle_, playWnd.Handle);
if (btn_check_render_scale_mode.Checked)
{
NTSmartPlayerSDK.NT_SP_SetRenderScaleMode(player_handle_, 1);
}
else
{
NTSmartPlayerSDK.NT_SP_SetRenderScaleMode(player_handle_, 0);
}
}
else
{
is_gdi_render_ = true;
playWnd.Visible = false;
// 不支持D3D就让播放器吐出数据来,用GDI绘制
//video frame callback (YUV/RGB)
//format请参见 NT_SP_E_VIDEO_FRAME_FORMAT,如需回调YUV,请设置为 NT_SP_E_VIDEO_FRAME_FROMAT_I420
video_frame_call_back_ = new SP_SDKVideoFrameCallBack(SetVideoFrameCallBack);
NTSmartPlayerSDK.NT_SP_SetVideoFrameCallBack(player_handle_, (Int32)NT.NTSmartPlayerDefine.NT_SP_E_VIDEO_FRAME_FORMAT.NT_SP_E_VIDEO_FRAME_FORMAT_RGB32, IntPtr.Zero, video_frame_call_back_);
}
user_data_call_back_ = new SP_SDKUserDataCallBack(SDKUserDataCallBack);
NTSmartPlayerSDK.NT_SP_SetUserDataCallBack(player_handle_, IntPtr.Zero, user_data_call_back_);
if (btn_check_add_osd.Checked)
{
DrawOSD("叠加字符展示");
}
else
{
DrawOSD(null);
}
UInt32 ret_start = NTSmartPlayerSDK.NT_SP_StartPlay(player_handle_);
if (ret_start != 0)
{
MessageBox.Show("播放失败..");
return;
}
//转发相关,具体参见转发demo
/*
pull_stream_video_data_call_back_ = new SP_SDKPullStreamVideoDataCallBack(OnVideoDataHandle);
pull_stream_audio_data_call_back_ = new SP_SDKPullStreamAudioDataCallBack(OnAudioDataHandle);
NTSmartPlayerSDK.NT_SP_SetPullStreamVideoDataCallBack(pull_handle_, IntPtr.Zero, pull_stream_video_data_call_back_);
NTSmartPlayerSDK.NT_SP_SetPullStreamAudioDataCallBack(pull_handle_, IntPtr.Zero, pull_stream_audio_data_call_back_);
UInt32 ret = NTSmartPlayerSDK.NT_SP_StartPullStream(pull_handle_);
*/
btn_capture_image.Enabled = true;
is_playing_ = true;
btn_play.Text = "停止";
}
else
{
StopPlayback();
}
}
停止播放:
private void StopPlayback()
{
if (player_handle_ == IntPtr.Zero)
{
return;
}
if (is_playing_)
{
NTSmartPlayerSDK.NT_SP_StopPlay(player_handle_);
is_playing_ = false;
//playWnd.Invalidate(); //清空最后一帧数据,如不加,默认保留最后一帧画面
}
if (cur_video_frame_.plane0_ != IntPtr.Zero)
{
Marshal.FreeHGlobal(cur_video_frame_.plane0_);
cur_video_frame_.plane0_ = IntPtr.Zero;
}
btn_full_screen_.Enabled = false;
btn_capture_image.Enabled = false;
textBox_resolution.Text = "";
btn_play.Text = "播放";
lable_cur_status_txt.Text = "";
edit_player_msg_.Text = "";
DrawOSD(null);
}
如需要实时快照:
private void btn_capture_image_Click(object sender, EventArgs e)
{
if ( String.IsNullOrEmpty(capture_image_path_) )
{
MessageBox.Show("请先设置保存截图文件的目录! 点击截图左边的按钮设置!");
return;
}
if ( player_handle_ == IntPtr.Zero )
{
return;
}
if ( !is_playing_)
{
MessageBox.Show("请在播放状态下截图!");
return;
}
String name = capture_image_path_ + "\\" + DateTime.Now.ToString("hh-mm-ss") + ".png";
byte[] buffer1 = Encoding.Default.GetBytes(name);
byte[] buffer2 = Encoding.Convert(Encoding.Default, Encoding.UTF8, buffer1, 0, buffer1.Length);
byte[] buffer3 = new byte[buffer2.Length + 1];
buffer3[buffer2.Length] = 0;
Array.Copy(buffer2, buffer3, buffer2.Length);
IntPtr file_name_ptr = Marshal.AllocHGlobal(buffer3.Length);
Marshal.Copy(buffer3, 0, file_name_ptr, buffer3.Length);
capture_image_call_back_ = new SP_SDKCaptureImageCallBack(SDKCaptureImageCallBack);
UInt32 ret = NTSmartPlayerSDK.NT_SP_CaptureImage(player_handle_, file_name_ptr, IntPtr.Zero, capture_image_call_back_);
Marshal.FreeHGlobal(file_name_ptr);
if (NT.NTBaseCodeDefine.NT_ERC_OK == ret)
{
// 发送截图请求成功
}
else if ((UInt32)NT.NTSmartPlayerDefine.SP_E_ERROR_CODE.NT_ERC_SP_TOO_MANY_CAPTURE_IMAGE_REQUESTS == ret)
{
// 通知用户延时
MessageBox.Show("Too many capture image requests!");
}
else
{
// 其他失败
}
}
如果需要实时录像:
private void btn_record_Click(object sender, EventArgs e)
{
if (player_handle_ == IntPtr.Zero)
return;
if (btn_record.Text == "录像")
{
if (!is_rec_video_ && !is_rec_audio_)
{
MessageBox.Show("音频录制选项和视频录制选项至少需要选择一个!");
return;
}
if (!is_playing_)
{
if (!InitCommonSDKParam())
{
MessageBox.Show("设置参数错误!");
return;
}
}
NTSmartPlayerSDK.NT_SP_SetRecorderVideo(player_handle_, is_rec_video_ ? 1 : 0);
NTSmartPlayerSDK.NT_SP_SetRecorderAudio(player_handle_, is_rec_audio_ ? 1 : 0);
UInt32 ret = NTSmartPlayerSDK.NT_SP_SetRecorderDirectoryW(player_handle_, rec_dir_);
if (NT.NTBaseCodeDefine.NT_ERC_OK != ret)
{
MessageBox.Show("设置录像目录失败");
return;
}
NTSmartPlayerSDK.NT_SP_SetRecorderFileMaxSize(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(player_handle_, ref rec_name_ruler);
record_call_back_ = new SP_SDKRecorderCallBack(SDKRecorderCallBack);
NTSmartPlayerSDK.NT_SP_SetRecorderCallBack(player_handle_, IntPtr.Zero, record_call_back_);
NTSmartPlayerSDK.NT_SP_SetRecorderAudioTranscodeAAC(player_handle_, is_audio_transcode_aac_ ? 1 : 0);
if (NT.NTBaseCodeDefine.NT_ERC_OK != NTSmartPlayerSDK.NT_SP_StartRecorder(player_handle_))
{
MessageBox.Show("录像失败!");
return;
}
btn_record.Text = "停止录像";
is_recording_ = true;
}
else
{
StopRecorder();
}
}
停止录像:
private void StopRecorder()
{
if (player_handle_ == IntPtr.Zero)
{
return;
}
NTSmartPlayerSDK.NT_SP_StopRecorder(player_handle_);
btn_record.Text = "录像";
is_recording_ = false;
}
RTSP|RTMP播放器,如果需要低延迟的播放50帧以上的高帧率码率的数据,需要有好的解码性能、做好音视频的同步处理,确保播放器的每个环节可控。才可以达到高稳定、低延迟的播放体验,感兴趣的开发者,可以单独跟我沟通讨论。
?
原创声明:本文系作者授权便宜云主机开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权便宜云主机开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。