nextcloud / nextcloud/ios

Streaming high bitrate MP4s stalls every couple of seconds, AVPlayer re-downloads the same data 5-6x

Open
#4,235 0 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
Swift
Stars
2.5k
Forks
1k
Avg merge
2d 18h
Merged PRs (30d)
13

Description

How to use GitHub
  • Please use the 👍 reaction to show that you are affected by the same issue.
  • Please don't comment if you have no relevant information to add. It's just extra noise for everyone subscribed to this issue.
  • Subscribe to receive notifications on status change and new comments.

Steps to reproduce
  1. Upload a high bitrate MP4 (40+ Mbps, e.g. an NVIDIA ShadowPlay gameplay recording, H.264 or AV1) to Nextcloud
  2. Open it in the iOS app and stream it without downloading first
  3. Watch for 30 seconds or so
Expected behaviour

Smooth playback. The connection delivers well over 100 Mbps, so a 50 Mbps file should stream comfortably.

Actual behaviour

Playback freezes for about a second every 1-2 seconds, video and audio together, then resumes, and the cycle repeats for the whole video.

The same files play fine in Safari on the same phone, fine in the app once downloaded, and MKVs (which the app routes to VLC) stream fine at any size.

I built the app from master and added logging to find out why. After 40s of streaming, AVPlayerItem.accessLog() shows 2089 media requests and 1.37 GB transferred, for a stretch of video that only needs ~250 MB. observedBitrate is 110+ Mbps, and the playback buffer still never grows past ~2.6s (even with preferredForwardBufferDuration = 30). So CoreMedia's loader is re-downloading 5-6x the data it needs and starving its own buffer.

Things I ruled out: the files themselves (faststart, clean interleaving, no PTS gaps), the endpoint (direct download and WebDAV behave the same), custom asset headers (same without them), and the app's own player code (a bare stock AVPlayerViewController on the same URL thrashes identically). Safari doesn't count as a counterexample here, WebKit fetches media through its own loader rather than CoreMedia's.

Logs
[FREEZE-DIAG] PLAYBACK STALLED @ 4.21s
[FREEZE-DIAG] accessLog requests=80   stalls=1  transferred=287.2MB  observed=414.8Mbps
[FREEZE-DIAG] PLAYBACK STALLED @ 8.47s
[FREEZE-DIAG] accessLog requests=259  stalls=4  transferred=357.5MB  observed=212.2Mbps
[FREEZE-DIAG] PLAYBACK STALLED @ 19.73s
[FREEZE-DIAG] accessLog requests=847  stalls=20 transferred=726.7MB  observed=141.4Mbps
[FREEZE-DIAG] PLAYBACK STALLED @ 40.46s
[FREEZE-DIAG] accessLog requests=2005 stalls=41 transferred=1346.4MB observed=112.7Mbps

(from my own instrumentation: KVO on the playing AVPlayerItem plus its access log.
40s of a 50 Mbps video should be ~250 MB, not 1.3 GB.)
Reasoning or why should it be changed/implemented?

My best guess at the trigger is HTTP/2 flow control or proxy buffering making CoreMedia's range request pattern pathological (it reproduces on LAN and through a Cloudflare tunnel, so the common hop is nginx + PHP streaming), but I can't verify that without tearing up my server.

One idea: the app already ships a VLC engine that streams these exact files perfectly. Detecting this state (numberOfStalls climbing while observedBitrate sits far above the media bitrate) and offering to switch to VLC, the way alwaysUseVLCForVideo already works per file, would cover users regardless of their server setup.

The player has gotten noticeably better recently, so credit where it's due. Once this one is cleaned up it'll be really nice. Happy to run more instrumented builds if that helps.

Environment data

iOS version: iOS 27.0 Public Beta 2 (iPhone 16 Pro)

Nextcloud iOS app version: 34.1.1.2 (current master built from source), also happens on the App Store release

Server operating system: TrueNAS SCALE (Nextcloud app)

Web server: NGINX

Database: PostgreSQL

PHP version: 8.5.8

Nextcloud version: 34.0.2

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by locating the existing alwaysUseVLCForVideo handling and the AVPlayer playback path. Reproduce with a high-bitrate MP4 while observing AVPlayerItem accessLog() and playback stalls, then compare the behavior with the VLC engine. Done should be defined by the project maintainers, since the issue proposes a VLC fallback but does not identify a specific implementation path or test.

Written by the indexing model from the issue text.

Assessment

Tech stack
ios, swift
Domain
mobile-dev
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.