From 56f3538ca4a0183be8618651b5576f72acb249df Mon Sep 17 00:00:00 2001 From: nitinn Date: Thu, 23 Jul 2026 11:15:34 +0530 Subject: [PATCH] video: enhance GStreamer encode/decode test framework - Add video-specific helper functionality in lib_gstreamer.sh - Enhance encode/decode execution workflow - Improve validation and artifact handling - Update README documentation Signed-off-by: nitinn --- .../Video/Video_Encode_Decode/README.md | 375 +++++- .../Video/Video_Encode_Decode/run.sh | 1113 ++++++++++++++++- Runner/utils/lib_gstreamer.sh | 196 +++ 3 files changed, 1611 insertions(+), 73 deletions(-) diff --git a/Runner/suites/Multimedia/GSTreamer/Video/Video_Encode_Decode/README.md b/Runner/suites/Multimedia/GSTreamer/Video/Video_Encode_Decode/README.md index 2b9b5fe8f..8b1242688 100644 --- a/Runner/suites/Multimedia/GSTreamer/Video/Video_Encode_Decode/README.md +++ b/Runner/suites/Multimedia/GSTreamer/Video/Video_Encode_Decode/README.md @@ -75,6 +75,191 @@ By default, the test runs the following test cases at 4K resolution for H.264/H. --- +## Advanced Test Cases + +The test suite includes 9 advanced test cases that can be run using the `--test-type` parameter. These tests validate specific video features and scenarios. + +### Test Type: UVC Camera Preview + +**Test: UVC_Live_Preview_1080p (PR76474)** +- **Description**: UVC camera live preview at 1080p@5fps with Wayland display output +- **Requirements**: UVC camera device, Wayland compositor +- **Pipeline**: `v4l2src → videoconvert → qtivtransform (optional) → waylandsink` +- **Usage**: `./run.sh --test-type uvc --duration 30` +- **Features**: + - Automatic UVC camera detection + - Wayland display setup (auto-starts Weston if needed) + - Optional rotation support via qtivtransform + +### Test Type: Dynamic Resolution Change (DRC) + +**Test: DRC_H264_Decode_1080p_720p (FR82787)** +- **Description**: Validates dynamic resolution change during H.264 decode (1080p → 720p) +- **Requirements**: Special DRC test clip, Wayland display, fpsdisplaysink +- **Pipeline**: `filesrc → qtdemux → h264parse → v4l2h264dec → fpsdisplaysink → waylandsink` +- **Usage**: `./run.sh --test-type drc --clip-path /path/to/clips` +- **Test Clip**: `1080_720_h264.mp4` (automatically downloaded or copied from --clip-path) +- **Features**: + - Monitors FPS and resolution changes during playback + - Validates seamless resolution transitions + +### Test Type: Concurrent Decode + +**Concurrency Tests (PR43865, PR43866, FR98277)** + +These tests validate concurrent decode sessions using qtivcomposer for multi-stream composition. + +**1. H264_Decode_Concurrency_16x480p (PR43865)** +- **Description**: 16 concurrent H.264 480p@30fps decode sessions +- **Requirements**: qtivcomposer, Wayland display +- **Test Clip**: `H264_480p_30fps.mp4` +- **Pipeline (per session)**: `filesrc → qtdemux → h264parse → v4l2h264dec → qtivcomposer → waylandsink` + +**2. H265_Decode_Concurrency_16x480p (PR43866)** +- **Description**: 16 concurrent H.265 480p decode sessions +- **Requirements**: qtivcomposer, Wayland display +- **Test Clip**: `H265_480p_30fps.mp4` +- **Pipeline (per session)**: `filesrc → qtdemux → h265parse → v4l2h265dec → qtivcomposer → waylandsink` + +**3. MJPEG_Decode_Concurrency_16x1080p (FR98277)** +- **Description**: 16 concurrent MJPEG 1080p decode sessions +- **Requirements**: jpegdec (software decoder), qtivcomposer, Wayland display +- **Test Clip**: `mjpeg1.avi` +- **Pipeline (per session)**: `filesrc → avidemux → jpegdec → qtivcomposer → waylandsink` + +**Usage**: +```bash +# Run all 3 concurrency tests with default 16 sessions +./run.sh --test-type concurrency --clip-path /path/to/clips + +# Run with custom session count +./run.sh --test-type concurrency --concurrency-sessions 16 --clip-path /path/to/clips +``` + +**Features**: +- Configurable number of concurrent sessions (default: 16) +- Background process management +- Per-session log files +- Aggregate error checking across all sessions + +### Test Type: Advanced Encoding (Downstream Only) + +**Advanced Encoding Tests (FR74943, FR82773, FR82771, FR72846)** + +These tests require downstream video driver (Config2) and Qualcomm camera source (qtiqmmfsrc). + +**1. HEVC_Encode_Smart_Bitrate_FPS_720p (FR74943)** +- **Description**: HEVC 720p encode with smart FPS and bitrate adaptation using dual camera streams +- **Requirements**: Downstream driver, qtiqmmfsrc, qtismartvencbin +- **Pipeline**: `qtiqmmfsrc (dual streams: video + control) → qtismartvencbin (smart-fps=true, smart-bitrate=true, smart-gop=false) → h265parse → mp4mux → filesink` +- **Features**: + - Adaptive FPS and bitrate control + - Dual camera streams (720p video + 480p control) + - Noise reduction (noise-reduction=2) + - Extra buffers (extra-buffers=20) + - Control sink for smart encoding decisions + +**2. HEVC_Encode_UHD_Cyclic_IR (FR82773)** +- **Description**: HEVC UHD (3840x2160) encode with Cyclic Intra Refresh +- **Requirements**: Downstream driver, qtiqmmfsrc, v4l2h265enc +- **Pipeline**: `qtiqmmfsrc → v4l2h265enc (intra_refresh_period_type=1) → h265parse → mp4mux → filesink` +- **Features**: Cyclic intra refresh mode for error resilience + +**3. H264_Encode_Slice_MB_VGA (FR82771)** +- **Description**: H.264 VGA (640x480) encode with Slice MB mode +- **Requirements**: Downstream driver, qtiqmmfsrc, v4l2h264enc +- **Pipeline**: `qtiqmmfsrc → v4l2h264enc (slice_partitioning_method=1, number_of_mbs_in_a_slice=368) → h264parse → mp4mux → filesink` +- **Features**: Macroblock-based slice partitioning + +**4. HEVC_Encode_4K_Rotate90 (FR72846)** +- **Description**: HEVC 4K (3840x2160) encode with 90° VPU rotation +- **Requirements**: qtiqmmfsrc, v4l2h265enc (rotate control requires downstream driver) +- **Pipeline**: `qtiqmmfsrc → v4l2h265enc (rotate=90) → h265parse → mp4mux → filesink` +- **Features**: Hardware-accelerated rotation during encoding + +**Usage**: +```bash +# Run all 4 advanced encoding tests (requires downstream driver + camera) +./run.sh --test-type advanced-encode --stack downstream --duration 30 +``` + +**Note**: These tests will automatically skip if: +- Downstream video driver is not detected +- Camera source (qtiqmmfsrc) is not available +- Required encoder elements are missing + +--- + +## Test Clip Requirements + +### Automatic Download + +Test clips are automatically downloaded from the configured URL (default: GitHub releases) or copied from a local path if provided via `--clip-path`. + +### Required Clips by Test Type + +| Test Type | Clips Required | Auto-Download | +|-----------|----------------|---------------| +| basic | VP9_640x480_10s.webm | ✅ Yes | +| uvc | None (uses camera) | N/A | +| drc | 1080_720_h264.mp4 | ✅ Yes | +| concurrency | H264_480p_30fps.mp4
H265_480p_30fps.mp4
mjpeg1.avi | ✅ Yes | +| advanced-encode | None (uses camera) | N/A | +| all (default) | All clips above | ✅ Yes | + +### Clip Download Behavior + +1. **Check if clip exists** in output directory +2. **Try local path** if `--clip-path` is provided +3. **Download from URL** if not found locally +4. **Skip test** if download fails (offline mode) + +### Manual Clip Provision + +If you have clips locally, provide the path: + +```bash +./run.sh --test-type concurrency --clip-path /path/to/clips +``` + +The script will look for clips in the specified directory and copy them to the output directory. + +--- + +## Advanced Test Examples + +### 1) Run UVC camera preview for 60 seconds + +```bash +./run.sh --test-type uvc --duration 60 +``` + +### 2) Run DRC test with local clips + +```bash +./run.sh --test-type drc --clip-path /mnt/test_clips +``` + +### 3) Run concurrency tests with 8 sessions + +```bash +./run.sh --test-type concurrency --concurrency-sessions 8 +``` + +### 4) Run all advanced encoding tests (downstream only) + +```bash +./run.sh --test-type advanced-encode --stack downstream +``` + +### 5) Run basic tests with custom settings + +```bash +./run.sh --test-type basic --codecs h264,h265 --resolutions 480p,1080p --duration 10 +``` + +--- + ## PASS / FAIL / SKIP criteria ### PASS @@ -167,6 +352,14 @@ Help: ### Options +- `--test-type ` + - Default: `all` (run ALL tests - basic + uvc + drc + concurrency + advanced-encode) + - `basic`: Standard encode/decode tests only (H.264, H.265, VP9) + - `uvc`: UVC camera live preview test + - `drc`: Dynamic Resolution Change H.264 decode test + - `concurrency`: Concurrent decode tests (H.264, H.265, MJPEG) + - `advanced-encode`: Advanced encoding tests (downstream only) + - `--mode ` - Default: `all` (run both encode and decode tests) - `encode`: Run only encoding tests @@ -178,12 +371,27 @@ Help: - Examples: `h264`, `h265`, `h264,h265`, `vp9`, `h264,vp9` - Note: VP9 only supports decode (no encode) -- `--resolutions <480p,4k>` +- `--resolutions <480p,720p,1080p,4k>` - Comma-separated list of resolutions to test - - Default: `480p,4k` + - Default: `480p` - Supported: `480p` (640x480), `720p` (1280x720), `1080p` (1920x1080), `4k` (3840x2160) - Examples: `480p`, `4k`, `480p,1080p,4k` +- `--concurrency-sessions ` + - Number of concurrent sessions for concurrency tests + - Default: `16` + - Example: `--concurrency-sessions 8` + +- `--clip-path ` + - Local path to test video files + - Overrides --clip-url if files exist + - Example: `--clip-path /opt` or `--clip-path /mnt/test_clips` + +- `--clip-url ` + - URL to download test video files + - Default: GitHub release URL + - Example: `--clip-url https://example.com/clips.tar.gz` + - `--duration ` - Duration for encoding (in seconds) - Default: `30` @@ -222,84 +430,109 @@ Help: ## Examples -### 1) Run all tests (default - encode + decode for H.264/H.265/VP9 at 4K for 30 seconds) +### 1) Run all tests (default - ALL test types including basic, uvc, drc, concurrency, advanced-encode) ```bash ./run.sh ``` -**Note:** Default behavior runs H.264, H.265, and VP9 tests at 4K resolution with 30 second duration. +**Note:** Default behavior (`--test-type all`) runs ALL 9+ tests: +- Basic encode/decode tests (H.264, H.265, VP9) at 480p for 30 seconds +- UVC camera preview (if camera available) +- DRC H.264 decode (if clip available) +- Concurrency tests: 16x H.264, 16x H.265, 16x MJPEG (if clips available) +- Advanced encode tests (if downstream stack + camera available) + +Tests that require missing hardware or clips will be gracefully skipped. -### 2) Run only encoding tests +### 2) Run only basic encode/decode tests ```bash -./run.sh --mode encode +./run.sh --test-type basic ``` -### 3) Run only decoding tests (requires encoded files from previous run) +### 3) Run only UVC camera test ```bash -./run.sh --mode decode +./run.sh --test-type uvc --duration 60 +``` + +### 4) Run only concurrency tests + +```bash +./run.sh --test-type concurrency --clip-path /opt +``` + +### 5) Run only advanced encoding tests (downstream only) + +```bash +./run.sh --test-type advanced-encode --stack downstream ``` -### 4) Test only H.264 codec +### 6) Run basic tests with specific codecs and resolutions ```bash -./run.sh --codecs h264 +./run.sh --test-type basic --codecs h264,h265 --resolutions 480p,1080p ``` -### 5) Test only H.265 codec at 4K resolution +### 7) Run only encoding tests ```bash -./run.sh --codecs h265 --resolutions 4k +./run.sh --mode encode ``` -### 6) Test all codecs at 480p only +### 8) Run only decoding tests (requires encoded files from previous run) ```bash -./run.sh --resolutions 480p +./run.sh --mode decode ``` -### 7) Test with longer duration (10 seconds) +### 9) Test only H.264 codec + +```bash +./run.sh --test-type basic --codecs h264 +``` + +### 10) Test with shorter duration (10 seconds) ```bash ./run.sh --duration 10 ``` -### 8) Test with higher framerate (60fps) +### 11) Test with higher framerate (60fps) ```bash -./run.sh --framerate 60 +./run.sh --test-type basic --framerate 60 ``` -### 9) Test multiple resolutions +### 12) Test multiple resolutions ```bash -./run.sh --resolutions 480p,720p,1080p,4k +./run.sh --test-type basic --resolutions 480p,720p,1080p,4k ``` -### 10) Increase GStreamer debug verbosity +### 13) Increase GStreamer debug verbosity ```bash ./run.sh --gst-debug 5 ``` -### 11) Quick test - H.264 only at 480p with 3 second duration +### 14) Quick test - H.264 only at 480p with 3 second duration ```bash -./run.sh --codecs h264 --resolutions 480p --duration 3 +./run.sh --test-type basic --codecs h264 --resolutions 480p --duration 3 ``` -### 12) Test VP9 decode only (requires network connectivity) +### 15) Test VP9 decode only (requires network connectivity) ```bash -./run.sh --codecs vp9 --mode decode +./run.sh --test-type basic --codecs vp9 --mode decode ``` -### 13) Test all codecs including VP9 +### 16) Provide local test clips ```bash -./run.sh --codecs h264,h265,vp9 +./run.sh --clip-path /opt ``` --- @@ -528,6 +761,89 @@ This test uses reusable helper functions from `lib_gstreamer.sh` that other GStr gstreamer_run_gstlaunch_timeout 40 "$pipeline" ``` +#### 5. `gstreamer_build_uvc_preview_pipeline` + +Build pipeline for UVC camera live preview (used by UVC test). + +- **Parameters**: + - `device`: UVC device path (e.g., `/dev/video2`) + - `width`: Video width (default: 1920) + - `height`: Video height (default: 1080) + - `framerate`: Framerate (default: 5) +- Returns: Complete pipeline string for UVC preview +- Automatically includes `qtivtransform` if available +- Example: + ```sh + uvc_dev=$(gstreamer_detect_uvc_camera) + pipeline=$(gstreamer_build_uvc_preview_pipeline "$uvc_dev" "1920" "1080" "5") + gstreamer_run_gstlaunch_timeout 30 "$pipeline" + ``` + +#### 6. `gstreamer_build_drc_decode_pipeline` + +Build pipeline for Dynamic Resolution Change H.264 decode test. + +- **Parameters**: + - `clip_path`: Path to DRC test clip + - `video_stack`: `upstream` or `downstream` +- Returns: Complete pipeline string with fpsdisplaysink wrapper +- Example: + ```sh + pipeline=$(gstreamer_build_drc_decode_pipeline "/tmp/drc_clip.mp4" "downstream") + gstreamer_run_gstlaunch_timeout 60 "$pipeline" + ``` + +#### 7. `gstreamer_build_concurrency_decode_pipeline` + +Build pipeline for concurrent decode tests with qtivcomposer (4x4 grid). + +- **Parameters**: + - `codec`: `h264`, `h265`, or `mjpeg` + - `clip_path`: Path to test clip + - `sessions`: Number of concurrent sessions (default: 16) +- Returns: Complete pipeline string with 4x4 grid layout +- Automatically handles codec-specific decode chains +- Example: + ```sh + pipeline=$(gstreamer_build_concurrency_decode_pipeline \ + "h264" "/tmp/test.mp4" "16") + gstreamer_run_gstlaunch_timeout 40 "$pipeline" + ``` + +#### 8. `gstreamer_build_smart_encode_pipeline` + +Build pipeline for HEVC Smart Encode with dual camera streams. + +- **Parameters**: + - `output_file`: Output file path +- Returns: Complete pipeline string with qtismartvencbin +- Requires: qtiqmmfsrc, qtismartvencbin (downstream only) +- Example: + ```sh + pipeline=$(gstreamer_build_smart_encode_pipeline "/tmp/output.mp4") + gstreamer_run_gstlaunch_timeout 40 "$pipeline" + ``` + +#### 9. `gstreamer_build_camera_encode_pipeline` + +Build pipeline for camera-based encoding with custom controls. + +- **Parameters**: + - `codec`: `h264` or `h265` + - `width`: Video width + - `height`: Video height + - `output_file`: Output file path + - `extra_controls`: Custom encoder controls string (optional) + - `video_stack`: `upstream` or `downstream` +- Returns: Complete pipeline string with qtiqmmfsrc +- Example: + ```sh + controls="controls,video_bitrate=8000000,intra_refresh_period_type=1" + pipeline=$(gstreamer_build_camera_encode_pipeline \ + "h265" "3840" "2160" "/tmp/output.mp4" "$controls" "downstream") + gstreamer_run_gstlaunch_timeout 40 "$pipeline" + ``` + ### Usage in Other Tests To use these functions in your GStreamer test: @@ -582,16 +898,19 @@ This will output example pipelines for various codecs, resolutions, and video st The test supports these environment variables (can be set in LAVA job definition): +- `VIDEO_TEST_TYPE` - Test type (all/basic/uvc/drc/concurrency/advanced-encode) (default: all) - `VIDEO_TEST_MODE` - Test mode (all/encode/decode) (default: all) - `VIDEO_CODECS` - Comma-separated codec list (default: `h264,h265,vp9`) -- `VIDEO_RESOLUTIONS` - Comma-separated resolution list (default: `4k`) +- `VIDEO_RESOLUTIONS` - Comma-separated resolution list (default: `480p`) - `VIDEO_DURATION` - Encoding duration in seconds (default: 30) - `RUNTIMESEC` - Alternative to VIDEO_DURATION - `VIDEO_FRAMERATE` - Video framerate (default: 30) - `VIDEO_STACK` - Video stack selection (auto/upstream/downstream) (default: auto) - `VIDEO_GST_DEBUG` - GStreamer debug level (default: 2) - `GST_DEBUG_LEVEL` - Alternative to VIDEO_GST_DEBUG -- `VIDEO_CLIP_URL` - URL for VP9 clip download (default: GitHub releases) +- `VIDEO_CLIP_URL` - URL for test clip download (default: GitHub releases) +- `VIDEO_CLIP_PATH` - Local path to test clips (overrides VIDEO_CLIP_URL) +- `VIDEO_CONCURRENCY_SESSIONS` - Number of concurrent sessions (default: 16) - `LAVA_TESTCASE_ID` - Override test case name for LAVA reporting (default: Video_Encode_Decode) **Priority order for duration**: `VIDEO_DURATION` > `RUNTIMESEC` > default (30) diff --git a/Runner/suites/Multimedia/GSTreamer/Video/Video_Encode_Decode/run.sh b/Runner/suites/Multimedia/GSTreamer/Video/Video_Encode_Decode/run.sh index a0e556ee8..dd77b3f66 100755 --- a/Runner/suites/Multimedia/GSTreamer/Video/Video_Encode_Decode/run.sh +++ b/Runner/suites/Multimedia/GSTreamer/Video/Video_Encode_Decode/run.sh @@ -51,6 +51,9 @@ fi # shellcheck disable=SC1091 [ -f "$TOOLS/lib_video.sh" ] && . "$TOOLS/lib_video.sh" +# shellcheck disable=SC1091 +[ -f "$TOOLS/lib_display.sh" ] && . "$TOOLS/lib_display.sh" + # Use the shared encoded directory if supported; otherwise default to $OUTDIR/encoded. if command -v gstreamer_shared_encoded_dir >/dev/null 2>&1; then ENCODED_DIR="$(gstreamer_shared_encoded_dir "$SCRIPT_DIR" "$OUTDIR")" @@ -79,6 +82,7 @@ total_tests=0 # -------------------- Defaults (LAVA env vars -> defaults; CLI overrides) -------------------- testMode="${VIDEO_TEST_MODE:-all}" +testType="${VIDEO_TEST_TYPE:-all}" codecList="${VIDEO_CODECS:-h264,h265,vp9}" resolutionList="${VIDEO_RESOLUTIONS:-480p}" duration="${VIDEO_DURATION:-${RUNTIMESEC:-30}}" @@ -87,6 +91,7 @@ gstDebugLevel="${VIDEO_GST_DEBUG:-${GST_DEBUG_LEVEL:-2}}" videoStack="${VIDEO_STACK:-auto}" clipUrl="${VIDEO_CLIP_URL:-https://github.com/qualcomm-linux/qcom-linux-testkit/releases/download/GST-Video-Files-v1.0/video_clips_gst.tar.gz}" clipPath="${VIDEO_CLIP_PATH:-}" +concurrencySessions="${VIDEO_CONCURRENCY_SESSIONS:-16}" # Validate environment variables if set # Validate numeric parameters (POSIX-safe; no indirect expansion) @@ -141,6 +146,37 @@ while [ $# -gt 0 ]; do shift 2 ;; + --test-type) + if [ $# -lt 2 ] || [ "${2#--}" != "$2" ]; then + log_warn "Missing/invalid value for --test-type" + echo "$RESULT_TESTNAME SKIP" >"$RES_FILE" + exit 0 + fi + [ -n "$2" ] && testType="$2" + shift 2 + ;; + + --concurrency-sessions) + if [ $# -lt 2 ] || [ "${2#--}" != "$2" ]; then + log_warn "Missing/invalid value for --concurrency-sessions" + echo "$RESULT_TESTNAME SKIP" >"$RES_FILE" + exit 0 + fi + if [ -n "$2" ]; then + case "$2" in + ''|*[!0-9]*) + log_warn "Invalid --concurrency-sessions '$2' (must be numeric)" + echo "$RESULT_TESTNAME SKIP" >"$RES_FILE" + exit 0 + ;; + *) + concurrencySessions="$2" + ;; + esac + fi + shift 2 + ;; + --codecs) if [ $# -lt 2 ] || [ "${2#--}" != "$2" ]; then log_warn "Missing/invalid value for --codecs" @@ -275,6 +311,26 @@ OPTIONS: - encode: Run only encoding tests - decode: Run only decoding tests + --test-type + Test type (default: all) + - all: Run ALL tests (basic + uvc + drc + concurrency + advanced-encode) + - basic: Standard encode/decode tests only (H.264, H.265, VP9) + - uvc: UVC camera live preview at 1080p@5fps (PR76474) + - drc: Dynamic Resolution Change H.264 decode (FR82787) + - concurrency: Concurrent decode tests (PR43865, PR43866, FR98277) + * 16x H.264 480p decode sessions + * 16x H.265 1080p decode sessions + * 16x MJPEG 1080p decode sessions + - advanced-encode: Advanced encoding tests (downstream only) + * HEVC Smart encode 720p (FR74943) + * HEVC UHD Cyclic IR (FR82773) + * H.264 VGA Slice MB (FR82771) + * HEVC 4K Rotate 90° (FR72846) + + --concurrency-sessions + Number of concurrent sessions for concurrency tests + (default: 16) + --codecs Comma-separated list of codecs to test (default: h264,h265,vp9) @@ -303,6 +359,7 @@ OPTIONS: --clip-path Local path to test video files (overrides --clip-url if files exist) + Example: --clip-path /opt --lava-testcase-id Override the test case name reported to LAVA @@ -311,6 +368,35 @@ OPTIONS: -h, --help Display this help message +FILE LOCATIONS: + Test clips and logs are stored in: + $SCRIPT_DIR/logs/Video_Encode_Decode/ + + On device, this typically resolves to: + /var/Runner/suites/Multimedia/GSTreamer/Video/Video_Encode_Decode/logs/Video_Encode_Decode/ + + Downloaded/copied clips: + - VP9_640x480_10s.webm + - H264_480p_30fps.mp4 + - H265_480p_30fps.mp4 + - mjpeg1.avi + - 1080_720_h264.mp4 + + Encoded files: + logs/Video_Encode_Decode/encoded/ + - encode_h264_480p.mp4 + - encode_h265_480p.mp4 + - (etc.) + + Log files: + - gst.log (GStreamer debug output) + - encode_*.log (individual test logs) + - decode_*.log + - UVC_*.log + - DRC_*.log + - H264_Decode_Concurrency_*.log + - (etc.) + ENVIRONMENT VARIABLES: VIDEO_TEST_MODE Same as --mode VIDEO_CODECS Same as --codecs @@ -361,6 +447,13 @@ case "$testMode" in all|encode|decode) : ;; *) ;; esac +case "$testType" in basic|uvc|drc|concurrency|advanced-encode|all) : ;; *) + log_warn "Invalid --test-type '$testType' (allowed: basic, uvc, drc, concurrency, advanced-encode, all)" + echo "$RESULT_TESTNAME SKIP" >"$RES_FILE" + exit 0 + ;; +esac + case "$gstDebugLevel" in 1|2|3|4|5|6|7|8|9) : ;; *) log_warn "Invalid --gst-debug '$gstDebugLevel' (allowed: 1-9)" echo "$RESULT_TESTNAME SKIP" >"$RES_FILE" @@ -597,63 +690,993 @@ run_decode_test() { fi } -# -------------------- Main test execution -------------------- -log_info "Starting video encode/decode tests..." - -# Parse codec list -codecs=$(printf '%s' "$codecList" | tr ',' ' ') - -# Parse resolution list -resolutions=$(printf '%s' "$resolutionList" | tr ',' ' ') +# -------------------- Helper: Check display connection -------------------- +check_display_connected() { + # Check if a physical display is connected + # Returns 0 if display is confirmed connected, 1 otherwise + if ! command -v display_connected_summary >/dev/null 2>&1; then + log_warn "display_connected_summary not available, cannot verify display connection" + return 1 + fi + + display_status=$(display_connected_summary) + if [ "$display_status" = "none" ] || [ -z "$display_status" ]; then + log_warn "No display connected (status: ${display_status:-empty})" + return 1 + fi + + log_info "Display connected: $display_status" + return 0 +} -# -------------------- VP9 clip prep -------------------- -need_vp9_clip=0 -for codec in $codecs; do - if [ "$codec" = "vp9" ]; then - need_vp9_clip=1 - break +# -------------------- UVC Live Preview Test -------------------- +# Test: UVC_Live_Preview_1080p (PR76474) +run_uvc_preview_test() { + testname="UVC_Live_Preview_1080p" + log_info "==========================================" + log_info "Running: $testname (PR76474)" + log_info "==========================================" + + total_tests=$((total_tests + 1)) + + # Check for required elements first (fast checks) + if ! has_element v4l2src; then + log_skip "$testname: v4l2src not available" + skip_count=$((skip_count + 1)) + return 1 fi -done + + if ! has_element waylandsink; then + log_skip "$testname: waylandsink not available" + skip_count=$((skip_count + 1)) + return 1 + fi + + # Detect UVC camera + uvc_dev=$(gstreamer_detect_uvc_camera) + if [ -z "$uvc_dev" ]; then + log_skip "$testname: No UVC camera detected" + skip_count=$((skip_count + 1)) + return 1 + fi + + log_info "UVC camera detected: $uvc_dev" + + # Check if display is connected (required for waylandsink) + if ! check_display_connected; then + log_skip "$testname: No display connected (required for waylandsink)" + skip_count=$((skip_count + 1)) + return 1 + fi + + # Set up Wayland display using shared function + if ! camera_setup_wayland_environment "uvc-preview"; then + log_skip "$testname: Failed to set up Wayland environment" + skip_count=$((skip_count + 1)) + return 1 + fi + + test_log="$OUTDIR/${testname}.log" + : >"$test_log" + + # Build pipeline using library function + pipeline=$(gstreamer_build_uvc_preview_pipeline "$uvc_dev" "1920" "1080" "5") + + if [ -z "$pipeline" ]; then + log_fail "$testname: FAIL (could not build pipeline)" + fail_count=$((fail_count + 1)) + return 1 + fi + + log_info "Pipeline: $pipeline" + + # Run for specified duration (default 30s) + if gstreamer_run_gstlaunch_timeout "$duration" "$pipeline" >>"$test_log" 2>&1; then + gstRc=0 + else + gstRc=$? + fi + + log_info "Exit code: $gstRc" + + # Validate log + if ! gstreamer_validate_log "$test_log" "$testname"; then + log_fail "$testname: FAIL (GStreamer errors detected)" + fail_count=$((fail_count + 1)) + return 1 + fi + + if [ "$gstRc" -eq 0 ] || [ "$gstRc" -eq 124 ]; then + log_pass "$testname: PASS" + pass_count=$((pass_count + 1)) + return 0 + else + log_fail "$testname: FAIL (rc=$gstRc)" + fail_count=$((fail_count + 1)) + return 1 + fi +} -if [ "$need_vp9_clip" -eq 1 ] && [ "$testMode" != "encode" ]; then +# -------------------- DRC H.264 Decode Test -------------------- +# Test: DRC_H264_Decode_1080p_UHD_1080p (FR82787) +run_drc_decode_test() { + testname="DRC_H264_Decode_1080p_UHD_1080p" log_info "==========================================" - log_info "VP9 CLIP PREP" + log_info "Running: $testname (FR82787)" log_info "==========================================" - vp9_clip_webm="$OUTDIR/VP9_640x480_10s.webm" + total_tests=$((total_tests + 1)) + + # Check for required elements first (fast checks) + if ! has_element v4l2h264dec; then + log_skip "$testname: v4l2h264dec not available" + skip_count=$((skip_count + 1)) + return 1 + fi + + if ! has_element fpsdisplaysink; then + log_skip "$testname: fpsdisplaysink not available" + skip_count=$((skip_count + 1)) + return 1 + fi + + if ! has_element waylandsink; then + log_skip "$testname: waylandsink not available" + skip_count=$((skip_count + 1)) + return 1 + fi + + # Check for DRC test clip + drc_clip="$OUTDIR/1080_720_h264.mp4" + if [ ! -f "$drc_clip" ]; then + if [ -n "$clipPath" ] && [ -f "$clipPath/1080_720_h264.mp4" ]; then + cp "$clipPath/1080_720_h264.mp4" "$drc_clip" + else + log_skip "$testname: DRC test clip not found (1080_720_h264.mp4)" + skip_count=$((skip_count + 1)) + return 1 + fi + fi + + # Check if display is connected (required for waylandsink) + if ! check_display_connected; then + log_skip "$testname: No display connected (required for waylandsink)" + skip_count=$((skip_count + 1)) + return 1 + fi + + # Set up Wayland display using shared function + if ! camera_setup_wayland_environment "drc-test"; then + log_skip "$testname: Failed to set up Wayland environment" + skip_count=$((skip_count + 1)) + return 1 + fi + + test_log="$OUTDIR/${testname}.log" + : >"$test_log" + + # Build pipeline using library function + pipeline=$(gstreamer_build_drc_decode_pipeline "$drc_clip" "$detected_stack") + + if [ -z "$pipeline" ]; then + log_fail "$testname: FAIL (could not build pipeline)" + fail_count=$((fail_count + 1)) + return 1 + fi + + log_info "Pipeline: $pipeline" - # Check if WebM file already exists - if [ -f "$vp9_clip_webm" ]; then - log_info "VP9 WebM clip already exists: $vp9_clip_webm" + # Run for duration or until clip ends + if gstreamer_run_gstlaunch_timeout "$((duration + 10))" "$pipeline" >>"$test_log" 2>&1; then + gstRc=0 else - # Try to get WebM file from provided path or URL - if [ -n "$clipPath" ]; then - log_info "Attempting to get VP9 WebM clip from local path: $clipPath" - if [ -f "$clipPath/VP9_640x480_10s.webm" ]; then - cp "$clipPath/VP9_640x480_10s.webm" "$vp9_clip_webm" - log_info "VP9 WebM clip copied from local path" - else - log_warn "VP9 WebM clip not found in local path: $clipPath" - fi + gstRc=$? + fi + + log_info "Exit code: $gstRc" + + # Validate log + if ! gstreamer_validate_log "$test_log" "$testname"; then + log_fail "$testname: FAIL (GStreamer errors detected)" + fail_count=$((fail_count + 1)) + return 1 + fi + + if [ "$gstRc" -eq 0 ] || [ "$gstRc" -eq 124 ]; then + log_pass "$testname: PASS" + pass_count=$((pass_count + 1)) + return 0 + else + log_fail "$testname: FAIL (rc=$gstRc)" + fail_count=$((fail_count + 1)) + return 1 + fi +} + +# -------------------- H.264 Concurrency Decode Test -------------------- +# Test: H264_Decode_Concurrency_16x480p (PR43865) +run_h264_concurrency_test() { + testname="H264_Decode_Concurrency_16x480p" + log_info "==========================================" + log_info "Running: $testname (PR43865)" + log_info "==========================================" + + total_tests=$((total_tests + 1)) + + # Check for required elements first (fast checks) + if ! has_element v4l2h264dec; then + log_skip "$testname: v4l2h264dec not available" + skip_count=$((skip_count + 1)) + return 1 + fi + + if ! has_element qtivcomposer; then + log_skip "$testname: qtivcomposer not available" + skip_count=$((skip_count + 1)) + return 1 + fi + + if ! has_element waylandsink; then + log_skip "$testname: waylandsink not available" + skip_count=$((skip_count + 1)) + return 1 + fi + + # Check for test clip + h264_clip="$OUTDIR/H264_480p_30fps.mp4" + if [ ! -f "$h264_clip" ]; then + if [ -n "$clipPath" ] && [ -f "$clipPath/H264_480p_30fps.mp4" ]; then + cp "$clipPath/H264_480p_30fps.mp4" "$h264_clip" + else + log_skip "$testname: Test clip not found (H264_480p_30fps.mp4)" + skip_count=$((skip_count + 1)) + return 1 fi + fi + + # Set up Wayland display using shared function + if ! camera_setup_wayland_environment "h264-concurrency"; then + log_skip "$testname: Failed to set up Wayland environment" + skip_count=$((skip_count + 1)) + return 1 + fi + + test_log="$OUTDIR/${testname}.log" + : >"$test_log" + + log_info "Building single pipeline with $concurrencySessions concurrent H.264 decode sessions..." + + # Increase file descriptor limit + ulimit -n 4096 2>/dev/null || true + + # Build pipeline using library function + pipeline=$(gstreamer_build_concurrency_decode_pipeline "h264" "$h264_clip" "$concurrencySessions") + + if [ -z "$pipeline" ]; then + log_fail "$testname: FAIL (could not build pipeline)" + fail_count=$((fail_count + 1)) + return 1 + fi + + log_info "Pipeline: gst-launch-1.0 -e $pipeline" + + # Run single pipeline with all 16 sessions + if gstreamer_run_gstlaunch_timeout "$((duration + 10))" "$pipeline" >>"$test_log" 2>&1; then + gstRc=0 + else + gstRc=$? + fi + + log_info "Exit code: $gstRc" + + # Validate log + if ! gstreamer_validate_log "$test_log" "$testname"; then + log_fail "$testname: FAIL (GStreamer errors detected)" + fail_count=$((fail_count + 1)) + return 1 + fi + + if [ "$gstRc" -eq 0 ] || [ "$gstRc" -eq 124 ]; then + log_pass "$testname: PASS (all $concurrencySessions sessions in single pipeline)" + pass_count=$((pass_count + 1)) + return 0 + else + log_fail "$testname: FAIL (rc=$gstRc)" + fail_count=$((fail_count + 1)) + return 1 + fi +} - # If not found locally, try URL download - if [ ! -f "$vp9_clip_webm" ]; then - log_info "VP9 WebM clip not found locally; attempting download from URL..." - if extract_tar_from_url "$clipUrl" "$OUTDIR"; then - # Move the extracted file from current directory to OUTDIR - if [ -f "VP9_640x480_10s.webm" ]; then - mv "VP9_640x480_10s.webm" "$vp9_clip_webm" - log_pass "VP9 WebM clip downloaded and moved successfully" - else - log_warn "VP9 WebM clip not found in downloaded content" - fi - else - log_warn "VP9 WebM clip download failed (offline or URL issue)" - fi +# -------------------- H.265 Concurrency Decode Test -------------------- +# Test: H265_Decode_Concurrency_16x1080p (PR43866) +run_h265_concurrency_test() { + testname="H265_Decode_Concurrency_16x1080p" + log_info "==========================================" + log_info "Running: $testname (PR43866)" + log_info "==========================================" + + total_tests=$((total_tests + 1)) + + # Check for required elements first (fast checks) + if ! has_element v4l2h265dec; then + log_skip "$testname: v4l2h265dec not available" + skip_count=$((skip_count + 1)) + return 1 + fi + + if ! has_element qtivcomposer; then + log_skip "$testname: qtivcomposer not available" + skip_count=$((skip_count + 1)) + return 1 + fi + + if ! has_element waylandsink; then + log_skip "$testname: waylandsink not available" + skip_count=$((skip_count + 1)) + return 1 + fi + + # Check for test clip + h265_clip="$OUTDIR/H265_480p_30fps.mp4" + if [ ! -f "$h265_clip" ]; then + if [ -n "$clipPath" ] && [ -f "$clipPath/H265_480p_30fps.mp4" ]; then + cp "$clipPath/H265_480p_30fps.mp4" "$h265_clip" + else + log_skip "$testname: Test clip not found (H265_480p_30fps.mp4)" + skip_count=$((skip_count + 1)) + return 1 fi fi -fi + + # Check if display is connected (required for waylandsink) + if ! check_display_connected; then + log_skip "$testname: No display connected (required for waylandsink)" + skip_count=$((skip_count + 1)) + return 1 + fi + + # Set up Wayland display using shared function + if ! camera_setup_wayland_environment "h265-concurrency"; then + log_skip "$testname: Failed to set up Wayland environment" + skip_count=$((skip_count + 1)) + return 1 + fi + + test_log="$OUTDIR/${testname}.log" + : >"$test_log" + + log_info "Building single pipeline with $concurrencySessions concurrent H.265 decode sessions..." + + # Increase file descriptor limit + ulimit -n 4096 2>/dev/null || true + + # Build pipeline using library function + pipeline=$(gstreamer_build_concurrency_decode_pipeline "h265" "$h265_clip" "$concurrencySessions") + + if [ -z "$pipeline" ]; then + log_fail "$testname: FAIL (could not build pipeline)" + fail_count=$((fail_count + 1)) + return 1 + fi + + log_info "Pipeline: gst-launch-1.0 -e $pipeline" + + # Run single pipeline with all 16 sessions + if gstreamer_run_gstlaunch_timeout "$((duration + 10))" "$pipeline" >>"$test_log" 2>&1; then + gstRc=0 + else + gstRc=$? + fi + + log_info "Exit code: $gstRc" + + # Validate log + if ! gstreamer_validate_log "$test_log" "$testname"; then + log_fail "$testname: FAIL (GStreamer errors detected)" + fail_count=$((fail_count + 1)) + return 1 + fi + + if [ "$gstRc" -eq 0 ] || [ "$gstRc" -eq 124 ]; then + log_pass "$testname: PASS (all $concurrencySessions sessions in single pipeline)" + pass_count=$((pass_count + 1)) + return 0 + else + log_fail "$testname: FAIL (rc=$gstRc)" + fail_count=$((fail_count + 1)) + return 1 + fi +} + +# -------------------- MJPEG Concurrency Decode Test -------------------- +# Test: MJPEG_Decode_Concurrency_16x1080p (FR98277) +run_mjpeg_concurrency_test() { + testname="MJPEG_Decode_Concurrency_16x1080p" + log_info "==========================================" + log_info "Running: $testname (FR98277)" + log_info "==========================================" + + total_tests=$((total_tests + 1)) + + # Check for required elements first (fast checks) + if ! has_element jpegdec; then + log_skip "$testname: jpegdec not available" + skip_count=$((skip_count + 1)) + return 1 + fi + + if ! has_element qtivcomposer; then + log_skip "$testname: qtivcomposer not available" + skip_count=$((skip_count + 1)) + return 1 + fi + + if ! has_element waylandsink; then + log_skip "$testname: waylandsink not available" + skip_count=$((skip_count + 1)) + return 1 + fi + + # Check for test clip + mjpeg_clip="$OUTDIR/mjpeg1.avi" + if [ ! -f "$mjpeg_clip" ]; then + if [ -n "$clipPath" ] && [ -f "$clipPath/mjpeg1.avi" ]; then + cp "$clipPath/mjpeg1.avi" "$mjpeg_clip" + else + log_skip "$testname: Test clip not found (mjpeg1.avi)" + skip_count=$((skip_count + 1)) + return 1 + fi + fi + + # Check if display is connected (required for waylandsink) + if ! check_display_connected; then + log_skip "$testname: No display connected (required for waylandsink)" + skip_count=$((skip_count + 1)) + return 1 + fi + + # Set up Wayland display using shared function + if ! camera_setup_wayland_environment "mjpeg-concurrency"; then + log_skip "$testname: Failed to set up Wayland environment" + skip_count=$((skip_count + 1)) + return 1 + fi + + test_log="$OUTDIR/${testname}.log" + : >"$test_log" + + log_info "Building single pipeline with $concurrencySessions concurrent MJPEG decode sessions..." + + # Build pipeline using library function + pipeline=$(gstreamer_build_concurrency_decode_pipeline "mjpeg" "$mjpeg_clip" "$concurrencySessions") + + if [ -z "$pipeline" ]; then + log_fail "$testname: FAIL (could not build pipeline)" + fail_count=$((fail_count + 1)) + return 1 + fi + + log_info "Pipeline: gst-launch-1.0 -e $pipeline" + + # Run single pipeline with all 16 sessions + if gstreamer_run_gstlaunch_timeout "$((duration + 10))" "$pipeline" >>"$test_log" 2>&1; then + gstRc=0 + else + gstRc=$? + fi + + log_info "Exit code: $gstRc" + + # Validate log + if ! gstreamer_validate_log "$test_log" "$testname"; then + log_fail "$testname: FAIL (GStreamer errors detected)" + fail_count=$((fail_count + 1)) + return 1 + fi + + if [ "$gstRc" -eq 0 ] || [ "$gstRc" -eq 124 ]; then + log_pass "$testname: PASS (all $concurrencySessions sessions in single pipeline)" + pass_count=$((pass_count + 1)) + return 0 + else + log_fail "$testname: FAIL (rc=$gstRc)" + fail_count=$((fail_count + 1)) + return 1 + fi +} + +# -------------------- HEVC Smart Encode Test -------------------- +# Test: HEVC_Encode_Smart_Bitrate_FPS_720p (FR74943) - Downstream only +run_smart_encode_test() { + testname="HEVC_Encode_Smart_Bitrate_FPS_720p" + log_info "==========================================" + log_info "Running: $testname (FR74943)" + log_info "==========================================" + + total_tests=$((total_tests + 1)) + + # Check if downstream stack + if ! gstreamer_is_downstream_stack; then + log_skip "$testname: Requires downstream video driver (Config2)" + skip_count=$((skip_count + 1)) + return 1 + fi + + # Check for required elements + if ! has_element qtiqmmfsrc; then + log_skip "$testname: qtiqmmfsrc not available (camera source required)" + skip_count=$((skip_count + 1)) + return 1 + fi + + if ! has_element qtismartvencbin; then + log_skip "$testname: qtismartvencbin not available" + skip_count=$((skip_count + 1)) + return 1 + fi + + test_log="$OUTDIR/${testname}.log" + output_file="$ENCODED_DIR/${testname}.mp4" + : >"$test_log" + + # Build pipeline matching production command with control sink + # Production: qtiqmmfsrc camera=0 noise-reduction=2 video_0::extra-buffers=20 video_0::type=preview name=camsrc + # video_0::type=video ! video/x-raw,format=NV12,width=1280,height=720,framerate=30/1 ! queue ! scb.sink + # qtismartvencbin default-gop=30 max-gop=600 smart-framerate=true smart-gop=false max-bitrate=4200000 name=scb encoder=v4l2h265enc ! queue ! h265parse ! queue ! mp4mux ! queue ! filesink location=... + # camsrc. ! video/x-raw,format=NV12,width=640,height=480,framerate=15/1 ! queue ! scb.sink_ctrl + pipeline="qtiqmmfsrc camera=0 noise-reduction=2 video_0::extra-buffers=20 video_0::type=preview name=camsrc video_0::type=video ! video/x-raw,format=NV12,width=1280,height=720,framerate=30/1 ! queue ! scb.sink qtismartvencbin default-gop=30 max-gop=600 smart-framerate=true smart-gop=false max-bitrate=4200000 name=scb encoder=v4l2h265enc ! queue ! h265parse ! queue ! mp4mux ! queue ! filesink location=${output_file} camsrc. ! video/x-raw,format=NV12,width=640,height=480,framerate=15/1 ! queue ! scb.sink_ctrl" + + log_info "Pipeline: $pipeline" + + # Run encoding + if gstreamer_run_gstlaunch_timeout "$((duration + 10))" "$pipeline" >>"$test_log" 2>&1; then + gstRc=0 + else + gstRc=$? + fi + + log_info "Exit code: $gstRc" + + # Validate log + if ! gstreamer_validate_log "$test_log" "$testname"; then + log_fail "$testname: FAIL (GStreamer errors detected)" + fail_count=$((fail_count + 1)) + return 1 + fi + + # Check output file + if [ -f "$output_file" ] && [ -s "$output_file" ]; then + file_size=$(gstreamer_file_size_bytes "$output_file") + log_info "Encoded file: $output_file (size: $file_size bytes)" + + if [ "$file_size" -gt 1000 ]; then + log_pass "$testname: PASS" + pass_count=$((pass_count + 1)) + return 0 + fi + fi + + log_fail "$testname: FAIL (no valid output)" + fail_count=$((fail_count + 1)) + return 1 +} + +# -------------------- HEVC Cyclic IR Encode Test -------------------- +# Test: HEVC_Encode_UHD_Cyclic_IR (FR82773) - Downstream only +run_cyclic_ir_encode_test() { + testname="HEVC_Encode_UHD_Cyclic_IR" + log_info "==========================================" + log_info "Running: $testname (FR82773)" + log_info "==========================================" + + total_tests=$((total_tests + 1)) + + # Check if downstream stack + if ! gstreamer_is_downstream_stack; then + log_skip "$testname: Requires downstream video driver (Config2)" + skip_count=$((skip_count + 1)) + return 1 + fi + + # Check for required elements + if ! has_element qtiqmmfsrc; then + log_skip "$testname: qtiqmmfsrc not available (camera source required)" + skip_count=$((skip_count + 1)) + return 1 + fi + + if ! has_element v4l2h265enc; then + log_skip "$testname: v4l2h265enc not available" + skip_count=$((skip_count + 1)) + return 1 + fi + + test_log="$OUTDIR/${testname}.log" + output_file="$ENCODED_DIR/${testname}.mp4" + : >"$test_log" + + # Build pipeline with cyclic intra refresh + pipeline="qtiqmmfsrc name=qmmf ! video/x-raw,format=NV12,width=3840,height=2160,framerate=30/1 ! v4l2h265enc extra-controls=\"controls,video_bitrate=8000000,intra_refresh_period_type=1\" capture-io-mode=4 output-io-mode=4 ! h265parse ! mp4mux ! filesink location=${output_file}" + + log_info "Pipeline: $pipeline" + + # Run encoding + if gstreamer_run_gstlaunch_timeout "$((duration + 10))" "$pipeline" >>"$test_log" 2>&1; then + gstRc=0 + else + gstRc=$? + fi + + log_info "Exit code: $gstRc" + + # Validate log + if ! gstreamer_validate_log "$test_log" "$testname"; then + log_fail "$testname: FAIL (GStreamer errors detected)" + fail_count=$((fail_count + 1)) + return 1 + fi + + # Check output file + if [ -f "$output_file" ] && [ -s "$output_file" ]; then + file_size=$(gstreamer_file_size_bytes "$output_file") + log_info "Encoded file: $output_file (size: $file_size bytes)" + + if [ "$file_size" -gt 1000 ]; then + log_pass "$testname: PASS" + pass_count=$((pass_count + 1)) + return 0 + fi + fi + + log_fail "$testname: FAIL (no valid output)" + fail_count=$((fail_count + 1)) + return 1 +} + +# -------------------- H.264 Slice MB Encode Test -------------------- +# Test: H264_Encode_Slice_MB_VGA (FR82771) - Downstream only +run_slice_mb_encode_test() { + testname="H264_Encode_Slice_MB_VGA" + log_info "==========================================" + log_info "Running: $testname (FR82771)" + log_info "==========================================" + + total_tests=$((total_tests + 1)) + + # Check if downstream stack + if ! gstreamer_is_downstream_stack; then + log_skip "$testname: Requires downstream video driver (Config2)" + skip_count=$((skip_count + 1)) + return 1 + fi + + # Check for required elements + if ! has_element qtiqmmfsrc; then + log_skip "$testname: qtiqmmfsrc not available (camera source required)" + skip_count=$((skip_count + 1)) + return 1 + fi + + if ! has_element v4l2h264enc; then + log_skip "$testname: v4l2h264enc not available" + skip_count=$((skip_count + 1)) + return 1 + fi + + test_log="$OUTDIR/${testname}.log" + output_file="$ENCODED_DIR/${testname}.mp4" + : >"$test_log" + + # Build pipeline with slice MB mode + pipeline="qtiqmmfsrc name=qmmf ! video/x-raw,format=NV12,width=640,height=480,framerate=30/1 ! v4l2h264enc extra-controls=\"controls,video_bitrate=1000000,slice_partitioning_method=1,number_of_mbs_in_a_slice=368\" capture-io-mode=4 output-io-mode=4 ! h264parse ! mp4mux ! filesink location=${output_file}" + + log_info "Pipeline: $pipeline" + + # Run encoding + if gstreamer_run_gstlaunch_timeout "$((duration + 10))" "$pipeline" >>"$test_log" 2>&1; then + gstRc=0 + else + gstRc=$? + fi + + log_info "Exit code: $gstRc" + + # Validate log + if ! gstreamer_validate_log "$test_log" "$testname"; then + log_fail "$testname: FAIL (GStreamer errors detected)" + fail_count=$((fail_count + 1)) + return 1 + fi + + # Check output file + if [ -f "$output_file" ] && [ -s "$output_file" ]; then + file_size=$(gstreamer_file_size_bytes "$output_file") + log_info "Encoded file: $output_file (size: $file_size bytes)" + + if [ "$file_size" -gt 1000 ]; then + log_pass "$testname: PASS" + pass_count=$((pass_count + 1)) + return 0 + fi + fi + + log_fail "$testname: FAIL (no valid output)" + fail_count=$((fail_count + 1)) + return 1 +} + +# -------------------- HEVC 4K Rotate90 Encode Test -------------------- +# Test: HEVC_Encode_4K_Rotate90 (FR72846) +run_rotate_encode_test() { + testname="HEVC_Encode_4K_Rotate90" + log_info "==========================================" + log_info "Running: $testname (FR72846)" + log_info "==========================================" + + total_tests=$((total_tests + 1)) + + # Check for required elements + if ! has_element qtiqmmfsrc; then + log_skip "$testname: qtiqmmfsrc not available (camera source required)" + skip_count=$((skip_count + 1)) + return 1 + fi + + if ! has_element v4l2h265enc; then + log_skip "$testname: v4l2h265enc not available" + skip_count=$((skip_count + 1)) + return 1 + fi + + test_log="$OUTDIR/${testname}.log" + output_file="$ENCODED_DIR/${testname}.mp4" + : >"$test_log" + + # Build pipeline with 90-degree rotation + # Note: rotate control may require downstream driver + if [ "$detected_stack" = "downstream" ]; then + pipeline="qtiqmmfsrc name=qmmf ! video/x-raw,format=NV12,width=3840,height=2160,framerate=30/1 ! v4l2h265enc extra-controls=\"controls,video_bitrate=8000000,rotate=90\" capture-io-mode=4 output-io-mode=4 ! h265parse ! mp4mux ! filesink location=${output_file}" + else + # Upstream may not support rotate control + pipeline="qtiqmmfsrc name=qmmf ! video/x-raw,format=NV12,width=3840,height=2160,framerate=30/1 ! v4l2h265enc extra-controls=\"controls,video_bitrate=8000000\" ! h265parse ! mp4mux ! filesink location=${output_file}" + log_warn "Rotate control may not be supported on upstream driver" + fi + + log_info "Pipeline: $pipeline" + + # Run encoding + if gstreamer_run_gstlaunch_timeout "$((duration + 10))" "$pipeline" >>"$test_log" 2>&1; then + gstRc=0 + else + gstRc=$? + fi + + log_info "Exit code: $gstRc" + + # Validate log + if ! gstreamer_validate_log "$test_log" "$testname"; then + log_fail "$testname: FAIL (GStreamer errors detected)" + fail_count=$((fail_count + 1)) + return 1 + fi + + # Check output file + if [ -f "$output_file" ] && [ -s "$output_file" ]; then + file_size=$(gstreamer_file_size_bytes "$output_file") + log_info "Encoded file: $output_file (size: $file_size bytes)" + + if [ "$file_size" -gt 1000 ]; then + log_pass "$testname: PASS" + pass_count=$((pass_count + 1)) + return 0 + fi + fi + + log_fail "$testname: FAIL (no valid output)" + fail_count=$((fail_count + 1)) + return 1 +} + +# -------------------- Main test execution -------------------- +log_info "Starting video encode/decode tests..." +log_info "Test type: $testType" + +# Route to appropriate test based on test type +case "$testType" in + basic) + # Run only basic encode/decode tests + log_info "Running basic encode/decode tests only" + ;; + uvc) + # Run only UVC test + run_uvc_preview_test + # Skip to summary + log_info "Skipping other tests (test type: uvc)" + ;; + drc) + # Run only DRC test + run_drc_decode_test + # Skip to summary + log_info "Skipping other tests (test type: drc)" + ;; + concurrency) + # Run only concurrency tests + run_h264_concurrency_test + run_h265_concurrency_test + run_mjpeg_concurrency_test + # Skip to summary + log_info "Skipping other tests (test type: concurrency)" + ;; + advanced-encode) + # Run only advanced encoding tests (downstream only) + run_smart_encode_test + run_cyclic_ir_encode_test + run_slice_mb_encode_test + run_rotate_encode_test + # Skip to summary + log_info "Skipping other tests (test type: advanced-encode)" + ;; + all) + # Run ALL tests (new default behavior) + log_info "Running ALL video tests (basic + advanced)" + + # Run basic tests (will be executed below in normal flow) + log_info "Will run: basic encode/decode tests" + + # Run UVC test + log_info "Will run: UVC camera test" + run_uvc_preview_test || true + + # Run DRC test + log_info "Will run: DRC decode test" + run_drc_decode_test || true + + # Run concurrency tests + log_info "Will run: Concurrency tests" + run_h264_concurrency_test || true + run_h265_concurrency_test || true + run_mjpeg_concurrency_test || true + + # Run advanced encode tests + log_info "Will run: Advanced encode tests" + run_smart_encode_test || true + run_cyclic_ir_encode_test || true + run_slice_mb_encode_test || true + run_rotate_encode_test || true + ;; + *) + log_warn "Unknown test type: $testType" + echo "$RESULT_TESTNAME SKIP" >"$RES_FILE" + exit 0 + ;; +esac + +# Skip basic tests if we ran a specific test type (not 'basic' or 'all') +if [ "$testType" != "basic" ] && [ "$testType" != "all" ]; then + # Jump directly to dmesg scan and summary + log_info "Skipping basic encode/decode tests (test type: $testType)" +else + # Continue with basic tests below + : +fi + +# Parse codec list +codecs=$(printf '%s' "$codecList" | tr ',' ' ') + +# Parse resolution list +resolutions=$(printf '%s' "$resolutionList" | tr ',' ' ') + +# -------------------- Test clip preparation -------------------- +log_info "==========================================" +log_info "TEST CLIP PREPARATION" +log_info "==========================================" + +# Function to get clip from local path or download +get_test_clip() { + clip_name="$1" + dest_path="$2" + + # Check if clip already exists + if [ -f "$dest_path" ]; then + log_info "Clip already exists: $clip_name" + return 0 + fi + + # Try to get from provided local path first + if [ -n "$clipPath" ] && [ -f "$clipPath/$clip_name" ]; then + log_info "Copying $clip_name from local path: $clipPath" + if cp "$clipPath/$clip_name" "$dest_path"; then + log_info "Successfully copied $clip_name" + return 0 + else + log_warn "Failed to copy $clip_name from local path" + fi + fi + + # Try to download from URL + log_info "Attempting to download $clip_name from URL..." + if extract_tar_from_url "$clipUrl" "$OUTDIR"; then + # Check if file was extracted + if [ -f "$clip_name" ]; then + mv "$clip_name" "$dest_path" + log_info "Successfully downloaded and moved $clip_name" + return 0 + elif [ -f "$OUTDIR/$clip_name" ]; then + log_info "Clip $clip_name already in correct location" + return 0 + else + log_warn "Clip $clip_name not found in downloaded content" + return 1 + fi + else + log_warn "Download failed for $clip_name (offline or URL issue)" + return 1 + fi +} + +# Prepare clips based on test type +case "$testType" in + basic) + # VP9 clip for basic decode tests + need_vp9_clip=0 + for codec in $codecs; do + if [ "$codec" = "vp9" ]; then + need_vp9_clip=1 + break + fi + done + + if [ "$need_vp9_clip" -eq 1 ] && [ "$testMode" != "encode" ]; then + get_test_clip "VP9_640x480_10s.webm" "$OUTDIR/VP9_640x480_10s.webm" + fi + ;; + + drc) + # DRC test clip + get_test_clip "1080_720_h264.mp4" "$OUTDIR/1080_720_h264.mp4" + ;; + + concurrency) + # Concurrency test clips + get_test_clip "H264_480p_30fps.mp4" "$OUTDIR/H264_480p_30fps.mp4" + get_test_clip "H265_480p_30fps.mp4" "$OUTDIR/H265_480p_30fps.mp4" + get_test_clip "mjpeg1.avi" "$OUTDIR/mjpeg1.avi" + ;; + + all) + # All test clips needed + log_info "Preparing all test clips..." + + # VP9 clip for basic decode tests + need_vp9_clip=0 + for codec in $codecs; do + if [ "$codec" = "vp9" ]; then + need_vp9_clip=1 + break + fi + done + + if [ "$need_vp9_clip" -eq 1 ] && [ "$testMode" != "encode" ]; then + get_test_clip "VP9_640x480_10s.webm" "$OUTDIR/VP9_640x480_10s.webm" + fi + + # DRC test clip + get_test_clip "1080_720_h264.mp4" "$OUTDIR/1080_720_h264.mp4" + + # Concurrency test clips + get_test_clip "H264_480p_30fps.mp4" "$OUTDIR/H264_480p_30fps.mp4" + get_test_clip "H265_480p_30fps.mp4" "$OUTDIR/H265_480p_30fps.mp4" + get_test_clip "mjpeg1.avi" "$OUTDIR/mjpeg1.avi" + ;; + + uvc|advanced-encode) + # No clips needed - uses camera + log_info "No test clips needed for test type: $testType" + ;; +esac + +log_info "Test clip preparation complete" # Run encode tests (skip VP9 as it doesn't support encoding in this test) if [ "$testMode" = "all" ] || [ "$testMode" = "encode" ]; then diff --git a/Runner/utils/lib_gstreamer.sh b/Runner/utils/lib_gstreamer.sh index d852bbf47..389a1e3ad 100755 --- a/Runner/utils/lib_gstreamer.sh +++ b/Runner/utils/lib_gstreamer.sh @@ -1198,6 +1198,7 @@ prepare_vp9_from_local_path() { return 1 } + # -------------------------------------------------------------- # download_resource # $1 url – URL to download @@ -1682,3 +1683,198 @@ camera_setup_wayland_environment() { return $((1 - wayland_ready)) } + +# -------------------- Downstream-Only Test Detection -------------------- +# gstreamer_is_downstream_stack +# Checks if current video stack is downstream +# Returns: 0 if downstream, 1 otherwise +gstreamer_is_downstream_stack() { + if ! command -v video_stack_status >/dev/null 2>&1; then + return 1 + fi + stack=$(video_stack_status "" 2>/dev/null || echo "unknown") + [ "$stack" = "downstream" ] +} + +# -------------------- UVC Camera Detection -------------------- +# gstreamer_detect_uvc_camera +# Detects UVC camera device by checking kernel driver via sysfs +# Prints: device path (e.g., /dev/video2) or empty if not found +gstreamer_detect_uvc_camera() { + for dev in /dev/video*; do + [ -c "$dev" ] || continue + + # Check if device uses uvcvideo driver via sysfs + drv=$(readlink -f "/sys/class/video4linux/${dev##*/}/device/driver" 2>/dev/null) + + if printf '%s\n' "$drv" | grep -q "/uvcvideo$"; then + printf '%s\n' "$dev" + return 0 + fi + done + + return 1 +} + +# -------------------- Advanced Test Pipeline Builders -------------------- + +# gstreamer_build_uvc_preview_pipeline +# Build pipeline for UVC camera live preview +# Args: device width height framerate +# Returns: pipeline string or empty on error +gstreamer_build_uvc_preview_pipeline() { + device="$1" + width="$2" + height="$3" + framerate="$4" + + [ -n "$device" ] || return 1 + [ -n "$width" ] || width="1920" + [ -n "$height" ] || height="1080" + [ -n "$framerate" ] || framerate="5" + + if has_element qtivtransform; then + printf 'v4l2src device=%s ! qtivtransform rotate=0 ! video/x-raw,width=%s,height=%s,framerate=%s/1 ! waylandsink fullscreen=true' \ + "$device" "$width" "$height" "$framerate" + else + printf 'v4l2src device=%s ! video/x-raw,width=%s,height=%s,framerate=%s/1 ! waylandsink fullscreen=true' \ + "$device" "$width" "$height" "$framerate" + fi +} + +# gstreamer_build_drc_decode_pipeline +# Build pipeline for Dynamic Resolution Change H.264 decode test +# Args: clip_path video_stack +# Returns: pipeline string or empty on error +gstreamer_build_drc_decode_pipeline() { + clip_path="$1" + video_stack="$2" + + [ -n "$clip_path" ] || return 1 + [ -f "$clip_path" ] || return 1 + + printf 'filesrc location=%s ! qtdemux ! queue ! h264parse ! v4l2h264dec capture-io-mode=4 output-io-mode=4 ! video/x-raw,format=NV12 ! fpsdisplaysink video-sink="waylandsink fullscreen=true" text-overlay=false' \ + "$clip_path" +} + +# gstreamer_build_concurrency_decode_pipeline +# Build pipeline for concurrent decode tests (H.264, H.265, MJPEG) +# Args: codec clip_path sessions +# Returns: pipeline string or empty on error +gstreamer_build_concurrency_decode_pipeline() { + codec="$1" + clip_path="$2" + sessions="$3" + + [ -n "$codec" ] || return 1 + [ -n "$clip_path" ] || return 1 + [ -f "$clip_path" ] || return 1 + [ -n "$sessions" ] || sessions="16" + + # Build qtivcomposer with 4x4 grid layout + pipeline="qtivcomposer name=mix" + pipeline="$pipeline sink_0::position=\"<0,0>\" sink_0::dimensions=\"<480,270>\"" + pipeline="$pipeline sink_1::position=\"<480,0>\" sink_1::dimensions=\"<480,270>\"" + pipeline="$pipeline sink_2::position=\"<960,0>\" sink_2::dimensions=\"<480,270>\"" + pipeline="$pipeline sink_3::position=\"<1440,0>\" sink_3::dimensions=\"<480,270>\"" + pipeline="$pipeline sink_4::position=\"<0,270>\" sink_4::dimensions=\"<480,270>\"" + pipeline="$pipeline sink_5::position=\"<480,270>\" sink_5::dimensions=\"<480,270>\"" + pipeline="$pipeline sink_6::position=\"<960,270>\" sink_6::dimensions=\"<480,270>\"" + pipeline="$pipeline sink_7::position=\"<1440,270>\" sink_7::dimensions=\"<480,270>\"" + pipeline="$pipeline sink_8::position=\"<0,540>\" sink_8::dimensions=\"<480,270>\"" + pipeline="$pipeline sink_9::position=\"<480,540>\" sink_9::dimensions=\"<480,270>\"" + pipeline="$pipeline sink_10::position=\"<960,540>\" sink_10::dimensions=\"<480,270>\"" + pipeline="$pipeline sink_11::position=\"<1440,540>\" sink_11::dimensions=\"<480,270>\"" + pipeline="$pipeline sink_12::position=\"<0,810>\" sink_12::dimensions=\"<480,270>\"" + pipeline="$pipeline sink_13::position=\"<480,810>\" sink_13::dimensions=\"<480,270>\"" + pipeline="$pipeline sink_14::position=\"<960,810>\" sink_14::dimensions=\"<480,270>\"" + pipeline="$pipeline sink_15::position=\"<1440,810>\" sink_15::dimensions=\"<480,270>\"" + + # Different output for MJPEG (needs mix. before waylandsink) + case "$codec" in + mjpeg) + pipeline="$pipeline mix. ! queue ! waylandsink fullscreen=true" + ;; + *) + pipeline="$pipeline ! queue ! waylandsink fullscreen=true" + ;; + esac + + # Add decode chains based on codec + i=0 + while [ "$i" -lt "$sessions" ]; do + case "$codec" in + h264) + pipeline="$pipeline filesrc location=${clip_path} ! qtdemux ! queue ! h264parse ! v4l2h264dec capture-io-mode=4 output-io-mode=4 ! video/x-raw,format=NV12 ! queue ! mix." + ;; + h265) + pipeline="$pipeline filesrc location=${clip_path} ! qtdemux ! queue ! h265parse ! v4l2h265dec capture-io-mode=4 output-io-mode=4 ! video/x-raw,format=NV12 ! queue ! mix." + ;; + mjpeg) + pipeline="$pipeline filesrc location=${clip_path} ! avidemux ! queue ! jpegdec ! videoconvert ! video/x-raw,format=RGB ! queue ! mix." + ;; + *) + return 1 + ;; + esac + i=$((i + 1)) + done + + printf '%s' "$pipeline" +} + +# gstreamer_build_smart_encode_pipeline +# Build pipeline for HEVC Smart Encode with dual camera streams +# Args: output_file +# Returns: pipeline string or empty on error +gstreamer_build_smart_encode_pipeline() { + output_file="$1" + + [ -n "$output_file" ] || return 1 + + printf 'qtiqmmfsrc camera=0 noise-reduction=2 video_0::extra-buffers=20 video_0::type=preview name=camsrc video_0::type=video ! video/x-raw,format=NV12,width=1280,height=720,framerate=30/1 ! queue ! scb.sink qtismartvencbin default-gop=30 max-gop=600 smart-framerate=true smart-gop=false max-bitrate=4200000 name=scb encoder=v4l2h265enc ! queue ! h265parse ! queue ! mp4mux ! queue ! filesink location=%s camsrc. ! video/x-raw,format=NV12,width=640,height=480,framerate=15/1 ! queue ! scb.sink_ctrl' \ + "$output_file" +} + +# gstreamer_build_camera_encode_pipeline +# Build pipeline for camera-based encoding with custom controls +# Args: codec width height output_file extra_controls video_stack +# Returns: pipeline string or empty on error +gstreamer_build_camera_encode_pipeline() { + codec="$1" + width="$2" + height="$3" + output_file="$4" + extra_controls="$5" + video_stack="$6" + + [ -n "$codec" ] || return 1 + [ -n "$width" ] || return 1 + [ -n "$height" ] || return 1 + [ -n "$output_file" ] || return 1 + + # Determine encoder and parser + case "$codec" in + h264) + encoder="v4l2h264enc" + parser="h264parse" + ;; + h265|hevc) + encoder="v4l2h265enc" + parser="h265parse" + ;; + *) + return 1 + ;; + esac + + # Build pipeline + if [ -n "$extra_controls" ]; then + printf 'qtiqmmfsrc name=qmmf ! video/x-raw,format=NV12,width=%s,height=%s,framerate=30/1 ! %s extra-controls="%s" capture-io-mode=4 output-io-mode=4 ! %s ! mp4mux ! filesink location=%s' \ + "$width" "$height" "$encoder" "$extra_controls" "$parser" "$output_file" + else + printf 'qtiqmmfsrc name=qmmf ! video/x-raw,format=NV12,width=%s,height=%s,framerate=30/1 ! %s capture-io-mode=4 output-io-mode=4 ! %s ! mp4mux ! filesink location=%s' \ + "$width" "$height" "$encoder" "$parser" "$output_file" + fi +} +