Blog

Kako implementirati prijenos uživo u Next.js-u

· Andrij Tkachenko · 7 min čitanja

Prozor preglednika s osobom na videu i gostom pokraj nje, koji emitira u četiri manja prozora, u jednom netko diže ruku, u drugom netko piše

Prijenos uživo izgleda kao jedna značajka, a u Next.js aplikaciji riječ je o nekoliko njih. Netko izlazi u eter iz preglednika ili iz enkodera kao što je OBS. Svi koji smiju gledati vide ga i čuju u istom trenutku, dovoljno blizu da postave pitanje i dobiju odgovor. Gledatelji pišu u chatu, reagiraju, a neki izlaze na pozornicu s vlastitom kamerom. A prije svega toga vaša aplikacija mora odlučiti tko emitira, tko gleda i tko saznaje da je prijenos počeo.

Ovaj članak prolazi kroz svaki dio koji prava aplikacija treba, na potpunom primjeru koji radi: Live Lessons, stranici za satove uživo iz našeg vodiča za Next.js, na kojoj nastavnici kuhanja, glazbe i fitnessa izlaze u eter u svojim satovima, a učenici gledaju, pitaju i izlaze na pozornicu. Možete ga isprobati uživo: budite Mia i izađite u eter iz njezina studija, a zatim otvorite njezin sat u privatnom prozoru kao Ava, jedna od njezinih učenica, da gledate i dignete ruku.

Što prijenos uživo treba

  • Slika unutra. Kamera i mikrofon iz preglednika, dijeljeni zaslon sa zvukom ili enkoder preko WHIP-a, za scene i više kamera.
  • Slika van, mnogima, u istom trenutku. Prijenos čija publika odgovara ide preko WebRTC-a, kao i pozivi, kroz medijske poslužitelje koji ga prosljeđuju svakom gledatelju (kako rade medijski poslužitelji). Njegove veze ostaju otvorene tijekom cijelog prijenosa, a to serverless funkcija ne može održati (zašto i što učiniti).
  • Tko emitira, a tko gleda. Pravilo na vašem poslužitelju, s vašim podacima: nastavnik emitira, učenici tečaja gledaju, a otvoreni sat smije gledati svatko.
  • Studio i player. Izlazak u eter i završetak, kamera i zaslon, chat, reakcije i koliko ljudi gleda.
  • Pozornica. Gledatelj digne ruku, nastavnik ga pozove i svi ga vide pokraj nastavnika.
  • Znati što je uživo. Popisi prijenosa koji su upravo uživo i koji se sami ažuriraju, te obavijest onima koji to žele znati.

Svaki od tih dijelova možete izgraditi sami, na WebRTC-u i vlastitim medijskim poslužiteljima. Ili pustite da oblak za rad u stvarnom vremenu vodi medije, studio i player, dok vaš poslužitelj zadržava jedini dio koji je stvarno vaš: pravilo. To radi Live Lessons, preko WordPlus Clouda.

Primjer: satovi uživo

Nastavnici imaju tečajeve, svaki sa svojim satovima. Sat je otvoren svima ili samo učenicima svojeg tečaja. Nastavnik izlazi u eter u satu iz svojeg studija, a učenici gledaju na stranici sata. To je Next.js aplikacija s App Routerom, Better Authom i Postgresom, a njezin dio za rad u stvarnom vremenu stane u nekoliko datoteka. Pet koraka čini njezine prijenose.

1. Tko smije gledati

Svaki sat ima prijenos nazvan po svojem broju: lesson:7. Stranica ih deklarira jednom, po tom uzorku, s pravilom:

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

Nastavnik je domaćin, sa slikom do 1080p. Koga mayWatch pusti, taj gleda, a user je null za posjetitelja koji nije prijavljen, pa otvoreni sat ne traži račun. Svi ostali dobivaju false. Postavke dopuštaju goste na pozornici i reakcije. Stranica sata pita isti mayWatch, pa se stranica i prijenos nikad ne razilaze.

2. Gdje se pravilo izvodi

Kad stranica želi izaći u eter ili gledati, najprije pita vlastiti poslužitelj stranice, u route handleru:

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 je ista ruta kroz koju prolaze videopozivi stranice za upoznavanje (kako implementirati videopozive u Next.js-u). Pravilo se izvodi sa sesijom stranice, a stranica dobiva propusnicu za prijenos, potpisanu tajnom aplikacije, ili razlog zašto ne. Ruta odgovara jednim kratkim zahtjevom, upravo onim u čemu je serverless hosting dobar.

3. Nastavnikov studio

Nastavnik izlazi u eter s vlastite stranice, sa studiom WordPlus Clouda u njoj:

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 ispunjava visinu koju mu stranica daje: slika onoliko velika koliko stane, ispod nje kamera, zaslon, OBS i Go live, a pokraj nje, u karticama, chat, gosti s podignutim rukama i prijenos s poveznicom sata za dijeljenje. Prije toga stranica provjerava predaje li osoba koja pita taj sat, a svakoga drugoga šalje na sat. Nakon prijave nastavnik dolazi na popis svojih satova, svaki jedan klik od izlaska u eter.

4. Stranica sata

Učenici gledaju ondje gdje je sat:

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

Jedan element crta player i njegov chat. Prije početka sata to kaže, a reproducira se čim nastavnik izađe u eter, bez ponovnog učitavanja. Prijavljeni učenik može pisati, reagirati i pritisnuti Ask to join on stage. Posjetitelj gleda i čita, s putem do prijave. Nastavnik iznad playera vidi put do svojeg studija.

5. Što je uživo, uvijek ažurno

WordPlus Cloud zna koji su prijenosi uživo i koliko ljudi gleda svaki, pa stranica za to ne vodi nikakvu tablicu:

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

Početna stranica iz toga prikazuje satove koji su upravo uživo. Da bi taj popis ostao ažuran i na već otvorenim stranicama, WordPlus Cloud šalje stranici webhook kad prijenos počne i kad završi, a stranica ga prosljeđuje u sobu kojoj se pridružuje svaka stranica, pa i posjetiteljeva:

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 je u layoutu, pa svaka stranica ponovno traži od poslužitelja svoje popise, bez ponovnog učitavanja. Webhook stream.live radi isto i uz to obavještava učenike tečaja, a kod otvorenog sata i pratitelje njegova nastavnika, na kojoj god stranici bili: „Mia is live: Your first tune”.

Pozornica, bez vašeg koda

stage: true u pravilu je sve što pozornica treba. Dok je sat uživo, učenik digne ruku u playeru, nastavnik to vidi pod Guests i pritisne Invite, a čim učenik prihvati, svi ga vide pokraj nastavnika, s kamerom i mikrofonom. Nastavnik ga može utišati ili skinuti s pozornice, a učenik je može napustiti sam i vratiti se gledanju bez ponovnog učitavanja.

Što dolazi uz to

  • Iz preglednika ili s OBS-om. Studio izlazi u eter s kamerom, sa zaslonom i njegovim zvukom ili s oboje, ili prima OBS ili bilo koji enkoder preko WHIP-a.
  • U istom trenutku. Prijenos ide preko WebRTC-a, pa pitanje u chatu stiže do nastavnika dok je još na temi.
  • Uredan chat. Moderatori prikvačuju, brišu, utišavaju na neko vrijeme, uklanjaju i zabranjuju, a pravilo može nastavnikova asistenta učiniti moderatorom: { role: 'viewer', mod: true }.
  • Oduzimanje pristupa. Kad učenik napusti tečaj, stranica ga s realtime.stream(name).kick() uklanja iz satova tog tečaja koji su upravo uživo, a od tada pravilo kaže ne.

Isprobajte, pa izgradite svoju

U demu uživo nastavnici i učenici na jedan dan pripadaju samo vama, pa što god ondje emitirate ili napišete, nitko drugi to ne vidi. Ispod svake stranice ploča How this page works prikazuje kod koji je pokreće, korak po korak, s vrijednostima stranice koju imate otvorenu.

Vodič kroz Live Lessons izgrađuje cijelu web-stranicu i u cijelosti donosi svaku datoteku njezina dijela za rad u stvarnom vremenu, a Prijenosi uživo u Next.js-u pokazuju svaki način da prijenos stavite u vlastitu aplikaciju, uključujući vlastito sučelje.

WordPlus Cloud for Apps ima cijenu prema istodobnim vezama, od 1.000 naviše, uz mjesečnu ili godišnju naplatu. S 1.000 veza prijenosi uživo nisu obračunati do 50 gledatelja istodobno, a svakih daljnjih 1.000 veza dodaje 20. Iznad toga svaki dodatni gledatelj troši kredit po minuti, samo dok je prijenos u eteru. Pogledajte prijenose uživo za aplikacije ili cijene za aplikacije. Pitanja o vašoj integraciji? Pišite nam.