feat: sync group call livestream support
This commit is contained in:
parent
56d995474c
commit
f1a27996d3
37 changed files with 2219 additions and 83 deletions
103
internal/app/livestream/part_test.go
Normal file
103
internal/app/livestream/part_test.go
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
package livestream
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// readTLString 复刻 tgcalls readSerializedString,用于反解 packUnifiedPart 的头。
|
||||
func readTLString(data []byte, off *int) (string, bool) {
|
||||
if *off >= len(data) {
|
||||
return "", false
|
||||
}
|
||||
first := int(data[*off])
|
||||
*off++
|
||||
var length, padding int
|
||||
if first == 254 {
|
||||
if *off+3 > len(data) {
|
||||
return "", false
|
||||
}
|
||||
length = int(data[*off]) | int(data[*off+1])<<8 | int(data[*off+2])<<16
|
||||
*off += 3
|
||||
padding = (4 - length%4) % 4
|
||||
} else {
|
||||
length = first
|
||||
padding = (4 - (length+1)%4) % 4
|
||||
}
|
||||
if *off+length > len(data) {
|
||||
return "", false
|
||||
}
|
||||
s := string(data[*off : *off+length])
|
||||
*off += length + padding
|
||||
return s, true
|
||||
}
|
||||
|
||||
func readI32(data []byte, off *int) (int32, bool) {
|
||||
if *off+4 > len(data) {
|
||||
return 0, false
|
||||
}
|
||||
v := int32(binary.LittleEndian.Uint32(data[*off : *off+4]))
|
||||
*off += 4
|
||||
return v, true
|
||||
}
|
||||
|
||||
// TestPackUnifiedPartMatchesTgcallsHeader 校验打包头逐字段可被 tgcalls
|
||||
// consumeVideoStreamInfo 解出:签名、容器名、activeMask、单 event(endpoint="unified"),
|
||||
// 且 event.offset=0 对应紧随其后的 TS 数据。
|
||||
func TestPackUnifiedPartMatchesTgcallsHeader(t *testing.T) {
|
||||
ts := []byte{0x47, 0x40, 0x00, 0x10, 0xDE, 0xAD, 0xBE, 0xEF} // 伪 TS 数据
|
||||
part := packUnifiedPart(ts)
|
||||
|
||||
off := 0
|
||||
sig, ok := readI32(part, &off)
|
||||
if !ok || uint32(sig) != partSignature {
|
||||
t.Fatalf("signature = %#x ok=%v, want %#x", uint32(sig), ok, partSignature)
|
||||
}
|
||||
container, ok := readTLString(part, &off)
|
||||
if !ok || container != partContainer {
|
||||
t.Fatalf("container = %q ok=%v, want %q", container, ok, partContainer)
|
||||
}
|
||||
activeMask, ok := readI32(part, &off)
|
||||
if !ok || activeMask != 1 {
|
||||
t.Fatalf("activeMask = %d ok=%v, want 1", activeMask, ok)
|
||||
}
|
||||
eventCount, ok := readI32(part, &off)
|
||||
if !ok || eventCount != 1 {
|
||||
t.Fatalf("eventCount = %d ok=%v, want 1", eventCount, ok)
|
||||
}
|
||||
eventOffset, ok := readI32(part, &off)
|
||||
if !ok || eventOffset != 0 {
|
||||
t.Fatalf("event.offset = %d ok=%v, want 0", eventOffset, ok)
|
||||
}
|
||||
endpoint, ok := readTLString(part, &off)
|
||||
if !ok || endpoint != partEndpointID {
|
||||
t.Fatalf("endpoint = %q ok=%v, want %q", endpoint, ok, partEndpointID)
|
||||
}
|
||||
rotation, _ := readI32(part, &off)
|
||||
extra, _ := readI32(part, &off)
|
||||
if rotation != 0 || extra != 0 {
|
||||
t.Fatalf("rotation/extra = %d/%d, want 0/0", rotation, extra)
|
||||
}
|
||||
// 头之后(event.offset=0 起)应为原始 TS 数据。
|
||||
if got := part[off:]; string(got) != string(ts) {
|
||||
t.Fatalf("payload = %x, want %x", got, ts)
|
||||
}
|
||||
}
|
||||
|
||||
// TestAppendTLStringPadding 校验短/长字符串都补齐到 4 字节边界。
|
||||
func TestAppendTLStringPadding(t *testing.T) {
|
||||
for _, s := range []string{"", "a", "ab", "abc", "mpegts", "unified"} {
|
||||
out := appendTLString(nil, s)
|
||||
if len(out)%4 != 0 {
|
||||
t.Fatalf("appendTLString(%q) len=%d not 4-aligned", s, len(out))
|
||||
}
|
||||
off := 0
|
||||
got, ok := readTLString(out, &off)
|
||||
if !ok || got != s {
|
||||
t.Fatalf("roundtrip %q -> %q ok=%v", s, got, ok)
|
||||
}
|
||||
if off != len(out) {
|
||||
t.Fatalf("roundtrip %q consumed %d of %d bytes", s, off, len(out))
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue