web-dev-qa-db-fra.com

Échec de la définition de la réponse distante SDP: appelé intestinal State: Stable

J'essaie d'écrire une application WebRTC à l'aide de socket.io.

Le serveur de signalisation est écrit en python et ressemble à ceci.

import socketio
import uvicorn
from starlette.applications import Starlette

ROOM = 'room'


sio = socketio.AsyncServer(async_mode='asgi', cors_allowed_origins='*')
star_app = Starlette(debug=True)
app = socketio.ASGIApp(sio, star_app)


@sio.event
async def connect(sid, environ):
    await sio.emit('ready', room=ROOM, skip_sid=sid)
    sio.enter_room(sid, ROOM)


@sio.event
async def data(sid, data):
    await sio.emit('data', data, room=ROOM, skip_sid=sid)


@sio.event
async def disconnect(sid):
    sio.leave_room(sid, ROOM)


if __name__ == '__main__':
    uvicorn.run(app, Host='0.0.0.0', port=8003)

Le côté client ressemble à ceci

<script>
    const SIGNALING_SERVER_URL = 'http://127.0.0.1:8003?session_id=1';
    // WebRTC config: you don't have to change this for the example to work
    // If you are testing on localhost, you can just use PC_CONFIG = {}
    const PC_CONFIG = {};

    // Signaling methods
    let socket = io(SIGNALING_SERVER_URL, {autoConnect: false});

    socket.on('data', (data) => {
        console.log('Data received: ', data);
        handleSignalingData(data);
    });

    socket.on('ready', () => {
        console.log('Ready');
        // Connection with signaling server is ready, and so is local stream
        createPeerConnection();
        sendOffer();
    });

    let sendData = (data) => {
        socket.emit('data', data);
    };

    // WebRTC methods
    let pc;
    let localStream;
    let remoteStreamElement = document.querySelector('#remoteStream');

    let getLocalStream = () => {
        navigator.mediaDevices.getUserMedia({audio: true, video: true})
            .then((stream) => {
                console.log('Stream found');
                localStream = stream;
                // Connect after making sure that local stream is availble
                socket.connect();
            })
            .catch(error => {
                console.error('Stream not found: ', error);
            });
    }

    let createPeerConnection = () => {
        try {
            pc = new RTCPeerConnection(PC_CONFIG);
            pc.onicecandidate = onIceCandidate;
            pc.onaddstream = onAddStream;
            pc.addStream(localStream);
            console.log('PeerConnection created');
        } catch (error) {
            console.error('PeerConnection failed: ', error);
        }
    };

    let sendOffer = () => {
        console.log('Send offer');
        pc.createOffer().then(
            setAndSendLocalDescription,
            (error) => {
                console.error('Send offer failed: ', error);
            }
        );
    };

    let sendAnswer = () => {
        console.log('Send answer');
        pc.createAnswer().then(
            setAndSendLocalDescription,
            (error) => {
                console.error('Send answer failed: ', error);
            }
        );
    };

    let setAndSendLocalDescription = (sessionDescription) => {
        pc.setLocalDescription(sessionDescription);
        console.log('Local description set');
        sendData(sessionDescription);
    };

    let onIceCandidate = (event) => {
        if (event.candidate) {
            console.log('ICE candidate');
            sendData({
                type: 'candidate',
                candidate: event.candidate
            });
        }
    };

    let onAddStream = (event) => {
        console.log('Add stream');
        remoteStreamElement.srcObject = event.stream;
    };

    let handleSignalingData = (data) => {
        // let msg = JSON.parse(data);
        switch (data.type) {
            case 'offer':
                createPeerConnection();
                pc.setRemoteDescription(new RTCSessionDescription(data));
                sendAnswer();
                break;
            case 'answer':
                pc.setRemoteDescription(new RTCSessionDescription(data));
                break;
            case 'candidate':
                pc.addIceCandidate(new RTCIceCandidate(data.candidate));
                break;
        }
    };

    // Start connection
    getLocalStream();
</script>

Aussi j'utilise ce code pour le client comme socket.io

https://github.com/socketio/socket.io/blob/master/Client-Dist/socket.io.js

Lorsque deux personnes sont dans la connexion, tout fonctionne bien. Mais dès qu'un troisième utilisateur tente de les connecter, le streaming s'arrête avec une erreur

Non capturé (en promesse) Domexception: Échec de l'exécution de "SETREMOteDescription" sur "RTCPECONNection": Échec de la définition de la réponse distante SDP: appelé insuffisance de l'état: Stable

Je n'ai pas beaucoup de connaissances de javascript, alors j'ai besoin de votre aide. Merci.

P.S. Je vois cette erreur dans tous les navigateurs.

Voir ce référentiel

https://github.com/pfertyk/webrtc-working-example

Voir ces instructions

https://pfertyk.me/2020/03/webrtc-a-working-example/

4
unknown

J'ai répondu à cette question ci-dessus en détail de Pourquoi Vous avez ce problème. Mais on dirait que ce que vous cherchez vraiment est un exemple de code de travail sur comment Pour le réparer ... Alors voici, vous allez:

index.html: Mettez légèrement à jour la page HTML, alors nous avons maintenant une DIV que nous appendons des vidéos distantes entrantes.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>WebRTC working example</title>
</head>
<body>
    <div id="remoteStreams"></div>
    <script src="socket.io.js"></script>
    <script src="main.js"></script>
</body>
</html>

app.py: des données mises à jour et des gestionnaires d'événements prêts un peu afin que nous émettions correctement l'ID de prise vers d'autres pairs.

import socketio
import uvicorn
from starlette.applications import Starlette

ROOM = 'room'

sio = socketio.AsyncServer(async_mode='asgi', cors_allowed_origins='*')
star_app = Starlette(debug=True)
app = socketio.ASGIApp(sio, star_app)


@sio.event
async def connect(sid, environ):
    await sio.emit('ready', {'sid': sid}, room=ROOM, skip_sid=sid)
    sio.enter_room(sid, ROOM)


@sio.event
async def data(sid, data):
    peerToSend = None
    if 'sid' in data:
      peerToSend = data['sid']
    data['sid'] = sid
    await sio.emit('data', data, room=peerToSend if peerToSend else ROOM, skip_sid=sid)


@sio.event
async def disconnect(sid):
    sio.leave_room(sid, ROOM)


if __name__ == '__main__':
    uvicorn.run(app, Host='localhost', port=8003)

main.js: Création de cet objet de pairs pour mapper des identifiants de prise sur RTCPeercronnements et mis à jour certaines des fonctions à utiliser au lieu de la variable PC.

const SIGNALING_SERVER_URL = 'ws://127.0.0.1:8003';
// WebRTC config: you don't have to change this for the example to work
// If you are testing on localhost, you can just use PC_CONFIG = {}
const PC_CONFIG = {};

// Signaling methods
let socket = io(SIGNALING_SERVER_URL, {autoConnect: false});

socket.on('data', (data) => {
    console.log('Data received: ', data);
    handleSignalingData(data);
});

socket.on('ready', (msg) => {
    console.log('Ready');
    // Connection with signaling server is ready, and so is local stream
    peers[msg.sid] = createPeerConnection();
    sendOffer(msg.sid);
    addPendingCandidates(msg.sid);
});

let sendData = (data) => {
    socket.emit('data', data);
};

// WebRTC methods
let peers = {}
let pendingCandidates = {}
let localStream;

let getLocalStream = () => {
    navigator.mediaDevices.getUserMedia({audio: true, video: true})
        .then((stream) => {
            console.log('Stream found');
            localStream = stream;
            // Connect after making sure thzat local stream is availble
            socket.connect();
        })
        .catch(error => {
            console.error('Stream not found: ', error);
        });
}

let createPeerConnection = () => {
    const pc = new RTCPeerConnection(PC_CONFIG);
    pc.onicecandidate = onIceCandidate;
    pc.onaddstream = onAddStream;
    pc.addStream(localStream);
    console.log('PeerConnection created');
    return pc;
};

let sendOffer = (sid) => {
    console.log('Send offer');
    peers[sid].createOffer().then(
        (sdp) => setAndSendLocalDescription(sid, sdp),
        (error) => {
            console.error('Send offer failed: ', error);
        }
    );
};

let sendAnswer = (sid) => {
    console.log('Send answer');
    peers[sid].createAnswer().then(
        (sdp) => setAndSendLocalDescription(sid, sdp),
        (error) => {
            console.error('Send answer failed: ', error);
        }
    );
};

let setAndSendLocalDescription = (sid, sessionDescription) => {
    peers[sid].setLocalDescription(sessionDescription);
    console.log('Local description set');
    sendData({sid, type: sessionDescription.type, sdp: sessionDescription.sdp});
};

let onIceCandidate = (event) => {
    if (event.candidate) {
        console.log('ICE candidate');
        sendData({
            type: 'candidate',
            candidate: event.candidate
        });
    }
};

let onAddStream = (event) => {
    console.log('Add stream');
    const newRemoteStreamElem = document.createElement('video');
    newRemoteStreamElem.autoplay = true;
    newRemoteStreamElem.srcObject = event.stream;
    document.querySelector('#remoteStreams').appendChild(newRemoteStreamElem);
};

let addPendingCandidates = (sid) => {
    if (sid in pendingCandidates) {
        pendingCandidates[sid].forEach(candidate => {
            peers[sid].addIceCandidate(new RTCIceCandidate(candidate))
        });
    }
}

let handleSignalingData = (data) => {
    // let msg = JSON.parse(data);
    console.log(data)
    const sid = data.sid;
    delete data.sid;
    switch (data.type) {
        case 'offer':
            peers[sid] = createPeerConnection();
            peers[sid].setRemoteDescription(new RTCSessionDescription(data));
            sendAnswer(sid);
            addPendingCandidates(sid);
            break;
        case 'answer':
            peers[sid].setRemoteDescription(new RTCSessionDescription(data));
            break;
        case 'candidate':
            if (sid in peers) {
                peers[sid].addIceCandidate(new RTCIceCandidate(data.candidate));
            } else {
                if (!(sid in pendingCandidates)) {
                    pendingCandidates[sid] = [];
                }
                pendingCandidates[sid].Push(data.candidate)
            }
            break;
    }
};

// Start connection
getLocalStream();

J'ai essayé de changer le moins possible votre code, de sorte que vous devriez être capable de copier-coller et de le faire fonctionner.

Voici mon code de travail : https://github.com/lnogueir/webrtc-socketo

Si vous avez des problèmes qui le courent, laissez-moi savoir ou ouvrir un problème là-bas et je ferai de mon mieux pour vous aider.

1
lnogueir

En un mot, vous devez vous assurer que vous avez une peerconnection par pair et que votre protocole de signalisation permet de différencier qui vous a envoyé une offre ou une réponse.

Pour un boîtier de deux connexions, reportez-vous à l'échantillon canonique https://webrtc.github.io/samples/src/content/peconnection/multiple/

Pour généraliser cela dans plusieurs pairs avec socket.IO Le package simpleWebRTC (maintenant obsolète et homologué) pourrait être utile: https://github.com/simplewebrtc/simplewebrtc

La bibliothèque simple pae fournit une fonctionnalité similaire, mais vous devrez intégrer vous-même SocketoIo.

0
Philipp Hancke