1
0
mirror of https://github.com/ossrs/srs.git synced 2026-10-24 09:59:20 +03:00

59 Commits

Author SHA1 Message Date
Winlin
069016c714 SRT: Fix the timestamps of 48 kHz AAC from SRT to RTMP. v8.0.64 (#4786)
Fixes #4762: with `srt_to_rtmp on`, 48 kHz AAC published over SRT got
wrong per-frame timestamps on the RTMP side, so the audio of RTMP
playback, HLS and forwards crackled. This replaces #4765, and keeps its
author's credit for what was learned from it.

## Cause

An SRT publisher such as FFmpeg packs several AAC frames into one PES,
which carries a single PTS. `SrsSrtFrameBuilder::on_ts_audio()` times
frame `i` at `pts + i * 1024 / sample_rate`, and took `sample_rate` from
the FLV sound rate, which has only 5.5, 11, 22 and 44.1 kHz:

- 48 kHz frames were stamped 23.22 ms apart instead of 21.33 ms, and the
timeline stepped at each PES.
- Every other rate FLV can't represent was wrong too: 96, 88.2, 64, 32,
24, 16, 12, 8 and 7.35 kHz.
- Players that place audio by timestamp, such as hls.js and YouTube
ingest, played gaps and overlaps. flv.js and ffplay hide it.

It regressed in the SRS 5.0 SRT rewrite. The AAC sequence header was
already right, and the UDP MPEG-TS ingest path already used the AAC
rate.

## Fix

Take the rate from the ADTS `sampling_frequency_index`, as the UDP
MPEG-TS path does, and keep 44.1 kHz for a reserved index, which has no
rate.

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Nandisha <61039123+AlexFreik@users.noreply.github.com>
2026-10-21 21:28:21 -04:00
Winlin
7b737e64df Perf: Load and profile tools, and the first SRS and Go proxy fixes. v8.0.63 (#4785)
SRS gets performance tools that load and profile SRS in Docker for every
protocol: a lean load tool, an internal skill that runs an improvement
round, and a measurement method that holds up to a 10% verdict. The same
tools then measure the Go proxy. The first runs found one SRS fix and
three Go proxy fixes.

The load tool, a standalone Go module in cmd/perf (bin/srs-perf):

- Per-core worker processes shard the clients and report counters each
second. Lean clients for every protocol: RTMP publish and play,
HTTP-FLV, HTTP-TS, HLS, DASH, RTSP, SRT, WHIP and WHEP. No decode;
frames and lag come from timestamps.
- 17 scenarios: each protocol's fan-out, publishers (rtmp-pub,
whip-pub), many-streams, cross-protocol bridges (srt-rtmp, srt-whep,
whip-rtmp, whip-flv), and delay probes for the RTMP pause, forward and
edge.
- run ramps by doubling to a capacity, then holds at 50% and 90% of it.
compare checks that the setups match and reports capacity and CPU per
client at the 10% line.
- make perf-image builds an optimized SRS with symbols and frame
pointers, with perf, FlameGraph and FFmpeg. SRS runs on one pinned core
and is configured only by env files, one per scenario.

The measurement method:

- A flat CBR source, checked to ±3% per second, since SRS CPU follows
the bitrate.
- SRS CPU from its cgroup once a second. Holds have no profiler in the
measuring window; each count gets its own separate profile hold.
- perf-ab.sh builds two revisions and runs them side by side on separate
cpusets. compare gives the 95% CI of the paired per-second difference.
- perf-verify.sh checks every run: capacity, healthy steps, profiles and
symbols, noise, and what bound the run.

The internal-perf-for-srs skill:

- SKILL.md holds the rules, the shared tools and the round workflow. A
Target Router loads references/cpp-server.md for the C++ media server or
references/go-server.md for the Go proxy.
- Each reference covers its tools, topology, scenarios, measuring, how
to read its profiles (Linux perf, or Go pprof and trace), A/B, the
verified scenarios with their capacity and top bottleneck, and how to
start a round.
- srs-develop routes performance, benchmark, load test and profile tasks
to it. perf-skill-test.sh checks the text against the scenario registry.
- Reports are git-ignored, since their numbers hold only for the machine
that made them.
- The skills now name each server by its job: the C++ media server and
the Go proxy, with no "generation".

C++ media server, one core, Linux arm64 in Docker:

- Fix: an empty SrsSharedPtr holds no reference count, so each fan-out
copy of a message does one malloc instead of three. Covered by
srs_utest_ai05.cpp.
- Capacity: rtmp-rtmp, rtmp-flv, rtmp-hls, whip-rtmp and whip-flv
12,800; rtmp-dash and srt-rtmp 6,400; rtmp-ts and rtmp-rtsp 3,200;
whip-whep 2,000 (cap); rtmp-whep, srt-whep, srt-srt and rtmp-pub 1,280;
whip-pub 1,000 (cap); many-streams 640 streams.
- Top bottlenecks found, not fixed: the RTMP fan-out copy,
_st_epoll_dispatch with EAGAIN reads, 4 KB reads and writes for HLS and
DASH segments, one write per message for HTTP-TS and two per packet for
RTSP, the SRTP replay window in libsrtp, and libsrt for SRT.
- Delays: the paused RTMP player's 500 ms sleep, and the forward's and
edge publish's 500 ms receive timeout, each adding about 220 ms of lag.

The Go proxy, cmd/proxy, on one core in front of 1 to 4 SRS origins:

- perf-up.sh -target proxy, run -target proxy, which samples the proxy
and each origin, Go profiles in each profile hold (CPU, allocations,
mutex, block, trace, GC), and proxy A/B.
- Fix: HTTP-FLV, HTTP-TS and HLS relay the origin's error status,
headers and body, rather than a 500 with the origin's address.
- Fix: a WebRTC STUN binding routes by its ufrag, not a cached client
address. WHIP and WHEP failed from 160 clients on three origins.
- Capacity on three origins: rtmp-flv and rtmp-hls 12,800; rtmp-rtmp and
rtmp-ts 6,400; whip-whep 2,000 (cap); srt-srt and rtmp-pub 1,280;
whip-pub 1,000 (cap); many-streams 1,280 streams.

---------

Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 18:46:30 -04:00
Winlin
abf574d57d Skills: ST coroutine entry on every CPU and QEMU tests. v8.0.59 (#4766)
Update the AI skills for the State Threads coroutine entry and the QEMU tests of [state-threads#42](https://github.com/ossrs/state-threads/pull/42). No SRS media server or proxy code changes; only skills, and the version and changelog.

## State Threads code map and docs router

ST now starts every new coroutine in the `_st_md_thread_start` assembly entry on every platform but Cygwin64, and tests 10 Linux CPUs with QEMU. The skills that route to ST say so:

- `internal-codemap-for-srs` `references/state-threads.md`:
  - `md.h` has `MD_INIT_THREAD_ENTRY`, and each `md_*.S` has the entry. Cygwin64 has none and keeps the old start.
  - Lists `st_utest_entry.cpp`, and notes that `tools/backtrace` stops at the entry.
  - Tests another CPU with `auto/qemu.sh <cpu>` (QEMU user mode in Docker) or `auto/darwin.sh <cpu>` (macOS, x86_64 under Rosetta 2).
- `internal-docs-for-srs`: routes to the new `docs/coroutine_entry.md` for the entry on every CPU, keeps `docs/win64_coroutine.md` for Windows x64, and notes the README section on QEMU and Rosetta.

## Autopilot

- `srs-autopilot` decides open choices itself: it picks the best option, logs it as a decision, and goes on. It stops only for a dangerous operation or a severe, unexpected situation.
- Each workflow (plan, track, run, review) moves to its own file in `references/`, so a session loads only the one it uses.

## Docs content

- `srs-develop` `develop-code.md`: updating the English docs bundled in `skills/internal-docs-for-srs/` is done with the develop workflow. Translation, the website framework and releases stay with the private website skill.

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-10 17:01:24 -04:00
Winlin
89bdba34ba AI: Skills-only agent config, OpenClaw for SRSBot. v8.0.58 (#4764)
Clean the AI agent config so each agent loads only what it uses. No SRS
media server or proxy code changes; only AI config, skills, and a doc
copy change. Docs: ossrs/srs-docs2#1.

SRS keeps supporting four agents: OpenClaw, Claude Code, Codex, and
Kiro.

## Coding agents: skills only

Claude Code, Codex, and Kiro used to load the OpenClaw persona files
(SRSBot's IDENTITY, SOUL, MEMORY, TOOLS, USER) through an entry file in
each folder. Now each folder holds only a `skills -> ../skills` link:

- `.claude/skills`, with no `CLAUDE.md` and no persona links.
- `.agents/skills` for Codex and other Agent Skills tools;
`.agents/AGENTS.md` is removed, since no tool reads it.
- `.kiro/skills`; `.kiro/steering/` is removed, since Kiro auto-loads
every steering file, persona links included.
- `.codex/` is removed: its `config.toml` set `model_instructions_file`,
which replaces the Codex base prompt, and `.codex/skills` duplicated
`.agents/skills`.

There is no root `AGENTS.md`; the skills carry all the rules.

## OpenClaw: rewritten for SRSBot

`.openclaw/` serves the SRS community robot in Telegram and Discord, and
users' own OpenClaw.

- `AGENTS.md` replaces the stock personal-assistant template with an SRS
guide: session start, group-chat behavior, the `srs-support` skill,
platform formatting, safety (no private data, ask before external
actions, never push), memory, and a `## Tools` section merged from
`TOOLS.md`.
- `TOOLS.md` and `HEARTBEAT.md` are removed; current OpenClaw no longer
reads them.
- `SOUL.md` and `IDENTITY.md` drop the personal-assistant text and keep
SRSBot ⚡. `MEMORY.md` has current facts instead of the first-boot note.
`USER.md` gets the `air config` dictation row and a note for users of
their own OpenClaw.

## Skills and other cleanup

- `srs-support`: the workspace folder list names the real folders and
sibling links, and says how OpenClaw reaches `state-threads/` and
`website/` through `srs/`.
- `srs-develop`: commit prefixes add `Kiro:`; Claude uses the co-author
line its harness gives instead of a hard-coded model name.
- `srs-develop` Oryx local development guide: project-root paths and
subshells instead of `~/git/srs`.
- `internal-docs-for-srs`: the bundled `getting-started-ai.md` copy is
synced with the website doc.
- Root `.gitignore`: drops the OpenClaw-era patterns that matched
nothing; `/tasks` and `/objs` stay.

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-08 21:54:23 -04:00
Winlin
55b4218a6b Skills: Isolate test ports and test ST on every OS. v8.0.57 (#4761)
Improve test isolation and cross-platform testing, and update the AI
skills. No SRS media server or proxy code changes; only tests, test
tools and skills change.

## Test ports

Every test layer gets its own port range below the Linux ephemeral range
(32768+), so tests run in parallel, alongside a human SRS or Docker
container, and a client socket never takes a port a test picked.

- Each test script takes one optional `SRS_TEST_PORT_BASE` and defaults
to its own 100-port slot: 11000-11700 for the eight proxy E2E scripts,
12100-12800 for the config-file, reload, GB28181 and RTC scripts. This
replaces `SRS_E2E_PORT_OFFSET` and the per-port `SRS_GB_*`,
`SRS_PLAIN_*` and `SRS_RTX_*` variables, so a Full tier sets no port
variables.
- The regression SRS runs on 13000+ through the `SRS_*_LISTEN` env, with
its pid and log in `/tmp/srs-regression`. `srs_test` adds
`-srs-rtmp-port`, `-srs-api-port` and `-srs-rtsp-port`, so it no longer
needs 1935, 1985 or 8554.
- Black-box tests allocate from 21000-25999 (`SRS_BLACKBOX_PORTS`), and
the custom-port RTSP case now allocates its port too. C++ utests use
26000-29999 (`SRS_UTEST_PORTS`).
- The Port Plan in `integration-tests.md` is the source of truth, with
every range, its owner, and the ports bound and still free.

## State Threads on every OS

- New `st-test.sh` detects the platforms and runs every ST test on each:
the local OS first, then Linux in Docker and Windows x64 in parallel,
with a summary per OS and the platforms not tested and why.
`st-test-runner.sh` runs one build.
- New `st-windows-test.sh`, opted in by `SRS_TEST_WINDOWS_HOST`, mirrors
the local checkout on a Windows host over SSH. It syncs a commit by
pushing to the branch's upstream, never `origin`.
- `full-tier-lock.sh` takes `--product`, so ST runs hold their own lock
and run in parallel with SRS tiers.

## Skills

- `srs-autopilot`: `tasks/tracker.md` lists every task file and its
state, and this skill's rows are owned by `SRS`. Each task gets its own
worktree and branch in every repository it changes. Tests are reported
by OS and CPU.
- `srs-develop`: `testable-code.md` covers the test platforms and
running tests in a subagent. New Step 5 verifies ST on Windows. Issue
titles start with the owning component: `SRS:`, `Proxy:`, `ST:` or
`Oryx:`.
- `internal-codemap-for-srs` and `internal-docs-for-srs`: the C++ test
map moves into `cpp-server.md`, `go-server.md` adds the Go proxy tests,
and the docs list the native Windows x64 coroutine doc and
`md_win64.asm`.

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 09:46:29 -04:00
Winlin
08cc7a15b1 Fix failed live source crash, ST source of truth, testability. v8.0.54 (#4758)
A crash fix for a failed live source, State Threads moved to its own
source of truth with new feature tests, and the next round of
testability refactors.

## Bug fix

- **Live: Drop a live source whose initialize failed. v8.0.52**
`SrsLiveSourceManager::fetch_or_create()` adds a new source to the pool
before `initialize()`, and left it there when `initialize()` failed. The
next client for that stream got the half-built source as if it were
ready. With an unknown `dvr_plan`, which `check_config` accepts,
`SrsDvr::initialize()` fails with `plan_` NULL but `actived_` set. The
first RTMP publish was refused with 3083, and the second crashed SRS in
`SrsDvr::on_publish()`. Now a failed `initialize()` removes the source
from the pool, if the pool still holds that same source, so the next
client creates a new one. With `SRS_VHOST_DVR_DVR_PLAN=foo`, both
publishes are now refused with 3083 and the server stays up.

## State Threads

- `state-threads/` (a symlink to `../state-threads`) is now the source
of truth for ST. Changes start there, then sync into
`trunk/3rdparty/st-srs/`.
- `scripts/copy-from-st.sh` copies `state-threads/` into the mirror, and
`scripts/copy-to-st.sh` copies the other way. Both skip ST-only files
(`.github`, `.run`, `ide`, `gtest-fit`) and remove files that no longer
exist in the source.
- `trunk/3rdparty/st-srs/` is synced from State Threads 86c0e69
(v1.9.0):
- Feature tests for keys, TCP and UDP I/O, the scheduler, sync, epoll,
select and stacks.
- Fixes two missed switch callbacks and the restore of a randomized
stack's guard page.
  - Retries blocking I/O on `EINTR`.
- Marks allocation failures and debugger code as excluded from coverage.
- Moves the change list to `CHANGELOG.md` and drops the old CentOS
Dockerfiles.

## Testability refactors (no behavior change)

- `SrsServer`: creates its RTC UDP listeners and SRT acceptors through
the app factory. Its HTTP server, ingester, signal manager, RTC session
manager, pid-file locker and SRT event loop are now typed as interfaces.
The libsrt log setup moves into `SrsSrtEventLoop`.
- Clocks and random generators are injected or captured in the live,
RTC, SRT and RTSP sources, the statistic stream, client and vhost, the
RTMP handshake blocks (`SrsKeyBlock`, `SrsDigestBlock`, `SrsC2S2`) and
`SrsRtcPlayerNegotiator`'s SSRC generator.
- Config reads go through a captured `ISrsAppConfig` in `SrsStatistic`,
`SrsStatisticVhost`, `SrsCoWorkers`, the summaries,
`SrsDtlsCertificate`, `SrsRtcBlackhole`, `SrsRtcPublishStream` and
`SrsRtcFrameBuilder`. `SrsRtcRecvTrack` reads its circuit breaker
through a captured `ISrsCircuitBreaker`.
- `assemble()` now does the construction work for the live, RTC, SRT and
RTSP source managers' timers, `SrsResourceManager`'s condition variable,
`SrsSharedTimer`'s fast timers, the SRT and HTTP receive threads,
`SrsUdpMuxListener`, `SrsSignalManager`, `SrsExecutorCoroutine`,
`SrsDynamicHttpConn`, `SrsGbMediaTcpConn`, `SrsGbSession`,
`SrsClockWallMonitor`, `SrsSrtFrameBuilder`, `SrsSrtConnection`,
`SrsSrtListener`, `SrsOriginHub`'s DVR and `SrsRtcRtpBuilder`'s audio
transcoder.
- `SrsHttpHeartbeat` reaches the statistic, and `SrsServer` reaches the
heartbeat, through interfaces. `SrsGbListener` takes the API server
owner in its constructor.

## AI skills

- A State Threads code map and docs route, a State Threads workflow in
`develop-code.md`, and ST version and changelog rules.
- SRS issue Truth Records condensed to one short format.

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-10-02 12:16:27 -04:00
Pinal Desai
f7b7e091f5 Kernel: Fix the wrong picture size of an H.264 SPS with scaling lists. v8.0.53 (#4757)
Fixes #4756, the same bug as #1335. Thanks to @is-pdesai for the report,
the camera SPS and the original fix.

When an H.264 SPS has `seq_scaling_matrix_present_flag=1`, each
`seq_scaling_list_present_flag[i]` that is set is followed by a
`scaling_list()` (7.3.2.1.1 and 7.3.2.1.1.1 of ISO/IEC 14496-10).
`SrsFormat::avc_demux_sps_rbsp()` read the flags but not the lists, so
every later field, including the picture size, was read from the wrong
bit position. A 1280x720 High profile camera was reported as 80x48 by
`/api/v1/streams` and in the log. Playback was not affected, only the
parsed metadata.

## Changes

- **Kernel: Fix the wrong picture size of an H.264 SPS with scaling
lists. v8.0.53** For each present list, the parser reads the
`delta_scale` values, 16 for lists 0 to 5 and 64 for lists 6 to 11, and
stops early when `nextScale` becomes 0, which covers
`useDefaultScalingMatrixFlag` and the uncoded tail of a list.
`delta_scale` is read with `srs_avc_nalu_read_uev()` like the other AVC
SPS fields, so a truncated SPS now fails with `ERROR_AVC_NALU_UEV`
instead of returning a size parsed from garbage.
- **Tests:** `srs_utest_ai33` builds SPS bit by bit and covers the
camera SPS from the issue (also with trailing zero bytes, through
`on_video()` as an RTMP sequence header), fully coded lists, the default
matrix, early terminated lists, mixed lists, 4:2:2 with 8 lists, 4:4:4
with 12 lists, a truncated SPS, and Main and High profile SPS without
coded lists. The five scaling list cases fail without the fix, for
example 80x48 instead of 1280x720.
- **Skills:** route media streaming standards to the local copies in
`website/static/files/`, place git worktrees next to the SRS checkout,
and make Scan PRs report the goal of a pull request instead of a merge
verdict.

Not changed here: the frame crop offsets are multiplied by 2 regardless
of `chroma_format_idc` and `frame_mbs_only_flag`, and the `profile_idc`
list for the High profile fields lacks 134, 135, 138 and 139, which were
added after the 2012 edition.

---------

Co-authored-by: winlin <winlinvip@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-10-01 19:20:11 -04:00
Winlin
e50cccb74a Fix TS caster crash, RAW API reload and RTCP count. v8.0.51 (#4753)
Three bug fixes found while making `SrsServer` and the WebRTC transports
testable, plus the testability refactors themselves and a new reload E2E
test.

## Bug fixes

- **Caster: Fix the MPEG-TS over UDP caster crash. v8.0.51** A PES
spanning several TS packets is reaped by a later packet, but the reaped
message still referenced the packet that started it, which
`SrsTsContext::decode()` had already freed. The pithy-print trace in
`SrsMpegtsOverUdp::on_ts_message()` read freed memory, and SRS aborted
under ASAN about a second after FFmpeg began publishing to the caster.
Both reap points in `SrsTsPayloadPES::decode()` now point `packet_` at
the reaping packet.
- **API: Apply the RAW API switches of a reloaded config. v8.0.50**
`SrsGoApiRaw` copied `raw_api` and `allow_reload` once at startup, so
after a reload that turned `allow_reload` off, `rpc=reload` was still
accepted; after turning `raw_api` off, `rpc=reload-fetch` still
answered; and a RAW API enabled by a reload stayed refused until
restart. `serve_http()` now reads both switches from the config on each
request.
- **WebRTC: Count each sent RTCP packet once. v8.0.49** Every RTCP
packet was counted twice in `_srs_pps_srtcps`, by `send_rtcp()` and by
its caller, doubling the rtcp rate in the periodic `spkts` log.
Measurement only; no packet was sent twice.

## Testability refactors (no behavior change)

- `SrsServer`: the constructor is quiescent; a new `assemble()` queries
the parent pid and assembles the HTTP server, called right after
construction. The black hole, the nine TCP listeners
(`ISrsMultipleTcpListeners`, exporter as `ISrsIpListener`), and the
HTTP-FLV, MPEG-TS over UDP and GB28181 casters (`ISrsHttpFlvListener`,
`ISrsUdpCasterListener`, `ISrsGbListener`) are reached through interface
members.
- Reload status: the globals `_srs_reload_err`, `_srs_reload_state` and
`_srs_reload_id` are replaced by `ISrsReloadStatus`, captured by
`SrsServer` and `SrsGoApiRaw`.
- RTC black hole: `SrsRtcTcpNetwork`, `SrsSecurityTransport` and
`SrsRtcConnection` capture `ISrsRtcBlackhole` instead of reading
`_srs_blackhole`; the redundant `if (_srs_blackhole->blackhole_)` guards
are dropped since `sendto()` already returns when disabled.

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-09-25 21:48:06 -04:00
Winlin
928d1c30b1 Verify STUN integrity, fix SRT and WebRTC TCP republish, run SRS by env only. v8.0.48 (#4751)
- Bug fixes: checking the signature on WebRTC STUN binding requests
(v8.0.47), freeing the WebRTC TCP connection so the client can reconnect
(v8.0.46), and letting an SRT stream be republished after a failed
publish (v8.0.45).
- Run SRS by environment variables only:
  - SRS_RTMP_LISTEN (v8.0.48).
  - With -e: no pid file, daemon off and logs to the console by default.
  - New stream caster and edge variables, and the new AI section in -h.
  - The docs and test scripts now start SRS with -e.
  - The code name changes from Kai to Free. That change is easy to miss.
- Testability: the assemble() refactors for the SRT, WebRTC TCP and HTTP
connections, and the new interfaces for the WebRTC TCP network and the
blackhole.

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 15:28:26 -04:00
Winlin
4acaf221ab Fix RTSP SETUP crash, HLS ctx check bypass, log fd and timer bugs; make core classes testable. v8.0.44 (#4750)
Bug fixes found while making core classes testable, plus the refactors
that made them testable. v8.0.42 to v8.0.44.

## Bug fixes

- **RTSP: SETUP with no Transport header crashed the server.**
`on_rtsp_request()` asserted on `req->transport_`, which the client
controls. `srs_assert` stays live in release builds, and the path needs
no auth and no prior DESCRIBE. So one request from any client that could
open a TCP connection to the RTSP port aborted the whole process. The
server now answers `400 Bad Request` and keeps serving. Confirmed on a
real server before and after the fix.
- **HLS: a refused viewer could get in by retrying with the same
`hls_ctx`.** `serve_m3u8_ctx()` kept the client-chosen ctx even when the
security check or the `on_play` hook refused the viewer. The retry was
then served as an existing session and skipped both checks. A refused
viewer now keeps no session and is removed from the statistic, so every
retry is checked again.
- **HTTP: VOD MP4 range end is clamped to the last byte of the file.**
v8.0.42
- **Log: the log fd is cleared when the logger reopens it.** `reopen()`
closed `fd_` but didn't reset it on the console-tank and empty-filename
paths. Later writes went to a closed fd whose number may already belong
to another socket, and the next reopen closed it again. SIGUSR1 log
rotation triggers this. v8.0.43
- **Log: descriptor 0 counts as a log file.** The logger used `fd > 0`
as "I have a log file". A process started with stdin closed gets 0 for
its log file, and then every line was dropped without any error.
- **Timer: every subscriber is notified when one unsubscribes during the
round.** `SrsFastTimer::cycle()` looped over the handlers by index while
`unsubscribe()` erased from the same vector, so the next handler was
skipped. An RTC connection could lose a NACK, TWCC or RTCP round when
another connection was torn down. Each round now notifies from a queue.
v8.0.44
- **RTSP: a repeated DESCRIBE/SETUP now rebuilds tracks and frees the
old writer.** A repeated DESCRIBE leaked track descriptions. After a
republish it also left a stale track that made SETUP bind to a dead
SSRC, so playback went silent. A repeated SETUP leaked one
`SrsRtspTcpNetwork` per repeat. The send path no longer default-inserts
NULL entries through `operator[]`.
- **RTMP: `srs_usleep(mw_sleep)` renamed to `mw_sleep_`.** This was a
dead branch that would not compile if enabled.

## Testability

Collaborators are now injected, and constructor-time work moved into
`assemble()`, so these classes can be unit tested with mocks:

- `SrsSrtAcceptor`: config, listener factory and SRT options behind
`ISrsSrtListener` / `ISrsSrtOptions`.
- `SrsHlsStream::on_timer`, `SrsHttpStaticServer`, `SrsVodStream`:
injected config.
- `SrsFastTimer`, `SrsHourGlass`: the coroutine and clock are created in
`assemble()`.
- `SrsRtmpConn`, `SrsRtspConnection`, `SrsHttpServer`: the context id,
coroutine and wiring are set up in `assemble()`.
- `SrsFileLog`: output goes through `ISrsLogWriter`, and config is
captured in `initialize()`.
- Pithy print: the stage manager, clock and config are injected members.

New and extended unit tests are in `srs_utest_ai05`, `ai15`, `ai16`,
`ai22`, `ai25`, and the new `ai28` to `ai32`. Several coverage
exclusions were narrowed or removed.

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-09-23 10:05:35 -04:00
Winlin
3fc1810a46 WebRTC: Add RFC 4588 RTX config, tests, tools fix. v8.0.41 (#4746)
First step toward RFC 4588 RTX retransmission with SSRC multiplexing
(#4671). This PR adds the config, the server-side groundwork, the tests
and the test tools. RTX negotiation, the uplink unwrap and the downlink
RTX send path come in a follow-up PR.

## Config

New per-vhost `nack_prefer_rtx` directive, off by default. It can be
overridden with `SRS_VHOST_RTC_NACK_PREFER_RTX`.

```
vhost __defaultVhost__ {
    rtc {
        nack_prefer_rtx on;
    }
}
```

`rtc.rtx.conf` and `rtc.plain.conf` are sample configs for the two
modes. `regression-test.conf` turns RTX on.

## Server changes

- **NACK simulator fix:** simulated drops (and `drop_for_pt`) now happen
before SRTP unprotect. Before, a dropped packet had already been
unprotected, so its retransmission was rejected as a replay.
- SDP writes `a=ssrc-group` lines. `SrsRtxPayloadDes` uses the offered
clock rate and reads `apt` from the fmtp.
- A play NACK that names an RTX SSRC is ignored.
- RTX counters show up as cumulative totals in the `rtx=(s,r,u,p)` stats
group.
- Detailed NACK logs are compiled in only for `--simulator=on` or
debug-drop builds.

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-21 20:38:54 -04:00
winlin
5805a5c200 Claude: Reset the play track cache when the publisher republishes. v8.0.40
Forward-port 3fe0342f0 from 7.0release. When the publisher republishes, the
source calls on_stream_change on every player, which re-keys the play stream's
audio and video track maps to the new publisher's SSRCs and keeps the same
track objects. It never cleared the fast cache, so the slots kept the old
SSRCs. Packets were still delivered through the maps, but a new SSRC got a
cache slot only while one was free, so after a republish the cache stopped
covering the lookup for the rest of the session.

Clear the three cache slots at the top of on_stream_change in SrsRtcPlayStream
and in SrsRtspPlayStream, which is a copy with the same gap. The first packet
of the new stream then fills the cache again.

Add unit tests for both streams: fill the cache, change the stream to new
SSRCs, and check the cache is empty and the first new packet takes slot 0.

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-21 16:55:30 -04:00
winlin
37565b69bf Claude: Resolve RTC publish and play tracks once per SSRC. v8.0.39
Cherry-pick cb443a8d8 from 7.0release. The publish stream kept a fast cache of
the SSRC to track mapping, but only do_on_rtp_plaintext used it. The decode
handler on_before_decode_payload ran before it, and scanned both the audio and
the video tracks for every packet, so the cache never covered the lookup on the
decode path.

Extract find_track() in SrsRtcPublishStream and call it from both sites. The
decode handler now fills the cache, and do_on_rtp_plaintext hits it, so the
tracks are scanned once per SSRC instead of twice per packet. The handler is
declared by ISrsRtpPacketDecodeHandler on the audio and video tracks, not by
SrsRtcRecvTrack, so the cached track is cast by the is_audio it returns.

Extract the same find_track() in SrsRtcPlayStream, where send_packet was the
only user of its cache. This is a behavior preserving refactor.

An unfilled cache slot keeps the SSRC 0, so both helpers check the cached track
rather than the slot before using it, and fall back to the tracks when the slot
is empty. Without that check, a packet with SSRC 0 would be dropped instead of
being resolved by the tracks.

Add unit tests for the cache fill, the cache hit, and the empty slot in both
streams.

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-09-21 14:00:51 -04:00
Winlin
0d2d28913a Build: Fix make install failing on macOS due to BSD sed -i. v8.0.38 (#4745)
## Problem

On macOS, `make install` aborts partway through:

```
Now copy init.d script files
sed: 1: "/Users/home/.local/etc/ ...": extra characters at the end of h command
make: *** [install] Error 1
```

The install target rewrites `ROOT=` and `CONFIG=` in `etc/init.d/srs`
with:

```make
@sed -i "s|^ROOT=.*|ROOT=\"$(SRS_PREFIX)\"|g" $(__REAL_INSTALL)/etc/init.d/srs
```

GNU sed takes an *optional attached* suffix for `-i`; BSD sed requires
one. BSD sed therefore consumes the `s|...|g` script as the backup
suffix and parses the install path as the sed script, where `/Users/`
reads as an address and `h` as a command — hence the error. The trailing
letter varies with the install prefix (`/Volumes/...` reports `invalid
command code K`).

Install stops there, before the systemd service files are copied. These
lines date back to the original install support, so `make install` has
never worked on macOS.

## Fix

`sed -i.bak`, then remove the backup. That is the one form every sed
accepts:

| Form | GNU (Linux) | BSD (macOS) | BusyBox |
|---|---|---|---|
| `sed -i "script" file` (before) | works | **fails** | works |
| `sed -i "" "script" file` | **fails** | works | fails |
| `sed -i.bak "script" file` (this PR) | works | works | works |

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-19 21:03:51 -04:00
Winlin
2a0745f169 Protocol: Resolve multi-level paths as app folders and a stream file for all protocols. v8.0.37 (#4743)
Fixes #4739.

An SRT stream with a multi-level path, such as
`#!::r=tenant-1/live/0/0/AZR-001`, could be published and played over
HLS, but an HTTP-FLV or HTTP-TS viewer hung forever with no data. RTMP
publishers with the same path were not affected. Requesting a
multi-level HTTP-FLV/TS path before publishing, or on an edge, answered
404 where a two-segment path waits for the publisher.

**Root cause**

Protocols disagreed on where a stream path splits into app and stream.
RTMP splits at the last slash, so the path above is app
`tenant-1/live/0/0` and stream `AZR-001`. `srs_srt_streamid_to_request`
split at the first slash, giving app `tenant-1` and stream
`live/0/0/AZR-001`. Both produce the same stream URL, so the HTTP mount
was created at the right path.

`SrsLiveStream::serve_http_impl` then rebuilds the stream name from the
last segment of the HTTP path while keeping the publisher's app, so the
viewer of an SRT stream resolved to `/tenant-1/AZR-001`, a source with
no publisher (`create consumer, active=0`), and waited forever. HLS was
never affected because it is served as files from
`hls_path/[app]/[stream]`.

The 404 before publishing came from the slash-count check in
`SrsHttpStreamServer::dynamic_match`, which only runs when no exact
mount matches the path.

**The rule**

A stream path is resolved like a file path by every protocol: the last
segment is the stream and the others are the app.

- **SRT:** split the stream id at the last slash, after removing the
query, because a param value may contain slashes.
- **RTMP:** `srs_net_url_guess_stream` takes the stream from the last
segment of the app, ignoring slashes inside the query.
- **WHIP/WHEP:** folders in the `stream` parameter move to the app, so
`app=tenant&stream=live/s` is app `tenant/live` and stream `s`.
- **HTTP-FLV/TS:** a path with more segments than the template matches
when the template contains `[app]`, before publishing or on an edge,
instead of answering 404. A template without `[app]` still requires an
exact match.

No new configuration is needed; the `allow_multilevel_app` option
proposed in the issue is unnecessary.

**Compatibility**

SRT publishers and players whose stream id has three or more segments
now get a different app and stream: `a/b/c` was app `a` and stream
`b/c`, and is now app `a/b` and stream `c`. The stream URL is unchanged,
so playback URLs and HLS paths stay the same, but HTTP callbacks, the
HTTP API, and any config using `[app]` and `[stream]` separately, such
as DVR paths, see the new values. Two-segment stream ids are unchanged.

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-09-18 20:08:58 -04:00
Winlin
0c4d3c142d RTC: Answer the video codec of the stream for WebRTC play. v8.0.36 (#4742)
Fixes #4738. Truth Record:
https://github.com/ossrs/srs/issues/4738#issuecomment-5723763184

Playing an H.265 stream over WebRTC without `?vcodec=` answered H.264
and delivered no video at all. Affects `7.0.44`+ and all `8.0`, with
`rtmp_to_rtc on` and a non-WebRTC publisher. The workaround is
`?vcodec=h265`.

**Root cause**

`SrsRtcSource::init_for_play_before_publishing` creates one placeholder
track per supported codec, H.264 first, so that a client can negotiate
before anything is published. `negotiate_play_capability` fell back to
`get_track_desc("video", "").at(0)` whenever the client asked for no
codec, which is always that H.264 placeholder — regardless of what the
stream actually carries.

A non-WebRTC publisher keeps those placeholder tracks, and
`SrsRtcRtpBuilder` builds RTP on the track of the codec it detects in
the video sequence header. So the player subscribed to the H.264 SSRC
while every packet arrived on the H.265 one, and
`SrsRtcPlayStream::send_packet` dropped all of them as `Drop for ssrc %u
not found`. The SDP answer and the RTP stream disagreed on both the
codec and the SSRC.

**The rule**

The same for H.264, H.265 and AV1:

- **Before publishing** the codec of the stream is unknown, so the
client decides. Any codec it asks for is allowed, and asking for none
answers the first placeholder track.
- **While publishing** the stream decides. Asking for no codec answers
the codec of the stream, asking for that codec is allowed, and asking
for any other codec is refused — SRS never transcodes for WebRTC, so
that client could only ever get a black picture.

`SrsRtcRtpBuilder::initialize_video_track` records the codec it selected
on the source as `bridge_video_codec_`, which the new
`SrsRtcSource::publish_video_codec()` returns to the negotiator;
`on_unpublish` clears it so the next player may choose again. For a
WebRTC publisher, whose negotiated tracks replace the placeholders, the
single remaining video track description is the codec of the stream.

**Other changes**

- **Refuse instead of answering audio-only.** An empty `track_descs`
previously produced an answer with no video track, leaving the client
waiting for media that never arrives; it now returns an SDP exchange
error naming the codec. This also makes a VP9 request before publishing
an explicit error.
- **AV1 placeholder track** for play before publishing, alongside H.264
and H.265, so a client can negotiate the codec only a WebRTC publisher
can produce.
- **Testability.** `ISrsRtcSSRCGenerator` is a new interface over
`SrsRtcSSRCGenerator`, and `SrsRtcSource`, `SrsRtcRtpBuilder` and
`SrsRtcPlayerNegotiator` hold config, statistic, shared-timer and
generator pointers captured in their constructors instead of reaching
for the globals. `ISrsAppConfig` gains the five RTC getters the bridge
uses. Behavior-preserving; without it the negotiation path cannot be
exercised in a unit test.
- **Tests.** `MockRtcPlayScenario` in
`trunk/src/utest/srs_utest_workflow_rtc_conn.cpp` covers the matrix:
play before publishing, an H.264 and an H.265 stream from the bridge, an
AV1 stream from a WebRTC publisher, and an H.264-only client asking for
an H.265 stream. The bridge cases assert that the answered SSRC is the
one the bridge sends with — the part the reported failure got wrong.

**Also in this branch**

Two unrelated commits ride along, which is why the diff spans ~390
files:

- `Claude: Bump copyright year to 2026 in license headers.` — 2013-2025
→ 2013-2026 across SRS-owned headers only.
- `Claude: Add Oryx CI test script and centralize version and release
rules.` — skill-only, no runtime code.

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-18 08:26:21 -04:00
jurry
a82cc8db3e Codec: Validate lengthSizeMinusOne to avoid abort on malformed SPS. v8.0.35 (#4741)
Fixes #4740. Same crash as #4434, which was closed as completed but the
fix does not appear to have landed — the assert is still on `develop`.

A publisher that sends an H.264 sequence header with `lengthSizeMinusOne
== 2`, followed by any video frame, aborts the SRS process. The value
comes from publisher input, so this is remotely triggerable.

**Root cause**

`SrsFormat::avc_demux_sps_pps` wrote `lengthSizeMinusOne` into
`vcodec_->NAL_unit_length_` before validating it. The error return
leaves the member at `2`, because it is codec state, not a local.

The following frame reaches the assert because the rejected parse also
leaves the codec looking initialized: `avc_extra_data_` is populated at
the top of the same function, before any validation, and
`is_avc_codec_ok()` is just `!avc_extra_data_.empty()`. So the guard in
`video_nalu_demux` that would otherwise drop the frame lets it through,
and `do_avc_demux_ibmf_format` hits
`srs_assert(vcodec_->NAL_unit_length_ != 2)`.

---------

Co-authored-by: winlin <winlinvip@gmail.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 10:51:52 -04:00
Winlin
3422d61cfb Claude: Report on_play and on_publish rejection to the client. v8.0.34 (#4737)
Reimplements the fix proposed in #4736, designed from its problem
statement rather than its diff.

When an `on_play` or `on_publish` HTTP callback rejects a client, SRS
blocked it but never reported why — and on RTMP it reported the opposite
first. Every hook rejection arrives as the same `ERROR_RESPONSE_CODE`,
so each protocol now translates that one error into whatever status its
transport can carry.

- **HTTP-FLV and HLS**: add a single root error handler,
`srs_http_stream_serve_error()`, which maps `ERROR_SYSTEM_AUTH` onto
`401` and leaves every other error closing the connection as SRS has
always done. A failing branch only transforms the error code; the root
of the handler decides the status, so no branch responds on its own. It
sends the error code and its short name only — never `srs_error_desc`,
which carries the callback URL and the backend's response. This required
exposing `header_wrote()` on `ISrsHttpResponseWriter`, so the handler
never rewrites a response already on the wire.
- **WHEP**: reuse the status mapping WHIP already had. The play path
used `srs_error_wrap()` where publish used
`srs_error_transform(ERROR_SYSTEM_AUTH, ...)`, which is the entire
reason a rejected viewer got `500` instead of `401`. The deprecated JSON
play and publish APIs are annotated, since they answer HTTP 200 with a
fixed `{"code":400}` for every failure and must not be extended.
- **RTSP**: map the same transformed error onto `401 Unauthorized` in
`DESCRIBE`.
- **RTMP**: no status to fix, only an ordering one. `start_play()` and
`start_fmle_publish()` sent `onStatus(NetStream.Play.Start /
Publish.Start)` before the hook was consulted, so a rejected client was
told it was live and then silently disconnected. Authorization now runs
first on both sides, with the publish check extracted into
`SrsRtmpConn::authorize_publish()`.
- **SRT**: unchanged. MPEG-TS over SRT has no application-level status
channel, so refusal without a reason is the final, correct behavior, and
a test locks that limitation in.
- **Testability**: `SrsHlsStream` and `SrsHlsVirtualConn` were not
mockable, so they were converted to the codebase's standard shape —
globals captured in the constructor, `security_` typed as the interface,
and the timer subscription moved out of the constructor into
`assemble()`, forwarded by `SrsVodStream` and called at every
construction site. Behavior-preserving.
- **Skill**: `references/testable-code.md` becomes the single owner of
the test-first contract and the C++ mockability patterns this change
followed, plus a rule to reimplement a rejected reference PR from its
problem statement rather than its diff.

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 17:02:25 -04:00
Winlin
b14bc82aea Skills: Harden Oryx local stack lifecycle and transcode cleanup. v8.0.33 (#4735)
- Make local Oryx stack startup perform a clean restart after checking
prerequisites, with atomic PID-state recording for diagnostics,
including partial startup failures.
- Discover stack processes by TCP listening sockets and UDP sockets
rather than saved PIDs; send TERM, escalate to KILL when necessary, and
verify the ports are clear. Leave Redis running and exclude TCP clients.
- Harden the transcode test with JSON API-response validation,
exit/signal cleanup, and explicit disabling followed by polling until
the task stops.
- Update local-development documentation and require Bash 3.2-compatible
skill automation.

---------

Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
2026-09-05 13:33:55 -04:00
Winlin
37450ddfcf Skills: Add shared Oryx stack lifecycle and per-scenario API test scripts. v8.0.32 (#4734)
Builds out local, disposable-stack shell-script coverage for Oryx's
scenario pages under `skills/srs-develop/scripts/`:

- Split shared local-stack lifecycle (Redis/SRS/Go backend/React
dashboard) out of the test scripts into `oryx-stack-start.sh` /
`oryx-stack-stop.sh`, so multiple test scripts can run concurrently
against one instance instead of each managing (and fighting over) its
own. `oryx-run-tests.sh` starts the stack, runs every registered script
in parallel, always tears the stack down, and prints a PASS/FAIL
summary.
- Trimmed `oryx-api-smoke-test.sh` and `oryx-live-streaming-test.sh` to
pure assertions against the shared stack, and fixed a
`probe_has_audio_video` flake where WHIP's audio track wasn't always
muxed into HTTP-FLV yet at probe time.
- Added end-to-end API test scripts for the remaining scenario pages,
each verified against a real running stack and registered in
`oryx-run-tests.sh`:
- `oryx-live-room-test.sh` — Live Room CRUD/rename/remove plus a
room-scoped RTMP publish.
- `oryx-forward-test.sh` — RTMP and SRT forward targets (fixed platform
keys to avoid unbounded Redis growth across runs, since there is no
delete action, only update).
- `oryx-record-test.sh` — enable → publish → confirm in-progress →
force-end → verify MP4 has real video/audio → remove.
- `oryx-vlive-test.sh` — bind a stream source, self-loop forward, verify
playback.
- `oryx-camera-test.sh` — bind a source, enable forwarding, verify
status/playback.
- `oryx-transcode-test.sh` — enable the global transcode task, verify
the transcoded output stream has real video/audio, restore the original
config.
- Documented the shared-stack split and `oryx-run-tests.sh` in
`oryx-local-development.md`.

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-04 23:10:25 -04:00
Winlin
ab358068dc Skills: Oryx local development workflow, doc reorg, and API smoke test. v8.0.31 (#4733)
- Add an Oryx local-macOS-development workflow doc (Redis, local SRS, Go
backend, React UI, restart loop, health checks) and link it from the
Oryx development guide.
- Add `scripts/oryx-api-smoke-test.sh`: a self-contained smoke test for
the Oryx platform API (version, password login, Bearer security-key
auth) that starts Redis/SRS/the Go backend as needed and cleans up only
what it started; documented in the local-development guide.
- Reorganize bundled Oryx documentation into a dedicated
`references/oryx/` folder, update the internal documentation router, and
update `copy-from-docs.sh`/`copy-to-docs.sh` to sync from the new paths.
- Add OpenClaw workspace links for the SRS, Oryx, and Dev Docker
worktrees, and record the verified maintenance conclusion for issue
#4728.

All changes are confined to `.claude`/`skills` AI-tooling and workspace
files; no SRS or Oryx runtime source is touched.

---------

Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-03 22:44:23 -04:00
Jacob Su
b0d775a316 TS: Increment PMT version when codecs change for strict demuxers. v8.0.30 (#4731)
Fix HTTP-TS playback with strict MPEG-TS demuxers, such as
MobileVLCKit/libVLC on iOS, by incrementing the PMT `version_number`
whenever SRS refreshes the PMT after a codec-set change.

**Problem**

For HTTP-TS, SRS may start with unknown audio/video codecs when
`guess_has_av` is enabled. If the first media packet is video, SRS emits
an initial PMT that describes only the video PID. When AAC audio is
discovered later, SRS emits another PMT that adds the AAC PID `0x101`.

Before this fix, every PMT used `version_number = 0`. Strict demuxers
can treat a same-version PMT as unchanged, ignore the refreshed PMT, and
then drop AAC packets because PID `257 / 0x101` was never registered.
The visible symptom is video playback without audio for HTTP-TS on iOS
libVLC.

**Root cause**

`SrsTsPacket::create_pmt()` hardcoded the PMT version to zero, while
`SrsTsContext::encode()` already refreshes PAT/PMT when the codec set
changes. The PMT content changes, but the PMT version does not, so
strict receivers may reject the update.

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
2026-08-26 11:30:55 -04:00
Winlin
dc1120fc63 Claude: Upgrade libsrt to 1.5.6 for security enhancement. v8.0.29 (#4729)
Upgrade the vendored SRT library from 1.5.3 to 1.5.6 to fix two remotely
reachable security vulnerabilities.

Fixes #4727.

SRS vendored SRT 1.5.3 on all maintained branches. Both CVEs fixed in
upstream 1.5.6 were confirmed reachable in SRS, not merely present by
version number:

- **CVE-2026-55869** (upstream PR #3317) — `processSrtMsg_KMREQ` copied
`bytelen/4` words into a 104-byte stack buffer with no capacity check.
The existing `bytelen <= HCRYPT_MSG_KM_OFS_SALT` test is a lower bound
and runs *after* the copy.
- **CVE-2026-55868** (upstream PR #3319) — `processSrtMsg_KMRSP` did the
same with no validation of `len` at all.

The enforced-encryption gate in `interpretSrtHandshake` blocks the
handshake path when no passphrase is configured, but it does **not**
cover the post-connect `UMSG_EXT` control packet path
(`CUDT::processSrtMsg`), which has no passphrase gate and reaches both
handlers on any established connection. With the default MSS of 1500,
that is roughly **1352 bytes of stack overflow** from any peer that
completes a handshake — and SRS's streamid and callback auth run after
`srt_accept` returns, so they do not gate it.

SRT is off by default at runtime (`get_srt_enabled()` returns false and
stock `conf/srs.conf` has no `srt_server` block), so only deployments
that explicitly enable `srt_server` were exposed.

1.5.6 also carries three hardening changes (upstream PRs #3324, #3320,
out-of-bounds reads land inside the same 1500-byte unit slot allocated
by `CUnitQueue::allocateEntry`, making them stale-data logic bugs rather
than memory-safety violations, and the `CRcvBuffer::dropMessage` guard
is unreachable under SRS defaults because its caller sits behind
`!m_bTLPktDrop || !m_bTsbPd`.

Full reachability analysis:
https://github.com/ossrs/srs/issues/4727#issuecomment-5356428807

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 10:27:10 -04:00
Winlin
bb5fde228f feat: support Bearer authentication for SRS and proxy APIs. v8.0.28 (#4726)
Fix #4690

- Require `SRS_HTTP_API_AUTH_TYPE` when SRS HTTP API authentication is
enabled.
- Add Bearer token authentication for the SRS HTTP API while preserving
Basic authentication.
- Add Bearer authentication for proxy origin registration with
`PROXY_HTTP_API_AUTH_*`.
- Allow SRS heartbeat requests to authenticate to the proxy with
`SRS_HEARTBEAT_AUTH_*`.
- Add unit tests, an end-to-end Bearer authentication test,
documentation, and separate Basic/Bearer sample configurations.

---------

Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
2026-08-19 08:03:08 -04:00
sevico
69cbfe5e97 Proxy: Fix resource leak in ParseBody. v8.0.26 (#4659)
## Problem

`ParseBody` in `internal/utils/utils.go` defers `r.Close()` only after
`io.ReadAll(r)` succeeds. If `io.ReadAll` returns an error, the function
returns before registering the deferred close, so the request/body
reader is left open.

```go
func ParseBody(r io.ReadCloser, v interface{}) error {
    b, err := io.ReadAll(r)
    if err != nil {
        return errors.Wrapf(err, "read body") // r is not closed on this path
    }
    defer r.Close()
```

## Solution

Move `defer r.Close()` to the top of `ParseBody`, before reading the
body, so all return paths close the reader.

```go
func ParseBody(r io.ReadCloser, v interface{}) error {
    defer r.Close()

    b, err := io.ReadAll(r)
```

---------

Co-authored-by: Jacob Su <suzp1984@gmail.com>
Co-authored-by: winlin <winlinvip@gmail.com>
2026-08-17 21:36:23 -04:00
sevico
5491196678 Proxy: Replace io/ioutil and strengthen integration verification. v8.0.24 (#4660)
Replace the final deprecated `io/ioutil` use in the WebRTC proxy with
`io.ReadAll` and remove the obsolete import.

---------

Co-authored-by: Winlin <winlinvip@gmail.com>
2026-08-17 18:59:06 -04:00
Winlin
a4f5ac3e37 RTMP: Restore correct chunk stream IDs for strict clients. v8.0.23 (#4723)
## Problem

The `SrsSharedPtrMessage` to `SrsMediaPacket` refactor stopped carrying
each RTMP packet's preferred chunk stream ID (CSID). SRS then selected a
CSID only from the RTMP message type.

That fallback is insufficient because all AMF0 commands share message
type 20 even though connection commands and stream commands use
different CSIDs. It also sent protocol-control messages on CSID 3
instead of the protocol-required CSID 2. Strict clients such as Larix
Broadcaster may fail during RTMP connection setup.

| Packet | Expected CSID | Before this fix |
| --- | ---: | ---: |
| `SrsSetChunkSizePacket` | 2 | 3 |
| `SrsSetPeerBandwidthPacket` | 2 | 3 |
| `SrsConnectAppResPacket` | 3 | 5 |
| `SrsOnBWDonePacket` | 3 | 5 |
| `SrsPublishPacket` | 5 | 5 |
| `SrsPlayPacket` | 5 | 5 |

Related report and packet captures: #4683.

## Root cause

The packet-specific preferred CSID was lost between `SrsRtmpCommand`,
`SrsMessageHeader`, and `SrsMediaPacket`. Once lost, the chunk writer
could not distinguish NetConnection commands from NetStream commands.

---------

Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
2026-08-17 17:59:45 -04:00
Winlin
ee6cb4390b GB28181: Clean up external SIP sessions after timeout or TCP disconnect. v8.0.22 (#4721)
## Summary

Fixes #4719.

For deployments using an external SIP server, `POST /gb/v1/publish/`
reserves a stream ID and SSRC. SRS previously retained that session
after the active RTP/PS-over-TCP publisher disconnected, so publishing
the same ID or SSRC again returned `StreamBusy`. API-created sessions
that never established a media connection could also remain reserved.

This PR:

- ends the GB28181 session when its active media TCP connection closes,
releasing the ID, SSRC, and associated media resources;
- ignores disconnects from stale transports after the session has
switched to another connection;
- expires API-created sessions that do not bind a media connection
within the media-connect timeout;
- adds reusable external-SIP session/publisher helpers and focused
timeout and TCP-disconnect E2E tests;
- sends valid RTP/PS with ADTS AAC from the mock publisher; and
- documents the required C++ media-server maintenance verification
workflow.

The source branch also contains a small cleanup of repository
synchronization helper scripts.

## Verification

- `skills/srs-develop/scripts/gb28181-api-timeout-test.sh`
- `skills/srs-develop/scripts/gb28181-tcp-disconnect-test.sh`
- `./objs/srs_utest` — 2,252 tests passed

---------

Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
2026-08-13 22:06:44 -04:00
Alfredo Tupone
7790390263 StateThreads: Set Non-Executable Stack. v8.0.21 (#4679)
Inform the assembler that we don't need an executable stack

SRS works normally without this change, so this is not a functional bug fix. It is a Linux security-hardening improvement.

SRS includes hand-written State Threads assembly. Without a .note.GNU-stack declaration, the linker may assume that these assembly objects require an executable process stack. This can produce linker or packaging warnings and may cause the final SRS binary to have an executable stack.

The assembly code does not execute code from the stack, so marking it as non-executable is correct. This restores an important defense-in-depth mitigation: if SRS ever has a memory-corruption vulnerability, an attacker should not be allowed to execute injected code directly from the stack.

Therefore, this PR is worth merging because it:
* improves the security properties of the Linux binary;
* avoids executable-stack warnings from modern toolchains and distributions;
* accurately describes the requirements of the assembly code;
* has no expected effect on normal SRS behavior or performance.

This PR does not fix a known exploitable vulnerability, but it removes an unnecessary security weakness with a small and low-risk change.

---------

Co-authored-by: Winlin <winlinvip@gmail.com>
Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
2026-08-13 09:41:50 -04:00
Winlin
326b5fa817 Proxy: Preserve HTTP response headers and safely rewrite HLS. v8.0.20 (#4718)
## Summary

- Preserve end-to-end backend response headers for HTTP-FLV, HTTP-TS,
and HLS responses.
- Remove standard hop-by-hop headers and additional fields nominated by
`Connection`.
- Rewrite HLS segment URLs with a canonical single `&` when the original
URL already has query parameters.
- Repair response metadata after modifying an m3u8 body, and add
wire-level regression coverage.

## Problem

The HTTP proxy called `WriteHeader` before copying headers from the
backend. Go commits the response at that point, so later additions such
as `Content-Type`, `Cache-Control`, `ETag`, `Last-Modified`, `Vary`, and
custom origin metadata never reached the downstream client.

HLS playlist rewriting also generated segment URLs containing an
unnecessary empty query component:

```text
segment.ts?spbhid=xxx&&token=abc
```

Moving header copying before `WriteHeader` is not sufficient by itself.
An m3u8 playlist is modified when the proxy inserts `spbhid`, so the
backend's original `Content-Length`, strong `ETag`, digest, content
encoding, and range metadata no longer describe the downstream
representation. Forwarding the stale length can make Go reject the
enlarged response with `http: wrote more than the declared
Content-Length`.

A proxy must also avoid forwarding connection-specific headers to the
next hop.

## Changes

- Copy backend end-to-end headers before committing HTTP-FLV, HTTP-TS,
and HLS responses.
- Strip `Connection`, its nominated fields, and the standard
proxy/connection-specific header set.
- Keep byte-transparent FLV and TS representation metadata unchanged.
- Read and rewrite m3u8 playlists before sending response headers.
- Recompute `Content-Length` and remove stale validators, digests,
encodings, and range metadata when the playlist body changes.
- Generate existing-query segment URLs as:

  ```text
  segment.ts?spbhid=xxx&token=abc
  ```

- Add real HTTP-boundary tests for header preservation, hop-by-hop
removal, query rewriting, complete playlist delivery, and rewritten-body
metadata.

---------

Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
2026-08-12 22:17:39 -04:00
Winlin
b666bd6ff7 Codex: Correct RTC frame statistics. v8.0.19 (#4717)
Count primary media-frame observations instead of every RTP packet,
exclude RTX and FEC traffic, and expose stream lifetime for FPS
calculation. Add regression coverage for frame classification and the
stream alive field.
    
---------
    
Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
2026-08-12 20:19:27 -04:00
Winlin
141d79d53c JSON: Update vendored json-parser library. v8.0.18 (#4716)
## Summary

- Update the vendored
[json-parser](https://github.com/json-parser/json-parser) implementation
in `srs_protocol_json.cpp` from the verified upstream revisions for
`json.h` and `json.c`.
- Record the upstream repository, per-file revisions, dates, and
synchronization date so future AI-assisted maintenance can determine
whether another update is needed.
- Scope Clang's deprecated-declaration warning suppression to the
vendored block, preserving the upstream parser code while eliminating
the macOS `sprintf` warnings.
- Improve the local AI setup documentation by clarifying the related
Oryx and Dev Docker repositories and reorganizing the agent guidance.

---------

Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
2026-08-12 18:43:13 -04:00
Winlin
f31ad22dd0 CI: Modernize GitHub Actions. v8.0.17 (#4715)
## Summary

- Upgrade pinned GitHub, Docker, release, and retry actions.
- Upload release assets with the GitHub CLI and configure releases for
SRS 8.0.
- Remove Aliyun image publishing and the legacy server-update jobs while
preserving test and Docker release gating.
- Add the README AI Agent section and list the v7.0-d0 and v6.0-r1
releases.
- Record the verified resolution and FFmpeg 8 limitation for #4410, and
remove the unsupported documentation entry from the development router.

---------

Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
2026-08-12 16:24:53 -04:00
Winlin
64e861c819 feat(skills): expand development workflows and add Oryx support. v8.0.16 (#4714)
## Summary

- Refine the issue Truth Record workflow and add a read-only
pull-request scanning workflow.
- Split development tasks into focused references, add supported routes
for the SRS browser player and Dev Docker, and keep proxy verification
with its owning development workflow.
- Expand the trusted code maps for vendored C++ media dependencies and
clarify the in-process FFmpeg Fit versus external FFmpeg CLI integration
paths.
- Add Oryx documentation routing, a dedicated support workflow and
overview, and FAQ-grounded troubleshooting for deployment, HTTPS,
authentication, persistent data, recording to local or S3-compatible
storage, restreaming, virtual live, cameras, transcoding, and AI
features.

This pull request changes AI skills and their documentation only; it
does not change SRS runtime behavior.

---------

Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
2026-08-11 11:06:47 -04:00
Winlin
1acb366246 Fix graceful disconnect metrics and update issue records. v8.0.15 (#4712)
## Summary

- Fix `srs_clients_errs_total` so disconnects already classified by SRS
as graceful are not counted as client errors.
- Add regression coverage for graceful RTMP/TCP, SRT, and HTTP stream
termination while preserving coverage for genuine client errors.
- Refine the `srs-develop` bug workflow so the detailed GitHub Truth
Record is canonical and the local issue record is added only after
successful publication.
- Add maintainer-approved local Truth Records for #4697, #4621, #4620,
#4617, #4616, and #4611.

## Issue #4609

Before this change, `SrsStatistic::on_disconnect()` incremented the
client error counter for every nonzero internal error. Connection
handling later treated several of those errors as graceful termination,
so normal player exits were successful operationally but recorded as
failures statistically.

This PR excludes the existing client- and server-graceful-close
classifications from `srs_clients_errs_total`:

- `ERROR_SOCKET_READ`
- `ERROR_SOCKET_READ_FULLY`
- `ERROR_SOCKET_WRITE`
- `ERROR_SRT_IO`
- `ERROR_HTTP_STREAM_EOF`

Genuine disconnect errors continue to increment the counter.

The separate report that `srs_clients` retained a historical maximum was
not reproduced and is not changed by this PR. See the [approved Truth
Record](https://github.com/ossrs/srs/issues/4609#issuecomment-5247423361)
for the full reproduction, evidence, and remaining unknowns.

## Verification

- `ReproduceIssue4609.GracefulDisconnectsDoNotIncrementErrors`: passed.
- `StatisticTest.DumpsMetrics`: passed with genuine error codes.
- Full ASAN C++ unit suite: **2,249 tests from 291 suites passed**.
- Runtime verification with one publisher and five normally exiting RTMP
players confirmed that `srs_clients` returned to one,
`srs_clients_total` remained six, and `srs_clients_errs_total` remained
zero.

---------

Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
2026-08-10 20:17:32 -04:00
Winlin
798d351738 HTTP-FLV: Preserve forwarded client IP behind a reverse proxy. v8.0.14 (#4711)
## Summary

Preserve the original HTTP-FLV viewer address supplied by a reverse
proxy instead of replacing it with the proxy's TCP peer address.

## Problem

SRS already parses `X-Forwarded-For` and `X-Real-IP`, but
`SrsLiveStream::serve_http_impl()` unconditionally replaced the parsed
address with `hc->remote_ip()`. As a result, HTTP-FLV client statistics
and the SRS console displayed the reverse proxy address rather than the
viewer address.

## Changes

- Prefer the original address from `X-Forwarded-For` or `X-Real-IP` for
HTTP-FLV viewers.
- Fall back to the TCP peer address when neither header is present.
- Add regression test
`ReproduceIssue4621.PreserveForwardedIpForHttpFlvClient`.

---------

Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
2026-08-10 10:40:37 -04:00
Winlin
e340dfa814 Codex: Fix duplicated RTMP callback parameters. v8.0.13 (#4708)
## Summary

- Fix duplicated RTMP query parameters in HTTP callbacks when SRS parses
the request during connect and again after identifying the publish
stream.
- Make RTMP URL reconstruction idempotent when parameters are already
present in `tcUrl` or repeated in the publish stream.
- Refactor RTMP URL parsing into focused helpers for parameter
normalization, URL reconstruction, and legacy URL normalization.
- Add regression coverage for full-stream `tcUrl`, app-level `tcUrl`,
and parameters present in both RTMP fields.

Fixes #4622.

## Root cause

`SrsRtmpServer::connect_app()` first parsed `tcUrl` and stored its query
in `req->param_`. After `identify_client()` supplied the publish stream,
`SrsRtmpConn::stream_service_cycle()` parsed the request again. The
parser appended the previously extracted parameter back to a `tcUrl`
that already contained it, producing callback values such as:

```text
?token=abc?token=abc
```

## Fix

Normalize parameters before reconstructing the complete RTMP URL. When
`tcUrl` already contains the parameter, do not append it again; when the
publish stream repeats the same parameter, remove the duplicate suffix.
Legacy RTMP query placement and FMLE `/_definst_` paths are then
normalized before parsing with `SrsHttpUri`.

---------

Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
2026-08-10 09:44:08 -04:00
Winlin
7a5f5a81ea RTC2RTMP: Deduplicate AVC and HEVC sequence headers. v8.0.12 (#4706)
## Summary

- Suppress repeated, byte-identical AVC and HEVC sequence headers in the
RTC-to-RTMP bridge.
- Reset sequence-header deduplication state for each publication and
retain state only after successful delivery.
- Prevent unchanged WebRTC parameter sets from marking every open HLS
segment as discontinuous.
- Add H.264 and H.265 regression tests for repeated parameter sets and
update the existing AVC expectation.
- Synchronize the authorized Truth Records for #4623 and #4624.

Fixes #4695.

## Verification

- `make utest -j4`
- `ASAN_OPTIONS=detect_leaks=0 ./objs/srs_utest
--gtest_filter='ReproduceIssue4695.*:RtcFrameBuilderTest.PacketSequenceHeaderAvc_ComprehensiveCoverage'`
— 3 tests passed
- `ASAN_OPTIONS=detect_leaks=0 ./objs/srs_utest` — 2,244 tests passed
- `git diff --check`

The full WHIP-to-HLS workflow was not reproduced locally.

---------

Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
2026-08-09 20:57:53 -04:00
Winlin
82d33a58a9 Fix MP4 DVR timing for repeated DTS samples. v8.0.11 (#4704)
## Summary

- Fix MP4 DVR playback timing when consecutive RTMP H.264 messages have
the same DTS.
- Preserve valid zero-duration MP4 `stts` entries instead of treating
them as uninitialized.
- Add regression coverage for a picture and auxiliary H.264 packets
sharing one timestamp.
- Record the verified Truth Records for issues #4625 through #4629.

## Problem

Some publishers, including the drone stream from #4625, send a picture
plus separate SEI/AUD messages with the same DTS. SRS records each RTMP
video message as an MP4 sample.

The MP4 `stts` builder used `sample_delta_ == 0` to detect an empty
entry. However, zero is also the valid delta between samples sharing the
same DTS. When the next positive delta arrived, SRS replaced the
accumulated zero delta and assigned time to the same-DTS auxiliary
samples.

This produced inconsistent MP4 files: the reporter's header duration was
30.259 seconds, while its accumulated `stts` timeline was 140.099
seconds. VLC consequently listed the file as approximately 30 seconds
but played it for approximately 140 seconds.

## Simplified example

There are two decoded video pictures, but the publisher sends four RTMP
video messages:

| RTMP message | H.264 content | DTS | Decoded picture? |
|---|---|---:|---|
| 1 | IDR picture | 0 ms | Yes |
| 2 | SEI metadata | 0 ms | No |
| 3 | AUD delimiter | 0 ms | No |
| 4 | P-picture | 30 ms | Yes |

SEI and AUD are auxiliary H.264 NAL units, not additional pictures. The
IDR, SEI, and AUD share one timestamp, so only one 30 ms interval passes
before the P-picture:

```text
0 ms                              30 ms
|                                   |
IDR + SEI + AUD                     P-picture
```

The correct timing deltas are effectively:

```text
0 ms, 0 ms, 0 ms, 30 ms
```

The old `stts` builder mistook the valid zero-delta entry for an empty
entry. When the 30 ms delta arrived, it effectively changed the group
to:

```text
30 ms, 30 ms, 30 ms, 30 ms
```

The group therefore consumed about 120 ms instead of 30 ms. Repeating
this error throughout the recording inflated its playback timeline.

## Fix

Use `sample_count_ == 0` to determine whether the current `stts` entry
is empty. Entry state is now independent from the valid `sample_delta_`
value, so zero-duration runs are preserved.

For the regression DTS sequence `9, 9, 9, 9, 39` ms:

- Before: `(1,9), (4,30)` = 129 ms
- After: `(1,9), (3,0), (1,30)` = 39 ms

Fixes #4625.

## Verification

- Targeted ASAN regression test passed:
`ReproduceIssue4625.PreserveZeroDtsInMp4Stts`
- Full ASAN C++ unit suite passed: 2,191 tests from 275 suites
- DVR blackbox tests passed:
  - `TestFast_RtmpPublish_DvrFlv_Basic`
  - `TestFast_RtmpPublish_DvrMp4_Basic`
- Controlled FFmpeg → RTMP → SRS DVR MP4 integration passed
  - Before: header 28.920 seconds; `stts` 115.710 seconds
  - After: header 28.920 seconds; `stts` 28.920 seconds
- `git diff --check` passed

---------

Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
2026-08-09 15:30:38 -04:00
Winlin
8cba52441c RTMP: Clean up publish state after forward backend failures. v8.0.10 (#4703)
## Summary

- Fix #4631 by releasing partially acquired RTMP publish state after a
non-busy acquisition error.
- Preserve an existing publisher when a new session is rejected with
`StreamBusy`.
- Add regression coverage for repeated dynamic-forward backend failures.
- Record the verified Truth Records for #4632, #4633, and #4634.

## Behavior

A dynamic-forward backend error still rejects the current publication.
SRS now cleans up the partial publish state so the same stream can retry
after the backend recovers, without restarting SRS.

The external unpublish hook remains limited to sessions that
successfully acquired the stream and entered the publish lifecycle.

## Verification

- Targeted forward-backend rollback test passed.
- Complete C++ unit suite passed: 2,190 tests from 274 suites.
- Runtime verification passed: HTTP 500 rejected the first publication,
and the same stream published successfully after backend recovery
without restarting SRS.

Fixes #4631

---------

Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
2026-08-07 10:45:22 -04:00
Winlin
2bb5e4428f Fix #4639: Terminate SSRC group SDP lines. v8.0.9 (#4701)
## Summary

Fix #4639 by appending the SDP line terminator after
`SrsSSRCGroup::encode()` writes an `a=ssrc-group:` attribute.

Before this fix, an SSRC group followed by another SDP attribute could
be serialized as one concatenated line, for example:

```text
a=ssrc-group:FID 12345 67890a=ssrc:12345 cname:test-cname
```

This was a narrow encoder bug: other SDP encoders already append
`kCRLF`.

## Changes

- Add the missing `kCRLF` after encoded SSRC group lines.
- Tighten the existing SSRC group unit test to require the exact
CRLF-terminated output.
- Add a regression test for an SSRC group followed by an `a=ssrc:` line.
- Record the verified Truth Record for #4639 in the AI issue records.

## Verification

```bash
make utest -j4
ASAN_OPTIONS=detect_leaks=0 ./objs/srs_utest --gtest_filter='ProtocolSdpTest.SrsSSRCGroupEncode*'
ASAN_OPTIONS=detect_leaks=0 ./objs/srs_utest
```

Results:

- Targeted SSRC group tests passed: 2 tests.
- Full C++ utest suite passed: 2240 tests.

---------

Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
2026-08-07 00:06:10 -04:00
Winlin
9be7486e75 Fix #4642: Reject duplicate WebRTC TCP owners. v8.0.8 (#4699)
The reported explanation that `session_` is never cleared for the
recorded owner is not correct after PR #4083. However, the TCP handshake
still had a real ownership bug: connection B could replace connection A
as the RTC TCP network owner before the uniqueness check ran.

That left A alive with a raw `session_` pointer while teardown only
interrupted B. A could later dereference stale memory at
`session_->tcp()`, matching the reported crash area.

## Fix

The first TCP handshake now claims an empty owner slot. Later handshakes
for the same RTC session fail with `ERROR_RTC_TCP_UNIQUE` before
changing ownership or storing `session_`.

## Regression coverage

Added:

```text
ReproduceIssue4642.RejectSecondTcpConnForSameRtcSession
```

The test verifies:

- A succeeds and becomes owner.
- B fails with `ERROR_RTC_TCP_UNIQUE`.
- A remains owner.
- B never stores the session pointer.

On the unfixed baseline, this test failed because B succeeded, replaced
A, and stored the session pointer.

---------

Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
2026-08-06 23:08:29 -04:00
Winlin
ee0c5cd98a Player: Fix reverse-proxy URLs and refresh issue records. v8.0.7 (#4698)
Summary:

- Fix the browser player's default HTTP-FLV/HLS URL construction to use
the public page protocol and port while preserving explicit target
overrides.
- Keep direct SRS access on port 8080, omit standard HTTP/HTTPS ports,
and support custom reverse-proxy ports.
- Add an AI-owned Node.js regression script for browser URL generation
and route browser-client work through the internal code map.

The regression script passes all six scenarios:

- Direct SRS HTTP server on port 8080
- Public HTTP origin on port 80
- Public HTTPS origin on port 443
- Custom reverse-proxy port
- Explicit protocol change without a port
- Explicit target overrides

---------

Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
2026-08-06 06:48:23 -04:00
Winlin
b6b70164cd Codex: Add configurable proxy origin registration TTL. v8.0.6 (#4694)
Fixes #4647. Add `PROXY_ORIGIN_SERVER_TTL` so operators can tune how
long an origin registration remains healthy instead of relying on the
fixed 300-second lifetime. The default remains `300s`, preserving
existing behavior.

---------

Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
2026-08-03 22:24:06 -04:00
Winlin
7bb0a04343 Fix live source cleanup race before publisher activation. v8.0.5 (#4692)
Prevent the live-source cleanup timer from removing a source while a
publisher owns that stream's publish token but has not activated the
source yet. Fixes #4656.

**Root cause**

A publisher can acquire the stream publish token, fetch live source
**A**, and then yield before `acquire_publish()` marks the source
active. During that interval, cleanup can consider **A** dead and remove
it from the source pool.

The publisher still holds a shared pointer and later publishes into
**A**, but new players fetch or create source **B** for the same stream
URL. Those players create consumers with `active=0` and receive no RTMP
or HTTP-FLV media, while HLS attached to **A** can continue working.

---------

Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
2026-08-03 10:06:43 -04:00
Winlin
c88245e7b0 AI: Refine SRS skills and documentation structure (#4691)
This PR refines how SRS knowledge, documentation, code navigation, and
verification guidance are organized for AI maintainers.

- Introduce internal SRS skills for documentation and code-map routing.
- Move shared code maps and project knowledge out of the legacy
`memory/` structure.
- Bundle trusted C++ and Go proxy documentation with the documentation
skill.
- Route development and support workflows through the smallest relevant
references.
- Centralize Git workflow and AI attribution guidance in the development
skill.
- Remove obsolete memory and documentation symlinks from individual
AI-agent workspaces.
- Update the documentation synchronization script for the new bundled
layout.
- Document SRS 8.0, community support, and the findings for issue #4681.
- Add optional Redis key namespaces for isolating proxy clusters sharing
a Redis database.

The previous structure distributed knowledge across workspace memory,
documentation directories, and individual AI-agent configuration.

The new structure makes SRS knowledge portable and discoverable through
reusable skills. AI agents can first route a task to the correct code
map or documentation reference, then load only the relevant material
instead of broadly searching the repository.

This provides a clearer separation between:

- User support and development workflows.
- Documentation and source-code navigation.
- Server implementation maps and verification guidance.
- Shared project knowledge and agent-specific workspace memory.

---------

Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
2026-08-02 20:55:24 -04:00
Winlin
191aac208b Codex: Improve proxy tooling and maintainer workflows. v8.0.4 (#4689)
Improve the Go proxy’s browser tooling, documentation, shared agent
configuration, and issue-maintenance workflows.

- Support authenticated external WHIP and WHEP endpoints in the browser
players.
- Add a catalog of implemented Go proxy capabilities and current
limitations.
- Clarify heartbeat registration and expiration behavior for multi-proxy
deployments.
- Add shared `.agents` workspace configuration and correct stale
workspace paths.
- Add issue scanning and Truth Record workflows for bug maintenance.
- Update the codebase map for the new proxy feature documentation.

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-26 11:06:16 -04:00
Winlin
0f980d49a6 RTMP: Fix chunk timestamp/basic-header decoding and harden packet unmarshal. v8.0.3 (#4680)
Fixes three RTMP chunk-stream decoding bugs in the proxy and hardens AMF0 command-packet unmarshalling against malformed input, backed by a new protocol unit-test suite.

All changes are confined to the `internal/rtmp` package. No public API, log format, or emitted wire format changes — these are decode-correctness and robustness fixes only.

**3-byte chunk basic header decode (`readBasicHeader`) **

The 3-byte basic-header form (cid 64–65599) was selected by testing `cid == 1` *after* `cid` had already been overwritten with `64 + t`, so it was never detected. Capture the original marker before overwriting and test that instead.

**Extended-timestamp handling (`chunkStream`, `readMessageHeader`)**

- Use the extended timestamp as a delta for fmt=1/2 chunks (and a fmt=3 first chunk continuing them), required when the delta is ≥ `0xffffff`. Timestamp computation is unified into a single post-step: extended timestamp when present, otherwise the 3-byte header delta; fmt=0 absolute, fmt=1/2 accumulated.
- Detect Type-3 chunks that omit the extended timestamp. FMLE/FMS/Flash follow the RTMP 2012 spec and always send it on Type-3 chunks; librtmp/ffmpeg may not. Switched from an unconditional 4-byte read to `Peek` + conditional `Discard`: if the peeked value differs from the stored one on a non-first chunk, those 4 bytes are payload and are left in the reader.
- Split the single `extendedTimestamp` bool into `hasExtendedTimestamp` (bool) and `extendedTimestamp` (the last raw value, used for the detection above).

**Packet unmarshal hardening**
- Add an `advanceBytes(p, n)` helper that bounds-checks each `p = p[field.Size():]` advance, turning a slice-out-of-range panic into a clean error on truncated/untrusted input. Applied in `CallPacket`, `CreateStreamResPacket`, `PublishPacket`, and `PlayPacket`.
- Reset the optional `CommandObject` / `Args` to nil before probing for their presence, so a stale constructor default (e.g. Null) isn't counted by `Size()` and can't overflow a later advance.

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 07:17:32 -04:00
Winlin
8df9410880 Edge: Fix HTTP-FLV 404 and RTMP late-join missing sequence headers. v8.0.2 (#4678)
Two edge-cluster regressions surfaced when validating an RTMP
origin/edge setup. Each is a small, surgical fix in its own commit.

- **HTTP-FLV play on edge always 404'd.**
`SrsHttpStreamServer::assemble()` registered the dynamic matcher only
when the mux cast was `NULL` (inverted guard), so the matcher was never
wired up. On edge the FLV mount is created lazily by the dynamic
matcher, so every HTTP-FLV client got 404. Invert the guard to register
when the mux is valid, mirroring the destructor.
(`trunk/src/app/srs_app_http_stream.cpp`)

- **RTMP players that join an edge stream after the first player fail to
decode.** After v7.0.94 (#4513) stopped creating `SrsOriginHub` on edge,
the `hub_active` gate in `SrsLiveSource::consumer_dumps()` always
evaluated false on edge. That gate guards the dump of cached
`onMetaData` + AVC sequence header + AAC sequence header + GOP cache to
a new consumer. Result: the first player attaches before the edge-pull
starts and gets headers via the live fan-out, but every subsequent
player gets coded payload with no codec config and ffmpeg aborts with
`dimensions not set` / `Could not write header`. Fall back to the meta
cache state when `hub_` is `NULL`, so the dump path runs once the
edge-pull has populated the cache.
(`trunk/src/app/srs_app_rtmp_source.cpp`)

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 10:53:30 -04:00
Winlin
913b773282 Proxy: Fix RTC/SRT reader leak, legacy WHEP unwrap, WHEP perf guide. v8.0.1 (#4676)
- Fix a goroutine leak on the WHEP path: the backend→client reader was
being spawned on every inbound client packet (STUN keepalives + RTCP
feedback), leaking tens of thousands of goroutines under steady-state
load. Now spawned exactly once per connection via `sync.Once` on both
the RTC and SRT proxies. Listener and reader receive buffers are also
reused across iterations.
- Make the legacy SRS `/rtc/v1/play/` and `/rtc/v1/publish/` APIs work
end-to-end through the proxy. Those endpoints wrap the SDP in a JSON
envelope (`{"sdp":"v=0\r\n..."}` where `\r\n` is the literal 2-byte JSON
escape, not real CRLF), so ICE parsing previously absorbed the rest of
the body into the ufrag. Added `unwrapSDPEnvelope` for ICE extraction
and tightened `ParseIceUfragPwd`'s value class to stop at `\`. The bytes
forwarded to the client and the in-body candidate-port rewrite still
operate on the raw envelope.
- Enable `net/http/pprof` endpoints when `GO_PPROF` is set (blank import
in `internal/debug/pprof.go`) and add `docs/perf/proxy-whep.md` walking
through CPU/alloc/heap/goroutine/trace collection and `pprof -base`
before/after diffs for the WHEP workload (1 publisher + N players).
- Tighten `SRTHandshakePacket.UnmarshalBinary` to
`bytes.Clone(ExtraData)` so decoded handshakes kept on the connection
(`handshake0`, `handshake2`) stay valid once the receive buffer is
reused.

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 21:13:21 -04:00