diff --git a/contrib/checkbox-multimedia/.gitignore b/contrib/checkbox-multimedia/.gitignore
new file mode 100644
index 0000000000..fadfa13d80
--- /dev/null
+++ b/contrib/checkbox-multimedia/.gitignore
@@ -0,0 +1,3 @@
+__pycache__/
+.pytest_cache/
+*.pyc
diff --git a/contrib/checkbox-multimedia/Makefile b/contrib/checkbox-multimedia/Makefile
new file mode 100644
index 0000000000..9fb7715455
--- /dev/null
+++ b/contrib/checkbox-multimedia/Makefile
@@ -0,0 +1,38 @@
+.PHONY: help test validate develop run clean all
+
+help:
+ @echo "Targets:"
+ @echo " make test - Run unit tests (./manage.py test)"
+ @echo " make validate - Validate PXU syntax (./manage.py validate)"
+ @echo " make develop - Register provider for local development"
+ @echo " make test - Run unit tests (./manage.py test)"
+ @echo " make validate - Validate PXU syntax (./manage.py validate)"
+ @echo " make develop - Register provider for local development"
+ @echo " make run - Full end-to-end: validate + test + develop + run all codec tests"
+ @echo " make all - Run unit tests and full end-to-end tests"
+ @echo ""
+ @echo "Single command to run everything: make run"
+
+test:
+ ./manage.py test
+
+validate:
+ ./manage.py validate
+
+develop:
+ ./manage.py develop
+
+clean:
+ -rm -f /var/tmp/checkbox-providers-develop/checkbox-provider-multimedia.provider
+
+run: validate test clean develop
+ @echo ""
+ @echo "=== Running all VA-API codec conformance tests (decode + encode) ==="
+ @echo "Platform will be auto-detected (set PLATFORM_CONFIG for manual config)"
+ @echo ""
+ checkbox-cli run com.canonical.contrib::multimedia-codecs-vaapi
+ @echo ""
+ @echo "=== Running VA-API encoder-only tests ==="
+ checkbox-cli run com.canonical.contrib::multimedia-codecs-vaapi-encode
+
+all: test run
diff --git a/contrib/checkbox-multimedia/README.md b/contrib/checkbox-multimedia/README.md
new file mode 100644
index 0000000000..b6ac955400
--- /dev/null
+++ b/contrib/checkbox-multimedia/README.md
@@ -0,0 +1,21 @@
+# checkbox-provider-multimedia
+
+Checkbox provider for multimedia hardware validation (VA-API codecs).
+
+## Dependencies
+
+```bash
+sudo snap install fluster --edge
+sudo apt install gstreamer1.0-tools gstreamer1.0-vaapi \
+ gstreamer1.0-plugins-good gstreamer1.0-plugins-bad \
+ gstreamer1.0-plugins-ugly libva-utils vainfo
+```
+
+## How to run
+
+```bash
+./manage.py validate
+./manage.py test
+./manage.py develop
+checkbox-cli run com.canonical.contrib::multimedia-codecs-vaapi
+```
diff --git a/contrib/checkbox-multimedia/bin/gst_encoder_quality.py b/contrib/checkbox-multimedia/bin/gst_encoder_quality.py
new file mode 100755
index 0000000000..388c9fcefe
--- /dev/null
+++ b/contrib/checkbox-multimedia/bin/gst_encoder_quality.py
@@ -0,0 +1,159 @@
+#!/usr/bin/env python3
+#
+# This file is part of Checkbox.
+#
+# Copyright 2025 Canonical Ltd.
+#
+# Checkbox is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 3,
+# as published by the Free Software Foundation.
+#
+# Checkbox is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Checkbox. If not, see .
+#
+import argparse
+import logging
+import os
+import subprocess
+import sys
+import tempfile
+
+logging.basicConfig(level=logging.INFO)
+
+GST_LAUNCH = "gst-launch-1.0"
+
+ENCODER_MAP = {
+ "h264": "vah264enc",
+ "h265": "vah265enc",
+ "vp8": "vavp8enc",
+ "vp9": "vavp9enc",
+ "av1": "vaav1enc",
+ "jpeg": "jpegenc",
+ "mpeg4": "vampl4enc",
+}
+
+SUPPORTED_CODECS = sorted(ENCODER_MAP.keys())
+
+
+def register_arguments():
+ parser = argparse.ArgumentParser(
+ description="Encode a test pattern using a GStreamer encoder "
+ "and validate the output.",
+ )
+ parser.add_argument(
+ "--codec",
+ required=True,
+ choices=SUPPORTED_CODECS,
+ help="Codec to test",
+ )
+ parser.add_argument(
+ "--width",
+ type=int,
+ default=1920,
+ help="Video width (default: 1920)",
+ )
+ parser.add_argument(
+ "--height",
+ type=int,
+ default=1080,
+ help="Video height (default: 1080)",
+ )
+ parser.add_argument(
+ "--framerate",
+ type=int,
+ default=30,
+ help="Frame rate in fps (default: 30)",
+ )
+ parser.add_argument(
+ "--num-buffers",
+ type=int,
+ default=60,
+ help="Number of video frames to encode (default: 60)",
+ )
+ parser.add_argument(
+ "--output",
+ type=str,
+ default="",
+ help="Output file path (default: temp file)",
+ )
+ return parser.parse_args()
+
+
+def build_pipeline(codec, width, height, framerate, num_buffers, output_file):
+ encoder = ENCODER_MAP[codec]
+ caps = "video/x-raw,width={},height={},framerate={}/1".format(
+ width, height, framerate
+ )
+
+ if codec == "jpeg":
+ return (
+ "{} videotestsrc num-buffers={} ! {} ! videorate !"
+ " video/x-raw,framerate=1/1 ! videoconvert !"
+ " {} ! filesink location={}"
+ ).format(GST_LAUNCH, 1, caps, encoder, output_file)
+ elif codec == "av1":
+ pipeline = (
+ "{} videotestsrc num-buffers={} ! {} ! videoconvert !"
+ " {} ! filesink location={}"
+ ).format(GST_LAUNCH, num_buffers, caps, encoder, output_file)
+ else:
+ pipeline = (
+ "{} videotestsrc num-buffers={} ! {} ! videoconvert !"
+ " {} ! filesink location={}"
+ ).format(GST_LAUNCH, num_buffers, caps, encoder, output_file)
+
+ return pipeline
+
+
+def validate_output(file_path):
+ if not os.path.exists(file_path):
+ logging.error("Output file does not exist: %s", file_path)
+ return False
+ file_size = os.path.getsize(file_path)
+ if file_size == 0:
+ logging.error("Output file is empty: %s", file_path)
+ return False
+ logging.info("Output file created: %s (%d bytes)", file_path, file_size)
+ return True
+
+
+def main():
+ args = register_arguments()
+ output_file = args.output or tempfile.mktemp(
+ suffix=".{}".format("jpg" if args.codec == "jpeg" else "mkv")
+ )
+ pipeline = build_pipeline(
+ args.codec,
+ args.width,
+ args.height,
+ args.framerate,
+ args.num_buffers,
+ output_file,
+ )
+ logging.info("Running pipeline: %s", pipeline)
+ result = subprocess.run(
+ pipeline, shell=True, capture_output=True, text=True
+ )
+ if result.returncode != 0:
+ logging.error(
+ "GStreamer pipeline failed:\n%s\n%s",
+ result.stdout,
+ result.stderr,
+ )
+ sys.exit(result.returncode)
+
+ if not validate_output(output_file):
+ sys.exit(1)
+
+ logging.info("Encoder test passed for %s", args.codec)
+ if not args.output:
+ os.unlink(output_file)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/contrib/checkbox-multimedia/bin/platform_config_resource.py b/contrib/checkbox-multimedia/bin/platform_config_resource.py
new file mode 100755
index 0000000000..3859ddfa9f
--- /dev/null
+++ b/contrib/checkbox-multimedia/bin/platform_config_resource.py
@@ -0,0 +1,223 @@
+#!/usr/bin/env python3
+#
+# This file is part of Checkbox.
+#
+# Copyright 2025 Canonical Ltd.
+#
+# Checkbox is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 3,
+# as published by the Free Software Foundation.
+#
+# Checkbox is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Checkbox. If not, see .
+#
+import configparser
+import os
+import platform
+import subprocess
+
+ENCODER_MAP = {
+ "h264": "vah264enc",
+ "h265": "vah265enc",
+ "vp8": "vavp8enc",
+ "vp9": "vavp9enc",
+ "av1": "vaav1enc",
+ "jpeg": "jpegenc",
+ "mpeg4": "vampl4enc",
+}
+
+DECODER_MAP = {
+ "av1-profile0": "has_av1_profile0_decoder_vaapi",
+ "av1-profile1": "has_av1_profile1_decoder_vaapi",
+ "av1-profile2": "has_av1_profile2_decoder_vaapi",
+ "h264-constrained_baseline": (
+ "has_h264_constrained_baseline_decoder_vaapi"
+ ),
+ "h264-main": "has_h264_main_decoder_vaapi",
+ "h264-high": "has_h264_high_decoder_vaapi",
+ "h264-high10": "has_h264_high10_decoder_vaapi",
+ "h264-high422": "has_h264_high422_decoder_vaapi",
+ "h264-high444": "has_h264_high444_decoder_vaapi",
+ "h265-main": "has_h265_main_decoder_vaapi",
+ "h265-main10": "has_h265_main10_decoder_vaapi",
+ "h265-main12": "has_h265_main12_decoder_vaapi",
+ "h265-main422_10": "has_h265_main422_10_decoder_vaapi",
+ "h265-main444": "has_h265_main444_decoder_vaapi",
+ "h265-main444_10": "has_h265_main444_10_decoder_vaapi",
+ "h265-main444_12": "has_h265_main444_12_decoder_vaapi",
+ "jpeg-baseline": "has_jpeg_baseline_decoder_vaapi",
+ "mpeg2-simple": "has_mpeg2_simple_decoder_vaapi",
+ "mpeg2-main": "has_mpeg2_main_decoder_vaapi",
+ "mpeg4-simple": "has_mpeg4_simple_decoder_vaapi",
+ "mpeg4-advanced_simple": ("has_mpeg4_advanced_simple_decoder_vaapi"),
+ "mpeg4-main": "has_mpeg4_main_decoder_vaapi",
+ "vc1-simple": "has_vc1_simple_decoder_vaapi",
+ "vc1-main": "has_vc1_main_decoder_vaapi",
+ "vc1-advanced": "has_vc1_advanced_decoder_vaapi",
+ "vp8-version0_3": "has_vp8_version0_3_decoder_vaapi",
+ "vp9-profile0": "has_vp9_profile0_decoder_vaapi",
+ "vp9-profile1": "has_vp9_profile1_decoder_vaapi",
+ "vp9-profile2": "has_vp9_profile2_decoder_vaapi",
+ "vp9-profile3": "has_vp9_profile3_decoder_vaapi",
+}
+
+VA_ENCODER_NAMES = {
+ "h264": "H264",
+ "h265": "H265",
+ "vp8": "VP8",
+ "vp9": "VP9",
+ "av1": "AV1",
+ "jpeg": "JPEG",
+ "mpeg4": "MPEG4",
+}
+
+VA_DECODER_NAMES = {
+ "av1": "AV1",
+ "h264": "H264",
+ "h265": "H265",
+ "jpeg": "JPEG",
+ "mpeg2": "MPEG2",
+ "mpeg4": "MPEG4",
+ "vc1": "VC1",
+ "vp8": "VP8",
+ "vp9": "VP9",
+}
+
+DECODER_PROFILES = {
+ "av1": ["profile0", "profile1", "profile2"],
+ "h264": [
+ "constrained_baseline",
+ "main",
+ "high",
+ "high10",
+ "high422",
+ "high444",
+ ],
+ "h265": [
+ "main",
+ "main10",
+ "main12",
+ "main422_10",
+ "main444",
+ "main444_10",
+ "main444_12",
+ ],
+ "jpeg": ["baseline"],
+ "mpeg2": ["simple", "main"],
+ "mpeg4": ["simple", "advanced_simple", "main"],
+ "vc1": ["simple", "main", "advanced"],
+ "vp8": ["version0_3"],
+ "vp9": ["profile0", "profile1", "profile2", "profile3"],
+}
+
+
+def run_vainfo():
+ try:
+ result = subprocess.run(
+ ["vainfo"],
+ capture_output=True,
+ text=True,
+ timeout=10,
+ )
+ if result.returncode != 0:
+ return ""
+ return result.stdout + result.stderr
+ except (subprocess.TimeoutExpired, FileNotFoundError):
+ return ""
+
+
+def detect_vaapi_encoders():
+ output = run_vainfo()
+ if not output:
+ return {}
+ available = {}
+ for codec, va_name in VA_ENCODER_NAMES.items():
+ if "VAProfile{}".format(va_name) in output:
+ available[codec] = True
+ return available
+
+
+def detect_vaapi_decoders():
+ output = run_vainfo()
+ if not output:
+ return {}
+ available = {}
+ for codec, va_name in VA_DECODER_NAMES.items():
+ if "VAProfile{}".format(va_name) in output:
+ profiles = DECODER_PROFILES.get(codec, [])
+ for profile in profiles:
+ key = "{}-{}".format(codec, profile)
+ available[key] = True
+ return available
+
+
+def read_config_file(config_path):
+ config = configparser.ConfigParser()
+ config.read(config_path)
+ resources = {}
+
+ if config.has_section("platform"):
+ for key, value in config["platform"].items():
+ resources[key] = value
+
+ if config.has_section("encoder"):
+ for codec, enabled in config["encoder"].items():
+ resources["encoder_{}".format(codec)] = enabled
+
+ if config.has_section("decoder"):
+ for decoder, enabled in config["decoder"].items():
+ resources["decoder_{}".format(decoder)] = enabled
+
+ return resources
+
+
+def detect_gpu_vendor(output):
+ for vendor in ["intel", "amd", "nvidia", "mesa"]:
+ if vendor in output.lower():
+ return vendor
+ return "unknown"
+
+
+def emit_resources(resources):
+ for key, value in sorted(resources.items()):
+ print("{}: {}".format(key, value))
+
+
+def main():
+ resources = {}
+ config_path = os.environ.get("PLATFORM_CONFIG")
+
+ resources["arch"] = platform.machine()
+ resources["gpu_vendor"] = "unknown"
+
+ if config_path and os.path.exists(config_path):
+ config_resources = read_config_file(config_path)
+ resources.update(config_resources)
+ else:
+ vainfo_output = run_vainfo()
+ if vainfo_output:
+ available_encoders = detect_vaapi_encoders()
+ available_decoders = detect_vaapi_decoders()
+
+ for codec in ENCODER_MAP:
+ resources["encoder_{}".format(codec)] = str(
+ available_encoders.get(codec, False)
+ )
+
+ for profile in DECODER_MAP:
+ resources["decoder_{}".format(profile)] = str(
+ available_decoders.get(profile, False)
+ )
+
+ resources["gpu_vendor"] = detect_gpu_vendor(vainfo_output)
+
+ emit_resources(resources)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/contrib/checkbox-multimedia/example-config.ini b/contrib/checkbox-multimedia/example-config.ini
new file mode 100644
index 0000000000..4578e17791
--- /dev/null
+++ b/contrib/checkbox-multimedia/example-config.ini
@@ -0,0 +1,84 @@
+[test plan]
+unit = com.canonical.contrib::multimedia-codecs-vaapi-encode
+
+[platform]
+arch = x86_64
+gpu_vendor = intel
+
+[encoder]
+h264 = True
+h265 = True
+vp8 = True
+vp9 = True
+av1 = True
+jpeg = True
+mpeg4 = True
+
+[decoder]
+av1-profile0 = True
+av1-profile1 = True
+av1-profile2 = True
+h264-constrained_baseline = True
+h264-main = True
+h264-high = True
+h264-high10 = True
+h264-high422 = True
+h264-high444 = True
+h265-main = True
+h265-main10 = True
+h265-main12 = True
+h265-main422_10 = True
+h265-main444 = True
+h265-main444_10 = True
+h265-main444_12 = True
+jpeg-baseline = True
+mpeg2-simple = True
+mpeg2-main = True
+mpeg4-simple = True
+mpeg4-advanced_simple = True
+mpeg4-main = True
+vc1-simple = True
+vc1-main = True
+vc1-advanced = True
+vp8-version0_3 = True
+vp9-profile0 = True
+vp9-profile1 = True
+vp9-profile2 = True
+vp9-profile3 = True
+
+[manifest]
+com.canonical.contrib::has_h264_constrained_baseline_decoder_vaapi = True
+com.canonical.contrib::has_h264_main_decoder_vaapi = True
+com.canonical.contrib::has_h264_high_decoder_vaapi = True
+com.canonical.contrib::has_h264_high10_decoder_vaapi = True
+com.canonical.contrib::has_h264_high422_decoder_vaapi = True
+com.canonical.contrib::has_h264_high444_decoder_vaapi = True
+com.canonical.contrib::has_h265_main_decoder_vaapi = True
+com.canonical.contrib::has_h265_main10_decoder_vaapi = True
+com.canonical.contrib::has_h265_main12_decoder_vaapi = True
+com.canonical.contrib::has_h265_main422_10_decoder_vaapi = True
+com.canonical.contrib::has_h265_main444_decoder_vaapi = True
+com.canonical.contrib::has_h265_main444_10_decoder_vaapi = True
+com.canonical.contrib::has_h265_main444_12_decoder_vaapi = True
+com.canonical.contrib::has_jpeg_baseline_decoder_vaapi = True
+com.canonical.contrib::has_mpeg2_simple_decoder_vaapi = True
+com.canonical.contrib::has_mpeg2_main_decoder_vaapi = True
+com.canonical.contrib::has_mpeg4_simple_decoder_vaapi = True
+com.canonical.contrib::has_mpeg4_advanced_simple_decoder_vaapi = True
+com.canonical.contrib::has_mpeg4_main_decoder_vaapi = True
+com.canonical.contrib::has_vc1_simple_decoder_vaapi = True
+com.canonical.contrib::has_vc1_main_decoder_vaapi = True
+com.canonical.contrib::has_vc1_advanced_decoder_vaapi = True
+com.canonical.contrib::has_vp8_version0_3_decoder_vaapi = True
+com.canonical.contrib::has_vp9_profile0_decoder_vaapi = True
+com.canonical.contrib::has_vp9_profile1_decoder_vaapi = True
+com.canonical.contrib::has_vp9_profile2_decoder_vaapi = True
+com.canonical.contrib::has_vp9_profile3_decoder_vaapi = True
+com.canonical.contrib::has_h264_encoder_vaapi = True
+com.canonical.contrib::has_h265_encoder_vaapi = True
+com.canonical.contrib::has_vp8_encoder_vaapi = True
+com.canonical.contrib::has_vp9_encoder_vaapi = True
+com.canonical.contrib::has_av1_encoder_vaapi = True
+com.canonical.contrib::has_jpeg_encoder_vaapi = True
+com.canonical.contrib::has_mpeg4_encoder_vaapi = True
+com.canonical.contrib::has_va_api = True
diff --git a/contrib/checkbox-multimedia/manage.py b/contrib/checkbox-multimedia/manage.py
new file mode 100755
index 0000000000..9e33b8d73b
--- /dev/null
+++ b/contrib/checkbox-multimedia/manage.py
@@ -0,0 +1,29 @@
+#!/usr/bin/env python3
+#
+# This file is part of Checkbox.
+#
+# Copyright 2025 Canonical Ltd.
+#
+# Checkbox is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 3,
+# as published by the Free Software Foundation.
+#
+# Checkbox is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Checkbox. If not, see .
+#
+from plainbox.provider_manager import setup, N_
+
+setup(
+ name="checkbox-provider-multimedia",
+ namespace="com.canonical.contrib",
+ version="1.0",
+ description=N_(
+ "Checkbox provider for multimedia hardware validation" " (codecs)"
+ ),
+ gettext_domain="checkbox-provider-multimedia",
+)
diff --git a/contrib/checkbox-multimedia/tests/__init__.py b/contrib/checkbox-multimedia/tests/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/contrib/checkbox-multimedia/tests/test_gst_encoder_quality.py b/contrib/checkbox-multimedia/tests/test_gst_encoder_quality.py
new file mode 100644
index 0000000000..7a6a27993f
--- /dev/null
+++ b/contrib/checkbox-multimedia/tests/test_gst_encoder_quality.py
@@ -0,0 +1,130 @@
+import unittest
+from unittest.mock import patch, MagicMock
+import tempfile
+import os
+
+from bin.gst_encoder_quality import (
+ build_pipeline,
+ validate_output,
+ ENCODER_MAP,
+ SUPPORTED_CODECS,
+)
+
+
+class TestBuildPipeline(unittest.TestCase):
+ def test_build_pipeline_h264(self):
+ pipeline = build_pipeline("h264", 1920, 1080, 30, 60, "/tmp/test.mkv")
+ self.assertIn("gst-launch-1.0", pipeline)
+ self.assertIn("videotestsrc", pipeline)
+ self.assertIn("vah264enc", pipeline)
+ self.assertIn("num-buffers=60", pipeline)
+
+ def test_build_pipeline_h265(self):
+ pipeline = build_pipeline("h265", 1280, 720, 60, 120, "/tmp/test.mkv")
+ self.assertIn("vah265enc", pipeline)
+ self.assertIn("num-buffers=120", pipeline)
+
+ def test_build_pipeline_jpeg(self):
+ pipeline = build_pipeline("jpeg", 1920, 1080, 30, 1, "/tmp/test.jpg")
+ self.assertIn("jpegenc", pipeline)
+ self.assertIn("num-buffers=1", pipeline)
+ self.assertIn("framerate=1/1", pipeline)
+
+ def test_build_pipeline_av1(self):
+ pipeline = build_pipeline("av1", 1920, 1080, 30, 60, "/tmp/test.mkv")
+ self.assertIn("vaav1enc", pipeline)
+
+ def test_all_codecs_have_pipeline(self):
+ for codec in SUPPORTED_CODECS:
+ with self.subTest(codec=codec):
+ pipeline = build_pipeline(
+ codec, 640, 480, 30, 10, "/tmp/test.mkv"
+ )
+ self.assertIn(ENCODER_MAP[codec], pipeline)
+
+ def test_pipeline_includes_caps(self):
+ pipeline = build_pipeline("h264", 1920, 1080, 30, 60, "/tmp/test.mkv")
+ self.assertIn("video/x-raw", pipeline)
+ self.assertIn("width=1920", pipeline)
+ self.assertIn("height=1080", pipeline)
+ self.assertIn("framerate=30/1", pipeline)
+
+
+class TestValidateOutput(unittest.TestCase):
+ def test_validate_output_exists(self):
+ with tempfile.NamedTemporaryFile(delete=False) as f:
+ f.write(b"test data")
+ path = f.name
+ try:
+ self.assertTrue(validate_output(path))
+ finally:
+ os.unlink(path)
+
+ def test_validate_output_empty(self):
+ with tempfile.NamedTemporaryFile(delete=False) as f:
+ path = f.name
+ try:
+ self.assertFalse(validate_output(path))
+ finally:
+ os.unlink(path)
+
+ def test_validate_output_not_found(self):
+ self.assertFalse(validate_output("/nonexistent/file.mkv"))
+
+
+class TestMain(unittest.TestCase):
+ @patch("bin.gst_encoder_quality.subprocess.run")
+ @patch("bin.gst_encoder_quality.validate_output")
+ @patch("bin.gst_encoder_quality.tempfile.mktemp")
+ @patch("bin.gst_encoder_quality.os.unlink")
+ def test_main_success(
+ self, mock_unlink, mock_mktemp, mock_validate, mock_run
+ ):
+ mock_mktemp.return_value = "/tmp/test_output.mkv"
+ mock_run.return_value = MagicMock(returncode=0)
+ mock_validate.return_value = True
+
+ from bin.gst_encoder_quality import main
+ import sys
+
+ sys.argv = ["gst_encoder_quality.py", "--codec", "h264"]
+
+ main()
+ mock_run.assert_called_once()
+ mock_validate.assert_called_once_with("/tmp/test_output.mkv")
+ mock_unlink.assert_called_once_with("/tmp/test_output.mkv")
+
+ @patch("bin.gst_encoder_quality.subprocess.run")
+ @patch("bin.gst_encoder_quality.validate_output")
+ def test_main_pipeline_failure(self, mock_validate, mock_run):
+ mock_run.return_value = MagicMock(
+ returncode=1, stdout="", stderr="error"
+ )
+
+ from bin.gst_encoder_quality import main
+ import sys
+
+ sys.argv = ["gst_encoder_quality.py", "--codec", "h264"]
+
+ with self.assertRaises(SystemExit) as ctx:
+ main()
+ self.assertEqual(ctx.exception.code, 1)
+
+ @patch("bin.gst_encoder_quality.subprocess.run")
+ @patch("bin.gst_encoder_quality.validate_output")
+ def test_main_validation_failure(self, mock_validate, mock_run):
+ mock_run.return_value = MagicMock(returncode=0)
+ mock_validate.return_value = False
+
+ from bin.gst_encoder_quality import main
+ import sys
+
+ sys.argv = ["gst_encoder_quality.py", "--codec", "h264"]
+
+ with self.assertRaises(SystemExit) as ctx:
+ main()
+ self.assertEqual(ctx.exception.code, 1)
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/contrib/checkbox-multimedia/tests/test_platform_config_resource.py b/contrib/checkbox-multimedia/tests/test_platform_config_resource.py
new file mode 100644
index 0000000000..afcff24eb9
--- /dev/null
+++ b/contrib/checkbox-multimedia/tests/test_platform_config_resource.py
@@ -0,0 +1,157 @@
+import unittest
+from unittest.mock import patch
+import tempfile
+import os
+
+from bin.platform_config_resource import (
+ detect_vaapi_encoders,
+ detect_vaapi_decoders,
+ read_config_file,
+ emit_resources,
+ ENCODER_MAP,
+ detect_gpu_vendor,
+)
+
+
+class TestDetectVaapiEncoders(unittest.TestCase):
+ @patch("bin.platform_config_resource.run_vainfo")
+ def test_detect_encoders_found(self, mock_vainfo):
+ mock_vainfo.return_value = "VAProfileH264...\nVAProfileVP9..."
+ result = detect_vaapi_encoders()
+ self.assertIn("h264", result)
+ self.assertTrue(result["h264"])
+ self.assertIn("vp9", result)
+ self.assertTrue(result["vp9"])
+
+ @patch("bin.platform_config_resource.run_vainfo")
+ def test_detect_encoders_not_found(self, mock_vainfo):
+ mock_vainfo.return_value = "No VA-API support"
+ result = detect_vaapi_encoders()
+ for codec in ENCODER_MAP:
+ self.assertNotIn(codec, result)
+
+ @patch("bin.platform_config_resource.run_vainfo")
+ def test_detect_encoders_vainfo_empty(self, mock_vainfo):
+ mock_vainfo.return_value = ""
+ result = detect_vaapi_encoders()
+ self.assertEqual(result, {})
+
+
+class TestDetectVaapiDecoders(unittest.TestCase):
+ @patch("bin.platform_config_resource.run_vainfo")
+ def test_detect_decoders_found(self, mock_vainfo):
+ mock_vainfo.return_value = "VAProfileH264High\nVAProfileVP9Profile0"
+ result = detect_vaapi_decoders()
+ self.assertIn("h264-high", result)
+ self.assertIn("vp9-profile0", result)
+
+ @patch("bin.platform_config_resource.run_vainfo")
+ def test_detect_decoders_not_found(self, mock_vainfo):
+ mock_vainfo.return_value = ""
+ result = detect_vaapi_decoders()
+ self.assertEqual(result, {})
+
+
+class TestReadConfigFile(unittest.TestCase):
+ def test_read_config_file(self):
+ with tempfile.NamedTemporaryFile(
+ mode="w", suffix=".ini", delete=False
+ ) as f:
+ f.write("[platform]\n")
+ f.write("arch = x86_64\n")
+ f.write("gpu_vendor = intel\n")
+ f.write("[encoder]\n")
+ f.write("h264 = True\n")
+ f.write("h265 = False\n")
+ f.write("[decoder]\n")
+ f.write("av1-profile0 = True\n")
+ path = f.name
+ try:
+ result = read_config_file(path)
+ self.assertEqual(result["arch"], "x86_64")
+ self.assertEqual(result["gpu_vendor"], "intel")
+ self.assertEqual(result["encoder_h264"], "True")
+ self.assertEqual(result["encoder_h265"], "False")
+ self.assertEqual(result["decoder_av1-profile0"], "True")
+ finally:
+ os.unlink(path)
+
+ def test_read_config_file_empty(self):
+ with tempfile.NamedTemporaryFile(
+ mode="w", suffix=".ini", delete=False
+ ) as f:
+ f.write("")
+ path = f.name
+ try:
+ result = read_config_file(path)
+ self.assertEqual(result, {})
+ finally:
+ os.unlink(path)
+
+
+class TestEmitResources(unittest.TestCase):
+ def test_emit_resources(self):
+ resources = {"arch": "x86_64", "encoder_h264": "True"}
+ with patch("builtins.print") as mock_print:
+ emit_resources(resources)
+ mock_print.assert_any_call("arch: x86_64")
+ mock_print.assert_any_call("encoder_h264: True")
+
+ def test_emit_resources_sorted(self):
+ resources = {"b": "2", "a": "1"}
+ with patch("builtins.print") as mock_print:
+ emit_resources(resources)
+ calls = [call[0][0] for call in mock_print.call_args_list]
+ self.assertEqual(calls, ["a: 1", "b: 2"])
+
+
+class TestDetectGpuVendor(unittest.TestCase):
+ def test_detect_intel(self):
+ output = "vainfo: VA-API version: 1.20 (libva 2.22.0)\n"
+ output += "intel\n"
+ self.assertEqual(detect_gpu_vendor(output), "intel")
+
+ def test_detect_unknown(self):
+ output = "vainfo: no adapters found"
+ self.assertEqual(detect_gpu_vendor(output), "unknown")
+
+
+class TestMain(unittest.TestCase):
+ @patch("bin.platform_config_resource.platform.machine")
+ @patch("bin.platform_config_resource.run_vainfo")
+ @patch("bin.platform_config_resource.emit_resources")
+ def test_main_auto_detect(self, mock_emit, mock_vainfo, mock_machine):
+ mock_machine.return_value = "x86_64"
+ mock_vainfo.return_value = "VAProfileH264...\nVAProfileVP9..."
+
+ from bin.platform_config_resource import main
+
+ main()
+ mock_emit.assert_called_once()
+ resources = mock_emit.call_args[0][0]
+ self.assertEqual(resources["arch"], "x86_64")
+ self.assertEqual(resources["encoder_h264"], "True")
+
+ @patch("bin.platform_config_resource.os.environ")
+ @patch("bin.platform_config_resource.read_config_file")
+ @patch("bin.platform_config_resource.emit_resources")
+ @patch("bin.platform_config_resource.os.path.exists")
+ def test_main_with_config(
+ self, mock_exists, mock_emit, mock_read, mock_environ
+ ):
+ mock_environ.get.return_value = "/path/to/config.ini"
+ mock_exists.return_value = True
+ mock_read.return_value = {
+ "arch": "aarch64",
+ "encoder_h264": "True",
+ }
+ from bin.platform_config_resource import main
+
+ main()
+ mock_read.assert_called_once_with("/path/to/config.ini")
+ resources = mock_emit.call_args[0][0]
+ self.assertEqual(resources["arch"], "aarch64")
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/contrib/checkbox-multimedia/units/category.pxu b/contrib/checkbox-multimedia/units/category.pxu
new file mode 100644
index 0000000000..b005a67575
--- /dev/null
+++ b/contrib/checkbox-multimedia/units/category.pxu
@@ -0,0 +1,3 @@
+unit: category
+id: com.canonical.contrib.multimedia
+_name: Multimedia tests
diff --git a/contrib/checkbox-multimedia/units/image/codecs/av1/jobs.pxu b/contrib/checkbox-multimedia/units/image/codecs/av1/jobs.pxu
new file mode 100644
index 0000000000..059d5e7e0e
--- /dev/null
+++ b/contrib/checkbox-multimedia/units/image/codecs/av1/jobs.pxu
@@ -0,0 +1,43 @@
+id: multimedia/image/codecs/av1/decode/profile0/vaapi
+category_id: com.canonical.contrib.multimedia.codecs
+flags: simple
+estimated_duration: 1s
+_summary: VA-API AV1 Profile 0 decode
+imports: from com.canonical.plainbox import manifest
+requires:
+ manifest.has_av1_profile0_decoder_vaapi == 'True'
+command:
+ fluster run -d GStreamer-AV1-VAAPI-Gst1.0 -j1 -ts AV1-ARGON-PROFILE0-CORE-ANNEX-B
+
+id: multimedia/image/codecs/av1/decode/profile1/vaapi
+category_id: com.canonical.contrib.multimedia.codecs
+flags: simple
+estimated_duration: 1s
+_summary: VA-API AV1 Profile 1 decode
+imports: from com.canonical.plainbox import manifest
+requires:
+ manifest.has_av1_profile1_decoder_vaapi == 'True'
+command:
+ fluster run -d GStreamer-AV1-VAAPI-Gst1.0 -j1 -ts AV1-ARGON-PROFILE1-CORE-ANNEX-B
+
+id: multimedia/image/codecs/av1/decode/profile2/vaapi
+category_id: com.canonical.contrib.multimedia.codecs
+flags: simple
+estimated_duration: 1s
+_summary: VA-API AV1 Profile 2 decode
+imports: from com.canonical.plainbox import manifest
+requires:
+ manifest.has_av1_profile2_decoder_vaapi == 'True'
+command:
+ fluster run -d GStreamer-AV1-VAAPI-Gst1.0 -j1 -ts AV1-ARGON-PROFILE2-CORE-ANNEX-B
+
+id: multimedia/image/codecs/av1/encode/profile0/vaapi
+category_id: com.canonical.contrib.multimedia.codecs
+flags: simple
+estimated_duration: 10s
+_summary: VA-API AV1 Profile 0 encode
+imports: from com.canonical.plainbox import manifest
+requires:
+ manifest.has_av1_encoder_vaapi == 'True'
+command:
+ gst_encoder_quality.py --codec av1 --width 1920 --height 1080 --framerate 30 --num-buffers 60
diff --git a/contrib/checkbox-multimedia/units/image/codecs/av1/manifest.pxu b/contrib/checkbox-multimedia/units/image/codecs/av1/manifest.pxu
new file mode 100644
index 0000000000..f2504120b3
--- /dev/null
+++ b/contrib/checkbox-multimedia/units/image/codecs/av1/manifest.pxu
@@ -0,0 +1,19 @@
+unit: manifest entry
+id: has_av1_profile0_decoder_vaapi
+_name: Has VA-API support for AV1 Profile 0 decode
+value-type: bool
+
+unit: manifest entry
+id: has_av1_profile1_decoder_vaapi
+_name: Has VA-API support for AV1 Profile 1 decode
+value-type: bool
+
+unit: manifest entry
+id: has_av1_profile2_decoder_vaapi
+_name: Has VA-API support for AV1 Profile 2 decode
+value-type: bool
+
+unit: manifest entry
+id: has_av1_encoder_vaapi
+_name: Has VA-API support for AV1 encoding
+value-type: bool
diff --git a/contrib/checkbox-multimedia/units/image/codecs/av1/test-plan.pxu b/contrib/checkbox-multimedia/units/image/codecs/av1/test-plan.pxu
new file mode 100644
index 0000000000..54e02e3373
--- /dev/null
+++ b/contrib/checkbox-multimedia/units/image/codecs/av1/test-plan.pxu
@@ -0,0 +1,22 @@
+unit: test plan
+id: multimedia-codecs-vaapi-av1
+_name: VA-API AV1 tests
+include:
+ multimedia/image/codecs/av1/decode/profile0/vaapi
+ multimedia/image/codecs/av1/decode/profile1/vaapi
+ multimedia/image/codecs/av1/decode/profile2/vaapi
+ multimedia/image/codecs/av1/encode/profile0/vaapi
+
+unit: test plan
+id: multimedia-codecs-vaapi-av1-decode
+_name: VA-API AV1 decode tests
+include:
+ multimedia/image/codecs/av1/decode/profile0/vaapi
+ multimedia/image/codecs/av1/decode/profile1/vaapi
+ multimedia/image/codecs/av1/decode/profile2/vaapi
+
+unit: test plan
+id: multimedia-codecs-vaapi-av1-encode
+_name: VA-API AV1 encode tests
+include:
+ multimedia/image/codecs/av1/encode/profile0/vaapi
diff --git a/contrib/checkbox-multimedia/units/image/codecs/category.pxu b/contrib/checkbox-multimedia/units/image/codecs/category.pxu
new file mode 100644
index 0000000000..da3e02ccb6
--- /dev/null
+++ b/contrib/checkbox-multimedia/units/image/codecs/category.pxu
@@ -0,0 +1,3 @@
+unit: category
+id: com.canonical.contrib.multimedia.codecs
+_name: Codec tests
diff --git a/contrib/checkbox-multimedia/units/image/codecs/h264/jobs.pxu b/contrib/checkbox-multimedia/units/image/codecs/h264/jobs.pxu
new file mode 100644
index 0000000000..71436de706
--- /dev/null
+++ b/contrib/checkbox-multimedia/units/image/codecs/h264/jobs.pxu
@@ -0,0 +1,43 @@
+id: multimedia/image/codecs/h264/decode/avc_baseline_main/vaapi
+category_id: com.canonical.contrib.multimedia.codecs
+flags: simple
+estimated_duration: 1s
+_summary: VA-API H.264 Baseline / Main / Extended decode
+imports: from com.canonical.plainbox import manifest
+requires:
+ manifest.has_h264_constrained_baseline_decoder_vaapi == 'True' or manifest.has_h264_main_decoder_vaapi == 'True'
+command:
+ fluster run -d GStreamer-H.264-VAAPI-Gst1.0 -j1 -ts JVT-AVC_V1
+
+id: multimedia/image/codecs/h264/decode/frext/vaapi
+category_id: com.canonical.contrib.multimedia.codecs
+flags: simple
+estimated_duration: 1s
+_summary: VA-API H.264 High / High 10 / High 4:2:2 decode
+imports: from com.canonical.plainbox import manifest
+requires:
+ manifest.has_h264_high_decoder_vaapi == 'True' or manifest.has_h264_high10_decoder_vaapi == 'True' or manifest.has_h264_high422_decoder_vaapi == 'True'
+command:
+ fluster run -d GStreamer-H.264-VAAPI-Gst1.0 -j1 -ts JVT-FR-EXT
+
+id: multimedia/image/codecs/h264/decode/professional/vaapi
+category_id: com.canonical.contrib.multimedia.codecs
+flags: simple
+estimated_duration: 1s
+_summary: VA-API H.264 Professional profiles decode (High 4:4:4)
+imports: from com.canonical.plainbox import manifest
+requires:
+ manifest.has_h264_high444_decoder_vaapi == 'True'
+command:
+ fluster run -d GStreamer-H.264-VAAPI-Gst1.0 -j1 -ts JVT-Professional_profiles
+
+id: multimedia/image/codecs/h264/encode/main/vaapi
+category_id: com.canonical.contrib.multimedia.codecs
+flags: simple
+estimated_duration: 10s
+_summary: VA-API H.264 Main encode
+imports: from com.canonical.plainbox import manifest
+requires:
+ manifest.has_h264_encoder_vaapi == 'True'
+command:
+ gst_encoder_quality.py --codec h264 --width 1920 --height 1080 --framerate 30 --num-buffers 60
diff --git a/contrib/checkbox-multimedia/units/image/codecs/h264/manifest.pxu b/contrib/checkbox-multimedia/units/image/codecs/h264/manifest.pxu
new file mode 100644
index 0000000000..ac349675c9
--- /dev/null
+++ b/contrib/checkbox-multimedia/units/image/codecs/h264/manifest.pxu
@@ -0,0 +1,34 @@
+unit: manifest entry
+id: has_h264_constrained_baseline_decoder_vaapi
+_name: Has VA-API support for H.264 Constrained Baseline decode
+value-type: bool
+
+unit: manifest entry
+id: has_h264_main_decoder_vaapi
+_name: Has VA-API support for H.264 Main decode
+value-type: bool
+
+unit: manifest entry
+id: has_h264_high_decoder_vaapi
+_name: Has VA-API support for H.264 High decode
+value-type: bool
+
+unit: manifest entry
+id: has_h264_high10_decoder_vaapi
+_name: Has VA-API support for H.264 High 10 decode
+value-type: bool
+
+unit: manifest entry
+id: has_h264_high422_decoder_vaapi
+_name: Has VA-API support for H.264 High 4:2:2 decode
+value-type: bool
+
+unit: manifest entry
+id: has_h264_high444_decoder_vaapi
+_name: Has VA-API support for H.264 High 4:4:4 decode
+value-type: bool
+
+unit: manifest entry
+id: has_h264_encoder_vaapi
+_name: Has VA-API support for H.264 encoding
+value-type: bool
diff --git a/contrib/checkbox-multimedia/units/image/codecs/h264/test-plan.pxu b/contrib/checkbox-multimedia/units/image/codecs/h264/test-plan.pxu
new file mode 100644
index 0000000000..a3a649f0b2
--- /dev/null
+++ b/contrib/checkbox-multimedia/units/image/codecs/h264/test-plan.pxu
@@ -0,0 +1,22 @@
+unit: test plan
+id: multimedia-codecs-vaapi-h264
+_name: VA-API H.264 / AVC decode tests
+include:
+ multimedia/image/codecs/h264/decode/avc_baseline_main/vaapi
+ multimedia/image/codecs/h264/decode/frext/vaapi
+ multimedia/image/codecs/h264/decode/professional/vaapi
+ multimedia/image/codecs/h264/encode/main/vaapi
+
+unit: test plan
+id: multimedia-codecs-vaapi-h264-decode
+_name: VA-API H.264 / AVC decode tests
+include:
+ multimedia/image/codecs/h264/decode/avc_baseline_main/vaapi
+ multimedia/image/codecs/h264/decode/frext/vaapi
+ multimedia/image/codecs/h264/decode/professional/vaapi
+
+unit: test plan
+id: multimedia-codecs-vaapi-h264-encode
+_name: VA-API H.264 / AVC encode tests
+include:
+ multimedia/image/codecs/h264/encode/main/vaapi
diff --git a/contrib/checkbox-multimedia/units/image/codecs/h265/jobs.pxu b/contrib/checkbox-multimedia/units/image/codecs/h265/jobs.pxu
new file mode 100644
index 0000000000..4133a808a3
--- /dev/null
+++ b/contrib/checkbox-multimedia/units/image/codecs/h265/jobs.pxu
@@ -0,0 +1,32 @@
+id: multimedia/image/codecs/h265/decode/v1/vaapi
+category_id: com.canonical.contrib.multimedia.codecs
+flags: simple
+estimated_duration: 1s
+_summary: VA-API H.265 Main / Main 10 decode
+imports: from com.canonical.plainbox import manifest
+requires:
+ manifest.has_h265_main_decoder_vaapi == 'True' or manifest.has_h265_main10_decoder_vaapi == 'True'
+command:
+ fluster run -d GStreamer-H.265-VAAPI-Gst1.0 -j1 -ts JCT-VC-HEVC_V1
+
+id: multimedia/image/codecs/h265/decode/rext/vaapi
+category_id: com.canonical.contrib.multimedia.codecs
+flags: simple
+estimated_duration: 1s
+_summary: VA-API H.265 Range Extension decode (Main 12 / 4:2:2 / 4:4:4)
+imports: from com.canonical.plainbox import manifest
+requires:
+ manifest.has_h265_main12_decoder_vaapi == 'True' or manifest.has_h265_main422_10_decoder_vaapi == 'True' or manifest.has_h265_main444_decoder_vaapi == 'True' or manifest.has_h265_main444_10_decoder_vaapi == 'True' or manifest.has_h265_main444_12_decoder_vaapi == 'True'
+command:
+ fluster run -d GStreamer-H.265-VAAPI-Gst1.0 -j1 -ts JCT-VC-RExt
+
+id: multimedia/image/codecs/h265/encode/main/vaapi
+category_id: com.canonical.contrib.multimedia.codecs
+flags: simple
+estimated_duration: 10s
+_summary: VA-API H.265 Main encode
+imports: from com.canonical.plainbox import manifest
+requires:
+ manifest.has_h265_encoder_vaapi == 'True'
+command:
+ gst_encoder_quality.py --codec h265 --width 1920 --height 1080 --framerate 30 --num-buffers 60
diff --git a/contrib/checkbox-multimedia/units/image/codecs/h265/manifest.pxu b/contrib/checkbox-multimedia/units/image/codecs/h265/manifest.pxu
new file mode 100644
index 0000000000..bf65ba0bff
--- /dev/null
+++ b/contrib/checkbox-multimedia/units/image/codecs/h265/manifest.pxu
@@ -0,0 +1,39 @@
+unit: manifest entry
+id: has_h265_main_decoder_vaapi
+_name: Has VA-API support for H.265 Main decode
+value-type: bool
+
+unit: manifest entry
+id: has_h265_main10_decoder_vaapi
+_name: Has VA-API support for H.265 Main 10 decode
+value-type: bool
+
+unit: manifest entry
+id: has_h265_main12_decoder_vaapi
+_name: Has VA-API support for H.265 Main 12 decode
+value-type: bool
+
+unit: manifest entry
+id: has_h265_main422_10_decoder_vaapi
+_name: Has VA-API support for H.265 Main 4:2:2 10 decode
+value-type: bool
+
+unit: manifest entry
+id: has_h265_main444_decoder_vaapi
+_name: Has VA-API support for H.265 Main 4:4:4 decode
+value-type: bool
+
+unit: manifest entry
+id: has_h265_main444_10_decoder_vaapi
+_name: Has VA-API support for H.265 Main 4:4:4 10 decode
+value-type: bool
+
+unit: manifest entry
+id: has_h265_main444_12_decoder_vaapi
+_name: Has VA-API support for H.265 Main 4:4:4 12 decode
+value-type: bool
+
+unit: manifest entry
+id: has_h265_encoder_vaapi
+_name: Has VA-API support for H.265 encoding
+value-type: bool
diff --git a/contrib/checkbox-multimedia/units/image/codecs/h265/test-plan.pxu b/contrib/checkbox-multimedia/units/image/codecs/h265/test-plan.pxu
new file mode 100644
index 0000000000..4f51dad4d6
--- /dev/null
+++ b/contrib/checkbox-multimedia/units/image/codecs/h265/test-plan.pxu
@@ -0,0 +1,20 @@
+unit: test plan
+id: multimedia-codecs-vaapi-h265
+_name: VA-API H.265 / HEVC decode tests
+include:
+ multimedia/image/codecs/h265/decode/v1/vaapi
+ multimedia/image/codecs/h265/decode/rext/vaapi
+ multimedia/image/codecs/h265/encode/main/vaapi
+
+unit: test plan
+id: multimedia-codecs-vaapi-h265-decode
+_name: VA-API H.265 / HEVC decode tests
+include:
+ multimedia/image/codecs/h265/decode/v1/vaapi
+ multimedia/image/codecs/h265/decode/rext/vaapi
+
+unit: test plan
+id: multimedia-codecs-vaapi-h265-encode
+_name: VA-API H.265 / HEVC encode tests
+include:
+ multimedia/image/codecs/h265/encode/main/vaapi
diff --git a/contrib/checkbox-multimedia/units/image/codecs/jpeg/jobs.pxu b/contrib/checkbox-multimedia/units/image/codecs/jpeg/jobs.pxu
new file mode 100644
index 0000000000..8607810af7
--- /dev/null
+++ b/contrib/checkbox-multimedia/units/image/codecs/jpeg/jobs.pxu
@@ -0,0 +1,21 @@
+id: multimedia/image/codecs/jpeg/decode/baseline/vaapi
+category_id: com.canonical.contrib.multimedia.codecs
+flags: simple
+estimated_duration: 1s
+_summary: VA-API JPEG Baseline decode
+imports: from com.canonical.plainbox import manifest
+requires:
+ manifest.has_jpeg_baseline_decoder_vaapi == 'True'
+command:
+ gst-launch-1.0 -q videotestsrc num-buffers=1 ! jpegenc ! vaapidecode ! fakesink
+
+id: multimedia/image/codecs/jpeg/encode/baseline/vaapi
+category_id: com.canonical.contrib.multimedia.codecs
+flags: simple
+estimated_duration: 10s
+_summary: JPEG Baseline encode
+imports: from com.canonical.plainbox import manifest
+requires:
+ manifest.has_jpeg_encoder_vaapi == 'True'
+command:
+ gst_encoder_quality.py --codec jpeg --width 1920 --height 1080 --framerate 30 --num-buffers 1
diff --git a/contrib/checkbox-multimedia/units/image/codecs/jpeg/manifest.pxu b/contrib/checkbox-multimedia/units/image/codecs/jpeg/manifest.pxu
new file mode 100644
index 0000000000..36f2d132c7
--- /dev/null
+++ b/contrib/checkbox-multimedia/units/image/codecs/jpeg/manifest.pxu
@@ -0,0 +1,9 @@
+unit: manifest entry
+id: has_jpeg_baseline_decoder_vaapi
+_name: Has VA-API support for JPEG Baseline decode
+value-type: bool
+
+unit: manifest entry
+id: has_jpeg_encoder_vaapi
+_name: Has JPEG encoding support
+value-type: bool
diff --git a/contrib/checkbox-multimedia/units/image/codecs/jpeg/test-plan.pxu b/contrib/checkbox-multimedia/units/image/codecs/jpeg/test-plan.pxu
new file mode 100644
index 0000000000..68aaa91b97
--- /dev/null
+++ b/contrib/checkbox-multimedia/units/image/codecs/jpeg/test-plan.pxu
@@ -0,0 +1,18 @@
+unit: test plan
+id: multimedia-codecs-vaapi-jpeg
+_name: JPEG codec tests
+include:
+ multimedia/image/codecs/jpeg/decode/baseline/vaapi
+ multimedia/image/codecs/jpeg/encode/baseline/vaapi
+
+unit: test plan
+id: multimedia-codecs-vaapi-jpeg-decode
+_name: JPEG decode tests
+include:
+ multimedia/image/codecs/jpeg/decode/baseline/vaapi
+
+unit: test plan
+id: multimedia-codecs-vaapi-jpeg-encode
+_name: JPEG encode tests
+include:
+ multimedia/image/codecs/jpeg/encode/baseline/vaapi
diff --git a/contrib/checkbox-multimedia/units/image/codecs/mpeg2/jobs.pxu b/contrib/checkbox-multimedia/units/image/codecs/mpeg2/jobs.pxu
new file mode 100644
index 0000000000..668ea57828
--- /dev/null
+++ b/contrib/checkbox-multimedia/units/image/codecs/mpeg2/jobs.pxu
@@ -0,0 +1,10 @@
+id: multimedia/image/codecs/mpeg2/decode/simple_main/vaapi
+category_id: com.canonical.contrib.multimedia.codecs
+flags: simple
+estimated_duration: 1s
+_summary: VA-API MPEG-2 Simple / Main decode
+imports: from com.canonical.plainbox import manifest
+requires:
+ manifest.has_mpeg2_simple_decoder_vaapi == 'True' or manifest.has_mpeg2_main_decoder_vaapi == 'True'
+command:
+ fluster run -d GStreamer-MPEG2_VIDEO-Libav-Gst1.0 -j1 -ts MPEG2_VIDEO-MAIN
diff --git a/contrib/checkbox-multimedia/units/image/codecs/mpeg2/manifest.pxu b/contrib/checkbox-multimedia/units/image/codecs/mpeg2/manifest.pxu
new file mode 100644
index 0000000000..876b1e1412
--- /dev/null
+++ b/contrib/checkbox-multimedia/units/image/codecs/mpeg2/manifest.pxu
@@ -0,0 +1,9 @@
+unit: manifest entry
+id: has_mpeg2_simple_decoder_vaapi
+_name: Has VA-API support for MPEG-2 Simple decode
+value-type: bool
+
+unit: manifest entry
+id: has_mpeg2_main_decoder_vaapi
+_name: Has VA-API support for MPEG-2 Main decode
+value-type: bool
diff --git a/contrib/checkbox-multimedia/units/image/codecs/mpeg2/test-plan.pxu b/contrib/checkbox-multimedia/units/image/codecs/mpeg2/test-plan.pxu
new file mode 100644
index 0000000000..35e8cbda11
--- /dev/null
+++ b/contrib/checkbox-multimedia/units/image/codecs/mpeg2/test-plan.pxu
@@ -0,0 +1,11 @@
+unit: test plan
+id: multimedia-codecs-vaapi-mpeg2
+_name: VA-API MPEG-2 decode tests
+include:
+ multimedia/image/codecs/mpeg2/decode/simple_main/vaapi
+
+unit: test plan
+id: multimedia-codecs-vaapi-mpeg2-decode
+_name: VA-API MPEG-2 decode tests
+include:
+ multimedia/image/codecs/mpeg2/decode/simple_main/vaapi
diff --git a/contrib/checkbox-multimedia/units/image/codecs/mpeg4/jobs.pxu b/contrib/checkbox-multimedia/units/image/codecs/mpeg4/jobs.pxu
new file mode 100644
index 0000000000..aa5bbaa479
--- /dev/null
+++ b/contrib/checkbox-multimedia/units/image/codecs/mpeg4/jobs.pxu
@@ -0,0 +1,43 @@
+id: multimedia/image/codecs/mpeg4/decode/simple/vaapi
+category_id: com.canonical.contrib.multimedia.codecs
+flags: simple
+estimated_duration: 1s
+_summary: VA-API MPEG-4 Simple decode
+imports: from com.canonical.plainbox import manifest
+requires:
+ manifest.has_mpeg4_simple_decoder_vaapi == 'True'
+command:
+ fluster run -d GStreamer-MPEG4_VIDEO-Libav-Gst1.0 -j1 -ts MPEG4_VIDEO-SimpleProfile
+
+id: multimedia/image/codecs/mpeg4/decode/advanced_simple/vaapi
+category_id: com.canonical.contrib.multimedia.codecs
+flags: simple
+estimated_duration: 1s
+_summary: VA-API MPEG-4 Advanced Simple decode
+imports: from com.canonical.plainbox import manifest
+requires:
+ manifest.has_mpeg4_advanced_simple_decoder_vaapi == 'True'
+command:
+ fluster run -d GStreamer-MPEG4_VIDEO-Libav-Gst1.0 -j1 -ts MPEG4_VIDEO-AdvancedSimpleProfile
+
+id: multimedia/image/codecs/mpeg4/decode/main/vaapi
+category_id: com.canonical.contrib.multimedia.codecs
+flags: simple
+estimated_duration: 1s
+_summary: VA-API MPEG-4 Main decode
+imports: from com.canonical.plainbox import manifest
+requires:
+ manifest.has_mpeg4_main_decoder_vaapi == 'True'
+command:
+ gst-launch-1.0 -q videotestsrc num-buffers=30 ! vampl4enc ! vaapidecode ! fakesink
+
+id: multimedia/image/codecs/mpeg4/encode/simple/vaapi
+category_id: com.canonical.contrib.multimedia.codecs
+flags: simple
+estimated_duration: 10s
+_summary: VA-API MPEG-4 Simple encode
+imports: from com.canonical.plainbox import manifest
+requires:
+ manifest.has_mpeg4_encoder_vaapi == 'True'
+command:
+ gst_encoder_quality.py --codec mpeg4 --width 1920 --height 1080 --framerate 30 --num-buffers 60
diff --git a/contrib/checkbox-multimedia/units/image/codecs/mpeg4/manifest.pxu b/contrib/checkbox-multimedia/units/image/codecs/mpeg4/manifest.pxu
new file mode 100644
index 0000000000..902368ecce
--- /dev/null
+++ b/contrib/checkbox-multimedia/units/image/codecs/mpeg4/manifest.pxu
@@ -0,0 +1,19 @@
+unit: manifest entry
+id: has_mpeg4_simple_decoder_vaapi
+_name: Has VA-API support for MPEG-4 Simple decode
+value-type: bool
+
+unit: manifest entry
+id: has_mpeg4_advanced_simple_decoder_vaapi
+_name: Has VA-API support for MPEG-4 Advanced Simple decode
+value-type: bool
+
+unit: manifest entry
+id: has_mpeg4_main_decoder_vaapi
+_name: Has VA-API support for MPEG-4 Main decode
+value-type: bool
+
+unit: manifest entry
+id: has_mpeg4_encoder_vaapi
+_name: Has VA-API support for MPEG-4 encoding
+value-type: bool
diff --git a/contrib/checkbox-multimedia/units/image/codecs/mpeg4/test-plan.pxu b/contrib/checkbox-multimedia/units/image/codecs/mpeg4/test-plan.pxu
new file mode 100644
index 0000000000..0b4f9457aa
--- /dev/null
+++ b/contrib/checkbox-multimedia/units/image/codecs/mpeg4/test-plan.pxu
@@ -0,0 +1,22 @@
+unit: test plan
+id: multimedia-codecs-vaapi-mpeg4
+_name: VA-API MPEG-4 tests
+include:
+ multimedia/image/codecs/mpeg4/decode/simple/vaapi
+ multimedia/image/codecs/mpeg4/decode/advanced_simple/vaapi
+ multimedia/image/codecs/mpeg4/decode/main/vaapi
+ multimedia/image/codecs/mpeg4/encode/simple/vaapi
+
+unit: test plan
+id: multimedia-codecs-vaapi-mpeg4-decode
+_name: VA-API MPEG-4 decode tests
+include:
+ multimedia/image/codecs/mpeg4/decode/simple/vaapi
+ multimedia/image/codecs/mpeg4/decode/advanced_simple/vaapi
+ multimedia/image/codecs/mpeg4/decode/main/vaapi
+
+unit: test plan
+id: multimedia-codecs-vaapi-mpeg4-encode
+_name: VA-API MPEG-4 encode tests
+include:
+ multimedia/image/codecs/mpeg4/encode/simple/vaapi
diff --git a/contrib/checkbox-multimedia/units/image/codecs/test-plan.pxu b/contrib/checkbox-multimedia/units/image/codecs/test-plan.pxu
new file mode 100644
index 0000000000..281b18f0b0
--- /dev/null
+++ b/contrib/checkbox-multimedia/units/image/codecs/test-plan.pxu
@@ -0,0 +1,42 @@
+unit: test plan
+id: multimedia-codecs
+_name: Multimedia codec tests
+include:
+nested_part:
+ multimedia-codecs-vaapi-av1
+ multimedia-codecs-vaapi-h264
+ multimedia-codecs-vaapi-h265
+ multimedia-codecs-vaapi-vp8
+ multimedia-codecs-vaapi-vp9
+ multimedia-codecs-vaapi-jpeg
+ multimedia-codecs-vaapi-mpeg2
+ multimedia-codecs-vaapi-mpeg4
+ multimedia-codecs-vaapi-vc1
+
+unit: test plan
+id: multimedia-codecs-vaapi
+_name: VA-API codec conformance tests
+include:
+nested_part:
+ multimedia-codecs-vaapi-av1
+ multimedia-codecs-vaapi-h264
+ multimedia-codecs-vaapi-h265
+ multimedia-codecs-vaapi-jpeg
+ multimedia-codecs-vaapi-mpeg2
+ multimedia-codecs-vaapi-mpeg4
+ multimedia-codecs-vaapi-vc1
+ multimedia-codecs-vaapi-vp8
+ multimedia-codecs-vaapi-vp9
+
+unit: test plan
+id: multimedia-codecs-vaapi-encode
+_name: VA-API encoder conformance tests
+include:
+nested_part:
+ multimedia-codecs-vaapi-av1-encode
+ multimedia-codecs-vaapi-h264-encode
+ multimedia-codecs-vaapi-h265-encode
+ multimedia-codecs-vaapi-jpeg-encode
+ multimedia-codecs-vaapi-mpeg4-encode
+ multimedia-codecs-vaapi-vp8-encode
+ multimedia-codecs-vaapi-vp9-encode
diff --git a/contrib/checkbox-multimedia/units/image/codecs/vc1/jobs.pxu b/contrib/checkbox-multimedia/units/image/codecs/vc1/jobs.pxu
new file mode 100644
index 0000000000..bbb0cc5a38
--- /dev/null
+++ b/contrib/checkbox-multimedia/units/image/codecs/vc1/jobs.pxu
@@ -0,0 +1,48 @@
+# This file is part of Checkbox.
+#
+# Copyright 2025 Canonical Ltd.
+#
+# Checkbox is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 3,
+# as published by the Free Software Foundation.
+#
+# Checkbox is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Checkbox. If not, see .
+#
+id: multimedia/image/codecs/vc1/decode/simple/vaapi
+category_id: com.canonical.contrib.multimedia.codecs
+flags: simple
+estimated_duration: 1s
+_summary: VA-API VC-1 Simple Profile decode
+imports: from com.canonical.plainbox import manifest
+requires:
+ manifest.has_vc1_simple_decoder_vaapi == 'True'
+command:
+ fluster run -d GStreamer-VC1-VAAPI-Gst1.0 -j1 -ts VC1-TEST-VECTORS
+
+id: multimedia/image/codecs/vc1/decode/main/vaapi
+category_id: com.canonical.contrib.multimedia.codecs
+flags: simple
+estimated_duration: 1s
+_summary: VA-API VC-1 Main Profile decode
+imports: from com.canonical.plainbox import manifest
+requires:
+ manifest.has_vc1_main_decoder_vaapi == 'True'
+command:
+ fluster run -d GStreamer-VC1-VAAPI-Gst1.0 -j1 -ts VC1-TEST-VECTORS
+
+id: multimedia/image/codecs/vc1/decode/advanced/vaapi
+category_id: com.canonical.contrib.multimedia.codecs
+flags: simple
+estimated_duration: 1s
+_summary: VA-API VC-1 Advanced Profile decode
+imports: from com.canonical.plainbox import manifest
+requires:
+ manifest.has_vc1_advanced_decoder_vaapi == 'True'
+command:
+ fluster run -d GStreamer-VC1-VAAPI-Gst1.0 -j1 -ts VC1-TEST-VECTORS
diff --git a/contrib/checkbox-multimedia/units/image/codecs/vc1/manifest.pxu b/contrib/checkbox-multimedia/units/image/codecs/vc1/manifest.pxu
new file mode 100644
index 0000000000..905f2af14a
--- /dev/null
+++ b/contrib/checkbox-multimedia/units/image/codecs/vc1/manifest.pxu
@@ -0,0 +1,14 @@
+unit: manifest entry
+id: has_vc1_simple_decoder_vaapi
+_name: Has VA-API support for VC-1 Simple decode
+value-type: bool
+
+unit: manifest entry
+id: has_vc1_main_decoder_vaapi
+_name: Has VA-API support for VC-1 Main decode
+value-type: bool
+
+unit: manifest entry
+id: has_vc1_advanced_decoder_vaapi
+_name: Has VA-API support for VC-1 Advanced decode
+value-type: bool
diff --git a/contrib/checkbox-multimedia/units/image/codecs/vc1/test-plan.pxu b/contrib/checkbox-multimedia/units/image/codecs/vc1/test-plan.pxu
new file mode 100644
index 0000000000..abd13606e3
--- /dev/null
+++ b/contrib/checkbox-multimedia/units/image/codecs/vc1/test-plan.pxu
@@ -0,0 +1,15 @@
+unit: test plan
+id: multimedia-codecs-vaapi-vc1
+_name: VA-API VC-1 decode tests
+include:
+ multimedia/image/codecs/vc1/decode/simple/vaapi
+ multimedia/image/codecs/vc1/decode/main/vaapi
+ multimedia/image/codecs/vc1/decode/advanced/vaapi
+
+unit: test plan
+id: multimedia-codecs-vaapi-vc1-decode
+_name: VA-API VC-1 decode tests
+include:
+ multimedia/image/codecs/vc1/decode/simple/vaapi
+ multimedia/image/codecs/vc1/decode/main/vaapi
+ multimedia/image/codecs/vc1/decode/advanced/vaapi
diff --git a/contrib/checkbox-multimedia/units/image/codecs/vp8/jobs.pxu b/contrib/checkbox-multimedia/units/image/codecs/vp8/jobs.pxu
new file mode 100644
index 0000000000..69ad7de2a1
--- /dev/null
+++ b/contrib/checkbox-multimedia/units/image/codecs/vp8/jobs.pxu
@@ -0,0 +1,21 @@
+id: multimedia/image/codecs/vp8/decode/version0_3/vaapi
+category_id: com.canonical.contrib.multimedia.codecs
+flags: simple
+estimated_duration: 1s
+_summary: VA-API VP8 decode
+imports: from com.canonical.plainbox import manifest
+requires:
+ manifest.has_vp8_version0_3_decoder_vaapi == 'True'
+command:
+ fluster run -d GStreamer-VP8-VAAPI-Gst1.0 -j1 -ts VP8-TEST-VECTORS
+
+id: multimedia/image/codecs/vp8/encode/version0_3/vaapi
+category_id: com.canonical.contrib.multimedia.codecs
+flags: simple
+estimated_duration: 10s
+_summary: VA-API VP8 encode
+imports: from com.canonical.plainbox import manifest
+requires:
+ manifest.has_vp8_encoder_vaapi == 'True'
+command:
+ gst_encoder_quality.py --codec vp8 --width 1920 --height 1080 --framerate 30 --num-buffers 60
diff --git a/contrib/checkbox-multimedia/units/image/codecs/vp8/manifest.pxu b/contrib/checkbox-multimedia/units/image/codecs/vp8/manifest.pxu
new file mode 100644
index 0000000000..a9182f9865
--- /dev/null
+++ b/contrib/checkbox-multimedia/units/image/codecs/vp8/manifest.pxu
@@ -0,0 +1,9 @@
+unit: manifest entry
+id: has_vp8_version0_3_decoder_vaapi
+_name: Has VA-API support for VP8 decode
+value-type: bool
+
+unit: manifest entry
+id: has_vp8_encoder_vaapi
+_name: Has VA-API support for VP8 encoding
+value-type: bool
diff --git a/contrib/checkbox-multimedia/units/image/codecs/vp8/test-plan.pxu b/contrib/checkbox-multimedia/units/image/codecs/vp8/test-plan.pxu
new file mode 100644
index 0000000000..a7c656ff0c
--- /dev/null
+++ b/contrib/checkbox-multimedia/units/image/codecs/vp8/test-plan.pxu
@@ -0,0 +1,18 @@
+unit: test plan
+id: multimedia-codecs-vaapi-vp8
+_name: VA-API VP8 tests
+include:
+ multimedia/image/codecs/vp8/decode/version0_3/vaapi
+ multimedia/image/codecs/vp8/encode/version0_3/vaapi
+
+unit: test plan
+id: multimedia-codecs-vaapi-vp8-decode
+_name: VA-API VP8 decode tests
+include:
+ multimedia/image/codecs/vp8/decode/version0_3/vaapi
+
+unit: test plan
+id: multimedia-codecs-vaapi-vp8-encode
+_name: VA-API VP8 encode tests
+include:
+ multimedia/image/codecs/vp8/encode/version0_3/vaapi
diff --git a/contrib/checkbox-multimedia/units/image/codecs/vp9/jobs.pxu b/contrib/checkbox-multimedia/units/image/codecs/vp9/jobs.pxu
new file mode 100644
index 0000000000..01d99bc8ab
--- /dev/null
+++ b/contrib/checkbox-multimedia/units/image/codecs/vp9/jobs.pxu
@@ -0,0 +1,32 @@
+id: multimedia/image/codecs/vp9/decode/8bit/vaapi
+category_id: com.canonical.contrib.multimedia.codecs
+flags: simple
+estimated_duration: 1s
+_summary: VA-API VP9 8-bit decode (Profile 0 / 1)
+imports: from com.canonical.plainbox import manifest
+requires:
+ manifest.has_vp9_profile0_decoder_vaapi == 'True' or manifest.has_vp9_profile1_decoder_vaapi == 'True'
+command:
+ fluster run -d GStreamer-VP9-VAAPI-Gst1.0 -j1 -ts VP9-TEST-VECTORS
+
+id: multimedia/image/codecs/vp9/decode/high/vaapi
+category_id: com.canonical.contrib.multimedia.codecs
+flags: simple
+estimated_duration: 1s
+_summary: VA-API VP9 high bit-depth decode (Profile 2 / 3)
+imports: from com.canonical.plainbox import manifest
+requires:
+ manifest.has_vp9_profile2_decoder_vaapi == 'True' or manifest.has_vp9_profile3_decoder_vaapi == 'True'
+command:
+ fluster run -d GStreamer-VP9-VAAPI-Gst1.0 -j1 -ts VP9-TEST-VECTORS-HIGH
+
+id: multimedia/image/codecs/vp9/encode/profile0/vaapi
+category_id: com.canonical.contrib.multimedia.codecs
+flags: simple
+estimated_duration: 10s
+_summary: VA-API VP9 Profile 0 encode
+imports: from com.canonical.plainbox import manifest
+requires:
+ manifest.has_vp9_encoder_vaapi == 'True'
+command:
+ gst_encoder_quality.py --codec vp9 --width 1920 --height 1080 --framerate 30 --num-buffers 60
diff --git a/contrib/checkbox-multimedia/units/image/codecs/vp9/manifest.pxu b/contrib/checkbox-multimedia/units/image/codecs/vp9/manifest.pxu
new file mode 100644
index 0000000000..a45d2f9ffd
--- /dev/null
+++ b/contrib/checkbox-multimedia/units/image/codecs/vp9/manifest.pxu
@@ -0,0 +1,24 @@
+unit: manifest entry
+id: has_vp9_profile0_decoder_vaapi
+_name: Has VA-API support for VP9 Profile 0 decode
+value-type: bool
+
+unit: manifest entry
+id: has_vp9_profile1_decoder_vaapi
+_name: Has VA-API support for VP9 Profile 1 decode
+value-type: bool
+
+unit: manifest entry
+id: has_vp9_profile2_decoder_vaapi
+_name: Has VA-API support for VP9 Profile 2 decode
+value-type: bool
+
+unit: manifest entry
+id: has_vp9_profile3_decoder_vaapi
+_name: Has VA-API support for VP9 Profile 3 decode
+value-type: bool
+
+unit: manifest entry
+id: has_vp9_encoder_vaapi
+_name: Has VA-API support for VP9 encoding
+value-type: bool
diff --git a/contrib/checkbox-multimedia/units/image/codecs/vp9/test-plan.pxu b/contrib/checkbox-multimedia/units/image/codecs/vp9/test-plan.pxu
new file mode 100644
index 0000000000..db36c126dd
--- /dev/null
+++ b/contrib/checkbox-multimedia/units/image/codecs/vp9/test-plan.pxu
@@ -0,0 +1,20 @@
+unit: test plan
+id: multimedia-codecs-vaapi-vp9
+_name: VA-API VP9 decode tests
+include:
+ multimedia/image/codecs/vp9/decode/8bit/vaapi
+ multimedia/image/codecs/vp9/decode/high/vaapi
+ multimedia/image/codecs/vp9/encode/profile0/vaapi
+
+unit: test plan
+id: multimedia-codecs-vaapi-vp9-decode
+_name: VA-API VP9 decode tests
+include:
+ multimedia/image/codecs/vp9/decode/8bit/vaapi
+ multimedia/image/codecs/vp9/decode/high/vaapi
+
+unit: test plan
+id: multimedia-codecs-vaapi-vp9-encode
+_name: VA-API VP9 encode tests
+include:
+ multimedia/image/codecs/vp9/encode/profile0/vaapi
diff --git a/contrib/checkbox-multimedia/units/platform-config/jobs.pxu b/contrib/checkbox-multimedia/units/platform-config/jobs.pxu
new file mode 100644
index 0000000000..a2ef5b996a
--- /dev/null
+++ b/contrib/checkbox-multimedia/units/platform-config/jobs.pxu
@@ -0,0 +1,15 @@
+unit: manifest entry
+id: has_va_api
+_prompt: Does this machine have VA-API support?
+_name: VA-API
+value-type: bool
+
+id: platform_config_resource
+plugin: resource
+category_id: com.canonical.contrib.multimedia
+estimated_duration: 1
+_summary: Platform configuration resource
+_description: Detects platform capabilities (GPU vendor, architecture, available codecs)
+environ: PLATFORM_CONFIG
+command:
+ platform_config_resource.py
diff --git a/contrib/checkbox-multimedia/units/platform-config/test-plan.pxu b/contrib/checkbox-multimedia/units/platform-config/test-plan.pxu
new file mode 100644
index 0000000000..ac072461de
--- /dev/null
+++ b/contrib/checkbox-multimedia/units/platform-config/test-plan.pxu
@@ -0,0 +1,5 @@
+unit: test plan
+id: multimedia-platform-config
+_name: Platform configuration detection
+include:
+ platform_config_resource
diff --git a/contrib/checkbox-multimedia/units/test-plan.pxu b/contrib/checkbox-multimedia/units/test-plan.pxu
new file mode 100644
index 0000000000..16b65c8f41
--- /dev/null
+++ b/contrib/checkbox-multimedia/units/test-plan.pxu
@@ -0,0 +1,14 @@
+unit: test plan
+id: multimedia-all
+_name: All multimedia tests
+include:
+nested_part:
+ multimedia-codecs-vaapi-av1
+ multimedia-codecs-vaapi-h264
+ multimedia-codecs-vaapi-h265
+ multimedia-codecs-vaapi-vp8
+ multimedia-codecs-vaapi-vp9
+ multimedia-codecs-vaapi-jpeg
+ multimedia-codecs-vaapi-mpeg2
+ multimedia-codecs-vaapi-mpeg4
+ multimedia-codecs-vaapi-vc1