V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
• 请不要在回答技术问题时复制粘贴 AI 生成的内容
shalingye
V2EX  ›  程序员

我在琢磨写一个 HyperV 串流传输的程序,应该采用什么方案呢?

  •  
  •   shalingye · 4 天前 · 1364 次点击
    这个程序用于 HyperV ,将虚拟机画面通过 HVsocket 接口,也就是 vmbus ,将虚拟化画面实时传输到宿主机,从而显示和控制。目前已经在 ai 帮助下实现 HDC 的画面传输,但是采集效率很低,帧率只有 10-20 左右,使用 DirectX 等技术用于采集会如何呢?有了解的朋友吗?

    vmbus 并不依赖网络,但在 winsock 上的实现似乎和 tcpip 有些类似。研究这个的目的是和虚拟显示适配器配合,超越现有的 HyperV Video 以及 RDP 的效果,是不是很酷😉
    23 条回复    2025-04-10 00:09:19 +08:00
    ShadowPower
        1
    ShadowPower  
       4 天前
    用 DXGI 采集画面会好一点。但其实还有个问题,画面数据量很大,虚拟机里还不能保证能用硬件加速视频编码……
    hronro
        2
    hronro  
       4 天前
    HyperV Video 没用过,想要超过 RDP 的效果的话,Parsec 、Moonlight 这类串流软件已经能做到了。
    minami
        3
    minami  
       4 天前
    酷当然是酷,但是我建议你直接找个成熟方案,把 vmbus 改上去就好了。。。可以直接改或者搞个 tcp 本机转发。远程控制说简单确实简单,但想做好麻烦的一比
    shalingye
        4
    shalingye  
    OP
       4 天前
    @ShadowPower 好的,我研究下,硬件加速这块是有的,场景就是在分配了 gpu 分区的情况下,如果没有的话就降级到无硬件加速即可。
    shalingye
        5
    shalingye  
    OP
       4 天前
    @hronro 这类串流方案走的是网络方案,没有内核级别的 vmbus 通路好,连 gpupv 都用的是后者
    shalingye
        6
    shalingye  
    OP
       4 天前
    @minami 是的是的,正在找了
    hronro
        7
    hronro  
       4 天前
    @shalingye #5

    有点没懂,你这个东西不是为了远程操控吗?不需要网络怎么做的?
    shalingye
        8
    shalingye  
    OP
       3 天前
    @hronro #7 vmbus 啊,很底层的技术,比网络好多了
    naiquan1007
        9
    naiquan1007  
       3 天前
    有人做了,接管了 RDP ,再说了 RDP 协议的数据在 Windows 上自己抓取,可以 over 任何协议
    shalingye
        10
    shalingye  
    OP
       3 天前
    @naiquan1007 是否有链接可以访问?这个和 RDP 本质不一样。我要做的是高性能的桌面,预期 4K144hz 这种,而 RDP 的”渲染适配器“是落后的,图形性能低下,各种图形 api 实现也很差。
    hronro
        11
    hronro  
       3 天前
    @shalingye # 8
    我的意思是,如果不走网络,那你的 HOST 和 CLIENT 都在同一台机器上吗?如果都在同一台机器上,为什么还要串流?
    shalingye
        12
    shalingye  
    OP
       3 天前
    @hronro #11 是的,宿主和虚拟机都在同一物理平台上,我没有表达清楚。因为目前普遍的本地虚拟机使用方式都是 Hyper-V Video 或者 RDP ,前者限制在了 1080p 和 62hz ,后者更是无法调整分辨率,且刷新率解锁也只有 60 ,但是如果走 vmbus 加上渲染适配器,是有希望达成 4K144hz 的。而目前普遍的串流方案是网络栈,延迟和码率都不令人满意,即使是局域网玩 fps 游戏,延迟也比较明显,我期待可以解决这个难题。
    yanxiansheng
        13
    yanxiansheng  
       3 天前
    那是不是可以实现一台物理机(性能足够强)开网吧🐶
    shalingye
        14
    shalingye  
    OP
       2 天前
    @yanxiansheng 您好,如果对此感兴趣的话可以参见本人 Github 项目:

    https://github.com/Justsenger/ExHyperV

    其中的 DDA 功能模块,可以将一台物理机的显卡和键鼠分配给虚拟机独享使用,可能对你有所帮助。

    目前正在解决的问题是 Gpupv 功能模块将视频信号高效引出虚拟机的方式。
    kuanat
        15
    kuanat  
       2 天前
    Linux 这边 virtio 也有类似的需求和实现方案。

    我个人的理解是:

    - 如果虚拟机内部是虚拟的 gpu ,那么就可以直接共享内存,宿主机直接访问虚拟机内虚拟 gpu 的 framebuffer

    - 如果虚拟机内部有直通的 gpu ,那就只能从 framebuffer 入手抓取视频源(比如 nvfbc ),然后再共享给宿主机

    应该不存在比直接内存映射更高效的 IPC 方式了。
    shalingye
        16
    shalingye  
    OP
       2 天前
    @kuanat 存在三种情况:直通的 GPU ,分区的 GPU ,无 GPU ,这样看来访问内存是不太现实的?
    kuanat
        17
    kuanat  
       2 天前
    @shalingye #16

    如果我没理解错的话,你的需求是这样的:宿主机上运行的虚拟机,然后宿主机的某个客户端窗口,显示虚拟机的视频输出内容。

    为了达到目标性能要求,一方面需要虚拟机本身能够实现高分辨率高刷新率,另一方面这个达标的显示输出能够在宿主机上正确还原,中间的通信延迟要足够低(输入设备还在宿主机上并没有直通到虚拟机)。



    为了达到第一个要求,一般有两个方案:

    1. 使用半虚拟化驱动设备,比如 qxl ,virgl 还有一些都属于这种,在虚拟机里面使用虚拟的显卡设备,这个设备会将渲染指令传递给宿主机,通过宿主机执行后再将结果传回虚拟机

    2. 直通物理显卡或者利用 sriov 直通物理显卡虚拟出来的显卡

    我对 windows 不熟悉,猜测 windows 的几个实现原理上应该都是上面两个思路。



    不知道触发了什么关键词,我二分一下拆开回复。
    kuanat
        18
    kuanat  
       2 天前
    @kuanat #17
    @shalingye #16

    我之前回复的是满足第二个要求的方法。先不考虑虚拟机里的视频信号从哪里来的,假设你已经获得了实时 framebuffer 流,可以走网络协议,同时由于宿主机和虚拟机在一起,也可以走共享内存。技术上说,所有走网络协议的方案因为存在编解码、封装,都不可能比直接内存共享延迟更低。就拿 kvm 常见的虚拟化方案来说,SPICE 是个网络协议,但与 qxl 搭配的时候,使用的是共享内存。

    从原理上说,kvm 这边直接用 SPICE 就可以了。我搜了一下 vmbus 的文档,虽然提到了共享内存,但我感觉用它来共享 framebuffer 需要做的工作还很多。
    kuanat
        19
    kuanat  
       2 天前
    @kuanat #18
    @shalingye #16


    最后再回到之前说的共享内存方案。实际上 framebuffer 的数据结构某种程度上说是硬件相关的。也就是说,即便在虚拟机和宿主机之间通过虚拟设备共享了内存,一方面要对接这个虚拟内存的读写接口,另一方面需要将 framebuffer 封装成一种统一的格式。这样在宿主机上,用于显示虚拟机内容的窗口可以直接将 framebuffer 提交给窗口管理器进行合成,即再次 DMA 到显卡。

    如果我说得不是很清楚的话,可以参考 https://looking-glass.io/ 这个项目,它的核心实现是 KVMFR https://github.com/gnif/LookingGlass/blob/master/module/kvmfr.c 这个模块,基于 IVSHMEM 完成我之前说的流程。理论上 windows 上实现相同效果的原理是一样的。
    kuanat
        20
    kuanat  
       2 天前
    @kuanat #19
    @shalingye #16

    中间发不出来的一段是关于如何获取 framebuffer 的。通过系统 api 会有二次复制的延迟,通过显卡特定 api 更合适。
    shalingye
        21
    shalingye  
    OP
       2 天前
    @kuanat #20 感谢您的回复,半虚拟化在 windows 叫做 gpupv ,直通在 windows 叫做 dda ,对于您提到的 framebuffer 来源非常复杂,可能是无显卡环境下来自于虚拟显示器+cpu ,也可能来自虚拟显示器+半虚拟化 gpu ,还可能是物理/虚拟显示器+直通 gpu ,情况比较多变,用显卡特定 api 似乎不太可行?
    ”就拿 kvm 常见的虚拟化方案来说,SPICE 是个网络协议,但与 qxl 搭配的时候,使用的是共享内存。“——这里我似有所悟,win 这边对应 spice 的是 winsock ,对应 qxl 的是 vmbus 。但想用于共享 framebuffer ,似乎没有案例。目前我卡在了 winsock 的 recv 速率过低,以及获取图像的 HDC 可能效率低下的问题上,还在想办法解决,因为我只是个小白
    kuanat
        22
    kuanat  
       2 天前   ❤️ 1
    @shalingye #21

    考虑先实现出来吧,就用系统的 api 这样可以覆盖所有情况,延迟和性能的问题先不去处理。

    粗略估计 4k 120hz 10bit 无压缩每帧数据量在 30MB 左右,单帧时间 8.3ms ,单从数据量上来说,如果是通过某种网络协议传输是没有带宽压力的,主要困难是延迟,发送接收涉及编解码。obs 有个 ndi 实现(现在叫 DistroAV ),就是在一台设备上采集,然后通过网络将数据传输到另一台设备上进行编码,可以参考一下它去掉网络开销的延迟水平。



    共享内存的路线,Linux 的实现方式大概是这样的:

    1. Linux 宿主机内核驱动 virtio 实现了 IVSHMEM

    2. Linux 虚拟机或者 windows 虚拟机也通过 virtio 的驱动模块启用 IVSHMEM

    3. 虚拟机和宿主机通过指定相同的物理地址实现内存共享( IVSHMEM 本质是虚拟 PCI 设备)

    4. 虚拟机内的特定应用(你要开发的)将 framebuffer 封装好后写入 IVSHMEM 的特定区域(手动管理)

    5. (可选)宿主机应用通过 IVSHMEM 读取对应的 framebuffer 并做后续处理



    这里可以看到 IVSHMEM 实际只是非常粗糙的共享内存机制,还需要在它的基础上实现用它完成 fb 数据交换。之前提到的 looking-glass 就是实现了一个叫 KVMFR 的模块,在 IVSHMEM 的基础上封装了一个用于 fb 数据交换的接口(硬件设备),同时实现了宿主与虚拟机之间的同步、锁,另外它用来做数据交换的格式是 dma-buf ,这样宿主机上的窗口合成器可以直接使用。之后,虚拟机的采集应用( obs/ffmpeg/系统采集)直接将数据写入 KVMFR 。

    如果你要在 windows 实现类似的功能,需要把 vmbus 当作 IVSHMEM ,然后在上面实现一个 windows 版本的 KVMFR 。



    估计这样表述应该就清楚了,由于我对 windows 不是特别了解,所以上面的方案不一定正确。
    shalingye
        23
    shalingye  
    OP
       1 天前
    @kuanat #22 感谢大佬,我需要一点时间来慢慢理解,哈哈
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   4369 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 30ms · UTC 09:50 · PVG 17:50 · LAX 02:50 · JFK 05:50
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.