You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift 4中合并两个视频遇问题:代码拼接后黑屏且丢失音频

Fixing Video Merging: Black Screen Gaps & Missing Audio in AVMutableComposition

Hey there! Let's get your video merging working properly—those black screens and missing audio are easy fixes once you spot the issues in your code. Here's what's going wrong and how to fix it:

Key Issues in Your Current Code

  • Black Screen Overlap: You're inserting both videos starting at kCMTimeZero, which means they're playing on top of each other. The black screen happens because the exporter handles this overlap incorrectly, or one video's duration doesn't align with the other.
  • Missing Audio: You only created a mutable track for video—you never added any audio tracks from your source assets, so the exported video has no sound.

Corrected Code

Here's the updated version with fixes for both problems, plus safety checks to avoid unexpected crashes:

import AVFoundation

func mergeVideos(firstVideoURL: URL, secondVideoURL: URL, outputURL: URL) {
    let composition = AVMutableComposition()
    
    // MARK: - Handle Video Tracks
    guard let videoTrack = composition.addMutableTrack(withMediaType: .video, preferredTrackID: Int32(kCMPersistentTrackID_Invalid)) else {
        print("Failed to create video track")
        return
    }
    
    let firstAsset = AVAsset(url: firstVideoURL)
    let secondAsset = AVAsset(url: secondVideoURL)
    
    guard let firstVideoAssetTrack = firstAsset.tracks(withMediaType: .video).first,
          let secondVideoAssetTrack = secondAsset.tracks(withMediaType: .video).first else {
        print("Could not retrieve video tracks from assets")
        return
    }
    
    do {
        // Insert first video at the start
        try videoTrack.insertTimeRange(CMTimeRangeMake(start: .zero, duration: firstAsset.duration),
                                       of: firstVideoAssetTrack,
                                       at: .zero)
        
        // Insert second video RIGHT AFTER the first one ends (fixes black screen overlap)
        try videoTrack.insertTimeRange(CMTimeRangeMake(start: .zero, duration: secondAsset.duration),
                                       of: secondVideoAssetTrack,
                                       at: firstAsset.duration)
    } catch {
        print("Error inserting video tracks: \(error.localizedDescription)")
        return
    }
    
    // MARK: - Handle Audio Tracks (fixes missing audio)
    guard let audioTrack = composition.addMutableTrack(withMediaType: .audio, preferredTrackID: Int32(kCMPersistentTrackID_Invalid)) else {
        print("Failed to create audio track")
        return
    }
    
    guard let firstAudioAssetTrack = firstAsset.tracks(withMediaType: .audio).first,
          let secondAudioAssetTrack = secondAsset.tracks(withMediaType: .audio).first else {
        print("Could not retrieve audio tracks from assets")
        return
    }
    
    do {
        // Insert first audio track
        try audioTrack.insertTimeRange(CMTimeRangeMake(start: .zero, duration: firstAsset.duration),
                                       of: firstAudioAssetTrack,
                                       at: .zero)
        
        // Insert second audio track after the first one ends
        try audioTrack.insertTimeRange(CMTimeRangeMake(start: .zero, duration: secondAsset.duration),
                                       of: secondAudioAssetTrack,
                                       at: firstAsset.duration)
    } catch {
        print("Error inserting audio tracks: \(error.localizedDescription)")
        return
    }
    
    // MARK: - Export the Merged Video
    guard let exporter = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetHighestQuality) else {
        print("Failed to create export session")
        return
    }
    
    exporter.outputURL = outputURL
    exporter.outputFileType = .mp4
    exporter.shouldOptimizeForNetworkUse = true
    
    exporter.exportAsynchronously {
        switch exporter.status {
        case .completed:
            print("Merge successful! Video saved to \(outputURL)")
        case .failed:
            print("Export failed: \(exporter.error?.localizedDescription ?? "Unknown error")")
        case .cancelled:
            print("Export cancelled")
        default:
            break
        }
    }
}

// Usage example:
// let firstURL = URL(fileURLWithPath: "path/to/first.mp4")
// let secondURL = URL(fileURLWithPath: "path/to/second.mp4")
// let outputURL = URL(fileURLWithPath: "path/to/merged.mp4")
// mergeVideos(firstVideoURL: firstURL, secondVideoURL: secondURL, outputURL: outputURL)

What Changed?

  1. Fixed Video Placement: The second video is now inserted at firstAsset.duration instead of .zero, so it plays right after the first video ends—no more overlap or black screens.
  2. Added Audio Track Handling: We created a mutable audio track and inserted both source audio tracks, matching the timing of their respective video tracks.
  3. Added Safety Checks: We added guard statements to handle cases where tracks can't be retrieved or created, preventing unexpected crashes.
  4. Improved Export Status Handling: We now check the exporter's status to report success, failure, or cancellation instead of just printing a vague "done?".

Just make sure your output URL is a valid file path (and that you have write permissions for that location) when you call the function!

内容的提问来源于stack exchange,提问作者Jack Crawford

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 06:49:26