从视频中提取音频 AVFoundation

8
func extractAudioFromVideo(videoUrl:NSURL, audioPath:String){
    //2
    var asset = AVURLAsset(URL: videoUrl, options: nil)
    asset.loadValuesAsynchronouslyForKeys(NSArray(object: "tracks") as [AnyObject], completionHandler: { () -> Void in
        var audioTrack = asset.tracksWithMediaType(AVMediaTypeAudio)[0] as! AVAssetTrack

        var audioComposition = AVMutableComposition()

        var audioCompositionTrack:AVMutableCompositionTrack!

        audioCompositionTrack = audioComposition.addMutableTrackWithMediaType(AVMediaTypeAudio, preferredTrackID: CMPersistentTrackID())
        audioCompositionTrack.insertTimeRange(audioTrack.timeRange, ofTrack: audioTrack, atTime: CMTimeMake(0, 1), error: nil)

        var exportSession = AVAssetExportSession(asset: audioComposition, presetName: AVAssetExportPresetAppleM4A)
        var toFileUrl = NSURL(fileURLWithPath: audioPath)

        exportSession.outputURL = toFileUrl
        exportSession.outputFileType = "com.apple.m4a-audio"

        exportSession.exportAsynchronouslyWithCompletionHandler({ () -> Void in
            if exportSession.status == AVAssetExportSessionStatus.Completed {
                println("Succes")
            }else{
                println("not working")
            }
        })

    })

}

我使用上述代码从视频中获取音频,但它会输出“not working”。
我的音频路径是: var outStr = NSBundle.mainBundle().pathForResource("cheeseburger", ofType: "m4a") 请帮我解决这个问题。
谢谢。
2个回答

11

由于几个 API 的更改,我已经重新编写了 Swift 4.0 的答案。

import AVFoundation

extension AVAsset {
    // Provide a URL for where you wish to write
    // the audio file if successful
    func writeAudioTrack(to url: URL,
                         success: @escaping () -> (),
                         failure: @escaping (Error) -> ()) {
        do {
            let asset = try audioAsset()
            asset.write(to: url, success: success, failure: failure)
        } catch {
            failure(error)
        }
    }

    private func write(to url: URL,
                       success: @escaping () -> (),
                       failure: @escaping (Error) -> ()) {
        // Create an export session that will output an
        // audio track (M4A file)
        guard let exportSession = AVAssetExportSession(asset: self,
                                                       presetName: AVAssetExportPresetAppleM4A) else {
                                                        // This is just a generic error
                                                        let error = NSError(domain: "domain",
                                                                            code: 0,
                                                                            userInfo: nil)
                                                        failure(error)

                                                        return
        }

        exportSession.outputFileType = .m4a
        exportSession.outputURL = url

        exportSession.exportAsynchronously {
            switch exportSession.status {
            case .completed:
                success()
            case .unknown, .waiting, .exporting, .failed, .cancelled:
                let error = NSError(domain: "domain", code: 0, userInfo: nil)
                failure(error)
            }
        }
    }

    private func audioAsset() throws -> AVAsset {
        // Create a new container to hold the audio track
        let composition = AVMutableComposition()
        // Create an array of audio tracks in the given asset
        // Typically, there is only one
        let audioTracks = tracks(withMediaType: .audio)

        // Iterate through the audio tracks while
        // Adding them to a new AVAsset
        for track in audioTracks {
            let compositionTrack = composition.addMutableTrack(withMediaType: .audio,
                                                               preferredTrackID: kCMPersistentTrackID_Invalid)
            do {
                // Add the current audio track at the beginning of
                // the asset for the duration of the source AVAsset
                try compositionTrack?.insertTimeRange(track.timeRange,
                                                      of: track,
                                                      at: track.timeRange.start)
            } catch {
                throw error
            }
        }

        return composition
    }
}

然后,您调用扩展程序,并依赖不同的闭包来处理成功和失败。此示例中的错误处理非常简单,因此在实施时应改进。

asset.writeAudioTrack(to: url, success: {
    print("Success")
}) { (error) in
    print(error.localizedDescription)
}

操作无法完成。(域错误0),但它只对某些文件执行此操作,否则它可以正常工作。 - Yaroslav Dukal
显示如下:错误域=域代码=0“(空)” - Sharad Chauhan
1
@sharadchauhan 这是通用的错误,我创建它来指示导出会话创建失败。这很可能表明您正在使用的资产有问题。首先检查确保它具有音频轨道。 - CodeBender
@CodeBender 它有。但它也有绿屏。那可能是问题吗? - Sharad Chauhan

5

来自我的项目(Swift 4.1

import AVFoundation
extension AVAsset {

    func writeAudioTrackToURL(_ url: URL, completion: @escaping (Bool, Error?) -> ()) {
        do {
            let audioAsset = try self.audioAsset()
            audioAsset.writeToURL(url, completion: completion)
        } catch (let error as NSError){
            completion(false, error)
        }
    }

    func writeToURL(_ url: URL, completion: @escaping (Bool, Error?) -> ()) {

        guard let exportSession = AVAssetExportSession(asset: self, presetName: AVAssetExportPresetAppleM4A) else {
            completion(false, nil)
            return
        }

        exportSession.outputFileType = .m4a
        exportSession.outputURL      = url

        exportSession.exportAsynchronously {
            switch exportSession.status {
            case .completed:
                completion(true, nil)
            case .unknown, .waiting, .exporting, .failed, .cancelled:
                completion(false, nil)
            }
        }
    }

    func audioAsset() throws -> AVAsset {

        let composition = AVMutableComposition()
        let audioTracks = tracks(withMediaType: .audio)

        for track in audioTracks {

            let compositionTrack = composition.addMutableTrack(withMediaType: .audio, preferredTrackID: kCMPersistentTrackID_Invalid)
            try compositionTrack?.insertTimeRange(track.timeRange, of: track, at: track.timeRange.start)
            compositionTrack?.preferredTransform = track.preferredTransform
        }
        return composition
    }
}

使用方法如下

    let url = Bundle.main.url(forResource: "video", withExtension: "m4v")!
    let asset = AVURLAsset(url: url, options: nil)

    let pathWhereToSave = "<#path#>"
    asset.writeAudioTrackToURL(URL(fileURLWithPath: pathWhereToSave)) { (success, error) -> () in
        if !success {
            print(error)
        }
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接