Signatur
Beschreibung
createAnswer() ist die Gegenstück-Methode zu createOffer() in der Angebot/Antwort-Aushandlung von WebRTC. Der angerufene Peer erhält über einen eigenen Signalisierungskanal (z. B. WebSocket) das SDP-Angebot des Anrufers, setzt es mit setRemoteDescription() und erzeugt dann mit createAnswer() eine passende Antwort. Darin stehen die Codecs, Medienrichtungen, ICE-Zugangsdaten und DTLS-Fingerabdrücke, auf die sich die lokale Seite einlässt.
Die Methode gibt ein Promise zurück, das mit einem Objekt der Form { type: "answer", sdp: "..." } erfüllt wird. Diese Antwort wird nicht automatisch angewendet: Du musst sie selbst mit setLocalDescription() setzen und danach über die Signalisierung an den entfernten Peer schicken, der sie wiederum per setRemoteDescription() übernimmt.
In modernem Code kannst du createAnswer() oft ganz weglassen: Ein parameterloser Aufruf von setLocalDescription() im Zustand have-remote-offer erzeugt und setzt die Antwort automatisch (sogenannte „implizite Beschreibung“, Grundlage des Musters Perfect Negotiation). Den expliziten Aufruf brauchst du aber weiterhin, wenn du die SDP vor dem Setzen prüfen oder protokollieren willst.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $options | RTCAnswerOptions | object | {} | Optionales Einstellungsobjekt. Aktuell sind keine standardisierten Einträge definiert, die sich auf Antworten auswirken. Das veraltete Feld voiceActivityDetection wird von den meisten Browsern ignoriert. |
| $successCallback / failureCallback | Function | Veraltet: Alte Callback-Signatur createAnswer(successCallback, failureCallback). Nicht mehr verwenden, stattdessen das Promise nutzen. |
Rückgabewert
{ type: "answer", sdp: string } erfüllt wird. Wird mit einer InvalidStateError-DOMException zurückgewiesen, wenn kein Remote-Angebot gesetzt oder die Verbindung geschlossen ist, bzw. mit NotReadableError, wenn kein Zertifikat für DTLS erzeugt werden konnte.Beispiele
Eingehendes Angebot beantworten (klassischer Ablauf)
const pc = new RTCPeerConnection({
iceServers: [{ urls: 'stun:stun.l.google.com:19302' }]
});
const ws = new WebSocket('wss://signal.example.com');
// ICE-Kandidaten an die Gegenseite weiterreichen
pc.addEventListener('icecandidate', ({ candidate }) => {
if (candidate) ws.send(JSON.stringify({ type: 'candidate', candidate }));
});
ws.addEventListener('message', async (event) => {
const msg = JSON.parse(event.data);
if (msg.type === 'offer') {
// 1. Angebot des Anrufers übernehmen
await pc.setRemoteDescription(msg);
// 2. Eigene Medien hinzufügen (vor createAnswer!)
const stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: true });
stream.getTracks().forEach((track) => pc.addTrack(track, stream));
// 3. Antwort erzeugen, lokal setzen und zurückschicken
const answer = await pc.createAnswer();
await pc.setLocalDescription(answer);
ws.send(JSON.stringify(pc.localDescription));
console.log('Antwort gesendet:', answer.type);
} else if (msg.type === 'candidate') {
await pc.addIceCandidate(msg.candidate);
}
});
Vollständige Aushandlung lokal zwischen zwei Verbindungen
const caller = new RTCPeerConnection();
const callee = new RTCPeerConnection();
// Kandidaten direkt austauschen (ersetzt den Signalisierungsserver)
caller.onicecandidate = (e) => e.candidate && callee.addIceCandidate(e.candidate);
callee.onicecandidate = (e) => e.candidate && caller.addIceCandidate(e.candidate);
caller.createDataChannel('chat');
const offer = await caller.createOffer();
await caller.setLocalDescription(offer);
await callee.setRemoteDescription(offer);
const answer = await callee.createAnswer();
await callee.setLocalDescription(answer);
await caller.setRemoteDescription(answer);
console.log(callee.signalingState, caller.signalingState);
Fehler bei falschem Zustand abfangen
const pc = new RTCPeerConnection();
try {
// Kein Remote-Angebot gesetzt → ungültiger Zustand
await pc.createAnswer();
} catch (err) {
console.log(err.name);
}
Moderne Kurzform mit impliziter Beschreibung
const pc = new RTCPeerConnection();
const handleOffer = async (offer, sendToPeer) => {
await pc.setRemoteDescription(offer);
// Erzeugt und setzt die Antwort automatisch (ruft intern createAnswer auf)
await pc.setLocalDescription();
sendToPeer(pc.localDescription);
console.log(pc.localDescription.type);
};
// Wichtig · Fallstricke
- Reihenfolge beachten:
createAnswer()funktioniert nur im Signalisierungszustandhave-remote-offeroderhave-local-pranswer. Vorher musssetRemoteDescription()mit dem Angebot abgeschlossen sein (awaitnicht vergessen), sonst gibt es einenInvalidStateError. - Tracks vor der Antwort hinzufügen: Medien, die erst nach
createAnswer()peraddTrack()dazukommen, stehen nicht in der Antwort und lösen eine erneute Aushandlung (negotiationneeded) aus. - Antwort wird nicht automatisch gesetzt: Ohne
setLocalDescription(answer)startet kein ICE-Gathering und keine Verbindung. - SDP nicht manuell verändern („SDP-Munging“): Das ist fehleranfällig und browserabhängig. Codecs lieber mit
RTCRtpTransceiver.setCodecPreferences()und Richtungen übertransceiver.directionsteuern. - Sicherheit: Die SDP enthält ICE-Zugangsdaten und unter Umständen lokale IP-Adressen. Den Signalisierungskanal deshalb immer verschlüsselt (WSS/HTTPS) und authentifiziert betreiben.
getUserMedia()braucht einen sicheren Kontext. - Kompatibilität: Die Promise-Variante wird von allen modernen Browsern unterstützt. Die Callback-Signatur ist veraltet. Die parameterlose Kurzform
setLocalDescription()gibt es ab Chrome 80, Firefox 75 und Safari 14.1.