Audio Unit和ExtendedAudioFile播放音频

前言

相关文章: 使用VideoToolbox硬编码H.264 使用VideoToolbox硬解码H.264 使用AudioToolbox编码AAC 使用AudioToolbox播放AAC HLS点播实现(H.264和AAC码流) HLS推流的实现(iOS和OS X系统) iOS在线音频流播放 Audio Unit播放PCM文件 Audio Unit录音(播放伴奏+耳返) Audio Unit播放aac/m4a/mp3等文件 前文介绍了AudioUnit的录音/播放功能,也介绍了通过AudioConvert进行音频的转换,但是AudioConvert的API使用起来较为麻烦,除了需要调用AudioFileGetProperty获取许多信息之外,还要调用AudioConverterFillComplexBuffer进行ConvertBuffer的填充,并在其数据输入回调中调用AudioFileReadPacketData,且要考虑AudioStreamPacketDescription的赋值。 本文尝试使用更为简单的方法 Extended Audio File Services。 Extended Audio File Services是high-level的API,提供音频文件的读/写,是Audio File Services 和 Audio Converter Services 的结合,在AudioFile和AudioConvert的基础上提供统一的接口进行读写操作。

正文

概念储备

  • ExtAudioFileOpenURL是新建一个ExtAudioFileRef,用于读取音频文件;
  • ExtAudioFileWrapAudioFileID是通过一个已有的AudioFileID,创建一个ExtAudioFileRef; 开发者必须保证在ExtAudioFileRef被销毁前,AudioFileID是处于打开的状态,并且在ExtAudioFileRef被销毁后,手动关闭AudioFileID;
  • ExtAudioFileGetProperty 获取对应PropertyID的属性;
  • ExtAudioFileGetProperty 获取设置PropertyID的属性; ExtAudioFile对应的PropertyID如下
CF_ENUM(ExtAudioFilePropertyID) {
    kExtAudioFileProperty_FileDataFormat        = 'ffmt',   // AudioStreamBasicDescription
    kExtAudioFileProperty_FileChannelLayout     = 'fclo',   // AudioChannelLayout
    kExtAudioFileProperty_ClientDataFormat      = 'cfmt',   // AudioStreamBasicDescription
    kExtAudioFileProperty_ClientChannelLayout   = 'cclo',   // AudioChannelLayout
    kExtAudioFileProperty_CodecManufacturer     = 'cman',   // UInt32
    
    // read-only:
    kExtAudioFileProperty_AudioConverter        = 'acnv',   // AudioConverterRef
    kExtAudioFileProperty_AudioFile             = 'afil',   // AudioFileID
    kExtAudioFileProperty_FileMaxPacketSize     = 'fmps',   // UInt32
    kExtAudioFileProperty_ClientMaxPacketSize   = 'cmps',   // UInt32
    kExtAudioFileProperty_FileLengthFrames      = '#frm',   // SInt64
    
    // writable:
    kExtAudioFileProperty_ConverterConfig       = 'accf',   // CFPropertyListRef
    kExtAudioFileProperty_IOBufferSizeBytes     = 'iobs',   // UInt32
    kExtAudioFileProperty_IOBuffer              = 'iobf',   // void *
    kExtAudioFileProperty_PacketTable           = 'xpti'    // AudioFilePacketTableInfo
};

介绍其中常用的属性:

  • kExtAudioFileProperty_FileDataFormat:读取文件格式,只读,返回文件的ASBD;
  • kAudioFormatProperty_FormatInfo:根据给定的格式,尽可能填充格式的其他信息。
  • kExtAudioFileProperty_ClientDataFormat:设置这个属性,才能进行对非pcm格式的文件进行编解码,这个格式也是ExtAudioFileRead 和 ExtAudioFileWrite 时的格式。
  • kExtAudioFileProperty_FileLengthFrames:文件的长度,单位是sample frames,获取前需要先设置好输入和输出的格式;
  • kExtAudioFileProperty_AudioConverter,是获取系统的AudioConverterRef,如果在获取之后,手动修改converter的属性,比如说码率,必须通过kExtAudioFileProperty_ConverterConfig设置ExtAudioFileRef;
  • kExtAudioFileError_CodecUnavailableInputConsumed:当ExtAudioFileWrite被打断的时候会返回这个错误,需要先停止调用ExtAudioFileWrite,等待audioSession恢复,并调用AudioSessionSetActive,再进行resuming;与kExtAudioFileError_CodecUnavailableInputNotConsumed的区别是,前者的buffer已经被使用,下次调用需要赋值新的buffer,后者需要再次提供相同的buffer;

具体细节

  • 1、初始化AVAudioSession和AudioBufferList;
  • 2、通过url打开ExtAudioFileRef,并通过ExtAudioFileGetProperty获取文件格式;初始化读取的格式,并通过ExtAudioFileSetProperty设置给ExtAudioFileRef;输入和输出格式设置,类似初始化AudioConvert的过程。
  • 3、初始化AudioUnit,并设置输入的格式与ExtAudioFileRef的输出格式一致;
  • 4、在AudioUnit的播放回调中调用ExtAudioFileRead读取ExtAudioFileRef的数据,如果读取返回的数组长度是0表示播放结束;

demo播放

遇到的问题

1、获取的音频frame帧数不正常

如果在未设置好输入输出格式前,就通过kExtAudioFileProperty_FileLengthFrames获取的总frame数,此时获取的frame是不准确的,并且会导致后续的操作错误。 正确的做法是先设置好 kExtAudioFileProperty_ClientDataFormat属性的值,再获取总的frame数。

2、播放进度不准确

播放的进度=当前播放的帧数/音频文件的总帧数; 进度不准确问题是因为获取的是frame数,之前在计算已播放的帧数时没有正确的把读取的字节长度除以输出格式的mBytesPerFrame。 当前已播放的帧数 += 读取的字节长度 / ASBD.mBytesPerFrame。

总结

ExtendedAudioFile相对Audio File Services 和 Audio Converter Services ,API调用非常简单和明确,并且不需要去处理AudioStreamPacketDescription,在实际开发中逻辑更为清晰。 demo 的代码在这里,可以看到ExtendedAudioFile具体使用方式。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Java进阶架构师

Intellij IDEA神器那些让人爱不释手的小技巧

之前写了一篇介绍IntellIJ IDEA的文章,主要是列出一些平时大家可能没用过或者没怎么用,但是又非常好用的IntellIJ IDEA小技巧。由于篇幅原因,...

11410
来自专栏Golang语言社区

golang微信支付服务端

一般来说,使用golang主要还是写服务端。所以本文主要讲golang在处理微信移动支付的服务端时的统一下单接口和支付回调接口,以及查询接口。 微信支付流程 下...

1.7K70
来自专栏搞前端的李蚊子

ReactJs移动端兼容问题汇总

A:初步怀疑是css属性没有加前缀引发的兼容问题,但添加后发现也不行,通过webview调试后控制台输出Set is undefined,搜索后发现React依...

27850
来自专栏非著名程序员

倍数提高工作效率的 Android Studio 奇技

这是从Philippe Breault的系列文章《Android Studio Tips Of the Day》中提取出来的自认为精华的部分。这些技巧在实际应用...

25190
来自专栏GIS讲堂

Arcgis Add-In开发入门实例

作为一个本科侧重于应用,工作之后却做了开发的程序员来说,做GIS,开发应该是一门必修课,只是,苦于各种原因吧,做GIS应用的人会开发的很少,做GIS开发的大部分...

27250
来自专栏Google Dart

开始使用-编写你的第一个Flutter应用程序 顶

这是创建您的第一个Flutter应用程序的指南。 如果您熟悉面向对象的代码和基本编程概念(如变量,循环和条件),则可以完成本教程。 您不需要以前使用Dart或移...

11620
来自专栏平凡文摘

Intellij IDEA 那些隐藏好用的小技巧

20840
来自专栏Flutter入门

Flutter入门三部曲(1) - 基础认识

image.png 看到整体的架构图,它是由dart完成上层的framework,然后由通过skia来完成图形的绘制。

1.7K60
来自专栏听雨堂

ASP.Net Web Page深入探讨

这篇文章经典,看过之后大受启发。值得一看!看来ASP.NET跟JSP其实是一样的,本质上没区别,ASP.NET能做到的JSP一样可以做到,反之亦然。只不过ASP...

29770
来自专栏FreeBuf

WinRar 4.20 – 文件扩展名欺骗(0Day)

WinRar 是常用的压缩与解压缩软件工具。它能将数据压缩成.rar或则.zip格式的包。这篇文章就是给大家呈现Winrar 4.20的一个最新漏洞(0 day...

20580

扫码关注云+社区

领取腾讯云代金券