Skip to content

Commit

Permalink
rtmp: support additional Enhanced-RTMP features (#3685)
Browse files Browse the repository at this point in the history
  • Loading branch information
aler9 committed Dec 28, 2024
1 parent df3362a commit b8a84a6
Show file tree
Hide file tree
Showing 32 changed files with 388 additions and 174 deletions.
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Live streams can be published to the server with:
|[WebRTC servers](#webrtc-servers)|WHEP|AV1, VP9, VP8, [H265](#supported-browsers), H264|Opus, G722, G711 (PCMA, PCMU)|
|[RTSP clients](#rtsp-clients)|UDP, TCP, RTSPS|AV1, VP9, VP8, H265, H264, MPEG-4 Video (H263, Xvid), MPEG-1/2 Video, M-JPEG and any RTP-compatible codec|Opus, MPEG-4 Audio (AAC), MPEG-1/2 Audio (MP3), AC-3, G726, G722, G711 (PCMA, PCMU), LPCM and any RTP-compatible codec|
|[RTSP cameras and servers](#rtsp-cameras-and-servers)|UDP, UDP-Multicast, TCP, RTSPS|AV1, VP9, VP8, H265, H264, MPEG-4 Video (H263, Xvid), MPEG-1/2 Video, M-JPEG and any RTP-compatible codec|Opus, MPEG-4 Audio (AAC), MPEG-1/2 Audio (MP3), AC-3, G726, G722, G711 (PCMA, PCMU), LPCM and any RTP-compatible codec|
|[RTMP clients](#rtmp-clients)|RTMP, RTMPS, Enhanced RTMP|AV1, VP9, H265, H264|MPEG-4 Audio (AAC), MPEG-1/2 Audio (MP3), G711 (PCMA, PCMU), LPCM|
|[RTMP clients](#rtmp-clients)|RTMP, RTMPS, Enhanced RTMP|AV1, VP9, H265, H264|Opus, MPEG-4 Audio (AAC), MPEG-1/2 Audio (MP3), G711 (PCMA, PCMU), LPCM|
|[RTMP cameras and servers](#rtmp-cameras-and-servers)|RTMP, RTMPS, Enhanced RTMP|AV1, VP9, H265, H264|MPEG-4 Audio (AAC), MPEG-1/2 Audio (MP3), G711 (PCMA, PCMU), LPCM|
|[HLS cameras and servers](#hls-cameras-and-servers)|Low-Latency HLS, MP4-based HLS, legacy HLS|AV1, VP9, [H265](#supported-browsers-1), H264|Opus, MPEG-4 Audio (AAC)|
|[UDP/MPEG-TS](#udpmpeg-ts)|Unicast, broadcast, multicast|H265, H264, MPEG-4 Video (H263, Xvid), MPEG-1/2 Video|Opus, MPEG-4 Audio (AAC), MPEG-1/2 Audio (MP3), AC-3|
Expand Down Expand Up @@ -2438,9 +2438,10 @@ All the code in this repository is released under the [MIT License](LICENSE). Co
|----|----|
|[RTSP / RTP / RTCP specifications](https://github.com/bluenviron/gortsplib#specifications)|RTSP|
|[HLS specifications](https://github.com/bluenviron/gohlslib#specifications)|HLS|
|[RTMP](https://rtmp.veriskope.com/pdf/rtmp_specification_1.0.pdf)|RTMP|
|[Enhanced RTMP v1](https://veovera.org/docs/enhanced/enhanced-rtmp-v1.pdf)|RTMP|
|[Action Message Format](https://rtmp.veriskope.com/pdf/amf0-file-format-specification.pdf)|RTMP|
|[Action Message Format - AMF 0](https://veovera.org/docs/legacy/amf0-file-format-spec.pdf)|RTMP|
|[FLV](https://veovera.org/docs/legacy/video-file-format-v10-1-spec.pdf)|RTMP|
|[RTMP](https://veovera.org/docs/legacy/rtmp-v1-0-spec.pdf)|RTMP|
|[Enhanced RTMP v2](https://veovera.org/docs/enhanced/enhanced-rtmp-v2.pdf)|RTMP|
|[WebRTC: Real-Time Communication in Browsers](https://www.w3.org/TR/webrtc/)|WebRTC|
|[RFC8835, Transports for WebRTC](https://datatracker.ietf.org/doc/html/rfc8835)|WebRTC|
|[RFC7742, WebRTC Video Processing and Codec Requirements](https://datatracker.ietf.org/doc/html/rfc7742)|WebRTC|
Expand Down
26 changes: 0 additions & 26 deletions internal/protocols/rtmp/message/extended_mpeg2ts_sequence_start.go

This file was deleted.

57 changes: 35 additions & 22 deletions internal/protocols/rtmp/message/message.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,15 @@ const (
TypeSetChunkSize Type = 1
TypeAbortMessage Type = 2
TypeAcknowledge Type = 3
TypeUserControl Type = 4
TypeSetWindowAckSize Type = 5
TypeSetPeerBandwidth Type = 6

TypeUserControl Type = 4

TypeCommandAMF3 Type = 17
TypeCommandAMF0 Type = 20

TypeDataAMF3 Type = 15
TypeDataAMF0 Type = 18

TypeAudio Type = 8
TypeVideo Type = 9
TypeAudio Type = 8
TypeVideo Type = 9
TypeDataAMF3 Type = 15
TypeDataAMF0 Type = 18
TypeCommandAMF3 Type = 17
TypeCommandAMF0 Type = 20
)

// UserControlType is a user control type.
Expand All @@ -47,27 +43,44 @@ const (
UserControlTypePingResponse UserControlType = 7
)

// ExtendedType is a message extended type.
type ExtendedType uint8
// AudioExType is an audio message extended type.
type AudioExType uint8

// message extended types.
// video message extended types.
const (
ExtendedTypeSequenceStart ExtendedType = 0
ExtendedTypeCodedFrames ExtendedType = 1
ExtendedTypeSequenceEnd ExtendedType = 2
ExtendedTypeFramesX ExtendedType = 3
ExtendedTypeMetadata ExtendedType = 4
ExtendedTypeMPEG2TSSequenceStart ExtendedType = 5
AudioExTypeSequenceStart AudioExType = 0
AudioExTypeCodedFrames AudioExType = 1
AudioExTypeSequenceEnd AudioExType = 2
AudioExTypeMultichannelConfig AudioExType = 4
AudioExTypeMultitrack AudioExType = 5
)

// FourCC is an identifier of a video codec.
// VideoExType is a video message extended type.
type VideoExType uint8

// video message extended types.
const (
VideoExTypeSequenceStart VideoExType = 0
VideoExTypeCodedFrames VideoExType = 1
VideoExTypeSequenceEnd VideoExType = 2
VideoExTypeFramesX VideoExType = 3
VideoExTypeMetadata VideoExType = 4
VideoExTypeMPEG2TSSequenceStart VideoExType = 5
VideoExTypeMultitrack VideoExType = 6
)

// FourCC is an identifier of a Extended-RTMP codec.
type FourCC uint32

// video codec identifiers.
// codec identifiers.
var (
// video
FourCCAV1 FourCC = 'a'<<24 | 'v'<<16 | '0'<<8 | '1'
FourCCVP9 FourCC = 'v'<<24 | 'p'<<16 | '0'<<8 | '9'
FourCCHEVC FourCC = 'h'<<24 | 'v'<<16 | 'c'<<8 | '1'

// audio
FourCCOpus FourCC = 'O'<<24 | 'p'<<16 | 'u'<<8 | 's'
)

// Message is a message.
Expand Down
File renamed without changes.
54 changes: 54 additions & 0 deletions internal/protocols/rtmp/message/msg_audio_ex_coded_frames.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package message

import (
"fmt"
"time"

"github.com/bluenviron/mediamtx/internal/protocols/rtmp/rawmessage"
)

// AudioExCodedFrames is a CodedFrames extended message.
type AudioExCodedFrames struct {
ChunkStreamID byte
DTS time.Duration
MessageStreamID uint32
FourCC FourCC
Payload []byte
}

func (m *AudioExCodedFrames) unmarshal(raw *rawmessage.Message) error {
if len(raw.Body) < 6 {
return fmt.Errorf("not enough bytes")
}

m.ChunkStreamID = raw.ChunkStreamID
m.DTS = raw.Timestamp
m.MessageStreamID = raw.MessageStreamID
m.FourCC = FourCC(raw.Body[1])<<24 | FourCC(raw.Body[2])<<16 | FourCC(raw.Body[3])<<8 | FourCC(raw.Body[4])
m.Payload = raw.Body[5:]

return nil
}

func (m AudioExCodedFrames) marshalBodySize() int {
return 5 + len(m.Payload)
}

func (m AudioExCodedFrames) marshal() (*rawmessage.Message, error) {
body := make([]byte, m.marshalBodySize())

body[0] = (9 << 4) | byte(AudioExTypeCodedFrames)
body[1] = uint8(m.FourCC >> 24)
body[2] = uint8(m.FourCC >> 16)
body[3] = uint8(m.FourCC >> 8)
body[4] = uint8(m.FourCC)
copy(body[5:], m.Payload)

return &rawmessage.Message{
ChunkStreamID: m.ChunkStreamID,
Timestamp: m.DTS,
Type: uint8(TypeAudio),
MessageStreamID: m.MessageStreamID,
Body: body,
}, nil
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package message

import (
"fmt"

"github.com/bluenviron/mediamtx/internal/protocols/rtmp/rawmessage"
)

// AudioExChannelOrder is an audio channel order.
type AudioExChannelOrder uint8

// audio channel orders.
const (
AudioExChannelOrderUnspecified AudioExChannelOrder = 0
AudioExChannelOrderNative AudioExChannelOrder = 1
AudioExChannelOrderCustom AudioExChannelOrder = 2
)

// AudioExMultichannelConfig is a sequence start extended message.
type AudioExMultichannelConfig struct {
ChunkStreamID byte
MessageStreamID uint32
FourCC FourCC
AudioChannelOrder AudioExChannelOrder
ChannelCount uint8
AudioChannelMapping uint8 // only for AudioChannelOrder == AudioExChannelOrderCustom
AudioChannelFlags uint32 // only for AudioChannelOrder == AudioExChannelOrderNative
}

func (m *AudioExMultichannelConfig) unmarshal(raw *rawmessage.Message) error {
if len(raw.Body) < 7 {
return fmt.Errorf("not enough bytes")
}

m.ChunkStreamID = raw.ChunkStreamID
m.MessageStreamID = raw.MessageStreamID
m.FourCC = FourCC(raw.Body[1])<<24 | FourCC(raw.Body[2])<<16 | FourCC(raw.Body[3])<<8 | FourCC(raw.Body[4])
m.AudioChannelOrder = AudioExChannelOrder(raw.Body[5])
m.ChannelCount = raw.Body[6]

switch m.AudioChannelOrder {
case AudioExChannelOrderCustom:
if len(raw.Body) != 8 {
return fmt.Errorf("invalid AudioExMultichannelConfig size")
}
m.AudioChannelMapping = raw.Body[7]

case AudioExChannelOrderNative:
if len(raw.Body) != 11 {
return fmt.Errorf("invalid AudioExMultichannelConfig size")
}
m.AudioChannelFlags = uint32(raw.Body[7])<<24 | uint32(raw.Body[8])<<16 |
uint32(raw.Body[9])<<8 | uint32(raw.Body[10])

case AudioExChannelOrderUnspecified:
if len(raw.Body) != 7 {
return fmt.Errorf("invalid AudioExMultichannelConfig size")
}

default:
return fmt.Errorf("invalid AudioChannelOrder")
}

return nil
}

func (m AudioExMultichannelConfig) marshal() (*rawmessage.Message, error) {
return nil, fmt.Errorf("TODO")
}
32 changes: 32 additions & 0 deletions internal/protocols/rtmp/message/msg_audio_ex_sequence_start.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package message

import (
"fmt"

"github.com/bluenviron/mediamtx/internal/protocols/rtmp/rawmessage"
)

// AudioExSequenceStart is a sequence start extended message.
type AudioExSequenceStart struct {
ChunkStreamID byte
MessageStreamID uint32
FourCC FourCC
Config []byte
}

func (m *AudioExSequenceStart) unmarshal(raw *rawmessage.Message) error {
if len(raw.Body) < 6 {
return fmt.Errorf("not enough bytes")
}

m.ChunkStreamID = raw.ChunkStreamID
m.MessageStreamID = raw.MessageStreamID
m.FourCC = FourCC(raw.Body[1])<<24 | FourCC(raw.Body[2])<<16 | FourCC(raw.Body[3])<<8 | FourCC(raw.Body[4])
m.Config = raw.Body[5:]

return nil
}

func (m AudioExSequenceStart) marshal() (*rawmessage.Message, error) {
return nil, fmt.Errorf("TODO")
}
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import (
"github.com/bluenviron/mediamtx/internal/protocols/rtmp/rawmessage"
)

// ExtendedCodedFrames is a CodedFrames extended message.
type ExtendedCodedFrames struct {
// VideoExCodedFrames is a CodedFrames extended message.
type VideoExCodedFrames struct {
ChunkStreamID byte
DTS time.Duration
MessageStreamID uint32
Expand All @@ -17,7 +17,7 @@ type ExtendedCodedFrames struct {
Payload []byte
}

func (m *ExtendedCodedFrames) unmarshal(raw *rawmessage.Message) error {
func (m *VideoExCodedFrames) unmarshal(raw *rawmessage.Message) error {
if len(raw.Body) < 8 {
return fmt.Errorf("not enough bytes")
}
Expand All @@ -37,7 +37,7 @@ func (m *ExtendedCodedFrames) unmarshal(raw *rawmessage.Message) error {
return nil
}

func (m ExtendedCodedFrames) marshalBodySize() int {
func (m VideoExCodedFrames) marshalBodySize() int {
var l int
if m.FourCC == FourCCHEVC {
l = 8 + len(m.Payload)
Expand All @@ -47,10 +47,10 @@ func (m ExtendedCodedFrames) marshalBodySize() int {
return l
}

func (m ExtendedCodedFrames) marshal() (*rawmessage.Message, error) {
func (m VideoExCodedFrames) marshal() (*rawmessage.Message, error) {
body := make([]byte, m.marshalBodySize())

body[0] = 0b10000000 | byte(ExtendedTypeCodedFrames)
body[0] = 0b10000000 | byte(VideoExTypeCodedFrames)
body[1] = uint8(m.FourCC >> 24)
body[2] = uint8(m.FourCC >> 16)
body[3] = uint8(m.FourCC >> 8)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,16 @@ import (
"github.com/bluenviron/mediamtx/internal/protocols/rtmp/rawmessage"
)

// ExtendedFramesX is a FramesX extended message.
type ExtendedFramesX struct {
// VideoExFramesX is a FramesX extended message.
type VideoExFramesX struct {
ChunkStreamID byte
DTS time.Duration
MessageStreamID uint32
FourCC FourCC
Payload []byte
}

func (m *ExtendedFramesX) unmarshal(raw *rawmessage.Message) error {
func (m *VideoExFramesX) unmarshal(raw *rawmessage.Message) error {
if len(raw.Body) < 6 {
return fmt.Errorf("not enough bytes")
}
Expand All @@ -30,14 +30,14 @@ func (m *ExtendedFramesX) unmarshal(raw *rawmessage.Message) error {
return nil
}

func (m ExtendedFramesX) marshalBodySize() int {
func (m VideoExFramesX) marshalBodySize() int {
return 5 + len(m.Payload)
}

func (m ExtendedFramesX) marshal() (*rawmessage.Message, error) {
func (m VideoExFramesX) marshal() (*rawmessage.Message, error) {
body := make([]byte, m.marshalBodySize())

body[0] = 0b10000000 | byte(ExtendedTypeFramesX)
body[0] = 0b10000000 | byte(VideoExTypeFramesX)
body[1] = uint8(m.FourCC >> 24)
body[2] = uint8(m.FourCC >> 16)
body[3] = uint8(m.FourCC >> 8)
Expand Down
Loading

0 comments on commit b8a84a6

Please sign in to comment.