Source code
Revision control
Copy as Markdown
Other Tools
// Test helpers for constructing H.264 bitstream pieces and inspecting or
// rewriting fragmented-MP4 init segments. The current set covers:
// - a minimal SPS NAL builder (BitWriter / escapeEmulationBytes / buildSPSNALU),
// - generic MP4 box navigation (findBox / enclosingBoxes / splice),
// - splicing a replacement SPS into an init segment's avcC box
// (spliceSPSIntoInitSegment),
// - reading the track display dimensions from tkhd (parseDisplayDimensions).
//
// One use today is crafting an init segment whose SPS carries arbitrary
// pic_width_in_mbs_minus1 / pic_height_in_map_units_minus1 values (to exercise
// the parser's dimension handling) while keeping the rest of the container
// intact, but the pieces are deliberately independent so further H.264 / MP4
// test utilities (other NAL types, PPS rewriting, more box fields, etc.) can be
// added here as needed.
// Minimal bitstream writer for Exp-Golomb (ue) and fixed-width fields.
class BitWriter {
constructor() {
this.bytes = [];
this.cur = 0;
this.bits = 0;
}
writeBit(b) {
this.cur = (this.cur << 1) | (b ? 1 : 0);
if (++this.bits === 8) {
this.bytes.push(this.cur & 0xff);
this.cur = 0;
this.bits = 0;
}
}
writeBits(value, n) {
const v = BigInt.asUintN(64, BigInt(value));
for (let i = n - 1; i >= 0; i--) {
this.writeBit(Number((v >> BigInt(i)) & 1n));
}
}
writeUE(v) {
const value = BigInt(v) + 1n;
let leading = 0;
let t = value;
while (t > 1n) {
t >>= 1n;
leading++;
}
for (let i = 0; i < leading; i++) {
this.writeBit(0);
}
this.writeBit(1);
for (let i = leading - 1; i >= 0; i--) {
this.writeBit(Number((value >> BigInt(i)) & 1n));
}
}
closeWithRbspTrailing() {
this.writeBit(1);
while (this.bits !== 0) {
this.writeBit(0);
}
}
u8() {
return new Uint8Array(this.bytes);
}
}
// Insert H.264 emulation-prevention bytes (00 00 0x -> 00 00 03 0x).
function escapeEmulationBytes(rbsp) {
const out = [];
for (let i = 0; i < rbsp.length; i++) {
const v = rbsp[i];
if (
out.length >= 2 &&
v <= 0x03 &&
out[out.length - 2] === 0 &&
out[out.length - 1] === 0
) {
out.push(0x03);
}
out.push(v);
}
return new Uint8Array(out);
}
// Build a minimal, syntactically valid Constrained Baseline SPS NAL unit with
// the caller-supplied picture dimensions (in macroblock / map-unit units, minus
// one, per the spec). The other fields are fixed to the simplest legal values:
// no cropping, frame_mbs_only, no VUI.
function buildSPSNALU(picWidthInMbsMinus1, picHeightInMapUnitsMinus1) {
const bw = new BitWriter();
// NAL header: forbidden_zero=0, nal_ref_idc=3, nal_unit_type=7 (SPS).
bw.writeBits((0 << 7) | (3 << 5) | 7, 8);
bw.writeBits(0x42, 8); // profile_idc = Constrained Baseline
bw.writeBits(0x00, 8); // constraint_setN_flags + reserved
bw.writeBits(0x0a, 8); // level_idc = 1
bw.writeUE(0); // seq_parameter_set_id
bw.writeUE(0); // log2_max_frame_num_minus4
bw.writeUE(2); // pic_order_cnt_type = 2
bw.writeUE(1); // max_num_ref_frames
bw.writeBit(0); // gaps_in_frame_num_value_allowed_flag
bw.writeUE(picWidthInMbsMinus1);
bw.writeUE(picHeightInMapUnitsMinus1);
bw.writeBit(1); // frame_mbs_only_flag
bw.writeBit(0); // direct_8x8_inference_flag
bw.writeBit(0); // frame_cropping_flag
bw.writeBit(0); // vui_parameters_present_flag
bw.closeWithRbspTrailing();
return escapeEmulationBytes(bw.u8());
}
function readU32BE(buf, off) {
return (
((buf[off] << 24) >>> 0) |
(buf[off + 1] << 16) |
(buf[off + 2] << 8) |
buf[off + 3]
);
}
function writeU32BE(buf, off, value) {
buf[off] = (value >>> 24) & 0xff;
buf[off + 1] = (value >>> 16) & 0xff;
buf[off + 2] = (value >>> 8) & 0xff;
buf[off + 3] = value & 0xff;
}
// Find the first occurrence of an MP4 box with the given 4-char type tag.
// Returns the offset of the 4-byte size header preceding the type, or -1.
function findBox(buf, tag) {
for (let i = 0; i + 8 <= buf.length; i++) {
if (
buf[i + 4] === tag.charCodeAt(0) &&
buf[i + 5] === tag.charCodeAt(1) &&
buf[i + 6] === tag.charCodeAt(2) &&
buf[i + 7] === tag.charCodeAt(3)
) {
const size = readU32BE(buf, i);
if (size >= 8 && i + size <= buf.length) {
return i;
}
}
}
return -1;
}
// List every box whose payload range contains `target`. Returns array of
// {offset, tag, size} entries, outermost first.
function enclosingBoxes(buf, target) {
const out = [];
let pos = 0;
while (pos + 8 <= buf.length) {
const size = readU32BE(buf, pos);
if (size < 8 || pos + size > buf.length) {
break;
}
const tag = String.fromCharCode(
buf[pos + 4],
buf[pos + 5],
buf[pos + 6],
buf[pos + 7]
);
if (target >= pos + 8 && target < pos + size) {
out.push({ offset: pos, tag, size });
// recurse into this container box
pos += 8;
// skip per-box version/flags for stsd which has an extra 8-byte header
if (tag === "stsd") {
pos += 8;
}
// skip the avc1 sample entry's fixed 78-byte payload before nested boxes
else if (tag === "avc1") {
pos += 78;
}
} else {
pos += size;
}
}
return out;
}
// Splice `replacement` into `buf` at [start, start+removeLength), returning a
// new Uint8Array.
function splice(buf, start, removeLength, replacement) {
const out = new Uint8Array(buf.length - removeLength + replacement.length);
out.set(buf.subarray(0, start), 0);
out.set(replacement, start);
out.set(buf.subarray(start + removeLength), start + replacement.length);
return out;
}
// Given an init segment and an SPS NALU body, rebuild the avcC box with the new
// SPS (keeping the original PPS) and patch every enclosing box size so the
// resulting init segment stays self-consistent. Returns a new Uint8Array.
function spliceSPSIntoInitSegment(initSegment, spsNalu) {
let buf = new Uint8Array(initSegment);
const avccOffset = findBox(buf, "avcC");
is(avccOffset >= 0, true, "avcC box found");
const avccSize = readU32BE(buf, avccOffset);
const bodyStart = avccOffset + 8;
// Parse avcC body: 1+1+1+1+1 fixed, then numOfSPS (3 reserved bits + 5),
// SPS entries (each prefixed by u16 length), numOfPPS, PPS entries.
let p = bodyStart;
const configurationVersion = buf[p++];
const profileIndication = buf[p++];
const profileCompatibility = buf[p++];
const levelIndication = buf[p++];
const lengthSizeFlag = buf[p++];
const numSPSByte = buf[p++];
const numSPS = numSPSByte & 0x1f;
is(numSPS >= 1, true, "init segment has at least one SPS");
// Skip first SPS to find PPS section.
const firstSpsLength = (buf[p] << 8) | buf[p + 1];
p += 2 + firstSpsLength;
// Skip any additional SPSs (rare).
for (let i = 1; i < numSPS; i++) {
const len = (buf[p] << 8) | buf[p + 1];
p += 2 + len;
}
// PPS section.
const numPPS = buf[p++];
const ppsEntries = [];
for (let i = 0; i < numPPS; i++) {
const len = (buf[p] << 8) | buf[p + 1];
ppsEntries.push(buf.subarray(p + 2, p + 2 + len));
p += 2 + len;
}
// Build the new avcC body: keep header bytes, replace SPS, keep PPS.
const ppsBytes = ppsEntries.reduce((acc, e) => {
return new Uint8Array([
...acc,
(e.length >> 8) & 0xff,
e.length & 0xff,
...e,
]);
}, new Uint8Array());
const spsBytes = new Uint8Array([
(spsNalu.length >> 8) & 0xff,
spsNalu.length & 0xff,
...spsNalu,
]);
const newBody = new Uint8Array([
configurationVersion,
profileIndication,
profileCompatibility,
levelIndication,
lengthSizeFlag,
0xe0 | 1, // 3 reserved bits + numOfSequenceParameterSets = 1
...spsBytes,
numPPS,
...ppsBytes,
]);
const newAvccSize = 8 + newBody.length;
// Build the new avcC box (size + tag + body).
const newAvccBox = new Uint8Array(newAvccSize);
writeU32BE(newAvccBox, 0, newAvccSize);
newAvccBox[4] = 0x61; // 'a'
newAvccBox[5] = 0x76; // 'v'
newAvccBox[6] = 0x63; // 'c'
newAvccBox[7] = 0x43; // 'C'
newAvccBox.set(newBody, 8);
// Find every enclosing box and capture sizes BEFORE splicing.
const enclosing = enclosingBoxes(buf, avccOffset);
const delta = newAvccSize - avccSize;
// Splice avcC.
buf = splice(buf, avccOffset, avccSize, newAvccBox);
// Update each enclosing box's size by delta.
for (const box of enclosing) {
writeU32BE(buf, box.offset, box.size + delta);
}
return buf;
}
// Read the track's display dimensions from the tkhd box of an init segment.
// width/height are the last two 32-bit fields of tkhd, stored as 16.16
// fixed-point (ISO/IEC 14496-12); the integer part is the display size.
// Returns {width, height}.
function parseDisplayDimensions(initSegment) {
const buf = new Uint8Array(initSegment);
const tkhdOffset = findBox(buf, "tkhd");
is(tkhdOffset >= 0, true, "tkhd box found");
const tkhdSize = readU32BE(buf, tkhdOffset);
return {
width: readU32BE(buf, tkhdOffset + tkhdSize - 8) >>> 16,
height: readU32BE(buf, tkhdOffset + tkhdSize - 4) >>> 16,
};
}