Skip to content

Commit

Permalink
rtmp: support additional Enhanced-RTMP features (#3685)
Browse files Browse the repository at this point in the history
new features:
* Opus and AC-3 tracks
* multiple tracks
  • Loading branch information
aler9 committed Dec 29, 2024
1 parent df3362a commit 200e50e
Show file tree
Hide file tree
Showing 36 changed files with 1,434 additions and 621 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), AC-3, 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.

26 changes: 0 additions & 26 deletions internal/protocols/rtmp/message/extended_sequence_end.go

This file was deleted.

61 changes: 39 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,48 @@ 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.
// audio 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'
FourCCAVC FourCC = 'a'<<24 | 'v'<<16 | 'c'<<8 | '1'

// audio
FourCCOpus FourCC = 'O'<<24 | 'p'<<16 | 'u'<<8 | 's'
FourCCAC3 FourCC = 'a'<<24 | 'c'<<16 | '-'<<8 | '3'
FourCCMP4A FourCC = 'm'<<24 | 'p'<<16 | '4'<<8 | 'a'
FourCCMP3 FourCC = '.'<<24 | 'm'<<16 | 'p'<<8 | '3'
)

// Message is a message.
Expand Down
File renamed without changes.
61 changes: 61 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,61 @@
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) < 5 {
return fmt.Errorf("not enough bytes")
}

Check warning on line 22 in internal/protocols/rtmp/message/msg_audio_ex_coded_frames.go

View check run for this annotation

Codecov / codecov/patch

internal/protocols/rtmp/message/msg_audio_ex_coded_frames.go#L19-L22

Added lines #L19 - L22 were not covered by tests

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])
switch m.FourCC {
case FourCCOpus, FourCCAC3, FourCCMP4A:
default:
return fmt.Errorf("unsupported fourCC: %v", m.FourCC)

Check warning on line 32 in internal/protocols/rtmp/message/msg_audio_ex_coded_frames.go

View check run for this annotation

Codecov / codecov/patch

internal/protocols/rtmp/message/msg_audio_ex_coded_frames.go#L24-L32

Added lines #L24 - L32 were not covered by tests
}

m.Payload = raw.Body[5:]

return nil

Check warning on line 37 in internal/protocols/rtmp/message/msg_audio_ex_coded_frames.go

View check run for this annotation

Codecov / codecov/patch

internal/protocols/rtmp/message/msg_audio_ex_coded_frames.go#L35-L37

Added lines #L35 - L37 were not covered by tests
}

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

Check warning on line 41 in internal/protocols/rtmp/message/msg_audio_ex_coded_frames.go

View check run for this annotation

Codecov / codecov/patch

internal/protocols/rtmp/message/msg_audio_ex_coded_frames.go#L40-L41

Added lines #L40 - L41 were not covered by tests
}

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

Check warning on line 60 in internal/protocols/rtmp/message/msg_audio_ex_coded_frames.go

View check run for this annotation

Codecov / codecov/patch

internal/protocols/rtmp/message/msg_audio_ex_coded_frames.go#L44-L60

Added lines #L44 - L60 were not covered by tests
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
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 multichannel config extended message.
type AudioExMultichannelConfig struct {
ChunkStreamID byte
MessageStreamID uint32
FourCC FourCC
AudioChannelOrder AudioExChannelOrder
ChannelCount uint8
AudioChannelMapping uint8 // if AudioChannelOrder == AudioExChannelOrderCustom
AudioChannelFlags uint32 // if AudioChannelOrder == AudioExChannelOrderNative
}

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

Check warning on line 33 in internal/protocols/rtmp/message/msg_audio_ex_multichannel_config.go

View check run for this annotation

Codecov / codecov/patch

internal/protocols/rtmp/message/msg_audio_ex_multichannel_config.go#L30-L33

Added lines #L30 - L33 were not covered by tests

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])
switch m.FourCC {
case FourCCOpus, FourCCAC3, FourCCMP4A:
default:
return fmt.Errorf("unsupported fourCC: %v", m.FourCC)

Check warning on line 42 in internal/protocols/rtmp/message/msg_audio_ex_multichannel_config.go

View check run for this annotation

Codecov / codecov/patch

internal/protocols/rtmp/message/msg_audio_ex_multichannel_config.go#L35-L42

Added lines #L35 - L42 were not covered by tests
}

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]

Check warning on line 53 in internal/protocols/rtmp/message/msg_audio_ex_multichannel_config.go

View check run for this annotation

Codecov / codecov/patch

internal/protocols/rtmp/message/msg_audio_ex_multichannel_config.go#L45-L53

Added lines #L45 - L53 were not covered by tests

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])

Check warning on line 60 in internal/protocols/rtmp/message/msg_audio_ex_multichannel_config.go

View check run for this annotation

Codecov / codecov/patch

internal/protocols/rtmp/message/msg_audio_ex_multichannel_config.go#L55-L60

Added lines #L55 - L60 were not covered by tests

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

Check warning on line 65 in internal/protocols/rtmp/message/msg_audio_ex_multichannel_config.go

View check run for this annotation

Codecov / codecov/patch

internal/protocols/rtmp/message/msg_audio_ex_multichannel_config.go#L62-L65

Added lines #L62 - L65 were not covered by tests

default:
return fmt.Errorf("invalid AudioChannelOrder: %v", m.AudioChannelOrder)

Check warning on line 68 in internal/protocols/rtmp/message/msg_audio_ex_multichannel_config.go

View check run for this annotation

Codecov / codecov/patch

internal/protocols/rtmp/message/msg_audio_ex_multichannel_config.go#L67-L68

Added lines #L67 - L68 were not covered by tests
}

return nil

Check warning on line 71 in internal/protocols/rtmp/message/msg_audio_ex_multichannel_config.go

View check run for this annotation

Codecov / codecov/patch

internal/protocols/rtmp/message/msg_audio_ex_multichannel_config.go#L71

Added line #L71 was not covered by tests
}

func (m AudioExMultichannelConfig) marshal() (*rawmessage.Message, error) {
return nil, fmt.Errorf("TODO")

Check warning on line 75 in internal/protocols/rtmp/message/msg_audio_ex_multichannel_config.go

View check run for this annotation

Codecov / codecov/patch

internal/protocols/rtmp/message/msg_audio_ex_multichannel_config.go#L74-L75

Added lines #L74 - L75 were not covered by tests
}
78 changes: 78 additions & 0 deletions internal/protocols/rtmp/message/msg_audio_ex_multitrack.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package message //nolint:dupl

import (
"fmt"
"time"

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

// AudioExMultitrackType is a multitrack type.
type AudioExMultitrackType uint8

// multitrack types.
const (
AudioExMultitrackTypeOneTrack AudioExMultitrackType = 0
AudioExMultitrackTypeManyTracks AudioExMultitrackType = 1
AudioExMultitrackTypeManyTracksManyCodecs AudioExMultitrackType = 2
)

// AudioExMultitrack is a multitrack extended message.
type AudioExMultitrack struct {
ChunkStreamID byte
DTS time.Duration
MessageStreamID uint32
MultitrackType AudioExMultitrackType
TrackID uint8
Wrapped Message
}

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

Check warning on line 33 in internal/protocols/rtmp/message/msg_audio_ex_multitrack.go

View check run for this annotation

Codecov / codecov/patch

internal/protocols/rtmp/message/msg_audio_ex_multitrack.go#L30-L33

Added lines #L30 - L33 were not covered by tests

m.ChunkStreamID = raw.ChunkStreamID
m.DTS = raw.Timestamp
m.MessageStreamID = raw.MessageStreamID

m.MultitrackType = AudioExMultitrackType(raw.Body[1] >> 4)
switch m.MultitrackType {
case AudioExMultitrackTypeOneTrack:
default:
return fmt.Errorf("unsupported multitrack type: %v", m.MultitrackType)

Check warning on line 43 in internal/protocols/rtmp/message/msg_audio_ex_multitrack.go

View check run for this annotation

Codecov / codecov/patch

internal/protocols/rtmp/message/msg_audio_ex_multitrack.go#L35-L43

Added lines #L35 - L43 were not covered by tests
}

packetType := AudioExType(raw.Body[1] & 0b1111)
switch packetType {
case AudioExTypeSequenceStart:
m.Wrapped = &AudioExSequenceStart{}

Check warning on line 49 in internal/protocols/rtmp/message/msg_audio_ex_multitrack.go

View check run for this annotation

Codecov / codecov/patch

internal/protocols/rtmp/message/msg_audio_ex_multitrack.go#L46-L49

Added lines #L46 - L49 were not covered by tests

case AudioExTypeMultichannelConfig:
m.Wrapped = &AudioExMultichannelConfig{}

Check warning on line 52 in internal/protocols/rtmp/message/msg_audio_ex_multitrack.go

View check run for this annotation

Codecov / codecov/patch

internal/protocols/rtmp/message/msg_audio_ex_multitrack.go#L51-L52

Added lines #L51 - L52 were not covered by tests

case AudioExTypeCodedFrames:
m.Wrapped = &AudioExCodedFrames{}

Check warning on line 55 in internal/protocols/rtmp/message/msg_audio_ex_multitrack.go

View check run for this annotation

Codecov / codecov/patch

internal/protocols/rtmp/message/msg_audio_ex_multitrack.go#L54-L55

Added lines #L54 - L55 were not covered by tests

default:
return fmt.Errorf("unsupported multitrack packet type: %v", packetType)

Check warning on line 58 in internal/protocols/rtmp/message/msg_audio_ex_multitrack.go

View check run for this annotation

Codecov / codecov/patch

internal/protocols/rtmp/message/msg_audio_ex_multitrack.go#L57-L58

Added lines #L57 - L58 were not covered by tests
}

m.TrackID = raw.Body[6]

wrappedBody := make([]byte, 5+len(raw.Body[7:]))
copy(wrappedBody[1:], raw.Body[2:]) // fourCC
copy(wrappedBody[5:], raw.Body[7:]) // body
err := m.Wrapped.unmarshal(&rawmessage.Message{
Body: wrappedBody,
})
if err != nil {
return err
}

Check warning on line 71 in internal/protocols/rtmp/message/msg_audio_ex_multitrack.go

View check run for this annotation

Codecov / codecov/patch

internal/protocols/rtmp/message/msg_audio_ex_multitrack.go#L61-L71

Added lines #L61 - L71 were not covered by tests

return nil

Check warning on line 73 in internal/protocols/rtmp/message/msg_audio_ex_multitrack.go

View check run for this annotation

Codecov / codecov/patch

internal/protocols/rtmp/message/msg_audio_ex_multitrack.go#L73

Added line #L73 was not covered by tests
}

func (m AudioExMultitrack) marshal() (*rawmessage.Message, error) {
return nil, fmt.Errorf("TODO")

Check warning on line 77 in internal/protocols/rtmp/message/msg_audio_ex_multitrack.go

View check run for this annotation

Codecov / codecov/patch

internal/protocols/rtmp/message/msg_audio_ex_multitrack.go#L76-L77

Added lines #L76 - L77 were not covered by tests
}
Loading

0 comments on commit 200e50e

Please sign in to comment.