简体   繁体   English

AVAssetExportSession导出需要很多时间

[英]AVAssetExportSession export takes a lot of time

My goal is to let user select video from photos and then let him to add labels over it. 我的目标是让用户从照片中选择视频,然后让他在其上添加标签。

Here is what I've got: 这是我所拥有的:

let audioAsset = AVURLAsset(url: selectedVideoURL)
let videoAsset = AVURLAsset(url: selectedVideoURL)
let mixComposition = AVMutableComposition()
let compositionVideoTrack = mixComposition.addMutableTrack(withMediaType: AVMediaTypeVideo, preferredTrackID: Int32(kCMPersistentTrackID_Invalid))
let compositionAudioTrack = mixComposition.addMutableTrack(withMediaType: AVMediaTypeAudio, preferredTrackID: Int32(kCMPersistentTrackID_Invalid))
let clipVideoTrack = videoAsset.tracks(withMediaType: AVMediaTypeVideo)[0]
let clipAudioTrack = audioAsset.tracks(withMediaType: AVMediaTypeAudio)[0]
do {
    try compositionVideoTrack.insertTimeRange(CMTimeRangeMake(kCMTimeZero, videoAsset.duration), of: clipVideoTrack, at: kCMTimeZero)
    try compositionAudioTrack.insertTimeRange(CMTimeRangeMake(kCMTimeZero, audioAsset.duration), of: clipAudioTrack, at: kCMTimeZero)
    compositionVideoTrack.preferredTransform = clipVideoTrack.preferredTransform
} catch {
    print(error)
}
var videoSize = clipVideoTrack.naturalSize
if isVideoPortrait(asset: videoAsset) {
    videoSize = CGSize(width: videoSize.height, height: videoSize.width)
}
let parentLayer = CALayer()
let videoLayer = CALayer()
parentLayer.frame = CGRect(x: 0, y: 0, width: videoSize.width, height: videoSize.height)
videoLayer.frame = CGRect(x: 0, y: 0, width: videoSize.width, height: videoSize.height)
parentLayer.addSublayer(videoLayer)

// adding label
let helloLabelLayer = CATextLayer()
helloLabelLayer.string = "Hello"
helloLabelLayer.font = "Signika-Semibold" as CFTypeRef?
helloLabelLayer.fontSize = 30.0
helloLabelLayer.contentsScale = mainScreen.scale
helloLabelLayer.alignmentMode = kCAAlignmentNatural
helloLabelLayer.frame = CGRect(x: 0.0, y: 0.0, width: 100.0, height: 50.0)
parentLayer.addSublayer(helloLabelLayer)

// creating composition
let videoComp = AVMutableVideoComposition()
videoComp.renderSize = videoSize
videoComp.frameDuration = CMTimeMake(1, 30)
videoComp.animationTool = AVVideoCompositionCoreAnimationTool(postProcessingAsVideoLayer: videoLayer, in: parentLayer)

let instruction = AVMutableVideoCompositionInstruction()
instruction.timeRange = CMTimeRangeMake(kCMTimeZero, mixComposition.duration)
let layerInstruction = videoCompositionInstructionForTrack(track: compositionVideoTrack, asset: videoAsset)
instruction.layerInstructions = [layerInstruction]
videoComp.instructions = [instruction]
if let assetExport = AVAssetExportSession(asset: mixComposition, presetName: AVAssetExportPreset640x480) {
    let filename = NSTemporaryDirectory().appending("video.mov")

    if FileManager.default.fileExists(atPath: filename) {
    do {
        try FileManager.default.removeItem(atPath: filename)
    } catch {
        print(error)
    }
}

let url = URL(fileURLWithPath: filename)
assetExport.outputURL = url
assetExport.outputFileType = AVFileTypeMPEG4
assetExport.videoComposition = videoComp
print(NSDate().timeIntervalSince1970)
assetExport.exportAsynchronously {
    print(NSDate().timeIntervalSince1970)
    let library = ALAssetsLibrary()
    library.writeVideoAtPath(toSavedPhotosAlbum: url, completionBlock: {
        (url, error) in
        switch assetExport.status {
            case AVAssetExportSessionStatus.failed:
                p("failed \(assetExport.error)")
            case AVAssetExportSessionStatus.cancelled:
                p("cancelled \(assetExport.error)")
            default:
                p("complete")
                p(NSDate().timeIntervalSince1970)
                if FileManager.default.fileExists(atPath: filename) {
                    do {
                        try FileManager.default.removeItem(atPath: filename)
                    } catch {
                        p(error)
                    }
                }
                print("Exported")                                    
        }
    })
}

Implementation of isVideoPortrait function: isVideoPortrait函数的实现:

func isVideoPortrait(asset: AVAsset) -> Bool {
    var isPortrait = false
    let tracks = asset.tracks(withMediaType: AVMediaTypeVideo)
    if tracks.count > 0 {
        let videoTrack = tracks[0]
        let t = videoTrack.preferredTransform
        if t.a == 0 && t.b == 1.0 && t.c == -1.0 && t.d == 0 {
            isPortrait = true
        }
        if t.a == 0 && t.b == -1.0 && t.c == 1.0 && t.d == 0 {
            isPortrait = true
        }
        if t.a == 1.0 && t.b == 0 && t.c == 0 && t.d == 1.0 {
            isPortrait = false
        }
        if t.a == -1.0 && t.b == 0 && t.c == 0 && t.d == -1.0 {
            isPortrait = false
        }
    }
    return isPortrait
}

And the last function for video composition layer instruction : video composition layer instruction的最后一个功能是:

func videoCompositionInstructionForTrack(track: AVCompositionTrack, asset: AVAsset) -> AVMutableVideoCompositionLayerInstruction {
    let instruction = AVMutableVideoCompositionLayerInstruction(assetTrack: track)
    let assetTrack = asset.tracks(withMediaType: AVMediaTypeVideo)[0]
    let transform = assetTrack.preferredTransform
    instruction.setTransform(transform, at: kCMTimeZero)
    return instruction
}

The code works well, output video has label, but if I select 1 minute video, export takes 28 seconds. 代码运行良好,输出视频带有标签,但是如果我选择1分钟的视频,则导出需要28秒。

I've search for it and tried to remove layerInsctuction transform, but no effect. 我已经搜索了它,并试图删除layerInsctuction转换,但是没有效果。

Tried to add: assetExport.shouldOptimizeForNetworkUse = false no effect either. 试图添加: assetExport.shouldOptimizeForNetworkUse = false也无效。

Also, tried to set AVAssetExportPresetPassthrough for AVAssetExportSession , in this case video exports with 1 second but labels have gone. 另外,尝试为AVAssetExportSession设置AVAssetExportPresetPassthrough ,在这种情况下,视频导出时间为1秒,但标签消失了。

Any help would be appreciated, because I'm in stuck. 任何帮助将不胜感激,因为我陷入困境。 Thanks for your time. 谢谢你的时间。

This is not directly relevant to your question, but your code here is backwards: 这与您的问题没有直接关系,但是您的代码在这里是向后的:

assetExport.exportAsynchronously {
    let library = ALAssetsLibrary()
    library.writeVideoAtPath(toSavedPhotosAlbum: url, completionBlock: {
        switch assetExport.status {

No no no. 不不不。 First you complete the asset export. 首先,您完成资产导出。 Then you can copy again to somewhere else if that's what you want to do. 然后,如果您要这样做,则可以再次复制到其他地方。 So this needs to go like this: 所以这需要像这样:

assetExport.exportAsynchronously {
    switch assetExport.status {
    case .completed:
        let library = ALAssetsLibrary()
        library.writeVideoAtPath...

Other comments: 其他的建议:

  • ALAssetsLibrary is dead. ALAssetsLibrary已死。 This is not the way to copy into the user's photo library. 这不是复制到用户照片库中的方法。 Use Photo framework. 使用照片框架。

  • Your original code is very odd, because there are a lot of other cases you are not testing for. 您的原始代码很奇怪,因为您还没有测试很多其他情况。 You are just assuming that default means .completed . 您只是假设 default表示.completed That's dangerous. 太危险了

The only way I can think of is to reduce the quality via the bit rate and resolution. 我能想到的唯一方法是通过比特率和分辨率降低质量。

This is done through a dictionary applied to the videoSettings of the AssetExporter, for this to work I had to use a Framework called SDAVAssetExportSession 这是通过应用到AssetExporter的videoSettings的字典来完成的,为此,我不得不使用一个名为SDAVAssetExportSession的框架

Then by changing the videoSettings I could play with the quality to get an optimal quality / speed. 然后,通过更改videoSettings,我可以播放质量以获得最佳质量/速度。

    let compression = [AVVideoAverageBitRateKey : 2097152(DESIRED_BITRATE),AVVideoProfileLevelKey : AVVideoProfileLevelH264BaselineAutoLevel]

    let videoSettings = [AVVideoCodecKey : AVVideoCodecH264, AVVideoWidthKey : maxWidth, AVVideoHeightKey : maxHeight, AVVideoCompressionPropertiesKey:compression]

This was the only way I could speed things up. 这是我加快速度的唯一方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM