iOS迅速转换成MP3到AAC
我正在使用基于此的代码在Swift中将mp3转换为m4a
它生成一个PCM文件时工作。 当我将输出格式更改为m4a时,它会生成一个文件,但不会播放。 为什么腐败?
这是迄今为止的代码:
import AVFoundation import UIKit class ViewController: UIViewController { var rwAudioSerializationQueue:dispatch_queue_t! var asset:AVAsset! var assetReader:AVAssetReader! var assetReaderAudioOutput:AVAssetReaderTrackOutput! var assetWriter:AVAssetWriter! var assetWriterAudioInput:AVAssetWriterInput! var outputURL:NSURL! override func viewDidLoad() { super.viewDidLoad() let rwAudioSerializationQueueDescription = String(self) + " rw audio serialization queue" // Create the serialization queue to use for reading and writing the audio data. self.rwAudioSerializationQueue = dispatch_queue_create(rwAudioSerializationQueueDescription, nil) let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true) let documentsPath = paths[0] print(NSBundle.mainBundle().pathForResource("input", ofType: "mp3")) self.asset = AVAsset(URL: NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("input", ofType: "mp3")! )) self.outputURL = NSURL(fileURLWithPath: documentsPath + "/output.m4a") print(self.outputURL) // [self.asset loadValuesAsynchronouslyForKeys:@[@"tracks"] completionHandler:^{ self.asset.loadValuesAsynchronouslyForKeys(["tracks"], completionHandler: { print("loaded") var success = true var localError:NSError? success = (self.asset.statusOfValueForKey("tracks", error: &localError) == AVKeyValueStatus.Loaded) // Check for success of loading the assets tracks. //success = ([self.asset statusOfValueForKey:@"tracks" error:&localError] == AVKeyValueStatusLoaded); if (success) { // If the tracks loaded successfully, make sure that no file exists at the output path for the asset writer. let fm = NSFileManager.defaultManager() let localOutputPath = self.outputURL.path if (fm.fileExistsAtPath(localOutputPath!)) { do { try fm.removeItemAtPath(localOutputPath!) success = true } catch { } } } if (success) { success = self.setupAssetReaderAndAssetWriter() } if (success) { success = self.startAssetReaderAndWriter() } }) } func setupAssetReaderAndAssetWriter() -> Bool { do { try self.assetReader = AVAssetReader(asset: self.asset) } catch { } do { try self.assetWriter = AVAssetWriter(URL: self.outputURL, fileType: AVFileTypeCoreAudioFormat) } catch { } var assetAudioTrack:AVAssetTrack? = nil let audioTracks = self.asset.tracksWithMediaType(AVMediaTypeAudio) if (audioTracks.count > 0) { assetAudioTrack = audioTracks[0] } if (assetAudioTrack != nil) { let decompressionAudioSettings:[String : AnyObject] = [ AVFormatIDKey:Int(kAudioFormatLinearPCM) ] self.assetReaderAudioOutput = AVAssetReaderTrackOutput(track: assetAudioTrack!, outputSettings: decompressionAudioSettings) self.assetReader.addOutput(self.assetReaderAudioOutput) var channelLayout = AudioChannelLayout() memset(&channelLayout, 0, sizeof(AudioChannelLayout)); channelLayout.mChannelLayoutTag = kAudioChannelLayoutTag_Stereo; /*let compressionAudioSettings:[String : AnyObject] = [ AVFormatIDKey:Int(kAudioFormatMPEG4AAC) , AVEncoderBitRateKey:128000, AVSampleRateKey:44100 , // AVEncoderBitRatePerChannelKey:16, // AVEncoderAudioQualityKey:AVAudioQuality.High.rawValue, AVNumberOfChannelsKey:2, AVChannelLayoutKey: NSData(bytes:&channelLayout, length:sizeof(AudioChannelLayout)) ] var outputSettings:[String : AnyObject] = [ AVFormatIDKey: Int(kAudioFormatLinearPCM), AVSampleRateKey: 44100, AVNumberOfChannelsKey: 2, AVChannelLayoutKey: NSData(bytes:&channelLayout, length:sizeof(AudioChannelLayout)), AVLinearPCMBitDepthKey: 16, AVLinearPCMIsNonInterleaved: false, AVLinearPCMIsFloatKey: false, AVLinearPCMIsBigEndianKey: false ]*/ let outputSettings:[String : AnyObject] = [ AVFormatIDKey: Int(kAudioFormatMPEG4AAC), AVSampleRateKey: 44100, AVNumberOfChannelsKey: 2, AVChannelLayoutKey: NSData(bytes:&channelLayout, length:sizeof(AudioChannelLayout)) ] self.assetWriterAudioInput = AVAssetWriterInput(mediaType: AVMediaTypeAudio, outputSettings: outputSettings) self.assetWriter.addInput(self.assetWriterAudioInput) } return true } func startAssetReaderAndWriter() -> Bool { self.assetWriter.startWriting() self.assetReader.startReading() self.assetWriter.startSessionAtSourceTime(kCMTimeZero) self.assetWriterAudioInput.requestMediaDataWhenReadyOnQueue(self.rwAudioSerializationQueue, usingBlock: { while(self.assetWriterAudioInput.readyForMoreMediaData ) { var sampleBuffer = self.assetReaderAudioOutput.copyNextSampleBuffer() if(sampleBuffer != nil) { self.assetWriterAudioInput.appendSampleBuffer(sampleBuffer!) sampleBuffer = nil } else { self.assetWriterAudioInput.markAsFinished() self.assetReader.cancelReading() print("done") break } } }) return true } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } }
更新
你正在创build一个caf
文件而不是m4a
。
用AVFileTypeCoreAudioFormat
replaceAVFileTypeCoreAudioFormat
AVAssetWriter(URL: self.outputURL, fileType: AVFileTypeCoreAudioFormat)
完成后请调用self.assetWriter.finishWritingWithCompletionHandler()
。
将问题中的源代码更新到Swift 4,并将其包装在一个类中。 信贷去Castle和Rythmic Fistman获取原始的源代码和答案。 留下作者的意见,添加了一些断言和打印语句进行debugging。 在iOS上testing
输出文件的比特率是96kb / s的硬编码,你可以很容易地覆盖这个值。 我正在转换的大部分audio文件都是320kb / s,所以我使用这个类来压缩文件进行离线存储。 压缩结果在这个答案的底部。
用法:
let inputFilePath = URL(fileURLWithPath: "/path/to/file.mp3") let outputFileURL = URL(fileURLWithPath: "/path/to/output/compressed.mp4") if let audioConverter = AVAudioFileConverter(inputFileURL: inputFilePath, outputFileURL: outputFileURL) { audioConverter.convert() }
类
import AVFoundation final class AVAudioFileConverter { var rwAudioSerializationQueue: DispatchQueue! var asset:AVAsset! var assetReader:AVAssetReader! var assetReaderAudioOutput:AVAssetReaderTrackOutput! var assetWriter:AVAssetWriter! var assetWriterAudioInput:AVAssetWriterInput! var outputURL:URL var inputURL:URL init?(inputFileURL: URL, outputFileURL: URL) { inputURL = inputFileURL outputURL = outputFileURL if (FileManager.default.fileExists(atPath: inputURL.absoluteString)) { print("Input file does not exist at file path \(inputURL.absoluteString)") return nil } } func convert() { let rwAudioSerializationQueueDescription = " rw audio serialization queue" // Create the serialization queue to use for reading and writing the audio data. rwAudioSerializationQueue = DispatchQueue(label: rwAudioSerializationQueueDescription) assert(rwAudioSerializationQueue != nil, "Failed to initialize Dispatch Queue") asset = AVAsset(url: inputURL) assert(asset != nil, "Error creating AVAsset from input URL") print("Output file path -> ", outputURL.absoluteString) asset.loadValuesAsynchronously(forKeys: ["tracks"], completionHandler: { var success = true var localError:NSError? success = (self.asset.statusOfValue(forKey: "tracks", error: &localError) == AVKeyValueStatus.loaded) // Check for success of loading the assets tracks. if (success) { // If the tracks loaded successfully, make sure that no file exists at the output path for the asset writer. let fm = FileManager.default let localOutputPath = self.outputURL.path if (fm.fileExists(atPath: localOutputPath)) { do { try fm.removeItem(atPath: localOutputPath) success = true } catch { print("Error trying to remove output file at path -> \(localOutputPath)") } } } if (success) { success = self.setupAssetReaderAndAssetWriter() } else { print("Failed setting up Asset Reader and Writer") } if (success) { success = self.startAssetReaderAndWriter() return } else { print("Failed to start Asset Reader and Writer") } }) } func setupAssetReaderAndAssetWriter() -> Bool { do { assetReader = try AVAssetReader(asset: asset) } catch { print("Error Creating AVAssetReader") } do { assetWriter = try AVAssetWriter(outputURL: outputURL, fileType: AVFileType.m4a) } catch { print("Error Creating AVAssetWriter") } var assetAudioTrack:AVAssetTrack? = nil let audioTracks = asset.tracks(withMediaType: AVMediaType.audio) if (audioTracks.count > 0) { assetAudioTrack = audioTracks[0] } if (assetAudioTrack != nil) { let decompressionAudioSettings:[String : Any] = [ AVFormatIDKey:Int(kAudioFormatLinearPCM) ] assetReaderAudioOutput = AVAssetReaderTrackOutput(track: assetAudioTrack!, outputSettings: decompressionAudioSettings) assert(assetReaderAudioOutput != nil, "Failed to initialize AVAssetReaderTrackOutout") assetReader.add(assetReaderAudioOutput) var channelLayout = AudioChannelLayout() memset(&channelLayout, 0, MemoryLayout<AudioChannelLayout>.size); channelLayout.mChannelLayoutTag = kAudioChannelLayoutTag_Stereo; let outputSettings:[String : Any] = [ AVFormatIDKey: Int(kAudioFormatMPEG4AAC), AVSampleRateKey: 44100, AVEncoderBitRateKey: 96000, AVNumberOfChannelsKey: 2, AVChannelLayoutKey: NSData(bytes:&channelLayout, length:MemoryLayout<AudioChannelLayout>.size)] assetWriterAudioInput = AVAssetWriterInput(mediaType: AVMediaType.audio, outputSettings: outputSettings) assert(rwAudioSerializationQueue != nil, "Failed to initialize AVAssetWriterInput") assetWriter.add(assetWriterAudioInput) } print("Finsihed Setup of AVAssetReader and AVAssetWriter") return true } func startAssetReaderAndWriter() -> Bool { print("STARTING ASSET WRITER") assetWriter.startWriting() assetReader.startReading() assetWriter.startSession(atSourceTime: kCMTimeZero) assetWriterAudioInput.requestMediaDataWhenReady(on: rwAudioSerializationQueue, using: { while(self.assetWriterAudioInput.isReadyForMoreMediaData ) { var sampleBuffer = self.assetReaderAudioOutput.copyNextSampleBuffer() if(sampleBuffer != nil) { self.assetWriterAudioInput.append(sampleBuffer!) sampleBuffer = nil } else { self.assetWriterAudioInput.markAsFinished() self.assetReader.cancelReading() self.assetWriter.finishWriting { print("Asset Writer Finished Writing") } break } } }) return true } }
input文件:17.3 MB
// generated with afinfo on mac File: D290A73C37B777F1.mp3 File type ID: MPG3 Num Tracks: 1 ---- Data format: 2 ch, 44100 Hz, '.mp3' (0x00000000) 0 bits/channel, 0 bytes/packet, 1152 frames/packet, 0 bytes/frame no channel layout. estimated duration: 424.542025 sec audio bytes: 16981681 audio packets: 16252 bit rate: 320000 bits per second packet size upper bound: 1052 maximum packet size: 1045 audio data file offset: 322431 optimized audio 18720450 valid frames + 576 priming + 1278 remainder = 18722304 ----
输出文件:5.1 MB
// generated with afinfo on Mac File: compressed.m4a File type ID: m4af Num Tracks: 1 ---- Data format: 2 ch, 44100 Hz, 'aac ' (0x00000000) 0 bits/channel, 0 bytes/packet, 1024 frames/packet, 0 bytes/frame Channel layout: Stereo (LR) estimated duration: 424.542041 sec audio bytes: 5019294 audio packets: 18286 bit rate: 94569 bits per second packet size upper bound: 763 maximum packet size: 763 audio data file offset: 44 not optimized audio 18722304 valid frames + 2112 priming + 448 remainder = 18724864 format list: [ 0] format: 2 ch, 44100 Hz, 'aac ' (0x00000000) 0 bits/channel, 0 bytes/packet, 1024 frames/packet, 0 bytes/frame Channel layout: Stereo (LR) ----