Repository navigation
Fix divide-by-zero trap in FFmpegAssetTrack audio nominalFrameRate - #928
Conversation
AVCodecParameters.frame_size is audio-only and left at 0 by most demuxers
(AAC/MP3/Opus/FLAC/Vorbis) until the first frame is decoded. The
timebase.den / timebase.num fallback then integer-divides to 0 whenever
den < num (e.g. a valid AVRational{num:3,den:1}), and sample_rate can also
be 0 pre-decode, so the subsequent sample_rate / frameSize traps Swift's
integer division (Fatal error: Division by zero, SIGTRAP) on valid input.
Extract a pure audioNominalFrameRate(sampleRate:frameSize:timebase:) helper
that guards every divisor (frameSize, sampleRate, and the timebase.num
fallback) and falls back to the existing 48 floor, mirroring AudioDescriptor's
sampleRate <= 0 defence in Resample.swift. Integer division is preserved
verbatim, so well-formed streams are byte-for-byte unchanged; only the
previously-trapping inputs now return 48 instead of crashing.
Adds Tests/KSPlayerTests/FFmpegAssetTrackTest.swift with 7 focused cases.
|
I saw the related crash fixes (#926 and the subtitle parser loop fixes) land this week, so I thought I'd check in on this one while the area is still warm — the unguarded division traps on any audio stream whose frame_size is still 0 before the first decode. Is the guarded-division approach fine as-is, or would you rather see it handled differently, perhaps with a test fixture reproducing the trap? I'm glad to rework it whichever way makes it easiest to take. |
|
我对改动进行了评价。你可能没有看到。我建议的改动如下 |
|
你最近提的这些bug,lgpl分支之前就已经修复了。如果你想要更稳定、功能更丰富的代码的话,那你可以申请lgpl授权。 |
Replace the timebase-derived frame-size fallback (den/num can integer-divide to 0) with the constants suggested in review: TRUEHD falls back to 48, every other codec to 1000. Same divide guard for sample_rate; the 48 floor is unchanged. Tests updated to the new signature, including TRUEHD- and generic-fallback cases.
|
谢谢!已按你的建议更新(4dec3e6):fallback 改为 codec-aware 常量 —— TRUEHD 用 48,其他 codec 用 1000,不再从 timebase 推导(den/num 整除可能为 0)。 (Thanks! Updated per your suggestion in 4dec3e6: the fallback is now codec-aware constants — TRUEHD uses 48, everything else 1000 — instead of deriving from the timebase (whose den/num integer division can be 0). The divide-by-zero guard on Also noted your point that the LGPL branch already carries these fixes — understood; happy to close this if the guard lands upstream through that channel. |
Single-file FFmpeg-interop divide-by-zero guard — the same family as #926 (NULL
codecpar) and #908 (rotationNaN).Root cause
FFmpegAssetTrack.init?(stream:)computed the audionominalFrameRatewith unguarded integer division (FFmpegAssetTrack.swift:100):max(Float(codecpar.sample_rate / frameSize), 48), whereframeSizecould be0. Three FFmpeg facts combine into a deterministic trap on valid input:AVCodecParameters.frame_sizeis0pre-decode (audio-only; AAC/MP3/Opus/FLAC/Vorbis leave it at0until the first frame is decoded), so thetimebase.den / timebase.numfallback branch is hot, not theoretical.0wheneverden < num— a perfectly validAVRationallike{num:3,den:1}(exotic MPEG-TS audio, some MKV/AVI muxes, attached-pic pseudo-audio) yields1 / 3 == 0.sample_ratecan also be0pre-decode →0 / 0regardless oftime_base.Swift traps on integer divide-by-zero (
Fatal error: Division by zero, SIGTRAP, exit 133) — the app dies. The codebase already guardssample_rateelsewhere (AudioDescriptor.init,Resample.swift:294,if sampleRate <= 0 { 48000 }); this site forgot the same defence.Fix
Extract a pure
audioNominalFrameRate(sampleRate:frameSize:timebase:)helper that guardsframeSize > 0 && sampleRate > 0(andtimebase.num > 0) before dividing, falling back to the existing48floor otherwise. Byte-for-byte unchanged for well-formed streams;48was already themax(...)lower bound.Test plan
swift build— clean, 0 new warnings.swift test --filter FFmpegAssetTrackTest— 7 passed, 0 failed: div-to-zero fallback ({num:3,den:1}+frame_size=0), zerosample_rate(bothden>=numandden<num), zerotimebase.num, well-formed clamp-to-floor, above-floor (pins Int32 truncation48000/700==68so aFloat-division refactor would fail), and the timebase-fallback path.Fatal error: Division by zero(exit 133).