欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

iOS實(shí)現(xiàn)播放遠(yuǎn)程網(wǎng)絡(luò)音樂(lè)的核心技術(shù)點(diǎn)總結(jié)

 更新時(shí)間:2016年11月07日 11:41:41   作者:夜_闌珊  
本篇文章主要介紹了iOS播放遠(yuǎn)程網(wǎng)絡(luò)音樂(lè)的核心技術(shù),采用ios系統(tǒng)自帶的AVFoundation框架來(lái)實(shí)現(xiàn),有需要的朋友可以了解一下。

一、前言

這兩天做了個(gè)小項(xiàng)目涉及到了遠(yuǎn)程音樂(lè)播放,因?yàn)榈谝淮巫鲞@種音樂(lè)項(xiàng)目,邊查資料邊做,其中涉及到主要技術(shù)點(diǎn)有:

  • 如何播放遠(yuǎn)程網(wǎng)絡(luò)音樂(lè)
  • 如何切換當(dāng)前正在播放中的音樂(lè)資源
  • 如何監(jiān)聽(tīng)音樂(lè)播放的各種狀態(tài)(播放器狀態(tài)、播放的進(jìn)度、緩沖的進(jìn)度,播放完成)
  • 如何手動(dòng)操控播放進(jìn)度
  • 如何在后臺(tái)模式或者鎖屏情況下正常播放音樂(lè)
  • 如何在鎖屏模式下顯示音樂(lè)播放信息和遠(yuǎn)程操控音樂(lè)

如果您對(duì)一塊技術(shù)點(diǎn)有興趣或者正在尋找相關(guān)資料,那么本篇或許能提供一些參考或啟發(fā)。

二、 網(wǎng)絡(luò)音樂(lè)播放的核心技術(shù)點(diǎn)

根據(jù)自己的經(jīng)驗(yàn)和查了一些音樂(lè)播放的相關(guān)資料,最簡(jiǎn)單和最易上手的的技術(shù)方案我想應(yīng)該是采用ios系統(tǒng)自帶的AVFoundation框架。

我們知道AVFoundation框架是蘋果專門為多媒體打造的一個(gè)庫(kù),這個(gè)庫(kù)非常強(qiáng)大,專門用來(lái)處理音視頻等復(fù)雜的多媒體技術(shù),而本篇要講的所有技術(shù)點(diǎn)就是基于AVFoundation框架中的一個(gè)類——AVPlayer。

那么AVPlayer是什么?

你可以把他看成是一個(gè)已經(jīng)封裝好的播放器,它的作用是用來(lái)播放遠(yuǎn)程的或本地的視頻和音頻。因?yàn)楸镜氐囊粢曨l的播放比較簡(jiǎn)單,這里就不做講述,本編主要是講遠(yuǎn)程音樂(lè)播放,因?yàn)槎际腔贏VPlayer同一套API,所以掌握遠(yuǎn)程音樂(lè)播放其實(shí)就是相當(dāng)于掌握遠(yuǎn)程視頻播放。好了廢話就不多說(shuō)了,下面開始上菜。

1、導(dǎo)入AVFoundation框架,創(chuàng)建AVPlayer播放器

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

  return _player;
}

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

2、播放或停止音樂(lè)

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

這個(gè)沒(méi)什么好講的,只要調(diào)用AVPlayer的兩個(gè)實(shí)例方法

3、切換當(dāng)前正在播放中的音樂(lè)資源

//創(chuàng)建需要播放的AVPlayerItem
 AVPlayerItem *item = [[AVPlayerItem alloc] initWithURL:[NSURL URLWithString:model.url]];
 //替換當(dāng)前音樂(lè)資源 
 [self.player replaceCurrentItemWithPlayerItem:item];

這個(gè)可以用于歌曲的切換,如上一首、下一首。

4、通過(guò)KVO監(jiān)聽(tīng)播放器的狀態(tài)

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

拿到播放器的currentItem,注冊(cè)當(dāng)前對(duì)象為觀察者,監(jiān)聽(tīng)它的status屬性。status屬性是AVPlayerItemStatus類型,它是一個(gè)枚舉類型,如下:

typedef NS_ENUM(NSInteger, AVPlayerItemStatus) {
  AVPlayerItemStatusUnknown,//未知狀態(tài)
  AVPlayerItemStatusReadyToPlay,//準(zhǔn)備播放
  AVPlayerItemStatusFailed//加載失敗
};

當(dāng)status屬性值發(fā)生改變時(shí),就會(huì)觸發(fā)觀察者方法的回調(diào),如下:

//觀察者回調(diào)
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
  //注意這里查看的是self.player.status屬性
  if ([keyPath isEqualToString:@"status"]) {
    switch (self.player.status) {
      case AVPlayerStatusUnknown:
      {
        NSLog(@"未知轉(zhuǎn)態(tài)");
      }
        break;
      case AVPlayerStatusReadyToPlay:
      {
        NSLog(@"準(zhǔn)備播放");
      }
        break;
      case AVPlayerStatusFailed:
      {
        NSLog(@"加載失敗");
      }
        break;
         default:
        break;
    }
  }
}

當(dāng) self.player.status ==  AVPlayerStatusReadyToPlay時(shí),音樂(lè)就會(huì)開始正常播放,另外兩種狀態(tài)音樂(lè)是無(wú)法播放的,可以在上面方法相應(yīng)狀態(tài)里給出提示。這里需要特別強(qiáng)調(diào)一點(diǎn)的是觀察者監(jiān)聽(tīng)的對(duì)象是 self.player.currentItem,而不是 self.player,而當(dāng)監(jiān)聽(tīng)的屬性發(fā)生改變時(shí),觀察者回調(diào)的方法里需要查看的是 self.player.status。當(dāng)然,你也可以不這么干,但是我嘗試過(guò)好幾次,不這么干的后果是無(wú)法監(jiān)聽(tīng)到 self.player.status屬性的改變。

當(dāng)音樂(lè)播放完成,或者切換下一首歌曲時(shí),請(qǐng)務(wù)必記得移除觀察者,否則會(huì)crash。操作如下:

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

5、監(jiān)聽(tīng)音樂(lè)的緩沖進(jìn)度

這個(gè)也是通過(guò)KVO監(jiān)聽(tīng)播放器當(dāng)前播放的音樂(lè)資源AVPlayerItem的loadedTimeRanges屬性。我們先看監(jiān)聽(tīng),如下:

//KVO監(jiān)聽(tīng)音樂(lè)緩沖狀態(tài)
[self.player.currentItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];

當(dāng) loadedTimeRanges屬性發(fā)生改變時(shí),回調(diào)如下:

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
  if ([keyPath isEqualToString:@"loadedTimeRanges"]) {

    NSArray * timeRanges = self.player.currentItem.loadedTimeRanges;
    //本次緩沖的時(shí)間范圍
    CMTimeRange timeRange = [timeRanges.firstObject CMTimeRangeValue];
    //緩沖總長(zhǎng)度
    NSTimeInterval totalLoadTime = CMTimeGetSeconds(timeRange.start) + CMTimeGetSeconds(timeRange.duration);
    //音樂(lè)的總時(shí)間
    NSTimeInterval duration = CMTimeGetSeconds(self.player.currentItem.duration);
    //計(jì)算緩沖百分比例
    NSTimeInterval scale = totalLoadTime/duration;
    //更新緩沖進(jìn)度條
    self.loadTimeProgress.progress = scale;
  }

}

loadedTimeRanges這個(gè)屬性是一個(gè)數(shù)組,里面裝的是本次緩沖的時(shí)間范圍,這個(gè)范圍是用一個(gè)結(jié)構(gòu)體 CMTimeRange表示,當(dāng)然在oc中結(jié)構(gòu)體是不能直接存放數(shù)組的,所以它被包裝成了oc對(duì)象 NSValue。

我們來(lái)看下這個(gè)結(jié)構(gòu)體:

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

start表示本次緩沖時(shí)間的起點(diǎn),duratin表示本次緩沖持續(xù)的時(shí)間范圍,具體詳細(xì)的計(jì)算方法可以看上面方法的實(shí)現(xiàn)。

當(dāng)音樂(lè)播放完成,或者切換下一首歌曲時(shí),請(qǐng)務(wù)必記得移除觀察者,否則會(huì)crash。操作如下:

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

6、監(jiān)聽(tīng)音樂(lè)播放的進(jìn)度

這個(gè)不是通過(guò)KVO了,AVPlayer專門提供了下面這個(gè)api用來(lái)監(jiān)聽(tīng)播放的進(jìn)度:

/**
 監(jiān)聽(tīng)音樂(lè)播放進(jìn)度

 @param interval 監(jiān)聽(tīng)的時(shí)間間隔,用來(lái)設(shè)置多長(zhǎng)時(shí)間回調(diào)一次
 @param queue  隊(duì)列,一般傳主隊(duì)列
 @param block  回調(diào)的block,會(huì)把當(dāng)前的播放時(shí)間傳遞過(guò)來(lái)

 @return 監(jiān)聽(tīng)的對(duì)象
 */
- (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) {
    //當(dāng)前播放的時(shí)間
    float current = CMTimeGetSeconds(time);
    //總時(shí)間
    float total = CMTimeGetSeconds(item.duration);
    if (current) {
      float progress = current / total;
      //更新播放進(jìn)度條
      weakSelf.playSlider.value = progress;
      weakSelf.currentTime.text = [weakSelf timeFormatted:current];
    }
  }];

我們可以這個(gè)block里面拿到當(dāng)前播放時(shí)間,根據(jù)總時(shí)間計(jì)算出當(dāng)前播放所占的時(shí)間比例,最后更新播放進(jìn)度條。這里又涉及到了一個(gè)數(shù)據(jù)類類型CMTime,它也是一個(gè)結(jié)構(gòu)體,用來(lái)作為時(shí)間的格式,定義如下:

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

CMTime是以分?jǐn)?shù)的形式表示時(shí)間,value表示分子,timescale表示分母,flags是位掩碼,表示時(shí)間的指定狀態(tài)。所以我們要獲得時(shí)間的秒數(shù)需要分子除以分母。當(dāng)然你還可以用下面這個(gè)函數(shù)來(lái)獲取時(shí)間的秒數(shù):

Float64 CMTimeGetSeconds(CMTime time)

最后,當(dāng)音樂(lè)播放完成或者切換音樂(lè)時(shí),依然需要移除監(jiān)聽(tīng):

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

7、手動(dòng)超控(移動(dòng)滑塊)播放進(jìn)度

這是一個(gè)播放音視頻很常見(jiàn)的功能,所以強(qiáng)大的AVPlayer理所當(dāng)然的提供了幾個(gè)api,下面只講述其中最簡(jiǎn)單的一個(gè):

/**
 定位播放時(shí)間

 @param time 指定的播放時(shí)間
 */
- (void)seekToTime:(CMTime)time;
具體使用如下:

//移動(dòng)滑塊調(diào)整播放進(jìn)度
- (IBAction)playSliderValueChange:(UISlider *)sender
{
  //根據(jù)值計(jì)算時(shí)間
  float time = sender.value * CMTimeGetSeconds(self.player.currentItem.duration);
  //跳轉(zhuǎn)到當(dāng)前指定時(shí)間
  [self.player seekToTime:CMTimeMake(time, 1)];
}

8、監(jiān)聽(tīng)音樂(lè)播放完成

一般音視頻播放完成時(shí)我們或多或少的都要處理一些業(yè)務(wù),比如循環(huán)播放,播完退出界面等等。下面看下如何監(jiān)聽(tīng)AVPlayer的播放完成。

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

這里是采用注冊(cè)監(jiān)聽(tīng)AVPlayerItemDidPlayToEndTimeNotification通知,當(dāng)AVPlayer一播放完成時(shí),便會(huì)發(fā)出這個(gè)通知,我們收到通知后進(jìn)行處理即可

9、設(shè)置音樂(lè)后臺(tái)播放

我們知道運(yùn)行在ios系統(tǒng)下的程序一旦進(jìn)入后臺(tái)就會(huì)處于休眠狀態(tài),程序停止運(yùn)行了,也就播放不了什么音樂(lè)了。但是有一些特定功能的app還是處于可以后臺(tái)運(yùn)行的,比如音樂(lè)類型的app正處于這個(gè)范疇。但是,并不是說(shuō)你在應(yīng)用中播放音樂(lè)就能后臺(tái)高枕無(wú)憂的運(yùn)行了,你依然需要做如下幾步操作:

(1)開啟后臺(tái)模式

target ->capabilities-> Background modes ->打開開關(guān) ->勾選第一個(gè)選項(xiàng)

(2)程序啟動(dòng)時(shí)設(shè)置音頻會(huì)話

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

以上兩步設(shè)置無(wú)誤,程序進(jìn)入后臺(tái)模式,便可以進(jìn)行音樂(lè)播放

10、如何設(shè)置音樂(lè)鎖頻信息

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

//音樂(lè)鎖屏信息展示
- (void)setupLockScreenInfo
{
  // 1.獲取鎖屏中心
  MPNowPlayingInfoCenter *playingInfoCenter = [MPNowPlayingInfoCenter defaultCenter];

  //初始化一個(gè)存放音樂(lè)信息的字典
  NSMutableDictionary *playingInfoDict = [NSMutableDictionary dictionary];
  // 2、設(shè)置歌曲名
  if (self.currentModel.name) {
    [playingInfoDict setObject:self.currentModel.name forKey:MPMediaItemPropertyAlbumTitle];
  }
  // 設(shè)置歌手名
  if (self.currentModel.artist) {
    [playingInfoDict setObject:self.currentModel.artist forKey:MPMediaItemPropertyArtist];
  }
  // 3設(shè)置封面的圖片
  UIImage *image = [self getMusicImageWithMusicId:self.currentModel];
  if (image) {
    MPMediaItemArtwork *artwork = [[MPMediaItemArtwork alloc] initWithImage:image];
    [playingInfoDict setObject:artwork forKey:MPMediaItemPropertyArtwork];
  }

  // 4設(shè)置歌曲的總時(shí)長(zhǎng)
  [playingInfoDict setObject:self.currentModel.detailDuration forKey:MPMediaItemPropertyPlaybackDuration];

  //音樂(lè)信息賦值給獲取鎖屏中心的nowPlayingInfo屬性
  playingInfoCenter.nowPlayingInfo = playingInfoDict;

  // 5.開啟遠(yuǎn)程交互,只有開啟這個(gè)才能進(jìn)行遠(yuǎn)程操控
  [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
}

這里設(shè)置圖片時(shí)需要注意下,異步加載網(wǎng)絡(luò)圖片后再設(shè)置是無(wú)效的,所以圖片信息最好是先請(qǐng)求下來(lái)后再進(jìn)行設(shè)置。

遠(yuǎn)程超控的回調(diào)如下:

//監(jiān)聽(tīng)遠(yuǎn)程交互方法
- (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;
  }
}

三、總結(jié)

最后,畫了一張圖總結(jié)下播放遠(yuǎn)程網(wǎng)絡(luò)音樂(lè)的流程:

根據(jù)QQ音樂(lè)的界面做了個(gè)小demo,下面是demo的真機(jī)前臺(tái)和后臺(tái)播放的運(yùn)行效果:

四、結(jié)束語(yǔ)

播放遠(yuǎn)程網(wǎng)絡(luò)音樂(lè)的核心技術(shù)點(diǎn)基本上已經(jīng)寫完,當(dāng)然AVPlayer還有很多強(qiáng)大的功能沒(méi)有寫出來(lái),有興趣的可以進(jìn)一步挖掘。寫到這里已經(jīng)疲倦至極,后續(xù)會(huì)持續(xù)更新一些精彩的技術(shù)點(diǎn),也希望大家多多支持腳本之家。

相關(guān)文章

最新評(píng)論