Linux:网络屏幕桌面录制和VNC帧速率

34
抱歉给你带来了大量的文字 - 简而言之:
  • VNC连接的帧率是多少(以帧/秒为单位)- 或者更确切地说,是客户端还是服务器决定它?
  • 有没有其他建议用于桌面屏幕捕捉 - 但要“正确计时”/具有稳定周期的不抖动帧率(具有稳定周期); 并且可以将其作为未压缩(或无损)图像序列获取?
简而言之 - 我遇到了一个典型的问题:有时我会开发硬件,并希望录制视频,显示既输入在PC上的命令(“桌面捕捉”),又包括硬件的响应(“实时视频”)。在讨论具体细节之前,请先看一下简介/背景。
我的策略是:现在使用摄像机记录硬件测试过程(作为“实时”视频)- 同时进行桌面捕捉。摄像机生成29.97(30)FPS MPEG-2 .AVI视频;我想以相同的帧速率将桌面捕捉作为PNG图像序列获取。那么,这个想法就是:如果两个视频的帧速率相同;那么我可以简单地
  • 将桌面捕获的开始时间与“实时”视频中的匹配点对齐
  • 设置画中画,其中缩小版本的桌面捕获被放置在“实时”视频上方作为覆盖层
  • (其中“实时”视频屏幕的一部分用作与“桌面捕获”覆盖层的视觉同步源)
  • 导出适合互联网的“最终”组合视频,并进行适当压缩

原则上,我想可以使用像ffmpeg这样的命令行工具来完成此过程;但是,我更喜欢使用GUI来查找两个视频的对齐起始点。

最终,我想要实现的另一个目标是,在导出“最终”视频时保留最大质量:摄像机外的“实时”视频已经被压缩,这意味着它通过Theora .ogv编解码器时会有额外的降级。这就是为什么我想保留原始视频,并使用类似命令行的东西重新生成“最终”视频,如果需要不同的压缩/分辨率。这也是为什么我喜欢将“桌面捕获”视频作为PNG序列(尽管我想任何未压缩的格式都可以):我采取措施“调整”桌面,因此没有很多渐变,无损编码(即PNG)是适当的。

桌面捕获选项

嗯,在我目前使用的Ubuntu Lucid下,这个过程中有很多问题(你可以在10.04:Ubuntu论坛上使用Theora ogv进行视频叠加/复合编辑中了解一些我的经历)。然而,其中一个关键问题是假设两个输入视频的帧率相等——实际上,通常桌面捕获的帧率较低;更糟糕的是,往往帧不同步。
因此,这就需要在视频编辑器前坐着,手动剪切和编辑少于一秒钟的帧级别的剪辑——需要几个小时的工作才能得到最终的5分钟视频。另一方面,如果两个视频(“直播”和“捕获”)确实具有相同的帧率和同步:原则上,您只需要在视频编辑器中找到起始同步点几分钟即可——其余的“合并”视频处理可以通过单个命令行处理。这就是为什么在这篇文章中,我想重点介绍一下桌面捕获部分。
据我所见,在Linux / Ubuntu中,只有很少几个可行的(与5种在Linux桌面上录屏相对)桌面捕获替代方案(请注意,我通常使用笔记本电脑作为桌面捕获的目标)。
  1. 让目标PC(笔记本电脑)克隆其VGA输出的桌面; 使用VGA到复合视频或VGA到S视频硬件从VGA获取视频信号; 使用不同PC上的视频捕获卡抓取视频。
  2. 在目标PC上使用recordMyDesktop
  3. 在目标PC上设置VNC服务器(Ubuntu上的vinovncserver)以被捕获; 使用不同PC上的VNC捕获软件(例如vncrec)抓取/记录VNC流(随后可以转换为视频)。
  4. 使用ffmpegx11grab选项。
  5. *(在目标PC上使用某些工具,直接进行DMA传输,从图形卡帧缓冲存储器到网络适配器存储器的桌面图像帧
请注意,上述方法的实用性受到我的使用环境的限制:我想要捕获的目标PC通常运行利用测试硬件的软件,移动大量数据; 描述这样的系统最好的说法是“勉强稳定”:) 我猜这类似于游戏玩家面临的问题,当他们想要获得对一个要求很高的游戏的视频捕捉时。而一旦我开始使用像recordMyDesktop这样的东西,它也会使用相当多的资源,并希望在本地硬盘上进行捕捉 - 我立即遇到严重的内核崩溃(通常没有生成vmcore)。
因此,在我的情况下,我通常假设涉及第二台计算机 - 运行“目标”PC桌面的捕获和记录。除此之外,我目前可以看到上述选项的优缺点如下。
(桌面准备)
对于下面讨论的所有方法,我倾向于事先“准备”桌面:
- 删除桌面背景和图标 - 通过System/Preferences/Monitors(gnome-desktop-properties)将分辨率降至800x600 - 将颜色深度降至16 bpp(使用xdpyinfo | grep "of root"检查)
为了减少桌面捕获软件的负担,请注意更改Ubuntu上的颜色深度需要更改xorg.conf; 但是,"在/ etc / X11(Ubuntu 10.04)找不到xorg.conf" - 因此您可能需要先运行sudo Xorg-configure
为了保持图形资源使用低,我通常也会禁用compiz - 或者更确切地说,我将'System / Preferences / Appearance / Visual Effects'设置为“无”。然而,在我尝试通过将'Visual Effects'设置为“Normal”来启用compiz后(不会保存 ),我可以注意到LCD屏幕上的窗口重绘速度更快;因此,我保持这样,也适用于桌面捕获。我觉得这有点奇怪:如何增加效果会导致更快的屏幕刷新?看起来并不是由于专有驱动程序(该卡为“Intel Corporation N10 Family Integrated Graphics Controller”,Ubuntu在切换到compiz时没有提供专有驱动程序选项) - 尽管,可能是所有模糊和效果只是欺骗我的眼睛:))。

克隆VGA

这是最昂贵的选择(因为它需要额外购买两个硬件:VGA转换器和视频捕捉卡),主要适用于笔记本电脑(它们具有屏幕+额外的VGA输出 - 对于台式机,您可能还需要投资于额外的显卡或VGA克隆硬件)。然而,这也是唯一不需要目标PC任何额外软件(因此使用目标CPU的0%处理能力)的选项 - 也是唯一一个可以提供真实、稳定的30 fps帧率的视频的选项(因为它是由独立硬件执行的,尽管假设在各个硬件之间存在的时钟域不对齐可以忽略)。实际上,由于我已经拥有了类似于捕捉卡的东西,我已经投资于VGA转换器 - 希望它最终能够让我只需查找5分钟的对准点和一个单一的命令行即可生成最终的“合并”视频; 但我还没有看到这个过程是否按预期工作。我也想知道是否可能以未压缩的方式捕捉桌面视频@ 800x600,30 fps。

recordMyDesktop

如果您运行 recordMyDesktop 而没有任何参数-它首先开始捕获(看起来像)原始图像数据,在类似/ tmp / rMD-session-7247 的文件夹中; 在按Ctrl-C中止后,它将对该原始图像数据进行编码成.ogv。显然,在与我的测试软件(也移动大量数据)相同的硬盘上抓取大量图像数据通常会导致立即崩溃 :)

因此,我尝试设置Samba以在网络上共享驱动器;然后在目标计算机上,我将连接到这个驱动器,并指示recordMyDesktop使用这个网络驱动器(通过gvfs)作为其临时文件位置:
recordmydesktop --workdir /home/user/.gvfs/test\ on\ 192.168.1.100/capture/ --no-sound --quick-subsampling --fps 30 --overwrite -o capture.ogv 

请注意,虽然此命令将使用网络位置来存储临时文件(从而使recordMyDesktop能够与我的软件并行运行),但是一旦按下Ctrl-C,它将开始对目标计算机的本地硬盘进行编码和保存capture.ogv(尽管在那时,我真的不在意 :))。
首先,我对recordMyDesktop的抱怨之一是您无法指示它保留临时文件并避免在结束时对其进行编码:您可以使用Ctrl+Alt+p进行暂停,或者在第一个暂停后迅速按下Ctrl-C,以导致它崩溃;这将使临时文件留下(如果第二次没有足够快地按下Ctrl-C,则程序将“清除缓存…”)。然后,您可以运行以下命令:
recordmydesktop --rescue /home/user/.gvfs/test\ on\ 192.168.1.100/capture/rMD-session-7247/

为了转换原始临时数据,需要使用recordMyDesktop。然而,在进行这种“救援”时,更经常发生的是recordMyDesktop本身会崩溃。虽然,我想保留临时文件是为了拥有未压缩的源文件,用于画中画蒙太奇。请注意,“--on-the-fly-encoding”将完全避免使用临时文件——代价是使用更多的CPU处理能力(对我来说,这又是崩溃的原因)。

然后,有帧率——显然,你可以使用“--fps N”选项设置所需帧率;但是,这并不能保证你实际上会获得那个帧率;例如,我得到的是:

recordmydesktop --fps 25
...
Saved 2983 frames in a total of 6023 requests
...

我希望你能在我的测试软件运行时进行捕获;这意味着实际达到的速率更像是25*2983/6032 = 12.3632 fps!

显然,会有一些帧被丢掉 - 大多数情况下表现为视频回放过快。但是,如果我将请求的fps降低到12,则根据保存/总报告,我可以达到大约11 fps;在这种情况下,视频播放看起来不会“加速”。而且我还没有尝试将这样的捕获与实时视频对齐 - 所以我不知道那些实际上已经保存的帧是否也具有准确的时间戳。

VNC捕获

对我而言,VNC捕获包括在“目标”PC上运行VNC服务器,并在“记录器”PC上运行vncrec(twibright版)。作为VNC服务器,我使用vino,它位于“系统/首选项/远程桌面(首选项)”。显然,即使vino配置可能不是最容易管理的东西,vino作为服务器似乎对“目标”PC的负荷不太大;因为当它与我的测试软件并行运行时,我没有遇到崩溃问题。

另一方面,当vncrec在记录电脑上进行捕捉时,它也会弹出一个窗口,显示您在“实时”中看到的“目标”桌面;当“目标”上有大型更新(即整个窗口移动)时,可以很明显地看到“记录器”上的更新/刷新率存在问题。但是,对于只有小型更新的情况(即只有光标在静态背景上移动),则似乎没有问题。

这让我想起了这篇文章的一个主要问题 - 是什么设置了VNC连接中的帧率?

我没有找到一个清晰的答案,但从各种信息(见下文参考资料)中搜集到的碎片化信息,我得出以下结论:

  • VNC服务器只需尽可能快地发送更改(屏幕更改+点击等),当它们接收到时;受服务器可用的最大网络带宽限制
  • VNC客户端通过网络连接延迟和抖动接收这些更改事件,并尝试尽可能快地重建桌面“视频”流

... 这意味着,无法以稳定的、周期性的帧速率(如视频)为基础来确定任何内容。

就客户端而言,vncrec-twibright/README中指出,我通常得到的最终视频被声明为10 fps,尽管帧可能相当错位/抖动(这就需要在视频编辑器中进行剪辑)。请注意:“电影的采样率默认为10,或者通过VNCREC_MOVIE_FRAMERATE环境变量覆盖,默认为10”;然而,手册也指出:“VNCREC_MOVIE_FRAMERATE - 指定输出电影的帧速率。仅在-movie模式下有效。默认值为10。当您的转码器因10而呕吐时,请尝试24。”。如果查看“vncrec / sockets.c ”源代码,可以看到:
void print_movie_frames_up_to_time(struct timeval tv)
{
  static double framerate;
  ....
  memcpy(out, bufoutptr, buffered);
  if (appData.record)
    {
      writeLogHeader (); /* Writes the timestamp */
      fwrite (bufoutptr, 1, buffered, vncLog);
    }

...这表明一些时间戳是被写入的——但是这些时间戳是来自“原始”的“目标”PC,还是来自“记录器”PC,我无法确定。

编辑:感谢@kanaka的答案,我再次检查了vncrec/sockets.c,可以看到是writeLogHeader函数本身调用了gettimeofday;因此它写入的时间戳是本地的——也就是说,它们来自“记录器”PC(因此,这些时间戳并不能准确地描述帧在“目标”PC上产生的时间)。)

无论如何,对我来说,似乎服务器发送,而vncrec作为客户端接收——无论何时;只有在从原始捕获编码视频文件的过程中才设置/插值某种形式的帧速率。

我还想声明,在我的“目标”笔记本电脑上,有线网络连接断开了;所以无线是我访问路由器和本地网络的唯一选择——远低于路由器可以处理有线连接的100MB/s速度。然而,如果捕获的帧中的抖动是由于“目标”PC上的负载导致的错误时间戳,那么良好的网络带宽也不会太有帮助。

最后,就VNC而言,还有其他可尝试的替代方案,例如VNCast服务器(有前途,但需要一些时间从源代码构建,并处于“早期实验版本”);或者MultiVNC虽然它似乎只是一个客户端/查看器,没有录制选项)。

使用x11grab的ffmpeg

我没怎么用过这个,但是我尝试了与netcat连接; 这样:

# 'target'
ffmpeg -f x11grab -b 8000k -r 30 -s 800x600 -i :0.0 -f rawvideo - | nc 192.168.1.100 5678
# 'recorder'
nc -l 0.0.0.0 5678 > raw.video  #

…可以捕获文件,但是ffplay无法正确读取捕获的文件;而:

# 'target'
ffmpeg -f x11grab -b 500k -r 30 -s 800x600 -i :0.0 -f yuv4mpegpipe -pix_fmt yuv444p - | nc 192.168.1.100 5678
# 'recorder'
nc -l 0.0.0.0 5678 | ffmpeg -i - /path/to/samplimg%03d.png

它确实可以生成.png图像,但会有压缩伪影(我猜是与使用有关的压缩结果)。

因此,目前我并不太喜欢使用+ - 但也许我只是不知道如何为我的需求设置它。

*(图形卡-> DMA->网络)

我承认,我不确定是否存在这样的东西 - 实际上,我敢打赌它不存在 :) 我在这方面也不是专家,但我推测:

如果DMA内存传输可以从图形卡(或其保持当前桌面位图的缓冲区)作为源启动,并且网络适配器作为目标 - 那么原则上应该可以获得正确(和体面)帧速率的未压缩桌面捕获。当然,使用DMA传输的要点是减轻处理器复制桌面图像到网络接口的任务(因此,减少捕获软件对运行在“目标”PC上的进程的影响 - 特别是处理RAM或硬盘的进程)。

这样的建议当然假定:有大量的网络带宽(对于800x600、30fps,至少800*600*3*30 = 43200000 bps = 42 MiB/s,在本地100 MB/s网络中应该可以),另一台PC上有足够的硬盘来进行“记录”,最后,软件可以读取原始数据,并基于它生成图像序列或视频 :)

带宽和硬盘需求我可以接受,只要能保证稳定的帧率和未压缩的数据;这就是为什么我想知道是否已经存在这样的东西。

-- -- -- -- -- 

好吧,我想那就是全部了 - 尽可能简短 :) 有没有关于工具或流程的建议,可以生成桌面捕获

  • 以未压缩的格式(最终可转换为未压缩/无损PNG图像序列),并且
  • 带有“正确时间编码”的稳定帧率

..., 最终将有助于'易于', 单个命令行处理生成'画中画'叠加视频 - 将不胜感激!

非常感谢您的任何评论,
干杯!


参考资料

  1. 在Linux上为CryptoTE制作屏幕录像的经验 - idlebox.net
  2. VideoLAN论坛•查看主题 - VNC客户端输入支持(如screen://)
  3. VNCServer限制慢客户端用户输入 - Kyprianou,Mark - com.realvnc.vnc-list - MarkMail
  4. Linux常见问题解答 - X Windows:如何使用VNC显示和控制远程桌面?
  5. VNC需要多少带宽?RealVNC-常问问题
  6. x11vnc:真实X显示器的VNC服务器
  7. 如何记录VNC(X11会话)- Debian Wiki
  8. Ubuntu中替代gtk-RecordMyDesktop的工具
  9. (Ffmpeg-user)如何在ffmpeg中使用管道
  10. (ffmpeg-devel)(PATCH)修复在不支持XFixes扩展的X服务器上绘制光标时x11grab中的段错误

+1 针对史诗般长而连贯的问题。同时 +1 为引用 :P - Matt Ball
2
如果您正在使用Compiz,不妨试试vidcap插件 - ephemient
谢谢,@ephemient - 这看起来非常有趣!虽然我没有看到太多文档,但我看到了一些OpenGL调用 - 看起来很有前途!我相信我很快就会开始尝试它(尽管看起来需要我花一些时间才能完全理解它的作用 - 尽管我必须承认,我对其优雅的简单性感到有些困惑):) 再次感谢,干杯! - sdaau
在进行了所有的研究之后,你得出了什么解决方案? - BitBank
你的 ffmpeg 命令使用了原始流 muxer,因此没有任何带有流属性和时间戳的头文件。前者是录制器无法很好或完全播放的原因。 - Gyan
1个回答

16

你的提问思路很清晰,这么长的提问应该给你颁发勋章。;-)

针对你的主要问题,VNC使用了RFB协议,这是一种远程帧缓冲协议(因此得名),而不是流视频协议。 VNC客户端向服务器发送一个FrameBufferUpdateRequest消息,其中包含客户端感兴趣的视口区域和增量标志。如果未设置增量标志,则服务器将响应一个FrameBufferUpdate消息,其中包含所请求区域的内容。如果设置了增量标志,则服务器可能会响应一个FrameBufferUpdate消息,其中包含自上次向客户端发送该区域以来发生变化的任何部分。

请求和更新之间的交互方式的定义并不十分清晰。如果没有任何更改,服务器不一定会对每个请求做出响应。如果服务器有多个来自客户端的请求排队,它也可以发送单个响应中的更新。此外,客户端确实需要能够对来自服务器的异步更新消息做出响应(而不是作为请求的响应),否则客户端将失去同步(因为RFB不是帧协议)。

通常,客户端只是在周期性地为整个帧缓冲区视口发送增量更新请求,并在处理任何服务器更新消息时立即处理它们(即不尝试将请求和更新联系在一起)。

这里是关于FrameBufferUpdateRequest消息的描述。


1
先生,您肯定应该得到一个接受和+1 :) 非常感谢您提供简明扼要的解释 - 它最终让我浏览了vncrec/sockets.c,并意识到它记录的时间戳是客户端本地时间(在OP中进行编辑)。干杯! - sdaau
看起来原始链接已经失效了。这里是更新后的链接。 - Kim Burgess

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接