Tags: stackia/rtp2httpd
Tags
feat(config): add relative M3U path option (#575) ## Summary Adds a new `use-relative-path-in-m3u` config option and matching `--use-relative-path-in-m3u` CLI flag. When enabled, generated `playlist.m3u` output and HTTP proxy M3U/HLS rewrites omit the `http://host` prefix and emit root-relative paths, preserving `app-path-prefix` when configured. This also wires the option through OpenWrt UCI/LuCI, LuCI translations, iKuai packaging, sample config files, and the Chinese/English configuration docs. ## Validation - `cmake -B build -DCMAKE_BUILD_TYPE=Release -DENABLE_AGGRESSIVE_OPT=ON` - `cmake --build build -j$(getconf _NPROCESSORS_ONLN)` - `uv run pytest e2e/test_config.py::TestOptionSourcesAndPriority -v -n auto --dist loadscope` - `uv run pytest e2e/test_m3u.py::TestM3UURLRewriting e2e/test_http_proxy_m3u_rewrite.py::TestM3URewriteAbsoluteHTTP -v -n auto --dist loadscope` - `pnpm run docs:build` - `msgfmt -c -o /tmp/rtp2httpd.zh_Hans.mo openwrt-support/luci-app-rtp2httpd/po/zh_Hans/rtp2httpd.po` - `git diff --check`
feat(web-ui): robust MP2 software decode with frame-accurate demux an… …d WSOLA rate matching (#529) ## Summary Fixes audible glitches in MP2 software decode and A/V desync during live-sync playbackRate catch-up. Root causes addressed: - **Frame-accurate demux in WASM**: whole PES payloads are decoded with an internal frame loop + carry buffer, so payloads containing multiple MP2 frames and frames straddling PES boundaries decode correctly (previously only the first frame per payload was decoded). minimp3 switched to float output. - **WSOLA time stretcher** (`wsola.c`, compiled into the same WASM module, instantiated on the main thread): audio genuinely follows `video.playbackRate` with pitch preserved, instead of dropping chunks to catch up. Falls back to passthrough if WASM is unavailable. - **Gapless scheduling chain**: PCM is scheduled back-to-back on the AudioContext clock instead of per-chunk drift placement against `video.currentTime` (whose jitter caused clicks). A 250ms control loop measures drift (EMA) and adjusts the stretch ratio; >250ms drift triggers a hard resync with short fades. - **Exact timeline mapping**: audio timestamps are normalized in the worker using the remuxer dts base (same mapping as video) with sample-count PTS extrapolation, replacing the `buffered.end()` heuristic. No AudioWorklet is used — everything works in non-secure (HTTP) contexts. The iOS Silent Mode bypass and seek buffer behavior are preserved. ## Test plan - [x] WASM smoke test (`test-wasm.mjs`): irregular chunked decode is sample-exact; WSOLA at 1.0/1.2/2.0 produces correct duration ratios with pitch preserved (440Hz tone) - [x] Browser E2E (H.264+MP2 TS): A/V drift stays within ±2ms at 1x; converges within one control period after switching to 1.2x/1.5x with no hard resyncs; seek recovers instantly from buffer - [x] `pnpm run type-check:tsc`, `lint:biome`, `web-ui:build` pass - [x] Verify on iOS Safari over HTTP (Silent Mode bypass path unchanged but untested on device) - [x] Verify against a real IPTV multicast stream via rtp2httpd Made with [Cursor](https://cursor.com) --------- Co-authored-by: Cursor <cursoragent@cursor.com>
fix(web-ui): clamp non-monotonic video sample duration to survive spl… …iced catchup streams (#525) ## Summary - Fixes #448: telco catchup recordings (e.g. Hunan/Jiangsu Telecom) splice segments with overlapping timestamps. A mid-batch video dts regression produced a **negative sample duration**, which underflowed to ~2^32 ms in the trun box, corrupted the MSE buffered range (`0.00 – 4294971.67s` observed) and killed playback with a decode error → black screen after 3 retries. - `_remuxVideo` now clamps non-positive sample durations to the reference frame duration and re-anchors the remaining samples of the batch via `dtsCorrection` — mirroring the existing inter-batch re-anchoring and the audio track's monotonicity enforcement. One integer comparison per sample, no extra parsing. - Also clears `aac_last_incomplete_data_` after consumption (ADTS + LOAS) so a fully-parsed stale buffer is not prepended again on the next PES payload. ## Verification A/B test with an ffmpeg-crafted spliced TS (H.264 + AAC, 2 s timestamp regression at the 6 s splice point), played through `createPlayer` in Chrome: | | buffered range | result | |---|---|---| | before | `0.00 – 4294971.67` (corrupted) | playhead enters no-data zone | | after | `0.00 – 11.96` (single sane range) | plays through to the end | A normal continuous TS stream plays unchanged (regression check). `tsc --noEmit` and `biome check` pass. ## Test plan - [ ] Verify against a real Hunan/Jiangsu Telecom catchup stream (no test stream available locally) - [x] Synthetic spliced TS with 2 s dts regression plays without MediaError - [x] Normal continuous TS stream regression check - [x] type-check + lint Made with [Cursor](https://cursor.com) using Fable 5 --------- Co-authored-by: Cursor <cursoragent@cursor.com>
fix(m3u): preserve catchup seek parameter names (#473) ## Summary Fixes a 3.12.0 catchup rewrite regression where M3U `catchup-source` URLs using a meaningful single range seek parameter such as `tvdr=begin-end` were rewritten to `playseek=begin-end` before reaching the built-in web player. ## Root Cause The URL template refactor introduced a unified `playseek` carrier for all dynamic catchup query templates. That is still needed for path templates and split query templates so the server can resolve placeholders and apply seek offsets, but it was too broad for single range seek parameters that the server already understands or that are explicitly declared with `r2h-seek-name`. ## Changes - Preserve `tvdr=begin-end` in rewritten M3U catchup URLs. - Preserve custom single range seek parameters when the source URL declares the same name via `r2h-seek-name`. - Keep `playseek=begin-end` as the carrier for path templates and unrecognized query template parameters. - Preserve existing RTSP semantics where configured `r2h-seek-name` selects what to consume but does not rename a request-supplied `playseek` parameter. - Strip URL-supplied `r2h-token` from upstream HTTP and RTSP requests, case-insensitively. ## Validation - `git diff --cached --check` - `cmake --build build -j$(getconf _NPROCESSORS_ONLN)` - `./scripts/run-e2e.sh -p 1 test_url_template.py` - `./scripts/run-e2e.sh -p 1 test_rtsp_seek_mode.py` - `./scripts/run-e2e.sh -p 1 test_http_proxy.py` - `./scripts/run-e2e.sh -p 1 test_m3u.py` - `./scripts/run-e2e.sh -p 1 test_auth.py` Fixes #471
PreviousNext