UNPKG

mediabunny

Version:

Pure TypeScript media toolkit for reading, writing, and converting media files, directly in the browser.

92 lines (72 loc) 2.4 kB
/*! * Copyright (c) 2026-present, Vanilagy and contributors * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ import { RichImageData } from '../metadata'; import { textDecoder } from '../misc'; import { FileSlice, readAscii, readBytes, readI32Be, readU16Be, readU32Be, readU64Be, readU8 } from '../reader'; export const MIN_BOX_HEADER_SIZE = 8; export const MAX_BOX_HEADER_SIZE = 16; export const readBoxHeader = (slice: FileSlice) => { let totalSize = readU32Be(slice); const name = readAscii(slice, 4); let headerSize = 8; const hasLargeSize = totalSize === 1; if (hasLargeSize) { totalSize = readU64Be(slice); headerSize = 16; } const contentSize = totalSize - headerSize; if (contentSize < 0) { return null; // Hardly a box is it } return { name, totalSize, headerSize, contentSize }; }; export const readFixed_16_16 = (slice: FileSlice) => { return readI32Be(slice) / 0x10000; }; export const readFixed_2_30 = (slice: FileSlice) => { return readI32Be(slice) / 0x40000000; }; export const readIsomVariableInteger = (slice: FileSlice) => { let result = 0; for (let i = 0; i < 4; i++) { result <<= 7; const nextByte = readU8(slice); result |= nextByte & 0x7f; if ((nextByte & 0x80) === 0) { break; } } return result; }; export const readMetadataStringShort = (slice: FileSlice) => { let stringLength = readU16Be(slice); slice.skip(2); // Language stringLength = Math.min(stringLength, slice.remainingLength); return textDecoder.decode(readBytes(slice, stringLength)); }; export const readDataBox = (slice: FileSlice) => { const header = readBoxHeader(slice); if (!header || header.name !== 'data') { return null; } if (slice.remainingLength < 8) { // Box is too small return null; } const typeIndicator = readU32Be(slice); slice.skip(4); // Locale indicator const data = readBytes(slice, header.contentSize - 8); switch (typeIndicator) { case 1: return textDecoder.decode(data); // UTF-8 case 2: return new TextDecoder('utf-16be').decode(data); // UTF-16-BE case 13: return new RichImageData(data, 'image/jpeg'); case 14: return new RichImageData(data, 'image/png'); case 27: return new RichImageData(data, 'image/bmp'); default: return data; } };