Windows平台RTMP|RTSP播放器为什么要兼容GDI绘制

简介: 先说结论,Windows平台播放渲染这块,一般来说99%以上的机器都是支持D3D的,实现GDI模式绘制,除了为了好的兼容性外,在远程连接的场景下,D3D创建不成功,需要使用GDI模式。

为什么要支持GDI

先说结论,Windows平台播放渲染这块,一般来说99%以上的机器都是支持D3D的,实现GDI模式绘制,除了为了好的兼容性外,在远程连接的场景下,D3D创建不成功,需要使用GDI模式。


简单来说,Windows平台的RTMP播放器或RTSP播放器,设计如果系统支持D3D,优先D3D,如果检测到不支持D3D,数据回调上来,GDI模式绘制。


在之前的博客,我们提到过:D3D绘制出来的图像效果更细腻,绘制效率也更高,CPU占用相对GDI更低

20200620190631771.png

上图以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_ERC_OK == player_api_.IsSupportD3DRender(player_handle_,
      wrapper_render_wnd_.RenderWnd(), &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绘制
      player_api_.SetRenderWindow(player_handle_, wrapper_render_wnd_.RenderWnd());
      player_api_.SetRenderScaleMode(player_handle_, btn_check_render_scale_mode_.GetCheck() == BST_CHECKED ? 1 : 0);
    }
    else
    {
      is_gdi_render_ = true;
      // 不支持D3D就让播放器吐出数据来,用GDI绘制
      wrapper_render_wnd_.SetRenderScaleMode(btn_check_render_scale_mode_.GetCheck() == BST_CHECKED ? 1 : 0);
      player_api_.SetVideoFrameCallBack(player_handle_, NT_SP_E_VIDEO_FRAME_FORMAT_RGB32,
        GetSafeHwnd(), SM_SDKVideoFrameHandle);
    }
extern "C" NT_VOID NT_CALLBACK SM_SDKVideoFrameHandle(NT_HANDLE handle, NT_PVOID userData, NT_UINT32 status,
  const NT_SP_VideoFrame* frame)
{
  /*if (frame != NULL)
  {
  std::ostringstream ss;
  ss << "Receive frame time_stamp:" << frame->timestamp_ << "ms" << "\r\n";
  OutputDebugStringA(ss.str().c_str());
  }*/
  if ( frame != NULL )
  {
    if ( NT_SP_E_VIDEO_FRAME_FORMAT_RGB32 == frame->format_
      && frame->plane0_ != NULL
      && frame->stride0_ > 0
      && frame->height_ > 0 )
    {
      std::unique_ptr<nt_rgb32_image > pImage(new nt_rgb32_image());
      pImage->size_ = frame->stride0_* frame->height_;
      pImage->data_ = new NT_BYTE[pImage->size_];
      memcpy(pImage->data_, frame->plane0_, pImage->size_);
      pImage->width_  = frame->width_;
      pImage->height_ = frame->height_;
      pImage->stride_ = frame->stride0_;
      HWND hwnd = (HWND)userData;
      if ( hwnd != NULL && ::IsWindow(hwnd) )
      {
        ::PostMessage(hwnd, WM_USER_SDK_RGB32_IMAGE, (WPARAM)handle, (LPARAM)pImage.release());
      }
    }
  }
}

具体绘制代码:

LRESULT CSmartPlayerDlg::OnSDKRGB32Image(WPARAM wParam, LPARAM lParam)
{
  nt_rgb32_image* pImage = (nt_rgb32_image*)(lParam);
  if (pImage == NULL)
    return S_OK;
  std::shared_ptr<nt_rgb32_image> sp_image(pImage);
  if ( is_gdi_render_ )
  {
    wrapper_render_wnd_.OnRGB32Image(sp_image);
  }
  return S_OK;
}
void nt_render_wnd::OnPaint()
{
  CPaintDC dc(this); // device context for painting
  // TODO: Add your message handler code here
  // Do not call CWnd::OnPaint() for painting messages
  if ( IsIconic() )
  {
    return;
  }
  // 先绘制一个黑色的背景
  CRect rc_client(0, 0, 0, 0);
  GetClientRect(rc_client);
  if ( rc_client.IsRectNull()
    || rc_client.IsRectEmpty() )
  {
    return;
  }
  auto mem_dc = ::CreateCompatibleDC(dc.GetSafeHdc());
  if ( mem_dc == NULL )
    return;
  auto mem_bitmap = ::CreateCompatibleBitmap(dc.GetSafeHdc(), rc_client.Width(), rc_client.Height());
  if ( mem_bitmap == NULL )
  {
    ::DeleteDC(mem_dc);
    return;
  }
  ::SelectObject(mem_dc, mem_bitmap);
  HBRUSH brush = ::CreateSolidBrush(RGB(0, 0, 0));
  ::FillRect(mem_dc, &rc_client, brush);
  ::DeleteObject(brush);
  if ( rgb32_image_ )
  {
    if ( player_api_.GDIDrawRGB32 != NULL
      && player_handle_ != NULL )
    {
      auto render_rc = GetRenderRect(rc_client, rgb32_image_->width_, rgb32_image_->height_);
      player_api_.GDIDrawRGB32(player_handle_, mem_dc,
        render_rc.left, render_rc.top,
        render_rc.Width(), render_rc.Height(),
        0, 0,
        rgb32_image_->width_, rgb32_image_->height_,
        rgb32_image_->data_, rgb32_image_->size_,
        rgb32_image_->width_, rgb32_image_->height_,
        rgb32_image_->stride_);
      if (logo_ && logo_->data_ != nullptr)
      {
        player_api_.GDIDrawARGB(mem_dc,
          render_logo_left_, render_logo_top_,
          render_logo_width_, render_logo_height_,
          0, 0,
          logo_->width_, logo_->height_,
          logo_->data_.get(), logo_->stride_,
          logo_->width_, logo_->height_
          );
      }
    }
  }
  ::BitBlt(dc.GetSafeHdc(),
    0, 0,
    rc_client.Width(), rc_client.Height(),
    mem_dc,
    0, 0,
    SRCCOPY);
  ::DeleteObject(mem_bitmap);
  ::DeleteDC(mem_dc);
}

目前来看,不支持D3D的机器少之又少,在环境具备的情况下,优先建议考虑D3D模式绘制,不支持的情况下,同时兼容GDI绘制是个不错的选择。

相关文章
|
1月前
|
XML C# 数据格式
掌握了在Windows平台上查看DLL依赖的方法
掌握了在Windows平台上查看DLL依赖的方法
205 4
|
1月前
|
NoSQL Shell MongoDB
Windows 平台安装 MongoDB
10月更文挑战第10天
46 0
Windows 平台安装 MongoDB
|
2月前
|
监控 C# 块存储
Windows平台RTSP|RTMP播放器如何叠加OSD文字
做Windows平台RTSP|RTMP播放器的时候,特别是多路播放场景下,开发者希望可以给每一路RTSP或RTMP流添加个额外的OSD台标,以区分不同的设备信息(比如添加摄像头所在位置),本文主要探讨,如何动态添加OSD台标。
Windows平台RTSP|RTMP播放器如何叠加OSD文字
|
2月前
|
Linux Android开发 iOS开发
Windows平台RTSP|RTMP播放器如何实现实时录像功能
Windows平台RTSP、RTMP播放器实时录像接口设计,实际上,除了Windows平台,我们Linux、Android、iOS平台也是一样的设计,单纯的录像模块,如果做的全面,也不是一两个接口可以搞定的
|
1月前
|
并行计算 开发工具 异构计算
在Windows平台使用源码编译和安装PyTorch3D指定版本
【10月更文挑战第6天】在 Windows 平台上,编译和安装指定版本的 PyTorch3D 需要先安装 Python、Visual Studio Build Tools 和 CUDA(如有需要),然后通过 Git 获取源码。建议创建虚拟环境以隔离依赖,并使用 `pip` 安装所需库。最后,在源码目录下运行 `python setup.py install` 进行编译和安装。完成后即可在 Python 中导入 PyTorch3D 使用。
201 0
|
6天前
|
网络安全 Windows
Windows server 2012R2系统安装远程桌面服务后无法多用户同时登录是什么原因?
【11月更文挑战第15天】本文介绍了在Windows Server 2012 R2中遇到的多用户无法同时登录远程桌面的问题及其解决方法,包括许可模式限制、组策略配置问题、远程桌面服务配置错误以及网络和防火墙问题四个方面的原因分析及对应的解决方案。
|
12天前
|
监控 安全 网络安全
Windows Server管理:配置与管理技巧
Windows Server管理:配置与管理技巧
41 3
|
15天前
|
存储 安全 网络安全
Windows Server 本地安全策略
由于广泛使用及历史上存在的漏洞,Windows服务器成为黑客和恶意行为者的主要攻击目标。这些系统通常存储敏感数据并支持关键服务,因此组织需优先缓解风险,保障业务的完整性和连续性。常见的威胁包括勒索软件、拒绝服务攻击、内部威胁、恶意软件感染等。本地安全策略是Windows操作系统中用于管理计算机本地安全性设置的工具,主要包括用户账户策略、安全选项、安全设置等。实施强大的安全措施,如定期补丁更新、网络分段、入侵检测系统、数据加密等,对于加固Windows服务器至关重要。
|
1月前
|
边缘计算 安全 网络安全
|
1月前
|
数据安全/隐私保护 Windows
安装 Windows Server 2019
安装 Windows Server 2019