AnonymHK / betterplayer

Bug fix version for betterplayer
Apache License 2.0
42 stars 32 forks source link

[BUG] Playing videos that are over 100MB with useCache = true crashes with 'NSMallocException', reason: 'Failed to grow buffer' on Iphone 8 #7

Open blendicavlad opened 1 year ago

blendicavlad commented 1 year ago

Describe the bug Iphone 8 and lower models might have tighter maximum memory allocation sizes constrained by the OS. If we use useCaching = true on an iPhone 8 and try to play video over 100mb, the app crashes with 'NSMallocException', reason: 'Failed to grow buffer' in the haveEnoughDataToFulfillRequest function from the CachingPlayerItem class of the BetterPlayer ios library.
The cause seems to be that songDataUnwrapped byte buffer is allocated with the entire video memory and when dataToRespond = songDataUnwrapped.subdata(in: Range(uncheckedBounds: (currentOffset, currentOffset + bytesToRespond))) call happens, another byte array is allocated in the dataToRespond variable, exceeding the app memory allocation limits (wich i couldn't find anywhere in the Apple documentation, the NSMallocException is even marked as 'Obsolete' by them https://developer.apple.com/documentation/foundation/nsmallocexception.
The current workaround is to disable videoPlayer caching for devices older than Iphone X. I don't have any Swift or IOS APIs experience and my time is limited these weeks, if there is a fix possible and you don't have an older iPhone available i can be offered guidance so i can try to fix and test it.

        func haveEnoughDataToFulfillRequest(_ dataRequest: AVAssetResourceLoadingDataRequest) -> Bool {

            let requestedOffset = Int(dataRequest.requestedOffset)
            let requestedLength = dataRequest.requestedLength
            let currentOffset = Int(dataRequest.currentOffset)

            guard let songDataUnwrapped = mediaData,
                songDataUnwrapped.count > currentOffset else {
                // Don't have any data at all for this request.
                return false
            }

            let bytesToRespond = min(songDataUnwrapped.count - currentOffset, requestedLength)
            //here is the crash happening
            let dataToRespond = songDataUnwrapped.subdata(in: Range(uncheckedBounds: (currentOffset, currentOffset + bytesToRespond)))
            dataRequest.respond(with: dataToRespond)

            return songDataUnwrapped.count >= requestedLength + requestedOffset

        }

To Reproduce Steps to reproduce the behavior:

  1. Initialise the BetterPlayerCacheConfiguration with useCache = true on a iOS device older than iPhone X.
  2. Play a network video that is larger than 100MB
  3. See error

Expected behavior The app should not crash and the cache should work.

Flutter doctor [✓] Flutter (Channel stable, 3.10.1, on macOS 13.1 22C65 darwin-arm64, locale en-RO) [✓] Android toolchain - develop for Android devices (Android SDK version 33.0.0) [✓] Xcode - develop for iOS and macOS (Xcode 14.1) [✓] Chrome - develop for the web [✓] Android Studio (version 2022.1) [✓] VS Code (version 1.77.3) [✓] Connected device (4 available) [✓] Network resources

Better Player version

Smartphone (please complete the following information):

Y-ndm commented 11 months ago

I'm facing the same issue.. The app seems to crash on older devices due to it running out of memory. @AnonymHK