mirror of
https://github.com/AlexxIT/go2rtc.git
synced 2025-10-04 07:56:33 +08:00
175 lines
3.5 KiB
Go
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
|
|
}
|