Skip to content

fix: don't hang the main thread on live streams during chapter inspection - #106

Open
pke wants to merge 3 commits into
doublesymmetry:mainfrom
pke:fix/live-stream-main-thread-hang
Open

fix: don't hang the main thread on live streams during chapter inspection#106
pke wants to merge 3 commits into
doublesymmetry:mainfrom
pke:fix/live-stream-main-thread-hang

Conversation

@pke

@pke pke commented Jul 17, 2026

Copy link
Copy Markdown

Fixes #105.

The metadata completion reads pendingAsset.duration without it ever being async-loaded. An unloaded AVAsset property getter falls back to a synchronous XPC fetch to mediaserverd — and for indefinite-duration (live) streams, iOS 26 answers that query only after an internal ~20s timeout. The completion runs on the main thread, so every playback start of a SHOUTcast/Icecast stream freezes the whole app for ~20 seconds (touches queue up; backgrounding mid-hang gets the app watchdog-killed with 0x8BADF00D). Older iOS versions answered immediately, which is why this surfaced only recently.

Two changes, no API surface:

  • add "duration" to the async-loaded metadata keys, and
  • skip chapter inspection for indefinite assets — live streams have no chapters, and their metadata arrives via timed metadata regardless.

Verified on device (iPhone 13 Pro, iOS 26.5) against a live SHOUTcast stream: app start went from a 20.11s Severe Hang (Instruments) to instant, with ICY timed metadata unaffected. Full analysis and the captured watchdog backtrace in #105.

…tion

The metadata completion read `pendingAsset.duration` without ever
async-loading it. An unloaded AVAsset property getter falls back to a
synchronous XPC fetch to mediaserverd — and for indefinite-duration
(live) streams, iOS 26 answers that query only after an internal ~20s
timeout. Since the completion runs on the main thread, every playback
start of a SHOUTcast/Icecast stream froze the entire app for ~20s
(queued touches, watchdog 0x8BADF00D kills when backgrounded).

Load `duration` together with the other inspected keys, and skip
chapter inspection entirely for indefinite assets — live streams have
no chapters, and their metadata arrives via timed metadata anyway.

Watchdog backtrace mid-hang (iPhone 13 Pro, iOS 26.5):

  mach_msg2_trap
  xpc_connection_send_message_with_reply_sync
  CoreMedia  FigXPCConnectionSendSyncMessageCreatingReply
  MediaToolbox remoteXPCAsset_CopyPropertyAndBlockageWarning
  AVFCore -[AVAsset(AVAssetChapterInspection) _loadChapterInfo]
  AVFCore -[AVAsset(AVAssetChapterInspection) availableChapterLocales]
Making the reads async-loaded was not enough: AVAsset serializes its
property loading per asset, so requesting the metadata keys before
"playable" queued item creation — and audio start — behind the same
~20s live-stream probe. Move the inspection to the end of the playable
completion: playback starts immediately, chapters/common metadata
resolve whenever the daemon answers (instantly for files, late and
irrelevant for live streams).
@pke

pke commented Jul 17, 2026

Copy link
Copy Markdown
Author

Pushed a second commit after on-device verification: async-loading alone freed the UI but audio start was still delayed ~20s — AVAsset serializes property loading per asset, so the metadata request queued the playable load behind the same live-stream probe. The inspection now runs at the end of the playable completion (after replaceCurrentItem/rate), which restores instant starts on live streams while keeping chapter/common-metadata delivery for files. Verified on iPhone 13 Pro / iOS 26.5: play starts in ~1s, UI responsive throughout.

…duration

Requesting the metadata keys anywhere near playback start still stalls
audio on live streams: AVAsset serializes property loading, so the
~20s live-stream probe queues either the playable key or the item's
own preparation behind it. The player item's observed duration tells
live from file without touching the asset — inspect chapters and
common metadata only once a finite duration is reported, and never for
indefinite (live) streams.
@pke

pke commented Jul 17, 2026

Copy link
Copy Markdown
Author

Final design, verified on device (iPhone 13 Pro, iOS 26.5): metadata inspection is now deferred until the player item reports a finite duration. Any earlier trigger point still delayed audio — AVAsset serializes property loading, so a pre-playable request queued the playable key and an at-item-creation request queued the item's own preparation behind the same ~20s live-stream probe. The item's observed duration distinguishes live from file without touching the asset: files keep chapter/common-metadata delivery (post-ready), live streams never issue the probe at all. Result on a live SHOUTcast stream: instant audio start, responsive UI, ICY timed metadata unaffected. Also verified against an iOS 17.5 simulator runtime for no regression — older iOS was never affected, which is why this surfaced only as users updated.

@dcvz dcvz changed the title fix: don't hang the main thread on live streams during chapter inspection [4.X] fix: don't hang the main thread on live streams during chapter inspection Jul 17, 2026
@dcvz dcvz changed the title [4.X] fix: don't hang the main thread on live streams during chapter inspection fix: don't hang the main thread on live streams during chapter inspection Jul 17, 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.

iOS 26: ~20s main-thread hang on every play of a live (ICY/SHOUTcast) stream

1 participant