1. 程式人生 > WINDOWS開發 >能用D3D要啥自行車?Windows平臺播放RTSP或RTMP渲染模式比較

能用D3D要啥自行車?Windows平臺播放RTSP或RTMP渲染模式比較

廢話不多說,先說結論,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模式繪製,感興趣的開發者可以嘗試看看。