一个简单的播放器实现

最近有个简单播放器的需求需要实现,公司其他项目本身已有播放器实现但是业务逻辑不满足现有需求,所以参照原有模块并结合相关资料自己实现了一个简单播放器,该播放器是基于AVPlayer实现的。

播放器功能介绍

  • 单例实现
  • 播放、暂停、切歌、缓冲处理、播放进度实时更新
  • 后台持续播放
  • 音频中断处理

协议代理

播放器功能协议TTPlayerProtocol
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
@protocol TTPlayerProtocol <NSObject>

- (void) playMusicWithInfo:(id<TTMusicModelProtocol>)model;

- (void) enterPlayQueueWithSingle:(id<TTMusicModelProtocol>)model;
- (void) enterPlayQueueWithArray:(NSArray<id<TTMusicModelProtocol>> *)models;

- (void) replaceAllQueueWithSingle:(id<TTMusicModelProtocol>)model;
- (void) replaceAllQueueWithArray:(NSArray<id<TTMusicModelProtocol>> *)models;

- (BOOL) isPlaying;

- (BOOL) isNextExist;

- (BOOL) isPreExist;

- (void) next;

- (void) pre;

- (void) pause;

- (void) stop;

- (void) play;

- (void) continuePlay;

- (double) currentSecs;

- (double) durationSecs;

@end
播放器状态代理TTAudioPlayerStatusDelegate
1
2
3
4
5
6
7
8
9
10
11
12
13
@protocol TTAudioPlayerStatusDelegate <NSObject>

@optional
- (void)ttAudioPlayerPlayStart;
- (void)ttAudioPlayerPlayFinished;
- (void)ttAudioPlayerPause;
- (void)ttAudioPlayerStoped;
- (void)ttAudioPlayerPlayError:(NSError *)error;
- (void)ttAudioPlayerSeekPosition:(double)progress;
- (void)ttAudioPlayerUpdateProgress:(double)progress;
- (void)ttAduioPlayerMusicInfoUpdate:(id<TTMusicModelProtocol>)musicInfo;

@end
数据模型协议 TTMusicModelProtocol
1
2
3
4
5
6
7
8
9
10
11
12
@protocol TTMusicModelProtocol <NSObject>

@required
@property (nonatomic, copy) NSString *url;

@optional
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *cover;
@property (nonatomic, copy) NSString *author;
@property (nonatomic, copy) NSString *album;

@end

类介绍

TTAudioPlayer

播放器对象,实现了TTPlayerProtocol协议

1
2
3
4
5
6
7
@interface TTAudioPlayer : NSObject<TTPlayerProtocol>

@property (nonatomic, weak) id<TTAudioPlayerStatusDelegate> delegate;

+ (instancetype)shareInstance;

@end

TTMusicModel

音乐数据模型,遵守TTMusicModelProtocol协议,可扩展

1
2
3
@interface TTMusicModel : NSObject<TTMusicModelProtocol>

@end

问题与功能实现细节

设置播放器在后台,锁屏,静音模式下都可以播放
1.在后台模式中打开音频播放模式

Targets–>Capabilities–>BackgroundModes–>ON

勾选 Audio,Airplay,and Picture in Picture

2.设置音频类别

这段代码我写在播放器初始化的时候

1
2
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
[[AVAudioSession sharedInstance] setActive:YES error:nil];

3.开启后台任务

前面两步做完之后,app进入后台音频是可以继续播放,但是播完一首之后,后面的就不会播放了,这里开启后台任务就好了

1
2
3
4
5
@interface AppDelegate ()
{
UIBackgroundTaskIdentifier bgTask;
}
@end

进入后台后开启任务

1
2
3
4
5
6
7
8
9
- (void)applicationDidEnterBackground:(UIApplication *)application {

[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
bgTask = [application beginBackgroundTaskWithExpirationHandler:^{
[application endBackgroundTask:self->bgTask];
self->bgTask = UIBackgroundTaskInvalid;
}];

}

进入前台,结束后台任务

1
2
3
4
5
6
- (void)applicationWillEnterForeground:(UIApplication *)application {
if (bgTask != UIBackgroundTaskInvalid) {
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}
}

处理中断事件

监听中断通知

1
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleInterreptionNotification:) name:AVAudioSessionInterruptionNotification object:[AVAudioSession sharedInstance]];

中断处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
- (void)handleInterreptionNotification:(NSNotification *)notification {
if ([notification.userInfo count] == 0) {
return;
}
if (AVAudioSessionInterruptionTypeBegan == [notification.userInfo[AVAudioSessionInterruptionTypeKey] intValue]) {
NSLog(@"中断,暂停音乐");
[self pause];
}
else if (AVAudioSessionInterruptionTypeEnded == [notification.userInfo[AVAudioSessionInterruptionTypeKey] intValue]) {
NSError *error,*activeError;
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback withOptions:0 error:&error];
[[AVAudioSession sharedInstance] setActive:YES error:&activeError];

if (error) {
NSLog(@"中断结束,设置类别失败:%@",error);
} else if (activeError) {
NSLog(@"中断结束,获取音频焦点失败:%@",activeError);
} else {
NSLog(@"中断结束,恢复音乐播放");
[self continuePlay];
}
}
}

####