Reference · Media
media.rtp
media.rtp(str, map) -> nullWhat it does
Makes a stream that takes a camera's or an encoder's RTP on UDP ports, for pages to
play with <quillo-media stream="name">: { video: port, audio: port, host }, one or
both tracks, each on a port of its own. Video is H.264 and audio Opus, the only codecs
taken ({ port: 5004, codec: "h264" } says so in full). The server puts the packets
in order, makes frames of them and passes each on to every page watching, decoding
nothing; Quillo code never sees the packets. Pages watch it only as
media.on(name, { watch }) says: with no watch, no page does (since 0.7: any page
could). It is written at the top of a program or while it runs (a stream for each
room), once for a name: asked again, it is the same stream.
Examples
media.rtp("cam", { video: 5004, audio: 5006 })media.on("cam", { watch: (peer) => peer.session.user != null })route GET / = <html><body><quillo-media stream="cam" controls muted/></body></html>ffmpeg -f avfoundation -framerate 30 -video_size 1280x720 -i "0:0" \ -map 0:v -c:v libx264 -tune zerolatency -profile:v baseline -pix_fmt yuv420p -g 60 \ -f rtp rtp://127.0.0.1:5004 -map 0:a -c:a libopus -f rtp rtp://127.0.0.1:5006The ports are taken as soon as it is called, and the stream is listed at once:
print(media.rtp("cam", { video: 45004, audio: 45006 }))print(media.streams() | map((s) => s.name))print(media.close("cam"))null["cam"]trueEdge cases
- A name is as a channel's: letters, digits,
_,-,.and:. - Calling it again with the same name gives the same stream, not an error.
hostis the address to listen on, every IPv4 address unless given; one this machine does not have is a runtime error.- Any sender that reaches the port is taken (the payload type and SSRC are not checked), and there is no RTCP and no SRTP: keep the ports to the camera's network.
- The server keeps the video since the last keyframe (at most 600 frames, and 32 MiB; 256 MiB for all streams), so a page that starts watching shows a picture at once; a page that falls behind, or a lost packet of video, skips to the next keyframe.
- A stream made for no tenant (at the top level, in a job) is the program's; one made
for a tenant (in its request, or in
with_tenant) is that tenant's. A tenant's page watching a name its tenant has no stream of watches the program's. - A late packet is waited for 50 ms, or 256 packets.
- It opens UDP ports, so it is the
neteffect, asudp.listenis: a function declaredwithwithoutnetcannot call it, nor can a program under a policy that leavesnetout.
print(media.rtp("mic", { audio: { port: 45008, codec: "opus" }, host: "127.0.0.1" }))print(media.rtp("mic", { audio: { port: 45008, codec: "opus" }, host: "127.0.0.1" }))print(media.streams())print(media.close("mic"))nullnull[{ name: "mic", viewers: 0, video: { frames: 0, fps: 0, kbps: 0, lost: 0 }, audio: { frames: 0, fps: 0, kbps: 0, lost: 0 } }]trueErrors
A bad name, no ports map, neither track, a port out of range, a codec other than h264 for video or opus for audio, an option that does not exist, and a port that cannot be listened on are runtime errors, not error values.
media.rtp("cam", { video: { port: 45004, codec: "vp8" } })line 1: `media.rtp`: vp8 in video is not supported (h264 is)media.rtp("cam", {})line 1: `media.rtp` needs a video port, an audio port, or bothmedia.rtp("cam", { video: 45004, host: "203.0.113.1" })line 1: `media.rtp`: cannot listen on UDP port 45004: Cannot assign requested address (os error 99)Chaining
It gives null and is not piped. media.streams shows what it made, and media.close ends it.
media.on("room:*", { watch: (peer) => peer.session.user != null })route POST /rooms { form: { name: str } } { let id = db.exec("insert into rooms (name) values (?)", name).last_id media.rtp(`room:${id}`, { video: 5000 + id * 2, audio: 5001 + id * 2 }) redirect(`/rooms/${id}`)}media.rtp("a", { video: 45010 })print(media.streams() | map((s) => s.name), media.close("a"))["a"] trueSee also
media.streams, media.close, media.on, udp.listen
Thread 0 posts
On media.rtp(): questions, tips, corrections, and code that uses it.
No posts yet. The first one opens the thread.
Edit this page on GitHub · every example's output was made by running it (tools/refcheck.mjs) · questions on Discord or r/quillo