Skip to content

Commit 41d7681

Browse files
committed
Runner/utils: Add reusable GStreamer AI/ML helper functions
Signed-off-by: Vij Patel <vijpatel@qti.qualcomm.com>
1 parent 96feafd commit 41d7681

1 file changed

Lines changed: 252 additions & 60 deletions

File tree

Runner/utils/lib_gstreamer.sh

Lines changed: 252 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#!/bin/sh
22
# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
3-
# SPDX-License-Identifier: BSD-3-Clause
3+
# SPDX-License-Identifier: BSD-3-Clause#
44
# Runner/utils/lib_gstreamer.sh
55
#
66
# GStreamer helpers.
@@ -20,77 +20,31 @@ GSTLAUNCHFLAGS="${GSTLAUNCHFLAGS:--e -v -m}"
2020
# GST_ALSA_PLAYBACK_DEVICE=hw:0,0
2121
# GST_ALSA_CAPTURE_DEVICE=hw:0,1
2222

23-
# -------------------- Shared artifact directory (generic) --------------------
24-
# gstreamer_shared_artifact_dir <env_var_name> <shared_subdir> <local_subdir> <script_dir> <outdir>
25-
# Generic function to get shared artifact directory for any test type.
23+
# -------------------- Shared encoded-artifact directory --------------------
24+
# gstreamer_shared_encoded_dir <script_dir> <outdir>
25+
# Prints a directory path to use for encoded video artifacts.
2626
# Priority:
27-
# 1. Environment variable if explicitly provided (e.g., VIDEO_SHARED_ENCODE_DIR, AUDIO_SHARED_RECORDED_DIR)
27+
# 1. VIDEO_SHARED_ENCODE_DIR if explicitly provided
2828
# 2. A job-shared path derived from the common LAVA prefix before /tests/
29-
# 3. Fallback to <outdir>/<local_subdir> for local/manual runs
30-
#
31-
# Parameters:
32-
# env_var_name: Name of environment variable to check (e.g., "VIDEO_SHARED_ENCODE_DIR")
33-
# shared_subdir: Subdirectory name for shared path (e.g., "video-encode-decode", "audio-record-playback")
34-
# local_subdir: Subdirectory name for local fallback (e.g., "encoded", "recorded")
35-
# script_dir: Script directory path
36-
# outdir: Output directory path
37-
#
38-
# Example usage:
39-
# gstreamer_shared_artifact_dir "VIDEO_SHARED_ENCODE_DIR" "video-encode-decode" "encoded" "$SCRIPT_DIR" "$OUTDIR"
40-
# gstreamer_shared_artifact_dir "AUDIO_SHARED_RECORDED_DIR" "audio-record-playback" "recorded" "$SCRIPT_DIR" "$OUTDIR"
41-
gstreamer_shared_artifact_dir() {
42-
env_var_name="$1"
43-
shared_subdir="$2"
44-
local_subdir="$3"
45-
script_dir="$4"
46-
outdir="$5"
47-
48-
# Check if environment variable is set (using eval for dynamic variable name)
49-
env_value=$(eval "printf '%s' \"\${${env_var_name}:-}\"")
50-
if [ -n "$env_value" ]; then
51-
printf '%s\n' "$env_value"
29+
# 3. Fallback to <outdir>/encoded for local/manual runs
30+
gstreamer_shared_encoded_dir() {
31+
script_dir="$1"
32+
outdir="$2"
33+
34+
if [ -n "${VIDEO_SHARED_ENCODE_DIR:-}" ]; then
35+
printf '%s\n' "$VIDEO_SHARED_ENCODE_DIR"
5236
return 0
5337
fi
5438

55-
# Check if we're in a LAVA test structure (contains /tests/)
5639
case "$script_dir" in
5740
*/tests/*)
58-
printf '%s/shared/%s\n' "${script_dir%%/tests/*}" "$shared_subdir"
41+
printf '%s/shared/video-encode-decode\n' "${script_dir%%/tests/*}"
5942
;;
6043
*)
61-
printf '%s/%s\n' "$outdir" "$local_subdir"
44+
printf '%s/encoded\n' "$outdir"
6245
;;
6346
esac
6447
}
65-
66-
# -------------------- Shared encoded-artifact directory (video) --------------------
67-
# gstreamer_shared_encoded_dir <script_dir> <outdir>
68-
# Prints a directory path to use for encoded video artifacts.
69-
# This is a wrapper around gstreamer_shared_artifact_dir for backward compatibility.
70-
# Priority:
71-
# 1. VIDEO_SHARED_ENCODE_DIR if explicitly provided
72-
# 2. A job-shared path derived from the common LAVA prefix before /tests/
73-
# 3. Fallback to <outdir>/encoded for local/manual runs
74-
gstreamer_shared_encoded_dir() {
75-
script_dir="$1"
76-
outdir="$2"
77-
78-
gstreamer_shared_artifact_dir "VIDEO_SHARED_ENCODE_DIR" "video-encode-decode" "encoded" "$script_dir" "$outdir"
79-
}
80-
81-
# -------------------- Shared recorded-artifact directory (audio) --------------------
82-
# gstreamer_shared_recorded_dir <script_dir> <outdir>
83-
# Prints a directory path to use for recorded audio artifacts.
84-
# Priority:
85-
# 1. AUDIO_SHARED_RECORDED_DIR if explicitly provided
86-
# 2. A job-shared path derived from the common LAVA prefix before /tests/
87-
# 3. Fallback to <outdir>/recorded for local/manual runs
88-
gstreamer_shared_recorded_dir() {
89-
script_dir="$1"
90-
outdir="$2"
91-
92-
gstreamer_shared_artifact_dir "AUDIO_SHARED_RECORDED_DIR" "audio-record-playback" "recorded" "$script_dir" "$outdir"
93-
}
9448
# -------------------- Element check --------------------
9549
has_element() {
9650
elem="$1"
@@ -1122,3 +1076,241 @@ prepare_vp9_from_local_path() {
11221076

11231077
return 1
11241078
}
1079+
# --------------------------------------------------------------
1080+
# download_resource
1081+
# $1 url – URL to download
1082+
# $2 dest – Either a file name or an existing directory.
1083+
# Prints the full path of the downloaded file on stdout.
1084+
# --------------------------------------------------------------
1085+
download_resource() {
1086+
url=$1
1087+
dest=$2
1088+
1089+
if [ -d "${dest}" ]; then
1090+
filename=$(basename "${url}")
1091+
dest="${dest%/}/${filename}"
1092+
fi
1093+
1094+
# Check if file already exists and is non-empty
1095+
if [ -f "${dest}" ] && [ -s "${dest}" ]; then
1096+
if command -v realpath >/dev/null 2>&1; then
1097+
realpath "${dest}"
1098+
else
1099+
case "${dest}" in
1100+
./*) echo "${dest#./}" ;;
1101+
*) echo "${dest}" ;;
1102+
esac
1103+
fi
1104+
return 0
1105+
fi
1106+
if command -v ensure_network_online >/dev/null 2>&1; then
1107+
if ! ensure_network_online; then
1108+
echo "Network offline/limited; cannot fetch assets"
1109+
return 1
1110+
fi
1111+
fi
1112+
1113+
mkdir -p "$(dirname "${dest}")"
1114+
if command -v curl >/dev/null 2>&1; then
1115+
curl -fkL "${url}" -o "${dest}" || { echo "Error: curl failed to download ${url}" >&2; return 1; }
1116+
elif command -v wget >/dev/null 2>&1; then
1117+
wget -q "${url}" -O "${dest}" || { echo "Error: wget failed to download ${url}" >&2; return 1; }
1118+
else
1119+
echo "Error: neither 'curl' nor 'wget' is installed." >&2
1120+
return 1
1121+
fi
1122+
1123+
# Verify successful download with non-empty file
1124+
if [ ! -s "${dest}" ]; then
1125+
echo "Error: downloaded file is empty: ${dest}" >&2
1126+
return 1
1127+
fi
1128+
1129+
if command -v realpath >/dev/null 2>&1; then
1130+
realpath "${dest}"
1131+
else
1132+
case "${dest}" in
1133+
./*) echo "${dest#./}" ;;
1134+
*) echo "${dest}" ;;
1135+
esac
1136+
fi
1137+
}
1138+
# --------------------------------------------------------------
1139+
# extract_zip_to_dir
1140+
# --------------------------------------------------------------
1141+
extract_zip_to_dir() {
1142+
zip_path=$1
1143+
dest_dir=$2
1144+
1145+
mkdir -p "${dest_dir}"
1146+
if ! unzip -o "${zip_path}" -d "${dest_dir}" >/dev/null; then
1147+
echo "Unzip of ${zip_path} failed" >&2
1148+
return 1
1149+
fi
1150+
}
1151+
# -------------------------------------------------------------------------
1152+
# check_pipeline_elements <pipeline-string>
1153+
# Verify that every GStreamer element that appears in a gst-launch
1154+
# pipeline is installed on the system (via `has_element`).
1155+
# Returns:
1156+
# 0 – all elements are present
1157+
# 1 – at least one element is missing
1158+
# -------------------------------------------------------------------------
1159+
check_pipeline_elements() {
1160+
pipeline="${1:?missing pipeline argument}"
1161+
missing_count=0
1162+
missing_list=""
1163+
total_elements=0
1164+
1165+
log_info "Checking elements in pipeline"
1166+
1167+
# ---------------------------------------------------------
1168+
# Normalise the pipeline string
1169+
# ---------------------------------------------------------
1170+
pipeline=$(printf '%s' "$pipeline" | tr -d '\\\n')
1171+
pipeline=${pipeline#gst-launch-1.0* }
1172+
# Remove the literal "gst-launch-1.0" if present
1173+
pipeline=${pipeline#gst-launch-1.0}
1174+
# Trim any leading whitespace left by the previous step
1175+
pipeline=${pipeline#"${pipeline%%[![:space:]]*}"}
1176+
# Drop leading option tokens (e.g. "-e", "-v", "--no-fault")
1177+
while [ "${pipeline#-}" != "$pipeline" ]; do
1178+
# Remove the first token (option) and any following whitespace
1179+
pipeline=${pipeline#* }
1180+
pipeline=${pipeline#"${pipeline%%[![:space:]]*}"}
1181+
done
1182+
1183+
# ---------------------------------------------------------
1184+
# Write the token list to a temporary file
1185+
# ---------------------------------------------------------
1186+
tmpfile=$(mktemp)
1187+
printf '%s' "$pipeline" | tr '!' '\n' >"$tmpfile"
1188+
1189+
# ---------------------------------------------------------
1190+
# Read the file line‑by‑line – this runs in the *current*
1191+
# shell, so variable updates survive.
1192+
# ---------------------------------------------------------
1193+
while IFS= read -r element_spec; do
1194+
# ---- NEW ----
1195+
# Strip surrounding whitespace; skip blank lines
1196+
# element_spec=$(printf '%s' "$element_spec" | xargs)
1197+
element_spec=$(printf '%s\n' "$element_spec" | awk '{$1=$1; print}')
1198+
[ -z "$element_spec" ] && continue
1199+
# --------------
1200+
1201+
element_name=$(printf '%s' "$element_spec" | cut -d' ' -f1)
1202+
1203+
case "$element_name" in
1204+
*.) log_info "Skipping element reference: $element_name" ; continue ;;
1205+
name=*) log_info "Skipping property assignment: $element_name" ; continue ;;
1206+
*_::*) log_info "Skipping property assignment: $element_name" ; continue ;;
1207+
video/*|audio/*|application/*|text/*|image/*)
1208+
log_info "Skipping caps filter: $element_name" ; continue ;;
1209+
*)
1210+
total_elements=$(( total_elements + 1 ))
1211+
if ! has_element "$element_name"; then
1212+
missing_count=$(( missing_count + 1 ))
1213+
missing_list="${missing_list}${element_name} "
1214+
log_error "Required element missing: $element_name"
1215+
fi
1216+
;;
1217+
esac
1218+
done <"$tmpfile"
1219+
# Clean up the temporary file
1220+
rm -f "$tmpfile"
1221+
1222+
if [ "$missing_count" -eq 0 ]; then
1223+
log_pass "All $total_elements elements in pipeline are available"
1224+
return 0
1225+
else
1226+
log_fail "Missing $missing_count/$total_elements elements: $missing_list"
1227+
return 1
1228+
fi
1229+
}
1230+
# ----------------------------------------------------------------------
1231+
# Run a pipeline with timeout, capture console output and GST debug logs.
1232+
# ----------------------------------------------------------------------
1233+
run_pipeline_with_logs() {
1234+
name=$1
1235+
cmd=$2
1236+
logdir=${3:-logs}
1237+
TIMEOUT=${4:-60} # default 60 seconds
1238+
1239+
console_log="${logdir}/${name}_console.log"
1240+
gst_debug_log="${logdir}/${name}_gst_debug.log"
1241+
1242+
export GST_DEBUG_FILE="${gst_debug_log}"
1243+
1244+
log_info "Running ${name} (timeout=${TIMEOUT}s)"
1245+
gstreamer_run_gstlaunch_timeout "$TIMEOUT" "$cmd" >"$console_log" 2>&1
1246+
rc=$?
1247+
1248+
# Look for a successful PLAYING state and the absence of ERROR messages.
1249+
playing=$(grep -c "Setting pipeline to PLAYING" "$console_log" || true)
1250+
error_present=$(grep -c "ERROR:" "$console_log" || true)
1251+
1252+
if [ "$playing" -gt 0 ] && [ "$error_present" -eq 0 ]; then
1253+
log_pass "${name} PASS"
1254+
return 0
1255+
fi
1256+
1257+
# Special case: timeout (rc = 124) but PLAYING was already reached.
1258+
if [ "$rc" -eq 124 ] && [ "$playing" -gt 0 ]; then
1259+
log_pass "${name} PASS (completed before timeout)"
1260+
return 0
1261+
fi
1262+
1263+
# Anything else is a failure.
1264+
log_fail "${name} FAIL (rc=${rc})"
1265+
log_info "=== ERROR DETAILS ==="
1266+
if [ "$error_present" -gt 0 ]; then
1267+
grep -A10 -B5 "ERROR:" "$console_log" | tail -n 30 |
1268+
while IFS= read -r line; do log_info "$line"; done
1269+
else
1270+
tail -n 30 "$console_log" |
1271+
while IFS= read -r line; do log_info "$line"; done
1272+
fi
1273+
log_info "====================="
1274+
return 1
1275+
}
1276+
# ------------------------------------------------------------------
1277+
# Function: check_file_size
1278+
# Purpose : Check that a file exists and its size > 0.
1279+
# Returns : 0 → file size > 0 (success)
1280+
# 1 → file missing, unreadable, or size == 0 (failure)
1281+
# Requires: GNU coreutils (stat -c %s)
1282+
# ------------------------------------------------------------------
1283+
check_file_size() {
1284+
input_file_path="$1"
1285+
expected_file_size="$2"
1286+
1287+
if [ -z "$input_file_path" ]; then
1288+
log_fail "No input file path provided"
1289+
return 1
1290+
fi
1291+
if [ ! -e "$input_file_path" ]; then
1292+
log_fail "Encoded video file does not exist: $input_file_path"
1293+
return 1
1294+
fi
1295+
1296+
# ---- Ensure we have `stat` ------------------------------------------------
1297+
if ! command -v stat >/dev/null 2>&1; then
1298+
log_fail "stat command not found – cannot determine file size"
1299+
return 1
1300+
fi
1301+
1302+
# ---- Get the actual size -------------------------------------------------
1303+
size_in_bytes=$(stat -c %s "$input_file_path" 2>/dev/null || wc -c <"$input_file_path" 2>/dev/null) || {
1304+
log_fail "Unable to read size of file: $input_file_path"
1305+
return 1
1306+
}
1307+
1308+
# ---- Compare with the expected size --------------------------------------
1309+
if [ "$size_in_bytes" -ge "$expected_file_size" ]; then
1310+
log_pass "File OK (size ${size_in_bytes} bytes ≥ ${expected_file_size} bytes): $input_file_path"
1311+
return 0
1312+
else
1313+
log_info "File too small (size ${size_in_bytes} bytes < ${expected_file_size} bytes): $input_file_path"
1314+
return 1
1315+
fi
1316+
}

0 commit comments

Comments
 (0)