This repository contains a Qt-based application that publishes MPEG-2 transport
streams over MOQT using the moqxr publisher SDK.
It also contains a console receiver built on moq5 main that subscribes to an
MSF MPEG-TS track over MOQT draft 22, writes source packets, and prints live
transport and delivery statistics.
The media pipeline accepts TS or M2TS input and produces
draft-gregoire-moq-msfts media objects: each object payload is a
concatenation of whole 188-byte TS packets or 192-byte M2TS source packets.
The checked-in MSFTS reference is draft 02.
The catalog follows checked-in MSF draft 01.
The -00 MSFTS and CMSF text files and dated docs/superpowers plans/specs
are retained as historical references. With moqxr v0.4.4, this adapter supports
MOQT transport drafts 14, 16, 17, 18, 21, and 22, and defaults to draft 22.
The application targets:
- H.264 video encoding via
openh264 - AAC or Opus audio at 48kHz stereo via direct libav/libopus integration points
- M2TS stream ingestion for live audio/video paths
- Cross-platform desktop UI (Qt Widgets)
- CMake build with a Debian Bookworm Docker build entrypoint
GitHub Actions builds Linux, macOS, and Windows packages on every push and pull request (see the Build workflow):
- Linux —
moq2ts-publisher-linux-x86_64.AppImage(self-contained;chmod +xand run) andmoq2ts-cli-linux-x86_64.AppImage, the command-line publisher - macOS —
moq2ts-publisher-macos.zip(a bundled.app; the command-line publisher ismoq2ts-publisher.app/Contents/MacOS/moq2ts-cli) - Windows —
moq2ts-publisher-windows.zip(the.exeandmoq2ts-cli.exewith their Qt and codec DLLs). The Windows build is off by default: start the workflow by hand withwindowsset, or set the repository variableBUILD_WINDOWStotrue. A release without it has no Windows asset.
The receiver (moq2ts-receiver) is not packaged: it needs an installed moq5,
which has no prebuilt SDK yet (see docs/CLI.md).
Per-commit packages are attached to each run on the Actions tab. Tagged releases
(v*) additionally publish these assets to a GitHub Release.
These CI binaries are real, relay-capable builds: each links the prebuilt
openmoq publisher SDK from the public openmoq/moqxr releases (pinned via the
MOQXR_VERSION workflow variable) together with the full capture and transcode
stack (Qt6 + ffmpeg/libav + openh264 + opus), so they can publish to a real MOQT
relay endpoint. The SDK ships static libraries (publisher + picoquic/picotls);
OpenSSL is linked from the platform. On Windows the build uses MSVC + vcpkg to
match the SDK's ABI; Linux and macOS use the system GCC/Clang toolchains.
A mock build (UI, file packetization, and local object generation with mock://
endpoints only) is available locally with
-DMOQ2TS_BUILD_WITH_MOCK_MOQXR=ON. See moqxr integration
for linking the SDK (MOQXR_SDK_DIR) or a local ../moqxr source checkout.
To cut a release, push a version tag:
git tag v0.1.0
git push origin v0.1.0src/main.cpp: Qt application bootstrap and signal wiringsrc/app/*: UI and publish configuration modelsrc/media/*: media pipeline, capture preview, TS/M2TS packetizer, and draft MSFTS catalog helperssrc/publish/*: MOQT publisher adapter (moqxrpublish_live_objectsand a local mock path)src/receive/*: moq5-based MPEG-TS receiver CLI and transport-stream countersdocker/*: Bookworm container definitionscripts/*: build automationdocs/*: sequence diagrams and developer documentation
- Live configuration of endpoint/namespace/stream
- Separate video and audio source selectors (M2TS file/pipe)
- Camera and microphone enumeration through libavdevice/platform capture APIs, with Qt Multimedia fallback where enabled
- Preview tab with decoded video preview and dB-scaled left/right audio meters
- In-process libavdevice capture path for selected camera/microphone devices
- M2TS program selection for publishing one program/track from an MPTS source
- MSF timeline side track for wall-clock correlation
- Tunable codec and bitrate settings
- Fragment size control and pacing
- Status/log output and basic counters
- CMake >= 3.24
- Qt Widgets (Qt 5 or Qt 6)
- Optional (recommended for real encode path):
libopenh264-devlibav*dev packageslibavdevice-devlibswscale-devlibopus-dev
The default build requires moqxr v0.4.4 or newer publisher headers and
libraries, either from a sibling ../moqxr checkout or MOQXR_SDK_DIR. Set
MOQ2TS_BUILD_WITH_MOCK_MOQXR=ON explicitly for a local mock build; that build
only accepts mock://... endpoints.
cmake -S . -B build \
-DCMAKE_BUILD_TYPE=Release
cmake --build build -j"$(nproc)"Run:
./build/moq2ts-publisherIn the UI, replace the initial mock://local value with your relay's
moqt://host:port or https://host:port/path URL to publish over MOQT. For
local pipeline testing, configure a separate build with
-DMOQ2TS_BUILD_WITH_MOCK_MOQXR=ON and keep mock://local.
The console receiver uses moq5 and needs no Qt runtime. Build it against an
installed moq5 main build with the service and mpeg2ts targets enabled:
cmake -S src/receive -B build-receiver -DCMAKE_PREFIX_PATH=/path/to/moq5/install
cmake --build build-receiver -j
./build-receiver/moq2ts-receiver \
--url moqt://127.0.0.1:4433 --namespace live/ch1 \
--out capture.ts --duration 10 --insecureUse --insecure only with a local or test relay whose certificate is not
trusted. The receiver selects an MPEG-TS track from the MSF catalog, saves its
source packets, and prints a stats line each second. Use --track NAME when
the catalog has multiple matching tracks.
This capture from a local Red5 relay shows five reported MoQT gaps; its zero
sync, malformed, tei, and cc counters describe the TS packets that
arrived. See the CLI guide for all
receiver options, counter meanings, and the end-to-end test.
./scripts/build-debian-bookworm.shThe script builds a Bookworm container image, compiles the app, and installs
artifacts under build-bookworm/install.
The default Bookworm build links the sibling ../moqxr checkout for real MOQT
publishing. The script mounts that checkout into the container when present.
For local capture, muxing, preview, and object-generation testing without a
relay, choose the mock build explicitly:
MOQ2TS_BUILD_WITH_MOCK_MOQXR=ON ./scripts/build-debian-bookworm.shLaunch the packaged app with:
./build-bookworm/install/moq2ts-publisher-bookworm- MSFTS packaging: media object payloads are raw source packets only. The app does not add a private wrapper before publication.
- Packet size: source packets are validated as either 188-byte TS or 192-byte M2TS packets, with sync byte checks required by the draft.
- Catalog: the media track uses
packaging: "mpeg2ts"with MSF common fields (isLive,role,mimeType,targetLatency) and MSFTS mpeg2ts fields (mpeg2tsMode,mpeg2tsPacketSize,mpeg2tsProgramNumberand optionalmpeg2tsPcrPidon theper-programandunmodified-programmodes, andmpeg2tsRandomAccesswhen the first Object of every Group contains a random access point).mpeg2tsTimestampModeis emitted only for 192-octet source packets.mpeg2tsMuxRateappears only for derivedper-programtracks; SRT tracks omitmpeg2tsRandomAccessbecause recovery can start without a keyframe. - Initialization data: the packetizer scans the start of the source for
PAT and PMT packets and places them in a Base64
initDataListentry referenced by the media track'sinitRef, preserving source-packet form. - Program filtering: in
per-programmode, the selected program is discovered from PAT/PMT. Media objects include a PAT rewritten to list that program only, the selected PMT, the selected PCR PID, and the elementary PIDs for that program. Packets for other programs and null packets are dropped before publication. - Timeline track: each media track gets a
<stream>.timelineside track of MSFpackaging: "mediatimeline"thatdependson the media track. Timeline objects are a compact[mediaTimeMs, [groupId, objectId], wallclockMs]record array mapping media presentation time to Unix wall-clock milliseconds without modifying M2TS payloads. - Encoding: the in-process capture path uses libavcodec/libavformat to
encode and mux 188-byte MPEG-TS packets. The file, pipe, and SRT paths take
an existing TS/M2TS stream; the application does not invoke
ffmpeg. - Capture devices: camera and microphone devices are listed in the UI through libavdevice/platform capture APIs, with Qt Multimedia fallback where enabled. If no file/pipe source is provided, selected devices are opened through libavdevice, encoded in-process, muxed to MPEG-TS through libavformat, and published as MSFTS MPEG-TS objects. On Linux the capture path probes the camera's V4L2 modes and prefers an MJPEG mode at the requested resolution/framerate (so e.g. 1080p30 is honored where raw YUYV would be capped), reconciling the encoder to the framerate the driver actually negotiates. Transient corrupt decode frames are skipped rather than aborting capture.
- Preview: the Preview tab can open selected camera/microphone devices before publishing. During live capture publishing, the Preview tab is driven from the same decoded frames and audio samples used by the encoder path. Audio meters display RMS levels on a dBFS scale so normal speech is visible.
src/publish/MoqxrPublisher.cpp wires the MSFTS object stream into the moqxr
Publisher::publish_live_objects API. There are two ways to link the real
publisher (both require OpenSSL on the system).
Link a prebuilt SDK from the openmoq/moqxr releases (what CI uses) — extract the
archive for your platform and point MOQXR_SDK_DIR at it:
cmake -S . -B build \
-DMOQXR_SDK_DIR=/path/to/extracted-sdk \
-DMOQ2TS_BUILD_WITH_MOCK_MOQXR=OFFOr build against a neighboring moqxr source checkout:
cmake -S . -B build \
-DMOQXR_SOURCE_DIR=../moqxr \
-DMOQ2TS_BUILD_WITH_MOCK_MOQXR=OFFMock mode remains available with MOQ2TS_BUILD_WITH_MOCK_MOQXR=ON.
Mock mode is explicit: endpoints must start with mock://. Entering a real
relay URL in a mock build fails with an error instead of pretending to connect.
See docs/sequence-diagrams.md.
See docs/DEVELOPER.md.
Originally created by Paul Gregoire as an example for the msfts draft.