Transcript
00:00:00有个问题。如何在不使用网络、也不使用任何
00:00:05物理设备(如U盘)的情况下向他人发送文件?答案是光学文件传输。一位名叫埃van
00:00:13的开发者刚刚构建了一个名为 Deciman 的工具,它能够将文件从一台设备
00:00:19发送到另一台设备,只需闪烁并读取二维码即可。这是一种非常酷的文件传输技术,
00:00:26并且它运用了一些相当聪明的工程策略。所以在今天的视频中,我们将了解
00:00:31Deciman,看看它是如何工作的,并在不同的场景下进行测试,以验证光学文件传输
00:00:37到底有多强大。这会非常有趣,让我们深入探讨吧。
00:00:46Deciman 的工作原理是这样的。一台设备在其屏幕上显示类似二维码的帧流,
00:00:53而另一台设备则将摄像头对准它,并将其解码回文件。所以这里根本不涉及
00:01:00任何网络栈。因此,如果你需要与气隙设备交换文件,这是唯一的方法,
00:01:06无需物理插入任何像U盘这样的外部设备。其核心思路是,将文件
00:01:12编码为一系列二维码,在屏幕上依次闪烁,并让另一端的摄像头
00:01:19捕获并解码每一帧。这听起来可能很简单,但复杂之处在于
00:01:26摄像头的捕获并非瞬时完成的,屏幕的刷新
00:01:32也并非瞬时完成的。所以你是在让两件不同的硬件互相赛跑,如果它们失去同步,
00:01:38帧就会损坏或完全丢失。不过 Deciman 针对这个问题给出的解决方案是一种名为喷泉码(fountain coding)的方法。
00:01:46它不是发送第一帧、第二帧、第三帧并指望每一帧都能完好无损地送达,
00:01:53而是生成一个近乎无限的编码帧流,其中每一帧都是原始文件各个部分的数学混合,
00:02:00而不是文件的某一个特定块。这意味着没有任何单帧是不可替代的。
00:02:07接收端不需要特定的第47帧,它只需要足够数量的帧,无论具体收到哪几帧。
00:02:14如果你漏掉了半数帧也无所谓。它会一直广播,直到接收端收集到
00:02:22足够多的帧。但这种方法绝非万灵药。通过这种方法,我们传输的数据量
00:02:29存在一个上限。默认情况下,Deciman 的发送端以每秒 60 帧的速度每帧推送 2953 字节。
00:02:37如果你计算一下,就会发现理论上限大约是每秒 177 千字节。
00:02:44还要考虑到喷泉码带来的开销,因为其中一些混合帧按设计本身就是冗余的。
00:02:51最终剩下的就是 Deciman 的 README 文件中实际声称的数字。
00:02:56在手机对手机进行测试时,其峰值速度约为每秒 128 千字节。
00:03:02它之所以专门使用 2953 字节的块,是因为这对应于第 40 版二维码,
00:03:11即标准的最大二维码尺寸,一个 177 乘 177 的独立模块网格,被打包进单个帧中。
00:03:20真正的权衡也正是在这里显现出来的。如果你将更多数据塞进一帧,发送同一个文件所需的
00:03:26总帧数就会减少,这意味着读取它的任何摄像头都需要更高的分辨率、
00:03:33更稳的手持和更锐利的对焦,才能分清一个模块与下一个模块。
00:03:38因此,整个系统实际上是在三个变量之间寻找平衡。
00:03:42你发送的帧率是多少、其中每一帧的密度有多大、以及接收端的
00:03:49摄像头解析这种密度的能力到底有多强。Deciman 默认内置了一个特定的平衡方案,
00:03:57并且它是针对某个特定场景调整的,而正如我所发现的,那并不是我测试时所用的场景。
00:04:03所以我的设置是这样的:以笔记本电脑屏幕作为发送端,保持正常的手臂长度距离,
00:04:09就像你在实际中会使用的那样。在这种设置下,我的传输速度上限约为每秒 3 千字节。
00:04:16大约只有 1% 到 2% 的传输帧被成功解码。其余的都被捕获后丢弃了。
00:04:23这里有三个不利因素叠加在一起。首先也是最大的障碍是帧率不匹配。
00:04:30发送端在以每秒 60 帧的速度推送,但我的手机摄像头捕捉速度是每秒 30 帧。你无法用一个
00:04:38每秒只能抓取 30 张图像的传感器来采样 60 个不同的图像。而且情况比仅仅漏掉一半还要糟糕,因为每个
00:04:45捕获帧的曝光窗口横跨了屏幕上的两个不同二维码。因此,摄像头并非干净利落地漏掉一帧,而是将两帧混合在了一起,解码出来的结果什么也不是。
00:04:56在 main.ts 文件中,甚至有一条注释说明,即使应用程序明确向摄像头请求 60 帧,iOS 也会默默地以每秒 30 帧的速度输出。
00:05:07摄像头就是不给你请求的帧率,而代码已经解决了这个问题。
00:05:12在该文件的更下方,有一条注释几乎准确地预言了我在第一次尝试时所遭遇的情况。
00:05:19默认值(即每秒 60 帧、每帧 2953 字节)是专门针对近距离手机对手机演示调优的。
00:05:29而同样的组合在普通的臂距显示器上预计会遇到困难。
00:05:35换句话说,项目文档早就告诉我这会发生。只是我没有在代码里向下滚动得足够远去看到它。
00:05:41在发送端,60 赫兹的笔记本电脑面板存在相反的问题。LCD 像素需要大量时间才能完成灰度到灰度的颜色过渡。
00:05:51这种响应时间意味着,在一个代码完全呈现之前,下一个代码已经开始在其上方绘制了。
00:05:58因此你会得到重影伪影。第二个问题是代码密度与摄像头分辨率的矛盾。
00:06:042953 字节对应的是二维码版本 40,但可靠解码每个模块大约需要 3 到 4 个摄像头像素,仅就代码本身而言,这意味着在锐利对焦下横向需要 600 多个像素。
00:06:20手持臂距的笔记本电脑屏幕很难占满手机摄像头的那么多画面,而近距离的手机对手机则能轻松充满整个取景器。
00:06:30第三个问题是亮度和对比度。这是一个次要问题,如果接收端的二值化阈值能够干净利落地处理图像会有所帮助,但它仍然无法解决帧率不匹配或代码尺寸过小的问题。
00:06:44现在让我们尝试进行同样的传输,但这次从一部手机传到另一部手机。
00:06:50所以唯一改变的是发送端。这是一个近距离手持的小型高亮度 OLED 屏幕,填满了接收手机的整个画面。
00:06:58现在摄像头实际上可以更接近其真实的帧率。
00:07:02代码以高分辨率充满画面,并且没有 LCD 重影的干扰。
00:07:07这就是 README 文件中每秒 128 千字节这一数字实际测得的场景。
00:07:14接收端代码中还有一个小巧但确实有用的细节。
00:07:18它分别报告了捕获 FPS 和解码 FPS。捕获告诉我们摄像头实际看到了什么。
00:07:26解码告诉你其中有多少是真正可用的。
00:07:30当这两个数字彼此拉开差距时,比如捕获保持健康而解码崩塌,
00:07:35你面对的就是帧密度与摄像头在该距离下实际解析能力之间的不匹配。
00:07:42因此,如果你的实际用例是笔记本电脑到手机,解决方法就是在发送端重新平衡这三个变量。
00:07:50你应该将每帧字节数降至 1465,这对应于具有更大、更宽容模块的粗糙二维码版本。
00:07:59然后刻意将传输 FPS 降至 24,低于摄像头的 30 FPS 上限。
00:08:07这样帧就能一次一个地被干净采样,而不会互相混合。
00:08:12正如你所看到的,降低这些数字会带来大得多的吞吐量。
00:08:16这恰恰向你表明,光学文件传输并不是一种一刀切的解决方案。
00:08:21你必须根据所使用的硬件,针对每个用例手动进行调整。
00:08:26就是这样,各位。
00:08:27这就是 Deciman 的简要介绍。
00:08:29总的来说,这是一个非常有趣且值得深入研究的项目。
00:08:32光学文件传输是一项相当酷的技术。
00:08:36在这里看到喷泉码的概念被应用于实际案例场景中,真的非常有趣。
00:08:44在探索这个项目的同时,我也在思考,你会在哪里实际使用这种工具呢?
00:08:49我想显而易见的答案是用于气隙传输,在那种情况下你刻意不需要任何网络连接。
00:08:57或者甚至是那些连蓝牙或 Wi-Fi 都没有的设备,比如老旧硬件或嵌入式系统。
00:09:04我的意思是,在任何只有屏幕和摄像头这两样东西可以依赖的地方。
00:09:10但你对这个工具有什么看法?
00:09:11你以前用过光学传输工具吗?
00:09:14你觉得这有什么现实生活中的应用场景吗?
00:09:17请在下方评论区告诉我们。
00:09:19各位,如果你喜欢这类技术解析,请点击视频下方的点赞按钮让我知道。
00:09:25同时也别忘了订阅我们的频道。
00:09:28我是来自 BetterStack 的 Andrus,我们下期视频再见。
00:09:34我们下期视频见。