From cb16f68432a425735783e95305a3003ba61b9c37 Mon Sep 17 00:00:00 2001 From: hyesung913 <70494259+hyesung913@users.noreply.github.com> Date: Wed, 3 Sep 2025 22:11:32 +0900 Subject: [PATCH] rtmp: improve control message compatibility with non-standard cameras (#4909) * feat: improve RTMP control message compatibility with non-standard cameras - Add flexible chunk stream ID validation for control messages - Support common non-standard chunk stream IDs (2,3,4,5,6) used by various cameras - Maintain backward compatibility with standard RTMP clients - Fix connection issues with PTZ cameras using chunk stream ID 4 This resolves compatibility issues with IP cameras that don't strictly follow RTMP spec while maintaining full support for standard RTMP implementations. Previously, cameras using non-standard chunk stream IDs would fail with 'unexpected chunk stream ID' errors. Now MediaMTX provides nginx-rtmp level compatibility with real-world camera implementations." * add tests * entirely remove control chunk stream ID validation --------- Co-authored-by: Hyesung.Kim Co-authored-by: aler9 <46489434+aler9@users.noreply.github.com> --- .../protocols/rtmp/message/msg_acknowledge.go | 4 ---- .../protocols/rtmp/message/msg_set_chunk_size.go | 4 ---- .../rtmp/message/msg_set_peer_bandwidth.go | 4 ---- .../rtmp/message/msg_set_window_ack_size.go | 4 ---- .../message/msg_user_control_ping_request.go | 4 ---- .../message/msg_user_control_ping_response.go | 4 ---- .../msg_user_control_set_buffer_length.go | 4 ---- .../message/msg_user_control_stream_begin.go | 4 ---- .../rtmp/message/msg_user_control_stream_dry.go | 4 ---- .../rtmp/message/msg_user_control_stream_eof.go | 4 ---- .../msg_user_control_stream_is_recorded.go | 4 ---- internal/protocols/rtmp/message/reader_test.go | 16 ++++++++++++++++ 12 files changed, 16 insertions(+), 44 deletions(-) diff --git a/internal/protocols/rtmp/message/msg_acknowledge.go b/internal/protocols/rtmp/message/msg_acknowledge.go index de45f9ef..81738b68 100644 --- a/internal/protocols/rtmp/message/msg_acknowledge.go +++ b/internal/protocols/rtmp/message/msg_acknowledge.go @@ -12,10 +12,6 @@ type Acknowledge struct { } func (m *Acknowledge) unmarshal(raw *rawmessage.Message) error { - if raw.ChunkStreamID != ControlChunkStreamID { - return fmt.Errorf("unexpected chunk stream ID") - } - if len(raw.Body) != 4 { return fmt.Errorf("unexpected body size") } diff --git a/internal/protocols/rtmp/message/msg_set_chunk_size.go b/internal/protocols/rtmp/message/msg_set_chunk_size.go index 1abddc18..c322f786 100644 --- a/internal/protocols/rtmp/message/msg_set_chunk_size.go +++ b/internal/protocols/rtmp/message/msg_set_chunk_size.go @@ -12,10 +12,6 @@ type SetChunkSize struct { } func (m *SetChunkSize) unmarshal(raw *rawmessage.Message) error { - if raw.ChunkStreamID != ControlChunkStreamID { - return fmt.Errorf("unexpected chunk stream ID") - } - if len(raw.Body) != 4 { return fmt.Errorf("invalid body size") } diff --git a/internal/protocols/rtmp/message/msg_set_peer_bandwidth.go b/internal/protocols/rtmp/message/msg_set_peer_bandwidth.go index 74af5c5e..5b9dac30 100644 --- a/internal/protocols/rtmp/message/msg_set_peer_bandwidth.go +++ b/internal/protocols/rtmp/message/msg_set_peer_bandwidth.go @@ -13,10 +13,6 @@ type SetPeerBandwidth struct { } func (m *SetPeerBandwidth) unmarshal(raw *rawmessage.Message) error { - if raw.ChunkStreamID != ControlChunkStreamID { - return fmt.Errorf("unexpected chunk stream ID") - } - if len(raw.Body) != 5 { return fmt.Errorf("invalid body size") } diff --git a/internal/protocols/rtmp/message/msg_set_window_ack_size.go b/internal/protocols/rtmp/message/msg_set_window_ack_size.go index 8653260b..6cea3069 100644 --- a/internal/protocols/rtmp/message/msg_set_window_ack_size.go +++ b/internal/protocols/rtmp/message/msg_set_window_ack_size.go @@ -12,10 +12,6 @@ type SetWindowAckSize struct { } func (m *SetWindowAckSize) unmarshal(raw *rawmessage.Message) error { - if raw.ChunkStreamID != ControlChunkStreamID { - return fmt.Errorf("unexpected chunk stream ID") - } - if len(raw.Body) != 4 { return fmt.Errorf("invalid body size") } diff --git a/internal/protocols/rtmp/message/msg_user_control_ping_request.go b/internal/protocols/rtmp/message/msg_user_control_ping_request.go index b44b6181..b8879754 100644 --- a/internal/protocols/rtmp/message/msg_user_control_ping_request.go +++ b/internal/protocols/rtmp/message/msg_user_control_ping_request.go @@ -12,10 +12,6 @@ type UserControlPingRequest struct { } func (m *UserControlPingRequest) unmarshal(raw *rawmessage.Message) error { - if raw.ChunkStreamID != ControlChunkStreamID { - return fmt.Errorf("unexpected chunk stream ID") - } - if len(raw.Body) != 6 { return fmt.Errorf("invalid body size") } diff --git a/internal/protocols/rtmp/message/msg_user_control_ping_response.go b/internal/protocols/rtmp/message/msg_user_control_ping_response.go index c0aa5afd..be5486eb 100644 --- a/internal/protocols/rtmp/message/msg_user_control_ping_response.go +++ b/internal/protocols/rtmp/message/msg_user_control_ping_response.go @@ -12,10 +12,6 @@ type UserControlPingResponse struct { } func (m *UserControlPingResponse) unmarshal(raw *rawmessage.Message) error { - if raw.ChunkStreamID != ControlChunkStreamID { - return fmt.Errorf("unexpected chunk stream ID") - } - if len(raw.Body) != 6 { return fmt.Errorf("invalid body size") } diff --git a/internal/protocols/rtmp/message/msg_user_control_set_buffer_length.go b/internal/protocols/rtmp/message/msg_user_control_set_buffer_length.go index 66184393..5bcb8660 100644 --- a/internal/protocols/rtmp/message/msg_user_control_set_buffer_length.go +++ b/internal/protocols/rtmp/message/msg_user_control_set_buffer_length.go @@ -13,10 +13,6 @@ type UserControlSetBufferLength struct { } func (m *UserControlSetBufferLength) unmarshal(raw *rawmessage.Message) error { - if raw.ChunkStreamID != ControlChunkStreamID { - return fmt.Errorf("unexpected chunk stream ID") - } - if len(raw.Body) != 10 { return fmt.Errorf("invalid body size") } diff --git a/internal/protocols/rtmp/message/msg_user_control_stream_begin.go b/internal/protocols/rtmp/message/msg_user_control_stream_begin.go index f89de161..6cf2c9b2 100644 --- a/internal/protocols/rtmp/message/msg_user_control_stream_begin.go +++ b/internal/protocols/rtmp/message/msg_user_control_stream_begin.go @@ -12,10 +12,6 @@ type UserControlStreamBegin struct { } func (m *UserControlStreamBegin) unmarshal(raw *rawmessage.Message) error { - if raw.ChunkStreamID != ControlChunkStreamID { - return fmt.Errorf("unexpected chunk stream ID") - } - if len(raw.Body) != 6 { return fmt.Errorf("invalid body size") } diff --git a/internal/protocols/rtmp/message/msg_user_control_stream_dry.go b/internal/protocols/rtmp/message/msg_user_control_stream_dry.go index 206c7c85..647d4370 100644 --- a/internal/protocols/rtmp/message/msg_user_control_stream_dry.go +++ b/internal/protocols/rtmp/message/msg_user_control_stream_dry.go @@ -12,10 +12,6 @@ type UserControlStreamDry struct { } func (m *UserControlStreamDry) unmarshal(raw *rawmessage.Message) error { - if raw.ChunkStreamID != ControlChunkStreamID { - return fmt.Errorf("unexpected chunk stream ID") - } - if len(raw.Body) != 6 { return fmt.Errorf("invalid body size") } diff --git a/internal/protocols/rtmp/message/msg_user_control_stream_eof.go b/internal/protocols/rtmp/message/msg_user_control_stream_eof.go index dc7d6174..da5b87fe 100644 --- a/internal/protocols/rtmp/message/msg_user_control_stream_eof.go +++ b/internal/protocols/rtmp/message/msg_user_control_stream_eof.go @@ -12,10 +12,6 @@ type UserControlStreamEOF struct { } func (m *UserControlStreamEOF) unmarshal(raw *rawmessage.Message) error { - if raw.ChunkStreamID != ControlChunkStreamID { - return fmt.Errorf("unexpected chunk stream ID") - } - if len(raw.Body) != 6 { return fmt.Errorf("invalid body size") } diff --git a/internal/protocols/rtmp/message/msg_user_control_stream_is_recorded.go b/internal/protocols/rtmp/message/msg_user_control_stream_is_recorded.go index 79f3274a..0a9d2608 100644 --- a/internal/protocols/rtmp/message/msg_user_control_stream_is_recorded.go +++ b/internal/protocols/rtmp/message/msg_user_control_stream_is_recorded.go @@ -12,10 +12,6 @@ type UserControlStreamIsRecorded struct { } func (m *UserControlStreamIsRecorded) unmarshal(raw *rawmessage.Message) error { - if raw.ChunkStreamID != ControlChunkStreamID { - return fmt.Errorf("unexpected chunk stream ID") - } - if len(raw.Body) != 6 { return fmt.Errorf("invalid body size") } diff --git a/internal/protocols/rtmp/message/reader_test.go b/internal/protocols/rtmp/message/reader_test.go index 28f12589..13089245 100644 --- a/internal/protocols/rtmp/message/reader_test.go +++ b/internal/protocols/rtmp/message/reader_test.go @@ -645,6 +645,22 @@ func TestReader(t *testing.T) { } } +func TestReaderNonStandardControlChunkStreamID(t *testing.T) { + buf := []byte{ + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x8a, 0xce, + } + + bc := bytecounter.NewReader(bytes.NewReader(buf)) + r := NewReader(bc, bc, nil) + dec, err := r.Read() + require.NoError(t, err) + require.Equal(t, &UserControlStreamDry{ + StreamID: 35534, + }, dec) +} + func FuzzReader(f *testing.F) { for _, ca := range readWriterCases { f.Add(ca.enc)