Media Quality Assessment (MQA)
What is MQA?
Media Quality Assessment is an emerging extension to the CMSD specification (CTA-5006) being developed within the SVTA Measurement/QoE Working Group. The core idea: the encoder already computes perceptual quality metrics as a by-product of compression. MQA makes those metrics available in CMSD headers so they travel with the content through the delivery chain — from the encoder, through CDN caches, to the player and analytics systems — without requiring separate hardware probes or post-encode analysis passes. CDN routing, ABR decisions, and monitoring dashboards can all act on quality signals that are intrinsic to the stream.
Scout implements the baseline MQA metrics (PSNR and SSIM) with optional VMAF support.
MQA is per-stream and opt-in — there is no compute cost for streams that do not have it enabled.
Configuration
Plugin-level settings (scout.properties)
Key |
Description |
Default |
|---|---|---|
|
Absolute path to a custom libvmaf |
(built-in model) |
# scout.properties
cmsdVmafModelPath: "" # or e.g. /usr/local/share/vmaf/vmaf_4k_v0.6.1.json
Per-stream settings (REST API)
MQA flags are configured per stream, not globally, so operators can enable quality metrics only on the streams that need them.
Field |
Description |
Default |
|---|---|---|
|
Enable baseline MQA (PSNR + SSIM) for this stream. |
|
|
Additionally compute VMAF. Ignored (with a log warning) if libvmaf is not present in the bundled FFmpeg. |
|
|
Compute VMAF every N keyframes. |
|
REST API
Get MQA configuration for a stream
GET /rest/scout/stream/{appName}/{streamId}/mqa
Example:
curl http://localhost:5080/rest/scout/stream/LiveApp/myStream/mqa
Response:
{
"streamId": "myStream",
"cmsdMqaEnabled": false,
"cmsdVmafEnabled": false,
"cmsdVmafSampleInterval": 1,
"vmafLibAvailable": false
}
The vmafLibAvailable field shows whether the bundled FFmpeg was compiled with
--enable-libvmaf. This is determined at plugin startup and cannot be changed without
swapping the native FFmpeg libraries.
Set MQA configuration for a stream
PUT /rest/scout/stream/{appName}/{streamId}/mqa
Accepted body fields (all optional; missing fields are left unchanged):
# Enable PSNR + SSIM only
curl -X PUT http://localhost:5080/rest/scout/stream/LiveApp/myStream/mqa \
-H "Content-Type: application/json" \
-d '{"cmsdMqaEnabled":true}'
# Enable PSNR, SSIM, and VMAF (with sampled VMAF every 2 keyframes)
curl -X PUT http://localhost:5080/rest/scout/stream/LiveApp/myStream/mqa \
-H "Content-Type: application/json" \
-d '{"cmsdMqaEnabled":true,"cmsdVmafEnabled":true,"cmsdVmafSampleInterval":2}'
# Disable all MQA for a stream
curl -X PUT http://localhost:5080/rest/scout/stream/LiveApp/myStream/mqa \
-H "Content-Type: application/json" \
-d '{"cmsdMqaEnabled":false}'
Response:
{
"success": true,
"streamId": "myStream",
"cmsdMqaEnabled": true,
"cmsdVmafEnabled": false,
"cmsdVmafSampleInterval": 1,
"vmafLibAvailable": false
}
The configuration is persisted in the AntMedia datastore (Broadcast.metaData) and takes
effect the next time the stream starts.
CMSD-MQA header
When cmsdMqaEnabled=true for a stream, Scout injects a CMSD-MQA header on every
.ts and .m4s segment response for that stream. The header is absent on manifest
responses and on streams that have MQA disabled.
Header format:
CMSD-MQA: psnr=42.31, ssim=0.9823, vmaf=87.54
Fields:
Field |
Range |
Description |
|---|---|---|
|
dB (decimal) |
Peak Signal-to-Noise Ratio — full-reference, higher is better. Typical live stream values: 35–50 dB. Computed as mean PSNR over all frames in the segment. |
|
0 – 1 (decimal, 4 dp) |
Structural Similarity Index — full-reference, higher is better. Values above 0.95 are generally considered high quality. |
|
0 – 100 (decimal, 2 dp) |
Video Multimethod Assessment Fusion — perceptual quality, higher is better.
Only present when |
Note
The CMSD-MQA header name and field names (psnr, ssim, vmaf) are based
on draft conventions publicly documented by Touchstream eVQA and Norsk. These names
may be updated when the SVTA MQA specification is finalised and publishes normative
field identifiers.
Browser visibility
Both CMSD-Static and CMSD-MQA are listed in Access-Control-Expose-Headers on
every Scout-annotated response, so browser-based players can read them via
response.headers.get('CMSD-MQA').
Compute overhead
Metric |
Overhead |
|---|---|
PSNR |
Negligible. Computed in pure Java using the Y-channel pixels already present in memory; no additional decode pass is required beyond decoding the encoded segment (which Scout does internally, one decode per encoded packet). |
SSIM |
Low. Computed alongside PSNR in the same decode pass using an 8×8 sliding-window algorithm on the Y channel. No separate processing step. |
VMAF |
Moderate. Computed via the libvmaf AVFilter, sampled at keyframe boundaries (or
every N keyframes when |
To minimise overhead on high-bitrate renditions, consider:
Enabling MQA only on representative renditions (e.g. the 720p or 1080p track, not every adaptive variant).
Setting
cmsdVmafSampleIntervalto 2 or 3 to reduce VMAF sampling frequency.Keeping
cmsdVmafEnabled=falseunless VMAF scores are specifically required.
Checking for libvmaf
The vmafLibAvailable field in the GET response indicates whether the bundled FFmpeg was
compiled with --enable-libvmaf. The standard AntMedia Server distribution does not
include libvmaf (it requires a GPL-licensed FFmpeg build). If vmafLibAvailable is
false, setting cmsdVmafEnabled=true will save the flag but produce a log warning at
stream start and fall back to PSNR/SSIM only — it will never silently fail or crash.
To enable VMAF:
Build or obtain an FFmpeg 7.x binary compiled with
--enable-libvmaf.Replace the native FFmpeg libraries in AMS’s library path with the new build.
Restart AMS — Scout will detect libvmaf at startup and log
VMAF scoring is available.
You can verify the current state at any time:
curl http://localhost:5080/rest/scout/stream/LiveApp/anyStream/mqa | jq .vmafLibAvailable
Implementation notes
How PSNR and SSIM are computed: For each MQA-enabled stream, Scout registers two internal listeners on the encoding pipeline. A frame listener buffers raw input frames (the encoder’s input). A packet listener receives each encoded packet, decodes it using a lightweight software decoder, and compares the decoded output against the buffered reference frame using full-reference PSNR and SSIM algorithms on the Y (luma) channel. Scores are accumulated across all frames in a segment, and the mean is stored when the segment boundary is reached.
Memory implications: The raw frame buffer is capped at 32 frames per stream. For a 1080p stream this is approximately 100 MB worst-case. For adaptive streaming ladders with multiple renditions, each rendition has its own independent buffer. Operators running MQA on many simultaneous high-bitrate streams should account for this when sizing server memory.
Per-stream isolation: MQA listeners are registered and unregistered per-stream independently. Enabling MQA on stream A has no effect on the headers returned for stream B.
JNI bridge:
No new native JNI methods were added. All codec operations (decode, filter graph) use the
existing bytedeco FFmpeg Java bindings (org.bytedeco:ffmpeg:7.1.1-1.5.12).