1. WebRTC 基础入门:从零搭建音视频采集环境
第一次接触WebRTC时,我被它"浏览器直接通话"的能力震撼到了。记得当时为了测试,我打开两个Chrome标签页,不到50行代码就实现了视频对话——这比传统方案简单太多了。下面带你快速搭建第一个WebRTC demo。
1.1 设备权限获取实战
浏览器安全策略要求必须用户主动授权才能访问摄像头和麦克风。这个授权过程通过getUserMedia()API实现:
// 最简单的音视频采集
navigator.mediaDevices.getUserMedia({
audio: true,
video: true
}).then(stream => {
const video = document.getElementById('localVideo');
video.srcObject = stream;
}).catch(err => {
console.error('设备访问被拒绝:', err);
});
常见踩坑点:
- localhost和HTTPS:Chrome要求安全上下文才能调用媒体设备
- 参数配置技巧:建议明确指定分辨率,避免移动端默认低画质
video: {
width: { ideal: 1280 },
height: { ideal: 720 },
frameRate: { ideal: 30 }
}
1.2 媒体流高级控制
实际项目中我们常需要动态切换设备或调整参数。通过enumerateDevices()可以列出所有可用设备:
const devices = await navigator.mediaDevices.enumerateDevices();
const cameras = devices.filter(d => d.kind === 'videoinput');
切换摄像头时记得先停止原有轨道:
// 切换摄像头
async function switchCamera(deviceId) {
const stream = await navigator.mediaDevices.getUserMedia({
video: { deviceId: { exact: deviceId } }
});
localStream.getTracks().forEach(track => track.stop());
localStream = stream;
}
2. 穿透NAT的魔法:P2P连接建立全流程
2.1 ICE框架深度解析
WebRTC使用ICE框架穿越NAT,其工作流程像快递员找路:
- 先尝试最直接的STUN(获取公网IP)
- 不行就走TURN中转(兜底方案)
配置示例:
const pc = new RTCPeerConnection({
iceServers: [
{ urls: "stun:stun.l.google.com:19302" },
{
urls: "turn:your-turn-server.com",
username: "user",
credential: "pass"
}
]
});
2.2 信令服务实现方案
信令就像相亲时的媒人,帮双方交换基本信息。我用Socket.io实现过一个最简单的版本:
// 信令服务器
io.on('connection', socket => {
socket.on('offer', (offer, targetId) => {
io.to(targetId).emit('offer', offer, socket.id);
});
socket.on('answer', (answer, targetId) => {
io.to(targetId).emit('answer', answer);
});
});
客户端处理:
// 发送Offer
pc.createOffer().then(offer => {
pc.setLocalDescription(offer);
socket.emit('offer', offer, remoteUserId);
});
// 接收Answer
socket.on('answer', answer => {
pc.setRemoteDescription(new RTCSessionDescription(answer));
});
3. 数据传输黑科技:RTCDataChannel实战
3.1 文件传输实现
我曾用DataChannel做过一个免服务器的文件共享工具,核心代码如下:
// 发送方
const dc = pc.createDataChannel('fileTransfer');
fileInput.onchange = e => {
const file = e.target.files[0];
dc.send(JSON.stringify({
name: file.name,
size: file.size
}));
const chunkSize = 16384;
const reader = new FileReader();
reader.onload = e => dc.send(e.target.result);
let offset = 0;
function readNext() {
if(offset >= file.size) return;
reader.readAsArrayBuffer(file.slice(offset, offset + chunkSize));
offset += chunkSize;
}
readNext();
};
// 接收方
pc.ondatachannel = e => {
const dc = e.channel;
let fileData = null;
dc.onmessage = e => {
if(typeof e.data === 'string') {
fileData = JSON.parse(e.data);
fileData.buffer = [];
} else {
fileData.buffer.push(e.data);
if(fileData.buffer.length * 16384 >= fileData.size) {
saveFile(fileData);
}
}
};
};
3.2 性能调优参数
通过配置参数可以优化不同场景下的传输:
const dc = pc.createDataChannel('chat', {
ordered: false, // 是否保证顺序
maxRetransmits: 3, // 最大重传次数
maxPacketLifeTime: 1000, // 数据包有效期(ms)
priority: 'high' // 传输优先级
});
游戏场景推荐配置:
- ordered: false
- maxRetransmits: 0
- priority: 'high'
4. 企业级开发避坑指南
4.1 跨浏览器兼容方案
不同浏览器API差异曾让我头疼不已,直到发现adapter.js这个神器:
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
常见兼容问题处理:
// 统一前缀处理
const RTCPeerConnection = window.RTCPeerConnection ||
window.mozRTCPeerConnection ||
window.webkitRTCPeerConnection;
// 移动端特殊处理
if(/Android/i.test(navigator.userAgent)) {
// 安卓设备可能需要额外配置
}
4.2 监控与调试技巧
通过getStats API获取关键指标:
pc.getStats().then(stats => {
stats.forEach(report => {
if(report.type === 'outbound-rtp') {
console.log('发送码率:', report.bytesSent);
}
});
});
推荐监控指标:
- 往返时间(rtt)
- 丢包率(packetsLost)
- 抖动(jitter)
5. 实战:从零搭建视频会议系统
5.1 多人通话架构设计
我曾用Mesh架构实现过小型会议系统,核心逻辑:
// 每个新加入者与所有现有用户建立连接
socket.on('users', users => {
users.forEach(user => {
if(user !== selfId) {
const pc = createPeerConnection(user);
peerConnections[user] = pc;
}
});
});
// 处理新用户加入
socket.on('new-user', userId => {
const pc = createPeerConnection(userId);
peerConnections[userId] = pc;
});
5.2 高级功能实现
屏幕共享:
async function shareScreen() {
try {
const stream = await navigator.mediaDevices.getDisplayMedia();
const [videoTrack] = stream.getVideoTracks();
const sender = pc.getSenders().find(s => s.track.kind === 'video');
await sender.replaceTrack(videoTrack);
} catch(err) {
console.error('屏幕共享失败:', err);
}
}
音量检测:
const audioContext = new AudioContext();
const analyser = audioContext.createAnalyser();
audioContext.createMediaStreamSource(stream).connect(analyser);
function checkVolume() {
const dataArray = new Uint8Array(analyser.frequencyBinCount);
analyser.getByteFrequencyData(dataArray);
const volume = Math.max(...dataArray);
console.log('当前音量:', volume);
requestAnimationFrame(checkVolume);
}
开发过程中发现,移动端设备管理特别需要注意:
- 来电打断处理
- 前后台切换时流的恢复
- 不同机型的编解码支持差异
记得在一次项目上线后,我们突然收到安卓用户无法通话的反馈,最后发现是某些设备对VP8支持有问题,通过强制使用H.264才解决。这也提醒我们,实际开发中必须建立完善的设备兼容性测试矩阵。

285

被折叠的 条评论
为什么被折叠?



