我们常用的屏幕共享的技术实现,例如在客户端 Android 和 IOS 开发,实现屏幕共享基本上涉及两个方面:录制屏幕和传输屏幕,也就是推流。例如腾讯会议,QQ 远程桌面等。
本文主要来讨论在小程序侧的实现。在小程序内实现,无非就是两种方式:原生小程序 API 支持或 webview 内嵌的 H5 支持。
作者:kevin
来源:kevin技术blog / 原标题:小程序下H5页面同屏思考与落地
原文:https://mp.weixin.qq.com/s/8ougAgRzdVpazaNPbTW5yA
但从官方搜寻得到的信息,小程序 API 不支持屏幕共享!
尽管提供了<live-pusher>和 <live-player>两个标签,但它俩主要用于直播的场景,而且是需要通过类目审核。
那就剩一种方案了,小程序内嵌 H5 实现屏幕共享。
需求和挑战
在 H5 的探索中,我们抓到一个利器,WebRTC。
WebRTC(Web 实时通信)是一种用于在 Web 浏览器之间实现实时通信的开放标准,提供一系列的 API 和协议,使得浏览器可以直接进行音频、视频和数据的实时传输。应用场景包括实时音视频通话、视频会议、远程协作、屏幕共享、直播等。屏幕共享,这就是我们要的!
我们可以先借用 WebRTC 来聊下同屏的关键 3 个步骤实现:
1、获取屏幕流。WebRTC通过浏览器提供的API-getDisplayMedia来获取屏幕的媒体流。
2、传输流。传输获取的媒体流数据保存到服务器。通过信令服务器进行信令数据交换(如媒体描述信息SDP的交换,连接地址的交换等
3、渲染流。另一客户端获取屏幕流,使用video标签进行屏幕流的渲染播放。
但其实,在使用 webRTC 有另外的限制,兼容性和碎片化严重。在小程序 webview 中要使用 webRTC,需要 IOS 系统达到 14.3 版本及以上,并且微信版本为 6.5 及以上,这意味着要放弃一批用户,且新特性在 webview 中产生的问题,是无法评估完整,费时费力,可能达不到理想效果。
那有没有其他方式实现呢?分析一下需求,两边要看到同一页面内容,A 客户端输入,滑动,点击等操作,都需要真实反馈到 B 客户端,反之亦然。那能不能通过指令+事件同步呢?即A用户在页面操作的行为事件,通过指令发送到B客户端,B客户端根据接收的事件行为在页面进行还原操作。听起来可行,动手就干!
技术交互实现
指令:要求低延迟,传输快,开销小。Websocket满足此要求,还可进行双向数据传输。
事件:页面操作行为。除基本的JS事件外,定义其他行为,例如:进线、退出、切屏、网络异常等等等…
配合交互图和文字,来解析下实现:
1、A用户发送页面链接给B用户,等待B进入
2、完成Socket连接后,两端即可开始通信
3、由A暂获操作权,并开始操作页面,在操作页面的过程中,实时传输数据给B客户端,并由B客户端解析数据和事件,进行事件还原,B客户端页面UI发生变化
4、A可以转换操作权限,也是经过事件传输,等待B客户端获取操作权后,重复步骤3,来实现两端的同屏。
伪代码实现:
switch (Event) {
case 'click':
handleEventClick();
break;
case 'input':
handleEventInput();
break;
case 'scroll':
handleEventScroll();
break;
case 'switchOperation':
handleEventSwitchOperation();
break;
default:
console.log('Unknown Event.');
}
事件的思考
在JS中,事件是指在特定的情况下触发的动作或响应。常见的事件包括:点击、输入、滑动、拖拽等。但是这些事件是不能脱离特定的元素或者文档上,也就是任何用户事件都必须要绑定一个DOM元素或者文档,才能触发完成。例如一个简单的点击事件,通过getElementById()方法获取了一个按钮元素,并使用addEventListener()方法将点击事件与一个匿名函数关联起来。
// 获取按钮元素
const btn = document.getElementById('myButton');
// 添加点击事件监听器
btn.addEventListener('click', function() {
console.log("按钮被点击了!");
});
那么在指令+事件实现的同屏下,事件如何绑定元素,元素在页面的唯一性如何确定?
如下图,如何找到标红.child-9的位置呢?把页面组成部分当作树形结构,采用递归方式,自下而上,找到该元素。
确定元素路径为:html > body > div#app3 > div.child-4 > div.child-9
伪代码实现:
// 注:以下代码为测试代码,请根据实际情况优化
function getElementPath(element) {
if (element.nodeName === 'HTML') {
return 'html';
}
let path = '';
let parent = element.parentNode;
if (parent) {
path += getElementPath(parent) + ' > ';
}
path += element.nodeName.toLowerCase();
if (element.id) {
path += '#' + element.id;
} else if (element.className) {
path += '.' + element.className.trim().replace(/ +/g, '.');
}
return path;
}
最终,我们可以得到指令传输的数据结构:
{
"event": "click", // 事件类型
"domPath": "html > body > div#app3 > div.child-3 > div.child-9", // 元素路径
"extraData": {} // 额外的传输数据
}
踩过的坑
1、单页面实现,页面跳转用router.push。保证整个同屏在单页面上,采用小程序API提供的方法跳转或多页面设计,会导致页面重新载入,需要双方重新连接。
2、在使用音视频传输的时候,部分非主流机型存在接收不到声音的问题。
3、不适合场景特别复杂的页面,例如canvas绘图、可视化图表变更等操作。
客户端、H5同屏对比
最后,我们来对客户端以及本文实现的同屏技术,做一个对比:
版权声明:本文内容转自互联网,本文观点仅代表作者本人。本站仅提供信息存储空间服务,所有权归原作者所有。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至1393616908@qq.com 举报,一经查实,本站将立刻删除。