AVAssetExportSession выдает ошибку при экспорте AVAsset во временный iOS путь - PullRequest
0 голосов
/ 16 марта 2020

Я пытаюсь обрезать локальный MP3-файл, выбранный пользователем ранее, чтобы получить 18-секундный фрагмент. Этот фрагмент должен быть экспортирован во временный путь к файлу. Это мой код:

guard songUrl.startAccessingSecurityScopedResource() else {
        print("failed to access path")
        return
    }

    // Make sure you release the security-scoped resource when you are done.
    defer { songUrl.stopAccessingSecurityScopedResource() }

    // Use file coordination for reading and writing any of the URL’s content.
    var error: NSError? = nil
    NSFileCoordinator().coordinate(readingItemAt: songUrl, error: &error) { (url) in


        // Set temporary file path
        let temporaryDirectoryUrl: URL = FileManager.default.temporaryDirectory
        let temporaryDirectoryString: String = temporaryDirectoryUrl.absoluteString
        let temporaryFilename = ProcessInfo().globallyUniqueString + ".m4a"
        let temporaryFilepath = URL(string: (temporaryDirectoryString + temporaryFilename))!

        // shorten audio file
        let originalAsset = AVAsset(url: (url))

        if let exporter = AVAssetExportSession(asset: originalAsset, presetName: AVAssetExportPresetAppleM4A) {
            exporter.outputFileType = AVFileType.m4a
            exporter.outputURL = temporaryFilepath

            let originalDuration = Int64(CMTimeGetSeconds(originalAsset.duration))
            let halftime: Int64 = (originalDuration/2)
            let startTime = CMTimeMake(value: (halftime-9), timescale: 1)
            let stopTime = CMTimeMake(value: (halftime+9), timescale: 1)
            exporter.timeRange = CMTimeRangeFromTimeToTime(start: startTime, end: stopTime)
            print(CMTimeGetSeconds(startTime), CMTimeGetSeconds(stopTime))

            //Export audio snippet
            exporter.exportAsynchronously(completionHandler: {

                print("export complete \(exporter.status)")

                switch exporter.status {
                case  AVAssetExportSessionStatus.failed:

                    if let e = exporter.error {
                        print("export failed \(e)")
                    }

                case AVAssetExportSessionStatus.cancelled:
                    print("export cancelled \(String(describing: exporter.error))")

                default:
                    print("export complete")
                    self.shortenedExported(temporaryFilePath: temporaryFilepath)
                }
                })
        }
        else {
                print("cannot create AVAssetExportSession for asset \(originalAsset)")
        }
    }

Он печатает следующее:

экспорт завершен AVAssetExportSessionStatus

экспорт не выполнен Ошибка Domain = AVFoundationErrorDomain Code = -11800 "Операция не удалось завершить "UserInfo = {NSLocalizedFailureReason = Произошла неизвестная ошибка (-17508), NSLocalizedDescription = Операция не может быть завершена, NSUnderlyingError = 0x282368b40 {Ошибка домена = NSOSStatusErrorDomain Code = -17508" (null) "}}

Я не получаю сообщение об ошибке, когда использую MP3-файл из ресурсов моего пакета, используя Bundle.main.url(forResource: "sample_song", withExtension: "mp3") вместо URL Координатора

Заранее спасибо!

1 Ответ

0 голосов
/ 16 марта 2020

Для тех, у кого такая же проблема: я мог бы решить ее, используя AVMutableComposition():

// Access url
    guard songUrl.startAccessingSecurityScopedResource() else {
        print("failed to access path")
        return
    }

    // Make sure you release the security-scoped resource when you are done.
    defer { songUrl.stopAccessingSecurityScopedResource() }

    // Use file coordination for reading and writing any of the URL’s content.
    var error: NSError? = nil
    NSFileCoordinator().coordinate(readingItemAt: songUrl, error: &error) { (url) in

        // Set temporary file's path
        let temporaryDirectoryUrl: URL = FileManager.default.temporaryDirectory
        let temporaryFilename = ProcessInfo().globallyUniqueString
        let temporaryFilepath = temporaryDirectoryUrl.appendingPathComponent("\(temporaryFilename).m4a")

        // Prework
        let originalAsset = AVURLAsset(url: url)
        print(originalAsset)
        let composition = AVMutableComposition()
        let audioTrack: AVMutableCompositionTrack = composition.addMutableTrack(withMediaType: AVMediaType.audio, preferredTrackID: kCMPersistentTrackID_Invalid)!
        let originalDuration = Int64(CMTimeGetSeconds(originalAsset.duration))
        let startTime, stopTime: CMTime

        // Shorten audio file if longer than 20 seconds
        if originalDuration < 20 {
            startTime = CMTimeMake(value: 0, timescale: 1)
            stopTime = CMTimeMake(value: originalDuration, timescale: 1)
        }
        else {
            let halftime: Int64 = (originalDuration/2)
            startTime = CMTimeMake(value: (halftime-10), timescale: 1)
            stopTime = CMTimeMake(value: (halftime+10), timescale: 1)
        }

        // Export shortened file
        do {
            try audioTrack.insertTimeRange(CMTimeRangeFromTimeToTime(start: startTime, end: stopTime), of: originalAsset.tracks(withMediaType: AVMediaType.audio)[0], at: CMTime.zero)
            let assetExport = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetAppleM4A)!
            if FileManager.default.fileExists(atPath: temporaryFilepath.absoluteString) {
                try? FileManager.default.removeItem(atPath: temporaryFilepath.absoluteString)
                print("removed existing file")
            }
            assetExport.outputFileType = AVFileType.m4a
            assetExport.outputURL = temporaryFilepath
            assetExport.shouldOptimizeForNetworkUse = true
            assetExport.exportAsynchronously(completionHandler: {
                switch assetExport.status {
                case  AVAssetExportSessionStatus.failed:

                    if let e = assetExport.error {
                        print("export failed \(e)")
                    }
                case AVAssetExportSessionStatus.cancelled:
                    print("export cancelled \(String(describing: assetExport.error))")

                default:
                    print("export completed")
                }
            })
        }
        catch {
            print("error trying to shorten audio file")
        }
...