starkdmi/MediaToolSwift

iPhone 12 mini runs out of memory when compressing 4k video

Samigos opened this issue · 5 comments

I've been using this library, which is great btw, on my app and I noticed that whenever I try to compress an 1-minute, 4k video, it crashes due to running out of memory. I've discovered that it starts compressing, it reaches 60-70% and then it crashes.

Here's my implementation:

class MediaToolVideoCompressionService: VideoCompressor {
    private var task: CompressionTask?
    
    func compressVideo(url: URL,
                       outputURL: @escaping (URL?) -> Void) {
        task = nil
        
        Task {
            let outputTempURL = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("tempMovieVideo\(Date().timeIntervalSince1970)").appendingPathExtension("mp4")
            
            task = await VideoTool.convert(
                source: url,
                destination: outputTempURL,
                fileType: .mp4,
                videoSettings: .init(
                    codec: .h264,
                    bitrate: .value(3 * 1_000_000),
                    size: .init(width: 1280, height: 720)
                ),
                optimizeForNetworkUse: true,
                skipSourceMetadata: false,
                customMetadata: [],
                copyExtendedFileMetadata: true,
                overwrite: false,
                deleteSourceFile: false,
                callback: { state in
                    switch state {
                    case .started:
                        print("\(Date()) MediaToolVideoCompressionService Started")
                    case .progress(let progress):
                        print("Progress MediaToolVideoCompressionService: \(progress.fractionCompleted)")
                    case .completed(let info):
                        outputURL(info.url)
                        print("\(Date()) Done MediaToolVideoCompressionService: \(info.url.path)")
                    case .failed(let error):
                        print("\(Date()) Error MediaToolVideoCompressionService: \(error.localizedDescription)")
                        outputURL(url)
                    case .cancelled:
                        outputTempURL.deleteFile()
                        outputURL(nil)
                        print("\(Date()) Cancelled MediaToolVideoCompressionService")
                    }
                })
        }
    }
    
    func cancel() {
        task?.cancel()
    }
}

Am I doing anything wrong?

Hello, @Samigos , your code seems correct, could you try running an Example app and compress the exact same file?

I compressed 4K video (80 seconds long) to your specs without an issue on iPhone X.

@Samigos, do you have enough storage on the device?

I compressed 4K video (80 seconds long) to your specs without an issue on iPhone X.

@Samigos, do you have enough storage on the device?

I have about 10 GBs free. How much storage does the processing need?

As I try different things, I realize that the issue is that as the processing happens, I also play the 4k video on screen, which ramps up the memory usage! The thing is, I can't have the user stare at a loading screen for 30"... they need to watch the video they selected, and have that video get compressed at the same time.

I know it turns out it's not an issue with the library, but I would appreciate any help!

@Samigos, you could try the Metal framework for the video player. And there is probably no need to display a full resolution on the mobile phone.

I also suggest you to try the Xcode Instruments to trace the memory leaks.

Additionally there is an option to access a video frames during the compression via Frame Processors, which can be used for displaying current sample/pixel buffer on the screen.

Finally VideoToolBox (used under the hood in library) process frames one-by-one without storing a big chunks in memory, so displaying the video or maintain the app UI should not be a problem.