Skip to content

fix a stale channel state, truncated audio settings and an RTCP send to fd 0 - #41

Merged
wberube merged 2 commits into
OpenIPC:masterfrom
kasperiio:divinus-fixes
Sep 9, 2026
Merged

fix a stale channel state, truncated audio settings and an RTCP send to fd 0#41
wberube merged 2 commits into
OpenIPC:masterfrom
kasperiio:divinus-fixes

Conversation

@kasperiio

Copy link
Copy Markdown
Contributor

Four independent defects, found while bringing up a new platform but none of them platform-specific. Split out of a larger branch so they can be reviewed on their own.

  • media — a failed create_channel, video_create or bind_channel returned through HAL_ERROR leaving chnState[index].enable true, so the video thread kept polling a channel that was never created.
  • server — audio bitrate, gain and srate were parsed into a short. 48000 does not fit, so POST /api/audio?srate=48000 silently stored −17536.
  • server — a POST with no Content-Type header dereferenced NULL inside STARTS_WITH.
  • rtcp__rtcp_send_sr() sent on con->trans[track_id].server_port_rtp without checking the track had ever been SETUP. A client that sets up video only leaves the audio track at fd 0, so the sender report went to stdin.

Testing

Built for fh8856v100_lite (ARMv6). Runs as part of the combined branch this was split from, on three Fullhan FH8856 cameras. This branch on its own is build-tested, not separately run on hardware.

Two follow-ups are prepared on top of this: RTSP interleaved-send/timestamp work, and a Fullhan FH8852/FH8856 HAL.

…to fd 0

Four independent defects, found while bringing up a new platform but none of
them platform-specific.

media: a failed channel, encoder or bind left chnState[index].enable true, so
the video thread kept polling a channel that was never created.

server: audio bitrate, gain and srate were parsed into a short. 48000 does not
fit, so setting a 48 kHz sample rate over the API silently stored -17536.

server: a POST with no Content-Type dereferenced NULL in STARTS_WITH.

rtcp: __rtcp_send_sr() sent on con->trans[track_id].server_port_rtp without
checking the track was ever SETUP. An audio track a client never set up has
fd 0, so the report went to stdin.
@kasperiio
kasperiio marked this pull request as ready for review September 5, 2026 16:59
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Fix media state, audio parsing, POST handling, and RTCP transport

🐞 Bug fix 🕐 20-40 Minutes

Grey Divider

AI Description

• Clear enabled media-channel state when channel, encoder, or binding setup fails.
• Preserve full-width audio values and tolerate POST requests without Content-Type.
• Skip RTCP sender reports for tracks without initialized UDP or TCP transport.
Diagram

graph TD
  API["Server API"] -->|stores integers| CFG["Audio Config"]
  API -->|checks header| OSD["OSD Upload"]
  MEDIA["Media Enable"] -->|creates| HAL["HAL Setup"] -->|failure clears| STATE["Channel State"]
  RTCP["RTCP Sender"] -->|checks setup| TRANS["Track Transport"]
Loading
High-Level Assessment

The targeted guards are appropriate for these independent defects and minimize regression risk. A centralized media-initialization rollback helper was considered, but it would broaden this focused fix; clearing state immediately before HAL_ERROR is consistent with the macro’s immediate-return behavior.

Files changed (3) +25 / -4

Bug fix (3) +25 / -4
media.cClear channel enable state after media setup failures +18/-0

Clear channel enable state after media setup failures

• Marks MJPEG and MP4 channels disabled when channel creation, encoder creation, or channel binding fails. This prevents the video thread from polling channels that were never successfully initialized.

src/media.c

rtcp.hSkip RTCP reports for tracks without transport setup +3/-0

Skip RTCP reports for tracks without transport setup

• Returns successfully without sending a sender report when a track has neither a UDP server port nor interleaved TCP transport. This prevents unconfigured tracks from using the zero-initialized file descriptor.

src/rtsp/rtcp.h

server.cPreserve audio values and guard missing Content-Type +4/-4

Preserve audio values and guard missing Content-Type

• Parses audio bitrate, gain, and sample rate into full-width integers, preventing values such as 48000 from being truncated. Also checks that Content-Type exists before identifying multipart OSD uploads.

src/server.c

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Sep 5, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Failure cleanup is skipped ✓ Resolved 🐞 Bug ☼ Reliability
Description
On encoder or bind failure, the new assignment clears chnState[index].enable before HAL_ERROR
returns, causing both disable functions to skip cleanup of the channel and any successfully created
encoder. Subsequent API retries can reuse the slot while its previous HAL resources remain allocated
or bound.
Code

src/media.c[R587-590]

      if (ret)
+        {
+            chnState[index].enable = false;
          HAL_ERROR("media", "Creating encoder %d failed with %#x!\n%s\n",
Evidence
take_next_free_channel reserves a slot by setting enable=true, while the disable functions
immediately skip slots where it is false. HAL_ERROR returns from the function, so the changed
assignments prevent the only normal unbind/destroy paths from running; the HTTP reconfiguration
paths subsequently call disable and enable again while ignoring the enable result.

src/media.c[303-313]
src/media.c[525-539]
src/media.c[605-620]
src/hal/macros.h[35-41]
src/server.c[1064-1067]
src/server.c[1144-1147]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Failed media initialization clears the channel's enabled state before releasing resources created by earlier initialization stages. Because `HAL_ERROR` returns immediately and disable functions skip disabled slots, retries can encounter leaked or stale HAL resources.
## Issue Context
Apply stage-aware rollback to both MJPEG and MP4 initialization. Destroy or unbind only resources whose creation succeeded, then mark the slot disabled and return the failure.
## Fix Focus Areas
- src/media.c[525-603]
- src/media.c[605-691]
- src/media.c[303-409]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Track check reads out-of-bounds ✓ Resolved 🐞 Bug ≡ Correctness
Description
__rtcp_send_sr indexes con->trans[track_id] in the new early-return condition before validating
that track_id is within the two-element array. An invalid ID therefore causes an out-of-bounds
read before the existing assertion can return FAILURE.
Code

src/rtsp/rtcp.h[R23-24]

+    /* A track without a transport (never SETUP) has fd 0, which is stdin */
+    if (!con->trans[track_id].server_port_rtp && !con->trans[track_id].is_tcp) return SUCCESS;
Evidence
The changed line dereferences trans at line 24, whereas the bounds assertion is not executed until
lines 32–34. The connection structure defines trans as an array of exactly two elements.

src/rtsp/rtcp.h[21-35]
src/rtsp/rtsp.h[98-106]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The newly added transport-state guard dereferences the transport array before the function's track bounds assertion. Move validation ahead of every `con->trans[track_id]` access.
## Issue Context
`connection_item_t::trans` contains exactly two tracks, and the existing assertion already defines the required bounds.
## Fix Focus Areas
- src/rtsp/rtcp.h[21-35]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Audio ranges remain unchecked ✓ Resolved 🐞 Bug ≡ Correctness
Description
The widened strtol results are still accepted after any partial numeric conversion without
overflow or supported-range validation, allowing values such as negative sample rates or bitrates to
wrap when assigned to unsigned configuration fields. The invalid settings are immediately passed to
audio reinitialization even though configuration loading restricts bitrate, gain, and sample rate to
defined ranges.
Code

src/server.c[R889-891]

+                    int result = strtol(value, &remain, 10);
                  if (remain != value)
                      app_config.audio_srate = result;
Evidence
The API checks only whether parsing consumed at least one character, then assigns directly to the
configuration and restarts audio. The destination declarations make bitrate and sample rate
unsigned, while the configuration loader proves the intended accepted ranges.

src/server.c[866-897]
src/app_config.h[77-81]
src/app_config.c[454-459]
src/media.c[452-481]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The audio API directly stores unchecked `strtol` conversions. Reject overflow, trailing nonnumeric characters, and values outside the same ranges enforced by configuration loading before modifying or reinitializing audio state.
## Issue Context
The supported ranges are bitrate 32–320, gain -60–30, and sample rate 8000–96000. Bitrate and sample rate destinations are unsigned.
## Fix Focus Areas
- src/server.c[866-897]
- src/app_config.c[454-459]
- src/app_config.h[77-81]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Tip of the day
💡 Did you know, you can commit Qodo's fix in one click with committable suggestions (GitHub & GitLab)

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread src/media.c
Comment thread src/rtsp/rtcp.h Outdated
Comment thread src/server.c Outdated
media: clearing chnState[index].enable before HAL_ERROR returned made things
worse, not better. media_mjpeg_disable() and media_mp4_disable() skip slots
whose enable is already clear, so a failure after the channel or encoder had
been created stranded them in the HAL, and take_next_free_channel() would then
hand the same index out again. Roll back stage by stage instead: release the
encoder and channel that were created, then clear the slot.

rtcp: the new transport guard indexed con->trans[track_id] ahead of the bounds
assertion that follows it, so an out-of-range track read out of bounds before
the assertion could reject it. Moved below the assertion and expressed through
the t pointer it already sets up.

server: widening the audio settings from short to int stopped them being
truncated but still stored whatever strtol() returned, including trailing
garbage, saturated overflow and negatives - and audio_bitrate and audio_srate
are unsigned, so a negative wrapped rather than being rejected. They are now
accepted only as a complete number inside the same ranges the config loader
enforces: bitrate 32-320, gain -60-30, srate 8000-96000.
kasperiio added a commit to kasperiio/divinus that referenced this pull request Sep 5, 2026
Adds src/hal/fh: a HAL for the Fullhan FH8852/FH8856 V100 generation
(ARM1176 softfloat, kernel 3.0.8, SDK V1.2.0 "OSDRV" libraries libdsp/
libisp/libispcore/libvmm/libmipi/libadvapi/libacw_mpi). The SDK ships as
binary-only shared objects without headers; the interface was recovered
from the libraries and a vendor application that statically links the same
SDK, and verified on an Asecam/Vatilon PB1 (FH8856 + GC4653).

- fh_sys/fh_vpss/fh_venc/fh_isp/fh_aud: dlopen wrappers for the MPI subset
- fh_snr_gc4653: userspace GC4653 driver (the ISP calls back into a sensor
  op table; registers go over /dev/i2c-0)
- H.264 and H.265 over RTSP, MJPEG, JPEG snapshots, audio capture, OSD via
  the VPU graphic plane (ARGB1555 at sensor resolution)
- anti-flicker via the AE flicker command; SmartIR image-gain day/night
  detection wired into night mode (no external light sensor needed)
- the VPU exposes two scaler channels (main + one sub); the JPEG snapshot is
  taken from the MJPEG sub-stream when MJPEG is enabled, mirroring the vendor
- fh_compat: getifaddrs() over SIOCGIFCONF; gpio.c resolves GPIO<n> vs
  gpio<n> sysfs node naming (fh kernels use uppercase)
- server: do not crash on an OSD POST without a Content-Type header
- platform detection via /proc/driver/chip; built only for ARMv6 targets

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Epx86cKNLr14TY4B41nq89

Includes the fixes from the upstream review of the three PRs this branch is
split into (OpenIPC/divinus OpenIPC#41, OpenIPC#42, OpenIPC#43): stage-aware rollback on media
init failure, the RTCP bounds check before indexing, validated audio API
ranges, interleaved flush for audio-only sessions, a latched RTSP audio codec,
exact 8 kHz G.711 resampling, bounded config parsing for rtsp.audio_codec and
night_mode.lamp, JSON escaping in the new endpoints, OSD clipping and bitmap
scaling, per-region OSD opacity, and checked snapshot reallocations.
kasperiio added a commit to kasperiio/divinus that referenced this pull request Sep 5, 2026
Adds src/hal/fh: a HAL for the Fullhan FH8852/FH8856 V100 generation
(ARM1176 softfloat, kernel 3.0.8, SDK V1.2.0 "OSDRV" libraries libdsp/
libisp/libispcore/libvmm/libmipi/libadvapi/libacw_mpi). The SDK ships as
binary-only shared objects without headers; the interface was recovered
from the libraries and a vendor application that statically links the same
SDK, and verified on an Asecam/Vatilon PB1 (FH8856 + GC4653).

- fh_sys/fh_vpss/fh_venc/fh_isp/fh_aud: dlopen wrappers for the MPI subset
- fh_snr_gc4653: userspace GC4653 driver (the ISP calls back into a sensor
  op table; registers go over /dev/i2c-0)
- H.264 and H.265 over RTSP, MJPEG, JPEG snapshots, audio capture, OSD via
  the VPU graphic plane (ARGB1555 at sensor resolution)
- anti-flicker via the AE flicker command; SmartIR image-gain day/night
  detection wired into night mode (no external light sensor needed)
- the VPU exposes two scaler channels (main + one sub); the JPEG snapshot is
  taken from the MJPEG sub-stream when MJPEG is enabled, mirroring the vendor
- fh_compat: getifaddrs() over SIOCGIFCONF; gpio.c resolves GPIO<n> vs
  gpio<n> sysfs node naming (fh kernels use uppercase)
- server: do not crash on an OSD POST without a Content-Type header
- platform detection via /proc/driver/chip; built only for ARMv6 targets

Claude-Session: https://claude.ai/code/session_01Epx86cKNLr14TY4B41nq89

Includes the fixes from the upstream review of the three PRs this branch is
split into (OpenIPC/divinus OpenIPC#41, OpenIPC#42, OpenIPC#43): stage-aware rollback on media
init failure, the RTCP bounds check before indexing, validated audio API
ranges, interleaved flush for audio-only sessions, a latched RTSP audio codec,
exact 8 kHz G.711 resampling, bounded config parsing for rtsp.audio_codec and
night_mode.lamp, JSON escaping in the new endpoints, OSD clipping and bitmap
scaling, per-region OSD opacity, and checked snapshot reallocations.
@wberube
wberube merged commit ae00d22 into OpenIPC:master Sep 9, 2026
@wberube

wberube commented Sep 9, 2026

Copy link
Copy Markdown
Member

Thank you very much for the fixes! The stale channel state and RTCP-to-stdin issues were good catches, I refined a few things on top, mainly moving the ranged-value parser to hal/tools so it can be reused by other modules.

Apologies for the delay in reviewing this, I’ll make sure to get to the other two PRs you opened promptly as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants