Start · Sprachen · JavaScript · Referenz · RTCPeerConnection.createOffer()

RTCPeerConnection.createOffer()

Methode

Erzeugt ein SDP-Angebot (<code>RTCSessionDescriptionInit</code> vom Typ <code>"offer"</code>), mit dem eine WebRTC-Verbindung zu einem Remote-Peer ausgehandelt wird.

seit JavaScript Baseline: alle modernen Browser (Chrome, Firefox, Kategorie: webapi

Signatur

RTCPeerConnection.prototype.createOffer(options)

Beschreibung

createOffer() ist der erste Schritt der WebRTC-Aushandlung (Offer/Answer-Modell) auf Seiten des Anrufers. Die Methode erstellt eine SDP-Beschreibung (Session Description Protocol) mit den Medienspuren, Datenkanälen, Codecs, Verschlüsselungsparametern (DTLS-Fingerabdruck) und ICE-Zugangsdaten, die der lokale Peer anbietet.

Das Ergebnis wird nicht automatisch angewendet: Man übergibt es anschließend an setLocalDescription() und schickt es über einen eigenen Signalisierungskanal (z. B. WebSocket) an die Gegenstelle. Diese ruft setRemoteDescription() und danach createAnswer() auf.

Vor dem Aufruf sollten die gewünschten Spuren per addTrack()/addTransceiver() bzw. Datenkanäle per createDataChannel() hinzugefügt sein – sonst enthält das Angebot keine Medienabschnitte. In moderner Praxis wird createOffer() meist innerhalb des negotiationneeded-Events aufgerufen; alternativ erledigt ein parameterloses setLocalDescription() das Erzeugen des Angebots implizit („Perfect Negotiation“).

Parameter

Name Typ Default Beschreibung
$options RTCOfferOptions {} Optionales Objekt. Wichtigste Eigenschaft: iceRestart (boolean, Standard false) – erzeugt neue ICE-Zugangsdaten und erzwingt einen ICE-Neustart, z. B. nach Netzwerkwechsel. Die veralteten Optionen offerToReceiveAudio/offerToReceiveVideo sollten durch addTransceiver(kind, { direction: 'recvonly' }) ersetzt werden.

Rückgabewert

Typ
Promise<RTCSessionDescriptionInit>
Beschreibung
Ein Promise, das mit einem Objekt { type: 'offer', sdp: '...' } erfüllt wird. Es wird abgelehnt mit InvalidStateError (Verbindung geschlossen), NotReadableError (keine Zertifikate/Ressourcen) oder OperationError.

Beispiele

Einfaches Angebot mit Kamera und Mikrofon

const pc = new RTCPeerConnection({
  iceServers: [{ urls: 'stun:stun.l.google.com:19302' }]
});

const stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: true });
stream.getTracks().forEach((track) => pc.addTrack(track, stream));

const offer = await pc.createOffer();
await pc.setLocalDescription(offer);

console.log(offer.type);
console.log(pc.signalingState);
// offer über den Signalisierungskanal an die Gegenstelle senden
// signaling.send(JSON.stringify({ description: pc.localDescription }));
offer have-local-offer

Aushandlung im negotiationneeded-Event per WebSocket

const signaling = new WebSocket('wss://example.com/signal');
const pc = new RTCPeerConnection();

pc.addEventListener('negotiationneeded', async () => {
  try {
    const offer = await pc.createOffer();
    await pc.setLocalDescription(offer);
    signaling.send(JSON.stringify({ description: pc.localDescription }));
  } catch (err) {
    console.error('Angebot fehlgeschlagen:', err);
  }
});

pc.addEventListener('icecandidate', ({ candidate }) => {
  if (candidate) signaling.send(JSON.stringify({ candidate }));
});

signaling.addEventListener('message', async ({ data }) => {
  const { description, candidate } = JSON.parse(data);
  if (description?.type === 'answer') await pc.setRemoteDescription(description);
  if (candidate) await pc.addIceCandidate(candidate);
});

// Löst negotiationneeded aus
pc.createDataChannel('chat');

ICE-Neustart nach Verbindungsabbruch

pc.addEventListener('iceconnectionstatechange', async () => {
  if (pc.iceConnectionState === 'failed') {
    // Alternativ: pc.restartIce() (modern)
    const offer = await pc.createOffer({ iceRestart: true });
    await pc.setLocalDescription(offer);
    signaling.send(JSON.stringify({ description: pc.localDescription }));
  }
});

Nur empfangen (ohne eigene Spuren)

const pc = new RTCPeerConnection();
// Moderner Ersatz für offerToReceiveVideo/Audio
pc.addTransceiver('video', { direction: 'recvonly' });
pc.addTransceiver('audio', { direction: 'recvonly' });

const offer = await pc.createOffer();
console.log(offer.sdp.includes('a=recvonly'));
true

// Wichtig · Fallstricke

  • Nicht automatisch angewendet: Ohne setLocalDescription(offer) startet weder die ICE-Kandidatensuche noch ändert sich der signalingState.
  • Glare: Rufen beide Peers gleichzeitig createOffer() auf, kollidieren die Angebote. Lösung: das „Perfect Negotiation“-Muster mit höflichem/unhöflichem Peer und Rollback.
  • Leeres Angebot: Ohne Spuren, Transceiver oder Datenkanal enthält das SDP keine m=-Zeilen – die Verbindung transportiert dann nichts.
  • Veraltete Signaturen: Die Callback-Variante createOffer(success, failure, options) und die Optionen offerToReceiveAudio/offerToReceiveVideo gelten als veraltet; Promise-Variante und addTransceiver() nutzen.
  • SDP-Manipulation („SDP munging“) vor setLocalDescription() ist fehleranfällig; für Codec-Auswahl besser RTCRtpTransceiver.setCodecPreferences() verwenden.
  • Sicherheit: Das SDP enthält ICE-Zugangsdaten und ggf. IP-Adressen. Den Signalisierungskanal über TLS (wss://) absichern; WebRTC ist nur in sicheren Kontexten (HTTPS) mit Mediengeräten nutzbar.
  • Nach pc.close() wirft der Aufruf einen InvalidStateError.