天天看點

iOS開發之音頻解析第三方架構介紹

最近在做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中的具體實作。