Blog

Jak wdrożyć transmisje na żywo w Next.js

· Andrij Tkachenko · 7 min czytania

Okno przeglądarki z osobą na wideo i gościem obok, które nadaje do czterech mniejszych okien, w jednym ktoś podnosi rękę, w innym ktoś pisze

Transmisja na żywo wygląda jak jedna funkcja, a w aplikacji Next.js to kilka. Ktoś wchodzi na żywo z przeglądarki albo z enkodera, takiego jak OBS. Wszyscy, którzy mogą oglądać, widzą go i słyszą w tej samej chwili, na tyle blisko, żeby zadać pytanie i dostać odpowiedź. Widzowie piszą na czacie, reagują, a niektórzy wchodzą na scenę z własną kamerą. A zanim to wszystko się stanie, Twoja aplikacja musi zdecydować, kto nadaje, kto ogląda i kto dowie się, że transmisja się zaczęła.

Ten wpis omawia każdą część, której potrzebuje prawdziwa aplikacja, na pełnym, działającym przykładzie: Live Lessons, serwisie z lekcjami na żywo z naszego przewodnika po Next.js, gdzie nauczyciele gotowania, muzyki i fitnessu prowadzą lekcje na żywo, a uczniowie oglądają, pytają i wchodzą na scenę. Możesz wypróbować go na żywo: bądź Mią i wejdź na żywo z jej studia, a potem otwórz jej lekcję w oknie prywatnym jako Ava, jedna z jej uczennic, żeby oglądać i podnieść rękę.

Czego potrzebuje transmisja na żywo

  • Obraz do środka. Kamera i mikrofon z przeglądarki, udostępniony ekran z dźwiękiem albo enkoder przez WHIP, do scen i kilku kamer.
  • Obraz na zewnątrz, do wielu osób naraz. Transmisja, której widzowie odpowiadają, idzie przez WebRTC, tak jak rozmowy, przez serwery mediów, które przekazują ją każdemu widzowi (jak działają serwery mediów). Jej połączenia są otwarte przez całą transmisję, a tego funkcja serverless nie utrzyma (dlaczego i co z tym zrobić).
  • Kto nadaje, a kto ogląda. Reguła na Twoim serwerze, z Twoimi danymi: nauczyciel nadaje, uczniowie kursu oglądają, a otwartą lekcję może obejrzeć każdy.
  • Studio i odtwarzacz. Wejście na żywo i zakończenie, kamera i ekran, czat, reakcje i liczba widzów.
  • Scena. Widz podnosi rękę, nauczyciel go zaprasza i wszyscy widzą go obok nauczyciela.
  • Wiedza, co jest na żywo. Listy trwających transmisji, które aktualizują się same, i powiadomienie dla tych, którzy chcą wiedzieć.

Każdą z tych części możesz zbudować sam, na WebRTC i własnych serwerach mediów. Albo oddajesz media, studio i odtwarzacz chmurze czasu rzeczywistego, a Twój serwer zachowuje jedyną część, która naprawdę należy do Ciebie: regułę. Tak działa Live Lessons, z WordPlus Cloud.

Przykład: lekcje na żywo

Nauczyciele mają kursy, każdy ze swoimi lekcjami. Lekcja jest otwarta dla wszystkich albo tylko dla uczniów kursu. Nauczyciel wchodzi na żywo w lekcji ze swojego studia, a uczniowie oglądają na stronie lekcji. To aplikacja Next.js z App Routerem, Better Auth i Postgresem, a jej część czasu rzeczywistego to kilka plików. Pięć kroków tworzy jej transmisje.

1. Kto może oglądać

Każda lekcja ma transmisję nazwaną od swojego numeru: lesson:7. Serwis deklaruje je raz, według tego wzorca, razem z regułą:

lib/realtime.ts
realtime.streams(
    'lesson:{id}',
    async (user, { id }) => {
        const lesson = await lessonOf(id);
        if (!lesson) return false;
        const me = user === null ? null : String(user);
        if (me === lesson.teacher) return { role: 'host', quality: '1080p' };
        return (await mayWatch(me, lesson)) ? 'viewer' : false;
    },
    { stage: true, reactions: true },
);

Nauczyciel jest gospodarzem, z obrazem do 1080p. Kogo wpuści mayWatch, ten ogląda, a user to null dla gościa, który się nie zalogował, więc otwarta lekcja nie wymaga konta. Wszyscy inni dostają false. Ustawienia pozwalają na gości na scenie i reakcje. Strona lekcji pyta to samo mayWatch, więc strona i transmisja nigdy się nie rozjeżdżają.

2. Gdzie działa reguła

Gdy strona chce wejść na żywo albo oglądać, najpierw pyta własny serwer serwisu, w route handlerze:

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);
}

To ta sama trasa, przez którą przechodzą rozmowy wideo serwisu randkowego (jak wdrożyć rozmowy wideo w Next.js). Reguła działa z sesją serwisu, a strona dostaje przepustkę do transmisji, podpisaną sekretem aplikacji, albo powód odmowy. Trasa odpowiada w jednym krótkim żądaniu, a w tym hosting serverless sprawdza się najlepiej.

3. Studio nauczyciela

Nauczyciel wchodzi na żywo z własnej strony, ze studiem WordPlus Cloud w środku:

app/(site)/studio/[id]/page.tsx
<LiveStream
    name={roomOf(lesson.id)}
    view="studio-page"
    title={`${lesson.position}. ${lesson.title}`}
    url={`/lessons/${lesson.id}`}
    backUrl="/studio"
/>

Studio wypełnia wysokość, którą daje mu strona: obraz tak duży, jak się mieści, pod nim kamera, ekran, OBS i Go live, a obok, w kartach, czat, goście z podniesionymi rękami i transmisja z linkiem do lekcji do udostępnienia. Wcześniej strona sprawdza, czy pytająca osoba uczy tej lekcji, i każdego innego odsyła do lekcji. Po zalogowaniu nauczyciel trafia na listę swoich lekcji, każda o jedno kliknięcie od wejścia na żywo.

4. Strona lekcji

Uczniowie oglądają tam, gdzie jest lekcja:

app/(site)/lessons/[id]/page.tsx
<LiveStream
    name={roomOf(lesson.id)}
    title={lesson.title}
    loginUrl="/signin"
    studioUrl={teaches ? `/studio/${lesson.id}` : undefined}
/>

Jeden element rysuje odtwarzacz i jego czat. Zanim lekcja się zacznie, mówi o tym, a gra od chwili, gdy nauczyciel wejdzie na żywo, bez przeładowania. Zalogowany uczeń może pisać, reagować i nacisnąć Ask to join on stage. Gość ogląda i czyta, z drogą do zalogowania. Nauczyciel widzi nad odtwarzaczem drogę do swojego studia.

5. Co jest na żywo, zawsze aktualne

WordPlus Cloud wie, które transmisje trwają i ile osób ogląda każdą, więc serwis nie prowadzi do tego żadnej tabeli:

lib/realtime.ts
export async function liveNow(): Promise<Map<string, number>> {
    const live = new Map<string, number>();
    for (const stream of await realtime.listStreams()) {
        const lesson = lessonIn(stream.name);
        if (lesson) live.set(lesson, stream.viewers);
    }
    return live;
}

Strona główna pokazuje na tej podstawie lekcje, które są teraz na żywo. Żeby ta lista była aktualna także na już otwartych stronach, WordPlus Cloud wysyła do serwisu webhook, gdy transmisja się zaczyna i gdy się kończy, a serwis przekazuje go do pokoju, do którego dołącza każda strona, także strona gościa:

lib/realtime.ts
realtime.room('live', { public: true });

realtime.on('stream.ended', async ({ stream }) => {
    if (lessonIn(stream)) await realtime.to('live').emit('changed');
});
app/(site)/live.tsx
export function LiveNow() {
    const router = useRouter();
    useRoom('live', { changed: () => router.refresh() });
    return null;
}

LiveNow jest w layoucie, więc każda strona ponownie pyta serwer o swoje listy, bez przeładowania. Webhook stream.live robi to samo i dodatkowo powiadamia uczniów kursu, a przy otwartej lekcji także obserwujących nauczyciela, na dowolnej stronie, na której są: „Mia is live: Your first tune”.

Scena bez Twojego kodu

stage: true w regule to wszystko, czego potrzebuje scena. Gdy lekcja trwa, uczeń podnosi rękę w odtwarzaczu, nauczyciel widzi to w Guests i naciska Invite, a gdy uczeń przyjmie zaproszenie, wszyscy widzą go obok nauczyciela, z kamerą i mikrofonem. Nauczyciel może go wyciszyć albo zdjąć ze sceny, a uczeń może zejść sam i wrócić do oglądania bez przeładowania.

Co jest w zestawie

  • Z przeglądarki albo z OBS. Studio wchodzi na żywo z kamerą, z ekranem i jego dźwiękiem albo z jednym i drugim, albo przyjmuje OBS lub dowolny enkoder przez WHIP.
  • W tej samej chwili. Transmisja idzie przez WebRTC, więc pytanie na czacie trafia do nauczyciela, gdy ten jest jeszcze przy temacie.
  • Zadbany czat. Moderatorzy przypinają, usuwają, wyciszają na chwilę, usuwają z transmisji i blokują, a reguła może uczynić moderatorem asystenta nauczyciela: { role: 'viewer', mod: true }.
  • Odbieranie dostępu. Gdy uczeń opuszcza kurs, serwis usuwa go z trwających lekcji tego kursu przez realtime.stream(name).kick(), a od tej pory reguła odmawia.

Wypróbuj, a potem zbuduj własny

W demo na żywo nauczyciele i uczniowie należą tylko do Ciebie przez dobę, więc nikt inny nie widzi, co tam nadajesz ani piszesz. Pod każdą stroną panel How this page works pokazuje kod, który ją tworzy, krok po kroku, z wartościami strony, którą masz otwartą.

Przewodnik po Live Lessons buduje cały serwis, z każdym plikiem jego części czasu rzeczywistego w całości, a transmisje na żywo w Next.js pokazują wszystkie sposoby umieszczenia transmisji we własnej aplikacji, także z własnym interfejsem.

WordPlus Cloud for Apps ma cenę według jednoczesnych połączeń, od 1000 wzwyż, rozliczaną miesięcznie lub rocznie. Przy 1000 połączeń transmisje na żywo nie są naliczane do 50 widzów jednocześnie, a każde kolejne 1000 połączeń dodaje 20. Powyżej każdy dodatkowy widz zużywa kredyt na minutę, liczony tylko wtedy, gdy transmisja trwa. Zobacz transmisje na żywo dla aplikacji albo cennik dla aplikacji. Chcesz zapytać o swoją integrację? Napisz do nas.