AV Foundation 是iOS 和 OS X系统中用于处理基于时间的媒体数据的高级OC框架。AV Foundation 的构建考虑到了目前的硬件环境和应用程序,设计过程高度依赖多线程机制。充分利用了多核硬件的优势并且大量使用block和GCD机制。
下图是AV Foundation的适用范围
Core Audio
Core Audio是OS X和iOS系统上处理所有音频事件的框架。Core Audio是又多个框架一起的总称,为音频和MIDI内容的录制、播放和处理提供相应接口。
Core Video
Core Video是OS X和iOS系统上针对数字视频所提供的管道模式。Core Video为Core Media提供图片缓存和缓存池支持,提供了一个能够对数字视频逐帧访问的接口。
Core Media
Core Media是AV Foundation所用到的底层级媒体管道的一部分。他提供针对音频样本和视频帧处理所需的低层级数据类型和接口。Core Media还提供了AV Foundation用到的机遇CMTime数据类型的时基模型。CMTime及其相关数据类型一般在AV Foundation处理基于时间的操作时使用.
Core Animation
Core Animation主要提供苹果平台所具有的美观、流畅的动画效果。支持OpenGL 和 OpenGL ES功能。
接下来介绍一下AVFoundation的一些功能
1、文字转语音功能:
使用NSSpeechSynthesizer类实现该功能,自己创建了一个LKSpeechManager类,这个类有一个简单的接口,代码头导入了<Foundation/Foundation.h>。这个类的核心方法是beginConversation,会立即创建并开启(文本转语音)功能并运行。
#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>
@interface LKSpeechManager : NSObject
@property (nonatomic, strong, readonly) AVSpeechSynthesizer *synthesizer;
+ (instancetype) speechManager;
- (void)beginConversation;
@end
#import "LKSpeechManager.h"
@interface LKSpeechManager ()<AVSpeechSynthesizerDelegate>
@property (nonatomic, strong) AVSpeechSynthesizer *synthesizer;
@property (nonatomic, strong) NSArray *voices;
@property (nonatomic, strong) NSArray *speechStrings;
@end
@implementation LKSpeechManager
+ (instancetype) speechManager {
return [[self alloc] init];
}
- (instancetype)init {
self = [super init];
if (self) {
_synthesizer = [[AVSpeechSynthesizer alloc] init];
_synthesizer.delegate = self;
//自带的文字转语音
_voices = @[[AVSpeechSynthesisVoice voiceWithLanguage:@"en-US"],[AVSpeechSynthesisVoice voiceWithLanguage:@"en-GB"],[AVSpeechSynthesisVoice voiceWithLanguage:@"en-GB"],[AVSpeechSynthesisVoice voiceWithLanguage:@"zh-CN"]];
//
_speechStrings = [self buildSpeechStrings];
}
return self;
}
- (NSArray *)buildSpeechStrings {
return @[@"Hello AV Foundation. How are you?",
@"I'm fine! Thank you. And you?",
@"I'm fine too!",
@"小家伙挺能干"
];
}
#pragma mark-------delegate
-(void)speechSynthesizer:(AVSpeechSynthesizer*)synthesizer didFinishSpeechUtterance:(AVSpeechUtterance*)utterance API_AVAILABLE(ios(7.0), watchos(1.0), tvos(7.0), macos(10.14)){
NSLog(@"结束%@",utterance.speechString);
}
1、在类的扩展部分定义类所需的属性,重新对之前的头部定义的synthesizer属性进行定义,这样就可以支持读写操作。此外,还需要为具体对话中用到的声音和语音字符串定义属性。
2、创建一个新的AVSpeechSynthesizer实例。该对象用于执行具体的“文本到语音”会话。对于多个AVSpeechUtterance实例,该对象起到队列的作用,提供了接口来对正在进行的语音播放进行控制和监视。
3、创建一个包含几个AVSpeechSynthesisVoice 实例的NSArray对象。指定某个声音播放。目前对于声音的支持有限,可以通过调用 AVSpeechSynthesisVoice 的 speechVoices类方法查看可支持的语音语种。
- (void)beginConversation{
for (NSUInteger i = 0; i < self.speechStrings.count; i++){
//控制和监视正在进行的语音播放
AVSpeechUtterance *utterance = [[AVSpeechUtterance alloc] initWithString:self.speechStrings[i]];
utterance.voice = self.voices[i];
//速率
utterance.rate = 0.4f;
utterance.pitchMultiplier= 0.8f;
utterance.postUtteranceDelay = 0.1f;
[self.synthesizer speakUtterance:utterance];
}
}
beginConversation是文本转语音的核心方法,首先对语音字符串进行循环,并且每个循环都要创建一个新的AVSpeechUtterance实例,传递字符串到initWithString:初始化器;然后再预定义的两个语音之间向后进行切换,确定播放速率。在这里的最大速率和最小速率使用的是常量值AVSpeechUtteranceMinimumSpeechRate和AVSpeechUtteranceMaximumSpeechRate。由于这个常量值不固定,在新版iOS中可能会发生变化,所以要修改rate属性,可以按照百分比进行修改,更安全。
pitchMultiplier 可以播放改变音调,允许值一般在0.5和2之间。
postUtteranceDelay在语音合成器在播放下一句之前有一个短时间的暂停,相应的也有preUtteranceDelay属性
2、音频会话
音频会话在app和操作系统之间扮演着中间人的角色,它提供了一种简单实用的方法使OS得知app该如何与iOS音频环境进行交互。
所有的iOS app都有音频会话,无论有没有使用,都会有一个默认的预配置:
1.激活了音频播放,但是音频录制没有激活(访问麦克风功能)
2.当用户切换响铃/静音开关到“静音”模式时,app播放的音频都会消失
3.当设备显示解锁屏幕时,app的音频处于静音状态
4.当app播放音频时,所有后台播放的音频都处于静音状态。
对于一些媒体类app默认的音频会话不能满足该类app的需求,所以通过“分类”功能可以定义我们的需求。
AV Foundation定义了7种分类来描述app所使用的音频行为。
音频分类
如果开发者需要实现更复杂的功能,其中一些可以通过使用option和modes方法进行自定义开发。options可以让开发者使用一些附加行为,如使用Playback分类后,app允许将输出音频和背景声音进行混合。modes可以通过引入被定制的行为进一步对分类进行修改以满足一些特殊需求。视频聊天app常使用这些模式来获得需要的功能。
音频会话在app的生命周期中式可以修改的,但通常我们只配置一次,在应用程序启动的时候配置。
//配置音频会话
AVAudioSession *session = [AVAudioSession sharedInstance];
NSError *error;
//设置session类型为AVAudioSessionCategoryPlayback,AVAudioSessionCategoryRecord
if (![session setCategory:AVAudioSessionCategoryPlayback error:&error]) {
NSLog(@"Category Error: %@",[error localizedDescription]);
}
if (![session setCategory:AVAudioSessionCategoryRecord error:&error]) {
NSLog(@"Category Error: %@",[error localizedDescription]);
}
//是否激活该配置
if (![session setActive:YES error:&error]) {
NSLog(@"Activation Error: %@",[error localizedDescription]);
}
AVAudioSession提供了与app音频会话交互的接口,通过设置合适的分类。开发者可以为音频的播放指定需要的音频会话,其中指定一些行为,最后告知该音频会话激活该配置。
2.1 使用AVAudioPlayer
AVAudioPlayer构建与Core Audio中的C-base Audio Queue Services的最顶层。如果需要从网络流中播放音频,需要访问原始音频样本或者需要非常低的时延,那就不推荐使用AVAudioPlayer。
NSURL *fileUrl = [[NSBundle mainBundle] URLForResource:name withExtension:@"mp3"];
//因为有一个是wav格式,改成mp3后缀,所以转成data之后统一转成MP3格式
NSData *data = [NSData dataWithContentsOfURL:fileUrl];
NSError *error;
AVAudioPlayer *player = [[AVAudioPlayer alloc] initWithData:data fileTypeHint:AVFileTypeMPEGLayer3 error:&error];
CATextLayer
if (player) {
//无限循环
player.numberOfLoops = -1;
//允许修改速率
player.enableRate = YES;
[player prepareToPlay];
}else {
NSLog(@"Error creating player: %@",[error localizedDescription]);
}
AVAudioPlayer 调用play可以实现立即播放,pause方法可以对播放暂停,stop是停止播放。对外pause和stop 都是停止当前播放行为,主要的区别是stop方法会撤销调用prepareToPlay时所做的设置,而调用pause方法则不会。
AVAudioPlayer的其他方法,修改播放器的音量volume,定义从0.0-1.0
播放器pan值 pan 允许使用立体声播放声音,值从-1.0(极左)到1.0(极右)。默认0.0(居中)
调整播放速率:rate 范围从0.5(半速)到2.0(2倍速)
无缝循环次数numberOfLoops : -1(无限循环)
处理中断
有电话呼入,闹铃,FaceTime等都是中断情况,app的音视频会被打断,这个时候需要我们手动处理这种状态,所以我们要得到中断出现的通知AVAudioSessionInterruptionNotification,通知会包含一个带有许多重要信息的userInfo字典,根据这个字典可以确定采取哪些合适的操作。
- (void)interrupttionNoti:(NSNotification *)noti{
NSDictionary *info = noti.userInfo;
//中断类型(中段开始和结束)
AVAudioSessionInterruptionType type = [info[AVAudioSessionInterruptionTypeKey] unsignedIntegerValue];
if (type == AVAudioSessionInterruptionTypeBegan) { //中断开始
//AVAudioSessionInterruptionTypeBegan
if (self.delegate && [self.delegate respondsToSelector:@selector(playbackStop)]) {
[self.delegate playbackStop];
}
}else { //中断结束
// AVAudioSessionInterruptionTypeEnded
AVAudioSessionInterruptionOptions options = [info[AVAudioSessionInterruptionOptionKey] unsignedIntegerValue];
if (options == AVAudioSessionInterruptionOptionShouldResume) {
[self play];
if (self.delegate && [self.delegate respondsToSelector:@selector(playbackBegan)]) {
[self.delegate playbackBegan];
}
}
}
}
首先通过检索AVAudioSessionInterruptionTypeKey的值可以确定中断类型,返回值是AVAudioSessionInterruptionType ,用于表示中断开始或结束的枚举类型。
如果中断类型是AVAudioSessionInterruptionTypeEnded,userInfo字典会包含一个AVAudioSessionInterruptionOptionKey表明音频会话是否已经重新激活以及是否可以再次播放。
线路改变的响应
除了中断之外,还有需要确保线路改变时也能做出正确的响应,比如插入耳机或断开麦克风,都需要正确的操作,比如按照苹果公司的相关文档,当耳机插入的时候音频应该继续播放,当拔出的时候音频应该处于静音状态,
所以有通知AVAudioSessionRouteChangeNotification。该通知带有相应通知发送的原因信息以及前一个线路的描述,这样我们就可以确定线路变化的情况了。
接收到通知的第一件事情是判断线路变更发生的原因。原因在userInfo中的AVAudioSessionRouteChangeReasonKey值,对于耳机断开,对应的是AVAudioSessionRouteChangeReasonOldDeviceUnavailable。
知道有设备断开连接后,还需要确定是否为耳机接口,在userInfo字典中AVAudioSessionRouteDescription用来描述前一个线路的信息。
//线路改变的通知
- (void)routeChangeNoti:(NSNotification *)noti {
NSDictionary *info = noti.userInfo;
AVAudioSessionRouteChangeReason reason = [info[AVAudioSessionRouteChangeReasonKey] unsignedIntegerValue];
// AVAudioSessionRouteChangeReasonNewDeviceAvailable = 1, //有新设备插入
// AVAudioSessionRouteChangeReasonOldDeviceUnavailable = 2,//老设备不可用 有设备断开连接
if (reason == AVAudioSessionRouteChangeReasonOldDeviceUnavailable) { //有设备断开连接
AVAudioSessionPortDescription *previousOutput = info[AVAudioSessionRouteChangePreviousRouteKey];
NSString *portType = previousOutput.portType;
if ([portType isEqualToString:AVAudioSessionPortHeadphones]) { //前一个类型是耳机
[self stop];
if (self.delegate && [self.delegate respondsToSelector:@selector(playbackStop)]) {
[self.delegate playbackStop];
}
}
}
}
该断开设备的设备类型也有好多种比如AVAudioSessionPortBuiltInReceiver为打电话时的听筒播放模式,这里就不一一描述了。
网友评论