Blog

So implementieren Sie Videoanrufe in Next.js

· Andrij Tkachenko · 6 Min. Lesezeit

Zwei Browserfenster nebeneinander, in jedem die andere Person im Video und ein Chat daneben, verbunden durch ein Herz

Ein Videoanruf sieht aus wie ein einzelnes Feature, doch in einer Next.js-App sind es mehrere. Zwei Browser müssen einander finden und sich darauf einigen, wie sie sich verbinden. Bei der angerufenen Person muss es klingeln, auf welcher Seite sie auch gerade ist. Manche Netzwerke lassen nie eine direkte Verbindung durch. Und vor alledem muss Ihre App entscheiden, wer wen anrufen darf.

Die meisten Tutorials hören bei zwei Browsern auf einer Seite auf, die Offers von Hand austauschen. Dieser Beitrag geht jeden Teil durch, den eine echte App braucht, an einem vollständigen, funktionierenden Beispiel: der Dating-Website aus unserer Next.js-Anleitung, auf der zwei Mitglieder direkt in ihrem Chat ein Video-Date haben. Sie können es live ausprobieren: Wählen Sie, wer Sie sein möchten, öffnen Sie die Website dann in einem privaten Fenster, wählen Sie dort jemand anderen und rufen Sie an.

Was ein Videoanruf braucht

  • Signalisierung und Klingeln. Bevor es ein Bild gibt, tauschen die beiden Browser über einen Server Offers und Netzwerkkandidaten aus, und bei der angerufenen Person muss es in jedem Tab klingeln, den sie geöffnet hat. Dafür braucht es Verbindungen, die die ganze Zeit offen bleiben, und die kann eine Serverless-Funktion nicht halten (warum, und was sich dagegen tun lässt).
  • Ein Weg zwischen den Browsern. Direkt, wenn ihre Netzwerke es zulassen, und über ein Relay, wenn nicht (STUN- und TURN-Server erklärt).
  • Wer wen anrufen darf. Eine Regel auf Ihrem Server, mit den eigenen Daten Ihrer App, geprüft, bevor es bei irgendjemandem klingelt.
  • Kamera und Mikrofon. Eine einzige Abfrage des Browsers, wechselbare Geräte, Stummschalten und Kamera aus.
  • Ein Anrufbildschirm. Klingeln, Annehmen und Ablehnen, beide Videos, die Dauer, Auflegen.
  • Medienserver, sobald mehr als zwei Personen in einem Anruf sind (wie Gruppenanrufe funktionieren).

Jeden dieser Teile können Sie selbst auf Basis von WebRTC bauen. Oder Sie lassen eine Echtzeit-Cloud die Verbindungen, das Klingeln und die Relays betreiben, während Ihr Server den einen Teil behält, der wirklich Ihnen gehört: die Regel. Genau das macht die Dating-Website, mit WordPlus Cloud.

Das Beispiel: Video-Dates auf einer Dating-Website

Die Mitglieder der Website finden die Menschen, die sie suchen, sehen, wer online ist, chatten und verabreden sich zu einem Video-Date, bevor sie sich persönlich treffen. Es ist eine Next.js-App mit dem App Router und Postgres, und ihr Echtzeit-Teil besteht aus wenigen Dateien. Ihre Anrufe entstehen in vier Schritten.

1. Wer wen anrufen darf

Auf einer Dating-Website dürfen zwei Menschen miteinander sprechen, wenn beide einander suchen und keiner den anderen blockiert hat. Die Website hat diese Regel an einer Stelle, als canMeet, und einen Chat als Nummer mit seinen Mitgliedern. mayChat besagt, dass jemand einen Chat nutzen darf, wenn er zu dessen Mitgliedern gehört und jedes andere Mitglied weiterhin treffen darf. Anrufe werden über den Namen des Chat-Raums festgelegt, mit dieser Regel für beide Personen:

lib/realtime.ts
realtime.calls('chat:{id}', async (user, other, { id }) => {
    // The one asking: who calls, as they call, or who is called, as they answer.
    const userMayChat = await mayChat(user, id);
    // The other one of the call.
    const otherMayChat = await mayChat(other, id);
    return userMayChat && otherMayChat;
});

Die Regel läuft für die Person, die anruft, und noch einmal für die angerufene, wenn sie annimmt. Eine Blockierung wirkt also sofort: Ab dem Moment, in dem einer den anderen blockiert, sagt canMeet nein, und der nächste Anruf wird abgewiesen.

2. Wo die Regel läuft

Will eine Seite anrufen oder einen Anruf annehmen, fragt sie zuerst den eigenen Server der Website, in einem Route Handler:

app/api/realtime/calls/route.ts
import { currentUserId } from '@/lib/auth';
import { realtime } from '@/lib/realtime';

export async function POST(request: Request) {
    // JSON only, which a page of another site can't send here.
    if (!request.headers.get('content-type')?.includes('application/json')) {
        return new Response(null, { status: 415 });
    }
    const ask = await request.json();
    const user = await currentUserId();
    const pass = await realtime.authorizeCall(ask, user);
    return Response.json(pass);
}

currentUserId() ist die eigene Anmeldung der Website, die Session funktioniert hier also wie auf jeder Seite. Sagt die Regel ja, bekommt die Seite eine Freigabe, signiert mit dem Secret der App, und WordPlus Cloud lässt es damit bei der anderen Person klingeln. Die Route antwortet in einer einzigen kurzen Anfrage, und genau darin ist Serverless-Hosting gut.

3. Klingeln auf jeder Seite

Das Layout der Seiten für angemeldete Nutzer baut die Verbindung einmal auf und rendert den Anrufbildschirm neben den Seiten:

app/(app)/screens.tsx
'use client';

import '@wordplus/realtime-ui/calls.css';
import '@wordplus/realtime-ui/chat.css';
import { useRealtime } from '@wordplus/realtime/react';
import { CallScreen } from '@wordplus/realtime-ui/react';

export function Screens() {
    return <CallScreen rt={useRealtime()} />;
}

<Screens /> steht in app/(app)/layout.tsx innerhalb von RealtimeProvider. Ein Anruf klingelt also auf jeder Seite, die ein Mitglied geöffnet hat, in jedem Tab und auf jedem Gerät, und hört überall auf zu klingeln, sobald er auf einem davon angenommen wird. Der Anrufbildschirm ist der aus Better Messages, gerendert im eigenen React-Baum der Website, sodass deren CSS ihn erreicht.

4. Der Anruf in seinem Chat

Auf einer Dating-Website gehört der Anruf zum Gespräch. Der Chat auf der Seite eines Mitglieds zeigt seinen Anruf direkt neben den Nachrichten:

app/(app)/members/[id]/chat.tsx
<Chat
    rt={useRealtime()}
    room={room}
    to={to}
    name={name}
    avatar={avatar}
    call
    startCall={start}
/>

Seine obere Leiste zeigt die andere Person mit ihren Anruf-Buttons. Ein Video-Button auf einer Karte im Katalog öffnet den Chat und startet dabei gleich den Anruf (startCall), sodass das Date dort beginnt, wo die beiden sich auch schreiben können.

Der Chat ist nur einer von mehreren Orten für einen Anruf. Ein CallButton startet einen Anruf von überall aus, ein CallSpot zeigt Anrufe in einem Bereich der Seite, den Sie wählen, und useCalls gibt Ihnen jeden Anruf, damit Sie Ihren eigenen Bildschirm gestalten können.

Was dazugehört

  • Eine einzige Abfrage. Der Browser fragt einmal nach Kamera und Mikrofon zusammen.
  • Er kommt immer durch. Ein Anruf zwischen zwei Personen läuft direkt zwischen ihren Browsern, wenn das geht, über Relays auf den Ports, die jedes Netzwerk offen lässt, wenn nicht, und über Medienserver, wenn selbst die nicht durchkommen. Er wechselt zwischen diesen Wegen, ohne aufzulegen, und ist auf jedem davon verschlüsselt.
  • Er bleibt bestehen. Die Verbindung, und mit ihr der Anruf, bleibt erhalten, während das Mitglied zwischen den Seiten der Website wechselt.
  • Gruppenanrufe auf derselben API. Ein Raum mit mehr Personen läuft auf Medienservern, mit eigenem Button und eigenem Bildschirm für Gruppenanrufe (Videoanrufe für Apps).

Erst ausprobieren, dann selbst bauen

In der Live-Demo gehören die elf Personen einen Tag lang nur Ihnen: Was Sie dort schreiben und wen Sie anrufen, sieht sonst niemand. Unter jeder Seite zeigt How this page works Schritt für Schritt den Code, der sie erzeugt, mit den Werten der Seite, die Sie gerade geöffnet haben.

Die Anleitung zur Dating-Website baut die ganze Website auf, mit jeder Datei ihres Echtzeit-Teils in voller Länge, und Anrufe in Next.js zeigt jeden Weg, in Ihrer eigenen App einen Anruf zu starten.

Für WordPlus Cloud for Apps gelten Preise nach gleichzeitigen Verbindungen, ab 1.000, monatlich oder jährlich abgerechnet. Sehen Sie, was sie für Apps leistet, oder werfen Sie einen Blick auf die Preise für Apps. Fragen zu Ihrer Integration? Schreiben Sie uns.