UNPKG

tethr

Version:

Controlls USB-connected cameras, webcam, and smartphone camera from browser

196 lines 6.76 kB
import { parse as parseTimestamp } from 'date-format-parse'; import { times } from 'lodash'; const DEFAULT_BUFFER_SIZE = 2048; /** * DataView-like class that adds automatic read & write offset tracking, string operations, and useful methods for PTP packet encoding/decoding. Uses little-endian as default */ export class PTPDataView { currentByteOffset = 0; currentByteLength = 0; dataView; buffer; constructor(bufferOrSize = DEFAULT_BUFFER_SIZE) { if (typeof bufferOrSize === 'number') { this.buffer = new ArrayBuffer(bufferOrSize); } else { this.buffer = bufferOrSize; } this.dataView = new DataView(this.buffer); } get byteLength() { return this.currentByteLength; } skip(bytes) { if (this.currentByteOffset + bytes > this.buffer.byteLength) { throw new Error('Not enough byteLength to skip'); } this.currentByteOffset += bytes; return this; } goto(offset) { this.currentByteOffset = offset; return this; } readUint8 = () => { const off = this.currentByteOffset; this.currentByteOffset += 1; return this.dataView.getUint8(off); }; readInt8 = () => { const off = this.currentByteOffset; this.currentByteOffset += 1; return this.dataView.getInt8(off); }; readUint16 = () => { const off = this.currentByteOffset; this.currentByteOffset += 2; return this.dataView.getUint16(off, true); }; readInt16 = () => { const off = this.currentByteOffset; this.currentByteOffset += 2; return this.dataView.getInt16(off, true); }; readUint32 = () => { const off = this.currentByteOffset; this.currentByteOffset += 4; return this.dataView.getUint32(off, true); }; readUint64 = () => { const off = this.currentByteOffset; this.currentByteOffset += 8; return this.dataView.getBigUint64(off, true); }; readAsciiString = () => { const start = this.currentByteOffset; let end = start; for (end = start; end < this.buffer.byteLength; end++) { if (this.dataView.getUint8(end) === 0x00) break; } const chunk = this.buffer.slice(start, end); this.currentByteOffset = end + 1; const str = String.fromCharCode(...new Uint8Array(chunk)); return str; }; readFixedUTF16String = () => { const strLen = this.readUint8(); if (strLen === 0) { return ''; } const strBuffer = this.buffer.slice(this.currentByteOffset, this.currentByteOffset + (strLen - 1) * 2); this.currentByteOffset += strLen * 2; const str = String.fromCharCode(...new Uint16Array(strBuffer)); return str; }; readUTF16StringNT = () => { this.skip(2); let str = ''; while (this.currentByteOffset < this.dataView.byteLength) { const charCode = this.readUint16(); if (charCode === 0x0000) break; str += String.fromCharCode(charCode); } return str; }; readDate = () => { const timestamp = this.readFixedUTF16String(); return parseTimestamp(timestamp, 'YYYYMMDDThhmmss'); }; readUint8Array = () => { return this.readArray(this.readUint8); }; readUint16Array = () => { return this.readArray(this.readUint16); }; readInt16Array = () => { return this.readArray(this.readInt16); }; readUint32Array = () => { return this.readArray(this.readUint32); }; peekRest = () => { return this.buffer.slice(this.currentByteOffset); }; readArray = (getFunc) => { const length = this.readUint32(); return times(length, getFunc.bind(this)); }; // Write methods writeUint8(value) { this.resizeBuffer(this.currentByteOffset + 1); this.dataView.setUint8(this.currentByteOffset, value); this.currentByteOffset += 1; this.updateByteLength(); } writeInt8(value) { this.resizeBuffer(this.currentByteOffset + 1); this.dataView.setInt8(this.currentByteOffset, value); this.currentByteOffset += 1; this.updateByteLength(); } writeUint16(value) { this.resizeBuffer(this.currentByteOffset + 2); this.dataView.setUint16(this.currentByteOffset, value, true); this.currentByteOffset += 2; this.updateByteLength(); } writeInt16(value) { this.resizeBuffer(this.currentByteOffset + 2); this.dataView.setInt16(this.currentByteOffset, value, true); this.currentByteOffset += 2; this.updateByteLength(); } writeUint32(value) { this.resizeBuffer(this.currentByteOffset + 4); this.dataView.setUint32(this.currentByteOffset, value, true); this.currentByteOffset += 4; this.updateByteLength(); } writeInt32(value) { this.resizeBuffer(this.currentByteOffset + 4); this.dataView.setInt32(this.currentByteOffset, value, true); this.currentByteOffset += 4; this.updateByteLength(); } writeBigUint64(value) { this.resizeBuffer(this.currentByteOffset + 8); this.dataView.setBigUint64(this.currentByteOffset, value, true); this.currentByteOffset += 8; this.updateByteLength(); } writeBigInt64(value) { this.resizeBuffer(this.currentByteOffset + 8); this.dataView.setBigInt64(this.currentByteOffset, value, true); this.currentByteOffset += 8; this.updateByteLength(); } writeCheckSum() { // CheckSum8 Modulo 256 const array = new Uint8Array(this.toBuffer()); const checksum = array.reduce((a, b) => (a + b) % 0xff, 0); this.goto(this.currentByteLength); this.writeUint8(checksum); } toBuffer() { return this.buffer.slice(0, this.currentByteLength); } updateByteLength() { this.currentByteLength = Math.max(this.currentByteLength, this.currentByteOffset); } resizeBuffer(byteLength) { if (byteLength <= this.buffer.byteLength) { return; } const oldBufferArray = new Uint8Array(this.buffer); // Reallocate buffer then initialize dataView this.buffer = new ArrayBuffer(byteLength); this.dataView = new DataView(this.buffer); // Copy all existing data to newly allocated buffer const newBufferArray = new Uint8Array(this.buffer); newBufferArray.set(oldBufferArray, 0); } } //# sourceMappingURL=PTPDataView.js.map