【问题标题】:AVAssetExportSession export takes a lot of timeAVAssetExportSession 导出需要很多时间
【发布时间】:2016-11-30 00:02:39
【问题描述】:

我的目标是让用户从照片中选择视频,然后让他在上面添加标签。

这是我得到的:

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")                                    
        }
    })
}

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
}

还有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
}

代码运行良好,输出视频有标签,但如果我选择 1 分钟视频,导出需要 28 秒。

我已经搜索过并尝试删除 layerInsctuction 转换,但没有效果。

尝试添加: assetExport.shouldOptimizeForNetworkUse = false 也没有效果。

另外,尝试将AVAssetExportPresetPassthrough 设置为AVAssetExportSession,在这种情况下,视频导出时间为 1 秒,但标签已消失。

任何帮助将不胜感激,因为我陷入了困境。感谢您的宝贵时间。

【问题讨论】:

  • 您正在做两件耗时的事情:导出和复制到相册。哪一个需要很长时间?
  • 另外:当某件事需要很长时间并且您想知道原因时,请使用 Instruments。你试过吗?它告诉了你什么?
  • @matt 基本上,导出需要很多时间。复制到相册需要 1 秒钟。我还没有使用仪器,我应该在运行仪器时使用“活动监视器”吗?
  • 你应该计时。
  • @matt 我使用了 Time Profile,运行应用程序并执行所有常规步骤。 Here 是输出,但有点复杂。你能帮我找什么吗?

标签: ios swift avassetexportsession avasset


【解决方案1】:

我能想到的唯一方法是通过比特率和分辨率来降低质量。

这是通过应用到 AssetExporter 的 videoSettings 的字典来完成的,为此我必须使用一个名为 SDAVAssetExportSession 的框架

然后通过更改 videoSettings 我可以播放质量以获得最佳质量/速度。

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

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

这是我加快速度的唯一方法。

【讨论】:

  • 谢谢,它帮助我将时间减半。
  • 我认为您不必为此使用框架(除非我遗漏了什么),您可以从AVAssetExportSession.exportPresets(compatibleWith: yourVideoAsset) 中获取预设,然后将它们传递给您的AVAssetExportSession实例化。
  • 我相信这些特定的键与 AVAssetExportSession 不兼容,你必须使用较低的 AVAssetWriter,这就是这个文件在引擎盖下所做的。
  • 有没有人找到任何解决方案来减少 exportAsynchronously 导出视频/音频所需的时间。我需要花费大量时间来编码长度为 5:00 及以上的大型音频。我尝试使用 AVAssetExportSession 和 SDAVAssetExportSession 但两者的结果相同。 :(
  • @AliRaza 这取决于,你是在录制视频/音频,然后重新编码以减小大小,还是只是对现有的静态文件进行转码?
【解决方案2】:

这与您的问题没有直接关系,但您的代码是倒退的:

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

不不不。首先,您完成资产导出。然后,如果您想要这样做,您可以再次复制到其他地方。所以这需要像这样:

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

其他cmets:

  • ALAssetsLibrary 已死。这不是复制到用户照片库的方式。使用照片框架。

  • 您的原始代码很奇怪,因为还有很多其他情况您没有测试。您只是假设 default 表示.completed。这很危险。

【讨论】:

  • 不用担心。很抱歉,这不是一个真正的答案;我可能不得不删除它。 :) 但是没有办法在评论中说出所有这些。
猜你喜欢
  • 1970-01-01
  • 2020-03-19
  • 2018-05-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-02
  • 1970-01-01
相关资源
最近更新 更多