iOS基於FreeStreamer的仿網易雲音樂播放器

前言

前段時間寫過一個基於VLCKit實現的網易雲音樂播放demo-GKAudioPlayerDemo,以及3篇文章git

iOS-VLCKit實現仿網易雲音樂播放音樂(一)github

iOS-VLCKit實現仿網易雲音樂播放音樂(二)瀏覽器

iOS-VLCKit實現仿網易雲音樂播放音樂(三)緩存

有興趣的能夠看看。 基於VLCKit實現的有不少問題,好比不能播放本地音樂、不能獲取播放進度、播放時有不少狀況會被暫停或者閃退(有多是我寫的有問題)。因而通過各類搜索和探尋,最終發現FreeStreamer實現的比較好,因此又從新寫了GKWYMusic這個demo,本次demo的數據所有來自百度音樂,僅供學習使用。bash

說明

GKWYMusic實現的有如下功能:網絡

* 網絡音樂的播放、緩存、下載
* 本地音樂的播放(已下載的音樂)
* 歌詞滾動、音量控制、歌曲切換
* 設置循環類型、上一曲、下一曲、喜歡歌曲等
* 鎖屏控制(播放、暫停、喜歡、上一曲、下一曲、播放條拖動)
* 耳機線控(播放、暫停、上一曲、下一曲、快進、快退)
* 通知監聽(插拔耳機、播放打斷)
複製代碼

本次主要講一下對FreeStreamer的封裝及使用。async

效果圖

gkwymusic.gif

封裝

本次對FreeStreamer封裝了一個單例類GKAudioPlayer,可在demo中查看,使用到了FreeStreamer中的FSAudioStream。 一、建立FSAudioStream並監聽播放狀態,以下工具

#pragma mark - 懶加載
- (FSAudioStream *)audioStream {
    if (!_audioStream) {
        _audioStream = [[FSAudioStream alloc] init];
        
        __weak typeof(self) weakSelf = self;
        
        _audioStream.onCompletion = ^{
            NSLog(@"完成");
        };
        
        _audioStream.onStateChange = ^(FSAudioStreamState state) {
            switch (state) {
                case kFsAudioStreamRetrievingURL:       // 檢索url
                    NSLog(@"檢索url");
                    weakSelf.state = GKAudioPlayerStateLoading;
                    break;
                case kFsAudioStreamBuffering:           // 緩衝
                    NSLog(@"緩衝中。。");
                    weakSelf.state = GKAudioPlayerStateBuffering;
                    break;
                case kFsAudioStreamSeeking:             // seek
                    NSLog(@"seek中。。");
                    weakSelf.state = GKAudioPlayerStateLoading;
                    break;
                case kFsAudioStreamPlaying:             // 播放
                    NSLog(@"播放中。。");
                    weakSelf.state = GKAudioPlayerStatePlaying;
                    break;
                case kFsAudioStreamPaused:              // 暫停
                    NSLog(@"播放暫停");
                    weakSelf.state = GKAudioPlayerStatePaused;
                    break;
                case kFsAudioStreamStopped:              // 中止
                    NSLog(@"播放中止");
                    weakSelf.state = GKAudioPlayerStateStopped;
                    break;
                case kFsAudioStreamRetryingFailed:              // 檢索失敗
                    NSLog(@"檢索失敗");
                    weakSelf.state = GKAudioPlayerStateError;
                    break;
                case kFsAudioStreamRetryingStarted:             // 檢索開始
                    NSLog(@"檢索開始");
                    weakSelf.state = GKAudioPlayerStateLoading;
                    break;
                case kFsAudioStreamFailed:                      // 播放失敗
                    NSLog(@"播放失敗");
                    weakSelf.state = GKAudioPlayerStateError;
                    break;
                case kFsAudioStreamPlaybackCompleted:           // 播放完成
                    NSLog(@"播放完成");
                    weakSelf.state = GKAudioPlayerStateEnded;
                    break;
                case kFsAudioStreamRetryingSucceeded:           // 檢索成功
                    NSLog(@"檢索成功");
                    weakSelf.state = GKAudioPlayerStateLoading;
                    break;
                case kFsAudioStreamUnknownState:                // 未知狀態
                    NSLog(@"未知狀態");
                    weakSelf.state = GKAudioPlayerStateError;
                    break;
                case kFSAudioStreamEndOfFile:                   // 緩衝結束
                    NSLog(@"緩衝結束");
                    {
                        // 定時器中止後須要再次調用獲取進度方法,防止出現進度不許確的狀況
                        [weakSelf bufferTimerAction:nil];
                        
                        [weakSelf stopBufferTimer];
                    }
                    break;
                    
                default:
                    break;
            }
            [weakSelf setupPlayerState:weakSelf.state];
        };
    }
    return _audioStream;
}
複製代碼

二、緩衝進度的監聽,須要在播放時先建立定時器,當緩衝完成時在關閉定時器學習

// 建立定時器
self.bufferTimer = [GKTimer scheduledTimerWithTimeInterval:0.1f target:self selector:@selector(bufferTimerAction:) userInfo:nil repeats:YES];
複製代碼
// 獲取進度
- (void)bufferTimerAction:(id)sender {
    float preBuffer      = (float)self.audioStream.prebufferedByteCount;
    float contentLength  = (float)self.audioStream.contentLength;
    
    // 這裏獲取的進度不能準確地獲取到1
    float bufferProgress = contentLength > 0 ? preBuffer / contentLength : 0;
    
//    NSLog(@"緩衝進度%.2f", bufferProgress);
    
    // 爲了能使進度準確的到1,這裏作了一些處理
    int buffer = (int)(bufferProgress + 0.5);
    
    if (bufferProgress > 0.9 && buffer >= 1) {
        [self stopBufferTimer];
    }
    
    if ([self.delegate respondsToSelector:@selector(gkPlayer:bufferProgress:)]) {
        [self.delegate gkPlayer:self bufferProgress:bufferProgress];
    }
}
複製代碼

三、播放,覺得播放時會將數據緩存到本地,因此作了刪除處理,可根據需求修改。另外作了url的判斷,判斷是網絡仍是本地ui

- (void)setPlayUrlStr:(NSString *)playUrlStr {
    if (![_playUrlStr isEqualToString:playUrlStr]) {
        
        // 切換數據,清除緩存
        [self removeCache];
        
        _playUrlStr = playUrlStr;
        
        if ([playUrlStr hasPrefix:@"http"] || [playUrlStr hasPrefix:@"https"]) {
            self.audioStream.url = [NSURL URLWithString:playUrlStr];
        }else {
            self.audioStream.url = [NSURL fileURLWithPath:playUrlStr];
        }
    }
}
複製代碼

四、seek 設置播放進度

- (void)setPlayerProgress:(float)progress {
    FSStreamPosition position = {0};
    position.position = progress;
    
    dispatch_async(dispatch_get_main_queue(), ^{
        [self.audioStream seekToPosition:position];
    });
}
複製代碼

##最後 本次的demo裏面抽離出來了許多工具類如:搜索框、定時器、歌詞解析、滑桿、刷新、下載管理等,有須要的可使用。

demo會不斷更新,若是有需求歡迎隨時提出。

另外推薦下個人圖片瀏覽器GKPhotoBrowser

相關文章
相關標籤/搜索