用AVPlayer寫音樂播放器

iOS提供了幾種播放音頻的方式:AudioToolbox 、AVAudioPlayer、AVPlayer。

AudioToolbox:播放的時間不能超過30秒,支持格式:caf/wav/aiff
AVAudioPlayer: 使用簡單但只能播放本地音樂文件
AVPlayer:既可以播放音樂又可以播放視頻,支持播放本地音頻和支持流媒體播放,比起上面兩種更適合用來做音樂播放器

AVPlayer的官方說明
AVPlayer可以用來播放視頻和音頻,這里簡單的講下如何用AVPlayer寫音樂播放器。
首先需要了解這兩個類:

1 AVPlayer:用于控制播放器的播放,暫停,播放速度,跳轉(zhuǎn)播放位置等
2 AVPlayerItem:資源管理對象,管理播放鏈接,可用于監(jiān)聽播放狀態(tài),緩沖進度等

導(dǎo)入 AVFoundation.framework以及

#import <Foundation/Foundation.h> 

avplayer播放音頻方式用這幾種:

+(instancetype)playerWithURL:(NSURL *)URL;
-(instancetype)initWithURL:(NSURL *)URL;
+(instancetype)playerWithPlayerItem:(nullable AVPlayerItem *)item;
-(instancetype)initWithPlayerItem:(nullable AVPlayerItem *)item;

前兩種方法設(shè)置對應(yīng)鏈接可直接播放,如果只需直接播放不需要監(jiān)聽播放進度播放狀態(tài)等可使用直接播放對應(yīng)鏈接,但如果需要做一個功能比較完善的播放器就得使用AVPlayerItem。

播放器常用的功能:

-(void)play; //播放
-(void)pause;//暫停

播放本地音頻文件

NSURL *url = [NSURL fileURLWithPath:filePath];//filePath本地音頻路徑
self.player = [[AVPlayer alloc] initWithURL:url];
[self.player play]; // 播放

播放流媒體

NSURL *url = [NSURL URLWithString:urlString];//urlString音頻鏈接
self.player = [[AVPlayer alloc] initWithURL:url];
[self.player play]; // 播放

使用AVPlayerItem進行播放:

AVPlayerItem *playerItem = [[AVPlayerItem alloc]initWithURL:url];
self.player = [[AVPlayer alloc]initWithPlayerItem:playerItem];
[self.player play]; // 播放

對狀態(tài)進行監(jiān)聽:

#pragma mark - 監(jiān)聽
-(void)addPlayerItemListener{ 
    AVPlayerItem *playerItem = self.player.currentItem;
    //監(jiān)聽播放狀態(tài)
    [playerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];

    //監(jiān)聽緩存進度
    [playerItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil]; 

    //添加視頻播放結(jié)束通知
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playerToEnd:)
                                                 name:AVPlayerItemDidPlayToEndTimeNotification object:self.player.currentItem];
    //添加異常中斷通知
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playerStalled:)
                                                 name:AVPlayerItemPlaybackStalledNotification object:self.player.currentItem];
}

#pragma mark - kvo事件處理 
- (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary<NSString*, id> *)change context:(nullable void *)context {
    if ([keyPath isEqualToString:@"status"]) {
        AVPlayerItemStatus status = self.player.currentItem.status;
        switch (status) {
            case AVPlayerStatusUnknown:{
                NSLog(@"KVO:未知狀態(tài),此時不能播放"); 
            }
                break;
            case AVPlayerStatusReadyToPlay:{
                NSLog(@"KVO:準備完畢,可以播放"); 
                return;
            }
                break;
            case AVPlayerStatusFailed:{
                NSLog(@"KVO:加載失敗,網(wǎng)絡(luò)或者服務(wù)器出現(xiàn)問題");
                [self.player.currentItem cancelPendingSeeks];
                [self pause];
                return;
            }
                break;
            default:
                break;
        }
    }else if ([keyPath isEqualToString:@"loadedTimeRanges"]) {
        //緩存進度
       NSLog(@"loadedTimeRanges %f ",[self availableDuration] / self.durationTime));   
   } else {
        NSLog(@"其他");
    }
}

#pragma mark - 計算緩沖總進度
- (NSTimeInterval)availableDuration {
    NSArray *loadedTimeRanges = [[self.player currentItem] loadedTimeRanges];
    CMTimeRange timeRange = [loadedTimeRanges.firstObject CMTimeRangeValue];// 獲取緩沖區(qū)域
    float startSeconds = CMTimeGetSeconds(timeRange.start);
    float durationSeconds = CMTimeGetSeconds(timeRange.duration);
    NSTimeInterval result = startSeconds + durationSeconds;// 計算緩沖總進度
    return result;
}

播放器的狀態(tài):
typedef NS_ENUM(NSInteger, AVPlayerItemStatus) {
AVPlayerItemStatusUnknown,//未知
AVPlayerItemStatusReadyToPlay,//準備播放
AVPlayerItemStatusFailed//播放失敗
};

跟蹤當前的時間,顯示當前播放進度:

self.playTimeObserver = [self.player addPeriodicTimeObserverForInterval:CMTimeMake(3, 4) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
      // MARK: 播放進度
      weakSelf.currentTime = CMTimeGetSeconds(time);
      CGFloat progress = weakSelf.currentTime / weakSelf.durationTime; 
    }];

改變播放的速度,可以設(shè)置player的rate:

@property (nonatomic) float rate; //播放速度,正常播放為1.0,停止播放為0.0

self.player.rate = 0.5;//0.5倍速播放

也可以通過對rate進行監(jiān)聽來判斷播放狀態(tài)

[self.player addObserver:self forKeyPath:@"rate" options:NSKeyValueObservingOptionNew context:nil];

跳轉(zhuǎn)到指定的位置:

-(void)seekToTime:(CMTime)time;

NSTimeInterval durationTime = CMTimeGetSeconds(player.currentItem.duration);
CMTime changedTime = CMTimeMake(durationTime * progress, 1.0);//progress跳轉(zhuǎn)的位置,百分比
[self.player seekToTime:changedTime];

要做上一首和下一首切換歌曲功能,需要維護一個播放列表數(shù)組,監(jiān)聽用戶點擊上下一首按鈕,然后AVPlayer進行歌曲切換需要用到:

-(void)replaceCurrentItemWithPlayerItem:(nullable AVPlayerItem *)item;

重新設(shè)置播放的AVPlayerItem,如果之前設(shè)置了對當前PlayerItem的監(jiān)聽,*需要移除監(jiān)聽,設(shè)置好要切換的歌曲后再重新添加對AVPlayerItem的監(jiān)聽。

如何做到鎖屏界面的播放器顯示和控制呢,
先導(dǎo)入#import <MediaPlayer/MediaPlayer.h>

#pragma mark 添加遠程控制中心
- (void)addRemoteCommandCenterWithTarget:(id)target
                                 playAction:(SEL)playAction
                                pauseAction:(SEL)pauseAction
                             lastSongAction:(SEL)lastAction
                             nextSongAction:(SEL)nextAction {
    // 遠程控制類    播放/暫停/上一曲/下一曲
    MPRemoteCommandCenter *center = [MPRemoteCommandCenter sharedCommandCenter];
    [center.playCommand addTarget:target action:playAction];
    [center.pauseCommand addTarget:target action:pauseAction];
    [center.previousTrackCommand addTarget:target action:lastAction];
    [center.nextTrackCommand addTarget:target action:nextAction];
}

設(shè)置好對應(yīng)的控制事件,播放/暫停/上一曲/下一曲
然后設(shè)置顯示的信息,當歌曲信息變化時,如切換歌曲,歌詞變化時,需要刷新

    MPMediaItemArtwork *itemArtwork = [[MPMediaItemArtwork alloc] initWithImage:artworkImage];//封面
    
    /*  播放信息中心,用于控制鎖屏界面顯示的內(nèi)容
     MPMediaItemPropertyAlbumTitle       專輯
     MPMediaItemPropertyTitle            歌名
     MPMediaItemPropertyArtist           歌手
     MPMediaItemPropertyArtwork          歌曲封面
     MPMediaItemPropertyComposer         編曲
     MPMediaItemPropertyPlaybackDuration 持續(xù)時間
     MPNowPlayingInfoPropertyElapsedPlaybackTime  當前播放時間
     */
    MPNowPlayingInfoCenter *infoCenter = [MPNowPlayingInfoCenter defaultCenter];
    infoCenter.nowPlayingInfo = @{ MPMediaItemPropertyArtist : self.currentSongModel.singer,
                                  MPMediaItemPropertyTitle : self.currentSongModel.name,
                                  MPMediaItemPropertyPlaybackDuration : @(durationTime),
                                  MPNowPlayingInfoPropertyElapsedPlaybackTime : @(currentTime),
                                  MPMediaItemPropertyArtwork : itemArtwork,
                                  };
//    MPMediaItemPropertyAlbumTitle : self.currentSongModel.album,

demo(待完善):https://github.com/Luy7788/LyMusicPlayer

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容