Skip to content

Fix a hard crash when AVFoundation reports a non-finite estimatedDataRate - #936

Open
rochakagrawal wants to merge 1 commit into
kingslay:mainfrom
rochakagrawal:fix/nonfinite-track-metadata
Open

rochakagrawal wants to merge 1 commit into
kingslay:mainfrom
rochakagrawal:fix/nonfinite-track-metadata

Conversation

@rochakagrawal

Copy link
Copy Markdown
Contributor

AVMediaPlayerTrack.init reads estimatedDataRate through ?? 0, which only covers a nil assetTrack. The property itself is a non-optional Float, and AVFoundation returns it as NaN for a track whose data rate is simply not declared in the container — ordinary for progressive MP4s served over HTTP.

That value went straight into Int64(_: Float), which traps: EXC_BREAKPOINT, signal 5. The viewer sees a crash at the instant the item becomes ready to play, roughly three seconds into playback.

Zero is the honest reading for an unmeasurable bitrate, and every finite input still produces exactly the same answer as before.

Two neighbours on the same paths get the same treatment, both reachable from the same KVO callbacks:

  • bufferingProgress divided by preferredForwardBufferDuration and clamped afterwards, so an infinite quotient reached Int(_:) before the clamp. The clamp now happens before the conversion; an infinite value means "more is buffered than the target asked for", which is 100.
  • The currentTime / totalTime guard in KSVideoPlayer compared against Double(Int.max) bounds, but NaN compares false against every bound, so it passed the guard and trapped in Int(_:). isFinite is now tested first.

Verified against a model of Swift's conversion semantics (an IEEE fdiv in the same shape): 4 traps on the current code, 0 on this one, with identical results for every finite input.

Affected: any app that plays a container with an undeclared data rate. On iOS this is a hard crash, not a degradation. Kotlin returns 0 for a non-finite → Int conversion, which is why the same file plays on Android and only iOS dies.

AVMediaPlayerTrack.init read `estimatedDataRate` through `?? 0`, which only
covers a nil assetTrack. The property itself is a non-optional Float, and
AVFoundation returns it as NaN for a track whose data rate is not declared in
the container - ordinary for progressive MP4s served over HTTP. That value went
straight into `Int64(_: Float)`, which traps: EXC_BREAKPOINT, signal 5, seen by
the viewer as a crash at the moment the item becomes ready to play, roughly
three seconds into playback.

Zero is the honest reading for an unmeasurable bitrate, and every finite input
still produces the same answer as before.

Two neighbours on the same paths get the same treatment, both reachable from the
same KVO callbacks:

- bufferingProgress divided by `preferredForwardBufferDuration` and clamped
  afterwards, so an infinite quotient reached `Int(_:)` before the clamp.
- The `currentTime`/`totalTime` guard in KSVideoPlayer compared against
  `Double(Int.max)` bounds, but NaN compares false against every bound, so it
  passed the guard and trapped in `Int(_:)`. `isFinite` is now tested first.

Verified with a model of Swift's conversion semantics: 4 traps on the old code,
0 on this one, identical results for every finite input.
@kingslay

kingslay commented Oct 2, 2026 •

Copy link
Copy Markdown
Owner

这三个crash在lgpl分支已经修复好了 。
可以使用下面的代码进行,会更简单

bufferingProgress = item.preferredForwardBufferDuration == 0 ? 100 : (loadedTime * 100 / item.preferredForwardBufferDuration)

var current = Int(exactly: ceil(currentTime)), var total = Int(exactly: ceil(totalTime))

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.

2 participants