使用AVPlayer播放网络音频简单易行,但是在使用的时候我们需要很多时间的监控处理,这里就需要分装一下,好供我们使用,在不同的地方使用,省去了繁琐的事件监听出,KVO处理不好容易造成多移除崩溃,为移除崩溃,封装到一个类中,统一处理,简单方便,
<创建一个播放类文件>
**PlayerSource.h**
#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h> **导入框架**
<*顶一个枚举类型,记录播放的状态*>
typedef NS_ENUM(NSUInteger, cq_audioPayerState){
cq_audioPayerStateNormal = 0, /**< 未播放状态 */
cq_audioPayerStatePlaying = 2, /**< 正在播放 */
cq_audioPayerStateCancle = 3, /**< 播放被取消 */
};
<*定义代理方法将播放状态传到出去,方便事件的处理*>
@protocol cqAudioPlayerDelegate <NSObject>
- (void)audioPlayerStatuesDidChanged:(cq_audioPayerState)audioPlayer index:(NSInteger)index;
@end
@interface PlayerSource : NSObject
@property (nonatomic ,copy)NSString *URlString;
@property (nonatomic, assign) NSUInteger index;
@property (nonatomic, weak) id<cqAudioPlayerDelegate>delegate;
@property (nonatomic,strong)AVPlayer *audioPlayer;
@property (nonatomic ,assign)cq_audioPayerState audioStatues;
//记录是否在播放,方便记录是否移除了KVO
@property (nonatomic ,assign)BOOL isPlaying;
+ (instancetype)sharePlayer;
/**
播放事件
@param URl 资源链接
@param index 记录当前播放的原位置 //例如点击tableviewCell的一个 index为1 第5个cell为5
*/
- (void)playAudioWithURLString:(NSString *)URl atindex:(NSUInteger)index;
/**
直接播放事件
@param URlstring 资源链接
*/
- (void)playAudioWithWebUrlString:(NSString *)URlstring;
/**
停止事件
*/
- (void)stopAudioPlayer;
@end
<PlayerSource>
/**
这里使用单例模式方便方法的调用
*/
+ (instancetype)sharePlayer{
static dispatch_once_t onceToken;
static id shareInstance;
dispatch_once(&onceToken, ^{
shareInstance = [[self alloc] init];
});
return shareInstance;
}
- (void)playAudioWithURLString:(NSString *)URl atindex:(NSUInteger)index
{
if (!URl) {
return;
}
if ([self.URlString isEqualToString:URl] && _index == index) {
[self stopAudioPlayer];
[self setAudioStatues:cq_audioPayerStateCancle];
self.StatuesImage.selected = NO;
return;
}
self.URlString = URl;
self.index = index;
dispatch_async(dispatch_get_main_queue(), ^{
[self playAudioWithWebUrlString:URl];
});
}
- (void)playAudioWithWebUrlString:(NSString *)URlstring
{
if (_isPlaying) { //如果在播放 表示KVO未被移除,移除KVO
[_audioPlayer removeObserver:self forKeyPath:@"status"];
_isPlaying = NO;
}
AVAsset *asset = [AVURLAsset URLAssetWithURL:[NSURL URLWithString:URlstring] options:nil];
AVPlayerItem *playerItem = [AVPlayerItem playerItemWithAsset:asset];
_audioPlayer = [AVPlayer playerWithPlayerItem:playerItem];
[[AVAudioSession sharedInstance]setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionAllowBluetooth error:nil];
[[AVAudioSession sharedInstance]setActive:YES error:nil];
//添加监听博播状态的KVO
[_audioPlayer addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
[_audioPlayer play];
_isPlaying = YES; //表示播放,添加了KVO
[self addNotification];
}
//
-(void)setAudioStatues:(cq_audioPayerState)audioStatues
{
_audioStatues = audioStatues;
if (self.delegate && [self.delegate respondsToSelector:@selector(audioPlayerStatuesDidChanged:index:)]) {
[self.delegate audioPlayerStatuesDidChanged:audioStatues index:self.index];
}
if (_audioStatues == cq_audioPayerStateCancle || audioStatues == cq_audioPayerStateNormal) {
_URlString = nil;
_index = NSIntegerMax; //播放完成将数值改变,防止下次播放时判断是同一个在播放的资源
setAudioStatues:cq_audioPayerStateCancle];
}
}
//
<*添加KVO*>
- (void)addNotification
{
[[NSNotificationCenter defaultCenter]addObserver:self
selector:@selector(playbackFinished:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:_audioPlayer.currentItem];
}
<*监测是否自动播放完成*>
- (void)playbackFinished:(NSNotification *)notice
{
[self stopAudioFinish];
}
//事件的监听处理
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
if ([keyPath isEqualToString:@"status"]) {
AVPlayerStatus status= [[change objectForKey:@"new"]intValue];
if(status==AVPlayerStatusReadyToPlay){
self.StatuesImage.selected = YES;
[self setAudioStatues:cq_audioPayerStatePlaying];
[_timer invalidate];
_timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timeAction:) userInfo:nil repeats:YES];
}else if (status == AVPlayerStatusUnknown)
{
ReachabilityStatus statues = [GLobalRealReachability currentReachabilityStatus];
if (statues == RealStatusNotReachable) {
[self setAudioStatues:cq_audioPayerStateCancle];
[self stopAudioPlayer];
_hud = [MBProgressHUD showHUDAddedTo:[UIApplication sharedApplication].keyWindow animated:YES];
_hud.mode = MBProgressHUDModeText;
_hud.labelText = @"网络连接有误";
_hud.margin = 10.0f; //提示框的大小
[_hud customView];
_hud.removeFromSuperViewOnHide = YES; //隐藏之后删除view
[_hud hide:YES afterDelay:0.8]; //多久后隐藏
return;
}else
{
}
}else if (status == AVPlayerStatusFailed)
{
[self setAudioStatues:cq_audioPayerStateNormal];
[self stopAudioPlayer];
_hud = [MBProgressHUD showHUDAddedTo:[UIApplication sharedApplication].keyWindow animated:YES];
_hud.mode = MBProgressHUDModeText;
_hud.labelText = @"播放有误";
_hud.margin = 10.0f; //提示框的大小
[_hud customView];
_hud.removeFromSuperViewOnHide = YES; //隐藏之后删除view
[_hud hide:YES afterDelay:0.5]; //多久后隐藏
}
}else if([keyPath isEqualToString:@"loadedTimeRanges"]){
}
}
<*中途暂停的播放事件*>
- (void)stopAudioPlayer
{
if (_audioPlayer) {
if (_audioPlayer.status == AVPlayerStatusReadyToPlay) {
[_audioPlayer pause];
if (_isPlaying) {
[_audioPlayer removeObserver:self forKeyPath:@"status"];
_isPlaying = NO;
}
[_timer setFireDate:[NSDate distantFuture]];
self.StatuesImage.selected = NO;
[[PlayerSource sharePlayer] setAudioStatues:cq_audioPayerStateCancle];
}
}
}
<*正常播放完成的暂停事件*>
- (void)stopAudioFinish
{
if (_audioPlayer) {
if (_audioPlayer.status == AVPlayerStatusReadyToPlay) {
[_audioPlayer pause];
_isfinish = YES;
[[PlayerSource sharePlayer] setAudioStatues:cq_audioPayerStateNormal];
self.StatuesImage.selected = NO;
if (_isPlaying) {
[_audioPlayer removeObserver:self forKeyPath:@"status"];
_isPlaying = NO;
}
_isShowView = NO;
[self.backView removeFromSuperview];
self.backView = nil;
}
}
}
<以上便是播放的内容,下面介绍如何使用>
先介绍播放点击tableViewCell的播放事件
pragma mark ====点击播放声音=====
首先在需要播放的类这种的viewdidload中先遵循代理
[PlayerSource sharePlayer].delegate = self;
- (void)tapImgaeWithCell:(QuestionTableViewCell *)cell
{
if (IsLogin) {
NSIndexPath *indexPath = [_QuestionTable indexPathForCell:cell];
QuestionStatues *statues = [_DataArray objectAtIndex:indexPath.section];
if (self.index == indexPath.section) {
// [cell setVoicePlayStatues:cq_VoiceplayStatueCancle];
[[PlayerSource sharePlayer]playAudioWithURLString:[detailPublicUrl stringByAppendingString:statues.ansAudio] atindex:indexPath.section];
self.index = NSIntegerMax;
}else
{
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
_IndexQuesID = statues.queID;
[PlayerSource sharePlayer].timeText = statues.ansAudioSec;
[[PlayerSource sharePlayer]playAudioWithURLString:[detailPublicUrl stringByAppendingString:statues.ansAudio] atindex:indexPath.section];
[[PlayerSource sharePlayer] setTitleLabel:statues.queTitle timeLabel:statues.ansAudioSec];
self.index = indexPath.section;
}
}else
{
[PublickResource showTipAlertViewWith:self title:@"未登录" message:@"是否立即登录" CancleTitle:@"取消" downTitle:@"立即登录" CancelButton:^{
} DownButton:^{
LoginViewController *log = [[LoginViewController alloc]init];
MKNavigationController *nav = [[MKNavigationController alloc]initWithRootViewController:log];
[self presentViewController:nav animated:YES completion:nil];
}];
}
}
<*代理方法监听的处理*>
-(void)audioPlayerStatuesDidChanged:(cq_audioPayerState)audioPlayer index:(NSInteger)index
{
if (index < _DataArray.count - 1) {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:index];
QuestionStatues *statues = [_DataArray objectAtIndex:index];
QuestionTableViewCell *voiceMessageCell = [_QuestionTable cellForRowAtIndexPath:indexPath];
VoiceImageStatues voiceStatues;
switch (audioPlayer) {
case cq_audioPayerStateCancle:
voiceStatues = cq_VoiceplayStatueCancle;
_isPlaying = NO;
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
break;
case cq_audioPayerStatePlaying:
voiceStatues = cq_VoiceplayStatuePlaying;
_isPlaying = YES;
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
break;
case cq_audioPayerStateNormal:
_isPlaying = NO;
[self addClickNumberWithID:_IndexQuesID];
NSInteger inter = [statues.clicked integerValue];
inter++;
statues.clicked = [NSString stringWithFormat:@"%ld",(long)inter];
[self.QuestionTable reloadData];
voiceStatues = cq_VoiceplayStatueNormal;
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
default:
break;
}
dispatch_async(dispatch_get_main_queue(), ^{
[voiceMessageCell setVoicePlayStatues:voiceStatues];
});
}
}
<*cell中代理与定义的实现*>
<*cell.h中定义枚举类型*>
typedef NS_ENUM(NSUInteger,VoiceImageStatues){
cq_VoiceplayStatueNormal,/**< 未播放状态*/
cq_VoiceplayStatuePlaying,/**< 未播放状态*/
cq_VoiceplayStatueCancle,/**< 未播放状态*/
};
<*定义代理方法*>
@protocol SelectVoiceImageDelegate <NSObject>
- (void)tapImgaeWithCell:(QuestionTableViewCell *)cell;
@end
<*在cell中声明枚举与代理方法*>
@property (nonatomic ,weak)id<SelectVoiceImageDelegate>delegate;
@property (nonatomic ,assign)VoiceImageStatues VoicePlayStatues;
<介绍使用方法直接播放的方法>
<*同样的需要在类中遵循代理*>
[PlayerSource sharePlayer].delegate = self;
播放方法
[[PlayerSource sharePlayer] playAudioWithWebUrlString: @""];
<*代理时间监听处理*>
- (void)audioPlayerStatuesDidChanged:(cq_audioPayerState)audioPlayer index:(NSInteger)index
{
switch (audioPlayer) {
case cq_audioPayerStateCancle:
[_StatuesImage stopAnimating];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
break;
case cq_audioPayerStatePlaying:
[_StatuesImage startAnimating];
_isPlaying = YES;
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
break;
case cq_audioPayerStateNormal:
[_StatuesImage stopAnimating];
[self addClickNumberWithID:_QueID];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
default:
break;
}
}