UNPKG

tethr

Version:

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

153 lines 6.21 kB
import { times } from 'lodash'; import { PTPDataView } from './PTPDataView'; export var IFDType; (function (IFDType) { IFDType[IFDType["Byte"] = 1] = "Byte"; IFDType[IFDType["Ascii"] = 2] = "Ascii"; IFDType[IFDType["Short"] = 3] = "Short"; IFDType[IFDType["Long"] = 4] = "Long"; IFDType[IFDType["Rational"] = 5] = "Rational"; IFDType[IFDType["Undefined"] = 7] = "Undefined"; IFDType[IFDType["SignedShort"] = 8] = "SignedShort"; IFDType[IFDType["Slong"] = 9] = "Slong"; IFDType[IFDType["Srational"] = 10] = "Srational"; IFDType[IFDType["Float"] = 11] = "Float"; IFDType[IFDType["Double"] = 12] = "Double"; })(IFDType || (IFDType = {})); export function decodeIFD(data, scheme) { const dataView = new DataView(data); const asciiDecoder = new TextDecoder('ascii'); const tagToEntry = new Map(Object.entries(scheme).map(([name, { tag, type }]) => { return [tag, { name, type }]; })); const result = {}; // The first 4 byte represents the size of packet. const entryCount = dataView.getUint32(4, true); for (let i = 0; i < entryCount; i++) { // (packet size::Uint32) + (entry count::Uint32) = 8 const entryOffset = 8 + 12 * i; // Read a directory entry const tag = dataView.getUint16(entryOffset, true); const type = dataView.getUint16(entryOffset + 2, true); const count = dataView.getUint32(entryOffset + 4, true); // If the data size exceeds 4 bytes, this represents a offset // to the data. Oterwise, it's data itself. const valueOffset = dataView.getUint32(entryOffset + 8, true); const entryScheme = tagToEntry.get(tag); if (!entryScheme) continue; if (type !== entryScheme.type) throw new Error(`Invalid IFD: Tag type for entry ${entryScheme.name} does not match. Expected ${IFDType[entryScheme.type]}, got ${IFDType[type]}`); let value = null; switch (type) { case IFDType.Byte: { const offset = count > 4 ? valueOffset : entryOffset + 8; const buf = data.slice(offset, offset + count); value = [...new Uint8Array(buf)]; break; } case IFDType.Ascii: { const buf = data.slice(valueOffset, valueOffset + count - 1); value = asciiDecoder.decode(buf); break; } case IFDType.Short: { const offset = count > 2 ? valueOffset : entryOffset + 8; const buf = data.slice(offset, offset + count * 2); value = [...new Uint16Array(buf)]; break; } case IFDType.Long: { const offset = count > 1 ? valueOffset : entryOffset + 8; const buf = data.slice(offset, offset + count * 4); value = [...new Uint32Array(buf)]; break; } case IFDType.Undefined: { const offset = count > 4 ? valueOffset : entryOffset + 8; value = data.slice(offset, offset + count); break; } case IFDType.SignedShort: { // Signed SHORT const offset = count > 2 ? valueOffset : entryOffset + 8; value = times(count, i => { const f = dataView.getUint8(offset + i * 2); const d = dataView.getInt8(offset + i * 2 + 1); return d + f / 0x100; }); break; } case IFDType.Float: { const offset = count > 1 ? valueOffset : entryOffset + 8; const buf = data.slice(offset, offset + count * 4); value = [...new Float32Array(buf)]; break; } default: throw new Error(`Type ${IFDType[type]} is not yet supported`); } result[entryScheme.name] = value; } return result; } export function encodeIFD(data) { const dataView = new PTPDataView(); const entries = Object.values(data).sort((a, b) => { if (a.tag === b.tag) throw new Error('Same tag has found'); return a.tag < b.tag ? 1 : -1; }); const entryCount = entries.length; // the first 4 bytes represents the packet size, // but it somehow works just to fill 'em with zeros, at least in Sigma fp dataView.goto(4); dataView.writeUint32(entryCount); let dataOffset = 8 + entries.length * 12; for (const [index, entry] of entries.entries()) { const entryOffset = 8 + index * 12; dataView.goto(entryOffset); dataView.writeUint16(entry.tag); dataView.writeUint16(entry.type); const count = entry.value instanceof ArrayBuffer ? entry.value.byteLength : entry.value.length; dataView.writeUint32(count); switch (entry.type) { case IFDType.Byte: { if (count > 4) { throw new Error('Not yet supported'); } else { for (let i = 0; i < 4; i++) { dataView.writeUint8(entry.value[i]); } } break; } case IFDType.Short: { if (entry.value.length > 2) { dataView.writeUint32(dataOffset); dataView.goto(dataOffset); for (let i = 0; i < entry.value.length; i++) { dataView.writeUint16(entry.value[i]); } dataOffset += entry.value.length * 2; } else { for (let i = 0; i < 2; i++) { dataView.writeUint16(entry.value[i]); } } break; } default: throw new Error(`Type ${IFDType[entry.type]} is not yet supported`); } } dataView.writeCheckSum(); return dataView.toBuffer(); } ; window.encodeIFD = encodeIFD; //# sourceMappingURL=IFD.js.map