能用D3D要啥自行車?Windows平臺播放RTSP或RTMP渲染模式比較
阿新 • • 發佈:2020-06-21
廢話不多說,先說結論,Windows平臺播放渲染這塊,支援D3D的前提下,優先D3D,如果檢測到不支援D3D,資料回撥上來,GDI模式繪製。
相比GDI模式,D3D繪製更細膩,繪製效率更高,CPU佔用低,只要是系統檢測支援,一般建議D3D模式。
無圖無真相:
本文以1920*1080解析度、30幀、固定位元速率(採集螢幕左側區域)為例,通過大牛直播SDK ( github) 的Windows平臺SmartPublisherDemo.exe工具推送到內網nginx伺服器,然後分別以D3D模式和GDI模式拉流(播放端緩衝設定為0)。
可以看到:
D3D模式,CPU佔用只有2.7%,延遲:249-156 = 93ms;
GDI模式,CPU佔用19.5%,延遲249-73 = 176ms。
無論是從延遲和CPU佔用上看,D3D模式都佔優。
實現思路:
以C#的demo為例:
1. 先檢測系統是否支援D3D模式:
if (NT.NTBaseCodeDefine.NT_ERC_OK == NTSmartPlayerSDK.NT_SP_IsSupportD3DRender(player_handle_,ref in_support_d3d_render))
{
if (1 == in_support_d3d_render)
{
is_support_d3d_render = true;
}
}
2. 如不支援D3D,資料回到上層,做繪製:
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_,video_frame_call_back_);
}
public void SetVideoFrameCallBack(IntPtr handle,IntPtr frame)
{
if (frame == IntPtr.Zero)
{
return;
}
//如需直接處理RGB資料,請參考以下流程
NT_SP_VideoFrame video_frame = (NT_SP_VideoFrame)Marshal.PtrToStructure(frame,typeof(NT_SP_VideoFrame));
NT_SP_VideoFrame pVideoFrame = new NT_SP_VideoFrame();
pVideoFrame.format_ = video_frame.format_;
pVideoFrame.width_ = video_frame.width_;
pVideoFrame.height_ = video_frame.height_;
pVideoFrame.timestamp_ = video_frame.timestamp_;
pVideoFrame.stride0_ = video_frame.stride0_;
pVideoFrame.stride1_ = video_frame.stride1_;
pVideoFrame.stride2_ = video_frame.stride2_;
pVideoFrame.stride3_ = video_frame.stride3_;
Int32 argb_size = video_frame.stride0_ * video_frame.height_;
pVideoFrame.plane0_ = Marshal.AllocHGlobal(argb_size);
CopyMemory(pVideoFrame.plane0_,(UInt32)argb_size);
if (playWnd.InvokeRequired)
{
BeginInvoke(set_video_frame_call_back_,pVideoFrame);
}
else
{
set_video_frame_call_back_(status,pVideoFrame);
}
}
具體繪製程式碼:
private void SmartPlayerForm_Paint(object sender,PaintEventArgs e)
{
if (player_handle_ == IntPtr.Zero || !is_gdi_render_ || !is_playing_)
{
return;
}
if (cur_video_frame_.plane0_ == IntPtr.Zero)
{
return;
}
Bitmap bitmap = new Bitmap(cur_video_frame_.width_,System.Drawing.Imaging.PixelFormat.Format32bppRgb,cur_video_frame_.plane0_);
int image_width = cur_video_frame_.width_;
int image_height = cur_video_frame_.height_;
Graphics g = e.Graphics; //獲取窗體畫布
g.SmoothingMode = SmoothingMode.HighSpeed;
int limit_w = this.Width - 60;
int limit_h = this.Height - playWnd.Top - 60;
if (btn_check_render_scale_mode.Checked)
{
int d_w = 0,d_h = 0;
int left_offset = 0;
int top_offset = 0;
Brush brush = new SolidBrush(Color.Black);
g.FillRectangle(brush,limit_h);
GetRenderRect(limit_w,ref d_h);
g.DrawImage(bitmap,d_h); //在窗體的畫布中繪畫出記憶體中的影象
}
else
{
g.DrawImage(bitmap,limit_h); //在窗體的畫布中繪畫出記憶體中的影象
}
}
目前來看,不支援D3D的機器少之又少,在環境具備的情況下,優先建議考慮D3D模式繪製,感興趣的開發者可以嘗試看看。