问题
iOS15之后,推送多条语音会产生多条横幅,对于动态金额语音,多条横幅是不可取的
解决方案
- 做版本管理,iOS15以上,用新的解决方案实现,iOS15以下还是沿用旧的推送方案
/// !!!!: 推送语音播报总控制逻辑
/// @param sourceURLsArr mp3源文件数组
/// @param bestAttemptContent
/// @param completed
-(void)pushVoiceNotificationWithWithSourceURLs:(NSArray *)sourceURLsArr
bestAttemptContent:(UNMutableNotificationContent *)bestAttemptContent
completed:(XSNotificationPushCompleted)completed
{
if (@available(iOS 15.0, *)) {
// 合并音频文件生成新的音频
[self mergeAVAssetWithSourceURLs:sourceURLsArr completed:^(NSString *soundName, NSURL *soundsFileURL) {
if (!soundName) {
NSLog(@"声音生成失败!");
completed();
return;
}
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 150000
if (@available(iOS 15.0, *)) {
bestAttemptContent.interruptionLevel = UNNotificationInterruptionLevelTimeSensitive;
}
#endif
UNNotificationSound * sound = [UNNotificationSound soundNamed:soundName];
bestAttemptContent.sound = sound;
completed();
}];
}
else//iOS15以下,改用原来旧方式实现
{
[self pushLocalNotificationIniOS14ToApp:0 withArray:sourceURLsArr completed:^{
completed();
}];
}
}
- 新方案里面,通过NSFileManager把输出音频保存在【AppGroup】的/Library/Sounds/里面,坑点就是,AVAssetExportSession的输出路径必须要保证文件夹存在,不然会提示操作有误,当时直接通过contentsOfDirectoryAtPath来生成两个文件夹,结果不行, 必须要逐个生成,并且要留意生成的文件后缀要符合输出格式要求
///在AppGroup中合并音频
- (void)mergeAVAssetWithSourceURLs:(NSArray *)sourceURLsArr completed:(void (^)(NSString * soundName,NSURL * soundsFileURL)) completed{
//创建音频轨道,并获取多个音频素材的轨道
AVMutableComposition *composition = [AVMutableComposition composition];
//音频插入的开始时间,用于记录每次添加音频文件的开始时间
__block CMTime beginTime = kCMTimeZero;
[sourceURLsArr enumerateObjectsUsingBlock:^(id _Nonnull audioFileURL, NSUInteger idx, BOOL * _Nonnull stop) {
//获取音频素材
AVURLAsset *audioAsset1 = [AVURLAsset assetWithURL:[NSURL fileURLWithPath:audioFileURL]];
//音频轨道
AVMutableCompositionTrack *audioTrack1 = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:0];
//获取音频素材轨道
AVAssetTrack *audioAssetTrack1 = [[audioAsset1 tracksWithMediaType:AVMediaTypeAudio] firstObject];
//音频合并- 插入音轨文件
[audioTrack1 insertTimeRange:CMTimeRangeMake(kCMTimeZero, audioAsset1.duration) ofTrack:audioAssetTrack1 atTime:beginTime error:nil];
// 记录尾部时间
beginTime = CMTimeAdd(beginTime, audioAsset1.duration);
}];
//用动态日期会占用空间
// NSDateFormatter *formater = [[NSDateFormatter alloc] init];
// [formater setDateFormat:@"yyyy-MM-dd-HH:mm:ss-SSS"];
// NSString * timeFromDateStr = [formater stringFromDate:[NSDate date]];
// NSString *outPutFilePath = [NSHomeDirectory() stringByAppendingFormat:@"/tmp/sound-%@.mp4", timeFromDateStr];
NSURL *groupURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:kAppGroupID];
// NSURL * soundsURL = [groupURL URLByAppendingPathComponent:@"/Library/Sounds/" isDirectory:YES];
//建立文件夹
NSURL * soundsURL = [groupURL URLByAppendingPathComponent:@"Library/" isDirectory:YES];
if (![[NSFileManager defaultManager] contentsOfDirectoryAtPath:soundsURL.path error:nil]) {
[[NSFileManager defaultManager] createDirectoryAtPath:soundsURL.path withIntermediateDirectories:YES attributes:nil error:nil];
}
//建立文件夹
NSURL * soundsURL2 = [groupURL URLByAppendingPathComponent:@"Library/Sounds/" isDirectory:YES];
if (![[NSFileManager defaultManager] contentsOfDirectoryAtPath:soundsURL2.path error:nil]) {
[[NSFileManager defaultManager] createDirectoryAtPath:soundsURL2.path withIntermediateDirectories:YES attributes:nil error:nil];
}
// 新建文件名,如果存在就删除旧的
NSString * soundName = [NSString stringWithFormat:@"sound.m4a"];
NSString *outPutFilePath = [NSString stringWithFormat:@"Library/Sounds/%@", soundName];
NSURL * soundsFileURL = [groupURL URLByAppendingPathComponent:outPutFilePath isDirectory:NO];
// NSString * filePath = soundsURL.absoluteString;
if ([[NSFileManager defaultManager] fileExistsAtPath:soundsFileURL.path]) {
[[NSFileManager defaultManager] removeItemAtPath:soundsFileURL.path error:nil];
}
//导出合并后的音频文件
//音频文件目前只找到支持m4a 类型的
AVAssetExportSession *session = [[AVAssetExportSession alloc]initWithAsset:composition presetName:AVAssetExportPresetAppleM4A];
// 音频文件输出
session.outputURL = soundsFileURL;
session.outputFileType = AVFileTypeAppleM4A; //与上述的`present`相对应
session.shouldOptimizeForNetworkUse = YES; //优化网络
[session exportAsynchronouslyWithCompletionHandler:^{
if (session.status == AVAssetExportSessionStatusCompleted) {
NSLog(@"合并成功----%@", outPutFilePath);
if (completed) {
completed(soundName,soundsFileURL);
}
} else {
// 其他情况, 具体请看这里`AVAssetExportSessionStatus`.
NSLog(@"合并失败----%ld", (long)session.status);
if (completed) {
completed(nil,nil);
}
}
}];
}
- iOS15以下方案不变,通过循环递归推送多条语音信息来实现
////循环调用本地通知,播放音频文件
-(void)pushLocalNotificationIniOS14ToApp:(NSInteger)index withArray:(NSArray *)tmparray completed:(XSNotificationPushCompleted)completed{
__block NSInteger tmpindex = index;
if(tmpindex < [tmparray count]){
NSString *audioFileURL = tmparray[tmpindex];
NSString * mp3Name = [audioFileURL lastPathComponent];
AVURLAsset *audioAsset=[AVURLAsset URLAssetWithURL:[NSURL fileURLWithPath:audioFileURL] options:nil];
//获取本地mpe3e文件时长
CMTime audioDuration = audioAsset.duration;
float audioDurationSeconds = CMTimeGetSeconds(audioDuration);
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init]; //标题
content.sound = [UNNotificationSound soundNamed:[NSString stringWithFormat:@"%@",mp3Name]];
content.body = @"";
// repeats,是否重复,如果重复的话时间必须大于60s,要不会报错
UNTimeIntervalNotificationTrigger *trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:0.01 repeats:NO];
/* */
//添加通知的标识符,可以用于移除,更新等搡作
NSString * identifier = [[NSUUID UUID] UUIDString];
UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:identifier content:content trigger:trigger];
[center addNotificationRequest:request withCompletionHandler:^(NSError *_Nullable error) {
//第一条推送成功后,递归执行
float time = audioDurationSeconds+0.1;
if (![mp3Name containsString:@"pre"]) {
time = 0.4;
}
tmpindex = tmpindex+1;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, time * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[self pushLocalNotificationIniOS14ToApp:tmpindex withArray:tmparray completed:completed];
});
}];
}else{
completed();
}
}
参考:
https://www.jianshu.com/p/a01c0b59b9c4
https://juejin.cn/post/7026639897289031687