Rewrite mpegts producer and consumer
This commit is contained in:
@@ -53,3 +53,20 @@ func ConfigToCodec(conf []byte) *core.Codec {
|
||||
|
||||
return codec
|
||||
}
|
||||
|
||||
func DecodeConfig(b []byte) (objType, sampleFreqIdx, channels byte, sampleRate uint32) {
|
||||
rd := bits.NewReader(b)
|
||||
|
||||
objType = rd.ReadBits8(5)
|
||||
if objType == 0b11111 {
|
||||
objType = 32 + rd.ReadBits8(6)
|
||||
}
|
||||
|
||||
sampleFreqIdx = rd.ReadBits8(4)
|
||||
if sampleFreqIdx == 0b1111 {
|
||||
sampleRate = rd.ReadBits(24)
|
||||
}
|
||||
|
||||
channels = rd.ReadBits8(4)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -57,5 +57,45 @@ func ADTSToCodec(b []byte) *core.Codec {
|
||||
|
||||
func ReadADTSSize(b []byte) uint16 {
|
||||
// AAAAAAAA AAAABCCD EEFFFFGH HHIJKLMM MMMMMMMM MMMOOOOO OOOOOOPP (QQQQQQQQ QQQQQQQQ)
|
||||
_ = b[5] // bounds
|
||||
return uint16(b[3]&0x03)<<(8+3) | uint16(b[4])<<3 | uint16(b[5]>>5)
|
||||
}
|
||||
|
||||
func WriteADTSSize(b []byte, size uint16) {
|
||||
// AAAAAAAA AAAABCCD EEFFFFGH HHIJKLMM MMMMMMMM MMMOOOOO OOOOOOPP (QQQQQQQQ QQQQQQQQ)
|
||||
_ = b[5] // bounds
|
||||
b[3] |= byte(size >> (8 + 3))
|
||||
b[4] = byte(size >> 3)
|
||||
b[5] |= byte(size << 5)
|
||||
return
|
||||
}
|
||||
|
||||
func CodecToADTS(codec *core.Codec) []byte {
|
||||
s := core.Between(codec.FmtpLine, "config=", ";")
|
||||
conf, err := hex.DecodeString(s)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
objType, sampleFreqIdx, channels, _ := DecodeConfig(conf)
|
||||
profile := objType - 1
|
||||
|
||||
wr := bits.NewWriter(nil)
|
||||
wr.WriteAllBits(1, 12) // Syncword, all bits must be set to 1
|
||||
wr.WriteBit(0) // MPEG Version, set to 0 for MPEG-4 and 1 for MPEG-2
|
||||
wr.WriteBits8(0, 2) // Layer, always set to 0
|
||||
wr.WriteBit(1) // Protection absence, set to 1 if there is no CRC and 0 if there is CRC
|
||||
wr.WriteBits8(profile, 2) // Profile, the MPEG-4 Audio Object Type minus 1
|
||||
wr.WriteBits8(sampleFreqIdx, 4) // MPEG-4 Sampling Frequency Index
|
||||
wr.WriteBit(0) // Private bit, guaranteed never to be used by MPEG, set to 0 when encoding, ignore when decoding
|
||||
wr.WriteBits8(channels, 3) // MPEG-4 Channel Configuration
|
||||
wr.WriteBit(0) // Originality, set to 1 to signal originality of the audio and 0 otherwise
|
||||
wr.WriteBit(0) // Home, set to 1 to signal home usage of the audio and 0 otherwise
|
||||
wr.WriteBit(0) // Copyright ID bit
|
||||
wr.WriteBit(0) // Copyright ID start
|
||||
wr.WriteBits16(0, 13) // Frame length
|
||||
wr.WriteAllBits(1, 11) // Buffer fullness (variable bitrate)
|
||||
wr.WriteBits8(0, 2) // Number of AAC frames (Raw Data Blocks) in ADTS frame minus 1
|
||||
|
||||
return wr.Bytes()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package aac
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestADTS(t *testing.T) {
|
||||
// FFmpeg MPEG-TS AAC (one packet)
|
||||
s := "fff15080021ffc210049900219002380fff15080021ffc212049900219002380" //...
|
||||
src, err := hex.DecodeString(s)
|
||||
require.Nil(t, err)
|
||||
|
||||
codec := ADTSToCodec(src)
|
||||
require.Equal(t, uint32(44100), codec.ClockRate)
|
||||
require.Equal(t, uint16(2), codec.Channels)
|
||||
|
||||
size := ReadADTSSize(src)
|
||||
require.Equal(t, uint16(16), size)
|
||||
|
||||
dst := CodecToADTS(codec)
|
||||
WriteADTSSize(dst, size)
|
||||
|
||||
require.Equal(t, src[:len(dst)], dst)
|
||||
}
|
||||
+47
-8
@@ -77,16 +77,55 @@ func RTPPay(handler core.HandlerFunc) core.HandlerFunc {
|
||||
}
|
||||
}
|
||||
|
||||
func ADTStoRTP(b []byte) []byte {
|
||||
header := make([]byte, 2)
|
||||
for i := 0; i < len(b); {
|
||||
auSize := ReadADTSSize(b[i:])
|
||||
header = append(header, byte(auSize>>5), byte(auSize<<3)) // size in bits
|
||||
func ADTStoRTP(src []byte) (dst []byte) {
|
||||
dst = make([]byte, 2) // header bytes
|
||||
for i := 0; i < len(src); {
|
||||
auSize := ReadADTSSize(src[i:])
|
||||
dst = append(dst, byte(auSize>>5), byte(auSize<<3)) // size in bits
|
||||
i += int(auSize)
|
||||
}
|
||||
hdrSize := uint16(len(header) - 2)
|
||||
binary.BigEndian.PutUint16(header, hdrSize<<3) // size in bits
|
||||
return append(header, b...)
|
||||
hdrSize := uint16(len(dst) - 2)
|
||||
binary.BigEndian.PutUint16(dst, hdrSize<<3) // size in bits
|
||||
return append(dst, src...)
|
||||
}
|
||||
|
||||
func RTPTimeSize(b []byte) uint32 {
|
||||
// convert RTP header size to units count
|
||||
units := binary.BigEndian.Uint16(b) >> 4
|
||||
return 1024 * uint32(units)
|
||||
}
|
||||
|
||||
func RTPToADTS(codec *core.Codec, handler core.HandlerFunc) core.HandlerFunc {
|
||||
adts := CodecToADTS(codec)
|
||||
|
||||
return func(packet *rtp.Packet) {
|
||||
src := packet.Payload
|
||||
dst := make([]byte, 0, len(src))
|
||||
|
||||
headersSize := binary.BigEndian.Uint16(src) >> 3
|
||||
headers := src[2 : 2+headersSize]
|
||||
units := src[2+headersSize:]
|
||||
|
||||
for len(headers) > 0 {
|
||||
unitSize := binary.BigEndian.Uint16(headers) >> 3
|
||||
headers = headers[2:]
|
||||
unit := units[:unitSize]
|
||||
units = units[unitSize:]
|
||||
|
||||
if !IsADTS(unit) {
|
||||
i := len(dst)
|
||||
dst = append(dst, adts...)
|
||||
WriteADTSSize(dst[i:], ADTSHeaderSize+uint16(len(unit)))
|
||||
}
|
||||
|
||||
dst = append(dst, unit...)
|
||||
}
|
||||
|
||||
clone := *packet
|
||||
clone.Version = RTPPacketVersionAAC
|
||||
clone.Payload = dst
|
||||
handler(&clone)
|
||||
}
|
||||
}
|
||||
|
||||
func RTPToCodec(b []byte) *core.Codec {
|
||||
|
||||
Reference in New Issue
Block a user