AVAssetExportSession ignores frameDuration 60fps and exports at 30fps, but AVPlayer playback is correct

Hey everyone,

I'm stuck on a really frustrating AVFoundation problem. I'm building a video editor that uses a custom AVVideoCompositor to add effects, and I need the final output to be 60 FPS.

So basically, I create an AVMutableComposition to sequence my video clips. I create an AVMutableVideoComposition and set the frame rate to 60 FPS: videoComposition.frameDuration = CMTime(value: 1, timescale: 60)

I assign my CustomVideoCompositor class to the videoComposition.

I create an AVPlayerItem with the composition and video composition.

The Problem:

Playback Works: When I play the AVPlayerItem in an AVPlayer, it's perfect. It plays at a smooth 60 FPS, and my custom compositor's startRequest method is called 60 times per second.

Export Fails: When I try to export the exact same composition and video composition using AVAssetExportSession, the final .mp4 file is always 30 FPS (or 29.97).

I've logged inside my custom compositor during the export, and it's definitely being called 30 times per second, so it's generating the 30 frames. It seems like AVAssetExportSession is just dropping every other frame when it encodes the video.

My source videos are screen recordings which I recorded using ScreenCaptureKit itself with the minimum frame interval to be 60.

Here is my export function. I'm using the AVAssetExportPresetHighestQuality preset :-

func exportVideo(to outputURL: URL) async throws {
    
    guard let composition = composition,
          let videoComposition = videoComposition else {
        throw VideoCompositionError.noValidVideos
    }
    try? FileManager.default.removeItem(at: outputURL)
    guard let exportSession = AVAssetExportSession(
        asset: composition,
        presetName: AVAssetExportPresetHighestQuality // Is this the problem?
    ) else {
        throw VideoCompositionError.trackCreationFailed
    }
    exportSession.outputFileType = .mp4
    exportSession.videoComposition = videoComposition // This has the 60fps setting

    try await exportSession.export(to: outputURL, as: .mp4)
}

I've created a bare bones sample project that shows this exact bug in action. The resulting video is 60fps during playback, but only 30fps during the export. https://github.com/zaidbren/SimpleEditor

My Question:

Why is AVAssetExportSession ignoring my 60 FPS frameDuration and defaulting to 30 FPS, even though AVPlayer respects it?

I think the problem isn't so much the preset as the use of the AVExportSession itself, which can only accept presets.

To get more control, you could use an AVAssetWriter. You can set it up with a preset to begin with, then alter some of the parameters.

AVAssetExportSession ignores frameDuration 60fps and exports at 30fps, but AVPlayer playback is correct
 
 
Q