|
| 1 | +#!/bin/sh |
| 2 | +# Copyright (c) Qualcomm Technologies, Inc. |
| 3 | +# SPDX-License-Identifier: BSD-3-Clause-Clear |
| 4 | +# libcamera 'cam' runner with strong post-capture validation (CLI-config only) |
| 5 | +# ---------- Repo env + helpers (single-pass) ---------- |
| 6 | +SCRIPT_DIR=$(cd "$(dirname "$0")" || exit 1; pwd) |
| 7 | +SEARCH="$SCRIPT_DIR" |
| 8 | +INIT_ENV="${INIT_ENV:-}" |
| 9 | +LIBCAM_PATH="${LIBCAM_PATH:-}" |
| 10 | + |
| 11 | +# Find init_env quickly (single upward walk) |
| 12 | +while [ "$SEARCH" != "/" ] && [ -z "$INIT_ENV" ]; do |
| 13 | + [ -f "$SEARCH/init_env" ] && INIT_ENV="$SEARCH/init_env" && break |
| 14 | + SEARCH=${SEARCH%/*} |
| 15 | +done |
| 16 | + |
| 17 | +if [ -z "$INIT_ENV" ]; then |
| 18 | + printf '%s\n' "[ERROR] Could not find init_env (starting at $SCRIPT_DIR)" >&2 |
| 19 | + exit 1 |
| 20 | +fi |
| 21 | + |
| 22 | +# shellcheck disable=SC1090 |
| 23 | +. "$INIT_ENV" |
| 24 | +# shellcheck disable=SC1091 |
| 25 | +. "$TOOLS/functestlib.sh" |
| 26 | + |
| 27 | +# Prefer direct repo-root locations for lib_camera.sh |
| 28 | +if [ -z "$LIBCAM_PATH" ]; then |
| 29 | + REPO_ROOT=$(dirname "$INIT_ENV") |
| 30 | + for cand in \ |
| 31 | + "$REPO_ROOT/Runner/utils/camera/lib_camera.sh" \ |
| 32 | + "$REPO_ROOT/utils/camera/lib_camera.sh" |
| 33 | + do |
| 34 | + [ -f "$cand" ] && { LIBCAM_PATH="$cand"; break; } |
| 35 | + done |
| 36 | +fi |
| 37 | + |
| 38 | +# Fallback upward walk only if still not found |
| 39 | +if [ -z "$LIBCAM_PATH" ]; then |
| 40 | + SEARCH="$SCRIPT_DIR" |
| 41 | + while [ "$SEARCH" != "/" ]; do |
| 42 | + for cand in \ |
| 43 | + "$SEARCH/Runner/utils/camera/lib_camera.sh" \ |
| 44 | + "$SEARCH/utils/camera/lib_camera.sh" |
| 45 | + do |
| 46 | + [ -f "$cand" ] && { LIBCAM_PATH="$cand"; break 2; } |
| 47 | + done |
| 48 | + SEARCH=${SEARCH%/*} |
| 49 | + done |
| 50 | +fi |
| 51 | + |
| 52 | +if [ -z "$LIBCAM_PATH" ]; then |
| 53 | + if command -v log_error >/dev/null 2>&1; then |
| 54 | + log_error "lib_camera.sh not found (searched under Runner/utils/camera and utils/camera)" |
| 55 | + else |
| 56 | + printf '%s\n' "ERROR: lib_camera.sh not found (searched under Runner/utils/camera and utils/camera)" >&2 |
| 57 | + fi |
| 58 | + exit 1 |
| 59 | +fi |
| 60 | + |
| 61 | +# shellcheck source=../../../../utils/camera/lib_camera.sh disable=SC1091 |
| 62 | +. "$LIBCAM_PATH" |
| 63 | + |
| 64 | +TESTNAME="Libcamera_cam" |
| 65 | +RES_FILE="./${TESTNAME}.res" |
| 66 | +: > "$RES_FILE" |
| 67 | + |
| 68 | +# ---------- Defaults (override via CLI only) ---------- |
| 69 | +CAM_INDEX="auto" # --index <n>|all|n,m,k ; auto = first from `cam -l` |
| 70 | +CAPTURE_COUNT="10" # --count <n> |
| 71 | +OUT_DIR="./cam_out" # --out <dir> |
| 72 | +SAVE_AS_PPM="no" # --ppm | --bin |
| 73 | +CAM_EXTRA_ARGS="" # --args "<cam args>" |
| 74 | + |
| 75 | +# Validation knobs |
| 76 | +SEQ_STRICT="yes" # --no-strict to relax |
| 77 | +ERR_STRICT="yes" # --no-strict to relax |
| 78 | +DUP_MAX_RATIO="0.5" # --dup-max-ratio <0..1> |
| 79 | +BIN_TOL_PCT="5" # --bin-tol-pct <int %> |
| 80 | +PPM_SAMPLE_BYTES="65536" |
| 81 | +BIN_SAMPLE_BYTES="65536" |
| 82 | + |
| 83 | +print_usage() { |
| 84 | + cat <<EOF |
| 85 | +Usage: $0 [options] |
| 86 | +
|
| 87 | +Options: |
| 88 | + --index N|all|n,m Camera index (default: auto from 'cam -l'; 'all' = run every camera) |
| 89 | + --count N Frames to capture (default: 10) |
| 90 | + --out DIR Output directory (default: ./cam_out) |
| 91 | + --ppm Save as PPM files (frame-#.ppm) |
| 92 | + --bin Save as BIN files (default; frame-#.bin) |
| 93 | + --args "STR" Extra arguments passed to 'cam' (e.g. -s width=1280,height=720,role=viewfinder) |
| 94 | + --strict Enforce strict validation (default) |
| 95 | + --no-strict Relax validation (no seq/err strictness) |
| 96 | + --dup-max-ratio R Fail if max duplicate bucket / total > R (default: 0.5) |
| 97 | + --bin-tol-pct P BIN size tolerance vs bytesused in % (default: 5) |
| 98 | + -h, --help Show this help |
| 99 | +EOF |
| 100 | +} |
| 101 | + |
| 102 | +# ---------- CLI ---------- |
| 103 | +while [ $# -gt 0 ]; do |
| 104 | + case "$1" in |
| 105 | + --index) shift; CAM_INDEX="$1" ;; |
| 106 | + --count) shift; CAPTURE_COUNT="$1" ;; |
| 107 | + --out) shift; OUT_DIR="$1" ;; |
| 108 | + --ppm) SAVE_AS_PPM="yes" ;; |
| 109 | + --bin) SAVE_AS_PPM="no" ;; |
| 110 | + --args) shift; CAM_EXTRA_ARGS="$1" ;; |
| 111 | + --strict) SEQ_STRICT="yes"; ERR_STRICT="yes" ;; |
| 112 | + --no-strict) SEQ_STRICT="no"; ERR_STRICT="no" ;; |
| 113 | + --dup-max-ratio) shift; DUP_MAX_RATIO="$1" ;; |
| 114 | + --bin-tol-pct) shift; BIN_TOL_PCT="$1" ;; |
| 115 | + -h|--help) print_usage; exit 0 ;; |
| 116 | + *) log_warn "Unknown option: $1"; print_usage; exit 2 ;; |
| 117 | + esac |
| 118 | + shift |
| 119 | +done |
| 120 | + |
| 121 | +# ---------- DT / platform readiness ---------- |
| 122 | +# Print both sensor and CAMSS/ISP matches if they exist; skip only if neither is present. |
| 123 | +log_info "Verifying the availability of DT nodes, this process may take some time." |
| 124 | + |
| 125 | +PATTERNS="sony,imx577 imx577 isp cam camss" |
| 126 | +found_any=0 |
| 127 | +missing_list="" |
| 128 | + |
| 129 | +for pat in $PATTERNS; do |
| 130 | + out="$(dt_confirm_node_or_compatible "$pat" 2>/dev/null || true)" |
| 131 | + if [ -n "$out" ]; then |
| 132 | + printf '%s\n' "$out" |
| 133 | + found_any=1 |
| 134 | + else |
| 135 | + [ -n "$missing_list" ] && missing_list="$missing_list, $pat" || missing_list="$pat" |
| 136 | + fi |
| 137 | +done |
| 138 | + |
| 139 | +if [ "$found_any" -eq 1 ]; then |
| 140 | + log_info "DT nodes present (see matches above)." |
| 141 | +else |
| 142 | + log_skip "$TESTNAME SKIP – missing DT patterns: $missing_list" |
| 143 | + echo "$TESTNAME SKIP" >"$RES_FILE" |
| 144 | + exit 0 |
| 145 | +fi |
| 146 | + |
| 147 | +# ---------- Dependencies ---------- |
| 148 | +log_info "Reviewing the dependencies needed to run the cam test." |
| 149 | +check_dependencies cam || { |
| 150 | + log_error "cam utility not found" |
| 151 | + echo "$TESTNAME FAIL" > "$RES_FILE" |
| 152 | + exit 1 |
| 153 | +} |
| 154 | +# nice-to-haves for validation |
| 155 | +check_dependencies awk sed grep sort cut tr wc find stat head tail dd || true |
| 156 | +(check_dependencies sha256sum || check_dependencies md5sum) || true |
| 157 | + |
| 158 | +# ---------- Setup ---------- |
| 159 | +mkdir -p "$OUT_DIR" 2>/dev/null || true |
| 160 | +RUN_TS="$(date -u +%Y%m%d-%H%M%S)" |
| 161 | + |
| 162 | +log_info "Test: $TESTNAME" |
| 163 | +log_info "OUT_DIR=$OUT_DIR | COUNT=$CAPTURE_COUNT | SAVE_AS_PPM=$SAVE_AS_PPM" |
| 164 | +log_info "Extra args: ${CAM_EXTRA_ARGS:-<none>}" |
| 165 | + |
| 166 | +# ---- IPA workaround: disable simple/uncalibrated to avoid buffer allocation failures ---- |
| 167 | +UNCALIB="/usr/share/libcamera/ipa/simple/uncalibrated.yaml" |
| 168 | +if [ -f "$UNCALIB" ]; then |
| 169 | + log_info "Renaming $UNCALIB -> ${UNCALIB}.bk to avoid IPA buffer allocation issues" |
| 170 | + mv "$UNCALIB" "${UNCALIB}.bk" 2>/dev/null || log_warn "Failed to rename $UNCALIB (continuing)" |
| 171 | +elif [ -f "${UNCALIB}.bk" ]; then |
| 172 | + # Already renamed in a previous run; just log for clarity |
| 173 | + log_info "IPA workaround already applied: ${UNCALIB}.bk present" |
| 174 | +fi |
| 175 | + |
| 176 | +# ---------- Sensor presence ---------- |
| 177 | +SENSOR_COUNT="$(libcam_list_sensors_count 2>/dev/null)" |
| 178 | +# harden: ensure numeric |
| 179 | +case "$SENSOR_COUNT" in ''|*[!0-9]*) SENSOR_COUNT=0 ;; esac |
| 180 | +log_info "[cam -l] detected ${SENSOR_COUNT} camera(s)" |
| 181 | + |
| 182 | +if [ "$SENSOR_COUNT" -lt 1 ]; then |
| 183 | + log_skip "No sensors reported by 'cam -l' - marking SKIP" |
| 184 | + echo "$TESTNAME SKIP" > "$RES_FILE" |
| 185 | + exit 0 |
| 186 | +fi |
| 187 | + |
| 188 | + |
| 189 | +# ---------- Resolve indices (supports: auto | all | 0,2,5) ---------- |
| 190 | +INDICES="$(libcam_resolve_indices "$CAM_INDEX")" |
| 191 | +if [ -z "$INDICES" ]; then |
| 192 | + log_skip "No valid camera indices resolved (cam -l empty?) - SKIP" |
| 193 | + echo "$TESTNAME SKIP" > "$RES_FILE" |
| 194 | + exit 0 |
| 195 | +fi |
| 196 | +log_info "Resolved indices: $INDICES" |
| 197 | + |
| 198 | +OVERALL_PASS=1 |
| 199 | +ANY_RC_NONZERO=0 |
| 200 | +PASS_LIST="" |
| 201 | +FAIL_LIST="" |
| 202 | +: > "$OUT_DIR/summary.txt" |
| 203 | + |
| 204 | +for IDX in $INDICES; do |
| 205 | + # Per-camera logs & output dir |
| 206 | + CAM_DIR="${OUT_DIR%/}/cam${IDX}" |
| 207 | + mkdir -p "$CAM_DIR" 2>/dev/null || true |
| 208 | + RUN_LOG="${CAM_DIR%/}/cam-run-${RUN_TS}-cam${IDX}.log" |
| 209 | + INFO_LOG="${CAM_DIR%/}/cam-info-${RUN_TS}-cam${IDX}.log" |
| 210 | + |
| 211 | + log_info "---- Camera idx: $IDX ----" |
| 212 | + { |
| 213 | + echo "== cam -l ==" |
| 214 | + cam -l || true |
| 215 | + echo |
| 216 | + echo "== cam -I (index $IDX) ==" |
| 217 | + cam -c "$IDX" -I || true |
| 218 | + } >"$INFO_LOG" 2>&1 |
| 219 | + |
| 220 | + # Capture |
| 221 | + FILE_TARGET="$CAM_DIR/" |
| 222 | + [ "$SAVE_AS_PPM" = "yes" ] && FILE_TARGET="$CAM_DIR/frame-#.ppm" |
| 223 | + |
| 224 | + log_info "cmd:" |
| 225 | + log_info " cam -c $IDX --capture=$CAPTURE_COUNT \\" |
| 226 | + log_info " --file=\"$FILE_TARGET\" ${CAM_EXTRA_ARGS:+\\}" |
| 227 | + [ -n "$CAM_EXTRA_ARGS" ] && log_info " $CAM_EXTRA_ARGS" |
| 228 | + |
| 229 | + # shellcheck disable=SC2086 |
| 230 | + ( cam -c "$IDX" --capture="$CAPTURE_COUNT" --file="$FILE_TARGET" $CAM_EXTRA_ARGS ) \ |
| 231 | + >"$RUN_LOG" 2>&1 |
| 232 | + RC=$? |
| 233 | + |
| 234 | + tail -n 50 "$RUN_LOG" | sed "s/^/[cam idx $IDX] /" |
| 235 | + |
| 236 | + # Per-camera validation |
| 237 | + BIN_COUNT=$(find "$CAM_DIR" -maxdepth 1 -type f -name 'frame-*.bin' | wc -l | tr -d ' ') |
| 238 | + PPM_COUNT=$(find "$CAM_DIR" -maxdepth 1 -type f -name 'frame-*.ppm' | wc -l | tr -d ' ') |
| 239 | + TOTAL=$((BIN_COUNT + PPM_COUNT)) |
| 240 | + log_info "[idx $IDX] Produced files: bin=$BIN_COUNT ppm=$PPM_COUNT total=$TOTAL (requested $CAPTURE_COUNT)" |
| 241 | + |
| 242 | + PASS=1 |
| 243 | + [ "$TOTAL" -ge "$CAPTURE_COUNT" ] || { log_warn "[idx $IDX] Fewer files than requested"; PASS=0; } |
| 244 | + |
| 245 | + SEQ_REPORT="$(libcam_log_seqs "$RUN_LOG" | wc -l | tr -d ' ')" |
| 246 | + [ "$SEQ_REPORT" -ge "$CAPTURE_COUNT" ] || { log_warn "[idx $IDX] cam log shows fewer seq lines ($SEQ_REPORT) than requested ($CAPTURE_COUNT)"; PASS=0; } |
| 247 | + |
| 248 | + if [ "$SEQ_STRICT" = "yes" ]; then |
| 249 | + CSUM="$(libcam_log_seqs "$RUN_LOG" | libcam_check_contiguous 2>&1)" |
| 250 | + echo "$CSUM" | sed 's/^/[seq] /' |
| 251 | + echo "$CSUM" | grep -q 'MISSING=0' || { log_warn "[idx $IDX] non-contiguous sequences in log"; PASS=0; } |
| 252 | + fi |
| 253 | + |
| 254 | + libcam_files_and_seq "$CAM_DIR" "$SEQ_STRICT" || PASS=0 |
| 255 | + libcam_validate_content "$CAM_DIR" "$RUN_LOG" "$PPM_SAMPLE_BYTES" "$BIN_SAMPLE_BYTES" "$BIN_TOL_PCT" "$DUP_MAX_RATIO" || PASS=0 |
| 256 | + libcam_scan_errors "$RUN_LOG" "$ERR_STRICT" || PASS=0 |
| 257 | + |
| 258 | + [ $RC -eq 0 ] || { ANY_RC_NONZERO=1; PASS=0; } |
| 259 | + |
| 260 | + if [ "$PASS" -eq 1 ]; then |
| 261 | + log_pass "[idx $IDX] PASS" |
| 262 | + PASS_LIST="$PASS_LIST $IDX" |
| 263 | + echo "cam$IDX PASS" >> "$OUT_DIR/summary.txt" |
| 264 | + else |
| 265 | + log_fail "[idx $IDX] FAIL" |
| 266 | + FAIL_LIST="$FAIL_LIST $IDX" |
| 267 | + echo "cam$IDX FAIL" >> "$OUT_DIR/summary.txt" |
| 268 | + OVERALL_PASS=0 |
| 269 | + fi |
| 270 | +done |
| 271 | + |
| 272 | +# ---------- Per-camera summary (always printed) ---------- |
| 273 | +pass_trim="$(printf '%s' "$PASS_LIST" | sed 's/^ //')" |
| 274 | +fail_trim="$(printf '%s' "$FAIL_LIST" | sed 's/^ //')" |
| 275 | +log_info "---------- Per-camera summary ----------" |
| 276 | +if [ -n "$pass_trim" ]; then |
| 277 | + log_info "PASS: $pass_trim" |
| 278 | +else |
| 279 | + log_info "PASS: (none)" |
| 280 | +fi |
| 281 | +if [ -n "$fail_trim" ]; then |
| 282 | + log_info "FAIL: $fail_trim" |
| 283 | +else |
| 284 | + log_info "FAIL: (none)" |
| 285 | +fi |
| 286 | +log_info "Summary file: $OUT_DIR/summary.txt" |
| 287 | + |
| 288 | +# ---------- Final verdict ---------- |
| 289 | +if [ "$OVERALL_PASS" -eq 1 ] && [ $ANY_RC_NONZERO -eq 0 ]; then |
| 290 | + echo "$TESTNAME PASS" > "$RES_FILE" |
| 291 | + log_pass "$TESTNAME PASS" |
| 292 | + exit 0 |
| 293 | +else |
| 294 | + echo "$TESTNAME FAIL" > "$RES_FILE" |
| 295 | + log_fail "$TESTNAME FAIL" |
| 296 | + exit 1 |
| 297 | +fi |
| 298 | + |
| 299 | +# ---------- Artifacts ---------- |
| 300 | +log_info "Artifacts under: $OUT_DIR/" |
| 301 | +for IDX in $INDICES; do |
| 302 | + CAM_DIR="${OUT_DIR%/}/cam${IDX}" |
| 303 | + log_info " - $CAM_DIR/" |
| 304 | +done |
0 commit comments