IOS 音频实现以及后台播放音频
Posted Andy__Wu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了IOS 音频实现以及后台播放音频相关的知识,希望对你有一定的参考价值。
1、首先,音频播放的实现,我这里使用的是AVPlayer。
AVAudioPlayer只能播放本地资源。当然还有别的播放方法这里就不列举了。
以下代码实现的是如下图所示的效果,点击图标可以暂停或者继续播放:
需要的属性:
@property (nonatomic, strong) AVPlayer *player;
@property (nonatomic, strong) UIImageView *playerView;
@property (nonatomic, strong) UILabel *timeL;
@property (nonatomic, strong) UIImageView *animationV;//加载动画
初始化属性
//
- (AVPlayer *)player
if (_player == nil)
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"%@%@",kVedioUrl,_playItemInfo[@"ypwj"]]];
AVAsset *avset = [AVAsset assetWithURL:url] ;
AVPlayerItem *item = [AVPlayerItem playerItemWithAsset:avset];
_item = item;
// 创建AVPlayer
_player = [AVPlayer playerWithPlayerItem:_item];
// 添加AVPlayerLayer
AVPlayerLayer *layer = [AVPlayerLayer playerLayerWithPlayer:self.player];
layer.frame = CGRectMake(self.view.bounds.size.width - 55, self.view.bounds.size.height - 100, 50, 50);
[self.view.layer addSublayer:layer];
//监听是否可播放
[_item addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
//监听缓存状态,可以添加加载动画
[_item addObserver:self forKeyPath:@"playbackBufferEmpty" options:NSKeyValueObservingOptionNew context:nil];
//监听是否播放完成
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playToTheEnd) name:AVPlayerItemDidPlayToEndTimeNotification object:_item];
//监听播放状态,播放还是暂停
[_player addObserver:self forKeyPath:@"timeControlStatus" options:NSKeyValueObservingOptionNew context:nil];
return _player;
- (UIImageView *)playerView
if (_playerView == nil)
_playerView = [[UIImageView alloc] init];
_playerView.image = [UIImage imageNamed:@"L0"];
//播放动画
NSMutableArray *imgArr = [NSMutableArray array];
for (int i = 0; i<4; i ++)
UIImage *image = [UIImage imageNamed:[NSString stringWithFormat:@"L%d",i]];
[imgArr addObject:image];
_playerView.animationImages = imgArr;
_playerView.animationDuration = 2.0;
_playerView.animationRepeatCount = 0;
_playerView.userInteractionEnabled = YES;
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(playOrPause)];
[_playerView addGestureRecognizer:tap];
return _playerView;
- (UIImageView *)animationV
//旋转动画
if (_animationV == nil)
_animationV = [[UIImageView alloc] init];
_animationV.image = [UIImage imageNamed:@"loadcc"];
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
//默认是顺时针效果,若将fromValue和toValue的值互换,则为逆时针效果
animation.fromValue = [NSNumber numberWithFloat:0.f];
animation.toValue = [NSNumber numberWithFloat: M_PI *2];
animation.duration = 2;
animation.autoreverses = NO;
animation.fillMode = kCAFillModeForwards;
animation.repeatCount = MAXFLOAT; //如果这里想设置成一直自旋转,可以设置为MAXFLOAT,否则设置具体的数值则代表执行多少次
[_animationV.layer addAnimation:animation forKey:nil];
return _animationV;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
播放音频
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"%@%@",kVedioUrl,_mp3Url]];
AVAsset *avset = [AVAsset assetWithURL:url] ;
CMTime audioDuration = avset.duration; //获取音频时长
_countTime = CMTimeGetSeconds(audioDuration);
AVPlayerItem *item = [AVPlayerItem playerItemWithAsset:avset];
[self.player replaceCurrentItemWithPlayerItem:item];//替换当前播放的音频
[self.player play];
1
2
3
4
5
6
7
8
注:如果需要获取音频的时长等信息,必须使用AVAsset,不需要的话,可以直接使用[AVPlayerItem playerItemWithURL:url]就可以了
显示音频所剩的播放时间
//
__weak typeof(self) weakSelf = self;
[self.player addPeriodicTimeObserverForInterval:CMTimeMake(1, 1) queue:nil usingBlock:^(CMTime time)
AVPlayerItem *item = weakSelf.item;
//已播放时长
NSInteger currentTime = item.currentTime.value/item.currentTime.timescale;
//音频总时长
NSInteger allTime = CMTimeGetSeconds(weakSelf.player.currentItem.duration);
weakSelf.timeL.text = [weakSelf showPlayerTime:allTime - currentTime];
];
1
2
3
4
5
6
7
8
9
10
11
12
监听回调
//
#pragma 监听播放状态回调
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
if ([object isKindOfClass:[AVPlayerItem class]])
if ([keyPath isEqualToString:@"status"])
switch (_item.status)
case AVPlayerItemStatusReadyToPlay:
//推荐将视频播放在这里
break;
case AVPlayerItemStatusUnknown:
NSLog(@"AVPlayerItemStatusUnknown");
break;
case AVPlayerItemStatusFailed:
NSLog(@"AVPlayerItemStatusFailed");
break;
default:
break;
else if ([keyPath isEqualToString:@"playbackBufferEmpty"])
if (_item.playbackBufferEmpty)
_animationV.hidden = NO;
if ([object isKindOfClass:[AVPlayer class]])
if ([keyPath isEqualToString:@"timeControlStatus"])
switch (_player.timeControlStatus)
case AVPlayerTimeControlStatusPlaying:
_isPlaying = YES;
_animationV.hidden = YES;
break;
case AVPlayerTimeControlStatusPaused:
_isPlaying = NO;
_animationV.hidden = YES;
[_playerView stopAnimating];
_playerView.image = [UIImage imageNamed:@"L0"];
break;
case AVPlayerTimeControlStatusWaitingToPlayAtSpecifiedRate:
_animationV.hidden = NO;
default:
break;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
时间显示
//
- (NSString *)showPlayerTime:(NSInteger)countTime
NSInteger minute = countTime / 60;
NSInteger second = countTime % 60;
NSString *str = @"";
if (minute < 10)
if (second < 10)
str = [NSString stringWithFormat:@" 0%ld:0%ld",minute,second];
else
str = [NSString stringWithFormat:@" 0%ld:%ld",minute,second];
else
if (second < 10)
str = [NSString stringWithFormat:@" %ld:0%ld",minute,second];
else
str = [NSString stringWithFormat:@" %ld:%ld",minute,second];
return str;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
注意:有的说是NSTimer 计时器计时应该写在子线程中,但是写在子线程中发现倒计时与音频播放不同步,出现倒计时已经结束,但是音频还没播放完,所以这里我就都写在了主线程。(有见解的伙伴欢迎提点哦)
(2)然后,就是要实现后台播放以及返回其他页面,音频继续播放的过程
首先开启,允许后台运行模式
然后在APPDelegate 中:
- (void)applicationWillResignActive:(UIApplication *)application
//开启后台处理多媒体事件
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
AVAudiosession *session=[AVAudioSession sharedInstance];
[session setActive:YES error:nil];
//后台播放
[session setCategory:AVAudioSessionCategoryPlayback error:nil];
//这样做,可以在按home键进入后台后 ,播放一段时间,几分钟吧。但是不能持续播放,若需要持续播放,还需要申请后台任务id,具体做法是:
_bgTaskId=[AppDelegate backgroundPlayerID:_bgTaskId];
//其中的_bgTaskId是后台任务UIBackgroundTaskIdentifier _bgTaskId;
+(UIBackgroundTaskIdentifier)backgroundPlayerID:(UIBackgroundTaskIdentifier)backTaskId
//设置并激活音频会话类别
AVAudioSession *session=[AVAudioSession sharedInstance];
[session setCategory:AVAudioSessionCategoryPlayback error:nil];
[session setActive:YES error:nil];
//允许应用程序接收远程控制
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
//设置后台任务ID
UIBackgroundTaskIdentifier newTaskId=UIBackgroundTaskInvalid;
newTaskId=[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:nil];
if(newTaskId!=UIBackgroundTaskInvalid&&backTaskId!=UIBackgroundTaskInvalid)
[[UIApplication sharedApplication] endBackgroundTask:backTaskId];
return newTaskId;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
But:我们会发现当实现当只实现以上的applicationWillResignActive方法,或者只开启图中的background modes ,做到这两个中的一个,就可以实现后台播放。。。
进入后台播放可以了,但是当我们返回到其他的页面,再次进入到这个页面时,会发现,音频又叠加了一个音频,而不是我们想要的当前音频正常的继续播放。所以…我们想到了,对,揍死它——单例。
将当前控制器设置成单例,这样每次进入这个页面,音频可以毫无影响的继续播放啦~
static MyController *instance;
+(id)shareInstance
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
if(instance == nil)
instance = [[MyController alloc] init];
);
return instance;
1
2
3
4
5
6
7
8
9
10
注:以上只是部分主要代码,并非完整代码。
附:
参考文章:https://www.jianshu.com/p/ab300ea6e90c
---------------------
作者:Kaiccy
来源:CSDN
原文:https://blog.csdn.net/Kaiccy/article/details/81542353
版权声明:本文为博主原创文章,转载请附上博文链接!
以上是关于IOS 音频实现以及后台播放音频的主要内容,如果未能解决你的问题,请参考以下文章