diff --git a/Runner/suites/Multimedia/Video/README_Video.md b/Runner/suites/Multimedia/Video/README_Video.md index 28a458c2..d3cc3b24 100644 --- a/Runner/suites/Multimedia/Video/README_Video.md +++ b/Runner/suites/Multimedia/Video/README_Video.md @@ -10,19 +10,28 @@ Video scripts automates the validation of video encoding and decoding capabiliti ## Features - V4L2 driver level test -- Encoding YUV to H264 bitstream -- Decoding H264 bitstream to YUV +- Encoding YUV to H264/H265 bitstream +- Decoding H264/H265/VP9 bitstream to YUV - Compatible with Yocto-based root filesystem +- Supports both encode and decode test modes. +- Parses and runs multiple JSON config files. +- Automatically fetches missing input clips from a predefined URL. +- Supports timeout, repeat, dry-run, and JUnit XML output. +- Performs dmesg scanning for kernel errors. +- Generates summary reports. ## Prerequisites Ensure the following components are present in the target Yocto build: - `iris_v4l2_test` (available in /usr/bin/) - this test app can be compiled from https://github.com/quic/v4l-video-test-app -- input json file for iris_v4l2_test app +- input json config files for encode/decode tests - input bitstream for decode script - input YUV for encode script - Write access to root filesystem (for environment setup) +- POSIX shell utilities: grep, sed, awk, find, sort +- Optional: run_with_timeout function from functestlib.sh +- Internet access (for fetching missing clips) ## Directory Structure @@ -31,12 +40,16 @@ Runner/ ├── suites/ │ ├── Multimedia/ │ │ ├── Video/ -│ │ │ ├── iris_v4l2_video_encode/ -│ │ │ │ ├── H264Encoder.json +│ │ │ ├── Video_V4L2_Runner/ +│ │ │ │ ├── h264Decoder.json +│ │ │ │ ├── h265Decoder.json +│ │ │ │ ├── vp9Decoder.json +│ │ │ │ ├── h264Encoder.json +│ │ │ │ ├── h265Encoder.json │ │ │ │ ├── run.sh -│ │ │ ├── iris_v4l2_video_decode/ -│ │ │ │ ├── H264Decoder.json -│ │ │ │ ├── run.sh +│ │ │ ├── README_Video.md +├── utils/ +│ ├── lib_video.sh ``` ## Usage @@ -55,24 +68,89 @@ git clone cd scp -r Runner user@target_device_ip: ssh user@target_device_ip -cd /Runner && ./run-test.sh iris_v4l2_video_encode +cd /Runner && ./run-test.sh Video_V4L2_Runner ``` Sample output: ``` -sh-5.2# cd /Runner && ./run-test.sh iris_v4l2_video_encode -[Executing test case: /Runner/suites/Multimedia/Video/iris_v4l2_video_encode] 1980-01-08 22:22:15 - -[INFO] 1980-01-08 22:22:15 - ----------------------------------------------------------------------------------------- -[INFO] 1980-01-08 22:22:15 - -------------------Starting iris_v4l2_video_encode Testcase---------------------------- -[INFO] 1980-01-08 22:22:15 - Checking if dependency binary is available -[PASS] 1980-01-08 22:22:15 - Test related dependencies are present. -... -[PASS] 1980-01-08 22:22:17 - iris_v4l2_video_encode : Test Passed -[INFO] 1980-01-08 22:22:17 - -------------------Completed iris_v4l2_video_encode Testcase---------------------------- +sh-5.2# ./run-test.sh Video_V4L2_Runner +[Executing test case: Video_V4L2_Runner] 2025-09-02 05:08:36 - +[INFO] 2025-09-02 05:08:36 - ---------------------------------------------------------------------- +[INFO] 2025-09-02 05:08:36 - ------------------ Starting Video_V4L2_Runner (generic runner) ---------------- +[INFO] 2025-09-02 05:08:36 - === Initialization === +[INFO] 2025-09-02 05:08:36 - TIMEOUT=60s STRICT=0 DMESG_SCAN=1 SUCCESS_RE=SUCCESS +[INFO] 2025-09-02 05:08:36 - LOGLEVEL=15 +[INFO] 2025-09-02 05:08:36 - REPEAT=1 REPEAT_DELAY=0s REPEAT_POLICY=all +[INFO] 2025-09-02 05:08:36 - No config argument passed, searching for JSON files in ./Runner/suites/Multimedia/Video/ +[INFO] 2025-09-02 05:08:36 - Configs to run: +- ./Runner/suites/Multimedia/Video/Video_V4L2_Runner/h264Decoder.json +- ./Runner/suites/Multimedia/Video/Video_V4L2_Runner/h264Encoder.json +- ./Runner/suites/Multimedia/Video/Video_V4L2_Runner/h265Decoder.json +- ./Runner/suites/Multimedia/Video/Video_V4L2_Runner/h265Encoder.json +- ./Runner/suites/Multimedia/Video/Video_V4L2_Runner/vp9Decoder.json +[INFO] 2025-09-02 05:08:37 - No relevant, non-benign errors for modules [oom|memory|BUG|hung task|soft lockup|hard lockup|rcu|page allocation failure|I/O error] in recent dmesg. +[PASS] 2025-09-02 05:08:37 - [Decode1] PASS (1/1 ok) +[INFO] 2025-09-02 05:08:39 - No relevant, non-benign errors for modules [oom|memory|BUG|hung task|soft lockup|hard lockup|rcu|page allocation failure|I/O error] in recent dmesg. +[PASS] 2025-09-02 05:08:39 - [Encode1] PASS (1/1 ok) +[INFO] 2025-09-02 05:08:55 - No relevant, non-benign errors for modules [oom|memory|BUG|hung task|soft lockup|hard lockup|rcu|page allocation failure|I/O error] in recent dmesg. +[PASS] 2025-09-02 05:08:55 - [Decode2] PASS (1/1 ok) +[INFO] 2025-09-02 05:08:57 - No relevant, non-benign errors for modules [oom|memory|BUG|hung task|soft lockup|hard lockup|rcu|page allocation failure|I/O error] in recent dmesg. +[PASS] 2025-09-02 05:08:57 - [Encode2] PASS (1/1 ok) +[INFO] 2025-09-02 05:09:01 - No relevant, non-benign errors for modules [oom|memory|BUG|hung task|soft lockup|hard lockup|rcu|page allocation failure|I/O error] in recent dmesg. +[PASS] 2025-09-02 05:09:01 - [Decode3] PASS (1/1 ok) +[INFO] 2025-09-02 05:09:01 - Summary: total=5 pass=5 fail=0 skip=0 +[PASS] 2025-09-02 05:09:01 - Video_V4L2_Runner: PASS +[PASS] 2025-09-02 05:09:01 - Video_V4L2_Runner passed + +[INFO] 2025-09-02 05:09:01 - ========== Test Summary ========== +PASSED: +Video_V4L2_Runner + +FAILED: +None + +SKIPPED: +None +[INFO] 2025-09-02 05:09:01 - ================================== ``` -3. Results will be available in the `Runner/suites/Multimedia/Video/` directory under each usecase folder. + +4. Results will be available in the `Runner/suites/Multimedia/Video/Video_V4L2_Runner` directory. + +## Common Options + +| Option | Description | +|--------|-------------| + +| `--extract-input-clips true|false` | Auto-fetch missing clips (default: true) | +| `--stack auto|upstream|downstream|base|overlay|up|down` | Select video stack | +| `--platform lemans|monaco|kodiak` | Specify platform for validation | +| `--downstream-fw PATH` | Path to downstream firmware (Kodiak-specific) | +| `--force` | Force stack switch or firmware override | +| `--verbose` | Enable verbose logging | +| `--config path.json` | Run a specific config file | +| `--dir DIR` | Directory to search for configs | +| `--pattern GLOB` | Filter configs by glob pattern | +| `--timeout S` | Timeout per test (default: 60s) | +| `--strict` | Fail on critical dmesg errors | +| `--no-dmesg` | Disable dmesg scanning | +| `--max N` | Run at most N tests | +| `--stop-on-fail` | Stop on first failure | +| `--loglevel N` | Set log level for `iris_v4l2_test` | +| `--repeat N` | Repeat each test N times | +| `--repeat-delay S` | Delay between repeats | +| `--repeat-policy | all or any | +| `--junit FILE` | Output JUnit XML to file | +| `--dry-run` | Show commands without executing | + ## Notes -- The script does not take any arguments. +- The script auto-detects encode/decode mode based on config filename. - It validates the presence of required libraries before executing tests. -- If any critical tool is missing, the script exits with an error message. \ No newline at end of file +- If any critical tool is missing, the script exits with an error message. +- Missing input clips are fetched from: +https://github.com/qualcomm-linux/qcom-linux-testkit/releases/download/IRIS-Video-Files-v1.0/video_clips_iris.tar.gz + +## License + +SPDX-License-Identifier: BSD-3-Clause-Clear +(C) Qualcomm Technologies, Inc. and/or its subsidiaries. \ No newline at end of file diff --git a/Runner/suites/Multimedia/Video/iris_v4l2_video_decode/h264Decoder.json b/Runner/suites/Multimedia/Video/Video_V4L2_Runner/h264Decoder.json old mode 100755 new mode 100644 similarity index 67% rename from Runner/suites/Multimedia/Video/iris_v4l2_video_decode/h264Decoder.json rename to Runner/suites/Multimedia/Video/Video_V4L2_Runner/h264Decoder.json index 2afb90f4..6426a7a5 --- a/Runner/suites/Multimedia/Video/iris_v4l2_video_decode/h264Decoder.json +++ b/Runner/suites/Multimedia/Video/Video_V4L2_Runner/h264Decoder.json @@ -2,16 +2,16 @@ "ExecutionMode": "Sequential", "TestCases": [ { - "Name" : "Decoder Testcase", + "Name" : "H264 Decoder Testcase", "TestConfigs" : { "Domain": "Decoder", - "InputPath": "./simple_AVC_720p_10fps_90frames.264", + "InputPath": "./720p_AVC.h264", "NumFrames": -1, "CodecName": "AVC", "PixelFormat": "NV12", "Width": 1280, "Height": 720, - "Outputpath": "./output_simple_nv12_720p_90frms.yuv", + "Outputpath": "./720p_AVC.yuv", "InputBufferCount": 16, "OutputBufferCount": 16 } diff --git a/Runner/suites/Multimedia/Video/iris_v4l2_video_encode/h264Encoder.json b/Runner/suites/Multimedia/Video/Video_V4L2_Runner/h264Encoder.json old mode 100755 new mode 100644 similarity index 87% rename from Runner/suites/Multimedia/Video/iris_v4l2_video_encode/h264Encoder.json rename to Runner/suites/Multimedia/Video/Video_V4L2_Runner/h264Encoder.json index 4855b5bd..06263c31 --- a/Runner/suites/Multimedia/Video/iris_v4l2_video_encode/h264Encoder.json +++ b/Runner/suites/Multimedia/Video/Video_V4L2_Runner/h264Encoder.json @@ -5,13 +5,13 @@ "Name" : "Encoder Testcase", "TestConfigs" : { "Domain": "Encoder", - "InputPath": "./simple_nv12_720p_90frms.yuv", + "InputPath": "./90frames_yuv.yuv", "NumFrames": -1, "CodecName": "AVC", "PixelFormat": "NV12", "Width": 1280, "Height": 720, - "Outputpath": "./output_simple_AVC_720p_10fps.h264", + "Outputpath": "./Enc_AVC_720p.h264", "InputBufferCount": 32, "OutputBufferCount": 32, "OperatingRate": 10, diff --git a/Runner/suites/Multimedia/Video/Video_V4L2_Runner/h265Decoder.json b/Runner/suites/Multimedia/Video/Video_V4L2_Runner/h265Decoder.json new file mode 100644 index 00000000..0b3458bd --- /dev/null +++ b/Runner/suites/Multimedia/Video/Video_V4L2_Runner/h265Decoder.json @@ -0,0 +1,21 @@ +{ + "ExecutionMode": "Sequential", + "TestCases": [ + { + "Name": "HEVC Decoder TestCase", + "TestConfigs": { + "Domain": "Decoder", + "InputPath": "./720x1280_hevc.h265", + "NumFrames": -1, + "CodecName": "HEVC", + "PixelFormat": "NV12", + "Width": 1280, + "Height": 720, + "Outputpath": "./720x1280_hevc.yuv", + "InputBufferCount": 16, + "OutputBufferCount": 16, + "UseMinBufferCtrl": false + } + } + ] +} diff --git a/Runner/suites/Multimedia/Video/Video_V4L2_Runner/h265Encoder.json b/Runner/suites/Multimedia/Video/Video_V4L2_Runner/h265Encoder.json new file mode 100644 index 00000000..6715dd97 --- /dev/null +++ b/Runner/suites/Multimedia/Video/Video_V4L2_Runner/h265Encoder.json @@ -0,0 +1,102 @@ +{ + "ExecutionMode": "Sequential", + "TestCases": [ + { + "Name": "HEVC Encode TestCase", + "TestConfigs": { + "VideoDevice": "/dev/video1", + "Domain": "Encoder", + "InputPath": "./60frames_yuv.yuv", + "NumFrames": 60, + "CodecName": "HEVC", + "PixelFormat": "NV12", + "Width": 1280, + "Height": 720, + "Outputpath": "./Enc_HEVC__Main_1280_720.265", + "UseMinBufferCtrl": false, + "InputBufferCount": 32, + "OutputBufferCount": 32, + "OperatingRate": 30, + "FrameRate": 30, + "StaticControls": [ + { + "Id": "Profile", + "Vtype": "String", + "Value": "MAIN" + }, + { + "Id": "Level", + "Vtype": "String", + "Value": "5.0" + }, + { + "Id": "FrameRC", + "Vtype": "Int", + "Value": 1 + }, + { + "Id": "BitRate", + "Vtype": "Int", + "Value": 3662400 + }, + { + "Id": "BitRateMode", + "Vtype": "String", + "Value": "VBR" + }, + { + "Id": "PrefixHeaderMode", + "Vtype": "String", + "Value": "JOINED" + }, + { + "Id": "Tier", + "Vtype": "String", + "Value": "HIGH" + }, + { + "Id": "GOPSize", + "Vtype": "Int", + "Value": 59 + }, + { + "Id": "MultiSliceMode", + "Vtype": "String", + "Value": "SINGLE" + }, + { + "Id": "MinIQP", + "Vtype": "Int", + "Value": 10 + }, + { + "Id": "MinPQP", + "Vtype": "Int", + "Value": 10 + }, + { + "Id": "MinBQP", + "Vtype": "Int", + "Value": 10 + }, + { + "Id": "MaxIQP", + "Vtype": "Int", + "Value": 51 + }, + { + "Id": "MaxPQP", + "Vtype": "Int", + "Value": 51 + }, + { + "Id": "MaxBQP", + "Vtype": "Int", + "Value": 51 + } + ], + "DynamicControls": [] + } + } + ] +} diff --git a/Runner/suites/Multimedia/Video/Video_V4L2_Runner/run.sh b/Runner/suites/Multimedia/Video/Video_V4L2_Runner/run.sh new file mode 100755 index 00000000..0f6edf47 --- /dev/null +++ b/Runner/suites/Multimedia/Video/Video_V4L2_Runner/run.sh @@ -0,0 +1,397 @@ +#!/bin/sh + +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause-Clear +# POSIX-only Video V4L2 runner with stack selection via utils/lib_video.sh + +# --- Load init_env, functestlib, lib_video --- +SCRIPT_DIR="$(cd "$(dirname "$0")" && 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 + printf '%s\n' "[ERROR] Could not find init_env (starting at $SCRIPT_DIR)" >&2 + exit 1 +fi +# shellcheck disable=SC1090 +. "$INIT_ENV" +# shellcheck disable=SC1091 +. "$TOOLS/functestlib.sh" +# shellcheck disable=SC1091 +. "$TOOLS/lib_video.sh" + +TESTNAME="Video_V4L2_Runner" +RES_FILE="./${TESTNAME}.res" + +: "${TAR_URL:=https://github.com/qualcomm-linux/qcom-linux-testkit/releases/download/IRIS-Video-Files-v1.0/video_clips_iris.tar.gz}" + +# --- Defaults / knobs --- +TIMEOUT="${TIMEOUT:-60}" +STRICT="${STRICT:-0}" +DMESG_SCAN="${DMESG_SCAN:-1}" +PATTERN="" +MAX="${MAX:-0}" +STOP_ON_FAIL="${STOP_ON_FAIL:-0}" +DRY=0 +EXTRACT_INPUT_CLIPS="${EXTRACT_INPUT_CLIPS:-true}" +SUCCESS_RE="${SUCCESS_RE:-SUCCESS}" +LOGLEVEL="${LOGLEVEL:-15}" +REPEAT="${REPEAT:-1}" +REPEAT_DELAY="${REPEAT_DELAY:-0}" +REPEAT_POLICY="${REPEAT_POLICY:-all}" +JUNIT_OUT="" +VERBOSE=0 + +VIDEO_STACK="${VIDEO_STACK:-auto}" # accepts base/overlay/up/down +VIDEO_PLATFORM="${VIDEO_PLATFORM:-}" # lemans|monaco|kodiak|"" (auto) +VIDEO_FW_DS="${VIDEO_FW_DS:-}" # Kodiak DS firmware path +VIDEO_FW_BACKUP_DIR="${VIDEO_FW_BACKUP_DIR:-}" # override, default /opt (in lib) +VIDEO_NO_REBOOT="${VIDEO_NO_REBOOT:-0}" +VIDEO_FORCE="${VIDEO_FORCE:-0}" +VIDEO_APP="${VIDEO_APP:-/usr/bin/iris_v4l2_test}" + +usage() { + cat </dev/null || echo "$SCRIPT_DIR")" +cd "$test_path" || { log_error "cd failed: $test_path"; printf '%s\n' "$TESTNAME FAIL" >"$RES_FILE"; exit 1; } + +# --- Optional early fetch of bundle (best-effort) --- +if [ "$EXTRACT_INPUT_CLIPS" = "true" ]; then + video_step "" "Early bundle fetch (best-effort)" + extract_tar_from_url "$TAR_URL" || true +fi + +LOG_DIR="./logs_${TESTNAME}" +mkdir -p "$LOG_DIR" + +log_info "----------------------------------------------------------------------" +log_info "---------------------- Starting $TESTNAME (modular) -------------------" +log_info "STACK=$VIDEO_STACK PLATFORM=${VIDEO_PLATFORM:-auto} STRICT=$STRICT DMESG_SCAN=$DMESG_SCAN" +log_info "TIMEOUT=${TIMEOUT}s LOGLEVEL=$LOGLEVEL REPEAT=$REPEAT REPEAT_POLICY=$REPEAT_POLICY" +log_info "APP=$VIDEO_APP" +[ -n "$VIDEO_FW_DS" ] && log_info "Downstream FW override: $VIDEO_FW_DS" +[ -n "$VIDEO_FW_BACKUP_DIR" ] && log_info "FW backup override: $VIDEO_FW_BACKUP_DIR" +[ "$VERBOSE" -eq 1 ] && log_info "CWD=$(pwd) | SCRIPT_DIR=$SCRIPT_DIR | test_path=$test_path" + +# --- Required tools --- +check_dependencies iris_v4l2_test grep sed awk find sort || { + log_skip "$TESTNAME SKIP - required tools missing" + printf '%s\n' "$TESTNAME SKIP" >"$RES_FILE" + exit 0 +} +# App presence (not necessarily executable if set to custom path on target) +if [ ! -x "$VIDEO_APP" ]; then + log_warn "App not executable at VIDEO_APP=$VIDEO_APP (will still try to run)" +fi + +# Warn if not root (module/blacklist ops may fail) +video_warn_if_not_root + +# --- Ensure desired video stack (hot switch best-effort) --- +plat="$VIDEO_PLATFORM" +[ -n "$plat" ] || plat=$(video_detect_platform) +log_info "Detected platform: $plat" + +# Export envs used by lib +export VIDEO_FW_DS VIDEO_FW_BACKUP_DIR VIDEO_NO_REBOOT VIDEO_FORCE LOG_DIR TAR_URL + +# Normalize stack to accept base/overlay/up/down +VIDEO_STACK=$(video_normalize_stack "$VIDEO_STACK") + +pre_stack=$(video_stack_status "$plat") +log_info "Current video stack (pre): $pre_stack" + +video_step "" "Apply desired stack = $VIDEO_STACK" +post_stack=$(video_ensure_stack "$VIDEO_STACK" "$plat" || true) +if [ -z "$post_stack" ] || [ "$post_stack" = "unknown" ]; then + log_warn "Could not fully switch to requested stack=$VIDEO_STACK (platform=$plat). Blacklist updated; reboot may be required." + post_stack=$(video_stack_status "$plat") +fi +log_info "Video stack (post): $post_stack" + +# Per-platform module validation +case "$plat" in + lemans|monaco) + if [ "$post_stack" = "upstream" ]; then + if video_has_module_loaded qcom_iris && video_has_module_loaded iris_vpu; then + log_pass "Upstream validated: qcom_iris + iris_vpu present" + else + log_warn "Upstream expected but modules mismatch (need qcom_iris and iris_vpu)" + fi + elif [ "$post_stack" = "downstream" ]; then + if video_has_module_loaded iris_vpu && ! video_has_module_loaded qcom_iris; then + log_pass "Downstream validated: only iris_vpu present" + else + log_warn "Downstream expected but qcom_iris still loaded or iris_vpu missing" + fi + fi + ;; + kodiak) + if [ "$post_stack" = "upstream" ]; then + if video_has_module_loaded venus_core && video_has_module_loaded venus_dec && video_has_module_loaded venus_enc; then + log_pass "Upstream validated: venus_core/dec/enc present" + else + log_warn "Upstream expected but venus modules mismatch" + fi + elif [ "$post_stack" = "downstream" ]; then + if video_has_module_loaded iris_vpu; then + log_pass "Downstream validated: iris_vpu present (Kodiak)" + else + log_warn "Downstream expected but iris_vpu not present (Kodiak)" + fi + fi + ;; + *) log_warn "Unknown platform; skipping strict module validation" ;; +esac + +# Validate numeric loglevel +case "$LOGLEVEL" in ''|*[!0-9]* ) log_warn "Non-numeric --loglevel '$LOGLEVEL'; using 15"; LOGLEVEL=15 ;; esac + +# --- Discover config list --- +CFG_LIST="$LOG_DIR/.cfgs"; : > "$CFG_LIST" +if [ -z "$CFG" ]; then + log_info "No --config passed, searching for JSON under testcase dir: $test_path" + find "$test_path" -type f -name "*.json" 2>/dev/null | sort > "$CFG_LIST" +else + printf '%s\n' "$CFG" > "$CFG_LIST" +fi +if [ ! -s "$CFG_LIST" ]; then + [ -n "$DIR" ] || DIR="$test_path" + if [ -n "$PATTERN" ]; then + find "$DIR" -type f -name "$PATTERN" 2>/dev/null | sort > "$CFG_LIST" + else + find "$DIR" -type f -name "*.json" 2>/dev/null | sort > "$CFG_LIST" + fi + if [ ! -s "$CFG_LIST" ]; then + log_skip "$TESTNAME SKIP - no JSON configs found" + printf '%s\n' "$TESTNAME SKIP" >"$RES_FILE" + exit 0 + fi +fi + +cfg_count=$(wc -l < "$CFG_LIST" 2>/dev/null | tr -d ' ') +log_info "Discovered $cfg_count JSON config(s) to run" + +# --- 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" + +# --- Suite loop --- +total=0; pass=0; fail=0; skip=0; suite_rc=0 + +while IFS= read -r cfg; do + [ -n "$cfg" ] || continue + total=$((total + 1)) + + if video_is_decode_cfg "$cfg"; then mode="decode"; else mode="encode"; fi + + name_and_id=$(video_pretty_name_from_cfg "$cfg") + pretty=$(printf '%s' "$name_and_id" | cut -d'|' -f1) + raw_codec=$(video_guess_codec_from_cfg "$cfg") + codec=$(video_canon_codec "$raw_codec") + safe_codec=$(printf '%s' "$codec" | tr ' /' '__') + base_noext=$(basename "$cfg" .json) + id="${mode}-${safe_codec}-${base_noext}" + + log_info "[$id] START — mode=$mode codec=$codec name=\"$pretty\" cfg=\"$cfg\"" + + video_step "$id" "Check /dev/video* presence" + if ! video_devices_present; then + log_skip "[$id] SKIP - no /dev/video* nodes" + printf '%s\n' "$id SKIP $pretty" >> "$LOG_DIR/summary.txt" + printf '%s\n' "$mode,$id,SKIP,$pretty,0,0,0" >> "$LOG_DIR/results.csv" + skip=$((skip + 1)) + continue + fi + + if [ "$EXTRACT_INPUT_CLIPS" = "true" ]; then + video_step "$id" "Ensure clips present or fetch" + video_ensure_clips_present_or_fetch "$cfg" "$TAR_URL" + ce=$? + if [ "$ce" -eq 2 ] 2>/dev/null; then + # offline → only SKIP decode cases (align with audio semantics) + if [ "$mode" = "decode" ]; then + log_skip "[$id] SKIP - offline and clips missing (decode case)" + printf '%s\n' "$id SKIP $pretty" >> "$LOG_DIR/summary.txt" + printf '%s\n' "$mode,$id,SKIP,$pretty,0,0,0" >> "$LOG_DIR/results.csv" + skip=$((skip + 1)) + continue + fi + elif [ "$ce" -eq 1 ] 2>/dev/null; then + log_fail "[$id] FAIL - fetch/extract failed while online" + printf '%s\n' "$id FAIL $pretty" >> "$LOG_DIR/summary.txt" + printf '%s\n' "$mode,$id,FAIL,$pretty,0,0,0" >> "$LOG_DIR/results.csv" + fail=$((fail + 1)); suite_rc=1 + [ "$STOP_ON_FAIL" -eq 1 ] && break + continue + fi + fi + + # Strict clip existence check after optional fetch + video_step "$id" "Verify required clips exist" + missing_case=0 + clips_file="$LOG_DIR/.clips.$$" + video_extract_input_clips "$cfg" > "$clips_file" + if [ -s "$clips_file" ]; then + while IFS= read -r pth; do + [ -z "$pth" ] && continue + case "$pth" in + /*) abs="$pth" ;; + *) abs=$(cd "$(dirname "$cfg")" 2>/dev/null && pwd)/$pth ;; + esac + [ -f "$abs" ] || missing_case=1 + done < "$clips_file" + fi + rm -f "$clips_file" 2>/dev/null || true + if [ "$missing_case" -eq 1 ] 2>/dev/null; then + log_fail "[$id] Required input clip(s) not present — $pretty" + printf '%s\n' "$id FAIL $pretty" >> "$LOG_DIR/summary.txt" + printf '%s\n' "$mode,$id,FAIL,$pretty,0,0,0" >> "$LOG_DIR/results.csv" + fail=$((fail + 1)); suite_rc=1 + [ "$STOP_ON_FAIL" -eq 1 ] && break + continue + fi + + if [ "$DRY" -eq 1 ]; then + video_step "$id" "DRY RUN - print command" + log_info "[dry] [$id] $VIDEO_APP --config \"$cfg\" --loglevel $LOGLEVEL — $pretty" + printf '%s\n' "$id DRY-RUN $pretty" >> "$LOG_DIR/summary.txt" + printf '%s\n' "$mode,$id,DRY-RUN,$pretty,0,0,0" >> "$LOG_DIR/results.csv" + continue + fi + + pass_runs=0; fail_runs=0; rep=1 + start_case=$(date +%s 2>/dev/null || printf '%s' 0) + logf="$LOG_DIR/${id}.log" + + while [ "$rep" -le "$REPEAT" ]; do + [ "$REPEAT" -gt 1 ] && log_info "[$id] repeat $rep/$REPEAT — $pretty" + video_step "$id" "Execute app" + if video_run_once "$cfg" "$logf" "$TIMEOUT" "$SUCCESS_RE" "$LOGLEVEL"; 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 + + end_case=$(date +%s 2>/dev/null || printf '%s' 0) + elapsed=$((end_case - start_case)); [ "$elapsed" -lt 0 ] 2>/dev/null && elapsed=0 + + 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 ]; then + log_warn "[$id] dmesg reported errors (STRICT=$STRICT)" + [ "$STRICT" -eq 1 ] && final="FAIL" + fi + + # Append explicit RESULT line into the case log for CI grepability + { + printf 'RESULT id=%s mode=%s pretty="%s" final=%s pass_runs=%s fail_runs=%s elapsed=%s\n' \ + "$id" "$mode" "$pretty" "$final" "$pass_runs" "$fail_runs" "$elapsed" + } >> "$logf" 2>&1 + + video_junit_append_case "$JUNIT_TMP" "Video.$mode" "$pretty" "$elapsed" "$final" "$logf" + + case "$final" in + PASS) log_pass "[$id] PASS ($pass_runs/$REPEAT ok) — $pretty" ;; + FAIL) log_fail "[$id] FAIL (pass=$pass_runs fail=$fail_runs) — $pretty" ;; + SKIP) log_skip "[$id] SKIP — $pretty" ;; + esac + + printf '%s\n' "$id $final $pretty" >> "$LOG_DIR/summary.txt" + printf '%s\n' "$mode,$id,$final,$pretty,$elapsed,$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 ] && break + fi + + if [ "$MAX" -gt 0 ] && [ "$total" -ge "$MAX" ]; then + log_info "Reached MAX=$MAX tests; stopping" + break + fi +done < "$CFG_LIST" + +log_info "Summary: total=$total pass=$pass fail=$fail skip=$skip" + +# --- JUnit finalize --- +if [ -n "$JUNIT_OUT" ]; then + tests=$((pass + fail + skip)) + failures="$fail" + skipped="$skip" + { + printf '\n' "$TESTNAME" "$tests" "$failures" "$skipped" + cat "$JUNIT_TMP" + printf '\n' + } > "$JUNIT_OUT" + log_info "Wrote JUnit: $JUNIT_OUT" +fi + +if [ "$suite_rc" -eq 0 ] 2>/dev/null; then + log_pass "$TESTNAME: PASS"; printf '%s\n' "$TESTNAME PASS" >"$RES_FILE" +else + log_fail "$TESTNAME: FAIL"; printf '%s\n' "$TESTNAME FAIL" >"$RES_FILE" +fi +exit "$suite_rc" diff --git a/Runner/suites/Multimedia/Video/Video_V4L2_Runner/vp9Decoder.json b/Runner/suites/Multimedia/Video/Video_V4L2_Runner/vp9Decoder.json new file mode 100644 index 00000000..f876eb01 --- /dev/null +++ b/Runner/suites/Multimedia/Video/Video_V4L2_Runner/vp9Decoder.json @@ -0,0 +1,21 @@ +{ + "ExecutionMode": "Sequential", + "TestCases": [ + { + "Name": "VP9 Decode TestCase", + "TestConfigs": { + "Domain": "Decoder", + "InputPath": "./720x1280_vp9.ivf", + "NumFrames": -1, + "CodecName": "VP9", + "PixelFormat": "NV12", + "Width": 1280, + "Height": 720, + "Outputpath": "./720x1280_vp9.yuv", + "InputBufferCount": 16, + "OutputBufferCount": 16, + "UseMinBufferCtrl": false + } + } + ] +} diff --git a/Runner/suites/Multimedia/Video/iris_v4l2_video_decode/run.sh b/Runner/suites/Multimedia/Video/iris_v4l2_video_decode/run.sh deleted file mode 100755 index 88e6f7bc..00000000 --- a/Runner/suites/Multimedia/Video/iris_v4l2_video_decode/run.sh +++ /dev/null @@ -1,60 +0,0 @@ -#!/bin/sh - -# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. -# SPDX-License-Identifier: BSD-3-Clause-Clear - -# Robustly find and source init_env -SCRIPT_DIR="$(cd "$(dirname "$0")" && 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 if not already loaded (idempotent) -if [ -z "$__INIT_ENV_LOADED" ]; then - # shellcheck disable=SC1090 - . "$INIT_ENV" -fi -# Always source functestlib.sh, using $TOOLS exported by init_env -# shellcheck disable=SC1090,SC1091 -. "$TOOLS/functestlib.sh" - -TESTNAME="iris_v4l2_video_decode" -test_path=$(find_test_case_by_name "$TESTNAME") -cd "$test_path" || exit 1 -# shellcheck disable=SC2034 -res_file="./$TESTNAME.res" -TAR_URL="https://github.com/qualcomm-linux/qcom-linux-testkit/releases/download/IRIS-Video-Files-v1.0/video_clips_iris.tar.gz" - -log_info "-----------------------------------------------------------------------------------------" -log_info "-------------------Starting $TESTNAME Testcase----------------------------" -log_info "=== Test Initialization ===" - -log_info "Checking if dependency binary is available" -check_dependencies iris_v4l2_test -extract_tar_from_url "$TAR_URL" - -# Run the first test -iris_v4l2_test --config "${test_path}/h264Decoder.json" --loglevel 15 >> "${test_path}/video_dec.txt" - -if grep -q "SUCCESS" "${test_path}/video_dec.txt"; then - log_pass "$TESTNAME : Test Passed" - echo "$TESTNAME PASS" > "$test_path/$TESTNAME.res" - exit 0 -else - log_fail "$TESTNAME : Test Failed" - echo "$TESTNAME FAIL" > "$test_path/$TESTNAME.res" - exit 1 -fi - -log_info "-------------------Completed $TESTNAME Testcase----------------------------" diff --git a/Runner/suites/Multimedia/Video/iris_v4l2_video_encode/run.sh b/Runner/suites/Multimedia/Video/iris_v4l2_video_encode/run.sh deleted file mode 100755 index 6de21775..00000000 --- a/Runner/suites/Multimedia/Video/iris_v4l2_video_encode/run.sh +++ /dev/null @@ -1,60 +0,0 @@ -#!/bin/sh - -# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. -# SPDX-License-Identifier: BSD-3-Clause-Clear - -# Robustly find and source init_env -SCRIPT_DIR="$(cd "$(dirname "$0")" && 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 if not already loaded (idempotent) -if [ -z "$__INIT_ENV_LOADED" ]; then - # shellcheck disable=SC1090 - . "$INIT_ENV" -fi -# Always source functestlib.sh, using $TOOLS exported by init_env -# shellcheck disable=SC1090,SC1091 -. "$TOOLS/functestlib.sh" - -TESTNAME="iris_v4l2_video_encode" -test_path=$(find_test_case_by_name "$TESTNAME") -cd "$test_path" || exit 1 -# shellcheck disable=SC2034 -res_file="./$TESTNAME.res" -TAR_URL="https://github.com/qualcomm-linux/qcom-linux-testkit/releases/download/IRIS-Video-Files-v1.0/video_clips_iris.tar.gz" - -log_info "-----------------------------------------------------------------------------------------" -log_info "-------------------Starting $TESTNAME Testcase----------------------------" -log_info "=== Test Initialization ===" - -log_info "Checking if dependency binary is available" -check_dependencies iris_v4l2_test -extract_tar_from_url "$TAR_URL" - -# Run the first test -iris_v4l2_test --config "${test_path}/h264Encoder.json" --loglevel 15 >> "${test_path}/video_enc.txt" - -if grep -q "SUCCESS" "${test_path}/video_enc.txt"; then - log_pass "$TESTNAME : Test Passed" - echo "$TESTNAME PASS" > "$test_path/$TESTNAME.res" - exit 0 -else - log_fail "$TESTNAME : Test Failed" - echo "$TESTNAME FAIL" > "$test_path/$TESTNAME.res" - exit 1 -fi - -log_info "-------------------Completed $TESTNAME Testcase----------------------------" diff --git a/Runner/utils/lib_video.sh b/Runner/utils/lib_video.sh new file mode 100755 index 00000000..cf4097c2 --- /dev/null +++ b/Runner/utils/lib_video.sh @@ -0,0 +1,527 @@ +#!/bin/sh +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause-Clear +# Common, POSIX-compliant helpers for Qualcomm video stack selection and V4L2 testing. +# Requires functestlib.sh: log_info/log_warn/log_pass/log_fail/log_skip, +# check_dependencies, extract_tar_from_url, (optional) run_with_timeout, ensure_network_online. + +# ----------------------------------------------------------------------------- +# Public env knobs (may be exported by caller or set via CLI in run.sh) +# ----------------------------------------------------------------------------- +# VIDEO_STACK auto|upstream|downstream|base|overlay|up|down (default: auto) +# VIDEO_PLATFORM lemans|monaco|kodiak|"" (auto-detect) +# VIDEO_FW_DS Downstream FW for Kodiak (e.g. /opt/downstream/vpu20_p1_gen2.mbn) +# VIDEO_FW_BACKUP_DIR Where to backup FW (default: /opt) +# VIDEO_NO_REBOOT 1 = don't suggest reboot; we hot-switch best-effort (default 0) +# VIDEO_FORCE 1 = force re-switch even if looks OK (default 0) +# TAR_URL bundle for input clips (used by video_ensure_clips_present_or_fetch) +# VIDEO_APP path to iris_v4l2_test (default /usr/bin/iris_v4l2_test) + +# ----------------------------------------------------------------------------- +# Constants / tool paths +# ----------------------------------------------------------------------------- +IRIS_UP_MOD="qcom_iris" +IRIS_VPU_MOD="iris_vpu" +VENUS_CORE_MOD="venus_core" +VENUS_DEC_MOD="venus_dec" +VENUS_ENC_MOD="venus_enc" + +BLACKLIST_DIR="/etc/modprobe.d" +BLACKLIST_FILE="$BLACKLIST_DIR/blacklist.conf" + +FW_PATH_KODIAK="/lib/firmware/qcom/vpu/vpu20_p1_gen2.mbn" +FW_BACKUP_DIR="${VIDEO_FW_BACKUP_DIR:-/opt}" + +MODPROBE="$(command -v modprobe 2>/dev/null || printf '%s' /sbin/modprobe)" +LSMOD="$(command -v lsmod 2>/dev/null || printf '%s' /sbin/lsmod)" + +# Default app path (caller may override via env) +VIDEO_APP="${VIDEO_APP:-/usr/bin/iris_v4l2_test}" + +# ----------------------------------------------------------------------------- +# Tiny utils +# ----------------------------------------------------------------------------- +video_exist_cmd() { command -v "$1" >/dev/null 2>&1; } + +video_warn_if_not_root() { + uid="$(id -u 2>/dev/null || printf '%s' 1)" + if [ "$uid" -ne 0 ] 2>/dev/null; then + log_warn "Not running as root; module/blacklist operations may fail." + fi +} + +video_has_module_loaded() { + "$LSMOD" 2>/dev/null | awk '{print $1}' | grep -q "^$1$" +} + +video_devices_present() { + set -- /dev/video* 2>/dev/null + [ -e "$1" ] +} + +video_step() { + # Step logger for CI: video_step "[id]" "message" + id="$1"; msg="$2" + if [ -n "$id" ]; then + log_info "[$id] STEP: $msg" + else + log_info "STEP: $msg" + fi +} + +# ----------------------------------------------------------------------------- +# Stack normalization & auto preference +# ----------------------------------------------------------------------------- +video_normalize_stack() { + s="$(printf '%s' "$1" | tr '[:upper:]' '[:lower:]')" + case "$s" in + upstream|base|up) printf '%s\n' "upstream" ;; + downstream|overlay|down) printf '%s\n' "downstream" ;; + auto|"") printf '%s\n' "auto" ;; + *) printf '%s\n' "$s" ;; + esac +} + +video_is_blacklisted() { + tok="$1" + [ -f "$BLACKLIST_FILE" ] || return 1 + grep -q "^blacklist[[:space:]]\+$tok$" "$BLACKLIST_FILE" 2>/dev/null +} + +video_auto_preference_from_blacklist() { + plat="$1" + case "$plat" in + lemans|monaco) + if video_is_blacklisted "qcom-iris" || video_is_blacklisted "qcom_iris"; then + printf '%s\n' "downstream"; return 0 + fi + ;; + kodiak) + if video_is_blacklisted "venus-core" || video_is_blacklisted "venus_core" \ + || video_is_blacklisted "venus-dec" || video_is_blacklisted "venus_dec" \ + || video_is_blacklisted "venus-enc" || video_is_blacklisted "venus_enc"; then + printf '%s\n' "downstream"; return 0 + fi + ;; + esac + printf '%s\n' "unknown" + return 0 +} + +# ----------------------------------------------------------------------------- +# Blacklist management +# ----------------------------------------------------------------------------- +video_ensure_blacklist() { + tok="$1" + mkdir -p "$BLACKLIST_DIR" 2>/dev/null || true + if [ -f "$BLACKLIST_FILE" ] && grep -q "^blacklist[[:space:]]\+$tok$" "$BLACKLIST_FILE" 2>/dev/null; then + return 0 + fi + printf 'blacklist %s\n' "$tok" >>"$BLACKLIST_FILE" +} + +video_remove_blacklist() { + tok="$1" + if [ -f "$BLACKLIST_FILE" ]; then + tmp="$BLACKLIST_FILE.tmp.$$" + sed "/^[[:space:]]*blacklist[[:space:]]\+${tok}[[:space:]]*$/d" "$BLACKLIST_FILE" >"$tmp" 2>/dev/null && mv "$tmp" "$BLACKLIST_FILE" + fi +} + +# ----------------------------------------------------------------------------- +# Platform detect → lemans|monaco|kodiak|unknown +# ----------------------------------------------------------------------------- +video_detect_platform() { + model=""; compat="" + if [ -r /proc/device-tree/model ]; then model=$(tr -d '\000' /dev/null); fi + if [ -r /proc/device-tree/compatible ]; then compat=$(tr -d '\000' /dev/null); fi + s=$(printf '%s\n%s\n' "$model" "$compat" | tr '[:upper:]' '[:lower:]') + + echo "$s" | grep -q "qcs9100" && { printf '%s\n' "lemans"; return 0; } + echo "$s" | grep -q "qcs8300" && { printf '%s\n' "monaco"; return 0; } + echo "$s" | grep -q "qcs6490" && { printf '%s\n' "kodiak"; return 0; } + echo "$s" | grep -q "ride-sx" && echo "$s" | grep -q "9100" && { printf '%s\n' "lemans"; return 0; } + echo "$s" | grep -q "ride-sx" && echo "$s" | grep -q "8300" && { printf '%s\n' "monaco"; return 0; } + echo "$s" | grep -q "rb3" && echo "$s" | grep -q "6490" && { printf '%s\n' "kodiak"; return 0; } + + printf '%s\n' "unknown" +} + +# ----------------------------------------------------------------------------- +# Validation +# ----------------------------------------------------------------------------- +video_validate_upstream_loaded() { + plat="$1" + case "$plat" in + lemans|monaco) + video_has_module_loaded "$IRIS_UP_MOD" && video_has_module_loaded "$IRIS_VPU_MOD" + return $? + ;; + kodiak) + video_has_module_loaded "$VENUS_CORE_MOD" && video_has_module_loaded "$VENUS_DEC_MOD" && video_has_module_loaded "$VENUS_ENC_MOD" + return $? + ;; + *) return 1 ;; + esac +} + +video_validate_downstream_loaded() { + plat="$1" + case "$plat" in + lemans|monaco|kodiak) + video_has_module_loaded "$IRIS_VPU_MOD" + return $? + ;; + *) return 1 ;; + esac +} + +video_stack_status() { + plat="$1" + if video_validate_downstream_loaded "$plat"; then + printf '%s\n' "downstream" + elif video_validate_upstream_loaded "$plat"; then + printf '%s\n' "upstream" + else + printf '%s\n' "unknown" + fi +} + +# ----------------------------------------------------------------------------- +# Blacklist desired stack +# ----------------------------------------------------------------------------- +video_apply_blacklist_for_stack() { + plat="$1"; stack="$2" + case "$plat" in + lemans|monaco) + if [ "$stack" = "downstream" ]; then + video_ensure_blacklist "qcom-iris"; video_ensure_blacklist "qcom_iris" + video_remove_blacklist "iris-vpu"; video_remove_blacklist "iris_vpu" + else + video_remove_blacklist "qcom-iris"; video_remove_blacklist "qcom_iris" + video_remove_blacklist "iris-vpu"; video_remove_blacklist "iris_vpu" + fi + ;; + kodiak) + if [ "$stack" = "downstream" ]; then + video_ensure_blacklist "venus-core"; video_ensure_blacklist "venus_core" + video_ensure_blacklist "venus-dec"; video_ensure_blacklist "venus_dec" + video_ensure_blacklist "venus-enc"; video_ensure_blacklist "venus_enc" + video_remove_blacklist "iris-vpu"; video_remove_blacklist "iris_vpu" + else + video_remove_blacklist "venus-core"; video_remove_blacklist "venus_core" + video_remove_blacklist "venus-dec"; video_remove_blacklist "venus_dec" + video_remove_blacklist "venus-enc"; video_remove_blacklist "venus_enc" + video_remove_blacklist "iris-vpu"; video_remove_blacklist "iris_vpu" + fi + ;; + *) return 1 ;; + esac + return 0 +} + +# ----------------------------------------------------------------------------- +# Optional: log firmware hint after reload +# ----------------------------------------------------------------------------- +video_log_fw_hint() { + if video_exist_cmd dmesg; then + out="$(dmesg 2>/dev/null | tail -n 200 | grep -Ei 'firmware|iris_vpu|venus' | tail -n 10)" + if [ -n "$out" ]; then + printf '%s\n' "$out" | while IFS= read -r ln; do log_info "dmesg: $ln"; done + fi + fi +} + +# ----------------------------------------------------------------------------- +# Hot switch (best-effort, no reboot) +# ----------------------------------------------------------------------------- +video_hot_switch_modules() { + plat="$1"; stack="$2"; rc=0 + + case "$plat" in + lemans|monaco) + if [ "$stack" = "downstream" ]; then + if video_has_module_loaded "$IRIS_UP_MOD"; then "$MODPROBE" -r "$IRIS_UP_MOD" 2>/dev/null || rc=1; fi + if ! video_has_module_loaded "$IRIS_VPU_MOD"; then "$MODPROBE" "$IRIS_VPU_MOD" 2>/dev/null || rc=1; fi + else + if video_has_module_loaded "$IRIS_VPU_MOD"; then "$MODPROBE" -r "$IRIS_VPU_MOD" 2>/dev/null || true; fi + "$MODPROBE" "$IRIS_UP_MOD" 2>/dev/null || rc=1 + "$MODPROBE" "$IRIS_VPU_MOD" 2>/dev/null || true + fi + ;; + kodiak) + if [ "$stack" = "downstream" ]; then + if video_has_module_loaded "$IRIS_VPU_MOD"; then "$MODPROBE" -r "$IRIS_VPU_MOD" 2>/dev/null || rc=1; fi + "$MODPROBE" -r "$VENUS_ENC_MOD" 2>/dev/null || true + "$MODPROBE" -r "$VENUS_DEC_MOD" 2>/dev/null || true + "$MODPROBE" -r "$VENUS_CORE_MOD" 2>/dev/null || true + if [ -n "$VIDEO_FW_DS" ]; then + mkdir -p "$(dirname "$FW_PATH_KODIAK")" 2>/dev/null || true + mkdir -p "$FW_BACKUP_DIR" 2>/dev/null || true + ts=$(date +%Y%m%d%H%M%S 2>/dev/null || printf '%s' "now") + if [ -f "$FW_PATH_KODIAK" ]; then + cp -f "$FW_PATH_KODIAK" "$FW_BACKUP_DIR/vpu20_p1_gen2.mbn.$ts.bak" 2>/dev/null || true + fi + if [ -f "$VIDEO_FW_DS" ]; then + cp -f "$VIDEO_FW_DS" "$FW_PATH_KODIAK" 2>/dev/null || rc=1 + else + log_warn "Downstream FW not found: $VIDEO_FW_DS" + rc=1 + fi + fi + "$MODPROBE" "$IRIS_VPU_MOD" 2>/dev/null || rc=1 + video_log_fw_hint + else + "$MODPROBE" -r "$IRIS_VPU_MOD" 2>/dev/null || true + "$MODPROBE" "$VENUS_CORE_MOD" 2>/dev/null || rc=1 + "$MODPROBE" "$VENUS_DEC_MOD" 2>/dev/null || true + "$MODPROBE" "$VENUS_ENC_MOD" 2>/dev/null || true + video_log_fw_hint + fi + ;; + *) rc=1 ;; + esac + return $rc +} + +# ----------------------------------------------------------------------------- +# Entry point: ensure desired stack +# ----------------------------------------------------------------------------- +video_ensure_stack() { + want_raw="$1" # upstream|downstream|auto|base|overlay|up|down + plat="$2" + + if [ -z "$plat" ]; then plat=$(video_detect_platform); fi + want="$(video_normalize_stack "$want_raw")" + + if [ "$want" = "auto" ]; then + pref="$(video_auto_preference_from_blacklist "$plat")" + if [ "$pref" != "unknown" ]; then want="$pref"; else + cur="$(video_stack_status "$plat")" + if [ "$cur" != "unknown" ]; then want="$cur"; else + want="upstream" + fi + fi + log_info "AUTO stack selection => $want" + fi + + if [ "$want" = "upstream" ]; then + if video_validate_upstream_loaded "$plat"; then printf '%s\n' "upstream"; return 0; fi + else + if video_validate_downstream_loaded "$plat"; then printf '%s\n' "downstream"; return 0; fi + fi + + video_apply_blacklist_for_stack "$plat" "$want" || return 1 + video_hot_switch_modules "$plat" "$want" || true + + if [ "$want" = "upstream" ]; then + if video_validate_upstream_loaded "$plat"; then printf '%s\n' "upstream"; return 0; fi + else + if video_validate_downstream_loaded "$plat"; then printf '%s\n' "downstream"; return 0; fi + fi + + printf '%s\n' "unknown" + return 1 +} + +# ----------------------------------------------------------------------------- +# DMESG triage +# ----------------------------------------------------------------------------- +video_scan_dmesg_if_enabled() { + dm="$1"; logdir="$2" + if [ "$dm" -ne 1 ] 2>/dev/null; then return 2; fi + MODS='oom|memory|BUG|hung task|soft lockup|hard lockup|rcu|page allocation failure|I/O error' + EXCL='using dummy regulator|not found|EEXIST|probe deferred' + if scan_dmesg_errors "$logdir" "$MODS" "$EXCL"; then return 0; fi + return 1 +} + +# ----------------------------------------------------------------------------- +# JSON helpers (jq-free) +# ----------------------------------------------------------------------------- +video_is_decode_cfg() { + cfg="$1" + b=$(basename "$cfg" | tr '[:upper:]' '[:lower:]') + case "$b" in *dec*.json) return 0 ;; *enc*.json) return 1 ;; esac + dom=$(sed -n 's/.*"Domain"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' "$cfg" 2>/dev/null | head -n 1) + dom_l=$(printf '%s' "$dom" | tr '[:upper:]' '[:lower:]') + case "$dom_l" in decoder|decode) return 0 ;; encoder|encode) return 1 ;; esac + return 0 +} + +video_extract_scalar() { k="$1"; cfg="$2"; sed -n "s/.*\"$k\"[[:space:]]*:[[:space:]]*\"\\([^\"\r\n]*\\)\".*/\\1/p" "$cfg"; } +video_extract_array() { k="$1"; cfg="$2"; sed -n "s/.*\"$k\"[[:space:]]*:[[:space:]]*\\[\\(.*\\)\\].*/\\1/p" "$cfg" | tr ',' '\n' | sed -n 's/.*"\([^"]*\)".*/\1/p'; } + +video_extract_input_clips() { + cfg="$1" + { + video_extract_scalar "InputPath" "$cfg" + video_extract_scalar "Inputpath" "$cfg" + video_extract_scalar "inputPath" "$cfg" + video_extract_scalar "input" "$cfg" + video_extract_scalar "InputFile" "$cfg" + video_extract_scalar "Source" "$cfg" + video_extract_scalar "Clip" "$cfg" + video_extract_array "Inputs" "$cfg" + video_extract_array "Clips" "$cfg" + video_extract_array "Files" "$cfg" + } 2>/dev/null | sed '/^$/d' | sort -u +} + +video_guess_codec_from_cfg() { + cfg="$1" + for k in Codec codec CodecName codecName VideoCodec videoCodec DecoderName EncoderName Name name; do + v=$(video_extract_scalar "$k" "$cfg" | head -n 1) + if [ -n "$v" ]; then printf '%s\n' "$v"; return 0; fi + done + for tok in hevc h265 h264 av1 vp9 vp8 mpeg4 mpeg2 h263 avc; do + if grep -qiE "(^|[^A-Za-z0-9])${tok}([^A-Za-z0-9]|$)" "$cfg" 2>/dev/null; then printf '%s\n' "$tok"; return 0; fi + done + b=$(basename "$cfg" | tr '[:upper:]' '[:lower:]') + for tok in hevc h265 h264 av1 vp9 vp8 mpeg4 mpeg2 h263 avc; do case "$b" in *"$tok"*) printf '%s\n' "$tok"; return 0 ;; esac; done + printf '%s\n' "unknown" + return 0 +} + +video_canon_codec() { + c=$(printf '%s' "$1" | tr '[:upper:]' '[:lower:]') + case "$c" in + h265|hevc) printf '%s\n' "hevc" ;; + h264|avc) printf '%s\n' "h264" ;; + vp9) printf '%s\n' "vp9" ;; + vp8) printf '%s\n' "vp8" ;; + av1) printf '%s\n' "av1" ;; + mpeg4) printf '%s\n' "mpeg4" ;; + mpeg2) printf '%s\n' "mpeg2" ;; + h263) printf '%s\n' "h263" ;; + *) printf '%s\n' "$c" ;; + esac +} + +video_pretty_name_from_cfg() { + cfg="$1"; base=$(basename "$cfg" .json) + nm=$(video_extract_scalar "name" "$cfg"); [ -z "$nm" ] && nm=$(video_extract_scalar "Name" "$cfg") + if video_is_decode_cfg "$cfg"; then cd_op="Decode"; else cd_op="Encode"; fi + codec=$(video_extract_scalar "codec" "$cfg"); [ -z "$codec" ] && codec=$(video_extract_scalar "Codec" "$cfg") + nice="$cd_op:$base" + if [ -n "$nm" ]; then nice="$nm"; elif [ -n "$codec" ]; then nice="$cd_op:$codec ($base)"; fi + safe=$(printf '%s' "$nice" | tr ' ' '_' | tr -cd 'A-Za-z0-9._-') + printf '%s|%s\n' "$nice" "$safe" +} + +# ----------------------------------------------------------------------------- +# Network-aware clip ensure/fetch +# Returns: +# 0 = ok (clips present or fetched) +# 2 = offline/limited network → caller should SKIP decode cases +# 1 = attempted online but fetch/extract failed → caller should FAIL +# ----------------------------------------------------------------------------- +video_ensure_clips_present_or_fetch() { + cfg="$1"; tu="$2" + clips=$(video_extract_input_clips "$cfg") + if [ -z "$clips" ]; then return 0; fi + + tmp_list="${LOG_DIR:-.}/.video_missing.$$" + : > "$tmp_list" + printf '%s\n' "$clips" | while IFS= read -r p; do + [ -z "$p" ] && continue + case "$p" in /*) abs="$p" ;; *) abs=$(cd "$(dirname "$cfg")" 2>/dev/null && pwd)/$p ;; esac + [ -f "$abs" ] || printf '%s\n' "$abs" >> "$tmp_list" + done + + if [ ! -s "$tmp_list" ]; then + rm -f "$tmp_list" 2>/dev/null || true + return 0 + fi + + log_warn "Some input clips are missing (list: $tmp_list)" + [ -z "$tu" ] && tu="$TAR_URL" + + # Network awareness (uses functestlib's ensure_network_online if available) + if command -v ensure_network_online >/dev/null 2>&1; then + if ! ensure_network_online; then + log_warn "Network offline/limited; cannot fetch media bundle" + rm -f "$tmp_list" 2>/dev/null || true + return 2 + fi + fi + + if [ -n "$tu" ]; then + log_info "Attempting fetch via TAR_URL=$tu" + if extract_tar_from_url "$tu"; then + rm -f "$tmp_list" 2>/dev/null || true + return 0 + fi + log_warn "Fetch/extract failed for TAR_URL" + rm -f "$tmp_list" 2>/dev/null || true + return 1 + fi + + log_warn "No TAR_URL provided; cannot fetch media bundle." + rm -f "$tmp_list" 2>/dev/null || true + return 1 +} + +# ----------------------------------------------------------------------------- +# JUnit helper +# ----------------------------------------------------------------------------- +video_junit_append_case() { + of="$1"; class="$2"; name="$3"; t="$4"; st="$5"; logf="$6" + [ -n "$of" ] || return 0 + tailtxt="" + if [ -f "$logf" ]; then + tailtxt=$(tail -n 50 "$logf" 2>/dev/null | sed 's/&/\&/g;s//\>/g') + fi + { + printf ' \n' "$class" "$name" "$t" + case "$st" in + PASS) : ;; + SKIP) printf ' \n' ;; + FAIL) printf ' \n' "failed"; printf '%s\n' "$tailtxt"; printf ' \n' ;; + esac + printf ' \n' + } >> "$of" +} + +# ----------------------------------------------------------------------------- +# Timeout wrapper availability + single run +# ----------------------------------------------------------------------------- +video_have_run_with_timeout() { video_exist_cmd run_with_timeout; } + +video_run_once() { + cfg="$1"; logf="$2"; tmo="$3"; suc="$4"; lvl="$5" + : > "$logf" + + # Header lines for per-case debugging (captured in log) + { + iso_now="$(date -u +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || echo now)" + printf 'BEGIN-RUN %s\n' "$iso_now" + printf 'APP=%s\n' "$VIDEO_APP" + printf 'CFG=%s\n' "$cfg" + printf 'LOGLEVEL=%s TIMEOUT=%s\n' "$lvl" "${tmo:-none}" + printf 'CMD=%s %s %s %s\n' "$VIDEO_APP" "--config" "$cfg" "--loglevel $lvl" + } >>"$logf" 2>&1 + + if video_have_run_with_timeout; then + if run_with_timeout "$tmo" "$VIDEO_APP" --config "$cfg" --loglevel "$lvl" >>"$logf" 2>&1; then :; else + rc=$? + if [ "$rc" -eq 124 ] 2>/dev/null; then log_fail "[run] timeout after ${tmo}s"; else log_fail "[run] $VIDEO_APP exited rc=$rc"; fi + # Footer with explicit run result + printf 'END-RUN rc=%s\n' "$rc" >>"$logf" + grep -Eq "$suc" "$logf" + return $? + fi + else + if "$VIDEO_APP" --config "$cfg" --loglevel "$lvl" >>"$logf" 2>&1; then :; else + rc=$? + log_fail "[run] $VIDEO_APP exited rc=$rc (no timeout enforced)" + printf 'END-RUN rc=%s\n' "$rc" >>"$logf" + grep -Eq "$suc" "$logf" + return $? + fi + fi + + printf 'END-RUN rc=0\n' >>"$logf" + grep -Eq "$suc" "$logf" +} +