Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
cf1fc34
ci: add build deb
datagutt Jan 5, 2025
f164473
ci: use correct folder gstlibuvch264src
datagutt Jan 5, 2025
915770e
ci: reorganize folder structure
datagutt Jan 5, 2025
931973a
Merge pull request #1 from BELABOX/main
datagutt Jan 6, 2025
dd0aafa
Merge pull request #2 from BELABOX/main
datagutt Jan 8, 2025
a69dd5b
Merge branch 'BELABOX:main' into main
datagutt Jan 11, 2025
ecee7c5
added video control
moo-the-cow Nov 26, 2025
45ef3b4
added video control
moo-the-cow Nov 26, 2025
435e407
feat: add CI build check and modernize release workflow
andrescera Jan 15, 2026
34394b4
docs: add dependency chain to release notes
andrescera Jan 15, 2026
1a74865
Merge pull request #1 from CERALIVE/feat/ci-workflows
andrescera Jan 15, 2026
1380bd9
Fix multi-architecture build for native ARM64 runners
andrescera Jan 30, 2026
224f2c8
fix(ci): correct ARM64 runner label to ubuntu-24.04-arm
andrescera Jan 31, 2026
f4f3739
Merge pull request #2 from CERALIVE/feat/fix-multiarch-ci
andrescera Jan 31, 2026
a053820
docs(agents): add group-aware AGENTS.md (#3)
andrescera Jun 1, 2026
0111053
Merge irlserver/main: upstream timestamping, H265, libuvc v0.0.7 build
andrescera Jun 6, 2026
70d3890
chore: ignore .omo/ agent state directory (#4)
andrescera Jun 6, 2026
9b4c451
chore(gstlibuvch264src): add .clang-format + plugin-load smoke test
andrescera Jun 8, 2026
7cac3f7
docs: cerastream is the sole streaming engine (ceracoder retired 2026…
andrescera Jun 11, 2026
2251ab6
test(uvc): assert both H.264 and H.265 src caps
andrescera Jun 13, 2026
471e9dd
chore: add test-results/ to .gitignore
andrescera Jun 13, 2026
7cd54ba
docs: H.265 dual-codec confirmed
andrescera Jun 13, 2026
94d2378
chore(version): gstlibuvch264src CalVer version source
andrescera Jun 13, 2026
8622085
build(uvc): pin base image + libuvc SHA, fail-loud arch matrix, multi…
andrescera Jun 14, 2026
c6c75f1
docs(uvc): libuvc dead-handle teardown spike verdict for reconnect
andrescera Jun 14, 2026
34f38a6
refactor(uvc): split monolithic element into cohesive modules (no beh…
andrescera Jun 14, 2026
9543cf9
test(uvc): add libuvc mock harness + TSan/ASAN ctest variants
andrescera Jun 15, 2026
b1e0266
feat(uvc): add uvc_error_t → GST_ELEMENT_ERROR mapping helper
andrescera Jun 15, 2026
d7f8a1a
fix(uvc): sanitize SPS/PPS cache path, guard NULL, key by resolution
andrescera Jun 15, 2026
598e71b
fix(uvc): implement unlock/unlock_stop with timeout pop; fix control_…
andrescera Jun 15, 2026
10154f6
fix(uvc): free device list (ref-before-free), validate index, fatal e…
andrescera Jun 15, 2026
8ef18c1
fix(uvc): clamp SPS/PPS/VPS NAL copies to buffer size (heap overflow)
andrescera Jun 15, 2026
29efbc1
feat(uvc): native pan/tilt/zoom properties + signal via shared helper…
andrescera Jun 15, 2026
bcfbad2
fix(uvc): lock clock ref + PTS state in frame callback (narrow critic…
andrescera Jun 15, 2026
204596f
fix(uvc): let uvc_close own USB teardown; query real interface count
andrescera Jun 15, 2026
1701852
feat(uvc): opt-in hardened control socket via shared PTZ helper
andrescera Jun 15, 2026
497bef9
fix(uvc): clamp PTS monotonicity/duration; reset frame state on restart
andrescera Jun 15, 2026
e7af870
fix(uvc): negotiate caps leaks, framerate<=0 guard, zero-format error
andrescera Jun 15, 2026
d9692cf
feat(uvc): cheap V4L2 VIDIOC_TRY_FMT capability probe (log-only, no f…
andrescera Jun 15, 2026
88259a2
fix(uvc): report live latency + buffer offsets; write SPS/PPS only on…
andrescera Jun 15, 2026
eab6eee
feat(uvc): device selection by ordinal | vid:pid | serial | bus (back…
andrescera Jun 15, 2026
55d8743
fix(uvc): NAL parse — no unit drop, 3+4-byte start codes, size_t lengths
andrescera Jun 15, 2026
fd6cfa7
fix(uvc): lock frame_interval write in negotiate (TSan race with quer…
andrescera Jun 15, 2026
0743883
test(uvc): extend smoke test for new properties (originals preserved)
andrescera Jun 15, 2026
8afb593
test(uvc): functional suite vs mock + TSan/ASAN CI jobs
andrescera Jun 15, 2026
ae93a39
docs(uvc): document hardening changes, properties, control surface; b…
andrescera Jun 15, 2026
0ecfb48
feat(uvc): disconnect→error always; opt-in gated in-element auto-reco…
andrescera Jun 15, 2026
c20562c
Merge pull request #8 from CERALIVE/feat/gstlibuvch264src-hardening
andrescera Jun 15, 2026
ba16ac7
feat(libuvch264src): merge 2.0 hardening pass
datagutt Jun 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 90 additions & 0 deletions .clang-format
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
Language: Cpp
BasedOnStyle: LLVM
AccessModifierOffset: -4
# AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: true
AlignConsecutiveDeclarations: true
# AlignEscapedNewlinesLeft: false
AlignOperands: true
AlignTrailingComments: true
# AllowAllArgumentsOnNextLine: true # Requires clang-format v9 and higher
# AllowAllConstructorInitializersOnNextLine: false
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: false
# AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Inline
# AllowShortIfStatementsOnASingleLine: false
# AllowShortLoopsOnASingleLine: false
# AlwaysBreakAfterDefinitionReturnType: None
# AlwaysBreakAfterReturnType: None
# AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: true
BinPackArguments: false
BinPackParameters: false
# BraceWrapping:
# AfterClass: false
# AfterControlStatement: false
# AfterEnum: false
# AfterFunction: false
# AfterNamespace: false
# AfterObjCDeclaration: false
# AfterStruct: false
# AfterUnion: false
# BeforeCatch: false
# BeforeElse: false
# IndentBraces: false
# BreakBeforeBinaryOperators: None
BreakBeforeBraces: Allman
# BreakInheritanceList: BeforeComma
# BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeComma
# BreakAfterJavaFieldAnnotations: false
# BreakStringLiterals: true
ColumnLimit: 120
# CommentPragmas: '^ IWYU pragma:'
# ConstructorInitializerAllOnOneLineOrOnePerLine: false
# ConstructorInitializerIndentWidth: 4
# ContinuationIndentWidth: 4
# Cpp11BracedListStyle: true
# DerivePointerAlignment: false
# DisableFormat: false
# ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
# ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ]
# IncludeIsMainRegex: '$'
# IndentCaseLabels: false
IndentWidth: 4
# IndentWrappedFunctionNames: false
# JavaScriptQuotes: Leave
# JavaScriptWrapImports: true
# KeepEmptyLinesAtTheStartOfBlocks: true
# MacroBlockBegin: ''
# MacroBlockEnd: ''
# MaxEmptyLinesToKeep: 1
# NamespaceIndentation: None
# ObjCBlockIndentWidth: 2
# ObjCSpaceAfterProperty: false
# ObjCSpaceBeforeProtocolList: true
# PenaltyBreakBeforeFirstCallParameter: 19
# PenaltyBreakComment: 300
# PenaltyBreakFirstLessLess: 120
# PenaltyBreakString: 1000
# PenaltyExcessCharacter: 1000000
# PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Left
# ReflowComments: true
SortIncludes: false
# SpaceAfterCStyleCast: false
# SpaceAfterTemplateKeyword: true
# SpaceBeforeAssignmentOperators: true
SpaceBeforeParens: ControlStatements
# SpaceInEmptyParentheses: false
# SpacesBeforeTrailingComments: 1
# SpacesInAngles: false
# SpacesInContainerLiterals: true
# SpacesInCStyleCastParentheses: false
# SpacesInParentheses: false
# SpacesInSquareBrackets: false
Standard: Cpp03
TabWidth: 4
UseTab: Never
82 changes: 82 additions & 0 deletions .github/workflows/build-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
name: Build Check

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
Comment on lines +1 to +9

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Set least-privilege token permissions explicitly.

The workflow currently inherits default token permissions; declare minimal read-only permissions at workflow (or per-job) scope.

🔒 Suggested hardening
 name: Build Check
 
 on:
   push:
     branches: [main]
   pull_request:
     branches: [main]
+
+permissions:
+  contents: read
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
name: Build Check
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
name: Build Check
on:
push:
branches: [main]
pull_request:
branches: [main]
permissions:
contents: read
jobs:
🧰 Tools
🪛 zizmor (1.25.2)

[warning] 1-83: overly broad permissions (excessive-permissions): default permissions used due to no permissions: block

(excessive-permissions)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/build-check.yml around lines 1 - 9, The GitHub Actions
workflow in build-check.yml is using default token permissions instead of
explicitly declaring minimal, least-privilege permissions. Add a permissions
block at the workflow level (after the on section and before the jobs section)
to explicitly set minimal read-only permissions. For a build check workflow,
this typically means setting permissions to read-only access for repository
contents or using an empty permissions object if no special token permissions
are required.

Source: Linters/SAST tools

build:
name: Build ${{ matrix.config.arch }}
runs-on: ${{ matrix.config.runner }}
strategy:
matrix:
config:
- arch: arm64
runner: ubuntu-24.04-arm
- arch: amd64
runner: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
Comment on lines +21 to +22

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Disable credential persistence in checkout steps.

actions/checkout should set persist-credentials: false to avoid leaving token-backed git credentials in the workspace config.

🔐 Suggested change
       - name: Checkout code
         uses: actions/checkout@v4
+        with:
+          persist-credentials: false
@@
       - name: Checkout code
         uses: actions/checkout@v4
+        with:
+          persist-credentials: false

Also applies to: 49-50

🧰 Tools
🪛 zizmor (1.25.2)

[warning] 21-22: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)


[error] 22-22: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/build-check.yml around lines 21 - 22, The
actions/checkout@v4 steps are missing the persist-credentials configuration,
which allows token-backed git credentials to remain in the workspace and creates
a security risk. At .github/workflows/build-check.yml lines 21-22 (anchor) and
lines 49-50 (sibling), add persist-credentials: false to the with section of
each actions/checkout@v4 step to ensure credentials are not persisted after the
checkout completes.

Source: Linters/SAST tools

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Expect no output after pinning to 40-char SHAs.
rg -nP 'uses:\s*[^@]+@(?![0-9a-fA-F]{40}\b)' .github/workflows/build-check.yml

Repository: irlserver/gstlibuvch264src

Length of output: 236


Pin GitHub Actions to immutable commit SHAs instead of version tags.

Using mutable tags (@v*) exposes the workflow to upstream action substitution and supply-chain attacks. Pin all uses: statements to exact 40-character commit SHAs.

Applies to lines: 22, 25, 28, 50

🧰 Tools
🪛 zizmor (1.25.2)

[warning] 21-22: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)


[error] 22-22: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/build-check.yml at line 22, Replace all mutable GitHub
Actions version tags with exact 40-character commit SHAs in the build-check.yml
workflow file. For each uses statement at lines 22, 25, 28, and 50, change the
reference from the format actions/[owner]/[action]`@v`[version] to
actions/[owner]/[action]@[full-40-char-commit-sha]. This pins the actions to
immutable commit references, protecting against supply-chain attacks and
upstream action substitution.

Source: Linters/SAST tools


- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Build with Docker
uses: docker/build-push-action@v5
with:
context: .
outputs: build
platforms: linux/${{ matrix.config.arch }}

- name: Verify plugin was built
run: |
ls -la build/usr/lib/*/gstreamer-1.0/

- name: Build Summary
run: |
echo "## ✅ Build Check Passed" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Architecture:** ${{ matrix.config.arch }}" >> $GITHUB_STEP_SUMMARY
echo "**Runner:** ${{ matrix.config.runner }}" >> $GITHUB_STEP_SUMMARY

functional-test:
name: Functional suite + TSan/ASAN (ctest)
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Install build + GStreamer dev dependencies
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
build-essential \
cmake \
git \
patch \
pkg-config \
libgstreamer1.0-dev \
libgstreamer-plugins-base1.0-dev \
libjpeg-dev \
libusb-1.0-0-dev

# ENABLE_SANITIZERS is load-bearing: without it ctest runs only the plain
# variants, dropping all ASan (SPS/PPS overflow) and TSan (frame/PTS race)
# coverage. Keep it on so the full hardware-independent suite is gated.
- name: Configure and build (with sanitizers)
run: |
cmake -B build -DENABLE_SANITIZERS=ON
cmake --build build

- name: Run ctest (full suite incl. TSan/ASAN)
run: ctest --test-dir build --output-on-failure

- name: Functional Test Summary
if: always()
run: |
echo "## 🔌 Functional test suite (ctest + TSan/ASAN)" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo '`ctest --test-dir build` ran the full hardware-independent suite against the libuvc mock: plugin-load smoke, mock streaming, lifecycle, device selection, negotiate edges, SPS/PPS overflow (ASan), frame/PTS thread-safety (TSan), and the consolidated functional caps (H.264/H.265) + backpressure suite.' >> $GITHUB_STEP_SUMMARY
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@

# OpenCode agent state (local only)
.omo/

# Build directories (Meson + CMake/ctest)
/build/
/build*/

# Test artifacts (ctest output, coverage, traces)
/test-results/
83 changes: 83 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# CHANGELOG for gstlibuvch264src

## 2.0, Hardening release

The bulk of this release (module split, mock-backed test suite, memory-safety and correctness fixes, PTZ properties, device selection, hardened control socket) was contributed by [CeraLive](https://github.com/CERALIVE/gstlibuvch264src) and merged here.

### What

A comprehensive hardening pass across the entire element. The monolithic source was split into cohesive modules, a hardware-independent mock-backed test suite was built from scratch, and 19 distinct correctness and security issues were fixed. New capabilities: hardware-stable device selection, native PTZ properties, an opt-in hardened control socket, and a V4L2 capability probe.

### Why

The original element had several latent memory-safety issues (heap overflow on oversized SPS/PPS NALs, USB handle double-close, caps negotiation leaks), correctness bugs (PTS monotonicity, restart state not reset, NAL start-code detection missing the 3-byte form), and security concerns (world-accessible control socket at a fixed `/tmp` path, no index validation). None of these were caught by the existing smoke test because it only checked plugin registration. No frames flowed, no device was opened, no PTZ was exercised.

The hardening pass adds a full mock-backed ctest suite (TSan, ASAN, LSAN variants) that exercises all of these paths without requiring hardware.

### Changes

**Module split (behavior-preserving)**
The ~1100-line monolithic `gstlibuvch264src.c` was split into five cohesive translation units: `gstlibuvch264src.c` (GObject boilerplate), `frame_pipeline.c` (NAL parsing and PTS), `spspps_cache.c` (SPS/PPS disk cache), `ptz_control.c` (PTZ and control socket), `uvc_device.c` (USB teardown and V4L2 probe). No behavior change.

**Test infrastructure**
A libuvc mock harness (`mock_libuvc.c`) covers ~16 libuvc functions. A libusb mock (`mock_libusb.c`) enables teardown double-close testing. TSan and ASAN ctest variants run the full suite under sanitizers. The Dockerfile and CMakeLists.txt now pin the base image (`ubuntu:24.04@sha256:786a8b55...`) and libuvc source (`68d07a00`, v0.0.7) by SHA. The arch matrix fails loudly on unknown `TARGETARCH` values.

**Security and correctness fixes**
- Heap overflow: SPS/PPS/VPS NAL copies now clamp to buffer size before `memcpy`
- USB teardown UAF/double-close: `force_usb_release()` no longer calls `libusb_close()`, so `uvc_close()` owns the single close
- Caps negotiation leaks: single `goto out` cleanup path, `GValue` (GST_TYPE_LIST) unset after `gst_structure_set_value()`
- PTS monotonicity: clamp plus offset bound guard, first-frame underflow guard
- Restart state: `had_idr`, `send_sps_pps`, `frame_count`, `prev_int_ts` reset on every `start()`
- NAL parser: detects both 3-byte and 4-byte Annex-B start codes, dynamic unit array (no 10-unit cap), `size_t` lengths throughout
- Device-list leak: `uvc_ref_device()` before `uvc_free_device_list()`, fatal error on zero devices
- Index validation: strict `strtol` replaces silent `atoi()`, malformed index posts `RESOURCE/SETTINGS`
- Mutex lifecycle: `control_mutex` cleared once in `finalize()`, not in `stop()`
- `unlock()`/`unlock_stop()`: implemented with `FLUSH_SENTINEL` plus `g_async_queue_timeout_pop`, so `create()` never deadlocks on disconnect
- Framerate guard: `framerate <= 0` check prevents SIGFPE, zero device interval skipped
- PTS hot-path locking: clock ref plus PTS state under `GST_OBJECT_LOCK` in `frame_callback`
- SPS/PPS cache: path traversal blocked, NULL guards, resolution key (`<idx>_<codec>_<WxH>`)
- Error mapping: `uvc_error_t` to `GST_ELEMENT_ERROR` helper covers all libuvc error codes
- Interface count: real `bNumInterfaces` from `libusb_get_active_config_descriptor` replaces hardcoded 8
- SPS/PPS write-on-change: disk write suppressed when content is unchanged

**New capabilities**
- Device selection: `index` now accepts `"vid:pid"` (hex), `"serial:<sn>"`, `"bus:<bus>:<addr>"` in addition to the existing ordinal form. Backward-compatible, default `"0"` unchanged.
- Native PTZ properties: `pan`, `tilt`, `zoom` (GObject properties, capability-gated). The `set-ptz` action signal drives all three axes in one call. Always available, no socket required.
- Opt-in control socket: `control-socket=true` enables a Unix-domain PTZ socket. Default off. Per-instance path under `$XDG_RUNTIME_DIR` (mode 0600). The old world-accessible `/tmp/libuvc_control` is gone.
- Control socket protocol: `PAN_TILT <pan> <tilt>` and `ZOOM <zoom>` accept normalized floats in `[-1.0, 1.0]`, mapped onto the device's probed absolute range. Callers stay decoupled from per-device UVC units while the `pan`/`tilt`/`zoom` properties keep absolute units.
- Disconnect behavior: always posts `GST_ELEMENT_ERROR(RESOURCE, READ)` on device unplug.
- Opt-in reconnect: default off, gated on a confirmed-safe libuvc teardown spike (`libuvch264src/docs/notes/reconnect-spike.md`).
- LATENCY query: reports `live=TRUE`, `min=1/fps` instead of the GstBaseSrc default of zero.
- Buffer OFFSET: monotonic per-frame counter set on every buffer.
- V4L2 probe: one `VIDIOC_TRY_FMT` at open, logs H.264 availability, non-fatal.

**Build**
- Multiarch paths via `$(gcc -print-multiarch)`, no hardcoded `aarch64-linux-gnu`.
- Pinned base image and libuvc SHA in both Dockerfile and CMakeLists.txt.
- The Debian package CI (`publish-deb.yml`) is retained as the release path; the plugin version string is `2.0`.

### How to verify

```bash
# Full test suite with sanitizers
cmake -B build -DENABLE_SANITIZERS=ON && cmake --build build && ctest --test-dir build --output-on-failure

# Spot-check key areas
ctest --test-dir build -R "ptz_properties|ptz_capability_gate"
ctest --test-dir build -R "socket_default_off|socket_hardened"
ctest --test-dir build -R "device_selector"
ctest --test-dir build -R "usb_teardown"
ctest --test-dir build -R "negotiate_leak"
ctest --test-dir build -R "pts_monotonic|restart_idr"
ctest --test-dir build -R "nal_parse"
ctest --test-dir build -R "v4l2_probe"
```

On hardware: `gst-inspect-1.0 libuvch264src` should list `pan`, `tilt`, `zoom`, `control-socket`, `control-socket-path`, and the `set-ptz` action signal alongside `index`.

### Risks

- **Module split:** Zero behavior change by design. All five translation units compile to the same symbols as before, the split is purely organizational. The full ctest suite (including TSan/ASAN) validates this.
- **Control socket default off:** Any consumer that relied on the old `/tmp/libuvc_control` socket must now set `control-socket=true` and either read the resolved `control-socket-path` property or pass an explicit `control-socket-path`. Downstream consumers in this group standardize on `$XDG_RUNTIME_DIR/libuvc_control`.
- **Index selector expansion:** The `index` property now rejects malformed values that `atoi()` would have silently mapped to 0. A pipeline that passed a non-numeric string as `index` now fails loudly at `start()` instead of silently selecting device 0. This is the correct behavior.
- **TSan suppressions:** Two suppressions in `tsan_pts.suppressions` are permanent. `GST_OBJECT_LOCK` uses a raw futex in uninstrumented GLib, so TSan cannot see the happens-before relationship for PTS/clock fields. The behavioral tests (`pts_thread_safety`, `frame_throughput`) provide real coverage that the suppressions cannot mask.
108 changes: 108 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# =============================================================================
# CMake build for the libuvch264src plugin-load smoke test (ctest target).
#
# SCOPE: This CMake project exists ONLY to compile the GStreamer plugin together
# with a hardware-independent gst-check smoke test and expose it via ctest.
#
# The CANONICAL production build remains Meson (libuvch264src/meson.build) and
# the Dockerfile. Device-image artifacts must NOT be produced from this build.
# The plugin source is compiled verbatim - no runtime behavior is changed here.
# =============================================================================

cmake_minimum_required(VERSION 3.16)
project(gstlibuvch264src C)

set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)

enable_testing()

find_package(PkgConfig REQUIRED)
# Version floors. GStreamer 1.14 is the oldest series the plugin's pad/caps API
# usage compiles against (Ubuntu 18.04 baseline); no known incompatibility on
# the 1.x line, so no upper bound is imposed. libusb 1.0.20 is the floor for the
# auto-detach-kernel-driver call the libuvc patches rely on.
pkg_check_modules(GST REQUIRED IMPORTED_TARGET gstreamer-1.0>=1.14)
pkg_check_modules(GST_BASE REQUIRED IMPORTED_TARGET gstreamer-base-1.0>=1.14)
pkg_check_modules(GST_CHECK REQUIRED IMPORTED_TARGET gstreamer-check-1.0>=1.14)
pkg_check_modules(LIBUSB REQUIRED IMPORTED_TARGET libusb-1.0>=1.0.20)

# -----------------------------------------------------------------------------
# libuvc dependency.
#
# Prefer a system/pkg-config libuvc; otherwise vendor it at build time exactly
# like the Dockerfile does (upstream v0.0.7 + the UVC 1.5 and H.265 patches the
# plugin relies on). LIBUVC_RUNTIME_DIR is exported to the test so the vendored
# shared object is resolvable at dlopen() time.
# -----------------------------------------------------------------------------
pkg_check_modules(LIBUVC IMPORTED_TARGET libuvc)

if(LIBUVC_FOUND)
set(LIBUVC_LINK_TARGET PkgConfig::LIBUVC)
set(LIBUVC_EXTRA_INCLUDES "")
set(LIBUVC_RUNTIME_DIR "")
else()
message(STATUS "libuvc not found via pkg-config; vendoring upstream v0.0.7 + patches")
include(FetchContent)

# libuvc v0.0.7 ships pre-3.5 CMake policies; allow them under modern CMake.
set(CMAKE_POLICY_VERSION_MINIMUM 3.5 CACHE STRING "" FORCE)
set(BUILD_EXAMPLE OFF CACHE BOOL "" FORCE)
set(BUILD_TEST OFF CACHE BOOL "" FORCE)
set(BUILD_SHARED_LIBS ON CACHE BOOL "" FORCE)

FetchContent_Declare(libuvc
GIT_REPOSITORY https://github.com/libuvc/libuvc.git
# Pinned to the v0.0.7 commit SHA (tags are mutable). Keep in sync with the Dockerfile.
GIT_TAG 68d07a00e11d1944e27b7295ee69673239c00b4b
PATCH_COMMAND patch -p1 < ${CMAKE_CURRENT_SOURCE_DIR}/patches/uvc15-support.patch
&& patch -p1 < ${CMAKE_CURRENT_SOURCE_DIR}/patches/libuvc-h265-support.patch
UPDATE_DISCONNECTED 1
)
FetchContent_MakeAvailable(libuvc)

set(LIBUVC_LINK_TARGET uvc)
# v0.0.7 uses directory-level include_directories, so expose them explicitly.
set(LIBUVC_EXTRA_INCLUDES
${libuvc_SOURCE_DIR}/include
${libuvc_BINARY_DIR}/include
)
set(LIBUVC_RUNTIME_DIR ${libuvc_BINARY_DIR})
endif()

# -----------------------------------------------------------------------------
# GStreamer plugin module (compiled from the unmodified plugin source).
# Output: <build>/gstreamer-1.0/libgstlibuvch264src.so
# -----------------------------------------------------------------------------
set(GST_PLUGIN_BUILD_DIR ${CMAKE_BINARY_DIR}/gstreamer-1.0)

add_library(gstlibuvch264src MODULE
libuvch264src/src/gstlibuvch264src.c
libuvch264src/src/uvc_device.c
libuvch264src/src/frame_pipeline.c
libuvch264src/src/ptz_control.c
libuvch264src/src/spspps_cache.c
libuvch264src/src/gstlibuvch264src_error.c
)
set_target_properties(gstlibuvch264src PROPERTIES
PREFIX "lib"
LIBRARY_OUTPUT_DIRECTORY ${GST_PLUGIN_BUILD_DIR}
)
target_include_directories(gstlibuvch264src PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/libuvch264src/src
${LIBUVC_EXTRA_INCLUDES}
)
target_link_libraries(gstlibuvch264src PRIVATE
PkgConfig::GST
PkgConfig::GST_BASE
PkgConfig::LIBUSB
${LIBUVC_LINK_TARGET}
)
if(LIBUVC_RUNTIME_DIR)
# Make the vendored libuvc resolvable for the test even outside ctest's env.
set_target_properties(gstlibuvch264src PROPERTIES
BUILD_RPATH ${LIBUVC_RUNTIME_DIR}
)
endif()

add_subdirectory(tests)
Loading
Loading