// Package protocol builds the USB packets that the Lian Li Galahad II LCD // expects. It is a pure, dependency-free implementation of the framing used // by the reference Rust/Python drivers. // // Each H.264 frame is split into one or more 512-byte USB packets. Every // packet carries an 11-byte header: // // [0] report_id (0x02) // [1] command (0x0D = send H.264) // [2:6] total payload size (big-endian) // [6:9] packet index, 3 bytes (big-endian, wraps) // [9:11] chunk length in this packet (big-endian) // // followed by up to 501 bytes of frame data and zero padding. package protocol import ( "encoding/binary" "fmt" ) // Wire-level constants shared with the reference implementation. const ( ReportIDVideo = 0x02 CmdSendH264 = 0x0D HeaderSize = 11 PacketSize = 512 MaxPayloadSize = 501 ) // EncodePackets splits frame data into USB packets. It returns an error only // when frame is empty, which would otherwise produce a malformed stream. func EncodePackets(frame []byte) ([][]byte, error) { if len(frame) == 0 { return nil, fmt.Errorf("cannot encode empty frame") } var packets [][]byte for offset, idx := 0, uint32(0); offset < len(frame); offset += MaxPayloadSize { end := min(offset+MaxPayloadSize, len(frame)) chunk := frame[offset:end] pkt := make([]byte, PacketSize) pkt[0] = ReportIDVideo pkt[1] = CmdSendH264 binary.BigEndian.PutUint32(pkt[2:6], uint32(len(frame))) pkt[6] = byte(idx >> 16) pkt[7] = byte(idx >> 8) pkt[8] = byte(idx) binary.BigEndian.PutUint16(pkt[9:11], uint16(len(chunk))) copy(pkt[HeaderSize:HeaderSize+len(chunk)], chunk) packets = append(packets, pkt) idx++ } return packets, nil } func min(a, b int) int { if a < b { return a } return b }