Files
go2rtc/pkg/flv/muxer.go
2024-05-25 19:45:29 +03:00

175 lines
3.5 KiB
Go

package flv
import (
"encoding/binary"
"encoding/hex"
"github.com/AlexxIT/go2rtc/pkg/core"
"github.com/AlexxIT/go2rtc/pkg/flv/amf"
"github.com/AlexxIT/go2rtc/pkg/h264"
"github.com/pion/rtp"
)
type Muxer struct {
codecs []*core.Codec
}
const (
FlagsVideo = 0b001
FlagsAudio = 0b100
)
func (m *Muxer) GetInit() []byte {
b := []byte{
'F', 'L', 'V', // signature
1, // version
0, // flags (has video/audio)
0, 0, 0, 9, // header size
0, 0, 0, 0, // tag 0 size
}
obj := map[string]any{}
for _, codec := range m.codecs {
switch codec.Name {
case core.CodecH264:
b[4] |= FlagsVideo
obj["videocodecid"] = CodecAVC
case core.CodecAAC:
b[4] |= FlagsAudio
obj["audiocodecid"] = CodecAAC
obj["audiosamplerate"] = codec.ClockRate
obj["audiosamplesize"] = 16
obj["stereo"] = codec.Channels == 2
}
}
data := amf.EncodeItems("@setDataFrame", "onMetaData", obj)
b = append(b, EncodeTag(TagData, 0, data)...)
for _, codec := range m.codecs {
switch codec.Name {
case core.CodecH264:
sps, pps := h264.GetParameterSet(codec.FmtpLine)
if len(sps) == 0 {
sps = []byte{0x67, 0x42, 0x00, 0x0a, 0xf8, 0x41, 0xa2}
} else {
h264.FixPixFmt(sps)
}
if len(pps) == 0 {
pps = []byte{0x68, 0xce, 0x38, 0x80}
}
config := h264.EncodeConfig(sps, pps)
video := append(encodeAVData(codec, 0), config...)
b = append(b, EncodeTag(TagVideo, 0, video)...)
case core.CodecAAC:
s := core.Between(codec.FmtpLine, "config=", ";")
config, _ := hex.DecodeString(s)
audio := append(encodeAVData(codec, 0), config...)
b = append(b, EncodeTag(TagAudio, 0, audio)...)
}
}
return b
}
func (m *Muxer) GetPayloader(codec *core.Codec) func(packet *rtp.Packet) []byte {
m.codecs = append(m.codecs, codec)
var ts0 uint32
var k = codec.ClockRate / 1000
switch codec.Name {
case core.CodecH264:
buf := encodeAVData(codec, 1)
return func(packet *rtp.Packet) []byte {
if h264.IsKeyframe(packet.Payload) {
buf[0] = 1<<4 | 7
} else {
buf[0] = 2<<4 | 7
}
buf = append(buf[:5], packet.Payload...) // reset buffer to previous place
if ts0 == 0 {
ts0 = packet.Timestamp
}
timeMS := (packet.Timestamp - ts0) / k
return EncodeTag(TagVideo, timeMS, buf)
}
case core.CodecAAC:
buf := encodeAVData(codec, 1)
return func(packet *rtp.Packet) []byte {
buf = append(buf[:2], packet.Payload...)
if ts0 == 0 {
ts0 = packet.Timestamp
}
timeMS := (packet.Timestamp - ts0) / k
return EncodeTag(TagAudio, timeMS, buf)
}
}
return nil
}
func EncodeTag(tagType byte, timeMS uint32, payload []byte) []byte {
payloadSize := uint32(len(payload))
tagSize := payloadSize + 11
b := make([]byte, tagSize+4)
b[0] = tagType
b[1] = byte(payloadSize >> 16)
b[2] = byte(payloadSize >> 8)
b[3] = byte(payloadSize)
b[4] = byte(timeMS >> 16)
b[5] = byte(timeMS >> 8)
b[6] = byte(timeMS)
b[7] = byte(timeMS >> 24)
copy(b[11:], payload)
binary.BigEndian.PutUint32(b[tagSize:], tagSize)
return b
}
func encodeAVData(codec *core.Codec, isFrame byte) []byte {
switch codec.Name {
case core.CodecH264:
return []byte{
1<<4 | 7, // keyframe + AVC
isFrame, // 0 - config, 1 - frame
0, 0, 0, // composition time = 0
}
case core.CodecAAC:
var b0 byte = 10 << 4 // AAC
switch codec.ClockRate {
case 11025:
b0 |= 1 << 2
case 22050:
b0 |= 2 << 2
case 44100:
b0 |= 3 << 2
}
b0 |= 1 << 1 // 16 bits
if codec.Channels == 2 {
b0 |= 1
}
return []byte{b0, isFrame} // 0 - config, 1 - frame
}
return nil
}