博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
iOS--音乐播放器之DOUAudioStreamer
阅读量:7011 次
发布时间:2019-06-28

本文共 14764 字,大约阅读时间需要 49 分钟。

hot3.png

好久没有写东西了,最近加班太严重,今天抽空把用到的音乐播放器DOUAudioStreamer整理一下,由于项目之前用的是AVPlayer,这个也可以,但是就是要先缓存一段时间再播放,老板看了之后要求,要边缓存边播放(有网时,点击播放按钮就立刻播放),怎么不早说!怎么不早说!怎么不早说!还能怎样?只能原谅他,继续敲代码。。。。。。(还是直接上代码吧)

一、导入三方库   

pod 'DOUAudioStreamer'

      或者GitHup下载地址:    https://github.com/douban/DOUAudioStreamer

二、使用

1.从demo中获取NAKPlaybackIndicatorView文件和MusicIndicator.h和MusicIndicator.m 文件,并导入头文件

//音乐播放#import "DOUAudioStreamer.h"#import "NAKPlaybackIndicatorView.h"#import "MusicIndicator.h"#import "Track.h"

如图:

                

 

2.创建一个Track类,用于音乐播放的URL存放

3.需要的界面.h中,添加DOUAudioStreamer,并用单利来初始化

+ (instancetype)sharedInstance ;@property (nonatomic, strong) DOUAudioStreamer *streamer;

如图:

在.m中实现:

static void *kStatusKVOKey = &kStatusKVOKey;static void *kDurationKVOKey = &kDurationKVOKey;static void *kBufferingRatioKVOKey = &kBufferingRatioKVOKey;@property (strong, nonatomic) MusicIndicator *musicIndicator;@property (nonatomic, strong) Track *audioTrack;+ (instancetype)sharedInstance {        static HYNEntertainmentController *_sharedMusicVC = nil;    static dispatch_once_t onceToken;    dispatch_once(&onceToken, ^{                _sharedMusicVC = [[HYNEntertainmentController alloc] init];        _sharedMusicVC.streamer = [[DOUAudioStreamer alloc] init];           });        return _sharedMusicVC;}

播放按钮事件

#pragma mark ---音乐播放按钮-(void)playMusicStart:(UIButton *)sender{         //通过按钮获取cell     MusicCollectionViewCell *musicCell = (MusicCollectionViewCell *)[[sender superview] superview];    if(_playFirst == 0){//_playFirst == 0首次播放,其他为暂停                NSURL *url = [NSURL URLWithString:HttpImgUrl(musicCell.model.musicUrl)];                _audioTrack.audioFileURL = url;                       @try {            [self removeStreamerObserver];                    } @catch(id anException){                    }        //在DOUAudioStreamer进行播放时,必须先置为nil        _streamer = nil;                _streamer = [DOUAudioStreamer streamerWithAudioFile:_audioTrack];                        [self addStreamerObserver];                [_streamer play];            }        if([_streamer status] == DOUAudioStreamerPaused ||       [_streamer status] == DOUAudioStreamerIdle){                [sender setBackgroundImage:[UIImage imageNamed:@"music_play_icon"] forState:UIControlStateNormal];                [_streamer play];                            }else{                [sender setBackgroundImage:[UIImage imageNamed:@"music_stop_icon"] forState:UIControlStateNormal];                [_streamer pause];                    }        _playFirst++;    }

对DOUAudioStreamer添加监听

- (void)addStreamerObserver {        [_streamer addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:kStatusKVOKey];    [_streamer addObserver:self forKeyPath:@"duration" options:NSKeyValueObservingOptionNew context:kDurationKVOKey];    [_streamer addObserver:self forKeyPath:@"bufferingRatio" options:NSKeyValueObservingOptionNew context:kBufferingRatioKVOKey];    }/// 播放器销毁- (void)dealloc{        if (_streamer !=nil) {                        [_streamer pause];        [_streamer removeObserver:self forKeyPath:@"status" context:kStatusKVOKey];        [_streamer removeObserver:self forKeyPath:@"duration" context:kDurationKVOKey];        [_streamer removeObserver:self forKeyPath:@"bufferingRatio" context:kBufferingRatioKVOKey];                _streamer =nil;    }        }- (void)removeStreamerObserver {        [_streamer removeObserver:self forKeyPath:@"status"];    [_streamer removeObserver:self forKeyPath:@"duration"];    [_streamer removeObserver:self forKeyPath:@"bufferingRatio"];}- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {    if (context == kStatusKVOKey) {        [self performSelector:@selector(updateStatus)                     onThread:[NSThread mainThread]                   withObject:nil                waitUntilDone:NO];    } else if (context == kDurationKVOKey) {        [self performSelector:@selector(updateSliderValue:)                     onThread:[NSThread mainThread]                   withObject:nil                waitUntilDone:NO];    } else if (context == kBufferingRatioKVOKey) {        [self performSelector:@selector(updateBufferingStatus)                     onThread:[NSThread mainThread]                   withObject:nil                waitUntilDone:NO];    } else {        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];    }}- (void)updateSliderValue:(id)timer {        }-(void)updateBufferingStatus{        }- (void)updateStatus {    //self.musicIsPlaying = NO;    _musicIndicator.state = NAKPlaybackIndicatorViewStateStopped;    switch ([_streamer status]) {        case DOUAudioStreamerPlaying:            // self.musicIsPlaying = YES;            _musicIndicator.state = NAKPlaybackIndicatorViewStatePlaying;            break;                    case DOUAudioStreamerPaused:            break;                    case DOUAudioStreamerIdle:            break;                    case DOUAudioStreamerFinished:播放完成,可以修改播放按钮,也可以进行下一曲播放或重复播放        {                    UIButton *btn = (UIButton *)[self.view viewWithTag:2000];            [btn setBackgroundImage:[UIImage imageNamed:@"music_stop_icon"] forState:UIControlStateNormal];        }                        break;                    case DOUAudioStreamerBuffering:            _musicIndicator.state = NAKPlaybackIndicatorViewStatePlaying;            break;                    case DOUAudioStreamerError:            break;    }    }

这样就能播放了。

三、锁屏时的音乐显示、拔出耳机后暂停播放、监听音频打断事件

-(void)viewWillAppear:(BOOL)animated{        [super viewWillAppear:animated];//    接受远程控制    [self becomeFirstResponder];    [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];}//这个不能忘记了-(BOOL)canBecomeFirstResponder{    return YES;}- (void)viewDidLoad {    [super viewDidLoad];//音乐播放器    [self initPlayer];}#pragma mark =========================音乐播放==============================//音乐播放器-(void)initPlayer{    _audioTrack = [[Track alloc] init];    AVAudioSession *session = [AVAudioSession sharedInstance];    [session setActive:YES error:nil];    [session setCategory:AVAudioSessionCategoryPlayback error:nil];    //让app支持接受远程控制事件    [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];        //添加通知,拔出耳机后暂停播放    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(routeChange:) name:AVAudioSessionRouteChangeNotification object:nil];    // 监听音频打断事件    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(audioSessionWasInterrupted:) name:AVAudioSessionInterruptionNotification object:session];            }// 监听音频打断事件- (void)audioSessionWasInterrupted:(NSNotification *)notification{   //播放的音乐被打断时,停止音乐播放    if (AVAudioSessionInterruptionTypeBegan == [notification.userInfo[AVAudioSessionInterruptionTypeKey] intValue])    {               [_streamer pause];                UIButton *btn = (UIButton *)[self.view viewWithTag:2000];                [btn setBackgroundImage:[UIImage imageNamed:@"music_stop_icon"] forState:UIControlStateNormal];            }    else if (AVAudioSessionInterruptionTypeEnded == [notification.userInfo[AVAudioSessionInterruptionTypeKey] intValue])    {            }}// 拔出耳机后暂停播放-(void)routeChange:(NSNotification *)notification{                NSDictionary *dic=notification.userInfo;    int changeReason= [dic[AVAudioSessionRouteChangeReasonKey] intValue];    //等于AVAudioSessionRouteChangeReasonOldDeviceUnavailable表示旧输出不可用    if (changeReason==AVAudioSessionRouteChangeReasonOldDeviceUnavailable) {        AVAudioSessionRouteDescription *routeDescription=dic[AVAudioSessionRouteChangePreviousRouteKey];        AVAudioSessionPortDescription *portDescription= [routeDescription.outputs firstObject];        //原设备为耳机则暂停        if ([portDescription.portType isEqualToString:@"Headphones"]) {                        [_streamer pause];                        UIButton *btn = (UIButton *)[self.view viewWithTag:2000];                        [btn setBackgroundImage:[UIImage imageNamed:@"music_stop_icon"] forState:UIControlStateNormal];        }    }}//锁屏时音乐显示(这个方法可以在点击播放时,调用传值)- (void)setupLockScreenInfoWithSing:(NSString *)sign WithSigner:(NSString *)signer WithImage:(UIImage *)image{    // 1.获取锁屏中心    MPNowPlayingInfoCenter *playingInfoCenter = [MPNowPlayingInfoCenter defaultCenter];        //初始化一个存放音乐信息的字典    NSMutableDictionary *playingInfoDict = [NSMutableDictionary dictionary];    // 2、设置歌曲名    if (sign) {        [playingInfoDict setObject:sign forKey:MPMediaItemPropertyAlbumTitle];    }    // 设置歌手名    if (signer) {        [playingInfoDict setObject:signer forKey:MPMediaItemPropertyArtist];    }    // 3设置封面的图片    //        UIImage *image = [self getMusicImageWithMusicId:self.currentModel];    if (image) {        MPMediaItemArtwork *artwork = [[MPMediaItemArtwork alloc] initWithImage:image];        [playingInfoDict setObject:artwork forKey:MPMediaItemPropertyArtwork];    }        // 4设置歌曲的总时长    //    [playingInfoDict setObject:self.currentModel.detailDuration forKey:MPMediaItemPropertyPlaybackDuration];        //音乐信息赋值给获取锁屏中心的nowPlayingInfo属性    playingInfoCenter.nowPlayingInfo = playingInfoDict;            // 5.开启远程交互    [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];}//锁屏时操作- (void)remoteControlReceivedWithEvent:(UIEvent *)receivedEvent {    if (receivedEvent.type == UIEventTypeRemoteControl) {                UIButton *sender = (UIButton *)[self.view viewWithTag:2000];                switch (receivedEvent.subtype) {//判断是否为远程控制                            case UIEventSubtypeRemoteControlPause:               // [[HYNEntertainmentController sharedInstance].streamer pause];                [_streamer pause];                [sender setBackgroundImage:[UIImage imageNamed:@"music_stop_icon"] forState:UIControlStateNormal];                                break;            case UIEventSubtypeRemoteControlStop:                break;            case UIEventSubtypeRemoteControlPlay:                //[[HYNEntertainmentController sharedInstance].streamer play];                [_streamer play];                [sender setBackgroundImage:[UIImage imageNamed:@"music_play_icon"] forState:UIControlStateNormal];                                break;            case UIEventSubtypeRemoteControlTogglePlayPause:                break;            case UIEventSubtypeRemoteControlNextTrack:                                                break;            case UIEventSubtypeRemoteControlPreviousTrack:                                                break;            default:                break;        }    }}

四、对网络进行判断,当没有网络时,停止音乐播放并切换播放按钮图片

使用的是Reachability进行判断

pod 'Reachability'
//网络判断#import "Reachability.h"@property (nonatomic, strong) Reachability *hostReachability;

在viewDidLoad里注册通知,进行判断

- (void)viewDidLoad {    [super viewDidLoad]; //网络判断    [[NSNotificationCenter defaultCenter] addObserver:self                                             selector:@selector(appReachabilityChanged:)                                                 name:kReachabilityChangedNotification                                               object:nil];    // 检测指定服务器是否可达    NSString *remoteHostName = @"www.baidu.com";    self.hostReachability = [Reachability reachabilityWithHostName:remoteHostName];    [self.hostReachability startNotifier];}

 

页面消失时,注销网络判断通知

// 页面消失时候- (void)viewWillDisappear:(BOOL)animated {    [super viewWillDisappear:animated];        [self.playerView resetPlayer];        [[NSNotificationCenter defaultCenter]removeObserver:self name:kReachabilityChangedNotification object:nil ];        }

网络状态判断

/// 当网络状态发生变化时调用- (void)appReachabilityChanged:(NSNotification *)notification{    Reachability *reach = [notification object];    if([reach isKindOfClass:[Reachability class]]){        NetworkStatus status = [reach currentReachabilityStatus];                if (reach == self.hostReachability) {            if ([reach currentReachabilityStatus] == NotReachable) {                NSLog(@"网络网络不可达");                                _playFirst = 0;                                               [_streamer stop];                                UIButton *btn = (UIButton *)[self.view viewWithTag:2000];                                [btn setBackgroundImage:[UIImage imageNamed:@"music_stop_icon"] forState:UIControlStateNormal];                            } else if (status == ReachableViaWiFi) {                NSLog(@"WIFI");                                                [_collectionView reloadData];            } else if (status == ReachableViaWWAN) {                                                [_collectionView reloadData];            }        }            }}

整体图片:

上图为未播放

上图为播放中

 

 

上图为锁屏时状态

 

 

应该没有什么要添加的了,暂时告一段落,有不足之处,可以留言,谢谢!

(不是同一个项目)最近在音频播放时,要求断点续播,这个功能DOUAudioStreamer问题里也有提到,要在 kStatusKVOKey 的 Playing 触发之后调整 currentTime 才可以,也就是在代码中updateStatus方法里可以设置

如图

 

代码:

这个方法中的updateStatus方法里进行设置- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {    if (context == kStatusKVOKey) {        [self performSelector:@selector(updateStatus)                     onThread:[NSThread mainThread]                   withObject:nil                waitUntilDone:NO];    } else if (context == kDurationKVOKey) {        [self performSelector:@selector(updateSliderValue:)                     onThread:[NSThread mainThread]                   withObject:nil                waitUntilDone:NO];    } else if (context == kBufferingRatioKVOKey) {        [self performSelector:@selector(updateBufferingStatus)                     onThread:[NSThread mainThread]                   withObject:nil                waitUntilDone:NO];    } else {        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];    }}- (void)updateStatus {       switch ([_audioPlayer.streamer status]) {        case DOUAudioStreamerPlaying:                       //设置播放按钮            [self playButtonPlayImage];            NSLog(@"33333");            break;        case DOUAudioStreamerPaused:            //设置暂停按钮            [self stopButtonPlayImage];            NSLog(@"444444");            break;        case DOUAudioStreamerIdle:            break;         //播放完成,可以修改播放按钮,也可以进行下一曲播放或重复播放        case DOUAudioStreamerFinished:        {   //播放下一个            [self rightPlayClick];        }            break;        case DOUAudioStreamerBuffering:            break;                    case DOUAudioStreamerError:            break;    }    //断点续播设置  currentTime为秒    CourseModel *currentModel = self.listArr[self.playIndex];    //_audioPlayer.streamer.currentTime = 30;//测试设置30s    _audioPlayer.streamer.currentTime = [currentModel.paly_progress_last floatValue];}

 

 

 

 

 

 

 

 

转载于:https://my.oschina.net/huangyn/blog/1593368

你可能感兴趣的文章
Mysql 通过全量备份和binlog恢复整体数据
查看>>
使用paramiko模块在远程服务器执行命令
查看>>
Cannot change version of project facet Dynamic web
查看>>
Nginx中文手册
查看>>
jqgrid saveRow 保存行 编辑数据向后台保存的使用
查看>>
离职后的选择
查看>>
Java编写QQ邮件发送程序
查看>>
JavaWeb
查看>>
我的友情链接
查看>>
Struts2输入<无提示解决方法
查看>>
字符串资源多语言的出错问题
查看>>
集合框架
查看>>
Git 大杂烩(初学git 常用的命令)
查看>>
清除默认样式
查看>>
padding-left: 20%;position: relative; left: 50%;
查看>>
zabbix专题:第七章 添加图像Graphs,添加聚合图形Screens
查看>>
移动端随记:flexbox兼容处理
查看>>
CCIE集训营培训路由与交换的全套视频
查看>>
IOS,图片切换动画
查看>>
对象属性合并
查看>>