Skip to content

perf(fmp4): box parsing - #7956

Open
itsjamie wants to merge 8 commits into
masterfrom
perf/fmp4-box-parsing
Open

perf(fmp4): box parsing#7956
itsjamie wants to merge 8 commits into
masterfrom
perf/fmp4-box-parsing

Conversation

@itsjamie

@itsjamie itsjamie commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator

This PR will...

Reduce the CPU and wall time spent parsing fragmented MP4 content in
MP4Demuxer and PassThroughRemuxer.

findBox now reads box headers and compares four-byte box types directly from
the source array. Fragment parsing reuses parsed init metadata, flattens
moof/traf discovery, specializes the common CMAF trun layout, and avoids
creating detailed sample objects unless I-frame remuxing requires them.

Video sample timing and SEI extraction now share one fragment pass. The parsed
timing is handed to passthrough remuxing through a one-shot private cache. When
initialization data or chunk metadata is unavailable, the demuxer falls back to
the standalone parseSamples() SEI scan. Existing call signatures and metadata
event payloads remain unchanged.

For the complete 60-second video VOD transmux, the overall changes are:

  • Chromium
    • CPU: 99.277 → 25.552 µs (-74.26%)
    • Live heap: 23917.179 → 23980.847 KB (+0.27%, equivalent)
    • Wall: 112.908 → 28.624 µs (-74.65%)
  • Firefox
    • CPU: 246.972 → 59.469 µs (-75.92%)
    • Live heap: 27742.446 → 27745.774 KB (+0.01%, equivalent)
    • Wall: 246.161 → 59.258 µs (-75.93%)
  • WebKit
    • CPU: 214.393 → 40.255 µs (-81.22%)
    • Live heap: 22474.402 → 22469.759 KB (-0.02%, equivalent)
    • Wall: 253.073 → 46.978 µs (-81.44%)

The headline result uses only the complete video-vod-transmux workload. It
feeds all 15 video fragments from the Angel One HLS asset over 60 seconds.
Traced through the Transmuxer, MP4Demuxer, and the smaller parsing
cases isolate individual operations for regression checks and per-change
attribution. The complete 60-second audio track is retained as
an end-to-end correctness guardrail.

The checks verify box and track-run counts, 1,500 video samples

Why is this Pull Request needed?

findBox created temporary strings, subarrays, and path slices; the init
segment could be parsed independently by the demuxer and remuxer; and video
fragments were traversed separately for SEI messages and sample timing.

The sample parser also created detailed per-sample structures during normal
passthrough remuxing even though only I-frame remuxing consumes those details.

Moving the common work into direct numeric scans and handing already parsed
data between stages substantially reduces CPU and wall time without changing
the generated media or metadata. I would expect this to be most noticeable on
lower performing devices with a much weaker CPU.

Are there any points in the code the reviewer needs to double check?

The main areas I would like reviewed closely are the fast-path boundaries and
the compact sample path is limited to the common CMAF trun layout. Other flag
combinations and multiple track runs continue through the general parser.
I-frame remuxing still requests detailed sample data so that keyframe selection
and duration rewriting retain their existing behavior.

Resolves issues:

N/A

  • changes have been done against master branch, and PR does not conflict
  • new unit / functional tests have been added (whenever applicable)
  • API or design changes are documented in API.md (not applicable)

itsjamie added 7 commits July 30, 2026 09:23
Compare four-byte MP4 box types numerically and carry path offsets
through a single recursive search. This avoids temporary strings,
subarrays, and path slices during every box traversal.

Chromium:
  CPU improvement: +47.63% (21.599us -> 11.312us)
  Live heap equivalent: -0.18% (11033.592kb -> 11053.794kb)
  Wall-time improvement: +45.27% (24.251us -> 13.272us)
  Anchor drift: -0.28%

Firefox:
  CPU improvement: +51.26% (68.789us -> 33.531us)
  Live heap equivalent: -0.02% (8937.115kb -> 8938.976kb)
  Wall-time improvement: +51.28% (68.454us -> 33.347us)
  Anchor drift: +0.00%

WebKit:
  CPU improvement: +58.74% (50.999us -> 21.041us)
  Live heap equivalent: +0.03% (7420.412kb -> 7418.292kb)
  Wall-time improvement: +58.66% (60.177us -> 24.876us)
  Anchor drift: +1.19%

Tests: npm run test:unit; npm run type-check; bperf exact checks

Bperf-Benchmark: hlsjs.fmp4-box-parsing
Bperf-Cycle: cycle-607fd7ce05db7aff6945bd7148a98d31f5777e5128827a879c63c115dbb9dcf1
Make detailed trun sample objects optional and disable them for normal
passthrough remuxing. I-frame remuxing retains the existing detail path,
and the public helper default remains unchanged.

Chromium:
  CPU inconclusive: +3.48% (11.312us -> 10.918us)
  Live heap equivalent: -0.03% (11053.794kb -> 11057.34kb)
  Wall-time inconclusive: +5.23% (13.272us -> 12.578us)
  Anchor drift: -0.25%

Firefox:
  CPU improvement: +5.46% (33.531us -> 31.699us)
  Live heap equivalent: -0.00% (8938.976kb -> 8939.095kb)
  Wall-time inconclusive: +5.34% (33.347us -> 31.567us)
  Anchor drift: -1.43%

WebKit:
  CPU improvement: +10.39% (21.041us -> 18.855us)
  Live heap equivalent: -0.00% (7418.292kb -> 7418.555kb)
  Wall-time improvement: +10.08% (24.876us -> 22.369us)
  Anchor drift: -3.92%

Tests: npm run test:unit; npm run type-check; bperf exact checks

Bperf-Benchmark: hlsjs.fmp4-box-parsing
Bperf-Cycle: cycle-6d5257ae7abdec0a3fc40b0531ee7623c6217ff69676ec6ca5704b2c4197ad4a
Read box sizes and four-byte types directly from the source array inside
findBox. This removes helper calls and packed integer comparisons from
the hottest traversal loop.

Chromium:
  CPU equivalent: +1.50% (10.918us -> 10.754us)
  Live heap equivalent: -0.06% (11057.34kb -> 11064.134kb)
  Wall-time equivalent: +0.86% (12.578us -> 12.47us)
  Anchor drift: +0.00%

Firefox:
  CPU equivalent: +0.61% (31.699us -> 31.505us)
  Live heap equivalent: -0.01% (8939.095kb -> 8939.839kb)
  Wall-time equivalent: +0.71% (31.567us -> 31.343us)
  Anchor drift: +1.45%

WebKit:
  CPU improvement: +9.80% (18.855us -> 17.007us)
  Live heap equivalent: -0.08% (7418.555kb -> 7424.614kb)
  Wall-time improvement: +9.23% (22.369us -> 20.304us)
  Anchor drift: +4.89%

Tests: npm run test:unit; npm run type-check; bperf exact checks

Bperf-Benchmark: hlsjs.fmp4-box-parsing
Bperf-Cycle: cycle-b9a40fddc890735bbf56ee1191a51617e625af4188fa9e28e5a315a26e719c39
Parse video sample timing and SEI messages in one fragment pass and
cache the timing result for passthrough remuxing. This avoids walking
the same fMP4 samples again on the normal transmux path.

When initialization data or chunk metadata is unavailable, fall back
to the standalone parseSamples() SEI scan. Existing demuxer call
signatures remain valid and metadata event payloads are unchanged.

Chromium:
  CPU inconclusive: +6.74% (10.754us -> 10.029us)
  Live heap equivalent: -0.18% (11064.134kb -> 11084.145kb)
  Wall-time improvement: +7.03% (12.47us -> 11.593us)
  Anchor drift: -0.53%

Firefox:
  CPU inconclusive: +1.26% (31.505us -> 31.108us)
  Live heap equivalent: -0.12% (8939.839kb -> 8950.293kb)
  Wall-time inconclusive: +1.19% (31.343us -> 30.972us)
  Anchor drift: -1.59%

WebKit:
  CPU inconclusive: +0.00% (17.007us -> 17.006us)
  Live heap equivalent: -0.02% (7424.614kb -> 7426.069kb)
  Wall-time inconclusive: +0.30% (20.304us -> 20.243us)
  Anchor drift: -2.15%

Tests: npm run test:unit; npm run type-check; bperf exact checks

Bperf-Benchmark: hlsjs.fmp4-box-parsing
Bperf-Cycle: cycle-ff55b1ed04ebcda21221debbc76d0f159db0db16270e21213e1ca02b145ae342
Build unsigned 32-bit MP4 values directly instead of routing every read
through the signed helper. This removes a nested call from box, timing,
and sample-size parsing.

Chromium:
  CPU equivalent: +1.90% (10.029us -> 9.839us)
  Live heap equivalent: +0.02% (11084.145kb -> 11082.352kb)
  Wall-time equivalent: +2.07% (11.593us -> 11.353us)
  Anchor drift: -0.34%

Firefox:
  CPU improvement: +5.18% (31.108us -> 29.498us)
  Live heap equivalent: -0.00% (8950.293kb -> 8950.329kb)
  Wall-time improvement: +5.26% (30.972us -> 29.344us)
  Anchor drift: +0.29%

WebKit:
  CPU improvement: +11.63% (17.006us -> 15.028us)
  Live heap equivalent: +0.10% (7426.069kb -> 7418.892kb)
  Wall-time improvement: +9.75% (20.243us -> 18.27us)
  Anchor drift: +1.10%

Tests: npm run test:unit; npm run type-check; bperf exact checks

Bperf-Benchmark: hlsjs.fmp4-box-parsing
Bperf-Cycle: cycle-d1516adeffeb5d7d28728959d6c943b912af06a8326100dc8fc30082779b089a
Reuse parsed init metadata, scan moof and traf children once, and
specialize the common no-detail CMAF trun layout. Guarded compact size
reads, constant-duration aggregation, and native EPB search remove work
while uncommon layouts continue through the general parser.

Chromium:
  CPU improvement: +6.72% (9.839us -> 9.178us)
  Live heap equivalent: -0.59% (11082.352kb -> 11148.085kb)
  Wall-time improvement: +6.32% (11.353us -> 10.635us)
  Anchor drift: +0.61%

Firefox:
  CPU improvement: +6.47% (29.498us -> 27.589us)
  Live heap equivalent: -0.14% (8950.329kb -> 8962.675kb)
  Wall-time improvement: +6.48% (29.344us -> 27.444us)
  Anchor drift: +0.00%

WebKit:
  CPU improvement: +7.95% (15.028us -> 13.833us)
  Live heap equivalent: -0.32% (7418.892kb -> 7442.757kb)
  Wall-time improvement: +8.75% (18.270us -> 16.672us)
  Anchor drift: +0.31%

Tests: npm run test:unit; npm run type-check; bperf exact checks

Bperf-Benchmark: hlsjs.fmp4-box-parsing
Bperf-Cycle: cycle-ad3d19dafc554017f9b08b57f23b81aee89126722dc14fd01a7352ff1453a29c
Flatten moof and traf discovery, reuse a UTF-8 decoder, defer keyframe
and EOF work, and scan NAL units with absolute sample ends. Tracking
duration presence directly removes repeated allocation and branching
while preserving SEI event fields and raw payload bytes.

Chromium:
  CPU improvement: +5.39% (9.160us -> 8.667us)
  Live heap equivalent: +0.01% (11148.062kb -> 11147.321kb)
  Wall-time improvement: +5.54% (10.603us -> 10.015us)
  Anchor drift: -0.53%

Firefox:
  CPU improvement: +6.66% (27.552us -> 25.716us)
  Live heap equivalent: -0.00% (8962.679kb -> 8962.934kb)
  Wall-time improvement: +6.66% (27.396us -> 25.571us)
  Anchor drift: +0.00%

WebKit:
  CPU improvement: +5.15% (13.777us -> 13.067us)
  Live heap equivalent: +0.09% (7442.659kb -> 7436.056kb)
  Wall-time inconclusive: +3.98% (16.553us -> 15.895us)
  Anchor drift: -0.47%

Tests: npm run test:unit (1144); npm run type-check; API Extractor;
bperf exact checks

Bperf-Benchmark: hlsjs.fmp4-box-parsing
Bperf-Cycle: cycle-43ec7fa74936d6a0eea57b950d3af10bcd5538e83294303dbdbb49c94a8da4a4
@itsjamie itsjamie changed the title Perf/fmp4 box parsing perf(fmp4): box parsing Jul 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant