2015-11-12 67 views
3

我基于thisiOS的迅速转换MP3成AAC

它工作时,我产生一个PCM文件转换的MP3 M4A 斯威夫特与代码。当我将导出格式更改为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. 
} 
} 

回答

3

更新

你要创建一个caf文件,而不是一个m4a

AVAssetWriter(URL: self.outputURL, fileType: AVFileTypeCoreAudioFormat) 

呼叫self.assetWriter.finishWritingWithCompletionHandler()更换AVFileTypeCoreAudioFormatAVFileTypeAppleM4A当你完成。

+0

感谢您的回复,但那没有奏效。完成处理程序正在触发,但该文件仍然损坏。 – Castles

+0

你能提供一个文件链接吗? –

+0

https://www.dropbox.com/s/id384qko46kj4tb/output.m4a?dl=0 – Castles

1

将问题中的源代码更新到Swift 4并将其包装在类中。积分城堡Rythmic Fistman为原始的源代码和答案。留下作者的评论,添加了一些断言和打印语句进行调试。在iOS上测试。

输出文件的比特率是96kb/s硬编码的,你可以很容易地覆盖这个值。我要转换的大多数音频文件都是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 
    } 
} 

输入文件: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 (L R) 
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 (L R) 
----