Angular12通过Socket.io发摄像头流到Node.js为空对象怎么解决
问题根因
- Socket.io 传输数据时默认会做JSON序列化,而
MediaStream是浏览器提供的宿主对象,不属于可序列化的基础数据类型,直接塞进Socket.io的事件参数里会被序列化为空对象,服务端自然拿不到内容。 - 另外音视频流本身数据量极大,也不适合通过Socket.io走服务端中转的方式传输,正确的做法是用WebRTC做点对点音视频传输,Socket.io只负责传输WebRTC的建连信令即可。
具体实现方案
1. 调整客户端发送逻辑,仅传信令数据
不要把MediaStream放到Socket.io的事件参数里,先把获取到的本地流存在全局变量中,只传输用户标识、通话相关的信令字段:
toggleVideoCall(){ this.toggleVideo = false; let mediaDevices = navigator.mediaDevices; mediaDevices.getUserMedia({ video: true, audio: true }) .then((stream:MediaStream)=>{ this.addMyVideo(stream); // 本地流存在全局变量,后续WebRTC建连会用到 this.localStream = stream; console.log(stream); // 仅传输信令字段,不要带stream this.socket.emit("call", { callerName: this.myname, callerId: this.myId, recieverName: this.userToCall.username, reciever: this.userToCall._id }); }) .catch(alert) }
2. 服务端补充WebRTC信令转发逻辑
现有转发call事件的逻辑可以保留,额外新增三个WebRTC建连必须的信令转发即可,流内容全程不会经过服务端:
io.on("connection", (socket) => { socket.on("call", (data)=>{ console.log(data) io.emit("pickCall", data); }) // 新增WebRTC核心信令的转发逻辑 socket.on("offer", data => io.to(data.targetId).emit("offer", data)) socket.on("answer", data => io.to(data.targetId).emit("answer", data)) socket.on("iceCandidate", data => io.to(data.targetId).emit("iceCandidate", data)) })
3. 两端实现WebRTC建连逻辑
主叫收到被叫的接听响应后创建RTCPeerConnection实例,把本地流添加到实例中,生成offer信令通过Socket.io发给被叫;被叫收到offer后也创建RTCPeerConnection实例,添加本地流后生成answer回传给主叫,两端交换ICE候选完成建连后,就可以直接从RTCPeerConnection的回调中拿到对方的音视频流。
注意事项
- 如果需要支持跨内网通话,需要额外部署STUN/TURN服务器保证WebRTC可以完成内网穿透。
- 不要尝试把MediaStream转成Blob/ArrayBuffer后通过Socket.io传输,这种方式延迟极高,完全达不到实时音视频通话的要求。
内容的提问来源于stack exchange,提问作者Mr.EmD
相关产品推荐
相关产品推荐

