我想用本地主机上的WebRTC连接2台设备。所有设备均无法访问互联网。它们已连接到同一本地wifi。
我在React Native App上尝试过
在这种情况下,我是否需要滴流ICE候选人和addIceCandidate
?如果我正确理解,则ICE候选人适合iceServer
。但是我的情况iceServer
为空(因为我仅处于离线状态,并且连接在同一本地wifi上):
const configuration = { iceServers: [{ urls: [] }] };
实际上,我交换要约和答案,但在setRemoteDescription
之后,connectionState
停留在checking
上。
您可以看到我的React组件:
constructor(props) {
super(props);
this.pc = new RTCPeerConnection(configuration);
}
state = initialState;
componentDidMount() {
const { pc } = this;
if (pc) {
this.setState({
peerCreated: true
});
}
this.setConnectionState();
pc.oniceconnectionstatechange = () => this.setConnectionState();
pc.onaddstream = ({ stream }) => {
if (stream) {
this.setState({
receiverVideoURL: stream.toURL()
});
}
};
pc.onnegotiationneeded = () => {
if (this.state.initiator) {
this.createOffer();
}
};
pc.onicecandidate = ({ candidate }) => {
if (candidate === null) {
const { offer } = this.state;
const field = !offer ? 'offer' : 'data';
setTimeout(() => {
alert('setTimeout started');
this.setState({
[field]: JSON.stringify(pc.localDescription)
});
}, 2000);
}
};
}
@autobind
setConnectionState() {
this.setState({
connectionState: this.pc.iceConnectionState
});
}
getUserMedia() {
MediaStreamTrack.getSources(() => {
getUserMedia(
{
audio: false,
video: true
},
this.getUserMediaSuccess,
this.getUserMediaError
);
});
}
@autobind
async getUserMediaSuccess(stream) {
const { pc } = this;
pc.addStream(stream);
await this.setState({ videoURL: stream.toURL() });
if (this.state.initiator) {
return this.createOffer();
}
return this.createAnswer();
}
getUserMediaError(error) {
console.log(error);
}
@autobind
logError(error) {
const errorArray = [...this.state.error, error];
return this.setState({
error: errorArray
});
}
/**
* Create offer
*
* @memberof HomeScreen
*/
@autobind
createOffer() {
const { pc } = this;
pc.createOffer()
.then(offer => pc.setLocalDescription(offer))
.then(() => {
this.setState({
offerCreated: true
});
})
.catch(this.logError);
}
/**
* Create anwser
*
* @memberof HomeScreen
*/
@autobind
async createAnswer() {
const { pc } = this;
const { data } = this.state;
if (data) {
const sd = new RTCSessionDescription(JSON.parse(data));
await this.setState({
offerImported: true
});
pc.setRemoteDescription(sd)
.then(() => pc.createAnswer())
.then(answer => pc.setLocalDescription(answer))
.then(() => {
this.setState({
answerCreated: true
});
})
.catch(this.logError);
}
}
@autobind
receiveAnswer() {
const { pc } = this;
const { data } = this.state;
const sd = new RTCSessionDescription(JSON.parse(data));
return pc
.setRemoteDescription(sd)
.then(() => {
this.setState({
answerImported: true
});
})
.catch(this.logError);
}
/**
* Start communication
*
* @param {boolean} [initiator=true]
* @returns
* @memberof HomeScreen
*/
@autobind
async start(initiator = true) {
if (!initiator) {
await this.setState({
initiator: false
});
}
return this.getUserMedia();
}
有人可以帮助我吗?
答案 0 :(得分:0)
在局域网上没有iceServers
很好,但是对等方仍必须交换至少一个候选者:其 host 候选者(基于其计算机的LAN IP地址)。
要么:
像往常一样使用onicecandidate
->信令-> addIceCandidate
来滴答候选人,或者
在交换pc.localDescription
之前等待ICE处理(几秒钟)。
您似乎正在尝试后者。这种方法之所以有效,是因为...
使用onicecandidate
的单个候选冰块的发信号(点滴)是旨在加速协商的优化。 setLocalDescription
成功后,浏览器的内部 ICE Agent 启动,将发现的ICE候选者插入localDescription
本身。等待几秒钟进行协商,就完全不需要滴答滴答:所有ICE候选人都将在录取通知书中并发送答复。
从您的onicecandidate
代码看来,您似乎已经在ICE完成后尝试收集localDescription
(并且您已经编写了使其可以在两端使用):
pc.onicecandidate = ({ candidate }) => {
if (!candidate) {
const { offer } = this.state;
const field = !offer ? 'offer' : 'data';
this.setState({
[field]: JSON.stringify(pc.localDescription)
});
}
};
在要约方,您已经正确注释了createOffer
中的等效代码:
pc.createOffer()
.then(offer => pc.setLocalDescription(offer))
.catch(this.logError);
// .then(() => {
// this.setState({
// offer: JSON.stringify(pc.localDescription)
// });
// });
但是在答复者那边,您还没有,这很可能是问题所在:
createAnswer() {
const { pc } = this;
const { data } = this.state;
if (data) {
const sd = new RTCSessionDescription(JSON.parse(data));
pc.setRemoteDescription(sd)
.then(() => pc.createAnswer())
.then(answer => pc.setLocalDescription(answer))
.then(() => {
this.setState({
offer: JSON.stringify(pc.localDescription)
});
})
.catch(this.logError);
}
}
这意味着它会在答答者的 ICE代理有时间将任何候选者插入答案之前,立即将答案发送回去。 这可能就是失败的原因。
在旁注::似乎没有什么可以等待getUserMedia
完成,因此,根据您{{1 }}函数,该函数无法将任何轨道或流添加到连接中。但是假设您只是在做数据通道,这应该可以使用我建议的修复程序。