можно объединить одно и то же видео в одно видео - PullRequest
0 голосов
/ 14 февраля 2020

Я хочу объединить одно и то же видео в одно. Я искал уже в inte rnet для возможного решения. Я реализовал некоторый код, но проблема в том, что он не объединит все видео. Конечным результатом является только первое видео с несколькими секундами. Я не знаю, что я делаю неправильно.

Это мой код:

var atTimeM: CMTime = CMTimeMake(value: 0, timescale: 0)
var layerInstructionsArray = [AVVideoCompositionLayerInstruction]()
var completeTrackDuration: CMTime = CMTimeMake(value: 0, timescale: 1)
var videoSize: CGSize = CGSize(width: 0.0, height: 0.0)
var totalTime : CMTime = CMTimeMake(value: 0, timescale: 0)
var videoArrayOfAssets = [AVAsset]()


func mergeVideoArray(){

    print(videoArrayOfAssets)

   let mixComposition = AVMutableComposition()
    for videoAsset in videoArrayOfAssets {
        let videoTrack =
            mixComposition.addMutableTrack(withMediaType: AVMediaType.video,
                                           preferredTrackID: Int32(kCMPersistentTrackID_Invalid))
        do {
            if videoAsset == videoArrayOfAssets.first {
                atTimeM = CMTime.zero
            } else {
                atTimeM = totalTime // <-- Use the total time for all the videos seen so far.
            }
            try videoTrack!.insertTimeRange(CMTimeRangeMake(start: CMTime.zero, duration: videoAsset.duration),
                                            of: videoAsset.tracks(withMediaType: AVMediaType.video)[0],
                                           at: atTimeM)
            videoSize = videoTrack!.naturalSize
        } catch let error as NSError {
            print("error: \(error)")
        }
        completeTrackDuration = CMTimeAdd(completeTrackDuration, videoAsset.duration)
        let videoInstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: videoTrack!)
        if (videoAsset != videoArrayOfAssets.last) {
            videoInstruction.setOpacity(0.0, at: completeTrackDuration)

        }
        layerInstructionsArray.append(videoInstruction)
    }

    let mainInstruction = AVMutableVideoCompositionInstruction()
    mainInstruction.timeRange = CMTimeRangeMake(start: CMTime.zero, duration: completeTrackDuration)
    mainInstruction.layerInstructions = layerInstructionsArray

    let mainComposition = AVMutableVideoComposition()
    mainComposition.instructions = [mainInstruction]
    mainComposition.frameDuration = CMTimeMake(value: 1, timescale: 30)
    mainComposition.renderSize = CGSize(width: videoSize.width, height: videoSize.height)

    let documentDirectory = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
    let dateFormatter = DateFormatter()
    dateFormatter.dateStyle = .long
    dateFormatter.timeStyle = .short
    let date = dateFormatter.string(from: NSDate() as Date)
    let savePath = (documentDirectory as NSString).appendingPathComponent("mergeVideo-\(date).mov")
    let url = NSURL(fileURLWithPath: savePath)

    let exporter = AVAssetExportSession(asset: mixComposition, presetName: AVAssetExportPresetHighestQuality)
    exporter!.outputURL = url as URL
    exporter!.outputFileType = AVFileType.mov
    exporter!.shouldOptimizeForNetworkUse = true
    exporter!.videoComposition = mainComposition
    exporter!.exportAsynchronously {

        PHPhotoLibrary.shared().performChanges({
            PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: exporter!.outputURL!)
        }) { saved, error in
            if saved {
                print("video saved to camera roll")
        }
    }
}
}

Как мне это сделать, чтобы работать?

1 Ответ

1 голос
/ 14 февраля 2020

Вы можете использовать созданный мной класс для аналогичной цели:

import Foundation
import CoreMedia
import AVFoundation

public protocol MediaItemProtocol {    
    var duration: CMTime {get}
    var asset: AVAsset {get}
    func createFileOnDisk(completionHandler: @escaping (_ fileURL: URL?, _ error: Error?) -> Void)
}

final public class VideoMediaItem: MediaItemProtocol {
    public private (set) var duration: CMTime
    public private (set) var asset: AVAsset
    private var videoUrl: URL
    public init(url: URL) {
        videoUrl = url
        let options = [AVURLAssetPreferPreciseDurationAndTimingKey: true]
        let asset = AVURLAsset(url: url, options: options)
        self.asset = asset
        duration = asset.duration
    }
    public func createFileOnDisk(completionHandler: @escaping (_ fileURL: URL?, _ error: Error?) -> Void) {
        completionHandler(videoUrl, nil)
    }
}

final class VideoCompositionMediaItem: MediaItemProtocol {
    private (set) var duration: CMTime = kCMTimeZero
    private (set) var asset: AVAsset
    private var itemList: [MediaItemProtocol]
    private var exporter: AVAssetExportSession?
    init(item: [MediaItemProtocol]) {
        self.itemList = item
        let composition = AVMutableComposition()
        for mediaItem in item {
            if CMTIME_IS_VALID(mediaItem.duration) {
                let itemDuration = mediaItem.duration
                let range = CMTimeRangeMake(kCMTimeZero, itemDuration)
                try? composition.insertTimeRange(range, of: mediaItem.asset, at: duration)
                duration = CMTimeAdd(duration, itemDuration)
            }
        }

        if let track = item.first?.asset.tracks(withMediaType: .video).first {
            if let compositionTrack = composition.tracks(withMediaType: .video).first {
                compositionTrack.preferredTransform = track.preferredTransform
            }
        }

        asset = composition
    }

    func createFileOnDisk(completionHandler: @escaping (_ fileURL: URL?, _ error: Error?) -> Void) {
        if self.exporter != nil {
            completionHandler(nil, MediaItemError.AlreadyExporting)
            return
        }
        exporter = AVAssetExportSession(asset: asset, presetName: AVAssetExportPreset1280x720)
        guard let exporter = exporter else {
            completionHandler(nil, MediaItemError.UnknownError)
            return
        }
        let fileURL = URL(fileURLWithPath: NSTemporaryDirectory())
            .appendingPathComponent("Composition" + NSUUID().uuidString)
            .appendingPathExtension("mov")

        exporter.outputURL = fileURL
        exporter.outputFileType = AVFileType.mov
        exporter.shouldOptimizeForNetworkUse = true
        exporter.timeRange = CMTimeRangeMake(kCMTimeZero, duration)
        exporter.exportAsynchronously {
            if self.exporter?.status == .completed {
                self.exporter = nil
                completionHandler(fileURL, nil)
            } else {
                self.exporter = nil
                completionHandler(nil, MediaItemError.ErrorExporting)
            }
        }
    }

}
...