Compare commits

..

2 Commits

Author SHA1 Message Date
winlin
133f66afba Edge: Fix HTTP-FLV 404 and RTMP late-join missing sequence headers. v7.0.150 (#4678)
Cherry-pick of the v8.0.2 fix to the 7.0 release line. Two edge-cluster
regressions surface when validating an RTMP origin/edge setup:

- **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 11:20:25 -04:00
Winlin
386a3768df Proxy: Fix RTC/SRT reader leak, legacy WHEP unwrap, WHEP perf guide. v7.0.149 (#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:18:35 -04:00
13 changed files with 65 additions and 1456 deletions

View File

@ -10,7 +10,7 @@
[![](https://img.shields.io/docker/pulls/ossrs/srs)](https://hub.docker.com/r/ossrs/srs/tags) [![](https://img.shields.io/docker/pulls/ossrs/srs)](https://hub.docker.com/r/ossrs/srs/tags)
[![](https://codecov.io/gh/ossrs/srs/graph/badge.svg?token=Zx2LhdtA39)](https://codecov.io/gh/ossrs/srs) [![](https://codecov.io/gh/ossrs/srs/graph/badge.svg?token=Zx2LhdtA39)](https://codecov.io/gh/ossrs/srs)
SRS/8.0 ([Free](https://ossrs.io/lts/en-us/product#release-80)) is a simple, high-efficiency, and real-time video server, SRS/7.0 ([Kai](https://ossrs.io/lts/en-us/product#release-70)) is a simple, high-efficiency, and real-time video server,
supporting RTMP/WebRTC/HLS/HTTP-FLV/SRT/MPEG-DASH/GB28181, Linux/macOS, X86_64/ARMv7/AARCH64/M1/RISCV/LOONGARCH/MIPS, supporting RTMP/WebRTC/HLS/HTTP-FLV/SRT/MPEG-DASH/GB28181, Linux/macOS, X86_64/ARMv7/AARCH64/M1/RISCV/LOONGARCH/MIPS,
with codec support for H.264, H.265, AV1, VP9, AAC, Opus, and G.711, with codec support for H.264, H.265, AV1, VP9, AAC, Opus, and G.711,
and essential [features](trunk/doc/Features.md#features). and essential [features](trunk/doc/Features.md#features).

View File

@ -141,16 +141,7 @@ type chunkStream struct {
header messageHeader header messageHeader
message *message message *message
count uint64 count uint64
extendedTimestamp bool
// Whether the chunk carries an extended timestamp, set when the (delta) timestamp in
// the message header equals 0xffffff. Type-3 continuation chunks inherit this from the
// preceding Type-0/1/2 chunk.
hasExtendedTimestamp bool
// The raw value last read from the extended timestamp field. Kept separately from
// header.Timestamp (the accumulated message timestamp) so we can both detect Type-3
// chunks that omit the extended timestamp and use it as a delta for fmt=1/2 chunks.
// See readMessageHeader.
extendedTimestamp uint32
} }
func newChunkStream() *chunkStream { func newChunkStream() *chunkStream {
@ -549,7 +540,29 @@ func (v *protocol) readMessageHeader(ctx context.Context, chunk *chunkStream, fo
// 0x00ffffff), this value MUST be 16777215, and the 'extended // 0x00ffffff), this value MUST be 16777215, and the 'extended
// timestamp header' MUST be present. Otherwise, this value SHOULD be // timestamp header' MUST be present. Otherwise, this value SHOULD be
// the entire delta. // the entire delta.
chunk.hasExtendedTimestamp = uint64(chunk.header.timestampDelta) >= extendedTimestamp chunk.extendedTimestamp = uint64(chunk.header.timestampDelta) >= extendedTimestamp
if !chunk.extendedTimestamp {
// Extended timestamp: 0 or 4 bytes
// This field MUST be sent when the normal timsestamp is set to
// 0xffffff, it MUST NOT be sent if the normal timestamp is set to
// anything else. So for values less than 0xffffff the normal
// timestamp field SHOULD be used in which case the extended timestamp
// MUST NOT be present. For values greater than or equal to 0xffffff
// the normal timestamp field MUST NOT be used and MUST be set to
// 0xffffff and the extended timestamp MUST be sent.
if format == formatType0 {
// 6.1.2.1. Type 0
// For a type-0 chunk, the absolute timestamp of the message is sent
// here.
chunk.header.Timestamp = uint64(chunk.header.timestampDelta)
} else {
// 6.1.2.2. Type 1
// 6.1.2.3. Type 2
// For a type-1 or type-2 chunk, the difference between the previous
// chunk's timestamp and the current chunk's timestamp is sent here.
chunk.header.Timestamp += uint64(chunk.header.timestampDelta)
}
}
if format <= formatType1 { if format <= formatType1 {
payloadLength := uint32(p[0])<<16 | uint32(p[1])<<8 | uint32(p[2]) payloadLength := uint32(p[0])<<16 | uint32(p[1])<<8 | uint32(p[2])
@ -572,58 +585,27 @@ func (v *protocol) readMessageHeader(ctx context.Context, chunk *chunkStream, fo
p = p[4:] p = p[4:]
} }
} }
} else {
// Update the timestamp even fmt=3 for first chunk packet
if isFirstChunkOfMsg && !chunk.extendedTimestamp {
chunk.header.Timestamp += uint64(chunk.header.timestampDelta)
}
} }
// Read extended-timestamp, present when the (delta) timestamp in the message header is // Read extended-timestamp
// 0xffffff. Type-3 chunks inherit hasExtendedTimestamp from the preceding chunk. if chunk.extendedTimestamp {
if chunk.hasExtendedTimestamp { var timestamp uint32
// Peek instead of read, so the 4 bytes can be left in place when a sender omits the if err = binary.Read(v.r, binary.BigEndian, &timestamp); err != nil {
// extended timestamp on a Type-3 chunk (see the detection below).
var b []byte
if b, err = v.r.Peek(4); err != nil {
return errors.Wrapf(err, "read ext-ts, pkt-ts=%v", chunk.header.Timestamp) return errors.Wrapf(err, "read ext-ts, pkt-ts=%v", chunk.header.Timestamp)
} }
// We always use 31bits timestamp, for some server may use 32bits extended timestamp. // We always use 31bits timestamp, for some server may use 32bits extended timestamp.
// @see https://github.com/ossrs/srs/issues/111 // @see https://github.com/ossrs/srs/issues/111
timestamp := binary.BigEndian.Uint32(b) & 0x7fffffff timestamp &= 0x7fffffff
// For the RTMP v1 2009 version (6.1.3. Extended Timestamp), Type 3 chunks MUST NOT // TODO: FIXME: Support detect the extended timestamp.
// have this field. For the RTMP v1 2012 version (5.3.1.3. Extended Timestamp), it is
// present in Type 3 chunks when the most recent Type 0/1/2 chunk indicated one.
//
// FMLE/FMS/Flash Player follow the 2012 version and always send the extended
// timestamp in Type 3 chunks; librtmp/ffmpeg may not. So detect it: if this is not
// the first chunk of the message and the peeked value differs from the previously
// stored extended timestamp, the sender omitted it and these 4 bytes are payload, so
// leave them in the reader. Otherwise consume and store them.
// @see http://blog.csdn.net/win_lin/article/details/13363699 // @see http://blog.csdn.net/win_lin/article/details/13363699
// @see https://github.com/veovera/enhanced-rtmp/issues/42
if !isFirstChunkOfMsg && chunk.extendedTimestamp > 0 && chunk.extendedTimestamp != timestamp {
// No extended timestamp on this Type-3 chunk; the 4 bytes belong to the payload.
} else {
if _, err = v.r.Discard(4); err != nil {
return errors.Wrapf(err, "discard ext-ts, pkt-ts=%v", chunk.header.Timestamp)
}
chunk.extendedTimestamp = timestamp
}
}
// Compute the message timestamp. The source is the extended timestamp when present,
// otherwise the 3-byte (delta) timestamp from the message header.
//
// fmt=0: the value is the absolute timestamp of the message.
// fmt=1/2 (and a fmt=3 first chunk continuing them): the value is a delta and is
// accumulated onto the previous timestamp. This is required when the delta is >= 0xffffff
// and is therefore carried in the extended timestamp.
timestamp := chunk.header.timestampDelta
if chunk.hasExtendedTimestamp {
timestamp = chunk.extendedTimestamp
}
if format == formatType0 {
chunk.header.Timestamp = uint64(timestamp) chunk.header.Timestamp = uint64(timestamp)
} else if isFirstChunkOfMsg {
chunk.header.Timestamp += uint64(timestamp)
} }
// The extended-timestamp must be unsigned-int, // The extended-timestamp must be unsigned-int,
@ -714,11 +696,6 @@ func (v *protocol) readBasicHeader(ctx context.Context) (format formatType, cid
return return
} }
// Here cid is 0 or 1: a marker selecting the 2B or 3B form, not the real cid. Keep it,
// because cid is overwritten below and the marker decides whether a third byte (the
// high-order part of the cid) follows. Do not test the overwritten cid for this.
marker := cid
// 64-319, 2B chunk header // 64-319, 2B chunk header
if err = binary.Read(v.r, binary.BigEndian, &t); err != nil { if err = binary.Read(v.r, binary.BigEndian, &t); err != nil {
return format, cid, errors.Wrapf(err, "read basic header for cid=%v", cid) return format, cid, errors.Wrapf(err, "read basic header for cid=%v", cid)
@ -726,7 +703,7 @@ func (v *protocol) readBasicHeader(ctx context.Context) (format formatType, cid
cid = chunkID(64 + uint32(t)) cid = chunkID(64 + uint32(t))
// 64-65599, 3B chunk header // 64-65599, 3B chunk header
if marker == 1 { if cid == 1 {
if err = binary.Read(v.r, binary.BigEndian, &t); err != nil { if err = binary.Read(v.r, binary.BigEndian, &t); err != nil {
return format, cid, errors.Wrapf(err, "read basic header for cid=%v", cid) return format, cid, errors.Wrapf(err, "read basic header for cid=%v", cid)
} }
@ -1306,12 +1283,6 @@ func (v *variantCallPacket) UnmarshalBinary(data []byte) (err error) {
} }
p = p[v.TransactionID.Size():] p = p[v.TransactionID.Size():]
// Reset the optional command object before deciding whether it is present.
// A New*Packet constructor may have pre-set it to a default (e.g. Null), but
// when the wire data is exhausted here the object is absent. Leaving the stale
// default would make Size() count bytes that were never parsed, overflowing the
// caller's p = p[Size():] advance on truncated, untrusted input.
v.CommandObject = nil
if len(p) > 0 { if len(p) > 0 {
if v.CommandObject, err = Amf0Discovery(p); err != nil { if v.CommandObject, err = Amf0Discovery(p); err != nil {
return errors.WithMessage(err, "discovery command object") return errors.WithMessage(err, "discovery command object")
@ -1382,32 +1353,14 @@ func (v *CallPacket) Size() int {
return size return size
} }
// advanceBytes returns p[n:] after verifying n lies within p. Packet
// UnmarshalBinary advances its cursor by each embedded field's decoded Size();
// on untrusted wire input a malformed length can make Size() exceed the bytes
// actually present, so this guard turns a slice-out-of-range panic into a clean
// error. See the RTMP test plan, P8 (adversarial resource-safety).
func advanceBytes(p []byte, n int) ([]byte, error) {
if n < 0 || n > len(p) {
return nil, errors.Errorf("advance %v exceeds remaining %v bytes", n, len(p))
}
return p[n:], nil
}
func (v *CallPacket) UnmarshalBinary(data []byte) (err error) { func (v *CallPacket) UnmarshalBinary(data []byte) (err error) {
p := data p := data
if err = v.variantCallPacket.UnmarshalBinary(p); err != nil { if err = v.variantCallPacket.UnmarshalBinary(p); err != nil {
return errors.WithMessage(err, "unmarshal call") return errors.WithMessage(err, "unmarshal call")
} }
if p, err = advanceBytes(p, v.variantCallPacket.Size()); err != nil { p = p[v.variantCallPacket.Size():]
return errors.WithMessage(err, "advance call")
}
// Reset the optional args before deciding whether they are present, for the
// same reason as variantCallPacket.CommandObject: a stale constructor default
// would be counted by Size() and overflow a later advance.
v.Args = nil
if len(p) > 0 { if len(p) > 0 {
if v.Args, err = Amf0Discovery(p); err != nil { if v.Args, err = Amf0Discovery(p); err != nil {
return errors.WithMessage(err, "discovery args") return errors.WithMessage(err, "discovery args")
@ -1483,9 +1436,7 @@ func (v *CreateStreamResPacket) UnmarshalBinary(data []byte) (err error) {
if err = v.variantCallPacket.UnmarshalBinary(p); err != nil { if err = v.variantCallPacket.UnmarshalBinary(p); err != nil {
return errors.WithMessage(err, "unmarshal call") return errors.WithMessage(err, "unmarshal call")
} }
if p, err = advanceBytes(p, v.variantCallPacket.Size()); err != nil { p = p[v.variantCallPacket.Size():]
return errors.WithMessage(err, "advance call")
}
if err = v.StreamID.UnmarshalBinary(p); err != nil { if err = v.StreamID.UnmarshalBinary(p); err != nil {
return errors.WithMessage(err, "unmarshal sid") return errors.WithMessage(err, "unmarshal sid")
@ -1535,9 +1486,7 @@ func (v *PublishPacket) UnmarshalBinary(data []byte) (err error) {
if err = v.variantCallPacket.UnmarshalBinary(p); err != nil { if err = v.variantCallPacket.UnmarshalBinary(p); err != nil {
return errors.WithMessage(err, "unmarshal call") return errors.WithMessage(err, "unmarshal call")
} }
if p, err = advanceBytes(p, v.variantCallPacket.Size()); err != nil { p = p[v.variantCallPacket.Size():]
return errors.WithMessage(err, "advance call")
}
v.StreamName = newAmf0String("") v.StreamName = newAmf0String("")
if err = v.StreamName.UnmarshalBinary(p); err != nil { if err = v.StreamName.UnmarshalBinary(p); err != nil {
@ -1597,9 +1546,7 @@ func (v *PlayPacket) UnmarshalBinary(data []byte) (err error) {
if err = v.variantCallPacket.UnmarshalBinary(p); err != nil { if err = v.variantCallPacket.UnmarshalBinary(p); err != nil {
return errors.WithMessage(err, "unmarshal call") return errors.WithMessage(err, "unmarshal call")
} }
if p, err = advanceBytes(p, v.variantCallPacket.Size()); err != nil { p = p[v.variantCallPacket.Size():]
return errors.WithMessage(err, "advance call")
}
v.StreamName = newAmf0String("") v.StreamName = newAmf0String("")
if err = v.StreamName.UnmarshalBinary(p); err != nil { if err = v.StreamName.UnmarshalBinary(p); err != nil {

File diff suppressed because it is too large Load Diff

View File

@ -6,7 +6,7 @@ package version
import "fmt" import "fmt"
func VersionMajor() int { func VersionMajor() int {
return 8 return 7
} }
// VersionMinor specifies the typical version of SRS we adapt to. // VersionMinor specifies the typical version of SRS we adapt to.
@ -15,7 +15,7 @@ func VersionMinor() int {
} }
func VersionRevision() int { func VersionRevision() int {
return 3 return 150
} }
func Version() string { func Version() string {

View File

@ -9,14 +9,14 @@ import (
) )
func TestVersionComponents(t *testing.T) { func TestVersionComponents(t *testing.T) {
if got := VersionMajor(); got != 8 { if got := VersionMajor(); got != 7 {
t.Fatalf("VersionMajor = %d, want 8", got) t.Fatalf("VersionMajor = %d, want 7", got)
} }
if got := VersionMinor(); got != 0 { if got := VersionMinor(); got != 0 {
t.Fatalf("VersionMinor = %d, want 0", got) t.Fatalf("VersionMinor = %d, want 0", got)
} }
if got := VersionRevision(); got < 0 { if got := VersionRevision(); got <= 0 {
t.Fatalf("VersionRevision = %d, want >= 0", got) t.Fatalf("VersionRevision = %d, want > 0", got)
} }
} }

View File

@ -17,7 +17,7 @@ The C++ media server (`trunk/src/`) serves as both origin server and edge server
`core/` — Foundational definitions: `core/` — Foundational definitions:
- `core` — Core includes, macros, config generated by configure - `core` — Core includes, macros, config generated by configure
- `core_version` through `core_version8` — Version definitions per major release - `core_version` through `core_version7` — Version definitions per major release
- `core_autofree` — SrsUniquePtr smart pointer (RAII) - `core_autofree` — SrsUniquePtr smart pointer (RAII)
- `core_deprecated` — Deprecated SrsAutoFree, kept for compat - `core_deprecated` — Deprecated SrsAutoFree, kept for compat
- `core_performance` — Performance tuning constants (merge-read, etc.) - `core_performance` — Performance tuning constants (merge-read, etc.)
@ -227,7 +227,7 @@ The next-generation server (`cmd/` + `internal/`) is written in Go and maintaine
`internal/env` — Environment-based configuration. All settings via env vars (or `.env` file parsed by an in-tree custom parser — no third-party dep; supports comments, `export` prefix, quoted values, escape sequences, and inline comments). Exposes a `ProxyEnvironment` interface (with a counterfeiter-generated fake in `envfakes/` for downstream tests) with methods for each config value. Default ports: RTMP=11935, HTTP API=11985, HTTP Stream=18080, WebRTC=18000, SRT=20080, System API=12025. Timeouts: grace=20s, force=30s. Supports Redis config and default backend config for debugging. `internal/env` — Environment-based configuration. All settings via env vars (or `.env` file parsed by an in-tree custom parser — no third-party dep; supports comments, `export` prefix, quoted values, escape sequences, and inline comments). Exposes a `ProxyEnvironment` interface (with a counterfeiter-generated fake in `envfakes/` for downstream tests) with methods for each config value. Default ports: RTMP=11935, HTTP API=11985, HTTP Stream=18080, WebRTC=18000, SRT=20080, System API=12025. Timeouts: grace=20s, force=30s. Supports Redis config and default backend config for debugging.
`internal/version` — Version constants. Signature `SRSX`, version tracks the SRS project version (currently 8.0.x). Used in HTTP API responses and startup logging. `internal/version` — Version constants. Signature `SRSX`, version tracks the SRS project version (currently 7.0.x). Used in HTTP API responses and startup logging.
`internal/errors` — Error handling with stack traces, thin wrapper over stdlib `errors`. Provides `New`, `Errorf`, `Wrap`, `Wrapf`, `WithMessage`, `WithStack`, `Cause`, and re-exports `Is`/`As`/`Unwrap`/`Join`. Every error captures a stack trace at creation; `%+v` prints the full trace. `Cause()` walks the error chain to find the root error. `internal/errors` — Error handling with stack traces, thin wrapper over stdlib `errors`. Provides `New`, `Errorf`, `Wrap`, `Wrapf`, `WithMessage`, `WithStack`, `Cause`, and re-exports `Is`/`As`/`Unwrap`/`Join`. Every error captures a stack trace at creation; `%+v` prints the full trace. `Cause()` walks the error chain to find the root error.

View File

@ -77,13 +77,13 @@ Do NOT attempt unsupported tasks.
1. Ask the user for the PR number if they haven't given it. 1. Ask the user for the PR number if they haven't given it.
2. Bump revision by one in **both** version files, keeping them in sync: 2. Bump revision by one in **both** version files, keeping them in sync:
- `internal/version/version.go``VersionRevision()` - `internal/version/version.go``VersionRevision()`
- `trunk/src/core/srs_core_version8.hpp` — `VERSION_REVISION` - `trunk/src/core/srs_core_version7.hpp` — `VERSION_REVISION`
3. Add a new top entry to `trunk/doc/CHANGELOG.md` under `## SRS 8.0 Changelog`, matching the existing format: 3. Add a new top entry to `trunk/doc/CHANGELOG.md` under `## SRS 7.0 Changelog`, matching the existing format:
``` ```
* v8.0, YYYY-MM-DD, Merge [#PR](URL): <Prefix>: <one-line summary>. v8.0.<rev> (#PR) * v7.0, YYYY-MM-DD, Merge [#PR](URL): <Prefix>: <one-line summary>. v7.0.<rev> (#PR)
``` ```
Propose the summary to the user; don't invent one unilaterally. Propose the summary to the user; don't invent one unilaterally.
4. Stop. Let the user review. When they `git add` the version files and changelog, commit with a short message like `Proxy: Bump to v8.0.<rev> for #<PR>.`. 4. Stop. Let the user review. When they `git add` the version files and changelog, commit with a short message like `Proxy: Bump to v7.0.<rev> for #<PR>.`.
--- ---
@ -143,7 +143,7 @@ Only after the user confirms the routing do you proceed to Step 2.
``` ```
bash scripts/proxy-utest.sh --coverage bash scripts/proxy-utest.sh --coverage
``` ```
4. Run **all** of the proxy E2E tests below — every one, not just the first. Run them one at a time (they bind fixed ports, so they cannot run in parallel), and do not stop early: a later test can fail even when the earlier ones pass. 4. Run the proxy E2E tests:
- Single-origin RTMP proxy test (starts proxy + one SRS origin, publishes RTMP, verifies playback): - Single-origin RTMP proxy test (starts proxy + one SRS origin, publishes RTMP, verifies playback):
``` ```
bash scripts/proxy-e2e-test.sh bash scripts/proxy-e2e-test.sh

2
trunk/configure vendored
View File

@ -237,7 +237,7 @@ fi
MODULE_ID="CORE" MODULE_ID="CORE"
MODULE_DEPENDS=() MODULE_DEPENDS=()
ModuleLibIncs=(${SRS_OBJS}) ModuleLibIncs=(${SRS_OBJS})
MODULE_FILES=("srs_core" "srs_core_version" "srs_core_version8" "srs_core_autofree" MODULE_FILES=("srs_core" "srs_core_version" "srs_core_version7" "srs_core_autofree"
"srs_core_time" "srs_core_platform" "srs_core_deprecated" "srs_core_performance") "srs_core_time" "srs_core_platform" "srs_core_deprecated" "srs_core_performance")
CORE_INCS="src/core"; MODULE_DIR=${CORE_INCS} . $SRS_WORKDIR/auto/modules.sh CORE_INCS="src/core"; MODULE_DIR=${CORE_INCS} . $SRS_WORKDIR/auto/modules.sh
CORE_OBJS="${MODULE_OBJS[@]}" CORE_OBJS="${MODULE_OBJS[@]}"

View File

@ -4,17 +4,11 @@
The changelog for SRS. The changelog for SRS.
<a name="v8-changes"></a>
## SRS 8.0 Changelog
* v8.0, 2026-05-28, Merge [#4680](https://github.com/ossrs/srs/pull/4680): RTMP: Fix chunk timestamp/basic-header decoding and harden packet unmarshal. v8.0.3 (#4680)
* v8.0, 2026-05-19, Merge [#4678](https://github.com/ossrs/srs/pull/4678): Edge: Fix HTTP-FLV 404 and RTMP late-join missing sequence headers. v8.0.2 (#4678)
* v8.0, 2026-05-17, Merge [#4676](https://github.com/ossrs/srs/pull/4676): Proxy: Fix RTC/SRT reader goroutine leak; unwrap legacy WHEP JSON envelope; add WHEP pprof guide. v8.0.1 (#4676)
* v8.0, 2026-05-17, Init SRS 8.0, code Free. v8.0.0
<a name="v7-changes"></a> <a name="v7-changes"></a>
## SRS 7.0 Changelog ## SRS 7.0 Changelog
* v7.0, 2026-05-19, Merge [#4678](https://github.com/ossrs/srs/pull/4678): Edge: Fix HTTP-FLV 404 and RTMP late-join missing sequence headers. v7.0.150 (#4678)
* v7.0, 2026-05-17, Merge [#4676](https://github.com/ossrs/srs/pull/4676): Proxy: Fix RTC/SRT reader goroutine leak; unwrap legacy WHEP JSON envelope; add WHEP pprof guide. v7.0.149 (#4676)
* v7.0, 2026-05-17, Merge [#4675](https://github.com/ossrs/srs/pull/4675): Proxy: Refactor for testability; add SRT/WHIP E2E and unit tests. v7.0.148 (#4675) * v7.0, 2026-05-17, Merge [#4675](https://github.com/ossrs/srs/pull/4675): Proxy: Refactor for testability; add SRT/WHIP E2E and unit tests. v7.0.148 (#4675)
* v7.0, 2026-05-02, Merge [#4672](https://github.com/ossrs/srs/pull/4672): Proxy: Refactor server APIs and expand RTMP test coverage. v7.0.147 (#4672) * v7.0, 2026-05-02, Merge [#4672](https://github.com/ossrs/srs/pull/4672): Proxy: Refactor server APIs and expand RTMP test coverage. v7.0.147 (#4672)
* v7.0, 2026-04-28, Merge [#4670](https://github.com/ossrs/srs/pull/4670): Proxy: Refine logger and environment APIs. v7.0.146 (#4670) * v7.0, 2026-04-28, Merge [#4670](https://github.com/ossrs/srs/pull/4670): Proxy: Refine logger and environment APIs. v7.0.146 (#4670)

View File

@ -7,6 +7,6 @@
#ifndef SRS_CORE_VERSION_HPP #ifndef SRS_CORE_VERSION_HPP
#define SRS_CORE_VERSION_HPP #define SRS_CORE_VERSION_HPP
#include <srs_core_version8.hpp> #include <srs_core_version7.hpp>
#endif #endif

View File

@ -9,6 +9,6 @@
#define VERSION_MAJOR 7 #define VERSION_MAJOR 7
#define VERSION_MINOR 0 #define VERSION_MINOR 0
#define VERSION_REVISION 148 #define VERSION_REVISION 150
#endif #endif

View File

@ -1,7 +0,0 @@
//
// Copyright (c) 2013-2026 The SRS Authors
//
// SPDX-License-Identifier: MIT
//
#include <srs_core_version8.hpp>

View File

@ -1,14 +0,0 @@
//
// Copyright (c) 2013-2026 The SRS Authors
//
// SPDX-License-Identifier: MIT
//
#ifndef SRS_CORE_VERSION8_HPP
#define SRS_CORE_VERSION8_HPP
#define VERSION_MAJOR 8
#define VERSION_MINOR 0
#define VERSION_REVISION 3
#endif