最近在做iOS音頻相關的App,在做之前選擇了三種解決方案。第一種方案是使用蘋果自帶的音頻解析類AVPlayer,雖然AVPlayer也可以播放音頻。但是要做類似于QQ音樂這樣的App,使用AVPlayer就顯得無能為力了。第二種解決方案使用第三方音頻解析架構AudioStreamer,這是一個老外寫的音頻解析架構。其中包括本地和網絡的音頻資料解析。核心檔案
下面根據我自己的經驗說一下這個架構在項目中該如何使用。也許我們并非一開始就去研究架構源碼。這樣是不明智的,就這個架構來說,它涉及到了CoreAudio的知識。首先你要非常了解蘋果的音頻隊列服務,對CFNetwork也要很了解。
一般來說我們使用第三方架構都會看.h中給我們所提供的接口。能滿足需求就無需修改,不能滿足則在進行擴充。下面我們一起看看AudioPlayer.h中給我們提供了哪些接口。
AudioPlayer.h
1.緩沖設定
緩沖資料的大小設定 此處設定的是2M,在iPhone4上測試會發出記憶體溢出的警告, 故這個值不宜設定的太大,一般是1 * 1024 或者 2 * 1024。設定這個緩沖大小,可以實作音頻的斷點續傳的功能。
#define AudioPlayerDefaultNumberOfAudioQueueBuffers (2 * 1024)
2.播放網絡狀态
typedef enum
{
AudioPlayerInternalStateInitialised = 0,
AudioPlayerInternalStateRunning = 1,
AudioPlayerInternalStatePlaying = (1 << 1) | AudioPlayerInternalStateRunning, // 資料加載成功,進入音頻播放狀态
AudioPlayerInternalStateStartingThread = (1 << 2) | AudioPlayerInternalStateRunning, // 開始進行音頻的播放
AudioPlayerInternalStateWaitingForData = (1 << 3) | AudioPlayerInternalStateRunning, // 等待資料的加載,緩沖資料中
AudioPlayerInternalStateWaitingForQueueToStart = (1 << 4) | AudioPlayerInternalStateRunning, // 等待音頻隊列開始播放
AudioPlayerInternalStatePaused = (1 << 5) | AudioPlayerInternalStateRunning, // 暫停音頻的播放,資料停止加載
AudioPlayerInternalStateRebuffering = (1 << 6) | AudioPlayerInternalStateRunning, // 開始資料的重新加載
AudioPlayerInternalStateStopping = (1 << 7), // 一個音頻資料加載完畢,停止播放。
AudioPlayerInternalStateStopped = (1 << 8), // 資料加載完成,播放狀态為停止狀态
AudioPlayerInternalStateDisposed = (1 << 9), // 資料加載失敗,一般是無效資料
AudioPlayerInternalStateError = (1 << 10) // 資料加載出錯,一般是網絡錯誤。
}
AudioPlayerInternalState;
3. 播放器的播放狀态
AudioPlayerStateReady, // 播放器已經準備好
AudioPlayerStateRunning = 1, // 播放器正在運作中
AudioPlayerStatePlaying = (1 << 1) | AudioPlayerStateRunning, // 播放器開始播放音頻
AudioPlayerStatePaused = (1 << 2) | AudioPlayerStateRunning,
AudioPlayerStateStopped = (1 << 3), // 暫停播放和停止播放
AudioPlayerStateError = (1 << 4), // 播放音頻錯誤,一般是網絡資料錯誤
AudioPlayerStateDisposed = (1 << 5) // 播放錯誤,一般是由無效資料導緻的錯誤
AudioPlayerState;
4. 監聽播放器播放停止的原因
AudioPlayerStopReasonNoStop = 0, // 未知原因停止
AudioPlayerStopReasonEof, // 播放到檔案末尾導緻的停止
AudioPlayerStopReasonUserAction,// 使用者操作導緻的停止
AudioPlayerStopReasonUserActionFlushStop // 使用者重新整理播放導緻的停止
AudioPlayerStopReason;
5. 播放狀态碼
AudioPlayerErrorNone = 0, // 一般錯誤
AudioPlayerErrorDataSource, // 資料錯誤
AudioPlayerErrorStreamParseBytesFailed, // 解析資料流失敗
AudioPlayerErrorDataNotFound, // 資料沒有找到
AudioPlayerErrorQueueStartFailed, // 音頻隊列準備失敗
AudioPlayerErrorQueuePauseFailed, // 音頻隊列暫停失敗
AudioPlayerErrorUnknownBuffer, // 不知名的資料緩沖
AudioPlayerErrorQueueStopFailed, // 音頻隊列停止失敗
AudioPlayerErrorOther // 其他錯誤
AudioPlayerErrorCode;
@class AudioPlayer;
// 播放代理
@protocol AudioPlayerDelegate <NSObject>
// 播放狀态f發生變化的調用
-(void) audioPlayer:(AudioPlayer*)audioPlayer stateChanged:(AudioPlayerState)state;
// 播放出錯的調用
-(void) audioPlayer:(AudioPlayer*)audioPlayer didEncounterError:(AudioPlayerErrorCode)errorCode;
// 開始播放 得到itemId
-(void) audioPlayer:(AudioPlayer*)audioPlayer didStartPlayingQueueItemId:(NSObject*)queueItemId;
// 緩沖完成的調用
-(void) audioPlayer:(AudioPlayer*)audioPlayer didFinishBufferingSourceWithQueueItemId:(NSObject*)queueItemId;
// 已經播放結束的回調
-(void) audioPlayer:(AudioPlayer*)audioPlayer didFinishPlayingQueueItemId:(NSObject*)queueItemId withReason:(AudioPlayerStopReason)stopReason andProgress:(double)progress
andDuration:(double)duration;
@optional
-(void) audioPlayer:(AudioPlayer*)audioPlayer logInfo:(NSString*)line;
// 播放網絡變化的回調
-(void) audioPlayer:(AudioPlayer*)audioPlayer internalStateChanged:(AudioPlayerInternalState)state;
// 取消播放
-(void) audioPlayer:(AudioPlayer*)audioPlayer didCancelQueuedItems:(NSArray*)queuedItems;
@end
@class QueueEntry;
typedef struct
AudioQueueBufferRef ref; // 音頻隊列緩沖引用
int bufferIndex;
AudioQueueBufferRefLookupEntry;
@interface AudioPlayer : NSObject<DataSourceDelegate>
@private
UInt8* readBuffer;
int readBufferSize;
NSOperationQueue* fastApiQueue;
QueueEntry* currentlyPlayingEntry;
QueueEntry* currentlyReadingEntry;
NSMutableArray* upcomingQueue;
NSMutableArray* bufferingQueue;
AudioQueueBufferRef* audioQueueBuffer;
AudioQueueBufferRefLookupEntry* audioQueueBufferLookup;
unsigned int audioQueueBufferRefLookupCount;
unsigned int audioQueueBufferCount;
AudioStreamPacketDescription* packetDescs;
bool* bufferUsed;
int numberOfBuffersUsed;
AudioQueueRef audioQueue;
AudioStreamBasicDescription currentAudioStreamBasicDescription;
NSThread* playbackThread;
NSRunLoop* playbackThreadRunLoop;
NSConditionLock* threadFinishedCondLock;
AudioFileStreamID audioFileStream;
BOOL discontinuous;
int bytesFilled;
int packetsFilled;
int fillBufferIndex;
UIBackgroundTaskIdentifier backgroundTaskId;
AudioPlayerErrorCode errorCode;
AudioPlayerStopReason stopReason;
int currentlyPlayingLock;
pthread_mutex_t playerMutex;
pthread_mutex_t queueBuffersMutex;
pthread_cond_t queueBufferReadyCondition;
volatile BOOL waiting;
volatile BOOL disposeWasRequested;
volatile BOOL seekToTimeWasRequested;
volatile BOOL newFileToPlay;
volatile double requestedSeekTime;
volatile BOOL audioQueueFlushing;
volatile SInt64 audioPacketsReadCount;
volatile SInt64 audioPacketsPlayedCount;
BOOL meteringEnabled;
AudioQueueLevelMeterState* levelMeterState;
NSInteger numberOfChannels;
// 音頻的總時間 這個時間是由播放器自己計算出來的。
@property (readonly) double duration;
// 音頻目前播放的進度
@property (readonly) double progress;
// 播放器的狀态
@property (readwrite) AudioPlayerState state;
// 停止播放時的原因
@property (readonly) AudioPlayerStopReason stopReason;
@property (readwrite, unsafe_unretained) id<AudioPlayerDelegate> delegate;
@property (readwrite) BOOL meteringEnabled;
// 對外接口
-(id) init;
-(id) initWithNumberOfAudioQueueBuffers:(int)numberOfAudioQueueBuffers andReadBufferSize:(int)readBufferSizeIn;
// 從URL位址擷取資料源
-(DataSource*) dataSourceFromURL:(NSURL*)url;
// 播放指定URL的資源
-(void) play:(NSURL*)url;
-(void) queueDataSource:(DataSource*)dataSource withQueueItemId:(NSObject*)queueItemId;
// 設定資料源
-(void) setDataSource:(DataSource*)dataSourceIn withQueueItemId:(NSObject*)queueItemId;
// 設定播放進度
-(void) seekToTime:(double)value;
// 暫停播放
-(void) pause;
// 喚醒播放
-(void) resume;
// 停止播放
-(void) stop;
// 重新整理停止
-(void) flushStop;
-(void) mute;
-(void) unmute;
-(void) dispose;
// 得到目前播放id
-(NSObject*) currentlyPlayingQueueItemId;
// 重新整理
-(void) updateMeters;
-(float) peakPowerInDecibelsForChannel:(NSUInteger)channelNumber;
-(float) averagePowerInDecibelsForChannel:(NSUInteger)channelNumber;
今天先寫到這,以後有時間可以研究一下AudioPlayer.m中的具體實作。