feat(gateway): repair native Apollo provider path
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
package gateway
|
||||
|
||||
const (
|
||||
apolloAudioDataShards = 4
|
||||
apolloAudioParityShards = 2
|
||||
apolloAudioTotalShards = apolloAudioDataShards + apolloAudioParityShards
|
||||
apolloAudioMaximumBlocks = 4
|
||||
)
|
||||
|
||||
type apolloAudioFECBlock struct {
|
||||
base uint16
|
||||
timestamp uint32
|
||||
ssrc uint32
|
||||
haveFEC bool
|
||||
size int
|
||||
shards [apolloAudioTotalShards][]byte
|
||||
received [apolloAudioTotalShards]bool
|
||||
count int
|
||||
}
|
||||
|
||||
type apolloAudioAssembler struct {
|
||||
blocks map[uint16]*apolloAudioFECBlock
|
||||
}
|
||||
|
||||
func (a *apolloAudioAssembler) Add(codec *apolloMediaCodec, shard apolloAudioShard) ([][]byte, error) {
|
||||
if codec == nil || len(shard.payload) == 0 || len(shard.payload) > 1408 || len(shard.payload)%16 != 0 {
|
||||
return nil, errApolloMedia
|
||||
}
|
||||
if a.blocks == nil {
|
||||
a.blocks = make(map[uint16]*apolloAudioFECBlock)
|
||||
}
|
||||
base := shard.base
|
||||
if base&3 != 0 {
|
||||
return nil, errApolloMedia
|
||||
}
|
||||
block := a.blocks[base]
|
||||
if block == nil {
|
||||
if len(a.blocks) >= apolloAudioMaximumBlocks {
|
||||
return nil, errApolloMedia
|
||||
}
|
||||
block = &apolloAudioFECBlock{base: base}
|
||||
a.blocks[base] = block
|
||||
}
|
||||
if block.size == 0 {
|
||||
block.size = len(shard.payload)
|
||||
} else if block.size != len(shard.payload) {
|
||||
return nil, errApolloMedia
|
||||
}
|
||||
index := 0
|
||||
if shard.parity {
|
||||
if shard.parityIndex >= apolloAudioParityShards {
|
||||
return nil, errApolloMedia
|
||||
}
|
||||
index = apolloAudioDataShards + int(shard.parityIndex)
|
||||
if block.haveFEC && (block.timestamp != shard.timestamp || block.ssrc != shard.ssrc) {
|
||||
return nil, errApolloMedia
|
||||
}
|
||||
block.timestamp, block.ssrc, block.haveFEC = shard.timestamp, shard.ssrc, true
|
||||
} else {
|
||||
index = int(uint16(shard.sequence - base))
|
||||
if index >= apolloAudioDataShards {
|
||||
return nil, errApolloMedia
|
||||
}
|
||||
if block.haveFEC && (shard.timestamp != block.timestamp+uint32(index*5) || shard.ssrc != block.ssrc) {
|
||||
return nil, errApolloMedia
|
||||
}
|
||||
}
|
||||
if block.received[index] {
|
||||
return nil, errApolloMedia
|
||||
}
|
||||
block.shards[index] = append([]byte(nil), shard.payload...)
|
||||
block.received[index] = true
|
||||
block.count++
|
||||
if block.count < apolloAudioDataShards {
|
||||
return nil, nil
|
||||
}
|
||||
if err := reconstructApolloAudioBlock(block); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
output := make([][]byte, apolloAudioDataShards)
|
||||
for index := range output {
|
||||
payload, err := codec.openApolloAudioCipher(base+uint16(index), block.shards[index])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
output[index] = payload
|
||||
}
|
||||
delete(a.blocks, base)
|
||||
return output, nil
|
||||
}
|
||||
|
||||
func reconstructApolloAudioBlock(block *apolloAudioFECBlock) error {
|
||||
if block == nil || block.count < apolloAudioDataShards || block.size == 0 {
|
||||
return errApolloMedia
|
||||
}
|
||||
missing := false
|
||||
for index := 0; index < apolloAudioDataShards; index++ {
|
||||
if !block.received[index] {
|
||||
missing = true
|
||||
block.shards[index] = make([]byte, block.size)
|
||||
}
|
||||
}
|
||||
if !missing {
|
||||
return nil
|
||||
}
|
||||
if !block.haveFEC {
|
||||
return errApolloMedia
|
||||
}
|
||||
rows := make([][]byte, 0, apolloAudioDataShards)
|
||||
shards := make([][]byte, 0, apolloAudioDataShards)
|
||||
for index, received := range block.received {
|
||||
if !received {
|
||||
continue
|
||||
}
|
||||
rows = append(rows, apolloAudioFECRow(index))
|
||||
shards = append(shards, block.shards[index])
|
||||
if len(rows) == apolloAudioDataShards {
|
||||
break
|
||||
}
|
||||
}
|
||||
inverse, ok := apolloGFInvert(rows)
|
||||
if !ok {
|
||||
return errApolloMedia
|
||||
}
|
||||
for index := 0; index < apolloAudioDataShards; index++ {
|
||||
if block.received[index] {
|
||||
continue
|
||||
}
|
||||
for source, coefficient := range inverse[index] {
|
||||
apolloGFAXPY(block.shards[index], shards[source], coefficient)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func apolloAudioFECRow(index int) []byte {
|
||||
if index < apolloAudioDataShards {
|
||||
row := make([]byte, apolloAudioDataShards)
|
||||
row[index] = 1
|
||||
return row
|
||||
}
|
||||
parity := [8]byte{0x77, 0x40, 0x38, 0x0e, 0xc7, 0xa7, 0x0d, 0x6c}
|
||||
return append([]byte(nil), parity[(index-apolloAudioDataShards)*apolloAudioDataShards:(index-apolloAudioDataShards+1)*apolloAudioDataShards]...)
|
||||
}
|
||||
Reference in New Issue
Block a user