AVAudioPlayer、AVPlayer和AVQueuePlayer的使用

AVAudioPlayer 简述

AVAudioPlayer是属于 AVFundation.framework 的一个类,它的功能类似于一个功能强大的播放器,AVAudioPlayer每次播放都需要将上一个player对象释放掉,然后重新创建一个player来进行播放,AVAudioPlayer 支持广泛的音频格式,主要是以下这些格式。

  • ACC
  • AMR(Adaptive multi-Rate,一种语音格式)
  • ALAC (Apple lossless Audio Codec)
  • iLBC (internet Low Bitrate Codec,另一种语音格式)
  • IMA4 (IMA/ADPCM)
  • linearPCM (uncompressed)
  • u-law 和 a-law
  • MP3 (MPEG-Laudio Layer 3)

AVAudioPlayer 使用

AVAudioPlayer 初始化
 1. initWithContentsOfURL: error: 从URL加载音频,返回 AVAudioPlayer 对象
 2. initWithData: error: 加载NSdata对象的音频文件,返回 AVAudioPlayer 对象
AVAudioPlayer 方法调用
 //准备播放 可以判断player创建成功之后调用,调用[player play]则可以节省播放之前的间隔时间。
- (BOOL)prepareToPlay;
//异步播放
- (BOOL)play; 
//在某个时间点播放
- (BOOL)playAtTime:(NSTimeInterval)time NS_AVAILABLE(10_7, 4_0); 
//暂停播放,但仍然可以播放
- (void)pause; 
//停止播放,不再准备播放了
- (void)stop; 
//更新音频测量值,注意如果要更新音频测量值必须设置meteringEnabled为YES,通过音频测量值可以即时获得音频分贝等信息
- (void)updateMeters; 
//返回给定通道的分贝峰值功率
- (float)peakPowerForChannel:(NSUInteger)channelNumber; 
//获得指定声道的分贝峰值,注意如果要获得分贝峰值必须在此之前调用updateMeters方法
- (float)averagePowerForChannel:(NSUInteger)channelNumber;
AVAudioPlayer 属性设置
playing //播放器是否正在播放 获取通过isPlaying
numberOfChannels //该音频的声道次数,只读
duration //该音频播放时长
url //音频文件路径, 只读
data //音频数据,只读
pan NS_AVAILABLE(10_7, 4_0) //立体声设置 设为 -1.0 则左边播放 设为 0.0 则中央播放 设为 1.0 则右边播放
enableRate NS_AVAILABLE(10_8, 5_0);  //是否允许改变播放速率
rate NS_AVAILABLE(10_8, 5_0); //播放速率 0.5 (半速播放) ~ 2.0(倍速播放) 注1.0 是正常速度
currentTime //该音频的播放点
deviceCurrentTime //输出设备播放音频的时间,注意如果播放中被暂停此时间也会继续累加
numberOfLoops  //循环次数,如果要单曲循环,设置为负数
volume //播放器的音频增益,值:0.0~1.0
channelAssignments //获得或设置播放声道
AVAudioPlayer 代理方法
//音频播放完成
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag;
//音频解码发生错误
- (void)audioPlayerDecodeErrorDidOccur:(AVAudioPlayer *)player error:(NSError * __nullable)error
//如果音频被中断,比如有电话呼入,该方法就会被回调,该方法可以保存当前播放信息,以便恢复继续播放的进度
- (void)audioPlayerBeginInterruption:(AVAudioPlayer *)player

AVPlayer 简述

AVPlayer支持播放本地、分步下载、或在线流媒体音视频,不仅可以播放音频,配合AVPlayerLayer类可实现视频播放。另外支持播放进度监听。
AVPlayer只支持单个媒体资源播放。

AVPlayer需要配合AVPlayerItem关联来进行媒体播放。

AVPlayer 使用

AVPlayer 初始化
//类方法,从url加载音频
+ (instancetype)playerWithURL:(NSURL *)URL
//类方法,配合AVPlayerItem使用
+ (instancetype)playerWithPlayerItem:(nullable AVPlayerItem *)item
//实例方法,从url加载音频
- (instancetype)initWithURL:(NSURL *)URL
//实例方法,配合AVPlayerItem使用
- (instancetype)initWithPlayerItem:(nullable AVPlayerItem *)item
AVPlayer KVO添加监听

1.播放状态改变监听

//KVO监听播放状态
[self.player.currentItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];

处理KVO回调事件

//处理KVO回调事件
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    if ([keyPath isEqualToString:@"status"]) {
        switch (self.player.status) {
            case AVPlayerStatusUnknown:
                NSLog(@"未知转态");
                break;
            case AVPlayerStatusReadyToPlay:
                NSLog(@"准备播放");
                break;
            case AVPlayerStatusFailed:
                NSLog(@"加载失败");
                break;
                
            default:
                break;
        }
    }
}

2.KVO监听音乐缓冲状态

//KVO监听音乐缓冲状态
[self.player.currentItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];

处理KVO回调事件

//处理KVO回调事件
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    if ([keyPath isEqualToString:@"loadedTimeRanges"]) {
        
        NSArray *timeRanges = self.player.currentItem.loadedTimeRanges;
        //本次缓冲的时间范围
        CMTimeRange timeRange = [timeRanges.firstObject CMTimeRangeValue];
        //缓冲总长度
        NSTimeInterval totalLoadTime = CMTimeGetSeconds(timeRange.start) + CMTimeGetSeconds(timeRange.duration);
        //音乐的总时间
        NSTimeInterval duration = CMTimeGetSeconds(self.player.currentItem.duration);
        //计算缓冲百分比例
        NSTimeInterval scale = totalLoadTime/duration;
        NSLog(@"---%f,---%f,---%f",totalLoadTime,duration,scale);
        //更新缓冲进度条
        //        self.loadProgress.progress = scale;
        
    }
}
  1. 播放结束事件的监听
//播放结束事件的监听
[[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(playFinied:) name:AVPlayerItemDidPlayToEndTimeNotification
                                               object:self.player.currentItem];

处理KVO回调事件

//播放结束回调
- (void)playFinied:(AVPlayerItem *)item {
    NSLog(@"播放结束");
    self.slider.value = 0;
}
  1. 开始播放时,通过AVPlayer的方法监听播放进度,并更新进度条(定期监听的方法)
//开始播放时,通过AVPlayer的方法监听播放进度,并更新进度条(定期监听的方法)
__weak typeof(self) weakSelf = self;
    [self.player addPeriodicTimeObserverForInterval:CMTimeMake(1.0, 1.0) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
        //当前播放的时间
        float current = CMTimeGetSeconds(time);
        //总时间
        float total = CMTimeGetSeconds(item.duration);
        if (current) {
            float progress = current / total;
            //更新播放进度条
            weakSelf.slider.value = progress;
        }
    }];
  1. 拖动进度条改变播放进度
//拖动进度条改变播放进度
- (void)playValueChange:(UISlider *)sender {
    //计算时间
    float time = sender.value * CMTimeGetSeconds(self.player.currentItem.duration);
    //跳到当前指定时间
    [self.player seekToTime:CMTimeMake(time, 1)];
}

AVQueuePlayer 简述

AVPlayer只支持单个媒体资源的播放,我们可以使用AVPlayer的子类AVQueuePlayer实现列表播放。在AVPlayer的基础上,增加以下方法:

//通过给定的AVPlayerItem数组创建一个AVQueuePlayer实例
+ (instancetype)queuePlayerWithItems:(NSArray<AVPlayerItem *> *)items;
//通过给定的AVPlayerItem数组初始化一个AVQueuePlayer实例
- (AVQueuePlayer *)initWithItems:(NSArray<AVPlayerItem *> *)items;
//获取当前播放队列数组
- (NSArray<AVPlayerItem *> *)items;
//停止当前播放的,播放队列中的下一首
- (void)advanceToNextItem;
//判断是否可以插入AVPlayerItem
- (BOOL)canInsertItem:(AVPlayerItem *)item afterItem:(nullable AVPlayerItem *)afterItem;
//往播放队列中插入新的AVPlayerItem
- (void)insertItem:(AVPlayerItem *)item afterItem:(nullable AVPlayerItem *)afterItem;
//移除指定的AVPlayerItem
- (void)removeItem:(AVPlayerItem *)item;
//移除所有播放队列中的AVPlayerItem
- (void)removeAllItems;

*官方API中没找到播放上一首的方法,所以其实直接用AVPlayer做列表播放也是可以的,自己维护一个播放列表数组,监听用户点击上一首和下一首按钮,并监听播放结束事件,调用 AVPlayer 实例的replaceCurrentItemWithPlayerItem:方法传入播放列表中的上一首或下一首就行。

后台播放

首先在AppDelegate.m的- (BOOL)application:didFinishLaunchingWithOptions:方法中添加代码:

 //设置后台播放功能
AVAudioSession *session = [AVAudioSession sharedInstance];
[session setCategory:AVAudioSessionCategoryPlayback error:nil];
[session setActive:YES error:nil];

然后在info.plist里面配置:

<key>UIBackgroundModes</key>
<array>
    <string>audio</string>
</array>

锁屏信息

1、在播放控制界面接受远程控制

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:YES];
    // 开始接受远程控制
    [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
    [self resignFirstResponder];  
}

- (void)viewWillDisappear:(BOOL)animated
{
// 接触远程控制
    [[UIApplication sharedApplication] endReceivingRemoteControlEvents];
    [self becomeFirstResponder];
}
// 重写父类成为响应者方法
- (BOOL)canBecomeFirstResponder
{
    return YES;
}

2、对远程控制事件做出相应的操作

//重写父类方法,接受外部事件的处理
- (void) remoteControlReceivedWithEvent: (UIEvent *) receivedEvent {
    NSLog(@"remote");
    if (receivedEvent.type == UIEventTypeRemoteControl) {
        
        switch (receivedEvent.subtype) { // 得到事件类型
                
            case UIEventSubtypeRemoteControlTogglePlayPause: // 暂停 ios6
                [self.player pause]; // 调用你所在项目的暂停按钮的响应方法 下面的也是如此
                break;
                
            case UIEventSubtypeRemoteControlPreviousTrack:  // 上一首
                
                [self lastMusic:nil];
                break;
                
            case UIEventSubtypeRemoteControlNextTrack: // 下一首
                [self nextMusic:nil];
                break;
                
            case UIEventSubtypeRemoteControlPlay: //播放
                [self playMusic:nil];
                break;
                
            case UIEventSubtypeRemoteControlPause: // 暂停 ios7
                [self playMusic:nil];
                break;
                
            default:
                break;
        }
    }
}

3、设置锁屏主题

//锁屏信息
    NSMutableDictionary *songInfo = [ [NSMutableDictionary alloc] init];
    //锁屏图片
    UIImage *img = [UIImage imageNamed:@"iPhoneX"];
    if (img) {
        MPMediaItemArtwork *albumArt = [[MPMediaItemArtwork alloc]initWithImage:img];
        [songInfo setObject: albumArt forKey:MPMediaItemPropertyArtwork ];
    }
    //锁屏标题
    NSString *title = @"music";
    [songInfo setObject:[NSNumber numberWithFloat:100] forKey:MPMediaItemPropertyPlaybackDuration];
    [songInfo setObject:title forKey:MPMediaItemPropertyTitle];
    [songInfo setObject:title forKey:MPMediaItemPropertyAlbumTitle];
    [[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:songInfo ];

通过耳机、锁屏界面控制

// 直接使用sharedCommandCenter来获取MPRemoteCommandCenter的shared实例
MPRemoteCommandCenter *commandCenter = [MPRemoteCommandCenter sharedCommandCenter];

// 启用播放命令 (锁屏界面和上拉快捷功能菜单处的播放按钮触发的命令)
commandCenter.playCommand.enabled = YES;
// 为播放命令添加响应事件, 在点击后触发
[commandCenter.playCommand addTarget:self action:@selector(playAction:)];

// 播放, 暂停, 上下曲的命令默认都是启用状态, 即enabled默认为YES
// 为暂停, 上一曲, 下一曲分别添加对应的响应事件
[commandCenter.pauseCommand addTarget:self action:@selector(pauseAction:)];
[commandCenter.previousTrackCommand addTarget:self action:@selector(previousTrackAction:)];
[commandCenter.nextTrackCommand addTarget:self action:@selector(nextTrackAction:)];

// 启用耳机的播放/暂停命令 (耳机上的播放按钮触发的命令)
commandCenter.togglePlayPauseCommand.enabled = YES;
// 为耳机的按钮操作添加相关的响应事件
[commandCenter.togglePlayPauseCommand addTarget:self action:@selector(playOrPauseAction:)];
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容