iOS--音乐播放器之DOUAudioStreamer

2017 年 12 月 23 日 CocoaChina 指尖流年

好久没有写东西了,最近加班太严重,今天抽空把用到的音乐播放器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:

            

            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];

                [sender setBackgroundImage:[UIImage imageNamed:@"music_stop_icon"] forState:UIControlStateNormal];

                

                break;

            case UIEventSubtypeRemoteControlStop:

                break;

            case UIEventSubtypeRemoteControlPlay:

                [[HYNEntertainmentController sharedInstance].streamer play];

                [sender setBackgroundImage:[UIImage imageNamed:@"music_play_icon"] forState:UIControlStateNormal];

                

                break;

            case UIEventSubtypeRemoteControlTogglePlayPause:

                break;

            case UIEventSubtypeRemoteControlNextTrack:

                

                

                break;

            case UIEventSubtypeRemoteControlPreviousTrack:

                

                

                break;

            default:

                break;

        }

    }

}


整体图片:


上图为未播放


上图为播放中


上图为锁屏时状态


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


登录查看更多
0

相关内容

国际概念建模会议(ER)是介绍和讨论当前概念建模研究的主要国际论坛。感兴趣的主题内容横跨整个概念建模包括等领域的研究和实践。 官网地址:http://dblp.uni-trier.de/db/conf/er/
【2020新书】使用高级C# 提升你的编程技能,412页pdf
专知会员服务
56+阅读 · 2020年6月26日
【反馈循环自编码器】FEEDBACK RECURRENT AUTOENCODER
专知会员服务
22+阅读 · 2020年1月28日
机器学习入门的经验与建议
专知会员服务
92+阅读 · 2019年10月10日
MIT新书《强化学习与最优控制》
专知会员服务
275+阅读 · 2019年10月9日
用Now轻松部署无服务器Node应用程序
前端之巅
16+阅读 · 2019年6月19日
iOS自定义带动画效果的模态框
CocoaChina
7+阅读 · 2019年3月3日
C# 10分钟完成百度人脸识别
DotNet
3+阅读 · 2019年2月17日
抖音爬虫
专知
3+阅读 · 2019年2月11日
React Native 分包哪家强?看这文就够了!
程序人生
13+阅读 · 2019年1月16日
Android P正式发布,你需要尽快做适配了
前端之巅
3+阅读 · 2018年8月7日
已删除
生物探索
3+阅读 · 2018年2月10日
Arxiv
21+阅读 · 2019年8月21日
Music Transformer
Arxiv
5+阅读 · 2018年12月12日
Two Stream 3D Semantic Scene Completion
Arxiv
4+阅读 · 2018年7月16日
Arxiv
5+阅读 · 2018年5月22日
Arxiv
7+阅读 · 2017年12月28日
Arxiv
3+阅读 · 2015年5月16日
VIP会员
相关VIP内容
相关资讯
用Now轻松部署无服务器Node应用程序
前端之巅
16+阅读 · 2019年6月19日
iOS自定义带动画效果的模态框
CocoaChina
7+阅读 · 2019年3月3日
C# 10分钟完成百度人脸识别
DotNet
3+阅读 · 2019年2月17日
抖音爬虫
专知
3+阅读 · 2019年2月11日
React Native 分包哪家强?看这文就够了!
程序人生
13+阅读 · 2019年1月16日
Android P正式发布,你需要尽快做适配了
前端之巅
3+阅读 · 2018年8月7日
已删除
生物探索
3+阅读 · 2018年2月10日
相关论文
Arxiv
21+阅读 · 2019年8月21日
Music Transformer
Arxiv
5+阅读 · 2018年12月12日
Two Stream 3D Semantic Scene Completion
Arxiv
4+阅读 · 2018年7月16日
Arxiv
5+阅读 · 2018年5月22日
Arxiv
7+阅读 · 2017年12月28日
Arxiv
3+阅读 · 2015年5月16日
Top
微信扫码咨询专知VIP会员