大约在 2010 年年中,我第一次了解到爱立信实验室的人员正在开发一种用于 P2P 视频聊天的“开放标准”浏览器实现。我当时非常兴奋。事实上,您只能通过 Flash 或其他插件在 Web 浏览器中使用视频聊天,这让我感到困扰。网络摄像头已经存在多年了,但其用途主要限于 MSN Messenger 和 Skype 等专有程序。
现在三年过去了,一切都将改变。就在几天前,P2P 视频聊天功能已进入 Firefox 22 的最终版本。这意味着,随着 Google Chrome 也即将支持这项技术,现在有超过 10 亿人能够在他们的浏览器中使用原生网络摄像头聊天。我认为这真是太棒了,它可能会引发互联网上新一轮的重大变革。想象一下,在未来的几年里,老式的电话线将变得过时,我们都将通过浏览器进行视频通话。
PeerSquared
在阅读了 Google Chrome 和 Firefox 将数据通道添加到 P2P 连接的消息后,我变得更加高兴,因为它提供了大量新的可能性。我对电子学习非常感兴趣,因此我萌生了构建一个名为 PeerSquared 的在线辅导白板系统的想法。
当前版本是一个概念验证,用于我自己验证使用 PeerConnection API 和数据通道(尤其是数据通道)的真正可能性。要使用 PeerSquared,只需在两个不同的屏幕上分别以教师和学生的身份登录,但使用相同且唯一的房间名称。在两个屏幕上登录后,将建立 P2P 连接,教师就可以开始在白板上进行创作。
教师执行的操作,例如绘画、书写和创建形状,也会即时显示在学生的电子白板上,使其成为一种屏幕共享。大多数功能是不言自明的,但一个不太明显的特性是能够将文件系统中的图像拖放到白板上以向学生展示它们,如下面的图片所示(地球和月球作为数据 URL 图像绘制在画布上,画布本身以宇宙图像作为背景)。
注意:PeerSquared 目前尚不能在 Google Chrome 中运行,因为它尚未实现可靠的数据通道。
逐步上传数据
通过数据通道发送的所有数据消息都将被很好地排队。这意味着,例如,当教师向学生发送一个大图像,然后立即绘制一条线(发送的数据量很小)时,不会出现学生先收到线数据的情况。此外,它们也适用于上传更大的数据块。我已经将高达 6MB 的图片上传到学生的电子白板画布上,没有任何问题。
但是,对于较大的数据,能够查看上传进度会很好。因此,我开始思考教师是否能够可靠地将数据分块发送给学生。这似乎非常简单。只需将文件读取到 ArrayBuffer 中,使用slice 方法将其切片,然后通过数据通道发送这些块。
<pre lang="javascript">
// 从 FileReader 获取 'arrayBuffer' 后
var chunkSize = 1000, byteLength = arrayBuffer.byteLength;
for(i = 0; i < byteLength; i = i + chunkSize) {
dataChannel.send(arrayBuffer.slice(i, i + chunkSize));
}
</pre>
当然,还需要发送元信息(例如文件名、大小和类型),以便在学生端创建下载链接,但这很容易做到。只需发送原始的 ArrayBuffer 数据和作为字符串化 JSON 对象的文件元数据。然后,在学生端的 onmessage
事件处理程序中,您可以区分两者。
<pre lang="javascript">
/*
1. 教师发送元信息,例如:JSON.stringify({status : 'start', name: 'image.jpg', type: 'image/jpg', chunkCount : 20});
2. 教师发送文件块,请参阅上面的代码。
3. 发送最后一个块后,教师发送一条消息,表示上传已完成,例如:JSON.stringify({status : 'complete'});
*/
var arrayBufferChunks = [], blob = null, meta = {}, container = document.getElementById('some_div');
dataChannel.onmessage = function(evt) {
var data = evt.data;
if(typeof data == 'object') {
// 步骤 2:将块重新组合在一起
arrayBufferChunks.push();
// 注意:arrayBufferChunks.length / meta.chunkCount 将是进度状态的度量。
}
else if(typeof data == 'string') {
data = JSON.parse(data);
if(data.status == 'start') {
// 步骤 1:临时存储元数据
meta = data;
}
else if(data.status == 'complete') {
// 步骤 3:为下载链接创建对象 URL
blob = new Blob(arrayBufferChunks, { "type" : meta.type });
container.innerHTML = '<a href="' + URL.createObjectURL(blob) + '" download="' + meta.name + '">' + meta.name + '</a> 已完成
}
}
}
</pre>
像这样,我已经能够连续上传多个文件,并且文件大小超过 200 MB。对于更大的文件,浏览器开始占用大量内存,并可能冻结(这似乎是由于读取文件造成的,而不是发送文件)。另一个问题是,当从文件选择器中添加 8 个或更多文件时,有时会出现浏览器崩溃的情况。这可能是由于为每个读取的文件动态实例化独立的数据通道造成的,因此值得尝试将所有文件排队到一个数据通道中。
我还注意到几次文件上传冻结的情况。这可能是由于网络连接不畅造成的。因此,很高兴知道使渐进式下载可恢复也不应该太难。只要接收方跟踪最后一个接收到的数据块,它就可以在暂停或中断上传后向发送方发送一条消息:“请发送文件 X,但从块 Y 开始”。这样,您就可以轻松地创建相当复杂的 P2P 文件共享工具。
您可以通过在教师端的聊天输入框中选择系统上的一个或多个文件并将它们拖放到该框中来尝试 PeerSquared 中的渐进式文件上传,如下面的屏幕截图所示。
向 PeerConnection 添加和删除
目前,Firefox 中的 PeerConnection 对象的一个缺点是(尚)无法简单地向单个 PeerConnection 对象添加和删除多个数据通道和视频流,因为每次添加/删除都需要重新协商会话。来自http://www.w3.org/TR/webrtc/
特别是,如果 RTCPeerConnection 对象正在使用 MediaStream,并且通过例如调用 add() 方法向其中一个流的 MediaStreamTrackList 对象添加了轨道,则 RTCPeerConnection 对象必须触发
negotiationneeded
事件。媒体组件的删除也必须触发negotiationneeded
。
negotiationneeded
事件尚未出现。作为 PeerSquared 中的替代方案,我使用了多个独立的 PeercConnection
对象:一个用于所有数据通道,另一个用于每个视频流。这样就可以正常工作。
P2P 共享的下一步
我相信 PeerSquared 的白板和网络摄像头结合在一起,是进行一对一在线教学的绝佳工具,并且可以在白板的基础上构建更多交互选项。但是,有时需要共享视频甚至整个桌面。如何做到这一点呢?一种方法是使用像 manycam 这样的虚拟网络摄像头驱动程序,它能够将来自视频或桌面的流捕获到您的网络摄像头中。缺点是您再次依赖外部专有软件。
从版本 26 开始,Google Chrome 实验性地允许 getUserMedia()
访问屏幕并通过对等连接共享它,您可以在 WebRTC 无插件屏幕共享 中进行测试。我不确定这是否或何时会成为 Web 标准。我能想到的最后一个选项(仅捕获当前选项卡内容)是使用像 html2canvas 这样的库。我自己还没有尝试过,我想知道它是否足够快和可靠,能够提供良好的“选项卡共享体验”。
总结
Mozilla Hacks 上已经有一些关于使用 HTML5 进行在线多人游戏 和视频会议的精彩演示。我希望我的 PeerSquared 演示能够让您对使用它进行在线协作和教学的一些令人惊叹的可能性有一个很好的了解,并激励您转向 P2P。有任何问题或建议吗?请不要犹豫,联系我。
关于 Fabian Gort
Fabian Gort 目前是居住在阿姆斯特丹的独立企业家,也是 PeerSquared 的开发者。作为一名生物学学生,他早在 2001 年就开始为荷兰的一所大学开发教育软件。从那时起,他一直参与了多个面向非政府组织和大学的基于 Web 的项目。
关于 Robert Nyman [荣誉编辑]
Mozilla Hacks 的技术布道师和编辑。发表演讲和撰写关于 HTML5、JavaScript 和开放 Web 的博客文章。Robert 是 HTML5 和开放 Web 的坚定支持者,自 1999 年以来一直从事 Web 前端开发工作 - 在瑞典和纽约市。他还定期在 http://robertnyman.com 上发表博客文章,并且喜欢旅行和结识新朋友。
12 条评论