Salut les amis,
Je recherche à créer une connection entre deux téléphones en WebRTC et dans un contexte offline (aucun connexion à l'extérieur, mais les deux téléphones sont sur le même réseau local, connecté en wifi). Ce schéma ce déroule dans une app React Native.
Etant donné que je suis en local uniquement, je n'ai pas besoin d'utiliser de serveur ICE.
Pour l'instant je cherche à effectuer la base : échange de l'offre et de la réponse. Voici ce que donne mon component React :
const configuration = { iceServers: [{ urls: [] }] };
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(() => {
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 InitiatorScreen
*/
@autobind
createOffer() {
const { pc } = this;
pc.createOffer()
.then(offer => pc.setLocalDescription(offer))
.then(() => {
this.setState({
offerCreated: true
});
})
.catch(this.logError);
}
/**
* Create anwser
*
* @memberof InitiatorScreen
*/
@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 InitiatorScreen
*/
@autobind
async start(initiator = this.state.initiator) {
if (initiator) {
await this.setState({
initiator: true
});
}
return this.getUserMedia();
}
Une fois l'offre et la réponse échanger sur les deux téléphones (pour l'instant, l'échange est via slack pour les tests), le connectionState
reste sur checking
, uniquement du côté de l'initiator, celui qui créer l'offre.
Du côté answer, le state ne bouge jamais, il reste à new
(celui par défaut).
Je ne comprends pas pourquoi le connectionState
de mon answer ne change jamais. L'offre est pourquoi gérée, puis importé dans new RTCSessionDescription(JSON.parse(data));
.
Auriez-vous une petite idée ? J'imagine qu'il y a quelque chose que je dois mal faire :D
Merci à vous !
EDIT :
J'ai découvert qu'une fois l'offre importé du côté answer
, pc.signalingState
passe de stable
à have-remote-offer
pendant 1 seconde (vraiment rapide), puis revient à stable
. Du coup, cela cofirme qu'il y a un problème soit du côté de l'offre générée, soit du côté de la réponse. Mais comment savoir ? :/
Les deux téléphones sont bien sur le même wifi, en partage de connexion.