iOS--音樂播放器之DOUAudioStreamer

好久沒有寫東西了,最近加班太嚴重,今天抽空把用到的音樂播放器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];
}

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章