Devices
Publish from a single-board computer or your own device with the C library, using a publisher token and no ingest host.
A device with its own H.264 encoder (a camera board, a robot, a machine
controller) can publish straight to the relays with the C library, nearglass.h.
It skips the ingest: the device holds a publisher token from your
backend and hands its encoder's access units to the library, which sends
them over MoQ. The same library carries commands back from a driver, for
controller seats.
The library is libnearglass_ffi.a (also .so) and nearglass.h, in the
nearglass-ffi package. Rust programs use the nearglass-pub crate instead; the
calls are the same in shape.
1. The stream
Create the stream with "ingest":["sdk"]: the device publishes with a
token, so there is no stream key and no RTMP or SRT address.
curl -s $API/v1/streams -H "Authorization: Bearer $SK" -H "Content-Type: application/json" \
-d '{"name":"cam-1","admission":"public","ingest":["sdk"]}' | tee stream.jsonThe answer's publish.sdk lists the relays, the namespace and the endpoint
that mints tokens. POST /v1/streams/{id}/stream-key answers 409 no_stream_key for such a stream. LL-HLS, the fallback
for browsers that cannot play MoQ, is made only for streams that come
through the ingest, so a stream published by an SDK device has none.
2. A publisher token
Your backend (not the device's firmware image, if you can help it) asks for a token with the secret key:
curl -s $API/v1/streams/$ID/publisher-tokens -H "Authorization: Bearer $SK" \
-H "Content-Type: application/json" -d '{"ttl":86400,"subject":"cam-1"}' | tee pub.json| Field | Meaning |
|---|---|
ttl | seconds, 1 to 604800 (7 days), default 3600 |
subject | who it is for, in the relay logs as device:<subject> (default the stream id) |
token_id | true gives the token an id: one live session at a time, closed when the stream is deleted. Without it (default) a device can reuse the token across reconnects |
The answer is a descriptor: token, relays (each with a url),
namespace, expires_at and, for a relay with a self-signed certificate,
relay_cert_sha256. The relay ends a publishing session when the token
expires.
In C the token is fixed for the handle. nearglass_conn.token is one
token; the library reconnects with it across network drops but cannot ask
for a new one. Before it expires, make a new handle with a fresh token, or
set a long ttl and rotate on a schedule. (The Rust Broadcast takes a
token source that is asked on every connection.) Treat the token as a
secret: anyone who has it can publish to the stream until it expires.
3. The program
Pass the descriptor's pieces in nearglass_conn, create the handle, and push
each access unit your encoder produces:
#include "nearglass.h"
nearglass_conn conn = { relay_url, NULL /* tls root: NULL for system roots */, token_b64url };
char err[256];
nearglass_pub *p = nearglass_pub_new(&conn, namespace, "video", 1280, 720, 30, 3000000, err, sizeof err);
if (!p) { fprintf(stderr, "nearglass_pub_new: %s\n", err); return 1; }
nearglass_pub_set_encoder_callbacks(p, on_keyframe, on_bitrate, user); /* the SDK asks, the encoder acts */
/* for every encoded frame, in order: */
int rc = nearglass_pub_push(p, 0, au, au_len, capture_us, is_idr ? NEARGLASS_KEYFRAME : 0);
nearglass_pub_free(p);relay_urlis arelays[].urlfrom the descriptor;namespaceis itsnamespace.tls_rootis a PEM file for a relay with a certificate your system does not trust.- The size, frame rate and bitrate are hints for the catalog; the decoder reads the real values from the stream's SPS.
- An access unit is H.264 in Annex B form (start codes). The first frame of a stream, and every keyframe, should carry the SPS and PPS in front of the IDR, as most encoders' output does. Each IDR opens a group, so the keyframe interval is the join time: 1 s, 2 s at most, no B-frames.
capture_usis when the frame was captured, in microseconds since the Unix epoch on the device's clock. Keep the device's clock right: viewers and the relay read it (time sync).nearglass_pub_pushcopies the frame and never blocks. It returnsNEARGLASS_ERR_QUEUE(-2) when its queue of 64 frames is full, and the frame is dropped. A frame older than 100 ms when it would be written is dropped and counted too (nearglass_pub_get_stats:dropped_stale).- Callbacks run on an SDK thread and must not block.
on_keyframeis called when the SDK wants a keyframe (a viewer joins, a reconnect, a stale drop): set a flag and have the encoder make an IDR.on_bitratepasses a new target bitrate in bits per second. nearglass_pub_get_statsreturns the counters:frames_in,frames_sent,dropped_queue_full,dropped_disconnected,dropped_stale,sessions,keyframe_requests,commands.
Link the static library with -lpthread -ldl -lm. Check nearglass_abi_version()
against NEARGLASS_ABI_VERSION at start-up.
The sample
samples/c-publisher/publish_file.c is a complete device that reads an H.264
Annex B file and pushes it in real time, as a device would push its own
encoder's output. Replace read_au() with your encoder.
# libnearglass_ffi.a and nearglass.h are in the nearglass-ffi archive (lib/, include/)
cc -std=c11 -D_POSIX_C_SOURCE=200809L -O2 -I include publish_file.c lib/libnearglass_ffi.a \
-lpthread -ldl -lm -o publish_file
./publish_file <relay url> - pub.tok <namespace> film.h264 30 0The arguments are the relay URL, a TLS root (- for the system's), a file
holding the token (- for none), the namespace, the file, its frame rate,
and the seconds to run (0 loops for ever). Put the descriptor's token in
pub.tok, its namespace and one relays[].url on the line.
Single-board computers
- Rockchip boards (RK3588, RK3576) with a V4L2 camera need no code:
nearglass publish --encoder mppcaptures, encodes on the chip and publishes. See The Nearglass command. - Other boards: take the board's hardware encoder's output (or an
encoder such as ffmpeg's with
-f h264to a pipe) and push each access unit withnearglass_pub_push. The library is built for Linux x86_64 today; the Linux arm64 build needs a toolchain this release's build host did not have.
Sound
The C library publishes video only: it has no audio call yet. For a device that needs sound, publish through an encoder over SRT instead (Audio).
When it does not work
| You see | Likely cause |
|---|---|
nearglass_pub_new returns NULL, err says why | a bad relay URL or token, or an unreadable TLS root file |
frames_sent stays 0 and sessions climbs | the relay refuses the connection: no token, an expired token, a token for another namespace, or a relay certificate your system does not trust (tls_root) |
NEARGLASS_ERR_QUEUE from every push | the network task is stalled, usually because the relay is unreachable |
dropped_stale climbs | frames reach the library more than 100 ms late: push each frame as soon as it is encoded |
| Viewers see nothing | the stream is the wrong one: the namespace must be the descriptor's |