Publish

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.json

The 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
FieldMeaning
ttlseconds, 1 to 604800 (7 days), default 3600
subjectwho it is for, in the relay logs as device:<subject> (default the stream id)
token_idtrue 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_url is a relays[].url from the descriptor; namespace is its namespace. tls_root is 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_us is 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_push copies the frame and never blocks. It returns NEARGLASS_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_keyframe is 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_bitrate passes a new target bitrate in bits per second.
  • nearglass_pub_get_stats returns 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 0

The 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 mpp captures, 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 h264 to a pipe) and push each access unit with nearglass_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 seeLikely cause
nearglass_pub_new returns NULL, err says whya bad relay URL or token, or an unreadable TLS root file
frames_sent stays 0 and sessions climbsthe 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 pushthe network task is stalled, usually because the relay is unreachable
dropped_stale climbsframes reach the library more than 100 ms late: push each frame as soon as it is encoded
Viewers see nothingthe stream is the wrong one: the namespace must be the descriptor's

On this page