bdinfo-rs follows the classic BDInfo report format byte-for-byte, with a handful of deliberate exceptions: where the original BDInfo is provably wrong against the codec specification / FFmpeg, bdinfo-rs emits the correct value instead. Each divergence is verified bit-by-bit and stays within BDInfo's existing report vocabulary — bdinfo-rs never invents a report string BDInfo wouldn't emit.
This page shows the concrete before/after for every divergence and flags which ones you'll actually see on a normal disc.
Reading the diffs.
-is what the original BDInfo prints;+is what bdinfo-rs prints. The+lines are pinned verbatim by tests in the codec scanners. The-lines are reconstructed from the spec and from BDInfo's behavior (there are no captured BDInfo fixtures in this repo), so treat them as faithful reconstructions, not byte-captures. Surrounding values (language, resolution, fps) are illustrative; only the highlighted field changes.
| Divergence | Changes the report? | When you'll see it |
|---|---|---|
| DTS core bit rate (1509 → 1536 kbps) | Yes — bitrate number | Common — most DTS-HD MA cores and full-rate DTS tracks |
| DTS:X IMAX detection | Yes — codec name | DTS:X IMAX tracks |
| HDR10+ without a mastering display | Yes — HDR token | HDR10+ titles whose stream carries no static mastering-display SEI |
MaxCLL unit label (cd / m2 → cd/m2) |
Yes — unit spelling | Common — every HDR title with a content-light-level SEI |
| AVC High 4:4:4 (profile 244) | Yes — profile token | Rare — Blu-ray video is almost always 4:2:0 |
| PGS forced-caption counts | Yes — caption tally | Discs with multi-object subtitle compositions |
| Playlist attribute selection on short clips | Yes — which clip's streams are presented | Rare — multi-clip playlists whose early clips are short |
| Bitrate under a backwards PTS | Yes — bitrate numbers | Short playlists, by a few kbps; feature-length ones are unchanged |
| E-AC-3 reduced data-rate | Only on non-BD input | Reduced-rate E-AC-3 (24 / 22.05 / 16 kHz) isn't used on Blu-ray |
| AC-3 low-sample-rate shift | Only on non-BD input | Legacy bsid 9/10; conforming Blu-ray AC-3 is always bsid 8 |
| Undecoded MPEG-1/2 and AAC audio names | Only when the stream was never decoded | Damaged discs whose MPA/AAC frames the scan could not read |
HEVC profile_idc recovery |
Edge case only | Malformed headers with general_profile_idc == 0 |
| VC-1 interlaced-field picture type | No — internal only | Never — the picture tag is counted, never printed |
PAT table_id validation |
Only on malformed input | Never — no conforming disc carries a wrong PAT table id |
| AACS-encrypted discs are refused | Yes — no report at all | Discs whose stream content is still encrypted |
| Damaged-media read granularity | Only on damaged media — partial scans retain more | Discs with unreadable spans |
Read errors: folder input vs .iso input |
Only on damaged media — the same disc measures differently through the two inputs | Damaged discs and damaged disc images |
An unreadable .ssif falls back to the base view |
Only on damaged media — the clip is measured in 2D instead of lost | 3D discs whose STREAM/SSIF file will not open |
DTS core rate code 24 is 1536 kbps per the spec (ETSI TS 102 114) and FFmpeg; BDInfo
historically printed 1509 kbps. This is the most frequently seen divergence — the same
number appears standalone for a full-rate DTS track and embedded in the (DTS Core: …)
block of essentially every DTS-HD Master Audio track.
- Audio: English / DTS Audio / 5.1 / 48 kHz / 1509 kbps / 16-bit
+ Audio: English / DTS Audio / 5.1 / 48 kHz / 1536 kbps / 16-bitEmbedded inside a DTS-HD MA track:
- Audio: English / DTS-HD Master Audio / 5.1 / 48 kHz / 16-bit (DTS Core: 5.1 / 48 kHz / 1509 kbps / 16-bit)
+ Audio: English / DTS-HD Master Audio / 5.1 / 48 kHz / 16-bit (DTS Core: 5.1 / 48 kHz / 1536 kbps / 16-bit)Source: crates/bdinfo-rs-core/src/codec/dts.rs (bit-rate table).
DTS:X IMAX tracks carry the IMAX extension sync word (0xF14000D0) instead of the legacy
DTS:X word BDInfo knows. BDInfo therefore labels them as plain DTS-HD Master Audio;
bdinfo-rs recognizes the IMAX word and renders DTS:X Master Audio. Only the codec-name
field changes — the channel/rate/bit-depth numbers are identical.
- Audio: English / DTS-HD Master Audio / 5.1 / 48 kHz / 16-bit (DTS Core: 5.1 / 48 kHz / 1536 kbps / 16-bit)
+ Audio: English / DTS:X Master Audio / 5.1 / 48 kHz / 16-bit (DTS Core: 5.1 / 48 kHz / 1536 kbps / 16-bit)Short names map the same way: DTS-HD MA → DTS:X MA, DTS-HD HR → DTS:X HR.
Source: crates/bdinfo-rs-core/src/codec/dts_hd.rs.
The BDInfo lineage emits an HDR label only when a static mastering-display SEI is present.
bdinfo-rs also recognizes HDR10+ from the ST 2094-40 (T.35) dynamic-metadata SEI alone, so
a valid HDR10+ stream that carries no mastering display still gets its HDR10+ token
instead of dropping the descriptor entirely.
- Video: MPEG-H HEVC Video / … / 10 bits / Limited Range / …
+ Video: MPEG-H HEVC Video / … / 10 bits / HDR10+ / Limited Range / …Only changes output on the mastering-display-absent path; with a mastering display
present, both tools already emit a label. Source: crates/bdinfo-rs-core/src/codec/hevc.rs
(HDR-label gate).
BDInfo's HEVC scanner prints the Maximum Content Light Level unit as cd / m2 — spaced
around the slash on that one line only, while the adjacent Maximum Frame-Average Light
Level and both mastering-display luminance values use cd/m2. The spaced form is a typo
in the original source (one string literal out of step with its neighbors); bdinfo-rs
emits the cd/m2 spelling BDInfo itself uses everywhere else.
- Video: MPEG-H HEVC Video / … / Maximum Content Light Level: 992 cd / m2 / Maximum Frame-Average Light Level: 772 cd/m2
+ Video: MPEG-H HEVC Video / … / Maximum Content Light Level: 992 cd/m2 / Maximum Frame-Average Light Level: 772 cd/m2Only the unit spacing changes — the value and every other field are identical.
Source: crates/bdinfo-rs-core/src/codec/hevc.rs (extended-format info).
AVC profile_idc 244 is High 4:4:4 Predictive. BDInfo only mapped the equivalent legacy
code (144), so 244 fell through to Unknown Profile; bdinfo-rs maps both to the existing
High 4:4:4 Profile string. Rare in practice — Blu-ray video is almost always 4:2:0.
- Video: AVC Video / 1080p / 23.976 fps / 16:9 / Unknown Profile 4.1
+ Video: AVC Video / 1080p / 23.976 fps / 16:9 / High 4:4:4 Profile 4.1Within the same fix, the CAVLC 4:4:4 code (44) and constraint-flag refinements are
deliberately left at Unknown Profile — BDInfo has no string for them, and inventing one
would break the locked report. Source: crates/bdinfo-rs-core/src/codec/avc.rs.
BDInfo reads a composition object's four cropping fields unconditionally; per the HDMV
layout (FFmpeg pgssubdec, libbluray pg_decode) they are present only when the crop
flag (0x80) is set. On a multi-object composition the over-read consumes the next
object's bytes and then reads past the segment end into stale buffer memory, so the
forced flag comes from leftover data — captions get randomly misreported as forced.
bdinfo-rs parses the spec layout and reports the true normal/forced split.
- Presentation Graphics English 33.00 kbps 1920x1080 / 1546 Captions (54 Forced Captions)
+ Presentation Graphics English 33.00 kbps 1920x1080 / 1600 CaptionsThe total is unaffected — only the split; genuinely forced captions still render with
BDInfo's existing forms. Source: crates/bdinfo-rs-core/src/codec/pgs.rs (PCS
composition-object parse).
BDInfo 0.8 guards its per-playlist stream table against negligibly short clips: when two
play items declare the same PID, the later item's declared attributes win only if that
clip's RelativeLength exceeds 1% of the playlist, and the same guard gates whether a
clip with more streams becomes the reference clip whose streams the report presents. The
intent is sound — a 3-second logo clip must not decide a feature's stream layout — but
0.8 computes RelativeLength against the playlist length accumulated before the clip
is appended (TSPlaylistFile.LoadPlaylist): the first clip divides by zero into
+Infinity, the second is measured against the opener alone, and every clip against a
partial total — so early clips pass the guard however short they are. bdinfo-rs computes
each clip's true fraction of the finished playlist. (BDInfo 0.7.5.6 has no guard at all:
the last duplicate always wins.)
The outcome differs only when a playlist's clips disagree about the declared attributes or stream counts — mainly playlists that open with short logo or warning clips whose stream layout differs from the feature's. On such a playlist the reference clip, and with it the presented stream rows, can change; on a playlist whose clips agree, the report is byte-identical.
Source: crates/bdinfo-rs-core/src/bdrom/mpls.rs (the post-parse resolution),
crates/bdinfo-rs-core/src/bdrom/disc.rs (select_reference).
BDInfo 0.8 advances a stream's last-seen PTS whenever the value merely changes
(TSStreamFile's PES parse compares PTS != PTSLast), so a backwards step measures a
negative transfer interval — discarding that access unit's bitrate sample — and drags the
interval base backwards, overstating the next forward unit's interval and understating its
rate. bdinfo-rs advances only on a forward step, against the running maximum, as BDInfo
0.7.5.6 did (PTS > PTSLast) and as the PTS+DTS path of both tools already does.
The rule governs any stream whose PES headers carry a PTS without a DTS — not audio alone — and the same last-seen PTS is what the final playlist-level bitrate pass reads for the video streams, so a video row's kbps can move too. What moves is a small absolute span of the measured timeline, a fraction of a second: enough to clear the printed resolution on a short playlist, and lost in the rounding of a feature-length one. That is why every committed golden — all feature-length — is byte-identical across the change, while a real disc's short playlists shift by a few kbps.
Source: crates/bdinfo-rs-core/src/bdrom/m2ts.rs (the PTS-only PES arm and
finish_scan).
These are real spec/FFmpeg-correctness fixes, but they cannot change the report for a conforming Blu-ray — either the triggering input never occurs on Blu-ray, or the corrected value is never rendered.
- E-AC-3 reduced data-rate (
fscod2). When thefscodfield signals a reduced sample rate (24 / 22.05 / 16 kHz), bdinfo-rs halves the rate and derives the bit rate from it. Reduced-rate E-AC-3 is not used on Blu-ray (E-AC-3 there is 48 kHz), so this never fires on a real disc. - AC-3 low-sample-rate shift (
bsid9/10). Legacy half/quarter-rate AC-3 right-shifts both the sample rate and the table bit rate. Conforming Blu-ray AC-3 is alwaysbsid8 (shift 0), so the output is unchanged. - HEVC
profile_idcrecovery. When a malformed header carriesgeneral_profile_idc == 0, bdinfo-rs recovers the profile from the compatibility flags (FFmpeg parity). This is a parse-robustness fix for non-conforming headers, not a documented divergence from a known BDInfo value. - VC-1 interlaced-field picture-type handling. The per-frame picture type
(
I/P/B/BI) is classified more correctly (SMPTE 421M field-pair collapse), but the tag is only ever used as a frame-present counter — its string is never rendered. The report is byte-identical. - PAT
table_idvalidation. A PID-0 section is assembled only when itstable_idis 0x00 (ISO 13818-1); a wrong id abandons the section, exactly as both tools already do for a PMT whosetable_idis not 0x02. BDInfo assembles the mislabeled section and adopts whatever PMT PID it announces. No conforming disc carries a wrong PAT table id, so the report is unchanged for every real disc. - Undecoded MPEG-1/2 and AAC audio names. BDInfo derives these four codec names from
the decoded frame header alone and renders an empty name when no frame was ever decoded
(the unset
ExtendedDatacast to a null string). bdinfo-rs falls back to the type-derived constants BDInfo itself emits —MP1 Audio/MP2 Audio(0.7.5.6's fixed codec names) andMPEG-2 AAC/MPEG-4 AAC(the short-name constants) — so an audio row whose stream the scan could not read never renders a hole. A decoded stream prints the same header-derived name as BDInfo.
AACS leaves only the first 16 bytes of every 6144-byte Aligned Unit in the clear (AACS Blu-ray Prerecorded Book §3.10), so a demux of the rest reads ciphertext and every value it measures is meaningless. BDInfo scans such a disc anyway and prints the resulting statistics — stream layouts, bitrates and codec tokens describing nothing. bdinfo-rs detects the encryption during the metadata scan and refuses to measure:
- Command line — one notice on stderr, then exit code 4: no picker, no scan, no report file.
--liststill works and exits by its own rules, its output being read from the disc's own database rather than from the streams. - Desktop app — the disc opens, lists and browses like any other, the info box marks it encrypted, and the scan action stays disabled.
- Browser package — no policy: the disc model carries
isAacsEncryptedand the application decides. The package is a library and mirrors the library layering below.
Refusing is each front-end's policy, not the analyzer's: bdinfo-rs-core scans an encrypted disc
when asked, which is what keeps the decision with the caller.
Source: crates/bdinfo-rs-core/src/bdrom/disc.rs (the encryption probe),
crates/bdinfo-rs/src/main.rs (exit code 4).
BDInfo 0.8 reads stream files in 5 MiB requests in both measurement passes (raised from 0.7.5.6's 16 KiB by a throughput change with no correctness motive). bdinfo-rs reads 256 KiB per request in the full pass and 16 KiB in the quick codec pass; the browser package keeps 5 MiB for both, since its file reads each cost one synchronous round trip. Healthy discs produce byte-identical reports either way — the packet state machine carries its state across chunk boundaries, so the request size cannot change a measured value.
On damaged media the request size is the failure granularity, and there the outputs deliberately diverge from BDInfo 0.8:
- a failed request discards only its own bytes, so a partial scan retains up to ~5 MiB more measured data per damaged file than 0.8 would (toward 0.7.5.6's loss profile) — chapter rows and stream tallies fill further before the zero rows begin;
- cancellation, polled once per request, takes effect within 256 KiB of reading instead of 5 MiB;
- one unreadable span stalls one small request inside the drive's retry storm rather than a 5 MiB one — 16 KiB in the quick codec pass (which runs alone in a codec-only browse, and ahead of the full pass in a measurement scan that did not already read the codec detail — the desktop app's does, and skips it), 256 KiB in the full pass.
Source: crates/bdinfo-rs-core/src/bdrom/m2ts.rs (DATA_SIZE, QUICK_DATA_SIZE).
A read that fails is handled at a different layer for the two inputs, so the same damaged disc measures differently depending on which one it is scanned through. Both are deliberate; neither has a BDInfo counterpart, which reads folders only.
- Folder input — the failing read aborts that stream file. Everything demuxed before
it is kept (unless the retention switch discards it) and everything after it is never
read, so the file's chapter rows and tallies stop at the failure and stay zero past it.
The failure is recorded against the file and printed in the report
WARNING:block. .isoinput — the UDF reader sits under the demux and is resilient there: it records the unreadable sectors and serves that span as zeros, so the demux reads on to the end of the file. The damage shows up as depressed rates across the affected span rather than as a truncated file, the values after it are measured normally, and the retention switch has nothing to decide. The reader's recordings are drained into the scan and printed in the sameWARNING:block. Only file data degrades this way: the volume structures stay strict, since a damaged filesystem descriptor has no readable rest to degrade to, so an image damaged there does not open at all.
Zero-fill is what lets one bad sector cost its own sector rather than the rest of a 30 GB
stream file, which is why the .iso path keeps it. Every surface that takes an .iso
drains those recordings — the command line, the desktop app and the browser package alike
— so an unreadable image is never reported as a clean scan.
The split reaches the report text: the WARNING: block names a different thing for each
input, because a different thing failed.
WARNING: File errors were encountered during scan:
- 00011.M2TS <the read failure>
+ 00011.M2TS @ byte 5242880 <the read failure>The - line is folder input, the + line the same disc as an .iso. Folder input names
the stream file that a failed read aborted. .iso input names the open file handle and
the byte offset it first found unreadable; the reader then serves that handle zeros and
reads on, so further bad sectors under the same handle add no further lines. Both shapes
are per attempt, not per disc: a damaged span that both measurement passes read as far as
prints a line for each of them.
Neither shape is BDInfo's — it reads folders only, and prompts per failing file instead of collecting the failures into the report.
Source: crates/bdinfo-rs-core/src/vfs/udf/source.rs (UdfSource::open_resilient,
take_errors), crates/bdinfo-rs-core/src/scan.rs (open_iso),
crates/bdinfo-rs-wasm/src/lib.rs (scan_iso, run_iso_report).
A 3D clip stores its base and dependent views interleaved in
BDMV/STREAM/SSIF/<clip>.ssif, and both tools demux that file in preference to the plain
BDMV/STREAM/<clip>.m2ts — it is the superset, and it is the only place the dependent
(MVC) view exists. It is not, however, the only readable source of the base view.
When the .ssif cannot be opened, BDInfo abandons the clip: no streams, no bitrates, no
chapter rows. bdinfo-rs opens the .m2ts instead and measures the base view from it, so a
3D disc with an unreadable interleaved file still reports everything a 2D scan of it would
— minus the dependent-view row, which genuinely could not be read. The failure is not
swallowed: it is recorded against the .ssif and printed in the report WARNING: block,
and the clip is named by the .m2ts it was actually read from wherever the report names
its source file. The on-disk sizes still count the .ssif — the file is on the disc
either way, and an unreadable one is no smaller. A strict (fail-fast) scan still aborts;
degrading is the resilient scan's job.
This applies to the failed open only. An .ssif that opens and then fails partway is an
ordinary damaged stream file, handled as
above: what was demuxed before the failure is
kept, and the base view is not re-read from the .m2ts.
Source: crates/bdinfo-rs-core/src/bdrom/disc.rs (scan_one_stream_file).
The report is byte-identical; the window around it is not a pixel-for-pixel port. The three-pane Playlist / Stream File / Codec view, the playlist table, sorting, drag-and-drop, the settings dialog, and the report toggles all behave as in the original.
Not carried over:
- Bitrate charts. The original plots six chart types (
FormChart) from per-second and per-frame measurements; bdinfo-rs keeps only the aggregate statistics the report prints, so there is nothing to plot. The chart-tied image-prefix setting is absent with them. - Custom playlist builder. Playlists are selected from the disc's own table, not assembled.
- Taskbar progress. Progress is shown in the window, not painted into the taskbar button.
- The
KeepStreamOrder,EnableSSIF, andExtendedStreamDiagnosticstoggles. - Selectable report text. The text widgets in use cannot span-select; Copy report and Save report… cover the same need.
Deliberately different:
- Unreadable files do not prompt. The original asks Yes/No per failing file; bdinfo-rs collects
them, scans the rest, and shows one warning banner — the same
WARNINGblock the report carries. - Saving is both manual and automatic. A Save report… dialog exists alongside the save-on-completion setting.
- An encrypted disc is never scanned. The original measures the ciphertext; the app lists the disc and leaves the scan action disabled — see AACS-encrypted discs are refused.
- A dropped stream file loses its codec detail. The app browses a disc at codec depth before
you scan it, so its measured scan skips the quick codec pass rather than reading every stream
file's head a second time — on damaged media that second read can stall for minutes. The pass it
skips is also the fallback for a file the measured pass gives up on, so with Keep partially
scanned stream files turned off (it is on by default) a stream file that fails partway
through leaves its rows carrying only what the disc's clip info declares — codec name,
resolution, channel layout — without the profile, level, and HDR detail the stream itself would
have supplied. Nothing else changes: the file is still named in the warning banner and the
report's
WARNINGblock, and with the setting left on, or on media that reads cleanly, the rows are identical to the command line's.
CHANGELOG.md— the per-release record of these divergences.README.md— project overview and the locked-report contract.