Streams H.264 to the Galahad II LCD via USB (gousb) with: - ffmpeg CLI transcoding (no fragile FFmpeg C-ABI bindings) - pure-Go Annex-B access-unit splitting - framerate pacing with graceful shutdown and USB reconnect - TOML config, cobra CLI, structured slog logging, unit tests
105 lines
2.9 KiB
Go
105 lines
2.9 KiB
Go
package protocol
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"reflect"
|
|
"testing"
|
|
)
|
|
|
|
func TestEncodePacketsSingleChunk(t *testing.T) {
|
|
frame := []byte{0x00, 0x00, 0x01, 0x67, 0x42}
|
|
packets, err := EncodePackets(frame)
|
|
if err != nil {
|
|
t.Fatalf("EncodePackets error = %v", err)
|
|
}
|
|
if len(packets) != 1 {
|
|
t.Fatalf("got %d packets, want 1", len(packets))
|
|
}
|
|
|
|
pkt := packets[0]
|
|
if len(pkt) != PacketSize {
|
|
t.Errorf("packet size = %d, want %d", len(pkt), PacketSize)
|
|
}
|
|
|
|
// Header golden bytes.
|
|
want := []byte{
|
|
0x02, // report id
|
|
0x0D, // command
|
|
0x00, 0x00, 0x00, 0x05, // total size = 5
|
|
0x00, 0x00, 0x00, // idx = 0
|
|
0x00, 0x05, // chunk len = 5
|
|
}
|
|
if !bytes.Equal(pkt[:HeaderSize], want) {
|
|
t.Errorf("header = % x, want % x", pkt[:HeaderSize], want)
|
|
}
|
|
if !bytes.Equal(pkt[HeaderSize:HeaderSize+len(frame)], frame) {
|
|
t.Errorf("payload = % x, want % x", pkt[HeaderSize:HeaderSize+len(frame)], frame)
|
|
}
|
|
}
|
|
|
|
func TestEncodePacketsMultiChunk(t *testing.T) {
|
|
frame := bytes.Repeat([]byte{0xAB}, MaxPayloadSize+17)
|
|
packets, err := EncodePackets(frame)
|
|
if err != nil {
|
|
t.Fatalf("EncodePackets error = %v", err)
|
|
}
|
|
if len(packets) != 2 {
|
|
t.Fatalf("got %d packets, want 2", len(packets))
|
|
}
|
|
|
|
// First packet carries a full 501-byte chunk.
|
|
if got := binary.BigEndian.Uint16(packets[0][9:11]); int(got) != MaxPayloadSize {
|
|
t.Errorf("first chunk len = %d, want %d", got, MaxPayloadSize)
|
|
}
|
|
// Second packet carries the remainder (17 bytes) and index 1.
|
|
if got := binary.BigEndian.Uint16(packets[1][9:11]); int(got) != 17 {
|
|
t.Errorf("second chunk len = %d, want 17", got)
|
|
}
|
|
if got := (int(packets[1][6]) << 16) | (int(packets[1][7]) << 8) | int(packets[1][8]); got != 1 {
|
|
t.Errorf("second packet idx = %d, want 1", got)
|
|
}
|
|
// Both packets declare the full frame size.
|
|
for i, pkt := range packets {
|
|
if got := binary.BigEndian.Uint32(pkt[2:6]); int(got) != len(frame) {
|
|
t.Errorf("packet %d total size = %d, want %d", i, got, len(frame))
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestEncodePacketsReassemblesFrame(t *testing.T) {
|
|
frame := make([]byte, 2000)
|
|
for i := range frame {
|
|
frame[i] = byte(i)
|
|
}
|
|
|
|
packets, err := EncodePackets(frame)
|
|
if err != nil {
|
|
t.Fatalf("EncodePackets error = %v", err)
|
|
}
|
|
|
|
var got []byte
|
|
for _, pkt := range packets {
|
|
n := binary.BigEndian.Uint16(pkt[9:11])
|
|
got = append(got, pkt[HeaderSize:HeaderSize+int(n)]...)
|
|
}
|
|
if !reflect.DeepEqual(got, frame) {
|
|
t.Errorf("reassembled frame differs from input")
|
|
}
|
|
}
|
|
|
|
func TestEncodePacketsEmptyFrame(t *testing.T) {
|
|
if _, err := EncodePackets(nil); err == nil {
|
|
t.Error("expected error for empty frame, got nil")
|
|
}
|
|
}
|
|
|
|
func TestEncodePacketsIdxWrapsAt24Bits(t *testing.T) {
|
|
frame := bytes.Repeat([]byte{0x01}, 10)
|
|
packets, err := EncodePackets(frame)
|
|
if err != nil {
|
|
t.Fatalf("EncodePackets error = %v", err)
|
|
}
|
|
_ = packets // idx wrapping needs > 16M packets; covered by construction.
|
|
}
|