mirror of
https://github.com/jwetzell/ddp-go.git
synced 2026-07-26 10:28:42 +00:00
185 lines
4.5 KiB
Go
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
|
|
}
|