Files
2024-12-26 17:40:52 -06:00

185 lines
4.5 KiB
Go

package ddp
import (
"encoding/binary"
"errors"
)
type DDPDataType struct {
Standard bool
Type uint8
BitsPerPixel uint8
}
type DDPHeaderFlags struct {
Version uint8
Timecode bool
Storage bool
Reply bool
Query bool
Push bool
}
type DDPTimecode struct {
Seconds uint16
FractionalSeconds uint16
}
type DDPHeader struct {
Timecode *DDPTimecode
DataOffset uint32
DataLength uint16
Flags DDPHeaderFlags
DataType DDPDataType
SequenceNumber uint8
SourceOrDestinationID uint8
}
type DDPPacket struct {
Header DDPHeader
Data []byte
}
func Decode(bytes []byte) (DDPPacket, error) {
if len(bytes) < 10 {
return DDPPacket{}, errors.New("DDP packet must be at least 10 bytes")
}
flagsByte := bytes[0]
flags := DDPHeaderFlags{
Version: (flagsByte & 0xc0) >> 6,
Timecode: (flagsByte&0x10)>>4 == 1,
Storage: (flagsByte&0x08)>>3 == 1,
Reply: (flagsByte&0x04)>>2 == 1,
Query: (flagsByte&0x02)>>1 == 1,
Push: (flagsByte & 0x01) == 1,
}
sequenceNumber := bytes[1] & 0x0f
dataTypeByte := bytes[2]
dataType := DDPDataType{
Standard: dataTypeByte>>7 == 0,
Type: (dataTypeByte & 0x38) >> 3,
BitsPerPixel: dataTypeByte & 0x07,
}
if dataType.BitsPerPixel > 1 {
if dataType.BitsPerPixel == 2 {
dataType.BitsPerPixel = 4
} else if dataType.BitsPerPixel == 3 {
dataType.BitsPerPixel = 8
} else if dataType.BitsPerPixel == 4 {
dataType.BitsPerPixel = 16
} else if dataType.BitsPerPixel == 5 {
dataType.BitsPerPixel = 24
} else if dataType.BitsPerPixel == 6 {
dataType.BitsPerPixel = 32
} else {
return DDPPacket{}, errors.New("unknown DDP size")
}
}
sourceOrDestinationID := bytes[3]
dataOffset := (uint32(bytes[4]) << 24) + (uint32(bytes[5]) << 16) + (uint32(bytes[6]) << 8) + uint32(bytes[7])
dataLength := (uint16(bytes[8]) << 8) + uint16(bytes[9])
header := DDPHeader{
Flags: flags,
SequenceNumber: sequenceNumber,
DataType: dataType,
SourceOrDestinationID: sourceOrDestinationID,
DataOffset: dataOffset,
DataLength: dataLength,
}
dataStart := 10
if flags.Timecode {
if len(bytes) < 14 {
return DDPPacket{}, errors.New("DDP packet with timecode must be at least 14 bytes")
}
dataStart = 14
header.Timecode = &DDPTimecode{
Seconds: uint16(bytes[10])<<8 + uint16(bytes[11]),
FractionalSeconds: uint16(bytes[12])<<8 + uint16(bytes[13]),
}
}
return DDPPacket{
Header: header,
Data: bytes[dataStart:],
}, nil
}
func Encode(packet DDPPacket) ([]byte, error) {
flagByte := packet.Header.Flags.Version << 6
if packet.Header.Flags.Timecode {
flagByte += 16
}
if packet.Header.Flags.Storage {
flagByte += 8
}
if packet.Header.Flags.Reply {
flagByte += 4
}
if packet.Header.Flags.Query {
flagByte += 2
}
if packet.Header.Flags.Push {
flagByte += 1
}
var dataTypeByte uint8 = 0
if !packet.Header.DataType.Standard {
dataTypeByte += 128
}
dataTypeByte += packet.Header.DataType.Type << 3
if packet.Header.DataType.BitsPerPixel == 1 {
dataTypeByte += 1
} else if packet.Header.DataType.BitsPerPixel == 4 {
dataTypeByte += 2
} else if packet.Header.DataType.BitsPerPixel == 8 {
dataTypeByte += 3
} else if packet.Header.DataType.BitsPerPixel == 16 {
dataTypeByte += 4
} else if packet.Header.DataType.BitsPerPixel == 24 {
dataTypeByte += 5
} else if packet.Header.DataType.BitsPerPixel == 32 {
dataTypeByte += 6
} else if packet.Header.DataType.BitsPerPixel == 0 {
dataTypeByte += 0
} else {
return []byte{}, errors.New("DDP packet has unsupported bits per pixel value")
}
bytes := []byte{}
bytes = append(bytes, flagByte)
bytes = append(bytes, packet.Header.SequenceNumber)
bytes = append(bytes, dataTypeByte)
bytes = append(bytes, packet.Header.SourceOrDestinationID)
bytes = binary.BigEndian.AppendUint32(bytes, packet.Header.DataOffset)
bytes = binary.BigEndian.AppendUint16(bytes, packet.Header.DataLength)
if packet.Header.Flags.Timecode {
if packet.Header.Timecode != nil {
bytes = binary.BigEndian.AppendUint16(bytes, packet.Header.Timecode.Seconds)
bytes = binary.BigEndian.AppendUint16(bytes, packet.Header.Timecode.FractionalSeconds)
} else {
return []byte{}, errors.New("DDP packet had timecode flag set but no timecode value supplied")
}
}
// const bytes = new Uint8Array([flagByte, sequenceNumberByte, dataTypeByte, packet.Header.sourceOrDestinationID])
bytes = append(bytes, packet.Data...)
return bytes, nil
}