最近項目需求做個播放視頻功能,之前對這方面接觸的也不多,閱讀了一些開源播放器的源碼學習了一下,總結了一些使用方法,主要講述使用AVPlayer播放網(wǎng)絡音樂
iOS系統(tǒng)中音頻的四種播放方式(重點介紹前三種)
1)AVAudioPlayer 在<AVFoundation/AVFoundation.h>框架里面
使用簡單方便群井,但只能播放本地音頻,不支持流媒體播放,每一個audioplayer對象就是一段音頻
2) AVPlayer 也在 在<AVFoundation/AVFoundation.h>框架里面
iOS 4.0以后伪嫁,可以使用AVPlayer播放本地音頻和支持流媒體播放胁黑,但提供接口較少病游,處理音頻不夠靈活
3)系統(tǒng)聲音 在<AudioToolbox/AudioToolbox.h>框架里面
音頻數(shù)據(jù)文件可分為壓縮和非壓縮的存儲類型。壓縮的音頻文件雖然文件體積較新驳取(相對于非壓縮的)乍桂,但需要耗費處理器的性能進行解壓和解碼袜瞬。如果音頻文件體積較小怜俐,壓縮后的音頻文件,也不會節(jié)省較大的磁盤空間邓尤。像這一類小型非壓縮的文件可以注冊成為系統(tǒng)聲音
格式為:caf/wav/aiff格式拍鲤,且時長小于30s
4)音頻隊列
音頻隊列主要處理流媒體播放,提供了強大且靈活的API接口(C函數(shù)的接口)汞扎,但處理起來較為復雜
1季稳、AVAudioPlayer處理音頻
// 1.獲取要播放音頻文件的URL
NSURL *fileURL = [[NSBundle mainBundle]URLForResource:@"劉若英 - 原來你也在這里" withExtension:@".mp3"];
// 2.創(chuàng)建 AVAudioPlayer 對象
AVAudioPlayer *audioPlayer = [[AVAudioPlayer alloc]initWithContentsOfURL:fileURL error:nil];
// 3.打印歌曲信息
NSString *msg = [NSString stringWithFormat:@"音頻文件聲道數(shù):%ld\n 音頻文件持續(xù)時間:%g",audioPlayer.numberOfChannels,audioPlayer.duration];
NSLog(@"%@",msg);
// 4.設置循環(huán)播放
audioPlayer.numberOfLoops = -1;
audioPlayer.delegate = self;
// 5.開始播放
[audioPlayer play];
2之剧、系統(tǒng)聲音
1狞谱、需導入引入AudioToolbox框架
2汽烦、使用實例
NSBundle *bundle = [NSBundle mainBundle];
NSString *path = [bundle pathForResource:@"44th Street Medium" ofType:@"caf"];
// 初始化本地文件url
NSURL *url = [NSURL fileURLWithPath:path];
UInt32 soundID;
// 將URL所在的音頻文件注冊為系統(tǒng)聲音,soundID音頻ID標示該音頻
AudioServicesCreateSystemSoundID((__bridge CFURLRef)url, &soundID);
// 播放音頻
AudioServicesPlaySystemSound(soundID);
//播放系統(tǒng)震動
AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
//銷毀聲音
AudioServicesDisposeSystemSoundID(soundID);
3歼秽、AVPlayer
AVPlayer存在于AVFoundation中贰盗,其實它是一個視頻播放器斑鸦,不僅能夠播放音頻眯停,還可以播放視頻砸民,支持本地和網(wǎng)鏈鲫构,更加接近底層浓恶,定制也更加靈活。
AVPlayer首先了解一下幾個常用的類:
1结笨、AVAsset:AVAsset類專門用于獲取多媒體的相關信息,包括獲取多媒體的畫面包晰、聲音等信息,屬于一個抽象類炕吸,不能直接使用伐憾。
2、AVURLAsset:AVAsset的子類算途,可以根據(jù)一個URL路徑創(chuàng)建一個包含媒體信息的AVURLAsset對象塞耕。
3、AVPlayerItem:一個媒體資源管理對象嘴瓤,管理者視頻的一些基本信息和狀態(tài)扫外,一個AVPlayerItem對應著一個視頻資源莉钙。
4、AVPlayer:播放器筛谚。
5磁玉、CMTime:是一個結構體,里面存儲著當前的播放進度驾讲,總的播放時長蚊伞。
1、實例化一個AVPlayer:
- (AVPlayer *)player {
if (_player == nil) {
_player = [[AVPlayer alloc] init];
_player.volume = 1.0; // 默認最大音量
}
return _player;
}
播放音頻相關狀態(tài)監(jiān)聽和移除
1吮铭、監(jiān)聽status时迫,AVPlayerItemStatus有三種狀態(tài):
2、監(jiān)聽loadedTimeRanges谓晌,這個就是緩沖進度掠拳,可以進行緩沖進度條的設置
3、AVPlayerItemDidPlayToEndTimeNotification纸肉,注冊這個通知溺欧,當播放器播放完成的時候進行回調(diào)。
4柏肪、addPeriodicTimeObserverForInterval姐刁,監(jiān)聽當前播放進度。
//播放音頻的方法(下面會在控制器調(diào)用)
- (void)p_musicPlayerWithURL:(NSURL *)playerItemURL{
// 移除監(jiān)聽
[self p_currentItemRemoveObserver];
// 創(chuàng)建要播放的資源
AVPlayerItem *playerItem = [[AVPlayerItem alloc]initWithURL:playerItemURL];
// 播放當前資源
[self.player replaceCurrentItemWithPlayerItem:playerItem];
// 添加觀察者
[self p_currentItemAddObserver];
}
- (void)p_playerAddObserver {
[self.player addObserver:self forKeyPath:@"rate" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:nil];
[self.player addObserver:self forKeyPath:@"currentItem" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:nil];
}
- (void)p_playerRemoveObserver {
[self.player removeObserver:self forKeyPath:@"rate"];
[self.player removeObserver:self forKeyPath:@"currentItem"];
}
- (void)p_currentItemAddObserver {
//監(jiān)控狀態(tài)屬性烦味,注意AVPlayer也有一個status屬性聂使,通過監(jiān)控它的status也可以獲得播放狀態(tài)
[self.player.currentItem addObserver:self forKeyPath:@"status" options:(NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew) context:nil];
//監(jiān)控緩沖加載情況屬性
[self.player.currentItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:nil];
//監(jiān)控播放完成通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playbackFinished:) name:AVPlayerItemDidPlayToEndTimeNotification object:self.player.currentItem];
__weak typeof(self) weakSelf = self;
//監(jiān)控時間進度
self.timeObserver = [self.player addPeriodicTimeObserverForInterval:CMTimeMake(1, 1) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
NSLog(@"%@---%@",[weakSelf currentTimeStr],[weakSelf durationStr]);
}];
}
- (void)p_currentItemRemoveObserver {
[self.player.currentItem removeObserver:self forKeyPath:@"status"];
[self.player.currentItem removeObserver:self forKeyPath:@"loadedTimeRanges"];
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:nil];
[self.player removeTimeObserver:self.timeObserver];
}
#pragma mark - KVO
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
AVPlayerItem *playerItem = object;
if ([keyPath isEqualToString:@"status"]) {
AVPlayerItemStatus status = [change[@"new"] integerValue];
switch (status) {
case AVPlayerItemStatusReadyToPlay:
{
// 開始播放
[self play];
// 保存我聽過的
// [self p_saveHistory];
// // 代理回調(diào),開始初始化狀態(tài)
// if (self.delegate && [self.delegate respondsToSelector:@selector(startPlayWithplayer:)]) {
// [self.delegate startPlayWithplayer:self.player];
// }
}
break;
case AVPlayerItemStatusFailed:
{
NSLog(@"加載失敗");
}
break;
case AVPlayerItemStatusUnknown:
{
NSLog(@"未知資源");
}
break;
default:
break;
}
} else if([keyPath isEqualToString:@"loadedTimeRanges"]){
NSArray *array=playerItem.loadedTimeRanges;
//本次緩沖時間范圍
CMTimeRange timeRange = [array.firstObject CMTimeRangeValue];
float startSeconds = CMTimeGetSeconds(timeRange.start);
float durationSeconds = CMTimeGetSeconds(timeRange.duration);
//緩沖總長度
NSTimeInterval totalBuffer = startSeconds + durationSeconds;
NSLog(@"共緩沖:%.2f",totalBuffer);
// if (self.delegate && [self.delegate respondsToSelector:@selector(updateBufferProgress:)]) {
// [self.delegate updateBufferProgress:totalBuffer];
// }
} else if ([keyPath isEqualToString:@"rate"]) {
float rate = self.player.rate;
NSLog(@"%f---rate",rate);
// if (self.delegate && [self.delegate respondsToSelector:@selector(player:changeRate:)]) {
// [self.delegate player:self.player changeRate:rate];
// }
} else if ([keyPath isEqualToString:@"currentItem"]) {
NSLog(@"新的currentItem");
// if (self.delegate && [self.delegate respondsToSelector:@selector(changeNewPlayItem:)]) {
// [self.delegate changeNewPlayItem:self.player];
// }
}
}
- (void)playbackFinished:(NSNotification *)notifi {
NSLog(@"播放完成");
// // 需要自動播放下一首
if (self.playingSortType == KSPlayingSortTypeSequence1) {
// 播放列表中的最后一個故事
if (self.currentVoiceIndex == self.voices.count-1) {
[self pause];
[self playAnyVoiceWithIndex:0];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self pause];
[[NSNotificationCenter defaultCenter] postNotificationName:@"kKSPlayAlreadyStopNotification" object:nil userInfo:nil];
});
} else {
[self playNext];
}
} else if (self.playingSortType == KSPlayingSortTypeSingleloop1) {
[self playAnyVoiceWithIndex:self.currentVoiceIndex];
} else {
[self playNext];
}
}