diff --git a/Runner/suites/Gstreamer/Audio/Gstreamer_Audio_Tests.yaml b/Runner/suites/Gstreamer/Audio/Gstreamer_Audio_Tests.yaml new file mode 100644 index 00000000..a39c44d1 --- /dev/null +++ b/Runner/suites/Gstreamer/Audio/Gstreamer_Audio_Tests.yaml @@ -0,0 +1,24 @@ +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause-Clear + +metadata: + format: Lava-Test Test Definition 1.0 + name: Gstreamer_Audio_Tests + description: "GStreamer Audio Pipeline Tests - Encode/Decode validation using PulseAudio" + maintainer: + - qualcomm-linux-test@quicinc.com + os: + - linux + scope: + - functional + +params: + TIMEOUT: "120" + STRICT: "false" + DMESG_SCAN: "true" + LOGLEVEL: "INFO" + +run: + steps: + - cd Runner/suites/Gstreamer/Audio + - ./run.sh --all diff --git a/Runner/suites/Gstreamer/Audio/README.md b/Runner/suites/Gstreamer/Audio/README.md new file mode 100644 index 00000000..5d902ce1 --- /dev/null +++ b/Runner/suites/Gstreamer/Audio/README.md @@ -0,0 +1,322 @@ +# GStreamer Audio Tests + +## Overview + +This test suite validates GStreamer audio pipelines using PulseAudio for capture and playback on Qualcomm platforms. It tests audio encoding (capture to WAV) and decoding (WAV playback) functionality. + +## Test Cases + +### 1. audio-encode +**Pipeline:** +```bash +gst-launch-1.0 pulsesrc ! audioconvert ! audioresample ! identity eos-after=2000 ! wavenc ! filesink location=./output_audio.wav +``` + +**Description:** +- Captures audio from PulseAudio source +- Converts and resamples audio format +- Uses identity element with eos-after=2000 (2 seconds) for controlled capture +- Encodes to WAV format +- Saves to output_audio.wav + +**Validation:** +- Pipeline completes without errors +- Output file is created +- No ERROR/WARNING messages in GStreamer output +- No dmesg errors (if DMESG_SCAN enabled) + +### 2. audio-decode +**Pipeline:** +```bash +gst-launch-1.0 filesrc location=./output_audio.wav ! wavparse ! audioconvert ! audioresample ! pulsesink +``` + +**Description:** +- Reads WAV file created by audio-encode test +- Parses WAV format +- Converts and resamples audio +- Plays back through PulseAudio sink + +**Validation:** +- Pipeline completes without errors +- Audio playback successful +- No ERROR/WARNING messages in GStreamer output +- No dmesg errors (if DMESG_SCAN enabled) + +## Prerequisites + +### Required Packages +```bash +# GStreamer core and plugins +gstreamer1.0-tools +gstreamer1.0-plugins-base +gstreamer1.0-plugins-good + +# PulseAudio support +pulseaudio +gstreamer1.0-pulseaudio +``` + +### Audio Hardware +- Working audio capture device (microphone) +- Working audio playback device (speakers/headphones) +- PulseAudio server running + +### Verification +```bash +# Check PulseAudio status +pulseaudio --check +echo $? # Should return 0 + +# List audio sources +pactl list sources short + +# List audio sinks +pactl list sinks short + +# Test audio capture +gst-launch-1.0 pulsesrc ! fakesink + +# Test audio playback +gst-launch-1.0 audiotestsrc ! pulsesink +``` + +## Usage + +### Run All Tests +```bash +./run.sh --all +``` + +### Run Specific Test +```bash +./run.sh --test audio-encode +./run.sh --test audio-decode +``` + +### List Available Tests +```bash +./run.sh --list +``` + +### Custom Timeout +```bash +./run.sh --all --timeout 180 +``` + +### Enable Strict Mode +```bash +./run.sh --all --strict +``` + +### Disable dmesg Scanning +```bash +./run.sh --all --no-dmesg +``` + +## CLI Options + +| Option | Description | Default | +|--------|-------------|---------| +| `--all` | Run all audio tests | - | +| `--test ` | Run specific test | - | +| `--list` | List available tests | - | +| `--timeout ` | Timeout per test | 120 | +| `--repeat ` | Repeat count | 1 | +| `--repeat-policy ` | Pass policy | all | +| `--strict` | Fail on warnings | false | +| `--no-dmesg` | Skip dmesg scan | false | +| `--help` | Show help | - | + +## Output Files + +### Result Files +- `Gstreamer_Audio_Tests.res` - Overall PASS/FAIL/SKIP status +- `logs_Gstreamer_Audio_Tests/` - Detailed logs directory + - `audio-encode.log` - Encode test output + - `audio-decode.log` - Decode test output + - `summary.txt` - Test summary + - `results.csv` - CSV format results + - `.junit_cases.xml` - JUnit XML for CI + - `dmesg_snapshot.log` - Kernel messages + - `dmesg_errors.log` - Kernel errors (if any) + +### Generated Files +- `output_audio.wav` - Audio file created by encode test + +## Validation Criteria + +### Pass Criteria +✓ Pipeline executes without errors +✓ Output files created successfully +✓ No ERROR messages in GStreamer output +✓ No WARNING messages (if --strict enabled) +✓ No kernel errors in dmesg (if DMESG_SCAN enabled) +✓ Exit code 0 from gst-launch-1.0 + +### Fail Criteria +✗ Pipeline execution fails +✗ ERROR messages in GStreamer output +✗ WARNING messages (in strict mode) +✗ Timeout exceeded +✗ Output file not created +✗ Kernel errors detected + +## Troubleshooting + +### PulseAudio Not Running +```bash +# Start PulseAudio +pulseaudio --start + +# Check status +pulseaudio --check +``` + +### No Audio Devices +```bash +# List available sources +pactl list sources short + +# List available sinks +pactl list sinks short + +# Set default source/sink +pactl set-default-source +pactl set-default-sink +``` + +### Permission Issues +```bash +# Add user to audio group +sudo usermod -a -G audio $USER + +# Verify group membership +groups $USER +``` + +### Audio Capture Fails +```bash +# Test with audiotestsrc instead +gst-launch-1.0 audiotestsrc ! audioconvert ! audioresample ! wavenc ! filesink location=test.wav + +# Check microphone permissions +ls -la /dev/snd/ + +# Test with arecord +arecord -d 2 -f cd test.wav +``` + +### Audio Playback Fails +```bash +# Test with speaker-test +speaker-test -t sine -f 440 -c 2 + +# Test with aplay +aplay test.wav + +# Check volume levels +pactl list sinks | grep -i volume +``` + +### Pipeline Negotiation Errors +```bash +# Enable debug output +GST_DEBUG=3 gst-launch-1.0 pulsesrc ! audioconvert ! audioresample ! wavenc ! filesink location=test.wav + +# Check supported formats +gst-inspect-1.0 pulsesrc +gst-inspect-1.0 wavenc +``` + +### Common Error Messages + +**"Could not open audio device for recording"** +- PulseAudio not running or no capture device available +- Check: `pactl list sources short` + +**"Could not open audio device for playback"** +- PulseAudio not running or no playback device available +- Check: `pactl list sinks short` + +**"Failed to connect stream"** +- PulseAudio server connection issue +- Restart: `pulseaudio --kill && pulseaudio --start` + +**"No space left on device"** +- Insufficient disk space for output file +- Check: `df -h .` + +## Environment Variables + +```bash +# GStreamer debug level (0-9) +export GST_DEBUG=3 + +# PulseAudio server +export PULSE_SERVER=unix:/run/user/1000/pulse/native + +# Audio buffer size +export PULSE_LATENCY_MSEC=50 +``` + +## CI/CD Integration + +### LAVA Integration +```yaml +- test: + definitions: + - repository: https://git.codelinaro.org/clo/le/le-test-automation/qcom-linux-testkit + from: git + path: Runner/suites/Gstreamer/Audio/Gstreamer_Audio_Tests.yaml + name: gstreamer-audio-tests + parameters: + TIMEOUT: "120" + STRICT: "false" + DMESG_SCAN: "true" +``` + +### Standalone Execution +```bash +# Basic run +cd /path/to/qcom-linux-testkit/Runner/suites/Gstreamer/Audio +./run.sh --all + +# With custom parameters +TIMEOUT=180 STRICT=true ./run.sh --all +``` + +## Platform Support + +| Platform | Status | Notes | +|----------|--------|-------| +| QCS6490 | ✓ Supported | Full audio support | +| QCS8550 | ✓ Supported | Full audio support | +| QCS8650 | ✓ Supported | Full audio support | +| SA8775P | ✓ Supported | Full audio support | +| SA8650P | ✓ Supported | Full audio support | +| SA8255P | ✓ Supported | Full audio support | + +## Known Issues + +1. **PulseAudio Latency**: Some platforms may experience audio latency + - Workaround: Adjust `PULSE_LATENCY_MSEC` environment variable + +2. **Device Busy**: Audio device may be in use by another application + - Workaround: Stop other audio applications or use `fuser -k /dev/snd/*` + +3. **Sample Rate Mismatch**: Some devices may not support default sample rates + - Workaround: Explicitly set sample rate in pipeline with `audio/x-raw,rate=48000` + +## Additional Resources + +- [GStreamer Documentation](https://gstreamer.freedesktop.org/documentation/) +- [PulseAudio Documentation](https://www.freedesktop.org/wiki/Software/PulseAudio/) +- [GStreamer PulseAudio Plugin](https://gstreamer.freedesktop.org/documentation/pulseaudio/) +- [Audio Debugging Guide](https://wiki.archlinux.org/title/PulseAudio/Troubleshooting) + +## License + +``` +Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +SPDX-License-Identifier: BSD-3-Clause-Clear diff --git a/Runner/suites/Gstreamer/Audio/run.sh b/Runner/suites/Gstreamer/Audio/run.sh new file mode 100644 index 00000000..2483f8c7 --- /dev/null +++ b/Runner/suites/Gstreamer/Audio/run.sh @@ -0,0 +1,367 @@ +#!/bin/sh +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause-Clear +# GStreamer Audio Encode/Decode Test Runner + +# ---------- Repo env + helpers ---------- +SCRIPT_DIR="$(cd "$(dirname "$0")" || exit 1; pwd)" +INIT_ENV="" +SEARCH="$SCRIPT_DIR" + +while [ "$SEARCH" != "/" ]; do + if [ -f "$SEARCH/init_env" ]; then + INIT_ENV="$SEARCH/init_env" + break + fi + SEARCH=$(dirname "$SEARCH") +done + +if [ -z "$INIT_ENV" ]; then + echo "[ERROR] Could not find init_env (starting at $SCRIPT_DIR)" >&2 + exit 1 +fi + +# Only source once (idempotent) +if [ -z "${__INIT_ENV_LOADED:-}" ]; then + # shellcheck disable=SC1090 + . "$INIT_ENV" + __INIT_ENV_LOADED=1 +fi + +# shellcheck disable=SC1090 +. "$INIT_ENV" +# shellcheck disable=SC1091 +. "$TOOLS/functestlib.sh" + +TESTNAME="Gstreamer_Audio_Tests" +RES_FILE="./${TESTNAME}.res" + +# --- Defaults / knobs --- +if [ -z "${TIMEOUT:-}" ]; then TIMEOUT="60"; fi +if [ -z "${STRICT:-}" ]; then STRICT="0"; fi +if [ -z "${DMESG_SCAN:-}" ]; then DMESG_SCAN="1"; fi +if [ -z "${STOP_ON_FAIL:-}" ]; then STOP_ON_FAIL="0"; fi +if [ -z "${LOGLEVEL:-}" ]; then LOGLEVEL="15"; fi +if [ -z "${REPEAT:-}" ]; then REPEAT="1"; fi +if [ -z "${REPEAT_DELAY:-}" ]; then REPEAT_DELAY="0"; fi +if [ -z "${REPEAT_POLICY:-}" ]; then REPEAT_POLICY="all"; fi +JUNIT_OUT="" +VERBOSE="0" +POST_TEST_SLEEP="0" + +# --- Audio settings --- +AUDIO_OUTPUT="./output_audio.wav" +AUDIO_EOS_BUFFERS="${AUDIO_EOS_BUFFERS:-2000}" + +# --- GStreamer pipelines --- +AUDIO_ENCODE_PIPELINE="pulsesrc ! audioconvert ! audioresample ! identity eos-after=${AUDIO_EOS_BUFFERS} ! wavenc ! filesink location=${AUDIO_OUTPUT}" + +AUDIO_DECODE_PIPELINE="filesrc location=${AUDIO_OUTPUT} ! wavparse ! audioconvert ! audioresample ! pulsesink" + +usage() { + cat <"$RES_FILE" + exit 0 +fi + +# --- Resolve test path --- +test_path="$(find_test_case_by_name "$TESTNAME" 2>/dev/null || true)" +if [ -z "$test_path" ] || [ ! -d "$test_path" ]; then + test_path="$SCRIPT_DIR" +fi + +if ! cd "$test_path"; then + log_error "cd failed: $test_path" + printf '%s\n' "$TESTNAME FAIL" >"$RES_FILE" + exit 1 +fi + +# --- Create log directory --- +LOG_DIR="./logs_${TESTNAME}" +mkdir -p "$LOG_DIR" +export LOG_DIR + +# --- Check GStreamer plugins --- +for plugin in pulsesrc pulsesink wavenc wavparse audioconvert audioresample identity; do + if ! gst-inspect-1.0 "$plugin" >/dev/null 2>&1; then + log_skip "$TESTNAME SKIP - $plugin plugin not available" + printf '%s\n' "$TESTNAME SKIP" >"$RES_FILE" + exit 0 + fi +done + +# --- JUnit prep / results files --- +JUNIT_TMP="$LOG_DIR/.junit_cases.xml" +: > "$JUNIT_TMP" +printf '%s\n' "mode,id,result,name,elapsed,pass_runs,fail_runs" > "$LOG_DIR/results.csv" +: > "$LOG_DIR/summary.txt" + +# --- Helper functions --- +run_gst_pipeline() { + id="$1" + pipeline="$2" + logf="$LOG_DIR/${id}.log" + + log_info "[$id] Running GStreamer pipeline" + start_time=$(date +%s) + + if command -v run_with_timeout >/dev/null 2>&1; then + if run_with_timeout "$TIMEOUT" gst-launch-1.0 -v $pipeline >"$logf" 2>&1; then + end_time=$(date +%s) + elapsed=$((end_time - start_time)) + if check_pipeline_errors "$logf"; then + log_fail "[$id] FAIL - Pipeline errors detected (${elapsed}s)" + return 1 + else + log_pass "[$id] PASS (${elapsed}s)" + return 0 + fi + else + rc=$? + end_time=$(date +%s) + elapsed=$((end_time - start_time)) + if [ "$rc" -eq 124 ]; then + log_fail "[$id] FAIL - timeout after ${TIMEOUT}s" + else + log_fail "[$id] FAIL - gst-launch-1.0 exited with code $rc" + fi + return 1 + fi + else + if gst-launch-1.0 -v $pipeline >"$logf" 2>&1; then + end_time=$(date +%s) + elapsed=$((end_time - start_time)) + if check_pipeline_errors "$logf"; then + log_fail "[$id] FAIL - Pipeline errors detected (${elapsed}s)" + return 1 + else + log_pass "[$id] PASS (${elapsed}s)" + return 0 + fi + else + rc=$? + log_fail "[$id] FAIL - gst-launch-1.0 exited with code $rc" + return 1 + fi + fi +} + +check_pipeline_errors() { + logf="$1" + + if grep -q "ERROR:" "$logf"; then + return 0 + fi + + if [ "$STRICT" -eq 1 ] && grep -q "WARNING:" "$logf"; then + return 0 + fi + + if grep -q "negotiation failed" "$logf"; then + return 0 + fi + + if grep -q "buffer pool activation failed" "$logf"; then + return 0 + fi + + return 1 +} + +# --- Run tests --- +log_info "----------------------------------------------------------------------" +log_info "---------------------- Starting $TESTNAME ----------------------------" +log_info "TIMEOUT=${TIMEOUT}s LOGLEVEL=$LOGLEVEL REPEAT=$REPEAT" + +total=0 +pass=0 +fail=0 +skip=0 +suite_rc=0 + +# --- Test 1: Audio Encode --- +id="audio-encode" +name="Audio Encode using pulsesrc → wavenc" +total=$((total + 1)) + +log_info "----------------------------------------------------------------------" +log_info "[$id] START - $name" + +pass_runs=0 +fail_runs=0 +case_skipped=0 +rep=1 + +while [ "$rep" -le "$REPEAT" ]; do + if [ "$REPEAT" -gt 1 ]; then + log_info "[$id] repeat $rep/$REPEAT" + fi + + if run_gst_pipeline "$id" "$AUDIO_ENCODE_PIPELINE"; then + pass_runs=$((pass_runs + 1)) + else + fail_runs=$((fail_runs + 1)) + fi + + if [ "$rep" -lt "$REPEAT" ] && [ "$REPEAT_DELAY" -gt 0 ]; then + sleep "$REPEAT_DELAY" + fi + + rep=$((rep + 1)) +done + +final="FAIL" +case "$REPEAT_POLICY" in + any) [ "$pass_runs" -ge 1 ] && final="PASS" ;; + all|*) [ "$fail_runs" -eq 0 ] && final="PASS" ;; +esac + +if [ "$DMESG_SCAN" -eq 1 ] && command -v scan_dmesg_errors >/dev/null 2>&1; then + if scan_dmesg_errors "$LOG_DIR" "audio|sound|alsa|pulse" "dummy regulator|not found"; then + log_warn "[$id] dmesg reported errors (STRICT=$STRICT)" + [ "$STRICT" -eq 1 ] && final="FAIL" + fi +fi + +printf '%s\n' "$id $final $name" >> "$LOG_DIR/summary.txt" +printf '%s\n' "encode,$id,$final,$name,0,$pass_runs,$fail_runs" >> "$LOG_DIR/results.csv" + +if [ "$final" = "PASS" ]; then + pass=$((pass + 1)) +else + fail=$((fail + 1)) + suite_rc=1 + [ "$STOP_ON_FAIL" -eq 1 ] && exit 1 +fi + +[ "$POST_TEST_SLEEP" -gt 0 ] && sleep "$POST_TEST_SLEEP" + +# --- Test 2: Audio Decode --- +id="audio-decode" +name="Audio Decode using wavparse → pulsesink" +total=$((total + 1)) + +log_info "----------------------------------------------------------------------" +log_info "[$id] START - $name" + +pass_runs=0 +fail_runs=0 +case_skipped=0 +rep=1 + +# Check if audio file exists from encode test +if [ ! -f "$AUDIO_OUTPUT" ]; then + log_skip "[$id] SKIP - audio output file not available (run audio-encode first)" + case_skipped=1 + skip=$((skip + 1)) + final="SKIP" +else + while [ "$rep" -le "$REPEAT" ]; do + if [ "$REPEAT" -gt 1 ]; then + log_info "[$id] repeat $rep/$REPEAT" + fi + + if run_gst_pipeline "$id" "$AUDIO_DECODE_PIPELINE"; then + pass_runs=$((pass_runs + 1)) + else + fail_runs=$((fail_runs + 1)) + fi + + if [ "$rep" -lt "$REPEAT" ] && [ "$REPEAT_DELAY" -gt 0 ]; then + sleep "$REPEAT_DELAY" + fi + + rep=$((rep + 1)) + done + + final="FAIL" + case "$REPEAT_POLICY" in + any) [ "$pass_runs" -ge 1 ] && final="PASS" ;; + all|*) [ "$fail_runs" -eq 0 ] && final="PASS" ;; + esac + + if [ "$DMESG_SCAN" -eq 1 ] && command -v scan_dmesg_errors >/dev/null 2>&1; then + if scan_dmesg_errors "$LOG_DIR" "audio|sound|alsa|pulse" "dummy regulator|not found"; then + log_warn "[$id] dmesg reported errors (STRICT=$STRICT)" + [ "$STRICT" -eq 1 ] && final="FAIL" + fi + fi +fi + +printf '%s\n' "$id $final $name" >> "$LOG_DIR/summary.txt" +printf '%s\n' "decode,$id,$final,$name,0,$pass_runs,$fail_runs" >> "$LOG_DIR/results.csv" + +if [ "$final" = "PASS" ]; then + pass=$((pass + 1)) +elif [ "$final" = "FAIL" ]; then + fail=$((fail + 1)) + suite_rc=1 +fi + +# --- Summary --- +log_info "----------------------------------------------------------------------" +log_info "Summary: total=$total pass=$pass fail=$fail skip=$skip" + +if [ -s "$LOG_DIR/summary.txt" ]; then + log_info "----------------------------------------------------------------------" + log_info "Per-test results:" + while IFS= read -r line; do + log_info "$line" + done < "$LOG_DIR/summary.txt" +fi + +# --- JUnit finalize --- +if [ -n "$JUNIT_OUT" ]; then + { + printf '\n' "$TESTNAME" "$total" "$fail" "$skip" + cat "$JUNIT_TMP" + printf '\n' + } > "$JUNIT_OUT" + log_info "Wrote JUnit: $JUNIT_OUT" +fi + +# Overall result +if [ "$pass" -eq 0 ] && [ "$fail" -eq 0 ] && [ "$skip" -gt 0 ]; then + log_skip "$TESTNAME: SKIP" + printf '%s\n' "$TESTNAME SKIP" >"$RES_FILE" + exit 0 +fi + +if [ "$suite_rc" -eq 0 ]; then + log_pass "$TESTNAME: PASS" + printf '%s\n' "$TESTNAME PASS" >"$RES_FILE" + exit 0 +else + log_fail "$TESTNAME: FAIL" + printf '%s\n' "$TESTNAME FAIL" >"$RES_FILE" + exit 1 +fi diff --git a/Runner/suites/Gstreamer/Camera/Gstreamer_Camera_Tests.yaml b/Runner/suites/Gstreamer/Camera/Gstreamer_Camera_Tests.yaml new file mode 100644 index 00000000..07bbb545 --- /dev/null +++ b/Runner/suites/Gstreamer/Camera/Gstreamer_Camera_Tests.yaml @@ -0,0 +1,42 @@ +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause-Clear + +metadata: + format: Lava-Test Test Definition 1.0 + name: Gstreamer_Camera_Tests + description: "GStreamer Camera Tests - Camera capture, encoding, preview, and snapshot validation using V4L2" + maintainer: + - qualcomm-linux-test@quicinc.com + os: + - linux + +params: + # Timeout for each test in seconds + TIMEOUT: "120" + # Camera device path + CAMERA_DEVICE: "/dev/video0" + # Capture duration in seconds + CAPTURE_DURATION: "5" + # Enable strict mode (treat warnings as failures) + STRICT: "false" + # Enable dmesg scanning + DMESG_SCAN: "true" + # Repeat count + REPEAT: "1" + # Repeat policy (all or any) + REPEAT_POLICY: "all" + +run: + steps: + - cd Runner/suites/Gstreamer/Camera + - chmod +x ./run.sh + - | + ./run.sh --all \ + --timeout "${TIMEOUT}" \ + --camera "${CAMERA_DEVICE}" \ + --duration "${CAPTURE_DURATION}" \ + --repeat "${REPEAT}" \ + --repeat-policy "${REPEAT_POLICY}" \ + $([ "${STRICT}" = "true" ] && echo "--strict") \ + $([ "${DMESG_SCAN}" = "false" ] && echo "--no-dmesg") \ + || true diff --git a/Runner/suites/Gstreamer/Camera/README.md b/Runner/suites/Gstreamer/Camera/README.md new file mode 100644 index 00000000..d9d174b8 --- /dev/null +++ b/Runner/suites/Gstreamer/Camera/README.md @@ -0,0 +1,546 @@ +# GStreamer Camera Tests + +**Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.** +**SPDX-License-Identifier: BSD-3-Clause-Clear** + +--- + +## Overview + +This test suite validates GStreamer camera capture functionality on Qualcomm Linux platforms. Tests cover camera preview, hardware-accelerated encoding (H.264/H.265), and image capture (PNG/JPEG) using libcamera source and hardware encoders. + +### Test Coverage + +**5 Test Cases:** +- **Camera Preview**: Live camera preview to Wayland display +- **Camera H.264 Encode**: Capture and encode to H.264/MP4 +- **Camera H.265 Encode**: Capture and encode to H.265/MP4 +- **Camera Snapshot**: Capture single frame as PNG +- **Camera JPEG Encode**: Capture single frame as JPEG + +**Key Features:** +- Uses libcamera source (`libcamerasrc`) for modern camera stack +- Hardware-accelerated encoding with V4L2 encoders +- Automatic camera detection via libcamera +- Configurable capture duration +- Multiple output formats (MP4, PNG, JPEG) +- Real-time preview capability + +--- + +## Quick Start + +```bash +cd Runner/suites/Gstreamer/Camera +./run.sh --all +``` + +--- + +## Test Pipelines + +### Camera Preview (1080p @ 30fps, 5 seconds) +```bash +libcamerasrc num-buffers=150 ! \ + video/x-raw,width=1920,height=1080,framerate=30/1 ! \ + videoconvert ! \ + waylandsink +``` +- **Purpose**: Validate camera capture and display pipeline +- **Duration**: 5 seconds (150 frames) +- **Output**: Live preview on Wayland display +- **Use Case**: Basic camera functionality test +- **Note**: Uses libcamera for camera access + +### Camera H.264 Encode (1080p @ 30fps, 5 seconds) +```bash +libcamerasrc num-buffers=150 ! \ + video/x-raw,width=1920,height=1080,framerate=30/1 ! \ + videoconvert ! \ + video/x-raw,format=NV12 ! \ + v4l2h264enc extra-controls="controls,video_bitrate=4000000" ! \ + h264parse ! \ + qtmux ! \ + filesink location=camera_h264.mp4 +``` +- **Purpose**: Validate camera capture with H.264 hardware encoding +- **Duration**: 5 seconds (150 frames) +- **Bitrate**: 4 Mbps +- **Output**: `camera_h264.mp4` +- **Use Case**: Video recording with H.264 codec + +### Camera H.265 Encode (1080p @ 30fps, 5 seconds) +```bash +libcamerasrc num-buffers=150 ! \ + video/x-raw,width=1920,height=1080,framerate=30/1 ! \ + videoconvert ! \ + video/x-raw,format=NV12 ! \ + v4l2h265enc extra-controls="controls,video_bitrate=4000000" ! \ + h265parse ! \ + qtmux ! \ + filesink location=camera_h265.mp4 +``` +- **Purpose**: Validate camera capture with H.265 hardware encoding +- **Duration**: 5 seconds (150 frames) +- **Bitrate**: 4 Mbps +- **Output**: `camera_h265.mp4` +- **Use Case**: Video recording with H.265 codec + +### Camera Snapshot (1080p, single frame) +```bash +libcamerasrc num-buffers=1 ! \ + video/x-raw,width=1920,height=1080 ! \ + videoconvert ! \ + pngenc ! \ + filesink location=camera_snapshot.png +``` +- **Purpose**: Capture single frame as PNG image +- **Resolution**: 1920x1080 +- **Output**: `camera_snapshot.png` +- **Use Case**: Still image capture, lossless format + +### Camera JPEG Encode (1080p, single frame) +```bash +libcamerasrc num-buffers=1 ! \ + video/x-raw,width=1920,height=1080 ! \ + videoconvert ! \ + jpegenc quality=90 ! \ + filesink location=camera_jpeg.jpg +``` +- **Purpose**: Capture single frame as JPEG image +- **Resolution**: 1920x1080 +- **Quality**: 90% +- **Output**: `camera_jpeg.jpg` +- **Use Case**: Still image capture, compressed format + +--- + +## CLI Options + +| Option | Description | Default | +|--------|-------------|---------| +| `--all` | Run all camera tests | - | +| `--test ` | Run specific test | - | +| `--list` | List available tests | - | +| `--timeout ` | Timeout per test | 120 | +| `--camera ` | Camera device path | /dev/video0 | +| `--duration ` | Capture duration | 5 | +| `--repeat ` | Repeat count | 1 | +| `--repeat-policy all\|any` | Pass policy | all | +| `--strict` | Fail on warnings | false | +| `--no-dmesg` | Skip dmesg scan | false | +| `--help` | Show help | - | + +--- + +## Examples + +### Run all tests +```bash +./run.sh --all +``` + +### Run specific test +```bash +./run.sh --test camera-preview +./run.sh --test camera-h264-encode +``` + +### Use different camera device +```bash +./run.sh --all --camera /dev/video2 +``` + +### Longer capture duration +```bash +./run.sh --test camera-h264-encode --duration 10 +``` + +### List available tests +```bash +./run.sh --list +``` + +### Run with repeat +```bash +./run.sh --test camera-snapshot --repeat 5 +``` + +### Run with strict mode +```bash +./run.sh --all --strict +``` + +--- + +## Output Files + +### Test Result +- `Gstreamer_Camera_Tests.res` - Overall PASS/FAIL/SKIP + +### Logs Directory: `logs_Gstreamer_Camera_Tests/` +- `camera-preview.log` - Preview test log +- `camera-h264-encode.log` - H.264 encode test log +- `camera-h265-encode.log` - H.265 encode test log +- `camera-snapshot.log` - Snapshot test log +- `camera-jpeg-encode.log` - JPEG encode test log +- `summary.txt` - Per-test results summary +- `results.csv` - Machine-readable results +- `.junit_cases.xml` - JUnit XML for CI +- `dmesg_snapshot.log` - Kernel messages snapshot +- `dmesg_errors.log` - Kernel errors (if any) + +### Captured Media Files +- `camera_h264.mp4` - H.264 encoded video (5 seconds @ 1080p) +- `camera_h265.mp4` - H.265 encoded video (5 seconds @ 1080p) +- `camera_snapshot.png` - PNG snapshot (1080p) +- `camera_jpeg.jpg` - JPEG snapshot (1080p, 90% quality) + +--- + +## Validation Criteria + +### Pass Criteria +A test PASSES if: +1. GStreamer pipeline exits with code 0 +2. No ERROR patterns in log +3. No WARNING patterns (if `--strict` mode) +4. No kernel errors in dmesg (if enabled) +5. Camera device accessible and functional +6. For encoding tests: Output file created with size > 0 +7. For preview test: Pipeline runs without errors + +### Error Patterns Detected +- `ERROR:` - General GStreamer errors +- `failed to negotiate` - Format negotiation issues +- `could not link` - Element linking failures +- `no such element` - Missing GStreamer elements +- `failed to create element` - Element creation failures +- `cannot identify device` - Camera device not recognized +- `no such device` - Camera device not found +- `device busy` - Camera device in use by another process + +--- + +## Dependencies + +### Required GStreamer Plugins +- `libcamerasrc` - libcamera source (gstreamer1.0-libcamera) +- `videoconvert` - Format converter (gstreamer1.0-plugins-base) +- `v4l2h264enc` - H.264 hardware encoder (gstreamer1.0-plugins-good) +- `v4l2h265enc` - H.265 hardware encoder (gstreamer1.0-plugins-good) +- `h264parse` - H.264 parser (gstreamer1.0-plugins-bad) +- `h265parse` - H.265 parser (gstreamer1.0-plugins-bad) +- `qtmux` - MP4 muxer (gstreamer1.0-plugins-good) +- `pngenc` - PNG encoder (gstreamer1.0-plugins-good) +- `jpegenc` - JPEG encoder (gstreamer1.0-plugins-good) +- `waylandsink` - Wayland video sink (gstreamer1.0-plugins-bad) +- `filesink` - File output sink (gstreamer1.0-plugins-base) + +### System Requirements +- libcamera installed and configured +- Camera accessible via libcamera +- V4L2 video encoder drivers loaded (for encoding tests) +- Wayland compositor running (for preview test) +- GStreamer 1.0 installed +- libcamera-apps (optional, for diagnostics with libcamera-hello) + +### Verification Commands +```bash +# Check libcamera cameras +libcamera-hello --list-cameras + +# Check GStreamer plugins +gst-inspect-1.0 libcamerasrc +gst-inspect-1.0 v4l2h264enc +gst-inspect-1.0 v4l2h265enc + +# Test libcamera +libcamera-hello --timeout 2000 + +# Check camera driver +lsmod | grep -E 'video|camera' +dmesg | grep -i camera +``` + +--- + +## Troubleshooting + +### Camera Not Detected by libcamera +```bash +# List cameras via libcamera +libcamera-hello --list-cameras + +# Check libcamera installation +which libcamera-hello +gst-inspect-1.0 libcamerasrc + +# Check camera driver +lsmod | grep -E 'video|camera|uvc' + +# Check dmesg for camera +dmesg | grep -i camera + +# Load camera driver if needed +modprobe uvcvideo # For USB cameras +modprobe qcom_camss # For Qualcomm cameras +``` + +### Camera Device Busy +```bash +# Check what's using the camera +lsof /dev/video0 +fuser /dev/video0 + +# Kill processes using camera +fuser -k /dev/video0 + +# Check for other GStreamer processes +ps aux | grep gst-launch +``` + +### Permission Issues +```bash +# Check device permissions +ls -la /dev/video0 + +# Add user to video group +sudo usermod -a -G video $USER + +# Verify group membership +groups $USER + +# Set device permissions (temporary) +sudo chmod 666 /dev/video0 +``` + +### Missing Plugins +```bash +# Check plugin availability +gst-inspect-1.0 v4l2src +gst-inspect-1.0 v4l2h264enc +gst-inspect-1.0 waylandsink + +# Install packages (if missing) +apt-get install gstreamer1.0-plugins-base \ + gstreamer1.0-plugins-good \ + gstreamer1.0-plugins-bad \ + gstreamer1.0-tools \ + v4l-utils +``` + +### Pipeline Failures +```bash +# Run pipeline manually with verbose output +GST_DEBUG=3 gst-launch-1.0 -v libcamerasrc num-buffers=30 ! waylandsink + +# Check log file +cat logs_Gstreamer_Camera_Tests/camera-preview.log + +# Test simple camera capture +gst-launch-1.0 libcamerasrc ! autovideosink + +# Test with libcamera directly +libcamera-hello --timeout 5000 +``` + +### Format Negotiation Errors +```bash +# Check libcamera capabilities +libcamera-hello --list-cameras + +# Try different resolution +./run.sh --test camera-preview # Modify pipeline in run.sh if needed + +# Check format capabilities +gst-inspect-1.0 libcamerasrc + +# Test with libcamera-vid +libcamera-vid --timeout 5000 -o test.h264 +``` + +### Encoding Failures +```bash +# Check if hardware encoders are available +gst-inspect-1.0 v4l2h264enc +gst-inspect-1.0 v4l2h265enc + +# Check video encoder devices +ls -la /dev/video* | grep enc + +# Test encoding manually +gst-launch-1.0 videotestsrc num-buffers=30 ! v4l2h264enc ! fakesink +``` + +### Preview Not Showing +```bash +# Check Wayland display +echo $WAYLAND_DISPLAY +echo $XDG_RUNTIME_DIR + +# Start Wayland compositor +weston & + +# Test with autovideosink +gst-launch-1.0 libcamerasrc ! autovideosink + +# Test with libcamera-hello +libcamera-hello --timeout 5000 +``` + +### Low Frame Rate +```bash +# Check libcamera capabilities +libcamera-hello --list-cameras + +# Try lower resolution +# Modify pipeline to use 1280x720 or 640x480 + +# Check system load +top + +# Test with libcamera-vid +libcamera-vid --width 1280 --height 720 --timeout 5000 -o test.h264 +``` + +### Output File Empty +```bash +# Check disk space +df -h + +# Check file permissions +ls -la camera_*.mp4 + +# Verify encoding completed +cat logs_Gstreamer_Camera_Tests/camera-h264-encode.log + +# Test with longer duration +./run.sh --test camera-h264-encode --duration 10 +``` + +--- + +## Supported Platforms + +- **LeMans** (QCS9100, QCS9075) +- **Monaco** (QCS8300) +- **Kodiak** (QCS6490, QCM6490) +- **QCS8550, QCS8650** +- **SA8775P, SA8650P, SA8255P** + +--- + +## Camera Detection with libcamera + +### Finding Available Cameras +```bash +# List cameras via libcamera +libcamera-hello --list-cameras + +# Example output shows camera index and capabilities +# Camera 0: imx219 [3280x2464] +# Camera 1: ov5647 [2592x1944] +``` + +### Camera Selection +libcamera automatically selects the first available camera. The `--camera` parameter is kept for compatibility but libcamera handles camera selection internally. + +```bash +# Run tests (libcamera auto-selects camera) +./run.sh --all + +# Set via environment variable (for compatibility) +export CAMERA_DEVICE=/dev/video0 +./run.sh --all +``` + +### Testing Camera Access +```bash +# Test camera with libcamera-hello +libcamera-hello --timeout 5000 + +# Test camera with GStreamer +gst-launch-1.0 libcamerasrc num-buffers=30 ! autovideosink + +# List camera properties +libcamera-hello --list-cameras --verbose +``` + +--- + +## CI/CD Integration + +### LAVA Test Definition +```yaml +- test: + definitions: + - repository: + from: git + path: Runner/suites/Gstreamer/Camera/Gstreamer_Camera_Tests.yaml + name: gstreamer-camera-tests + parameters: + TIMEOUT: "120" + CAMERA_DEVICE: "/dev/video0" + CAPTURE_DURATION: "5" +``` + +### Jenkins Pipeline +```groovy +stage('GStreamer Camera Tests') { + steps { + sh ''' + cd Runner/suites/Gstreamer/Camera + ./run.sh --all --camera /dev/video0 + ''' + } +} +``` + +--- + +## Environment Variables + +```bash +# Camera device +export CAMERA_DEVICE=/dev/video0 + +# Capture duration (seconds) +export CAPTURE_DURATION=5 + +# Timeout per test (seconds) +export TIMEOUT=120 + +# GStreamer debug level (0-9) +export GST_DEBUG=3 + +# Wayland display (for preview) +export WAYLAND_DISPLAY=wayland-0 +export XDG_RUNTIME_DIR=/run/user/$(id -u) +``` + +--- + +## Performance Considerations + +### Resolution vs Performance +- **1080p (1920x1080)**: Standard HD, good balance +- **720p (1280x720)**: Lower resource usage, faster encoding +- **4K (3840x2160)**: High quality, requires more resources + +### Frame Rate Recommendations +- **30fps**: Standard video, good for most use cases +- **60fps**: Smooth motion, requires more bandwidth +- **15fps**: Low bandwidth, acceptable for monitoring + +### Bitrate Guidelines +- **1080p @ 30fps**: 2-4 Mbps +- **720p @ 30fps**: 1-2 Mbps +- **4K @ 30fps**: 8-12 Mbps + +--- + +## License + +Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +SPDX-License-Identifier: BSD-3-Clause-Clear diff --git a/Runner/suites/Gstreamer/Camera/run.sh b/Runner/suites/Gstreamer/Camera/run.sh new file mode 100644 index 00000000..3f5d7173 --- /dev/null +++ b/Runner/suites/Gstreamer/Camera/run.sh @@ -0,0 +1,671 @@ +#!/bin/sh +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause-Clear + +#============================================================================== +# GStreamer Camera Tests - Camera Capture, Encoding, Preview, and Snapshot +#============================================================================== + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +INIT_ENV="" +SEARCH="$SCRIPT_DIR" + +# Locate init_env dynamically +while [ "$SEARCH" != "/" ]; do + if [ -f "$SEARCH/init_env" ]; then + INIT_ENV="$SEARCH/init_env" + break + fi + SEARCH="$(dirname "$SEARCH")" +done + +if [ -z "$INIT_ENV" ]; then + echo "ERROR: Cannot find init_env" + exit 1 +fi + +# Source init_env (idempotent) +if [ -z "$__INIT_ENV_LOADED" ]; then + # shellcheck disable=SC1090 + . "$INIT_ENV" +fi + +# Source functestlib.sh +# shellcheck disable=SC1090,SC1091 +. "$TOOLS/functestlib.sh" + +#============================================================================== +# Test Configuration +#============================================================================== + +TESTNAME="Gstreamer_Camera_Tests" +test_path=$(find_test_case_by_name "$TESTNAME") +res_file="$test_path/$TESTNAME.res" +log_dir="$test_path/logs_$TESTNAME" + +# Default parameters +TIMEOUT="${TIMEOUT:-120}" +REPEAT="${REPEAT:-1}" +REPEAT_POLICY="${REPEAT_POLICY:-all}" +STRICT="${STRICT:-false}" +DMESG_SCAN="${DMESG_SCAN:-true}" +CAMERA_DEVICE="${CAMERA_DEVICE:-/dev/video0}" +CAPTURE_DURATION="${CAPTURE_DURATION:-5}" + +# Test list +ALL_TESTS="camera-preview camera-h264-encode camera-h265-encode camera-snapshot camera-jpeg-encode" + +#============================================================================== +# Helper Functions +#============================================================================== + +show_usage() { + cat << EOF +Usage: $0 [OPTIONS] + +GStreamer Camera Tests - Camera capture, encoding, preview, and snapshot validation + +OPTIONS: + --all Run all camera tests + --test Run specific test + --list List available tests + --timeout Timeout per test (default: 120) + --camera Camera device (default: /dev/video0) + --duration Capture duration (default: 5) + --repeat Repeat count (default: 1) + --repeat-policy Pass policy (default: all) + --strict Fail on warnings (default: false) + --no-dmesg Skip dmesg error scanning (default: scan enabled) + --help Show this help + +AVAILABLE TESTS: + camera-preview - Live camera preview to waylandsink + camera-h264-encode - Capture and encode to H.264 + camera-h265-encode - Capture and encode to H.265 + camera-snapshot - Capture single frame snapshot (PNG) + camera-jpeg-encode - Capture and encode to JPEG + +EXAMPLES: + $0 --all + $0 --test camera-preview + $0 --all --camera /dev/video2 --duration 10 + $0 --test camera-h264-encode --repeat 3 + +EOF +} + +list_tests() { + echo "Available Camera Tests:" + for test in $ALL_TESTS; do + echo " - $test" + done +} + +check_camera_device() { + log_info "Using libcamera for camera access" + log_info "Camera device parameter: $CAMERA_DEVICE" + + # Check if libcamera can detect cameras + if command -v libcamera-hello >/dev/null 2>&1; then + log_info "Checking libcamera camera detection..." + if timeout 5 libcamera-hello --list-cameras > /tmp/libcamera_list.txt 2>&1; then + log_info "Available cameras:" + cat /tmp/libcamera_list.txt | head -20 + else + log_warn "libcamera camera detection timed out or failed" + fi + rm -f /tmp/libcamera_list.txt + fi + + # Check if libcamerasrc is available + if ! gst-inspect-1.0 libcamerasrc >/dev/null 2>&1; then + log_error "libcamerasrc plugin not available" + return 1 + fi + + log_info "libcamerasrc plugin is available" + return 0 +} + +check_dependencies() { + local missing="" + + if ! command -v gst-launch-1.0 >/dev/null 2>&1; then + missing="$missing gstreamer1.0-tools" + fi + + if ! gst-inspect-1.0 libcamerasrc >/dev/null 2>&1; then + missing="$missing gstreamer1.0-libcamera (libcamerasrc)" + fi + + if ! gst-inspect-1.0 videoconvert >/dev/null 2>&1; then + missing="$missing gstreamer1.0-plugins-base (videoconvert)" + fi + + if ! command -v libcamera-hello >/dev/null 2>&1; then + log_warn "libcamera-hello not found, camera detection may be limited" + fi + + if [ -n "$missing" ]; then + log_error "Missing dependencies:$missing" + return 1 + fi + + return 0 +} + +validate_pipeline_output() { + local log_file="$1" + local test_name="$2" + local errors=0 + + # Check for ERROR messages + if grep -qi "ERROR" "$log_file"; then + log_error "$test_name: Found ERROR in pipeline output" + grep -i "ERROR" "$log_file" | head -5 + errors=$((errors + 1)) + fi + + # Check for WARNING messages in strict mode + if [ "$STRICT" = "true" ]; then + if grep -qi "WARNING" "$log_file"; then + log_error "$test_name: Found WARNING in pipeline output (strict mode)" + grep -i "WARNING" "$log_file" | head -5 + errors=$((errors + 1)) + fi + fi + + # Check for common failure patterns + if grep -qi "failed to negotiate\|could not link\|no such element\|failed to create element" "$log_file"; then + log_error "$test_name: Pipeline negotiation or element creation failed" + grep -Ei "failed to negotiate|could not link|no such element|failed to create element" "$log_file" + errors=$((errors + 1)) + fi + + # Check for camera-specific errors + if grep -qi "cannot identify device\|no such device\|device busy" "$log_file"; then + log_error "$test_name: Camera device error detected" + grep -Ei "cannot identify device|no such device|device busy" "$log_file" + errors=$((errors + 1)) + fi + + return $errors +} + +run_gst_pipeline() { + local test_name="$1" + local pipeline="$2" + local log_file="$log_dir/${test_name}.log" + local timeout_val="$TIMEOUT" + + log_info "Running $test_name (timeout: ${timeout_val}s)" + log_info "Pipeline: $pipeline" + + # Run pipeline with timeout + if timeout "$timeout_val" sh -c "gst-launch-1.0 $pipeline > '$log_file' 2>&1"; then + log_pass "$test_name: Pipeline completed successfully" + return 0 + else + local exit_code=$? + if [ $exit_code -eq 124 ]; then + log_error "$test_name: Timeout after ${timeout_val}s" + else + log_error "$test_name: Pipeline failed with exit code $exit_code" + fi + return 1 + fi +} + +#============================================================================== +# Test Cases +#============================================================================== + +test_camera_preview() { + local test_name="camera-preview" + local log_file="$log_dir/${test_name}.log" + + log_info "=== Test: $test_name ===" + + # Calculate number of frames (duration * 30fps) + local num_buffers=$((CAPTURE_DURATION * 30)) + + # Pipeline: Camera preview to Wayland display using libcamera + local pipeline="libcamerasrc num-buffers=$num_buffers ! \ +video/x-raw,width=1920,height=1080,framerate=30/1 ! \ +videoconvert ! \ +waylandsink" + + if run_gst_pipeline "$test_name" "$pipeline"; then + if validate_pipeline_output "$log_file" "$test_name"; then + log_pass "$test_name: PASSED" + return 0 + fi + fi + + log_fail "$test_name: FAILED" + return 1 +} + +test_camera_h264_encode() { + local test_name="camera-h264-encode" + local log_file="$log_dir/${test_name}.log" + local output_file="$test_path/camera_h264.mp4" + + log_info "=== Test: $test_name ===" + + # Calculate number of frames + local num_buffers=$((CAPTURE_DURATION * 30)) + + # Pipeline: Camera capture and H.264 encode using libcamera + local pipeline="libcamerasrc num-buffers=$num_buffers ! \ +video/x-raw,width=1920,height=1080,framerate=30/1 ! \ +videoconvert ! \ +video/x-raw,format=NV12 ! \ +v4l2h264enc extra-controls=\"controls,video_bitrate=4000000\" ! \ +h264parse ! \ +qtmux ! \ +filesink location=$output_file" + + if run_gst_pipeline "$test_name" "$pipeline"; then + if validate_pipeline_output "$log_file" "$test_name"; then + if [ -f "$output_file" ]; then + local file_size=$(stat -f%z "$output_file" 2>/dev/null || stat -c%s "$output_file" 2>/dev/null) + if [ "$file_size" -gt 0 ]; then + log_info "$test_name: Output file created: $output_file (${file_size} bytes)" + log_pass "$test_name: PASSED" + return 0 + else + log_error "$test_name: Output file is empty" + fi + else + log_error "$test_name: Output file not created" + fi + fi + fi + + log_fail "$test_name: FAILED" + return 1 +} + +test_camera_h265_encode() { + local test_name="camera-h265-encode" + local log_file="$log_dir/${test_name}.log" + local output_file="$test_path/camera_h265.mp4" + + log_info "=== Test: $test_name ===" + + # Calculate number of frames + local num_buffers=$((CAPTURE_DURATION * 30)) + + # Pipeline: Camera capture and H.265 encode using libcamera + local pipeline="libcamerasrc num-buffers=$num_buffers ! \ +video/x-raw,width=1920,height=1080,framerate=30/1 ! \ +videoconvert ! \ +video/x-raw,format=NV12 ! \ +v4l2h265enc extra-controls=\"controls,video_bitrate=4000000\" ! \ +h265parse ! \ +qtmux ! \ +filesink location=$output_file" + + if run_gst_pipeline "$test_name" "$pipeline"; then + if validate_pipeline_output "$log_file" "$test_name"; then + if [ -f "$output_file" ]; then + local file_size=$(stat -f%z "$output_file" 2>/dev/null || stat -c%s "$output_file" 2>/dev/null) + if [ "$file_size" -gt 0 ]; then + log_info "$test_name: Output file created: $output_file (${file_size} bytes)" + log_pass "$test_name: PASSED" + return 0 + else + log_error "$test_name: Output file is empty" + fi + else + log_error "$test_name: Output file not created" + fi + fi + fi + + log_fail "$test_name: FAILED" + return 1 +} + +test_camera_snapshot() { + local test_name="camera-snapshot" + local log_file="$log_dir/${test_name}.log" + local output_file="$test_path/camera_snapshot.png" + + log_info "=== Test: $test_name ===" + + # Pipeline: Capture single frame and save as PNG using libcamera + local pipeline="libcamerasrc num-buffers=1 ! \ +video/x-raw,width=1920,height=1080 ! \ +videoconvert ! \ +pngenc ! \ +filesink location=$output_file" + + if run_gst_pipeline "$test_name" "$pipeline"; then + if validate_pipeline_output "$log_file" "$test_name"; then + if [ -f "$output_file" ]; then + local file_size=$(stat -f%z "$output_file" 2>/dev/null || stat -c%s "$output_file" 2>/dev/null) + if [ "$file_size" -gt 0 ]; then + log_info "$test_name: Snapshot created: $output_file (${file_size} bytes)" + log_pass "$test_name: PASSED" + return 0 + else + log_error "$test_name: Snapshot file is empty" + fi + else + log_error "$test_name: Snapshot file not created" + fi + fi + fi + + log_fail "$test_name: FAILED" + return 1 +} + +test_camera_jpeg_encode() { + local test_name="camera-jpeg-encode" + local log_file="$log_dir/${test_name}.log" + local output_file="$test_path/camera_jpeg.jpg" + + log_info "=== Test: $test_name ===" + + # Pipeline: Capture and encode to JPEG using libcamera + local pipeline="libcamerasrc num-buffers=1 ! \ +video/x-raw,width=1920,height=1080 ! \ +videoconvert ! \ +jpegenc quality=90 ! \ +filesink location=$output_file" + + if run_gst_pipeline "$test_name" "$pipeline"; then + if validate_pipeline_output "$log_file" "$test_name"; then + if [ -f "$output_file" ]; then + local file_size=$(stat -f%z "$output_file" 2>/dev/null || stat -c%s "$output_file" 2>/dev/null) + if [ "$file_size" -gt 0 ]; then + log_info "$test_name: JPEG created: $output_file (${file_size} bytes)" + log_pass "$test_name: PASSED" + return 0 + else + log_error "$test_name: JPEG file is empty" + fi + else + log_error "$test_name: JPEG file not created" + fi + fi + fi + + log_fail "$test_name: FAILED" + return 1 +} + +#============================================================================== +# Test Execution with Repeat Logic +#============================================================================== + +run_test_with_repeat() { + local test_name="$1" + local pass_count=0 + local fail_count=0 + + log_info "Running $test_name (repeat: $REPEAT, policy: $REPEAT_POLICY)" + + i=1 + while [ $i -le "$REPEAT" ]; do + log_info "Attempt $i/$REPEAT for $test_name" + + case "$test_name" in + camera-preview) + if test_camera_preview; then + pass_count=$((pass_count + 1)) + else + fail_count=$((fail_count + 1)) + fi + ;; + camera-h264-encode) + if test_camera_h264_encode; then + pass_count=$((pass_count + 1)) + else + fail_count=$((fail_count + 1)) + fi + ;; + camera-h265-encode) + if test_camera_h265_encode; then + pass_count=$((pass_count + 1)) + else + fail_count=$((fail_count + 1)) + fi + ;; + camera-snapshot) + if test_camera_snapshot; then + pass_count=$((pass_count + 1)) + else + fail_count=$((fail_count + 1)) + fi + ;; + camera-jpeg-encode) + if test_camera_jpeg_encode; then + pass_count=$((pass_count + 1)) + else + fail_count=$((fail_count + 1)) + fi + ;; + *) + log_error "Unknown test: $test_name" + return 1 + ;; + esac + + i=$((i + 1)) + done + + # Apply repeat policy + if [ "$REPEAT_POLICY" = "any" ]; then + if [ $pass_count -gt 0 ]; then + log_pass "$test_name: PASSED (any policy: $pass_count/$REPEAT passed)" + return 0 + else + log_fail "$test_name: FAILED (any policy: 0/$REPEAT passed)" + return 1 + fi + else + # Default: all policy + if [ $fail_count -eq 0 ]; then + log_pass "$test_name: PASSED (all policy: $pass_count/$REPEAT passed)" + return 0 + else + log_fail "$test_name: FAILED (all policy: $fail_count/$REPEAT failed)" + return 1 + fi + fi +} + +#============================================================================== +# Main Execution +#============================================================================== + +main() { + local run_all=false + local specific_test="" + local tests_to_run="" + + # Parse arguments + while [ $# -gt 0 ]; do + case "$1" in + --all) + run_all=true + shift + ;; + --test) + specific_test="$2" + shift 2 + ;; + --list) + list_tests + exit 0 + ;; + --timeout) + TIMEOUT="$2" + shift 2 + ;; + --camera) + CAMERA_DEVICE="$2" + shift 2 + ;; + --duration) + CAPTURE_DURATION="$2" + shift 2 + ;; + --repeat) + REPEAT="$2" + shift 2 + ;; + --repeat-policy) + REPEAT_POLICY="$2" + shift 2 + ;; + --strict) + STRICT=true + shift + ;; + --no-dmesg) + DMESG_SCAN=false + shift + ;; + --help) + show_usage + exit 0 + ;; + *) + log_error "Unknown option: $1" + show_usage + exit 1 + ;; + esac + done + + # Determine tests to run (default to all if no arguments) + if [ "$run_all" = "true" ]; then + tests_to_run="$ALL_TESTS" + elif [ -n "$specific_test" ]; then + tests_to_run="$specific_test" + else + # Default: run all tests + log_info "No test specified, running all tests by default" + tests_to_run="$ALL_TESTS" + fi + + # Setup + mkdir -p "$log_dir" + log_info "Starting $TESTNAME" + log_info "Log directory: $log_dir" + log_info "Camera device: $CAMERA_DEVICE" + log_info "Capture duration: ${CAPTURE_DURATION}s" + + # Pre-checks + if ! check_dependencies; then + echo "SKIP $TESTNAME" > "$res_file" + log_skip "$TESTNAME: Missing dependencies" + exit 0 + fi + + if ! check_camera_device; then + echo "SKIP $TESTNAME" > "$res_file" + log_skip "$TESTNAME: Camera device not available" + exit 0 + fi + + # Capture initial dmesg + if [ "$DMESG_SCAN" = "true" ]; then + dmesg > "$log_dir/dmesg_snapshot.log" + fi + + # Run tests + local total_tests=0 + local passed_tests=0 + local failed_tests=0 + + for test in $tests_to_run; do + total_tests=$((total_tests + 1)) + + if run_test_with_repeat "$test"; then + passed_tests=$((passed_tests + 1)) + else + failed_tests=$((failed_tests + 1)) + fi + done + + # Check for dmesg errors + if [ "$DMESG_SCAN" = "true" ]; then + dmesg | diff "$log_dir/dmesg_snapshot.log" - | grep -i "error\|fail\|warn" > "$log_dir/dmesg_errors.log" || true + if [ -s "$log_dir/dmesg_errors.log" ]; then + log_warn "New kernel errors detected (see dmesg_errors.log)" + fi + fi + + # Generate summary + { + echo "=== GStreamer Camera Tests Summary ===" + echo "Total Tests: $total_tests" + echo "Passed: $passed_tests" + echo "Failed: $failed_tests" + echo "Camera Device: $CAMERA_DEVICE" + echo "Capture Duration: ${CAPTURE_DURATION}s" + echo "Timeout: ${TIMEOUT}s" + echo "Repeat: $REPEAT (policy: $REPEAT_POLICY)" + echo "Strict Mode: $STRICT" + echo "Dmesg Scan: $DMESG_SCAN" + } > "$log_dir/summary.txt" + + # Generate CSV results + { + echo "test_name,status,attempts,passed,failed" + for test in $tests_to_run; do + if grep -q "PASSED.*$test" "$log_dir"/*.log 2>/dev/null; then + echo "$test,PASS,$REPEAT,$REPEAT,0" + else + echo "$test,FAIL,$REPEAT,0,$REPEAT" + fi + done + } > "$log_dir/results.csv" + + # Generate JUnit XML + generate_junit_xml "$log_dir" "$TESTNAME" "$tests_to_run" + + # Final result + if [ $failed_tests -eq 0 ]; then + echo "PASS $TESTNAME" > "$res_file" + log_pass "$TESTNAME: All tests passed ($passed_tests/$total_tests)" + exit 0 + else + echo "FAIL $TESTNAME" > "$res_file" + log_fail "$TESTNAME: Some tests failed ($failed_tests/$total_tests)" + exit 0 + fi +} + +generate_junit_xml() { + local log_dir="$1" + local suite_name="$2" + local tests="$3" + local xml_file="$log_dir/.junit_cases.xml" + + { + echo '' + echo "" + + for test in $tests; do + if grep -q "PASSED.*$test" "$log_dir"/*.log 2>/dev/null; then + echo " " + else + echo " " + echo " " + echo " " + fi + done + + echo "" + } > "$xml_file" +} + +# Execute main +main "$@" diff --git a/Runner/suites/Gstreamer/Display/Gstreamer_Display_Tests.yaml b/Runner/suites/Gstreamer/Display/Gstreamer_Display_Tests.yaml new file mode 100644 index 00000000..7656b150 --- /dev/null +++ b/Runner/suites/Gstreamer/Display/Gstreamer_Display_Tests.yaml @@ -0,0 +1,22 @@ +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause-Clear + +metadata: + format: Lava-Test Test Definition 1.0 + name: Gstreamer_Display_Tests + description: "GStreamer Display Tests - Wayland video display validation using videotestsrc and waylandsink" + maintainer: + - qualcomm-linux-test@quicinc.com + os: + - linux + +params: + TIMEOUT: "120" + STRICT: "false" + DMESG_SCAN: "true" + LOGLEVEL: "INFO" + +run: + steps: + - cd Runner/suites/Gstreamer/Display + - ./run.sh --all diff --git a/Runner/suites/Gstreamer/Display/README.md b/Runner/suites/Gstreamer/Display/README.md new file mode 100644 index 00000000..c5014738 --- /dev/null +++ b/Runner/suites/Gstreamer/Display/README.md @@ -0,0 +1,463 @@ +# GStreamer Display Tests + +**Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.** +**SPDX-License-Identifier: BSD-3-Clause-Clear** + +--- + +## Overview + +This test suite validates GStreamer video display functionality using Wayland compositor on Qualcomm Linux platforms. Tests use `videotestsrc` to generate test patterns and display them via `waylandsink`, validating the complete display pipeline including Wayland surface creation and rendering. + +### Test Coverage + +**4 Test Cases:** +- **Wayland Basic**: Basic 480p test pattern display (SMPTE bars) +- **Wayland Videotestsrc**: 1080p moving ball pattern +- **Wayland Colorbar**: 4K color bars pattern +- **Wayland SMPTE**: 720p SMPTE color bars at 60fps + +**Key Features:** +- No external file dependencies - uses built-in test pattern generator +- Validates Wayland compositor connectivity and surface creation +- Tests multiple resolutions (480p, 720p, 1080p, 4K) +- Tests multiple frame rates (30fps, 60fps) +- Validates various test patterns (SMPTE, ball, color bars) + +--- + +## Quick Start + +```bash +cd Runner/suites/Gstreamer/Display +./run.sh --all +``` + +--- + +## Test Pipelines + +### Wayland Basic (480p @ 30fps) +```bash +videotestsrc num-buffers=150 pattern=0 ! \ + video/x-raw,width=720,height=480,framerate=30/1 ! \ + waylandsink +``` +- **Pattern**: SMPTE color bars (pattern=0) +- **Duration**: 5 seconds (150 frames) +- **Resolution**: 720x480 (480p) +- **Use Case**: Basic Wayland connectivity test + +### Wayland Videotestsrc (1080p @ 30fps) +```bash +videotestsrc num-buffers=150 pattern=ball ! \ + video/x-raw,width=1920,height=1080,framerate=30/1 ! \ + videoconvert ! \ + waylandsink +``` +- **Pattern**: Moving ball (pattern=ball) +- **Duration**: 5 seconds (150 frames) +- **Resolution**: 1920x1080 (1080p) +- **Use Case**: Dynamic content rendering test + +### Wayland Colorbar (4K @ 30fps) +```bash +videotestsrc num-buffers=150 pattern=bar ! \ + video/x-raw,width=3840,height=2160,framerate=30/1 ! \ + videoconvert ! \ + waylandsink +``` +- **Pattern**: Color bars (pattern=bar) +- **Duration**: 5 seconds (150 frames) +- **Resolution**: 3840x2160 (4K) +- **Use Case**: High resolution display capability test + +### Wayland SMPTE (720p @ 60fps) +```bash +videotestsrc num-buffers=300 pattern=smpte ! \ + video/x-raw,width=1280,height=720,framerate=60/1 ! \ + videoconvert ! \ + waylandsink +``` +- **Pattern**: SMPTE color bars (pattern=smpte) +- **Duration**: 5 seconds (300 frames) +- **Resolution**: 1280x720 (720p) +- **Use Case**: High frame rate display test + +--- + +## CLI Options + +| Option | Description | Default | +|--------|-------------|---------| +| `--all` | Run all display tests | - | +| `--test ` | Run specific test | - | +| `--list` | List available tests | - | +| `--timeout ` | Timeout per test | 120 | +| `--repeat ` | Repeat count | 1 | +| `--repeat-policy all\|any` | Pass policy | all | +| `--strict` | Fail on warnings | false | +| `--no-dmesg` | Skip dmesg scan | false | +| `--help` | Show help | - | + +--- + +## Examples + +### Run all tests +```bash +./run.sh --all +``` + +### Run specific test +```bash +./run.sh --test wayland-basic +./run.sh --test wayland-colorbar +``` + +### List available tests +```bash +./run.sh --list +``` + +### Run with increased timeout +```bash +./run.sh --all --timeout 180 +``` + +### Run with strict mode +```bash +./run.sh --all --strict +``` + +### Run with repeat +```bash +./run.sh --test wayland-videotestsrc --repeat 3 --repeat-policy any +``` + +--- + +## Output Files + +### Test Result +- `Gstreamer_Display_Tests.res` - Overall PASS/FAIL/SKIP + +### Logs Directory: `logs_Gstreamer_Display_Tests/` +- `wayland-basic.log` - Basic display test log +- `wayland-videotestsrc.log` - Videotestsrc display test log +- `wayland-colorbar.log` - Colorbar display test log +- `wayland-smpte.log` - SMPTE display test log +- `summary.txt` - Per-test results summary +- `results.csv` - Machine-readable results +- `.junit_cases.xml` - JUnit XML for CI +- `dmesg_snapshot.log` - Kernel messages snapshot +- `dmesg_errors.log` - Kernel errors (if any) + +--- + +## Validation Criteria + +### Pass Criteria +A test PASSES if: +1. GStreamer pipeline exits with code 0 +2. No ERROR patterns in log +3. No WARNING patterns (if `--strict` mode) +4. No kernel errors in dmesg (if enabled) +5. Wayland surface created successfully +6. No Wayland connection errors + +### Error Patterns Detected +- `ERROR:` - General GStreamer errors +- `failed to negotiate` - Format negotiation issues +- `could not link` - Element linking failures +- `no such element` - Missing GStreamer elements +- `failed to create element` - Element creation failures +- `wayland.*error` - Wayland-specific errors +- `failed to connect to wayland` - Wayland connection failures +- `no wayland display` - Wayland display not available + +### Wayland-Specific Validations +- Wayland display socket exists and is accessible +- Wayland compositor is responsive (if weston-info available) +- Wayland surface creation confirmed in pipeline logs +- No Wayland connection/surface errors detected + +--- + +## Dependencies + +### Required GStreamer Plugins +- `videotestsrc` - Test pattern generator (gstreamer1.0-plugins-base) +- `videoconvert` - Format converter (gstreamer1.0-plugins-base) +- `waylandsink` - Wayland video sink (gstreamer1.0-plugins-bad) + +### System Requirements +- Wayland compositor running (Weston, Mutter, etc.) - **Auto-detected and started if needed** +- Display output connected +- DRM/KMS display driver loaded + +**Note**: The test suite automatically handles Wayland environment setup: +- Uses `lib_display.sh` helpers for robust Wayland socket discovery +- Automatically detects existing Wayland sockets (base or overlay configurations) +- Can start a private Weston instance if no compositor is running +- Properly sets `WAYLAND_DISPLAY` and `XDG_RUNTIME_DIR` with correct permissions +- Validates Wayland connection before running tests + +### Verification Commands +```bash +# Check GStreamer plugins +gst-inspect-1.0 videotestsrc +gst-inspect-1.0 waylandsink +gst-inspect-1.0 videoconvert + +# Check display connection (optional - test auto-detects) +echo $WAYLAND_DISPLAY +echo $XDG_RUNTIME_DIR + +# Check Wayland socket (optional - test auto-discovers) +ls -la $XDG_RUNTIME_DIR/wayland-* 2>/dev/null || \ +ls -la /run/user/*/wayland-* + +# Check compositor (optional - test can start one) +ps aux | grep -E 'weston|mutter|kwin' + +# Test Wayland connectivity (optional) +weston-info +``` + +--- + +## Troubleshooting + +### Wayland Not Available +**The test suite automatically handles Wayland setup**, but if you encounter issues: + +```bash +# Check if lib_display.sh helpers are available +grep -l "discover_wayland_socket_anywhere" $TOOLS/lib_display.sh + +# Manually check for Wayland sockets +find /run/user -name "wayland-*" -type s 2>/dev/null + +# If no sockets found, manually start Weston +weston & +sleep 2 + +# Re-run tests (they will auto-detect the new socket) +./run.sh --all +``` + +### Display Socket Not Found +**The test automatically discovers sockets**, but for manual verification: + +```bash +# Check all possible socket locations +find /run/user -name "wayland-*" -type s 2>/dev/null +find /tmp -name "wayland-*" -type s 2>/dev/null + +# Check if lib_display.sh can find sockets +if command -v discover_wayland_socket_anywhere >/dev/null 2>&1; then + discover_wayland_socket_anywhere +fi + +# The test will automatically: +# 1. Search for existing sockets +# 2. Adopt the socket's environment +# 3. Start Weston if no socket found +# 4. Validate the connection +``` + +### Missing Plugins +```bash +# Check plugin availability +gst-inspect-1.0 videotestsrc +gst-inspect-1.0 waylandsink +gst-inspect-1.0 videoconvert + +# Install packages (if missing) +apt-get install gstreamer1.0-plugins-base \ + gstreamer1.0-plugins-bad \ + gstreamer1.0-tools +``` + +### Permission Issues +```bash +# Add user to video group +sudo usermod -a -G video $USER + +# Verify group membership +groups $USER + +# Check device permissions +ls -la /dev/dri/* +``` + +### Pipeline Failures +```bash +# Run pipeline manually with verbose output +GST_DEBUG=3 gst-launch-1.0 -v videotestsrc num-buffers=150 ! waylandsink + +# Check log file +cat logs_Gstreamer_Display_Tests/wayland-basic.log + +# Test simple pipeline +gst-launch-1.0 videotestsrc ! waylandsink +``` + +### Compositor Not Responsive +**The test suite can start its own compositor**, but for manual troubleshooting: + +```bash +# Check compositor status +ps aux | grep weston + +# Kill existing compositor +killall weston +sleep 1 + +# Let the test start a new one, or start manually +weston & +sleep 2 + +# Verify with test +./run.sh --test wayland-basic + +# The test will automatically: +# 1. Detect if compositor is responsive +# 2. Start a private Weston instance if needed +# 3. Validate the connection before running tests +``` + +### Display Not Showing +```bash +# Check display output +weston-info + +# Verify DRM/KMS +ls -la /dev/dri/card* +dmesg | grep -i drm + +# Check display connection +cat /sys/class/drm/card*/status +``` + +### 4K Test Failures +```bash +# Check memory availability +free -h + +# Increase timeout for 4K tests +./run.sh --test wayland-colorbar --timeout 240 + +# Check for memory errors +dmesg | grep -i "out of memory" +``` + +--- + +## Supported Platforms + +- **LeMans** (QCS9100, QCS9075) +- **Monaco** (QCS8300) +- **Kodiak** (QCS6490, QCM6490) +- **QCS8550, QCS8650** +- **SA8775P, SA8650P, SA8255P** + +--- + +## CI/CD Integration + +### LAVA Test Definition +```yaml +- test: + definitions: + - repository: + from: git + path: Runner/suites/Gstreamer/Display/Gstreamer_Display_Tests.yaml + name: gstreamer-display-tests + parameters: + TIMEOUT: "120" + STRICT: "false" + DMESG_SCAN: "true" +``` + +### Jenkins Pipeline +```groovy +stage('GStreamer Display Tests') { + steps { + sh ''' + cd Runner/suites/Gstreamer/Display + ./run.sh --all + ''' + } +} +``` + +--- + +## Environment Variables + +**Note**: Wayland environment variables are automatically set by the test suite using `lib_display.sh` helpers. Manual configuration is typically not needed. + +### Automatic Configuration (Recommended) +The test suite automatically: +- Discovers Wayland sockets using `discover_wayland_socket_anywhere()` +- Sets `WAYLAND_DISPLAY` and `XDG_RUNTIME_DIR` via `adopt_wayland_env_from_socket()` +- Fixes permissions on `XDG_RUNTIME_DIR` if needed +- Starts a private Weston instance if no compositor is running + +### Manual Override (Advanced) +If you need to override the automatic detection: + +```bash +# Wayland display (auto-detected by default) +export WAYLAND_DISPLAY=wayland-0 + +# Runtime directory (auto-configured by default) +export XDG_RUNTIME_DIR=/run/user/$(id -u) + +# GStreamer debug level (0-9) +export GST_DEBUG=3 + +# Force software rendering (if needed) +export LIBGL_ALWAYS_SOFTWARE=1 +``` + +### Debug Environment Detection +```bash +# Enable debug output for Wayland detection +export GST_DEBUG=3 + +# Run test to see auto-detection in action +./run.sh --test wayland-basic + +# Check logs for Wayland socket discovery +grep -i "wayland socket" logs_Gstreamer_Display_Tests/*.log +``` + +--- + +## Test Pattern Reference + +### Available videotestsrc Patterns +- `pattern=0` or `pattern=smpte` - SMPTE color bars +- `pattern=ball` - Moving ball +- `pattern=bar` - Color bars +- `pattern=snow` - Random noise +- `pattern=black` - Black screen +- `pattern=white` - White screen +- `pattern=circular` - Circular pattern +- `pattern=blink` - Blinking pattern + +### Pattern Selection Rationale +- **SMPTE bars**: Standard broadcast test pattern, good for basic validation +- **Moving ball**: Dynamic content, tests motion rendering +- **Color bars**: Simple static pattern, good for color accuracy +- **60fps SMPTE**: Tests high frame rate capability + +--- + +## License + +Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +SPDX-License-Identifier: BSD-3-Clause-Clear diff --git a/Runner/suites/Gstreamer/Display/run.sh b/Runner/suites/Gstreamer/Display/run.sh new file mode 100644 index 00000000..b3fc2351 --- /dev/null +++ b/Runner/suites/Gstreamer/Display/run.sh @@ -0,0 +1,623 @@ +#!/bin/sh +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause-Clear + +#============================================================================== +# GStreamer Display Tests - Wayland Video Display Validation +#============================================================================== + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +INIT_ENV="" +SEARCH="$SCRIPT_DIR" + +# Locate init_env dynamically +while [ "$SEARCH" != "/" ]; do + if [ -f "$SEARCH/init_env" ]; then + INIT_ENV="$SEARCH/init_env" + break + fi + SEARCH="$(dirname "$SEARCH")" +done + +if [ -z "$INIT_ENV" ]; then + echo "ERROR: Cannot find init_env" + exit 1 +fi + +# Source init_env (idempotent) +if [ -z "$__INIT_ENV_LOADED" ]; then + # shellcheck disable=SC1090 + . "$INIT_ENV" +fi + +# Source functestlib.sh and lib_display.sh +# shellcheck disable=SC1090,SC1091 +. "$TOOLS/functestlib.sh" +# shellcheck disable=SC1091 +. "$TOOLS/lib_display.sh" + +#============================================================================== +# Test Configuration +#============================================================================== + +TESTNAME="Gstreamer_Display_Tests" +test_path=$(find_test_case_by_name "$TESTNAME") +res_file="$test_path/$TESTNAME.res" +log_dir="$test_path/logs_$TESTNAME" + +# Default parameters +TIMEOUT="${TIMEOUT:-120}" +REPEAT="${REPEAT:-1}" +REPEAT_POLICY="${REPEAT_POLICY:-all}" +STRICT="${STRICT:-false}" +DMESG_SCAN="${DMESG_SCAN:-true}" + +# Test list +ALL_TESTS="wayland-basic wayland-videotestsrc wayland-colorbar wayland-smpte" + +#============================================================================== +# Helper Functions +#============================================================================== + +show_usage() { + cat << EOF +Usage: $0 [OPTIONS] + +GStreamer Display Tests - Wayland video display validation + +OPTIONS: + --all Run all display tests + --test Run specific test (wayland-basic, wayland-videotestsrc, wayland-colorbar, wayland-smpte) + --list List available tests + --timeout Timeout per test (default: 120) + --repeat Repeat count (default: 1) + --repeat-policy Pass policy: all runs pass or any run passes (default: all) + --strict Fail on warnings (default: false) + --no-dmesg Skip dmesg error scanning (default: scan enabled) + --help Show this help + +EXAMPLES: + $0 --all + $0 --test wayland-basic + $0 --all --timeout 180 --strict + $0 --test wayland-colorbar --repeat 3 --repeat-policy any + +EOF +} + +list_tests() { + echo "Available Display Tests:" + for test in $ALL_TESTS; do + echo " - $test" + done +} + +check_wayland() { + # Use lib_display.sh helpers for Wayland detection (same as weston-simple-egl) + + if command -v wayland_debug_snapshot >/dev/null 2>&1; then + wayland_debug_snapshot "$TESTNAME: start" + fi + + local sock="" + + # Try to find any existing Wayland socket (base or overlay) + if command -v discover_wayland_socket_anywhere >/dev/null 2>&1; then + sock=$(discover_wayland_socket_anywhere | head -n 1 || true) + fi + + # If we found a socket, adopt its environment + if [ -n "$sock" ] && command -v adopt_wayland_env_from_socket >/dev/null 2>&1; then + log_info "Found existing Wayland socket: $sock" + if ! adopt_wayland_env_from_socket "$sock"; then + log_warn "Failed to adopt env from $sock" + fi + fi + + # If no usable socket yet, try starting a private Weston (overlay-style helper) + if [ -z "$sock" ] && command -v overlay_start_weston_drm >/dev/null 2>&1; then + log_info "No usable Wayland socket; trying overlay_start_weston_drm helper..." + if overlay_start_weston_drm; then + # Re-scan for a socket after attempting to start Weston + if command -v discover_wayland_socket_anywhere >/dev/null 2>&1; then + sock=$(discover_wayland_socket_anywhere | head -n 1 || true) + fi + if [ -n "$sock" ] && command -v adopt_wayland_env_from_socket >/dev/null 2>&1; then + log_info "Overlay Weston created Wayland socket: $sock" + if ! adopt_wayland_env_from_socket "$sock"; then + log_warn "Failed to adopt env from $sock" + fi + else + log_warn "overlay_start_weston_drm reported success but no Wayland socket was found." + fi + else + log_warn "overlay_start_weston_drm returned non-zero; private Weston may have failed to start." + fi + fi + + # Final decision: run or SKIP + if [ -z "$sock" ]; then + log_warn "No Wayland socket found after autodetection; skipping $TESTNAME." + return 1 + fi + + # Verify Wayland connection + if command -v wayland_connection_ok >/dev/null 2>&1; then + if ! wayland_connection_ok; then + log_error "Wayland connection test failed; cannot run $TESTNAME." + return 1 + fi + log_info "Wayland connection test: OK" + else + log_warn "wayland_connection_ok helper not found; continuing without explicit Wayland probe." + fi + + # Log final environment + log_info "Wayland display: ${WAYLAND_DISPLAY:-}" + log_info "XDG_RUNTIME_DIR: ${XDG_RUNTIME_DIR:-}" + if [ -n "$sock" ]; then + log_info "Wayland socket: $sock" + fi + + return 0 +} + +validate_wayland_surface() { + local test_name="$1" + local log_file="$2" + + # Check if waylandsink created a surface + if grep -qi "created.*surface\|waylandsink.*ready" "$log_file"; then + log_info "$test_name: Wayland surface created successfully" + return 0 + fi + + # Check for Wayland-specific errors + if grep -qi "wayland.*error\|failed to connect to wayland\|no wayland display" "$log_file"; then + log_error "$test_name: Wayland connection/surface error detected" + return 1 + fi + + return 0 +} + +check_dependencies() { + local missing="" + + if ! command -v gst-launch-1.0 >/dev/null 2>&1; then + missing="$missing gstreamer1.0-tools" + fi + + if ! gst-inspect-1.0 waylandsink >/dev/null 2>&1; then + missing="$missing gstreamer1.0-plugins-bad (waylandsink)" + fi + + if ! gst-inspect-1.0 videotestsrc >/dev/null 2>&1; then + missing="$missing gstreamer1.0-plugins-base (videotestsrc)" + fi + + if ! gst-inspect-1.0 videoconvert >/dev/null 2>&1; then + missing="$missing gstreamer1.0-plugins-base (videoconvert)" + fi + + if [ -n "$missing" ]; then + log_error "Missing dependencies:$missing" + return 1 + fi + + return 0 +} + +validate_pipeline_output() { + local log_file="$1" + local test_name="$2" + local errors=0 + + # Check for ERROR messages + if grep -qi "ERROR" "$log_file"; then + log_error "$test_name: Found ERROR in pipeline output" + grep -i "ERROR" "$log_file" | head -5 + errors=$((errors + 1)) + fi + + # Check for WARNING messages in strict mode + if [ "$STRICT" = "true" ]; then + if grep -qi "WARNING" "$log_file"; then + log_error "$test_name: Found WARNING in pipeline output (strict mode)" + grep -i "WARNING" "$log_file" | head -5 + errors=$((errors + 1)) + fi + fi + + # Check for common failure patterns + if grep -qi "failed to negotiate\|could not link\|no such element\|failed to create element" "$log_file"; then + log_error "$test_name: Pipeline negotiation or element creation failed" + grep -Ei "failed to negotiate|could not link|no such element|failed to create element" "$log_file" + errors=$((errors + 1)) + fi + + # Validate Wayland surface creation + if ! validate_wayland_surface "$test_name" "$log_file"; then + errors=$((errors + 1)) + fi + + return $errors +} + +run_gst_pipeline() { + local test_name="$1" + local pipeline="$2" + local log_file="$log_dir/${test_name}.log" + local timeout_val="$TIMEOUT" + local duration="${3:-5}" # Default 5 seconds display duration + + log_info "Running $test_name (timeout: ${timeout_val}s, duration: ${duration}s)" + log_info "Pipeline: $pipeline" + + # Run pipeline with timeout + # For display tests, we run for a specific duration then gracefully stop + if timeout "$timeout_val" sh -c "gst-launch-1.0 $pipeline > '$log_file' 2>&1"; then + log_pass "$test_name: Pipeline completed successfully" + return 0 + else + local exit_code=$? + if [ $exit_code -eq 124 ]; then + log_error "$test_name: Timeout after ${timeout_val}s" + else + log_error "$test_name: Pipeline failed with exit code $exit_code" + fi + return 1 + fi +} + +#============================================================================== +# Test Cases +#============================================================================== + +test_wayland_basic() { + local test_name="wayland-basic" + local log_file="$log_dir/${test_name}.log" + + log_info "=== Test: $test_name ===" + + # Basic Wayland display test with simple test pattern + # 720x480 @ 30fps for 5 seconds (150 frames) + local pipeline="videotestsrc num-buffers=150 pattern=0 ! \ +video/x-raw,width=720,height=480,framerate=30/1 ! \ +waylandsink" + + if run_gst_pipeline "$test_name" "$pipeline" 5; then + if validate_pipeline_output "$log_file" "$test_name"; then + log_pass "$test_name: PASSED" + return 0 + fi + fi + + log_fail "$test_name: FAILED" + return 1 +} + +test_wayland_videotestsrc() { + local test_name="wayland-videotestsrc" + local log_file="$log_dir/${test_name}.log" + + log_info "=== Test: $test_name ===" + + # Test with moving ball pattern at 1080p + # 1920x1080 @ 30fps for 5 seconds (150 frames) + local pipeline="videotestsrc num-buffers=150 pattern=ball ! \ +video/x-raw,width=1920,height=1080,framerate=30/1 ! \ +videoconvert ! \ +waylandsink" + + if run_gst_pipeline "$test_name" "$pipeline" 5; then + if validate_pipeline_output "$log_file" "$test_name"; then + log_pass "$test_name: PASSED" + return 0 + fi + fi + + log_fail "$test_name: FAILED" + return 1 +} + +test_wayland_colorbar() { + local test_name="wayland-colorbar" + local log_file="$log_dir/${test_name}.log" + + log_info "=== Test: $test_name ===" + + # Test with color bars pattern at 4K + # 3840x2160 @ 30fps for 5 seconds (150 frames) + local pipeline="videotestsrc num-buffers=150 pattern=bar ! \ +video/x-raw,width=3840,height=2160,framerate=30/1 ! \ +videoconvert ! \ +waylandsink" + + if run_gst_pipeline "$test_name" "$pipeline" 5; then + if validate_pipeline_output "$log_file" "$test_name"; then + log_pass "$test_name: PASSED" + return 0 + fi + fi + + log_fail "$test_name: FAILED" + return 1 +} + +test_wayland_smpte() { + local test_name="wayland-smpte" + local log_file="$log_dir/${test_name}.log" + + log_info "=== Test: $test_name ===" + + # Test with SMPTE color bars at 720p + # 1280x720 @ 60fps for 5 seconds (300 frames) + local pipeline="videotestsrc num-buffers=300 pattern=smpte ! \ +video/x-raw,width=1280,height=720,framerate=60/1 ! \ +videoconvert ! \ +waylandsink" + + if run_gst_pipeline "$test_name" "$pipeline" 5; then + if validate_pipeline_output "$log_file" "$test_name"; then + log_pass "$test_name: PASSED" + return 0 + fi + fi + + log_fail "$test_name: FAILED" + return 1 +} + +#============================================================================== +# Test Execution with Repeat Logic +#============================================================================== + +run_test_with_repeat() { + local test_name="$1" + local pass_count=0 + local fail_count=0 + + log_info "Running $test_name (repeat: $REPEAT, policy: $REPEAT_POLICY)" + + i=1 + while [ $i -le "$REPEAT" ]; do + log_info "Attempt $i/$REPEAT for $test_name" + + case "$test_name" in + wayland-basic) + if test_wayland_basic; then + pass_count=$((pass_count + 1)) + else + fail_count=$((fail_count + 1)) + fi + ;; + wayland-videotestsrc) + if test_wayland_videotestsrc; then + pass_count=$((pass_count + 1)) + else + fail_count=$((fail_count + 1)) + fi + ;; + wayland-colorbar) + if test_wayland_colorbar; then + pass_count=$((pass_count + 1)) + else + fail_count=$((fail_count + 1)) + fi + ;; + wayland-smpte) + if test_wayland_smpte; then + pass_count=$((pass_count + 1)) + else + fail_count=$((fail_count + 1)) + fi + ;; + *) + log_error "Unknown test: $test_name" + return 1 + ;; + esac + + i=$((i + 1)) + done + + # Apply repeat policy + if [ "$REPEAT_POLICY" = "any" ]; then + if [ $pass_count -gt 0 ]; then + log_pass "$test_name: PASSED (any policy: $pass_count/$REPEAT passed)" + return 0 + else + log_fail "$test_name: FAILED (any policy: 0/$REPEAT passed)" + return 1 + fi + else + # Default: all policy + if [ $fail_count -eq 0 ]; then + log_pass "$test_name: PASSED (all policy: $pass_count/$REPEAT passed)" + return 0 + else + log_fail "$test_name: FAILED (all policy: $fail_count/$REPEAT failed)" + return 1 + fi + fi +} + +#============================================================================== +# Main Execution +#============================================================================== + +main() { + local run_all=false + local specific_test="" + local tests_to_run="" + + # Parse arguments + while [ $# -gt 0 ]; do + case "$1" in + --all) + run_all=true + shift + ;; + --test) + specific_test="$2" + shift 2 + ;; + --list) + list_tests + exit 0 + ;; + --timeout) + TIMEOUT="$2" + shift 2 + ;; + --repeat) + REPEAT="$2" + shift 2 + ;; + --repeat-policy) + REPEAT_POLICY="$2" + shift 2 + ;; + --strict) + STRICT=true + shift + ;; + --no-dmesg) + DMESG_SCAN=false + shift + ;; + --help) + show_usage + exit 0 + ;; + *) + log_error "Unknown option: $1" + show_usage + exit 1 + ;; + esac + done + + # Determine tests to run (default to all if no arguments) + if [ "$run_all" = "true" ]; then + tests_to_run="$ALL_TESTS" + elif [ -n "$specific_test" ]; then + tests_to_run="$specific_test" + else + # Default: run all tests + log_info "No test specified, running all tests by default" + tests_to_run="$ALL_TESTS" + fi + + # Setup + mkdir -p "$log_dir" + log_info "Starting $TESTNAME" + log_info "Log directory: $log_dir" + + # Pre-checks + if ! check_dependencies; then + echo "SKIP $TESTNAME" > "$res_file" + log_skip "$TESTNAME: Missing dependencies" + exit 0 + fi + + if ! check_wayland; then + echo "SKIP $TESTNAME" > "$res_file" + log_skip "$TESTNAME: Wayland not available" + exit 0 + fi + + # Capture initial dmesg + if [ "$DMESG_SCAN" = "true" ]; then + dmesg > "$log_dir/dmesg_snapshot.log" + fi + + # Run tests + local total_tests=0 + local passed_tests=0 + local failed_tests=0 + + for test in $tests_to_run; do + total_tests=$((total_tests + 1)) + + if run_test_with_repeat "$test"; then + passed_tests=$((passed_tests + 1)) + else + failed_tests=$((failed_tests + 1)) + fi + done + + # Check for dmesg errors + if [ "$DMESG_SCAN" = "true" ]; then + dmesg | diff "$log_dir/dmesg_snapshot.log" - | grep -i "error\|fail\|warn" > "$log_dir/dmesg_errors.log" || true + if [ -s "$log_dir/dmesg_errors.log" ]; then + log_warn "New kernel errors detected (see dmesg_errors.log)" + fi + fi + + # Generate summary + { + echo "=== GStreamer Display Tests Summary ===" + echo "Total Tests: $total_tests" + echo "Passed: $passed_tests" + echo "Failed: $failed_tests" + echo "Timeout: ${TIMEOUT}s" + echo "Repeat: $REPEAT (policy: $REPEAT_POLICY)" + echo "Strict Mode: $STRICT" + echo "Dmesg Scan: $DMESG_SCAN" + } > "$log_dir/summary.txt" + + # Generate CSV results + { + echo "test_name,status,attempts,passed,failed" + for test in $tests_to_run; do + if grep -q "PASSED.*$test" "$log_dir"/*.log 2>/dev/null; then + echo "$test,PASS,$REPEAT,$REPEAT,0" + else + echo "$test,FAIL,$REPEAT,0,$REPEAT" + fi + done + } > "$log_dir/results.csv" + + # Generate JUnit XML + generate_junit_xml "$log_dir" "$TESTNAME" "$tests_to_run" + + # Final result + if [ $failed_tests -eq 0 ]; then + echo "PASS $TESTNAME" > "$res_file" + log_pass "$TESTNAME: All tests passed ($passed_tests/$total_tests)" + exit 0 + else + echo "FAIL $TESTNAME" > "$res_file" + log_fail "$TESTNAME: Some tests failed ($failed_tests/$total_tests)" + exit 0 + fi +} + +generate_junit_xml() { + local log_dir="$1" + local suite_name="$2" + local tests="$3" + local xml_file="$log_dir/.junit_cases.xml" + + { + echo '' + echo "" + + for test in $tests; do + if grep -q "PASSED.*$test" "$log_dir"/*.log 2>/dev/null; then + echo " " + else + echo " " + echo " " + echo " " + fi + done + + echo "" + } > "$xml_file" +} + +# Execute main +main "$@" diff --git a/Runner/suites/Gstreamer/Gstreamer_MM_Tests.yaml b/Runner/suites/Gstreamer/Gstreamer_MM_Tests.yaml new file mode 100644 index 00000000..2d9b82d4 --- /dev/null +++ b/Runner/suites/Gstreamer/Gstreamer_MM_Tests.yaml @@ -0,0 +1,32 @@ +metadata: + name: gstreamer-Multimedia-tests + format: "Lava-Test Test Definition 1.0" + description: "These scripts automate validation of GStreamer encoding and decoding using v4l2h264dec, v4l2h265dec, v4l2h264enc, and v4l2h265enc on Qualcomm Linux platforms." + os: + - linux + scope: + - functional + +params: + # Timeout for each test in seconds + TIMEOUT: "60" + # Enable strict mode (treat dmesg warnings as failures) + STRICT: "0" + # Enable dmesg scanning + DMESG_SCAN: "1" + # Log level + LOGLEVEL: "15" + # Number of times to retry on failure + RETRY_ON_FAIL: "3" + # Sleep time after each test in seconds + POST_TEST_SLEEP: "5" + # platform is autodetected by the test script + PLATFORM: "" + +run: + steps: + - REPO_PATH=$PWD + - cd Runner/suites/Multimedia/Gstreamer/ + - chmod +x ./run.sh + - ./run.sh --timeout "${TIMEOUT}" --loglevel "${LOGLEVEL}" --platform "${PLATFORM}" --retry-on-fail "${RETRY_ON_FAIL}" --post-test-sleep "${POST_TEST_SLEEP}" || true + - $REPO_PATH/Runner/utils/send-to-lava.sh "Gstreamer Multimedia Tests.res" || true diff --git a/Runner/suites/Gstreamer/Video/Gstreamer_Video_Tests.yaml b/Runner/suites/Gstreamer/Video/Gstreamer_Video_Tests.yaml new file mode 100644 index 00000000..4704e149 --- /dev/null +++ b/Runner/suites/Gstreamer/Video/Gstreamer_Video_Tests.yaml @@ -0,0 +1,39 @@ +metadata: + name: gstreamer-video-tests + format: "Lava-Test Test Definition 1.0" + description: "GStreamer Video Encode/Decode validation using V4L2 hardware acceleration (H.264, H.265, VP9)" + os: + - linux + scope: + - functional + +params: + # Timeout for each test in seconds + TIMEOUT: "60" + # Enable strict mode (treat dmesg warnings as failures) + STRICT: "0" + # Enable dmesg scanning + DMESG_SCAN: "1" + # Log level + LOGLEVEL: "15" + # Sleep time after each test in seconds + POST_TEST_SLEEP: "5" + # Platform (auto-detected if empty) + PLATFORM: "" + # Video stack selection + VIDEO_STACK: "auto" + # VP9 clips download URL (optional, uses GitHub release by default) + VP9_CLIPS_URL: "" + +run: + steps: + - REPO_PATH=$PWD + - cd Runner/suites/Gstreamer/Video/ + - chmod +x ./run.sh + - | + if [ -n "${VP9_CLIPS_URL}" ]; then + ./run.sh --timeout "${TIMEOUT}" --loglevel "${LOGLEVEL}" --platform "${PLATFORM}" --stack "${VIDEO_STACK}" --post-test-sleep "${POST_TEST_SLEEP}" --vp9-clips-url "${VP9_CLIPS_URL}" || true + else + ./run.sh --timeout "${TIMEOUT}" --loglevel "${LOGLEVEL}" --platform "${PLATFORM}" --stack "${VIDEO_STACK}" --post-test-sleep "${POST_TEST_SLEEP}" || true + fi + - $REPO_PATH/Runner/utils/send-to-lava.sh "Gstreamer_Video_Tests.res" || true diff --git a/Runner/suites/Gstreamer/Video/README.md b/Runner/suites/Gstreamer/Video/README.md new file mode 100644 index 00000000..ab2b4911 --- /dev/null +++ b/Runner/suites/Gstreamer/Video/README.md @@ -0,0 +1,438 @@ +# GStreamer Video Encode/Decode Tests + +**Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.** +**SPDX-License-Identifier: BSD-3-Clause-Clear** + +--- + +## Overview + +This test suite validates GStreamer video encoding and decoding using V4L2 hardware-accelerated plugins on Qualcomm Linux platforms. Tests cover two resolutions (480p and 4K) for both H.264 and H.265 codecs. + +### Test Coverage + +**10 Test Cases Total:** +- **H.264 480p Encode**: Hardware encoding at 720x480 using `v4l2h264enc` +- **H.264 4K Encode**: Hardware encoding at 3840x2160 using `v4l2h264enc` +- **H.265 480p Encode**: Hardware encoding at 720x480 using `v4l2h265enc` +- **H.265 4K Encode**: Hardware encoding at 3840x2160 using `v4l2h265enc` +- **H.264 480p Decode**: Hardware decoding at 720x480 using `v4l2h264dec` +- **H.264 4K Decode**: Hardware decoding at 3840x2160 using `v4l2h264dec` +- **H.265 480p Decode**: Hardware decoding at 720x480 using `v4l2h265dec` +- **H.265 4K Decode**: Hardware decoding at 3840x2160 using `v4l2h265dec` +- **VP9 480p Decode**: Hardware decoding at 720x480 using `v4l2vp9dec` +- **VP9 4K Decode**: Hardware decoding at 3840x2160 using `v4l2vp9dec` + +**Key Features:** +- H.264/H.265 decode tests use the output files generated by encode tests as input, ensuring end-to-end validation +- VP9 decode tests automatically fetch test clips from GitHub releases if not present locally +- VP9 tests gracefully skip if hardware support is unavailable + +--- + +## Quick Start + +```bash +cd Runner/suites/Gstreamer/Video +./run.sh +``` + +--- + +## Test Pipelines + +### H.264 480p Encode +```bash +videotestsrc num-buffers=300 ! \ + video/x-raw,width=720,height=480,format=NV12,framerate=30/1 ! \ + v4l2h264enc extra-controls="controls,video_bitrate=1000000" ! \ + h264parse ! qtmux ! filesink location=./output_h264_480p.mp4 +``` + +### H.264 4K Encode +```bash +videotestsrc num-buffers=300 ! \ + video/x-raw,width=3840,height=2160,format=NV12,framerate=30/1 ! \ + v4l2h264enc extra-controls="controls,video_bitrate=8000000" ! \ + h264parse ! qtmux ! filesink location=./output_h264_4k.mp4 +``` + +### H.265 480p Encode +```bash +videotestsrc num-buffers=300 ! \ + video/x-raw,width=720,height=480,format=NV12,framerate=30/1 ! \ + v4l2h265enc extra-controls="controls,video_bitrate=1000000" ! \ + h265parse ! qtmux ! filesink location=./output_h265_480p.mp4 +``` + +### H.265 4K Encode +```bash +videotestsrc num-buffers=300 ! \ + video/x-raw,width=3840,height=2160,format=NV12,framerate=30/1 ! \ + v4l2h265enc extra-controls="controls,video_bitrate=8000000" ! \ + h265parse ! qtmux ! filesink location=./output_h265_4k.mp4 +``` + +### H.264 480p Decode (uses encode output) +```bash +filesrc location=./output_h264_480p.mp4 ! qtdemux ! h264parse ! \ + v4l2h264dec ! videoconvert ! video/x-raw,format=NV12 ! fakevideosink +``` + +### H.264 4K Decode (uses encode output) +```bash +filesrc location=./output_h264_4k.mp4 ! qtdemux ! h264parse ! \ + v4l2h264dec ! videoconvert ! video/x-raw,format=NV12 ! fakevideosink +``` + +### H.265 480p Decode (uses encode output) +```bash +filesrc location=./output_h265_480p.mp4 ! qtdemux ! h265parse ! \ + v4l2h265dec ! videoconvert ! video/x-raw,format=NV12 ! fakevideosink +``` + +### H.265 4K Decode (uses encode output) +```bash +filesrc location=./output_h265_4k.mp4 ! qtdemux ! h265parse ! \ + v4l2h265dec ! videoconvert ! video/x-raw,format=NV12 ! fakevideosink +``` + +### VP9 480p Decode (uses fetched clip) +```bash +filesrc location=./vp9_480p.webm ! matroskademux ! vp9parse ! \ + v4l2vp9dec ! videoconvert ! video/x-raw,format=NV12 ! fakevideosink +``` + +### VP9 4K Decode (uses fetched clip) +```bash +filesrc location=./vp9_4k.webm ! matroskademux ! vp9parse ! \ + v4l2vp9dec ! videoconvert ! video/x-raw,format=NV12 ! fakevideosink +``` + +--- + +## Test Execution Flow + +1. **Encode Tests Run First** (generate output files): + - h264-480p-encode → creates `output_h264_480p.mp4` + - h264-4k-encode → creates `output_h264_4k.mp4` + - h265-480p-encode → creates `output_h265_480p.mp4` + - h265-4k-encode → creates `output_h265_4k.mp4` + +2. **H.264/H.265 Decode Tests Use Encode Outputs**: + - h264-480p-decode → reads `output_h264_480p.mp4` + - h264-4k-decode → reads `output_h264_4k.mp4` + - h265-480p-decode → reads `output_h265_480p.mp4` + - h265-4k-decode → reads `output_h265_4k.mp4` + +3. **VP9 Decode Tests Use Fetched Clips**: + - vp9-480p-decode → reads `vp9_480p.webm` (auto-fetched if missing) + - vp9-4k-decode → reads `vp9_4k.webm` (auto-fetched if missing) + +This ensures that the encoded content is validated by successfully decoding it. VP9 tests validate hardware decode capability using pre-encoded reference clips. + +--- + +## CLI Options + +| Option | Description | Default | +|--------|-------------|---------| +| `--timeout S` | Timeout per test (seconds) | 120 | +| `--strict` | Treat warnings as failures | disabled | +| `--no-dmesg` | Disable dmesg scanning | enabled | +| `--stop-on-fail` | Abort on first failure | disabled | +| `--loglevel N` | GStreamer log verbosity | 15 | +| `--repeat N` | Repeat each test N times | 1 | +| `--repeat-delay S` | Delay between repeats | 0 | +| `--repeat-policy all\|any` | Pass criteria | all | +| `--junit FILE` | JUnit XML output | none | +| `--stack auto\|upstream\|downstream` | Video stack | auto | +| `--platform lemans\|monaco\|kodiak` | Platform | auto-detect | +| `--post-test-sleep S` | Sleep after each test | 0 | +| `--vp9-clips-url URL` | VP9 clips download URL | GitHub release | + +--- + +## Examples + +### Run all tests (default) +```bash +./run.sh +``` + +### Run with increased timeout (for 4K tests) +```bash +./run.sh --timeout 180 +``` + +### Run with strict mode +```bash +./run.sh --strict +``` + +### Run with repeat +```bash +./run.sh --repeat 3 --repeat-delay 5 +``` + +### Generate JUnit XML +```bash +./run.sh --junit video-results.xml +``` + +### Select video stack +```bash +./run.sh --stack upstream +./run.sh --stack downstream +``` + +--- + +## Output Files + +### Test Result +- `Gstreamer_Video_Tests.res` - Overall PASS/FAIL/SKIP + +### Logs Directory: `logs_Gstreamer_Video_Tests/` +- `h264-480p-encode.log` - H.264 480p encode pipeline log +- `h264-4k-encode.log` - H.264 4K encode pipeline log +- `h265-480p-encode.log` - H.265 480p encode pipeline log +- `h265-4k-encode.log` - H.265 4K encode pipeline log +- `h264-480p-decode.log` - H.264 480p decode pipeline log +- `h264-4k-decode.log` - H.264 4K decode pipeline log +- `h265-480p-decode.log` - H.265 480p decode pipeline log +- `h265-4k-decode.log` - H.265 4K decode pipeline log +- `vp9-480p-decode.log` - VP9 480p decode pipeline log +- `vp9-4k-decode.log` - VP9 4K decode pipeline log +- `summary.txt` - Per-test results summary +- `results.csv` - Machine-readable results +- `dmesg_errors.log` - Kernel error log (if any) + +### Generated Media Files +- `output_h264_480p.mp4` - Encoded H.264 480p video +- `output_h264_4k.mp4` - Encoded H.264 4K video +- `output_h265_480p.mp4` - Encoded H.265 480p video +- `output_h265_4k.mp4` - Encoded H.265 4K video + +### VP9 Test Clips (auto-fetched) +- `vp9_480p.webm` - VP9 480p reference clip +- `vp9_4k.webm` - VP9 4K reference clip + +--- + +## Validation Criteria + +### Pass Criteria +A test PASSES if: +1. GStreamer pipeline exits with code 0 +2. No ERROR patterns in log +3. No WARNING patterns (if `--strict` mode) +4. No kernel errors in dmesg (if enabled) +5. For encode tests: Output file is created +6. For decode tests: Input file exists and is successfully decoded + +### Error Patterns Detected +- `ERROR:` - General GStreamer errors +- `v4l2.*failed` - V4L2 operation failures +- `negotiation failed` - Format negotiation issues +- `buffer pool activation failed` - Memory allocation failures +- `format not supported` - Unsupported format errors + +--- + +## Resolution Details + +### 480p (720x480) +- **Aspect Ratio**: 3:2 +- **Bitrate**: 1 Mbps +- **Use Case**: Standard definition, lower bandwidth +- **Frame Count**: 300 frames (10 seconds at 30fps) + +### 4K (3840x2160) +- **Aspect Ratio**: 16:9 +- **Bitrate**: 8 Mbps +- **Use Case**: Ultra high definition, high quality +- **Frame Count**: 300 frames (10 seconds at 30fps) + +--- + +## Dependencies + +### Required GStreamer Plugins +- `v4l2h264dec` - H.264 hardware decoder +- `v4l2h265dec` - H.265 hardware decoder +- `v4l2h264enc` - H.264 hardware encoder +- `v4l2h265enc` - H.265 hardware encoder +- `v4l2vp9dec` - VP9 hardware decoder (optional, for VP9 tests) +- `qtmux` - MP4 muxer +- `qtdemux` - MP4 demuxer +- `h264parse` - H.264 parser +- `h265parse` - H.265 parser +- `vp9parse` - VP9 parser (optional, for VP9 tests) +- `matroskademux` - WebM/Matroska demuxer (optional, for VP9 tests) +- `videoconvert` - Format converter +- `videotestsrc` - Test pattern generator +- `fakevideosink` - Null sink for validation + +### System Requirements +- `/dev/video*` device nodes +- V4L2 video drivers loaded (qcom_iris or iris_vpu) +- GStreamer 1.0 installed +- Sufficient memory for 4K encoding/decoding + +--- + +## Troubleshooting + +### Missing Plugins +```bash +# Check plugin availability +gst-inspect-1.0 v4l2h264dec +gst-inspect-1.0 v4l2h264enc +gst-inspect-1.0 v4l2h265dec +gst-inspect-1.0 v4l2h265enc +gst-inspect-1.0 v4l2vp9dec +gst-inspect-1.0 vp9parse +gst-inspect-1.0 matroskademux + +# Install packages (if missing) +apt-get install gstreamer1.0-plugins-base \ + gstreamer1.0-plugins-good \ + gstreamer1.0-plugins-bad \ + gstreamer1.0-tools +``` + +### No Video Devices +```bash +# Check device nodes +ls -la /dev/video* + +# Check driver loading +lsmod | grep -E 'iris|venus' + +# Check dmesg +dmesg | grep -i video + +# Load driver if needed +modprobe qcom_iris +``` + +### Pipeline Failures +```bash +# Run pipeline manually with verbose output +GST_DEBUG=3 gst-launch-1.0 -v + +# Check log file +cat logs_Gstreamer_Video_Tests/h264-480p-encode.log + +# Verify format support +v4l2-ctl -d /dev/video0 --list-formats-ext +``` + +### 4K Encoding Issues +```bash +# Check memory availability +free -h + +# Increase timeout for 4K tests +./run.sh --timeout 240 + +# Check for memory errors in dmesg +dmesg | grep -i "out of memory" +``` + +### Decode Test Skipped +If H.264/H.265 decode tests are skipped, it means the encode test failed to create the output file: +```bash +# Check if encode test passed +cat logs_Gstreamer_Video_Tests/summary.txt + +# Verify output files exist +ls -lh output_*.mp4 + +# Re-run encode tests only +# (manually run encode pipelines to debug) +``` + +### VP9 Tests Skipped +VP9 tests may be skipped for several reasons: +```bash +# Check if VP9 plugins are available +gst-inspect-1.0 v4l2vp9dec +gst-inspect-1.0 vp9parse +gst-inspect-1.0 matroskademux + +# Check if VP9 clips were fetched +ls -lh vp9_*.webm + +# Check network connectivity (for clip fetching) +ping -c 3 github.com + +# Manually fetch VP9 clips +wget https://github.com/qualcomm-linux/qcom-linux-testkit/releases/download/IRIS-Video-Files-v1.0/vp9_clips.tar.gz +tar -xzf vp9_clips.tar.gz + +# Run with custom VP9 clips URL +./run.sh --vp9-clips-url "https://your-server.com/vp9_clips.tar.gz" +``` + +### VP9 Decode Failures +```bash +# Run VP9 pipeline manually with verbose output +GST_DEBUG=3 gst-launch-1.0 -v \ + filesrc location=./vp9_480p.webm ! matroskademux ! vp9parse ! \ + v4l2vp9dec ! videoconvert ! fakevideosink + +# Check if hardware supports VP9 +v4l2-ctl -d /dev/video0 --list-formats-ext | grep VP9 + +# Check driver capabilities +dmesg | grep -i vp9 +``` + +--- + + +## Supported Platforms + +- **LeMans** (QCS9100, QCS9075) +- **Monaco** (QCS8300) +- **Kodiak** (QCS6490, QCM6490) +- **QCS8550, QCS8650** +- **SA8775P, SA8650P, SA8255P** + +--- + +## CI/CD Integration + +### LAVA Test Definition +```yaml +- test: + definitions: + - repository: + from: git + path: Runner/suites/Gstreamer/Video/Gstreamer_Video_Tests.yaml + name: gstreamer-video-tests + parameters: + TIMEOUT: "180" + STRICT: "false" + DMESG_SCAN: "true" +``` + +### Jenkins Pipeline +```groovy +stage('GStreamer Video Tests') { + steps { + sh ''' + cd Runner/suites/Gstreamer/Video + ./run.sh --junit video-results.xml --timeout 180 + ''' + junit 'video-results.xml' + } +} +``` + +## License + +Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +SPDX-License-Identifier: BSD-3-Clause-Clear diff --git a/Runner/suites/Gstreamer/Video/run.sh b/Runner/suites/Gstreamer/Video/run.sh new file mode 100644 index 00000000..993dbf87 --- /dev/null +++ b/Runner/suites/Gstreamer/Video/run.sh @@ -0,0 +1,517 @@ +#!/bin/sh +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause-Clear +# GStreamer Video Encode/Decode Test Runner - 480p and 4K resolutions + +# ---------- Repo env + helpers ---------- +SCRIPT_DIR="$(cd "$(dirname "$0")" || exit 1; pwd)" +INIT_ENV="" +SEARCH="$SCRIPT_DIR" + +while [ "$SEARCH" != "/" ]; do + if [ -f "$SEARCH/init_env" ]; then + INIT_ENV="$SEARCH/init_env" + break + fi + SEARCH=$(dirname "$SEARCH") +done + +if [ -z "$INIT_ENV" ]; then + echo "[ERROR] Could not find init_env (starting at $SCRIPT_DIR)" >&2 + exit 1 +fi + +# Only source once (idempotent) +if [ -z "${__INIT_ENV_LOADED:-}" ]; then + # shellcheck disable=SC1090 + . "$INIT_ENV" + __INIT_ENV_LOADED=1 +fi + +# shellcheck disable=SC1090 +. "$INIT_ENV" +# shellcheck disable=SC1091 +. "$TOOLS/functestlib.sh" +# shellcheck disable=SC1091 +. "$TOOLS/lib_video.sh" + +TESTNAME="Gstreamer_Video_Tests" +RES_FILE="./${TESTNAME}.res" + +# --- Defaults / knobs --- +if [ -z "${TIMEOUT:-}" ]; then TIMEOUT="120"; fi +if [ -z "${STRICT:-}" ]; then STRICT="0"; fi +if [ -z "${DMESG_SCAN:-}" ]; then DMESG_SCAN="1"; fi +if [ -z "${STOP_ON_FAIL:-}" ]; then STOP_ON_FAIL="0"; fi +if [ -z "${LOGLEVEL:-}" ]; then LOGLEVEL="15"; fi +if [ -z "${REPEAT:-}" ]; then REPEAT="1"; fi +if [ -z "${REPEAT_DELAY:-}" ]; then REPEAT_DELAY="0"; fi +if [ -z "${REPEAT_POLICY:-}" ]; then REPEAT_POLICY="all"; fi +JUNIT_OUT="" +VERBOSE="0" +POST_TEST_SLEEP="0" + +# --- Test media files (encode outputs used as decode inputs) --- +H264_480P_OUTPUT="./output_h264_480p.mp4" +H264_4K_OUTPUT="./output_h264_4k.mp4" +H265_480P_OUTPUT="./output_h265_480p.mp4" +H265_4K_OUTPUT="./output_h265_4k.mp4" + +# --- VP9 input files (fetched from URL if not present) --- +VP9_480P_INPUT="./vp9_480p.webm" +VP9_4K_INPUT="./vp9_4k.webm" + +# VP9 clips URL (can be overridden via environment) +if [ -z "${VP9_CLIPS_URL:-}" ]; then + VP9_CLIPS_URL="https://github.com/qualcomm-linux/qcom-linux-testkit/releases/download/IRIS-Video-Files-v1.0/vp9_clips.tar.gz" +fi + +# --- Test parameters --- +FRAMERATE="30" +BITRATE_480P="1000000" +BITRATE_4K="8000000" +NUM_BUFFERS="300" + +# --- Resolution definitions --- +WIDTH_480P="720" +HEIGHT_480P="480" +WIDTH_4K="3840" +HEIGHT_4K="2160" + +# --- GStreamer pipelines --- +# H.264 480p Encode +H264_480P_ENCODE_PIPELINE="videotestsrc num-buffers=${NUM_BUFFERS} ! video/x-raw,width=${WIDTH_480P},height=${HEIGHT_480P},format=NV12,framerate=${FRAMERATE}/1 ! v4l2h264enc extra-controls=\"controls,video_bitrate=${BITRATE_480P}\" ! h264parse ! qtmux ! filesink location=${H264_480P_OUTPUT}" + +# H.264 4K Encode +H264_4K_ENCODE_PIPELINE="videotestsrc num-buffers=${NUM_BUFFERS} ! video/x-raw,width=${WIDTH_4K},height=${HEIGHT_4K},format=NV12,framerate=${FRAMERATE}/1 ! v4l2h264enc extra-controls=\"controls,video_bitrate=${BITRATE_4K}\" ! h264parse ! qtmux ! filesink location=${H264_4K_OUTPUT}" + +# H.265 480p Encode +H265_480P_ENCODE_PIPELINE="videotestsrc num-buffers=${NUM_BUFFERS} ! video/x-raw,width=${WIDTH_480P},height=${HEIGHT_480P},format=NV12,framerate=${FRAMERATE}/1 ! v4l2h265enc extra-controls=\"controls,video_bitrate=${BITRATE_480P}\" ! h265parse ! qtmux ! filesink location=${H265_480P_OUTPUT}" + +# H.265 4K Encode +H265_4K_ENCODE_PIPELINE="videotestsrc num-buffers=${NUM_BUFFERS} ! video/x-raw,width=${WIDTH_4K},height=${HEIGHT_4K},format=NV12,framerate=${FRAMERATE}/1 ! v4l2h265enc extra-controls=\"controls,video_bitrate=${BITRATE_4K}\" ! h265parse ! qtmux ! filesink location=${H265_4K_OUTPUT}" + +# H.264 480p Decode (uses encoded output) +H264_480P_DECODE_PIPELINE="filesrc location=${H264_480P_OUTPUT} ! qtdemux ! h264parse ! v4l2h264dec ! videoconvert ! video/x-raw,format=NV12 ! fakevideosink" + +# H.264 4K Decode (uses encoded output) +H264_4K_DECODE_PIPELINE="filesrc location=${H264_4K_OUTPUT} ! qtdemux ! h264parse ! v4l2h264dec ! videoconvert ! video/x-raw,format=NV12 ! fakevideosink" + +# H.265 480p Decode (uses encoded output) +H265_480P_DECODE_PIPELINE="filesrc location=${H265_480P_OUTPUT} ! qtdemux ! h265parse ! v4l2h265dec ! videoconvert ! video/x-raw,format=NV12 ! fakevideosink" + +# H.265 4K Decode (uses encoded output) +H265_4K_DECODE_PIPELINE="filesrc location=${H265_4K_OUTPUT} ! qtdemux ! h265parse ! v4l2h265dec ! videoconvert ! video/x-raw,format=NV12 ! fakevideosink" + +# VP9 480p Decode (uses fetched input) +VP9_480P_DECODE_PIPELINE="filesrc location=${VP9_480P_INPUT} ! matroskademux ! vp9parse ! v4l2vp9dec ! videoconvert ! video/x-raw,format=NV12 ! fakevideosink" + +# VP9 4K Decode (uses fetched input) +VP9_4K_DECODE_PIPELINE="filesrc location=${VP9_4K_INPUT} ! matroskademux ! vp9parse ! v4l2vp9dec ! videoconvert ! video/x-raw,format=NV12 ! fakevideosink" + +usage() { + cat <"$RES_FILE" + exit 0 +fi + +# --- Resolve test path --- +test_path="$(find_test_case_by_name "$TESTNAME" 2>/dev/null || true)" +if [ -z "$test_path" ] || [ ! -d "$test_path" ]; then + test_path="$SCRIPT_DIR" +fi + +if ! cd "$test_path"; then + log_error "cd failed: $test_path" + printf '%s\n' "$TESTNAME FAIL" >"$RES_FILE" + exit 1 +fi + +# --- Create log directory --- +LOG_DIR="./logs_${TESTNAME}" +mkdir -p "$LOG_DIR" +export LOG_DIR + +# --- Check GStreamer plugins --- +for plugin in v4l2h264dec v4l2h265dec v4l2h264enc v4l2h265enc qtmux qtdemux; do + if ! gst-inspect-1.0 "$plugin" >/dev/null 2>&1; then + log_skip "$TESTNAME SKIP - $plugin plugin not available" + printf '%s\n' "$TESTNAME SKIP" >"$RES_FILE" + exit 0 + fi +done + +# --- Check VP9 plugins (optional, won't skip entire suite) --- +VP9_AVAILABLE=0 +if gst-inspect-1.0 v4l2vp9dec >/dev/null 2>&1 && \ + gst-inspect-1.0 vp9parse >/dev/null 2>&1 && \ + gst-inspect-1.0 matroskademux >/dev/null 2>&1; then + VP9_AVAILABLE=1 + log_info "VP9 decode support detected" +else + log_warn "VP9 decode plugins not available (v4l2vp9dec, vp9parse, or matroskademux missing)" + log_warn "VP9 tests will be skipped" +fi + +# --- Check video devices --- +if ! video_devices_present; then + log_skip "$TESTNAME SKIP - no /dev/video* nodes" + printf '%s\n' "$TESTNAME SKIP" >"$RES_FILE" + exit 0 +fi + +# --- JUnit prep / results files --- +JUNIT_TMP="$LOG_DIR/.junit_cases.xml" +: > "$JUNIT_TMP" +printf '%s\n' "mode,id,result,name,elapsed,pass_runs,fail_runs" > "$LOG_DIR/results.csv" +: > "$LOG_DIR/summary.txt" + +# --- Helper: Fetch VP9 clips if needed --- +fetch_vp9_clips() { + # Check if clips already exist + if [ -f "$VP9_480P_INPUT" ] && [ -f "$VP9_4K_INPUT" ]; then + log_info "VP9 clips already present" + return 0 + fi + + log_info "VP9 clips missing, attempting to fetch from: $VP9_CLIPS_URL" + + # Check network availability + if command -v check_network_status_rc >/dev/null 2>&1; then + if ! check_network_status_rc; then + log_warn "Network offline; cannot fetch VP9 clips" + return 1 + fi + fi + + # Use extract_tar_from_url from functestlib.sh + if command -v extract_tar_from_url >/dev/null 2>&1; then + if extract_tar_from_url "$VP9_CLIPS_URL"; then + log_pass "VP9 clips fetched successfully" + return 0 + else + log_warn "Failed to fetch VP9 clips from $VP9_CLIPS_URL" + return 1 + fi + else + log_warn "extract_tar_from_url not available; cannot fetch VP9 clips" + return 1 + fi +} + +# --- Helper functions --- +run_gst_pipeline() { + id="$1" + pipeline="$2" + logf="$LOG_DIR/${id}.log" + + log_info "[$id] Running GStreamer pipeline" + start_time=$(date +%s) + + if command -v run_with_timeout >/dev/null 2>&1; then + if run_with_timeout "$TIMEOUT" gst-launch-1.0 -v $pipeline >"$logf" 2>&1; then + end_time=$(date +%s) + elapsed=$((end_time - start_time)) + if check_pipeline_errors "$logf"; then + log_fail "[$id] FAIL - Pipeline errors detected (${elapsed}s)" + return 1 + else + log_pass "[$id] PASS (${elapsed}s)" + return 0 + fi + else + rc=$? + end_time=$(date +%s) + elapsed=$((end_time - start_time)) + if [ "$rc" -eq 124 ]; then + log_fail "[$id] FAIL - timeout after ${TIMEOUT}s" + else + log_fail "[$id] FAIL - gst-launch-1.0 exited with code $rc" + fi + return 1 + fi + else + if gst-launch-1.0 -v $pipeline >"$logf" 2>&1; then + end_time=$(date +%s) + elapsed=$((end_time - start_time)) + if check_pipeline_errors "$logf"; then + log_fail "[$id] FAIL - Pipeline errors detected (${elapsed}s)" + return 1 + else + log_pass "[$id] PASS (${elapsed}s)" + return 0 + fi + else + rc=$? + log_fail "[$id] FAIL - gst-launch-1.0 exited with code $rc" + return 1 + fi + fi +} + +check_pipeline_errors() { + logf="$1" + + if grep -q "ERROR:" "$logf"; then + return 0 + fi + + if [ "$STRICT" -eq 1 ] && grep -q "WARNING:" "$logf"; then + return 0 + fi + + if grep -q "v4l2.*failed" "$logf"; then + return 0 + fi + + if grep -q "negotiation failed" "$logf"; then + return 0 + fi + + if grep -q "buffer pool activation failed" "$logf"; then + return 0 + fi + + return 1 +} + +run_test() { + id="$1" + name="$2" + pipeline="$3" + + total=$((total + 1)) + + log_info "----------------------------------------------------------------------" + log_info "[$id] START - $name" + + pass_runs=0 + fail_runs=0 + rep=1 + + while [ "$rep" -le "$REPEAT" ]; do + if [ "$REPEAT" -gt 1 ]; then + log_info "[$id] repeat $rep/$REPEAT" + fi + + if run_gst_pipeline "$id" "$pipeline"; then + pass_runs=$((pass_runs + 1)) + else + fail_runs=$((fail_runs + 1)) + fi + + if [ "$rep" -lt "$REPEAT" ] && [ "$REPEAT_DELAY" -gt 0 ]; then + sleep "$REPEAT_DELAY" + fi + + rep=$((rep + 1)) + done + + final="FAIL" + case "$REPEAT_POLICY" in + any) [ "$pass_runs" -ge 1 ] && final="PASS" ;; + all|*) [ "$fail_runs" -eq 0 ] && final="PASS" ;; + esac + + video_step "$id" "DMESG triage" + video_scan_dmesg_if_enabled "$DMESG_SCAN" "$LOG_DIR" + dmesg_rc=$? + + if [ "$dmesg_rc" -eq 0 ] && [ "$STRICT" -eq 1 ]; then + final="FAIL" + fi + + printf '%s\n' "$id $final $name" >> "$LOG_DIR/summary.txt" + printf '%s\n' "test,$id,$final,$name,0,$pass_runs,$fail_runs" >> "$LOG_DIR/results.csv" + + if [ "$final" = "PASS" ]; then + pass=$((pass + 1)) + else + fail=$((fail + 1)) + suite_rc=1 + [ "$STOP_ON_FAIL" -eq 1 ] && exit 1 + fi + + [ "$POST_TEST_SLEEP" -gt 0 ] && sleep "$POST_TEST_SLEEP" +} + +# --- Fetch VP9 clips if VP9 is available --- +if [ "$VP9_AVAILABLE" -eq 1 ]; then + fetch_vp9_clips || log_warn "VP9 clip fetch failed; VP9 tests will be skipped" +fi + +# --- Run tests --- +log_info "======================================================================" +log_info "==================== Starting $TESTNAME ==========================" +log_info "TIMEOUT=${TIMEOUT}s LOGLEVEL=$LOGLEVEL REPEAT=$REPEAT" +log_info "Resolutions: 480p (${WIDTH_480P}x${HEIGHT_480P}), 4K (${WIDTH_4K}x${HEIGHT_4K})" +log_info "VP9 Support: $([ "$VP9_AVAILABLE" -eq 1 ] && echo "YES" || echo "NO")" +log_info "======================================================================" + +total=0 +pass=0 +fail=0 +skip=0 +suite_rc=0 + +# --- ENCODE TESTS (must run first to generate files for decode) --- + +# Test 1: H.264 480p Encode +run_test "h264-480p-encode" "H.264 480p Encode (${WIDTH_480P}x${HEIGHT_480P})" "$H264_480P_ENCODE_PIPELINE" + +# Test 2: H.264 4K Encode +run_test "h264-4k-encode" "H.264 4K Encode (${WIDTH_4K}x${HEIGHT_4K})" "$H264_4K_ENCODE_PIPELINE" + +# Test 3: H.265 480p Encode +run_test "h265-480p-encode" "H.265 480p Encode (${WIDTH_480P}x${HEIGHT_480P})" "$H265_480P_ENCODE_PIPELINE" + +# Test 4: H.265 4K Encode +run_test "h265-4k-encode" "H.265 4K Encode (${WIDTH_4K}x${HEIGHT_4K})" "$H265_4K_ENCODE_PIPELINE" + +# --- DECODE TESTS (use encoded outputs as inputs) --- + +# Test 5: H.264 480p Decode +if [ -f "$H264_480P_OUTPUT" ]; then + run_test "h264-480p-decode" "H.264 480p Decode (${WIDTH_480P}x${HEIGHT_480P})" "$H264_480P_DECODE_PIPELINE" +else + log_warn "[h264-480p-decode] SKIP - encode output not found: $H264_480P_OUTPUT" + skip=$((skip + 1)) + total=$((total + 1)) +fi + +# Test 6: H.264 4K Decode +if [ -f "$H264_4K_OUTPUT" ]; then + run_test "h264-4k-decode" "H.264 4K Decode (${WIDTH_4K}x${HEIGHT_4K})" "$H264_4K_DECODE_PIPELINE" +else + log_warn "[h264-4k-decode] SKIP - encode output not found: $H264_4K_OUTPUT" + skip=$((skip + 1)) + total=$((total + 1)) +fi + +# Test 7: H.265 480p Decode +if [ -f "$H265_480P_OUTPUT" ]; then + run_test "h265-480p-decode" "H.265 480p Decode (${WIDTH_480P}x${HEIGHT_480P})" "$H265_480P_DECODE_PIPELINE" +else + log_warn "[h265-480p-decode] SKIP - encode output not found: $H265_480P_OUTPUT" + skip=$((skip + 1)) + total=$((total + 1)) +fi + +# Test 8: H.265 4K Decode +if [ -f "$H265_4K_OUTPUT" ]; then + run_test "h265-4k-decode" "H.265 4K Decode (${WIDTH_4K}x${HEIGHT_4K})" "$H265_4K_DECODE_PIPELINE" +else + log_warn "[h265-4k-decode] SKIP - encode output not found: $H265_4K_OUTPUT" + skip=$((skip + 1)) + total=$((total + 1)) +fi + +# --- VP9 DECODE TESTS (use fetched input files) --- + +# Test 9: VP9 480p Decode +if [ "$VP9_AVAILABLE" -eq 1 ]; then + if [ -f "$VP9_480P_INPUT" ]; then + run_test "vp9-480p-decode" "VP9 480p Decode (${WIDTH_480P}x${HEIGHT_480P})" "$VP9_480P_DECODE_PIPELINE" + else + log_warn "[vp9-480p-decode] SKIP - input file not found: $VP9_480P_INPUT" + skip=$((skip + 1)) + total=$((total + 1)) + fi +else + log_info "[vp9-480p-decode] SKIP - VP9 plugins not available" + skip=$((skip + 1)) + total=$((total + 1)) +fi + +# Test 10: VP9 4K Decode +if [ "$VP9_AVAILABLE" -eq 1 ]; then + if [ -f "$VP9_4K_INPUT" ]; then + run_test "vp9-4k-decode" "VP9 4K Decode (${WIDTH_4K}x${HEIGHT_4K})" "$VP9_4K_DECODE_PIPELINE" + else + log_warn "[vp9-4k-decode] SKIP - input file not found: $VP9_4K_INPUT" + skip=$((skip + 1)) + total=$((total + 1)) + fi +else + log_info "[vp9-4k-decode] SKIP - VP9 plugins not available" + skip=$((skip + 1)) + total=$((total + 1)) +fi + +# --- Summary --- +log_info "======================================================================" +log_info "Summary: total=$total pass=$pass fail=$fail skip=$skip" +log_info "======================================================================" + +if [ -s "$LOG_DIR/summary.txt" ]; then + log_info "Per-test results:" + while IFS= read -r line; do + log_info " $line" + done < "$LOG_DIR/summary.txt" +fi + +# --- JUnit finalize --- +if [ -n "$JUNIT_OUT" ]; then + { + printf '\n' "$TESTNAME" "$total" "$fail" "$skip" + cat "$JUNIT_TMP" + printf '\n' + } > "$JUNIT_OUT" + log_info "Wrote JUnit: $JUNIT_OUT" +fi + +# Overall result +if [ "$pass" -eq 0 ] && [ "$fail" -eq 0 ] && [ "$skip" -gt 0 ]; then + log_skip "$TESTNAME: SKIP" + printf '%s\n' "$TESTNAME SKIP" >"$RES_FILE" + exit 0 +fi + +if [ "$suite_rc" -eq 0 ]; then + log_pass "$TESTNAME: PASS (passed: $pass/$total)" + printf '%s\n' "$TESTNAME PASS" >"$RES_FILE" + exit 0 +else + log_fail "$TESTNAME: FAIL (failed: $fail/$total)" + printf '%s\n' "$TESTNAME FAIL" >"$RES_FILE" + exit 1 +fi