### Motivation
- Prevent unbounded recursive-descent parsing in the AMF decoder that
allowed deeply nested AMF objects/arrays to exhaust the process stack
and cause a pre-auth remote DoS.
### Description
- Add an RAII depth guard `AMFDecoder::DepthGuard` and a per-decoder
`depth` counter with `kMaxDepth = 64` to `AMFDecoder` in
`src/Rtmp/amf.h`/`src/Rtmp/amf.cpp`.
- Instrument `load_object()`, `load_ecma()`, and `load_arr()` to create
a `DepthGuard` at entry so nesting is incremented and reliably
decremented on return or exception, and throw
`std::runtime_error("Maximum AMF nesting depth exceeded")` when the
limit is exceeded.
- Add `tests/test_amf.cpp` to assert that a 64-level nested AMF
container is accepted and a 65-level nested container is rejected with
the expected error.
- Keep non-nested parsing behavior unchanged; the guard only enforces a
maximum container nesting depth.
### Testing
- Configured the project with tests enabled using `cmake -S . -B build
-DENABLE_TESTS=ON`, which generated the `test_amf` target successfully.
- Built the test target with `cmake --build build --target test_amf` and
performed smoke compiles of modified sources, with `src/Rtmp/amf.cpp`
compiling to object (`/tmp/amf.o`) successfully.
- Compiled the new test source with `c++ -std=gnu++11 -c
tests/test_amf.cpp` successfully and verified `git diff --cached
--check` passed.
------
[Codex
Task](https://chatgpt.com/codex/cloud/tasks/task_e_6a63c9326b9883209c857c0f4fa5d0cc)
## Summary
This PR fixes GStreamer interoperability issues during WebRTC/WHEP
negotiation with ZLMediaServer.
GStreamer could fail to establish the connection for two separate
reasons:
1. ZLMediaServer generated a non-compliant ICE `ufrag`. The generated
value contained `_`, which is not a valid ICE character, so GStreamer
rejected the SDP.
2. ZLMediaServer did not correctly handle `bundle-only` offers and could
answer an accepted bundled m-line with `port=0`, which caused the later
WHEP negotiation to fail.
## Changes
- Generate ICE `ufrag` values using ICE-compliant characters only.
- Preserve and handle `a=bundle-only` correctly during SDP parsing and
answer generation.
- Return `port=9` instead of `port=0` for accepted bundled m-lines.
- Add regression coverage for `bundle-only` SDP handling.
- URL-encode `delete_webrtc` query parameters in the returned `Location`
header so ICE-safe identifiers remain round-trippable over HTTP.
## Validation
- Built with WebRTC and SCTP enabled.
- Added regression test: `test_webrtc_regression`
- Verified:
- ICE-safe identifier round-trip through `delete_webrtc`
- `bundle-only` SDP answer generation