你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發基礎 >> iOS播放遠程網絡音樂的核心技術點

iOS播放遠程網絡音樂的核心技術點

編輯:IOS開發基礎

2756501-050160b9b0f2aa53.jpg

一、前言

這兩天做了個小項目涉及到了遠程音樂播放,因為第一次做這種音樂項目,邊查資料邊做,其中涉及到主要技術點有:

  • 如何播放遠程網絡音樂

  • 如何切換當前正在播放中的音樂資源

  • 如何監聽音樂播放的各種狀態(播放器狀態、播放的進度、緩沖的進度,播放完成)

  • 如何手動操控播放進度

  • 如何在後台模式或者鎖屏情況下正常播放音樂

  • 如何在鎖屏模式下顯示音樂播放信息和遠程操控音樂

如果您對一塊技術點有興趣或者正在尋找相關資料,那麼本篇或許能提供一些參考或啟發。

二、 網絡音樂播放的核心技術點

根據自己的經驗和查了一些音樂播放的相關資料,最簡單和最易上手的的技術方案我想應該是采用ios系統自帶的AVFoundation框架。

我們知道AVFoundation框架是蘋果專門為多媒體打造的一個庫,這個庫非常強大,專門用來處理音視頻等復雜的多媒體技術,而本篇要講的所有技術點就是基於AVFoundation框架中的一個類——AVPlayer。

那麼AVPlayer是什麼?

你可以把他看成是一個已經封裝好的播放器,它的作用是用來播放遠程的或本地的視頻和音頻。因為本地的音視頻的播放比較簡單,這裡就不做講述,本編主要是講遠程音樂播放,因為都是基於AVPlayer同一套API,所以掌握遠程音樂播放其實就是相當於掌握遠程視頻播放。好了廢話就不多說了,下面開始上菜。

1、導入AVFoundation框架,創建AVPlayer播放器

-(AVPlayer *)player
{    if (_player == nil) {         // AVPlayerItem是一個包裝音樂資源的類,初始化時可以傳入一個音樂的url
        AVPlayerItem *item = [[AVPlayerItem alloc] initWithURL:[NSURL URLWithString:@"http://xxxxxxxx"]];        //通過AVPlayerItem初始化player
        _player = [[AVPlayer alloc] initWithPlayerItem:item];
    }    return _player;
}

此處懶加載創建,讓播放器成為控制器的全局屬性,注意需要強引用,否則回收釋放掉了就無法播放。

2、播放或停止音樂

    //開始播放
    [self.player play];     //停止播放
    [self.player pause];

這個沒什麼好講的,只要調用AVPlayer的兩個實例方法

3、切換當前正在播放中的音樂資源

//創建需要播放的AVPlayerItem
 AVPlayerItem *item = [[AVPlayerItem alloc] initWithURL:[NSURL URLWithString:model.url]]; //替換當前音樂資源  
 [self.player replaceCurrentItemWithPlayerItem:item];

這個可以用於歌曲的切換,如上一首、下一首。

4、通過KVO監聽播放器的狀態

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

拿到播放器的currentItem,注冊當前對象為觀察者,監聽它的status屬性。status屬性是AVPlayerItemStatus類型,它是一個枚舉類型,如下:

typedef NS_ENUM(NSInteger, AVPlayerItemStatus) {    AVPlayerItemStatusUnknown,//未知狀態
    AVPlayerItemStatusReadyToPlay,//准備播放
    AVPlayerItemStatusFailed//加載失敗};

當status屬性值發生改變時,就會觸發觀察者方法的回調,如下:

//觀察者回調-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{   //注意這裡查看的是self.player.status屬性
    if ([keyPath isEqualToString:@"status"]) {        switch (self.player.status) {            case AVPlayerStatusUnknown:
            {                NSLog(@"未知轉態");
            }                break;            case AVPlayerStatusReadyToPlay:
            {                NSLog(@"准備播放");
            }                break;            case AVPlayerStatusFailed:
            {                NSLog(@"加載失敗");
            }                break;                 default:                break;
        }
   }
}

self.player.status ==  AVPlayerStatusReadyToPlay時,音樂就會開始正常播放,另外兩種狀態音樂是無法播放的,可以在上面方法相應狀態裡給出提示。這裡需要特別強調一點的是觀察者監聽的對象是self.player.currentItem,而不是self.player,而當監聽的屬性發生改變時,觀察者回調的方法裡需要查看的是self.player.status。當然,你也可以不這麼干,但是我嘗試過好幾次,不這麼干的後果是無法監聽到self.player.status屬性的改變。

當音樂播放完成,或者切換下一首歌曲時,請務必記得移除觀察者,否則會crash。操作如下:

//移除觀察者
 [self.player.currentItem removeObserver:self forKeyPath:@"status"];

5、監聽音樂的緩沖進度

這個也是通過KVO監聽播放器當前播放的音樂資源AVPlayerItemloadedTimeRanges屬性。我們先看監聽,如下:

//KVO監聽音樂緩沖狀態[self.player.currentItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];

loadedTimeRanges屬性發生改變時,回調如下:

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{    if ([keyPath isEqualToString:@"loadedTimeRanges"]) {        NSArray * timeRanges = self.player.currentItem.loadedTimeRanges;        //本次緩沖的時間范圍
        CMTimeRange timeRange = [timeRanges.firstObject CMTimeRangeValue];        //緩沖總長度
        NSTimeInterval totalLoadTime = CMTimeGetSeconds(timeRange.start) + CMTimeGetSeconds(timeRange.duration);        //音樂的總時間
        NSTimeInterval duration = CMTimeGetSeconds(self.player.currentItem.duration);        //計算緩沖百分比例
        NSTimeInterval scale = totalLoadTime/duration;        //更新緩沖進度條
       self.loadTimeProgress.progress = scale;
    }

}

loadedTimeRanges這個屬性是一個數組,裡面裝的是本次緩沖的時間范圍,這個范圍是用一個結構體CMTimeRange表示,當然在oc中結構體是不能直接存放數組的,所以它被包裝成了oc對象NSValue

我們來看下這個結構體:

typedef struct{
    CMTime            start;        
    CMTime            duration;    
} CMTimeRange;

start表示本次緩沖時間的起點,duratin表示本次緩沖持續的時間范圍,具體詳細的計算方法可以看上面方法的實現。

當音樂播放完成,或者切換下一首歌曲時,請務必記得移除觀察者,否則會crash。操作如下:

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

6、監聽音樂播放的進度

這個不是通過KVO了,AVPlayer專門提供了下面這個api用來監聽播放的進度:

/**
 監聽音樂播放進度 @param interval 監聽的時間間隔,用來設置多長時間回調一次 @param queue    隊列,一般傳主隊列 @param block    回調的block,會把當前的播放時間傳遞過來 @return 監聽的對象
 */- (id)addPeriodicTimeObserverForInterval:(CMTime)interval queue:(nullable dispatch_queue_t)queue usingBlock:(void (^)(CMTime time))block;

操作如下:

 __weak typeof(self) weakSelf = self;    self.timeObserver =  [self.player addPeriodicTimeObserverForInterval:CMTimeMake(1.0, 1.0) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {        //當前播放的時間
        float current = CMTimeGetSeconds(time);        //總時間
        float total = CMTimeGetSeconds(item.duration);        if (current) {            float progress = current / total;            //更新播放進度條
           weakSelf.playSlider.value = progress;
            weakSelf.currentTime.text = [weakSelf timeFormatted:current];
        }
    }];

我們可以這個block裡面拿到當前播放時間,根據總時間計算出當前播放所占的時間比例,最後更新播放進度條。這裡又涉及到了一個數據類類型CMTime,它也是一個結構體,用來作為時間的格式,定義如下:

   typedef struct
     CMTimeValue    value;        
     CMTimeScale    timescale;    
     CMTimeFlags    flags;        
     CMTimeEpoch    epoch;        
   } CMTime;

CMTime是以分數的形式表示時間,value表示分子,timescale表示分母,flags是位掩碼,表示時間的指定狀態。所以我們要獲得時間的秒數需要分子除以分母。當然你還可以用下面這個函數來獲取時間的秒數:

Float64 CMTimeGetSeconds(CMTime time)

最後,當音樂播放完成或者切換音樂時,依然需要移除監聽:

if (self.timeObserver) {
        [self.player removeTimeObserver:self.timeObserver];        self.timeObserver = nil;
    }

7、手動超控(移動滑塊)播放進度

這是一個播放音視頻很常見的功能,所以強大的AVPlayer理所當然的提供了幾個api,下面只講述其中最簡單的一個:

/**
 定位播放時間

 @param time 指定的播放時間
 */- (void)seekToTime:(CMTime)time;

具體使用如下:

//移動滑塊調整播放進度- (IBAction)playSliderValueChange:(UISlider *)sender
{    //根據值計算時間
    float time = sender.value * CMTimeGetSeconds(self.player.currentItem.duration);    //跳轉到當前指定時間
    [self.player seekToTime:CMTimeMake(time, 1)];
}

8、監聽音樂播放完成

一般音視頻播放完成時我們或多或少的都要處理一些業務,比如循環播放,播完退出界面等等。下面看下如何監聽AVPlayer的播放完成。

//給AVPlayerItem添加播放完成通知
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playFinished:) name:AVPlayerItemDidPlayToEndTimeNotification object:_player.currentItem];

這裡是采用注冊監聽AVPlayerItemDidPlayToEndTimeNotification通知,當AVPlayer一播放完成時,便會發出這個通知,我們收到通知後進行處理即可

9、設置音樂後台播放

我們知道運行在ios系統下的程序一旦進入後台就會處於休眠狀態,程序停止運行了,也就播放不了什麼音樂了。但是有一些特定功能的app還是處於可以後台運行的,比如音樂類型的app正處於這個范疇。但是,並不是說你在應用中播放音樂就能後台高枕無憂的運行了,你依然需要做如下幾步操作:

(1)開啟後台模式

target ->capabilities-> Background modes ->打開開關 ->勾選第一個選項

2756501-d937b7fde84dfe72.jpg

設置後台播放模式

(2)程序啟動時設置音頻會話

   //一般在方法:application: didFinishLaunchingWithOptions:設置
   //獲取音頻會話
    AVAudioSession *session = [AVAudioSession sharedInstance];    //設置類型是播放。
    [session setCategory:AVAudioSessionCategoryPlayback error:nil];    //激活音頻會話。
    [session setActive:YES error:nil];

以上兩步設置無誤,程序進入後台模式,便可以進行音樂播放

10、如何設置音樂鎖頻信息

我們看百度音樂鎖頻時,也依然能在屏幕上展示歌曲的信息,以及切換歌曲等。下面看看這個功能是如何實現的:

//音樂鎖屏信息展示- (void)setupLockScreenInfo
{    // 1.獲取鎖屏中心
    MPNowPlayingInfoCenter *playingInfoCenter = [MPNowPlayingInfoCenter defaultCenter];   //初始化一個存放音樂信息的字典
    NSMutableDictionary *playingInfoDict = [NSMutableDictionary dictionary];    // 2、設置歌曲名
    if (self.currentModel.name) {
        [playingInfoDict setObject:self.currentModel.name forKey:MPMediaItemPropertyAlbumTitle];
    }    // 設置歌手名
    if (self.currentModel.artist) {
        [playingInfoDict setObject:self.currentModel.artist 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 *)event
{    switch (event.subtype) {        //播放
        case UIEventSubtypeRemoteControlPlay:{
            [self.player play];
                    }            break;        //停止
        case UIEventSubtypeRemoteControlPause:{
            [self.player pause];
                   }            break;        //下一首
        case UIEventSubtypeRemoteControlNextTrack:
            [self nextBtnAction:nil];            break;        //上一首
        case UIEventSubtypeRemoteControlPreviousTrack:
            [self lastBtnAction:nil];            break;        default:            break;
    }
}

三、總結

最後,畫了一張圖總結下播放遠程網絡音樂的流程:

2756501-a1b1a700cfe0d02b.png

根據QQ音樂的界面做了個小demo,下面是demo的真機前台和後台播放的運行效果:

2756501-8ffbf6db2094ba13.PNG

前台播放

2756501-5950833ed28f43e6.PNG

後台播放

附上github下載地址:https://github.com/yedexiong/MsuicPlayDemo.git

四、結束語

播放遠程網絡音樂的核心技術點基本上已經寫完,當然AVPlayer還有很多強大的功能沒有寫出來,有興趣的可以進一步挖掘。寫到這裡已經疲倦至極,如果喜歡的可以點贊和關注,後續會持續更新一些精彩的技術點。



  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved