AudioToolbox概述
通過AudioToolbox框架摔吏,可以將短聲音注冊到system sound服務上葱峡,被注冊到system sound服務上的聲音稱之為 system sounds。它必須滿足下面幾個條件宝惰。
(1).播放的時間不能超過30秒
(2).數(shù)據(jù)必須是 PCM或者IMA4流格式
(3).必須被打包成下面三個格式之一:Core Audio Format (.caf), Waveform audio (.wav), 或者 Audio Interchange File (.aiff)
聲音文件必須放到設備的本地文件夾下面足丢。通過AudioServicesCreateSystemSoundID方法注冊這個聲音文件.
音效
AudioToolbox.framework是一套基于C語言的框架粱腻,使用它來播放音效其本質是將短音頻注冊到系統(tǒng)聲音 服務(System Sound Service)。System Sound Service是一種簡單斩跌、底層的聲音播放服務绍些,但是它本身也存在著一些限制:
音頻播放時間不能超過30s
數(shù)據(jù)必須是PCM或者IMA4格式
音頻文件必須打包成.caf、.aif耀鸦、.wav中的一種(注意這是官方文檔的說法柬批,實際測試發(fā)現(xiàn)一些.mp3也可以播放)
使用System Sound Service 播放音效的步驟如下:
調(diào)用AudioServicesCreateSystemSoundID( CFURLRef inFileURL, SystemSoundID* outSystemSoundID)函數(shù)獲得系統(tǒng)聲音ID。
如果需要監(jiān)聽播放完成操作袖订,則使用AudioServicesAddSystemSoundCompletion( SystemSoundID inSystemSoundID,CFRunLoopRef inRunLoop, CFStringRef inRunLoopMode, AudioServicesSystemSoundCompletionProc inCompletionRoutine, void* inClientData)方法注冊回調(diào)函數(shù)氮帐。
調(diào)用AudioServicesPlaySystemSound(SystemSoundID inSystemSoundID) 或者AudioServicesPlayAlertSound(SystemSoundID inSystemSoundID) 方法播放音效(后者帶有震動效果)。
用到了AudioToolbox這個音頻接口洛姑,總結下上沐,希望對需要的朋友有幫助。AudioToolbox這個庫是C的接口楞艾,偏向于底層参咙,用于在線流媒體音樂的播放,可以調(diào)用該庫的相關接口自己封裝一個在線播放器類硫眯,AudioStreamer是老外封裝的一個播放器類蕴侧,有興趣的朋友可以研究下。
其實IOS庫中有兩個可以播放在線音樂的播放器類两入,AVPlayer和MPMusicPlayerController
這兩個做簡單的播放還不錯戈盈,但是如果要做專業(yè)的音樂播放項目,功能還不夠強大谆刨,例如:邊聽邊存、斷點續(xù)傳归斤、播放事件等等都無法滿足痊夭。一下是以前做的筆記,僅供參考 :
音樂
如果播放較大的音頻或者要對音頻有精確的控制則System Sound Service可能就很難滿足實際需求了脏里,通常這種情況會選擇使用AVFoundation.framework中的AVAudioPlayer來實現(xiàn)她我。AVAudioPlayer可以看成一個播放器,它支持多種音頻格式,而且能夠進行進度番舆、音量酝碳、播放速度等控制。首先簡單看一下AVAudioPlayer常用的屬性和方法:
屬性 說明
@property(readonly, getter=isPlaying) BOOL playing 是否正在播放恨狈,只讀
@property(readonly) NSUInteger numberOfChannels 音頻聲道數(shù)疏哗,只讀
@property(readonly) NSTimeInterval duration 音頻時長
@property(readonly) NSURL *url 音頻文件路徑,只讀
@property(readonly) NSData *data 音頻數(shù)據(jù)禾怠,只讀
@property float pan 立體聲平衡返奉,如果為-1.0則完全左聲道,如果0.0則左右聲道平衡吗氏,如果為1.0則完全為右聲道
@property float volume 音量大小芽偏,范圍0-1.0
@property BOOL enableRate 是否允許改變播放速率
@property float rate 播放速率,范圍0.5-2.0弦讽,如果為1.0則正常播放污尉,如果要修改播放速率則必須設置enableRate為YES
@property NSTimeInterval currentTime 當前播放時長
@property(readonly) NSTimeInterval deviceCurrentTime 輸出設備播放音頻的時間,注意如果播放中被暫停此時間也會繼續(xù)累加
@property NSInteger numberOfLoops 循環(huán)播放次數(shù)往产,如果為0則不循環(huán)被碗,如果小于0則無限循環(huán),大于0則表示循環(huán)次數(shù)
@property(readonly) NSDictionary *settings 音頻播放設置信息捂齐,只讀
@property(getter=isMeteringEnabled) BOOL meteringEnabled 是否啟用音頻測量蛮放,默認為NO,一旦啟用音頻測量可以通過updateMeters方法更新測量值
對象方法 說明
- (instancetype)initWithContentsOfURL:(NSURL *)url error:(NSError **)outError 使用文件URL初始化播放器奠宜,注意這個URL不能是HTTP URL包颁,AVAudioPlayer不支持加載網(wǎng)絡媒體流,只能播放本地文件
- (instancetype)initWithData:(NSData *)data error:(NSError **)outError 使用NSData初始化播放器压真,注意使用此方法時必須文件格式和文件后綴一致娩嚼,否則出錯,所以相比此方法更推薦使用上述方法或- (instancetype)initWithData:(NSData *)data fileTypeHint:(NSString *)utiString error:(NSError **)outError方法進行初始化
- (BOOL)prepareToPlay; 加載音頻文件到緩沖區(qū)滴肿,注意即使在播放之前音頻文件沒有加載到緩沖區(qū)程序也會隱式調(diào)用此方法岳悟。
- (BOOL)play; 播放音頻文件
- (BOOL)playAtTime:(NSTimeInterval)time 在指定的時間開始播放音頻
- (void)pause; 暫停播放
- (void)stop; 停止播放
- (void)updateMeters 更新音頻測量值,注意如果要更新音頻測量值必須設置meteringEnabled為YES泼差,通過音頻測量值可以即時獲得音頻分貝等信息
- (float)peakPowerForChannel:(NSUInteger)channelNumber; 獲得指定聲道的分貝峰值贵少,注意如果要獲得分貝峰值必須在此之前調(diào)用updateMeters方法
- (float)averagePowerForChannel:(NSUInteger)channelNumber 獲得指定聲道的分貝平均值,注意如果要獲得分貝平均值必須在此之前調(diào)用updateMeters方法
@property(nonatomic, copy) NSArray *channelAssignments 獲得或設置播放聲道
代理方法 說明 - (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag 音頻播放完成
- (void)audioPlayerDecodeErrorDidOccur:(AVAudioPlayer *)player error:(NSError *)error 音頻解碼發(fā)生錯誤
AVAudioPlayer的使用比較簡單:
初始化AVAudioPlayer對象堆缘,此時通常指定本地文件路徑滔灶。
設置播放器屬性,例如重復次數(shù)吼肥、音量大小等录平。
調(diào)用play方法播放麻车。
下面就使用AVAudioPlayer實現(xiàn)一個簡單播放器,在這個播放器中實現(xiàn)了播放斗这、暫停动猬、顯示播放進度功能,當然例如調(diào)節(jié)音量表箭、設置循環(huán)模式赁咙、甚至是聲波圖像(通過分析音頻分貝值)等功能都可以實現(xiàn),這里就不再一一演示燃逻。界面效果如下:
音頻會話
事實上上面的播放器還存在一些問題序目,例如通常我們看到的播放器即使退出到后臺也是可以播放的,而這個播放器如果退出到后臺它會自動暫停伯襟。如果要支持后臺播放需要做下面幾件事情:
1.設置后臺運行模式:在plist文件中添加Required background modes猿涨,并且設置item 0=App plays audio or streams audio/video using AirPlay(其實可以直接通過Xcode在Project Targets-Capabilities-Background Modes中設置)
2.設置AVAudioSession的類型為AVAudioSessionCategoryPlayback并且調(diào)用setActive::方法啟動會話。 AVAudioSession *audioSession=[AVAudioSession sharedInstance]; [audioSession setCategory:AVAudioSessionCategoryPlayback error:nil]; [audioSession setActive:YES error:nil];
3.為了能夠讓應用退到后臺之后支持耳機控制姆怪,建議添加遠程控制事件(這一步不是后臺播放必須的)
前 兩步是后臺播放所必須設置的叛赚,第三步主要用于接收遠程事件,這部分內(nèi)容之前的文章中有詳細介紹稽揭,如果這一步不設置雖讓也能夠在后臺播放俺附,但是無法獲得音頻 控制權(如果在使用當前應用之前使用其他播放器播放音樂的話,此時如果按耳機播放鍵或者控制中心的播放按鈕則會播放前一個應用的音頻)溪掀,并且不能使用耳機 進行音頻控制事镣。第一步操作相信大家都很容易理解,如果應用程序要允許運行到后臺必須設置揪胃,正常情況下應用如果進入后臺會被掛起璃哟,通過該設置可以上應用程序 繼續(xù)在后臺運行。但是第二步使用的AVAudioSession有必要進行一下詳細的說明喊递。
在iOS中每個應用都有一個音頻會話随闪,這個會話 就通過AVAudioSession來表示。AVAudioSession同樣存在于AVFoundation框架中骚勘,它是單例模式設計铐伴,通過 sharedInstance進行訪問。在使用Apple設備時大家會發(fā)現(xiàn)有些應用只要打開其他音頻播放就會終止俏讹,而有些應用卻可以和其他應用同時播放当宴, 在多種音頻環(huán)境中如何去控制播放的方式就是通過音頻會話來完成的。下面是音頻會話的幾種會話模式:
會話類型
說明
是否要求輸入
是否要求輸出
是否遵從靜音鍵
AVAudioSessionCategoryAmbient
混音播放泽疆,可以與其他音頻應用同時播放
否
是
是
AVAudioSessionCategorySoloAmbient
獨占播放
否
是
是
AVAudioSessionCategoryPlayback
后臺播放即供,也是獨占的
否
是
否
AVAudioSessionCategoryRecord
錄音模式,用于錄音時使用
是
否
否
AVAudioSessionCategoryPlayAndRecord
播放和錄音于微,此時可以錄音也可以播放
是
是
否
AVAudioSessionCategoryAudioProcessing
硬件解碼音頻逗嫡,此時不能播放和錄制
否
否
否
AVAudioSessionCategoryMultiRoute
多種輸入輸出,例如可以耳機株依、USB設備同時播放
是
是
否
注意:是否遵循靜音鍵表示在播放過程中如果用戶通過硬件設置為靜音是否能關閉聲音驱证。
擴展--播放音樂庫中的音樂
眾所周知音樂是iOS的重要組成播放,無論是iPod恋腕、iTouch抹锄、iPhone還是iPad都可以在iTunes購買音樂或添加本地音樂到音樂庫中同步到你的iOS設備。在MediaPlayer.frameowork中有一個MPMusicPlayerController用于播放音樂庫中的音樂荠藤。
下面先來看一下MPMusicPlayerController的常用屬性和方法:
屬性 說明
@property (nonatomic, readonly) MPMusicPlaybackState playbackState 播放器狀態(tài)伙单,枚舉類型:
MPMusicPlaybackStateStopped:停止播放MPMusicPlaybackStatePlaying:正在播放
MPMusicPlaybackStatePaused:暫停播放
MPMusicPlaybackStateInterrupted:播放中斷
MPMusicPlaybackStateSeekingForward:向前查找
MPMusicPlaybackStateSeekingBackward:向后查找
@property (nonatomic) MPMusicRepeatMode repeatMode 重復模式,枚舉類型:
MPMusicRepeatModeDefault:默認模式哈肖,使用用戶的首選項(系統(tǒng)音樂程序設置)
MPMusicRepeatModeNone:不重復
MPMusicRepeatModeOne:單曲循環(huán)
MPMusicRepeatModeAll:在當前列表內(nèi)循環(huán)
@property (nonatomic) MPMusicShuffleMode shuffleMode 隨機播放模式吻育,枚舉類型:
MPMusicShuffleModeDefault:默認模式,使用用戶首選項(系統(tǒng)音樂程序設置)
MPMusicShuffleModeOff:不隨機播放
MPMusicShuffleModeSongs:按歌曲隨機播放
MPMusicShuffleModeAlbums:按專輯隨機播放
@property (nonatomic, copy) MPMediaItem *nowPlayingItem 正在播放的音樂項
@property (nonatomic, readonly) NSUInteger indexOfNowPlayingItem 當前正在播放的音樂在播放隊列中的索引
@property(nonatomic, readonly) BOOL isPreparedToPlay 是否準好播放準備
@property(nonatomic) NSTimeInterval currentPlaybackTime 當前已播放時間淤井,單位:秒
@property(nonatomic) float currentPlaybackRate 當前播放速度布疼,是一個播放速度倍率,0表示暫停播放币狠,1代表正常速度
類方法 說明
- (MPMusicPlayerController *)applicationMusicPlayer; 獲取應用播放器游两,注意此類播放器無法在后臺播放
- (MPMusicPlayerController *)systemMusicPlayer 獲取系統(tǒng)播放器,支持后臺播放
對象方法 說明
- (void)setQueueWithQuery:(MPMediaQuery *)query 使用媒體隊列設置播放源媒體隊列
- (void)setQueueWithItemCollection:(MPMediaItemCollection *)itemCollection 使用媒體項集合設置播放源媒體隊列
- (void)skipToNextItem 下一曲
- (void)skipToBeginning 從起始位置播放
- (void)skipToPreviousItem 上一曲
- (void)beginGeneratingPlaybackNotifications 開啟播放通知漩绵,注意不同于其他播放器贱案,MPMusicPlayerController要想獲得通知必須首先開啟,默認情況無法獲得通知
- (void)endGeneratingPlaybackNotifications 關閉播放通知
- (void)prepareToPlay 做好播放準備(加載音頻到緩沖區(qū))止吐,在使用play方法播放時如果沒有做好準備回自動調(diào)用該方法
- (void)play 開始播放
- (void)pause 暫停播放
- (void)stop 停止播放
- (void)beginSeekingForward 開始向前查找(快進)
- (void)beginSeekingBackward 開始向后查找(快退)
- (void)endSeeking 結束查找
通知 說明
(注意:要想獲得MPMusicPlayerController通知必須首先調(diào)用beginGeneratingPlaybackNotifications開啟通知)
MPMusicPlayerControllerPlaybackStateDidChangeNotification 播放狀態(tài)改變
MPMusicPlayerControllerNowPlayingItemDidChangeNotification 當前播放音頻改變
MPMusicPlayerControllerVolumeDidChangeNotification 聲音大小改變
MPMediaPlaybackIsPreparedToPlayDidChangeNotification 準備好播放
MPMusicPlayerController有兩種播放器:applicationMusicPlayer和systemMusicPlayer宝踪,前者在應用退出后音樂播放會自動停止,后者在應用停止后不會退出播放狀態(tài)祟印。
MPMusicPlayerController加載音樂不同于前面的AVAudioPlayer是通過一個文件路徑來加載肴沫,而是需要一個播放隊列。在MPMusicPlayerController中提供了兩個方法來加載播放隊列:- (void)setQueueWithQuery:(MPMediaQuery *)query和- (void)setQueueWithItemCollection:(MPMediaItemCollection *)itemCollection蕴忆,正是由于它的播放音頻來源是一個隊列颤芬,因此MPMusicPlayerController支持上一曲、下一曲等操作套鹅。
那么接下來的問題就是如何獲取MPMediaQueue或者MPMediaItemCollection站蝠?MPMediaQueue對象有一系列的類方法來獲得媒體隊列:
- (MPMediaQuery *)albumsQuery;
- (MPMediaQuery *)artistsQuery;
- (MPMediaQuery *)songsQuery;
- (MPMediaQuery *)playlistsQuery;
- (MPMediaQuery *)podcastsQuery;
- (MPMediaQuery *)audiobooksQuery;
- (MPMediaQuery *)compilationsQuery;
- (MPMediaQuery *)composersQuery;
- (MPMediaQuery *)genresQuery;
有了這些方法,就可以很容易獲到歌曲卓鹿、播放列表菱魔、專輯媒體等媒體隊列了,這樣就可以通過:- (void)setQueueWithQuery:(MPMediaQuery *)query方法設置音樂來源了吟孙。又或者得到MPMediaQueue之后創(chuàng)建MPMediaItemCollection澜倦,使用- (void)setQueueWithItemCollection:(MPMediaItemCollection *)itemCollection設置音樂來源聚蝶。
有時候可能希望用戶自己來選擇要播放的音樂,這時可以使用MPMediaPickerController藻治,它是一個視圖控制器碘勉,類似于UIImagePickerController,選擇完播放來源后可以在其代理方法中獲得MPMediaItemCollection對象桩卵。
無論是通過哪種方式獲得MPMusicPlayerController的媒體源验靡,可能都希望將每個媒體的信息顯示出來,這時候可以通過MPMediaItem對象獲得雏节。一個MPMediaItem代表一個媒體文件胜嗓,通過它可以訪問媒體標題、專輯名稱钩乍、專輯封面辞州、音樂時長等等。無論是MPMediaQueue還是MPMediaItemCollection都有一個items屬性件蚕,它是MPMediaItem數(shù)組孙技,通過這個屬性可以獲得MPMediaItem對象。
音樂的選擇排作、播放牵啦、暫停、通知妄痪、下一曲哈雏、上一曲功能,相信有了上面的概念衫生,代碼讀起來并不復雜(示例中是直接通過MPMeidaPicker進行音樂選擇的裳瘪,但是仍然提供了兩個方法getLocalMediaQuery和getLocalMediaItemCollection來演示如何直接通過MPMediaQueue獲得媒體隊列或媒體集合):
錄音
除了上面說的,在AVFoundation框架中還要一個AVAudioRecorder類專門處理錄音操作罪针,它同樣支持多種音頻格式彭羹。與AVAudioPlayer類似,你完全可以將它看成是一個錄音機控制類泪酱,下面是常用的屬性和方法:
屬性 說明
@property(readonly, getter=isRecording) BOOL recording; 是否正在錄音派殷,只讀
@property(readonly) NSURL *url 錄音文件地址,只讀
@property(readonly) NSDictionary *settings 錄音文件設置墓阀,只讀
@property(readonly) NSTimeInterval currentTime 錄音時長毡惜,只讀,注意僅僅在錄音狀態(tài)可用
@property(readonly) NSTimeInterval deviceCurrentTime 輸入設置的時間長度斯撮,只讀经伙,注意此屬性一直可訪問
@property(getter=isMeteringEnabled) BOOL meteringEnabled; 是否啟用錄音測量,如果啟用錄音測量可以獲得錄音分貝等
數(shù)據(jù)信息
@property(nonatomic, copy) NSArray *channelAssignments 當前錄音的通道
對象方法 說明
- (instancetype)initWithURL:(NSURL *)url settings:(NSDictionary *)settings error:(NSError **)outError 錄音機對象初始化方法勿锅,注意其中的url必須是本地文件url帕膜,settings是錄音格式枣氧、編碼等設置
- (BOOL)prepareToRecord 準備錄音,主要用于創(chuàng)建緩沖區(qū)泳叠,如果不手動調(diào)用作瞄,在調(diào)用record錄音時也會自動調(diào)用
- (BOOL)record 開始錄音
- (BOOL)recordAtTime:(NSTimeInterval)time 在指定的時間開始錄音,一般用于錄音暫停再恢復錄音
- (BOOL)recordForDuration:(NSTimeInterval) duration 按指定的時長開始錄音
- (BOOL)recordAtTime:(NSTimeInterval)time forDuration:(NSTimeInterval) duration 在指定的時間開始錄音危纫,并指
定錄音時長
- (void)pause; 暫停錄音
- (void)stop; 停止錄音
- (BOOL)deleteRecording; 刪除錄音,注意要刪除錄音此時錄音機必須處于停止狀態(tài)
- (void)updateMeters; 更新測量數(shù)據(jù)乌庶,注意只有meteringEnabled為YES此方法才可用
- (float)peakPowerForChannel:(NSUInteger)channelNumber; 指定通道的測量峰值种蝶,注意只有調(diào)用完updateMeters才有值
- (float)averagePowerForChannel:(NSUInteger)channelNumber 指定通道的測量平均值,注意只有調(diào)用完updateMeters才有值
代理方法 說明
- (void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag 完成錄音
- (void)audioRecorderEncodeErrorDidOccur:(AVAudioRecorder *)recorder error:(NSError *)error 錄音編碼發(fā)生錯誤
AVAudioRecorder很多屬性和方法跟AVAudioPlayer都是類似的,但是它的創(chuàng)建有所不同瞒大,在創(chuàng)建錄音機時除了指定路徑外還必須指定錄音設置信息螃征,因為錄音機必須知道錄音文件的格式、采樣率透敌、通道數(shù)盯滚、每個采樣點的位數(shù)等信息酗电,但是也并不是所有的信息都必須設置魄藕,通常只需要幾個常用設置。關于錄音設置詳見幫助文檔中的“AV Foundation Audio Settings Constants”撵术。
在這個示例中將實行一個完整的錄音控制,包括錄音嫩与、暫停寝姿、恢復、停止划滋,同時還會實時展示用戶錄音的聲音波動饵筑,當用戶點擊完停止按鈕還會自動播放錄音文件。程序的構建主要分為以下幾步:
設置音頻會話類型為AVAudioSessionCategoryPlayAndRecord处坪,因為程序中牽扯到錄音和播放操作根资。
創(chuàng)建錄音機AVAudioRecorder,指定錄音保存的路徑并且設置錄音屬性稻薇,注意對于一般的錄音文件要求的采樣率嫂冻、位數(shù)并不高,需要適當設置以保證錄音文件的大小和效果塞椎。
設置錄音機代理以便在錄音完成后播放錄音桨仿,打開錄音測量保證能夠實時獲得錄音時的聲音強度。(注意聲音強度范圍-160到0,0代表最大輸入)
創(chuàng)建音頻播放器AVAudioPlayer案狠,用于在錄音完成之后播放錄音服傍。
創(chuàng)建一個定時器以便實時刷新錄音測量值并更新錄音強度到UIProgressView中顯示钱雷。
添加錄音、暫停吹零、恢復罩抗、停止操作,需要注意錄音的恢復操作其實是有音頻會話管理的灿椅,恢復時只要再次調(diào)用record方法即可套蒂,無需手動管理恢復時間等。
音頻隊列服務
大家應該已經(jīng)注意到了茫蛹,無論是前面的錄音還是音頻播放均不支持網(wǎng)絡流媒體播放操刀,當然對于錄音來說這種需求可能不大, 但是對于音頻播放來說有時候就很有必要了婴洼。AVAudioPlayer只能播放本地文件骨坑,并且是一次性加載所以音頻數(shù)據(jù),初始化 AVAudioPlayer時指定的URL也只能是File URL而不能是HTTP URL柬采。當然欢唾,將音頻文件下載到本地然后再調(diào)用AVAudioPlayer來播放也是一種播放網(wǎng)絡音頻的辦法,但是這種方式最大的弊端就是必須等到整個音 頻播放完成才能播放粉捻,而不能使用流式播放礁遣,這往往在實際開發(fā)中是不切實際的。那么在iOS中如何播放網(wǎng)絡流媒體呢杀迹?就是使用AudioToolbox框架 中的音頻隊列服務Audio Queue Services亡脸。
使用音頻隊列服務完全可以做到音頻播放和錄制,首先看一下錄音音頻服務隊列:
一個音頻服務隊列Audio Queue有三部分組成:
三個緩沖器Buffers:每個緩沖器都是一個存儲音頻數(shù)據(jù)的臨時倉庫树酪。
一個緩沖隊列Buffer Queue:一個包含音頻緩沖器的有序隊列浅碾。
一個回調(diào)Callback:一個自定義的隊列回調(diào)函數(shù)。
聲音通過輸入設備進入緩沖隊列中续语,首先填充第一個緩沖器垂谢;當?shù)谝粋€緩沖器填充滿之后自動填充下一個緩沖器,同時會調(diào)用回調(diào)函數(shù)疮茄;在回調(diào)函數(shù)中需要將緩沖器中的音頻數(shù)據(jù)寫入磁盤滥朱,同時將緩沖器放回到緩沖隊列中以便重用。下面是Apple官方關于音頻隊列服務的流程示意圖:
類似的力试,看一下音頻播放緩沖隊列徙邻,其組成部分和錄音緩沖隊列類似。
但 是在音頻播放緩沖隊列中畸裳,回調(diào)函數(shù)調(diào)用的時機不同于音頻錄制緩沖隊列缰犁,流程剛好相反。將音頻讀取到緩沖器中,一旦一個緩沖器填充滿之后就放到緩沖隊列中帅容, 然后繼續(xù)填充其他緩沖器颇象;當開始播放時,則從第一個緩沖器中讀取音頻進行播放并徘;一旦播放完之后就會觸發(fā)回調(diào)函數(shù)遣钳,開始播放下一個緩沖器中的音頻,同時填充 第一個緩沖器放麦乞;填充滿之后再次放回到緩沖隊列蕴茴。下面是詳細的流程:
當 然,要明白音頻隊列服務的原理并不難姐直,問題是如何實現(xiàn)這個自定義的回調(diào)函數(shù)荐开,這其中我們有大量的工作要做,控制播放狀態(tài)简肴、處理異常中斷、進行音頻編碼等 等百侧。由于牽扯內(nèi)容過多砰识,而且不是本文目的,如果以后有時間將另開一篇文章重點介紹佣渴,目前有很多第三方優(yōu)秀框架可以直接使用辫狼,例如AudioStreamer、FreeStreamer辛润。由于前者當前只有非ARC版本膨处,所以下面不妨使用FreeStreamer來簡單演示在線音頻播放的過程,當然在使用之前要做如下準備工作:
1.拷貝FreeStreamer中的Reachability.h砂竖、Reachability.m和Common真椿、astreamer兩個文件夾中的內(nèi)容到項目中。
2.添加FreeStreamer使用的類庫:CFNetwork.framework乎澄、AudioToolbox.framework突硝、AVFoundation.framework
、libxml2.dylib置济、MediaPlayer.framework解恰。
3.如果引用libxml2.dylib編譯不通過,需要在Xcode的Targets-Build Settings-Header Build Path中添加$(SDKROOT)/usr/include/libxml2浙于。
4.將FreeStreamer中的FreeStreamerMobile-Prefix.pch文件添加到項目中并將Targets-Build Settings-Precompile Prefix Header設置為YES护盈,在Targets-Build Settings-Prefix Header設置為$(SRCROOT)/項目名稱/FreeStreamerMobile-Prefix.pch(因為Xcode6默認沒有pch文件)
MPMoviePlayerController
在iOS中播放視頻可以使用MediaPlayer.framework種的MPMoviePlayerController類來完成,它支持本地視頻和網(wǎng)絡視頻播放羞酗。這個類實現(xiàn)了MPMediaPlayback協(xié)議腐宋,因此具備一般的播放器控制功能,例如播放、暫停脏款、停止等围苫。但是MPMediaPlayerController自身并不是一個完整的視圖控制器,如果要在UI中展示視頻需要將view屬性添加到界面中撤师。下面列出了MPMoviePlayerController的常用屬性和方法:
屬性 說明
@property (nonatomic, copy) NSURL *contentURL 播放媒體URL剂府,這個URL可以是本地路徑,也可以是網(wǎng)絡路徑
@property (nonatomic, readonly) UIView *view 播放器視圖剃盾,如果要顯示視頻必須將此視圖添加到控制器視圖中
@property (nonatomic, readonly) UIView *backgroundView 播放器背景視圖
@property (nonatomic, readonly) MPMoviePlaybackState playbackState 媒體播放狀態(tài)腺占,枚舉類型:
MPMoviePlaybackStateStopped:停止播放
MPMoviePlaybackStatePlaying:正在播放
MPMoviePlaybackStatePaused:暫停
MPMoviePlaybackStateInterrupted:中斷
MPMoviePlaybackStateSeekingForward:向前定位
MPMoviePlaybackStateSeekingBackward:向后定位
@property (nonatomic, readonly) MPMovieLoadState loadState 網(wǎng)絡媒體加載狀態(tài),枚舉類型:
MPMovieLoadStateUnknown:位置類型
MPMovieLoadStatePlayable:
MPMovieLoadStatePlaythroughOK:這種狀態(tài)如果shouldAutoPlay為YES將自動播放
MPMovieLoadStateStalled:停滯狀態(tài)
@property (nonatomic) MPMovieControlStyle controlStyle 控制面板風格痒谴,枚舉類型:
MPMovieControlStyleNone:無控制面板
MPMovieControlStyleEmbedded:嵌入視頻風格
MPMovieControlStyleFullscreen:全屏
MPMovieControlStyleDefault:默認風格
@property (nonatomic) MPMovieRepeatMode repeatMode; 重復播放模式衰伯,枚舉類型:
MPMovieRepeatModeNone:不重復,默認值
MPMovieRepeatModeOne:重復播放
@property (nonatomic) BOOL shouldAutoplay 當網(wǎng)絡媒體緩存到一定數(shù)據(jù)時是否自動播放积蔚,默認為YES
@property (nonatomic, getter=isFullscreen) BOOL fullscreen 是否全屏展示意鲸,默認為NO,注意如果要通過此屬性設置全屏必須在視圖顯示完成后設置尽爆,否則無效
@property (nonatomic) MPMovieScalingMode scalingMode 視頻縮放填充模式怎顾,枚舉類型:
MPMovieScalingModeNone:不進行任何縮放
MPMovieScalingModeAspectFit:固定縮放比例并且盡量全部展示視頻,不會裁切視頻
MPMovieScalingModeAspectFill:固定縮放比例并填充滿整個視圖展示漱贱,可能會裁切視頻
MPMovieScalingModeFill:不固定縮放比例壓縮填充整個視圖槐雾,視頻不會被裁切但是比例失衡
@property (nonatomic, readonly) BOOL readyForDisplay 是否有相關媒體被播放
@property (nonatomic, readonly) MPMovieMediaTypeMask movieMediaTypes 媒體類別,枚舉類型:
MPMovieMediaTypeMaskNone:未知類型
MPMovieMediaTypeMaskVideo:視頻
MPMovieMediaTypeMaskAudio:音頻
@property (nonatomic) MPMovieSourceType movieSourceType 媒體源幅狮,枚舉類型:
MPMovieSourceTypeUnknown:未知來源
MPMovieSourceTypeFile:本地文件
MPMovieSourceTypeStreaming:流媒體(直播或點播)
@property (nonatomic, readonly) NSTimeInterval duration 媒體時長募强,如果未知則返回0
@property (nonatomic, readonly) NSTimeInterval playableDuration 媒體可播放時長,主要用于表示網(wǎng)絡媒體已下載視頻時長
@property (nonatomic, readonly) CGSize naturalSize 視頻實際尺寸崇摄,如果未知則返回CGSizeZero
@property (nonatomic) NSTimeInterval initialPlaybackTime 起始播放時間
@property (nonatomic) NSTimeInterval endPlaybackTime 終止播放時間
@property (nonatomic) BOOL allowsAirPlay 是否允許無線播放擎值,默認為YES
@property (nonatomic, readonly, getter=isAirPlayVideoActive) BOOL airPlayVideoActive 當前媒體是否正在通過AirPlay播放
@property(nonatomic, readonly) BOOL isPreparedToPlay 是否準備好播放
@property(nonatomic) NSTimeInterval currentPlaybackTime 當前播放時間,單位:秒
@property(nonatomic) float currentPlaybackRate 當前播放速度配猫,如果暫停則為0幅恋,正常速度為1.0,非0數(shù)據(jù)表示倍率
對象方法 說明
- (instancetype)initWithContentURL:(NSURL *)url 使用指定的URL初始化媒體播放控制器對象
- (void)setFullscreen:(BOOL)fullscreen animated:(BOOL)animated 設置視頻全屏泵肄,注意如果要通過此方法設置全屏則必須在其視圖顯示之后設置捆交,否則無效
- (void)requestThumbnailImagesAtTimes:(NSArray *)playbackTimes timeOption:(MPMovieTimeOption)option 獲取在指定播放時間的視頻縮略圖,第一個參數(shù)是獲取縮略圖的時間點數(shù)組腐巢;第二個參數(shù)代表時間點精度品追,枚舉類型:
MPMovieTimeOptionNearestKeyFrame:時間點附近
MPMovieTimeOptionExact:準確時間 - (void)cancelAllThumbnailImageRequests 取消所有縮略圖獲取請求
- (void)prepareToPlay 準備播放,加載視頻數(shù)據(jù)到緩存冯丙,當調(diào)用play方法時如果沒有準備好會自動調(diào)用此方法
- (void)play 開始播放
- (void)pause 暫停播放
- (void)stop 停止播放
- (void)beginSeekingForward 向前定位
- (void)beginSeekingBackward 向后定位
- (void)endSeeking 停止快進/快退
通知 說明
MPMoviePlayerScalingModeDidChangeNotification 視頻縮放填充模式發(fā)生改變
MPMoviePlayerPlaybackDidFinishNotification 媒體播放完成或用戶手動退出肉瓦,具體完成原因可以通過通知userInfo中的key為MPMoviePlayerPlaybackDidFinishReasonUserInfoKey的對象獲取
MPMoviePlayerPlaybackStateDidChangeNotification 播放狀態(tài)改變遭京,可配合playbakcState屬性獲取具體狀態(tài)
MPMoviePlayerLoadStateDidChangeNotification 媒體網(wǎng)絡加載狀態(tài)改變
MPMoviePlayerNowPlayingMovieDidChangeNotification 當前播放的媒體內(nèi)容發(fā)生改變
MPMoviePlayerWillEnterFullscreenNotification 將要進入全屏
MPMoviePlayerDidEnterFullscreenNotification 進入全屏后
MPMoviePlayerWillExitFullscreenNotification 將要退出全屏
MPMoviePlayerDidExitFullscreenNotification 退出全屏后
MPMoviePlayerIsAirPlayVideoActiveDidChangeNotification 當媒體開始通過AirPlay播放或者結束AirPlay播放
MPMoviePlayerReadyForDisplayDidChangeNotification 視頻顯示狀態(tài)改變
MPMovieMediaTypesAvailableNotification 確定了媒體可用類型后
MPMovieSourceTypeAvailableNotification 確定了媒體來源后
MPMovieDurationAvailableNotification 確定了媒體播放時長后
MPMovieNaturalSizeAvailableNotification 確定了媒體的實際尺寸后
MPMoviePlayerThumbnailImageRequestDidFinishNotification 縮略圖請求完成之后
MPMediaPlaybackIsPreparedToPlayDidChangeNotification 做好播放準備后
注意MPMediaPlayerController的狀態(tài)等信息并不是通過代理來和外界交互的,而是通過通知中心泞莉,因此從上面的列表中可以看到常用的一 些通知哪雕。由于MPMoviePlayerController本身對于媒體播放做了深度的封裝,使用起來就相當簡單:創(chuàng)建 MPMoviePlayerController對象鲫趁,設置frame屬性斯嚎,將MPMoviePlayerController的view添加到控制器視圖中。
擴展--使用AVFoundation生成縮略圖
通過前面的方法大家應該已經(jīng)看到挨厚,使用MPMoviePlayerController來生成縮略圖足夠簡單堡僻,但是如果僅僅是是為了生成縮略圖而不 進行視頻播放的話,此刻使用MPMoviePlayerController就有點大材小用了疫剃。其實使用AVFundation框架中的 AVAssetImageGenerator就可以獲取視頻縮略圖钉疫。使用AVAssetImageGenerator獲取縮略圖大致分為三個步驟:
創(chuàng)建AVURLAsset對象(此類主要用于獲取媒體信息,包括視頻巢价、聲音等)牲阁。
根據(jù)AVURLAsset創(chuàng)建AVAssetImageGenerator對象。
使用AVAssetImageGenerator的copyCGImageAtTime::方法獲得指定時間點的截圖壤躲。
MPMoviePlayerViewController
其實MPMoviePlayerController如果不作為嵌入視 頻來播放(例如在新聞中嵌入一個視頻)咨油,通常在播放時都是占滿一個屏幕的,特別是在iPhone柒爵、iTouch上。因此從iOS3.2以后蘋果也在思考既 然MPMoviePlayerController在使用時通常都是將其視圖view添加到另外一個視圖控制器中作為子視圖赚爵,那么何不直接創(chuàng)建一個控制器 視圖內(nèi)部創(chuàng)建一個MPMoviePlayerController屬性并且默認全屏播放棉胀,開發(fā)者在開發(fā)的時候直接使用這個視圖控制器。這個內(nèi)部有一個 MPMoviePlayerController的視圖控制器就是MPMoviePlayerViewController冀膝,它繼承于 UIViewController唁奢。MPMoviePlayerViewController內(nèi)部多了一個moviePlayer屬性和一個帶有url的 初始化方法,同時它內(nèi)部實現(xiàn)了一些作為模態(tài)視圖展示所特有的功能窝剖,例如默認是全屏模式展示麻掸、彈出后自動播放、作為模態(tài)窗口展示時如果點擊“Done”按鈕 會自動退出模態(tài)窗口等赐纱。在下面的示例中就不直接將播放器放到主視圖控制器脊奋,而是放到一個模態(tài)視圖控制器中,簡單演示 MPMoviePlayerViewController的使用疙描。
這里需要強調(diào)一下诚隙,由于MPMoviePlayerViewController的初始化方法做了大量工作(例如設置URL、自動播放起胰、添加點擊Done 完成的監(jiān)控等)久又,所以當再次點擊播放彈出新的模態(tài)窗口的時如果不銷毀之前的MPMoviePlayerViewController,那么新的對象就無法 完成初始化,這樣也就不能再次進行播放地消。
AVPlayer
MPMoviePlayerController足夠強大炉峰,幾乎不用寫幾行代碼就能完成一個播放器,但是正是由 于它的高度封裝使得要自定義這個播放器變得很復雜脉执,甚至是不可能完成疼阔。例如有些時候需要自定義播放器的樣式,那么如果要使用 MPMoviePlayerController就不合適了适瓦,如果要對視頻有自由的控制則可以使用AVPlayer竿开。AVPlayer存在于 AVFoundation中,它更加接近于底層否彩,所以靈活性也更強:
AVPlayer 本身并不能顯示視頻,而且它也不像MPMoviePlayerController有一個view屬性嗦随。如果AVPlayer要顯示必須創(chuàng)建一個播放器層 AVPlayerLayer用于展示,播放器層繼承于CALayer,有了AVPlayerLayer之添加到控制器視圖的layer中即可破镰。要使用 AVPlayer首先了解一下幾個常用的類:
AVAsset:主要用于獲取多媒體信息宇整,是一個抽象類傻工,不能直接使用泄伪。
AVURLAsset:AVAsset的子類尔苦,可以根據(jù)一個URL路徑創(chuàng)建一個包含媒體信息的AVURLAsset對象。
AVPlayerItem:一個媒體資源管理對象贬养,管理者視頻的一些基本信息和狀態(tài)咖杂,一個AVPlayerItem對應著一個視頻資源。