mirror of
https://github.com/jwetzell/ddp-js.git
synced 2026-08-08 16:23:39 +00:00
bare DDP packet decoding
This commit is contained in:
@@ -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)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user