iOS swift 将 mp3 转换为 aac

2023-11-27

我正在转换一个mp3 to m4a在 Swift 中,代码基于this.

当我生成 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
    }
}

将问题中的源代码更新为 Swift 4 并将其包装在一个类中。信用去往Castles and 节奏拳手获取原始源代码和答案。留下作者的评论,添加一些断言和打印语句以进行调试。在 iOS 上测试。

输出文件的比特率硬编码为 96kb/s,您可以轻松覆盖该值。我要转换的大多数音频文件都是 320kb/s,因此我使用此类来压缩文件以供离线存储。压缩结果位于此答案的底部。

Usage:

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

Class

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)
----
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

iOS swift 将 mp3 转换为 aac 的相关文章

随机推荐

  • MySQLi 中的 SELECT * FROM

    我的网站相当广泛 而且我最近刚刚切换到 PHP5 请称我为大器晚成者 我之前的所有 MySQL 查询都是这样构建的 SELECT FROM tablename WHERE field1 value field2 value2 这使得一切变得
  • 在两个圆之间画一个箭头?

    如何在两个圆之间绘制箭头线 给定 圆心的位置 圆的半径 我在用line and markersvg 对象 If I draw the arrows to the center of the circle then the arrow is
  • 为什么这个多重绑定不起作用

    我从我的复选框命令发送了多个参数 我用过转换器 代码如下 如果我放置一个调试器并看到这里的值就是我的结果 当复选框检查被选中或取消选中时 在转换器中 它具有值 项目对象和布尔值的数组 但是当我使用我的方法时 该值是一个对象 2 但两个值都是
  • 为单元测试创​​建对象 MockHttpServletResponse 时出错

    我试图使用 sprint test 为 Servlet 编写单元测试 模拟对象 我的 Maven 依赖项是
  • 将 double 转换为小数点后特定数字的科学记数法

    我想将双精度转换为科学计数法 如下所示 0 00752382528 gt 752383E 1 我可以用 ToString 或 Regex 来做到这一点吗 您可以使用标准格式字符串对于科学计数法 0 00752382528 ToString
  • SSL 证书链不同;如何验证?

    简洁版本 我看到 SSL 证书链根据我访问 https 服务器的方式而有所不同 这是怎么回事 在这种情况下我该如何验证证书 稍微长一点的版本 我正在尝试使用 libcurl 来验证 SSL 连接的证书 我连接到的服务器是 Amazon S3
  • 如何在Python中获取文件关闭事件

    在 windows 7 64 位机器上使用 python 2 7 如何获取文件关闭事件 当文件在文件打开器的新进程中打开时 例如记事本 写字板 每次在写字板的新进程中打开文件 当文件在文件打开器的选项卡中打开时 例如notepad 它在新选
  • 无法在 Heroku 上进行 pg_restore:“无法从输入文件读取:文件结尾”

    我正在尝试将本地 PostgreSQL 数据库复制到 Heroku 应用程序pg dump pg restore实用程序 按照 Heroku 的官方指南进行 https devcenter heroku com articles herok
  • 递归引用数据框

    有没有办法让数据框引用自身 我发现自己花了很多时间写类似的东西y Category1 is na y Category1 lt NULL这些内容很难阅读 而且感觉就像是大量缓慢的重复打字 我想知道是否有类似的内容 y Category1 i
  • SQL 嵌套替换

    我有一个复杂的嵌套替换 我用它来连接 MSSQL 中的两个表 select from A left outer join select from B on replace replace replace replace replace A
  • Numpy 源代码中的常量是在哪里定义的?

    我试图找到常量在 numpy 中存储的位置 一些方向会很好 里面定义了很多常量 numpy core include numpy npy math h 当前定义的是 从第 48 行开始 define NPY INFINITYF npy in
  • 如何从结构元素的嵌套数组创建 Spark DataFrame?

    我已将 JSON 文件读入 Spark 该文件具有以下结构 scala gt tweetBlob printSchema root related struct nullable true next struct nullable true
  • 在 ASP.NET Core 2 中获取用户 ID

    我正在尝试获取 ASP NET Core 2 1 MVC 项目中的用户 ID 但是 我只能收到电子邮件 我几乎肯定必须有一个 1 2 行的方法来获取它 在 ASP NET MVC 成员资格中 它只是 varloggingInUserId U
  • 使 JList 中的按钮可点击

    我不敢相信这行不通 我有一个 JList 我已将其渲染器设置如下 基本上RankingPanel是一个带有两个标签和一个按钮的 JPanel topAchieverList new JList topAchieverList setCell
  • 在 Python 中查找每一行的最大值

    如何在Python中找到每一行的最大值并将其存储在NumPy数组或Pandas DataFrame中并将其存储在NumPy数组中 即下面的输出 0 511474 0 488526 0 468783 0 531217 0 35111 0 64
  • java.io.FileNotFoundException:打开失败:EACCES(权限被拒绝)

    当我尝试将位图存储到存储中时出现此错误 File path new File Environment getExternalStoragePublicDirectory Environment DIRECTORY PICTURES pict
  • 如何让 tslint 监视特定文件夹中的更改?

    我正在使用 webpack 2 它会告诉我我的打字稿代码是否存在编译问题 但是 我还没有找到一种方法来通过它运行 tslint 并让它在 webpack 在开发服务器模式下运行时检测到的每个更改中运行 我尝试过tslint 加载程序工作 但
  • 在 JBoss EAP 7 中配置 Http 标头

    您知道是否有标准方法来配置 JBoss EAP 7 发送到客户端的 Http 标头 我主要感兴趣的是能够配置以下内容 X XSS 保护 X 框架选项 严格的运输安全 内容安全策略 X 内容类型选项 我在互联网上找到了这个链接 https b
  • 使用 CSS3 模糊滤镜时图像周围出现白色模糊?

    今天我想重新设计我的网站并以不同的方式处理它 我并没有专注于排版 而是添加了大图像和小文本 我简单地做了 html background url img background png no repeat center center fixe
  • iOS swift 将 mp3 转换为 aac

    我正在转换一个mp3 to m4a在 Swift 中 代码基于this 当我生成 PCM 文件时它起作用 当我将导出格式更改为 m4a 时 它会生成一个文件 但无法播放 为什么会腐败 这是到目前为止的代码 import AVFoundati