bare DDP packet decoding

This commit is contained in:
2024-12-25 23:14:48 -06:00
parent bf7d020bfc
commit 83a0deab20
2 changed files with 175 additions and 3 deletions
+116
View File
@@ -0,0 +1,116 @@
export enum DDPDataType {
UNDEFINED = 0,
RGB = 1,
HSL = 2,
RGBW = 3,
GRAYSCALE = 4,
}
export enum DDPID {
JSON_CONTROL = 246,
JSON_CONFIG = 250,
JSON_STATUS = 251,
DMX_TRANSIT = 254,
ALL = 255,
}
export type DDPHeader = {
flags: {
version: number;
timecode: boolean;
storage: boolean;
reply: boolean;
query: boolean;
push: boolean;
};
sequenceNumber: number;
dataType: {
standard: boolean;
type: number;
bitsPerPixel: number;
};
sourceOrDestinationID: number;
dataOffset: number;
dataLength: number;
timecode?: {
seconds: number;
fractionalSeconds: number;
};
};
export type DDPPacket = {
header: DDPHeader;
data: Uint8Array;
};
export function decode(bytes: Uint8Array): DDPPacket {
if (bytes.length < 10) {
throw new Error('DDP packet must be at least 10 bytes');
}
const flagsByte = bytes[0];
const flags = {
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,
};
const sequenceNumber = bytes[1] & 0x0f;
const dataTypeByte = bytes[2];
const dataType = {
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 {
throw new Error('unknown DDP size');
}
}
const sourceOrDestinationID = bytes[3];
const dataOffset = (bytes[4] << 24) + (bytes[5] << 16) + (bytes[6] << 8) + bytes[7];
const dataLength = (bytes[8] << 8) + bytes[9];
const header: DDPHeader = {
flags,
sequenceNumber,
dataType,
sourceOrDestinationID,
dataOffset,
dataLength,
};
let dataStart = 10
if (flags.timecode) {
if (bytes.length < 14) {
throw new Error('DDP packet with timecode must be at least 14 bytes');
}
dataStart = 14
header.timecode = {
seconds: (bytes[10] << 8) + bytes[11],
fractionalSeconds: (bytes[12] << 8) + bytes[13],
};
}
return {
header,
data: bytes.subarray(dataStart)
};
}
+59 -3
View File
@@ -3,16 +3,72 @@ const { describe, it } = require('node:test');
const ddp = require('../dist/cjs/index');
const goodTests = [
{
description: 'simple DDP packet',
bytes: new Uint8Array([0x40, 0x0f, 0x92, 0x01, 0x45, 0x67, 0x89, 0x10, 0x10, 0x11]),
expected: {
header: {
flags: {
version: 1,
timecode: false,
storage: false,
reply: false,
query: false,
push: false,
},
sequenceNumber: 15,
dataType: {
standard: false,
type: 2,
bitsPerPixel: 4,
},
sourceOrDestinationID: 1,
dataOffset: 1164413200,
dataLength: 4113,
},
data: new Uint8Array([])
},
},
{
description: 'DDP packet with timecode',
bytes: new Uint8Array([0x50, 0x0f, 0x92, 0x01, 0x45, 0x67, 0x89, 0x10, 0x10, 0x11, 0x10, 0x11, 0x11, 0x10]),
expected: {
header: {
flags: {
version: 1,
timecode: true,
storage: false,
reply: false,
query: false,
push: false,
},
sequenceNumber: 15,
dataType: {
standard: false,
type: 2,
bitsPerPixel: 4,
},
sourceOrDestinationID: 1,
dataOffset: 1164413200,
dataLength: 4113,
timecode: {
seconds: 4113,
fractionalSeconds: 4368,
},
},
data: new Uint8Array([])
},
},
];
const badTests = [
];
const badTests = [];
describe('DDP Message Decoding Pass', () => {
goodTests.forEach((messageTest) => {
it(messageTest.description, () => {
const decoded = ddp.decode(messageTest.bytes);
console.log(decoded.header);
deepEqual(decoded, messageTest.expected);
});
});