9.6 KiB
MXL Player
MXL Player is a desktop player for local Media eXchange Layer audio and video flows, with GUI controls and command-line startup configuration. It can play either feed independently, play both simultaneously, or switch a configured pair into and out of native MXL synchronization while running.
The application uses:
- SDL3 for its window, input, and audio output;
- Vulkan for video and ImGui rendering;
go-mxlfor MXL access.
Features
- Synchronized playback for compatible same-domain feeds; cross-domain feeds currently use independent readers.
- Finite or unlimited reconnect attempts with exponential backoff.
- JSON playlists with manual selection, timed advance, looping, and failure policies.
- Playback, media, timing, retry, and dropped-frame statistics.
Planned
- MXL Fabrics support;
- MXL URI/addressability support;
- Coordinated cross-domain reading using TAI timestamps;
- GUI-created playlist;
- GPU selection;
- LUFS meter;
- patching audio channels to selected device.
Installation
Prebuilt packages are available from GitHub Releases.
- macOS ARM64:
.appbundle with SDL3, libmxl, and MoltenVK included. - Linux AMD64: binary built on Ubuntu. A compatible libmxl installation, SDL3, Vulkan loader, and Vulkan-capable driver are required.
Download the archive for your platform and verify its SHA-256 checksum before running it.
Tested platforms
- Ubuntu and Fedora: build and feed playback tested.
- macOS ARM64: build and feed playback tested.
Building from source: requirements
- Go 1.26 or newer.
- A C/C++ build toolchain for cgo dependencies.
- A compatible shared build of
libmxland its development headers. - pkg-config metadata for
libmxl. - SDL3 shared library.
- Vulkan loader and a working Vulkan driver.
- libmxl 1.2.0 or a compatible build providing
mxlCreateFlowSynchronizationGroup.
The project currently pins:
github.com/qvest-digital/go-mxl v1.1.0-rc.2
Building on Linux
First verify that pkg-config resolves the intended MXL installation:
pkg-config --modversion libmxl
pkg-config --cflags --libs libmxl
pkg-config --variable=pcfiledir libmxl
pkg-config --variable=libdir libmxl
If MXL is installed in a nonstandard prefix:
export MXL_PREFIX=/opt/mxl/1.2.0
export PKG_CONFIG_PATH="$MXL_PREFIX/lib/pkgconfig"
export LD_LIBRARY_PATH="$MXL_PREFIX/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
Then build and test:
go mod download
go test ./...
mkdir -p build
go build -o build/mxl-player ./cmd/mxl-player
Confirm which native library the executable will use:
ldd build/mxl-player | grep libmxl
macOS
The SDL3 and Vulkan loaders contain macOS library-name support. The Vulkan dependency is temporarily replaced by a portability fork while the upstream change is reviewed.
Building currently requires:
- Xcode Command Line Tools;
- Go;
- SDL3;
- MoltenVK;
- a macOS build of libmxl with matching headers and pkg-config metadata.
When libmxl has been built from its source tree but has not been installed into a standard prefix, expose its build metadata, source headers, and library directory to cgo. For example:
export MXL_SOURCE=/path/to/mxl
export MXL_BUILD="$MXL_SOURCE/build/Darwin-Clang-Release"
export PKG_CONFIG_PATH="$MXL_BUILD${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}"
export CGO_CFLAGS="-I$MXL_SOURCE/lib/include"
export CGO_LDFLAGS="-L$MXL_BUILD/lib"
mkdir -p build
go build -o build/mxl-player ./cmd/mxl-player
The CGO_CFLAGS workaround is needed because the headers are still in the MXL
source tree. A properly installed MXL SDK should expose them through
libmxl.pc instead.
Nonstandard loader locations can be supplied explicitly (for example, in case of installing with homebrew):
export SDL3_LIBRARY="$(brew --prefix sdl3)/lib/libSDL3.dylib"
export VULKAN_LIBRARY=/absolute/path/to/libMoltenVK.dylib
export DYLD_LIBRARY_PATH="$MXL_BUILD/lib${DYLD_LIBRARY_PATH:+:$DYLD_LIBRARY_PATH}"
./build/mxl-player
The resulting executable may currently need an additional libmxl runtime search path, for example:
install_name_tool -add_rpath "$MXL_BUILD/lib" build/mxl-player
These workarounds are only needed for development builds. The packaged .app
uses bundled libraries and relative runtime paths instead.
Running the macOS application bundle
The package is ad-hoc signed but is not signed with an Apple Developer ID or notarized by Apple. Consequently, Gatekeeper may block its first launch. Right-click MXL Player.app, select Open, and confirm the prompt. It can also be allowed from System Settings → Privacy & Security after a blocked launch.
If macOS still retains a quarantine restriction, it can be removed manually:
xattr -dr com.apple.quarantine "/Applications/MXL Player.app"
Only remove quarantine from an archive downloaded from the official project release and verified against its SHA-256 checksum.
Running
Start with no configured feed and enter domains/UUIDs in the GUI:
./build/mxl-player
Video only:
./build/mxl-player \
--domain /dev/shm/mxl \
--video VIDEO_UUID
Audio only:
./build/mxl-player \
--domain /dev/shm/mxl \
--audio AUDIO_UUID
Independent video and audio:
./build/mxl-player \
--video-domain /dev/shm/video \
--video VIDEO_UUID \
--audio-domain /dev/shm/audio \
--audio AUDIO_UUID
Start a compatible pair synchronized:
./build/mxl-player \
--domain /dev/shm/mxl \
--video VIDEO_UUID \
--audio AUDIO_UUID \
--sync
Synchronization is not a permanent startup mode. It can be enabled and disabled at runtime from the settings window.
Command-line options
| Option | Short | Meaning |
|---|---|---|
--help |
-h |
Show help and exit. |
--domain PATH |
-d |
Default domain for feeds without a specific domain. |
--video-domain PATH |
Domain for the video feed. | |
--audio-domain PATH |
Domain for the audio feed. | |
--video UUID |
-v |
Initial video flow UUID. |
--audio UUID |
-a |
Initial audio flow UUID. |
--sync |
-s |
Start a configured A/V pair synchronized. |
--max-attempts N |
Attempts per playback lifecycle; 0 means unlimited. |
|
--playlist FILE |
Load a JSON playlist. | |
--fullscreen |
-f |
Start fullscreen. |
--gpu-id N |
-g |
Select GPU ID; currently incomplete. |
--playback-id N |
-p |
Select the SDL playback-device ID. |
--list-playback |
List SDL playback devices and exit. | |
--list-gpu |
List Vulkan physical devices and exit. | |
--verbose |
Reserved; currently incomplete. |
Run ./build/mxl-player --help for the authoritative list.
GUI controls
The settings window accepts separate video/audio domains and UUIDs.
- Apply feeds applies the complete desired configuration.
- Stop stops a feed while retaining its UUID and domain.
- Resume starts a stopped configured feed with a fresh lifecycle.
- Remove stops a feed and clears its configuration.
- Stop all and Resume all operate on both configured feeds.
- Synchronize switches a compatible active pair between synchronized and independent playback.
Keyboard shortcuts:
| Key | Action |
|---|---|
F1 |
Show or hide settings. |
F2 |
Show or hide statistics. |
F |
Toggle fullscreen. |
Q or Esc |
Quit. |
Retry behavior
Each playback lifecycle starts at attempt 1. Failed connections/readers are retried with exponential delay from 500 ms up to 5 seconds.
# Retry indefinitely
./build/mxl-player -d /dev/shm/mxl -v VIDEO_UUID --max-attempts 0
# Stop after three failed attempts
./build/mxl-player -d /dev/shm/mxl -v VIDEO_UUID --max-attempts 3
With independent A/V, one failed feed retries without stopping the other. A synchronized pair reconnects as one lifecycle.
Playlists
Start a playlist with:
./build/mxl-player --playlist playlist.json
Example:
{
"loop": true,
"on_failure": "next",
"retry": {
"max_attempts": 3,
"initial_delay": "500ms",
"max_delay": "5s"
},
"entries": [
{
"name": "Synchronized pair",
"video": {
"domain": "/dev/shm/mxl",
"uuid": "VIDEO_UUID"
},
"audio": {
"domain": "/dev/shm/mxl",
"uuid": "AUDIO_UUID"
},
"sync": true,
"duration": "10s"
},
{
"name": "Video only",
"video": {
"domain": "/dev/shm/mxl",
"uuid": "ANOTHER_VIDEO_UUID"
},
"duration": "15s"
},
{
"name": "Audio until manually advanced",
"audio": {
"domain": "/dev/shm/mxl",
"uuid": "ANOTHER_AUDIO_UUID"
}
}
]
}
Playlist rules:
durationuses Go duration syntax such as500ms,10s, or2m.- Missing/empty duration means manual advance.
loop: truereturns to the first entry after the last.on_failure: "wait"remains on an exhausted entry.on_failure: "next"advances after retry exhaustion.- A synchronized entry must contain both video and audio.
- Playlist retry settings override the CLI defaults unless
--max-attemptswas explicitly supplied.
The GUI supports entry selection, previous/next navigation, and pausing or resuming a timed entry.
Third-party assets
The embedded JetBrainsMonoNL Nerd Font Mono is distributed under the SIL Open
Font License 1.1. Its copyright notice, license, and source information are in
internal/assets/fonts.
License
MXL Player is licensed under the GNU General Public License, version 3 or any
later version. See LICENSE.
The embedded font retains its separate SIL Open Font License 1.1.