mirror of
https://github.com/jwetzell/ddp-js.git
synced 2026-08-22 06:49:00 +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 ddp = require('../dist/cjs/index');
|
||||||
|
|
||||||
const goodTests = [
|
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', () => {
|
describe('DDP Message Decoding Pass', () => {
|
||||||
goodTests.forEach((messageTest) => {
|
goodTests.forEach((messageTest) => {
|
||||||
it(messageTest.description, () => {
|
it(messageTest.description, () => {
|
||||||
const decoded = ddp.decode(messageTest.bytes);
|
const decoded = ddp.decode(messageTest.bytes);
|
||||||
|
console.log(decoded.header);
|
||||||
deepEqual(decoded, messageTest.expected);
|
deepEqual(decoded, messageTest.expected);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user