iOS 播放pcm文件 播放udp數(shù)據(jù)包傳輸過來的pcmData

由于項目需要 自己用socket 加udp做一個小區(qū)物業(yè)和業(yè)主的視頻通話功能。我們需要采集麥克風聲音傳輸出去和播放接收到的udp傳過來的pcm原始音頻文件。因為我們這個功能很簡單,只需要通話幾秒鐘? 最多20秒。就沒有做音頻編碼和解碼的操作,直接傳輸?shù)膒cm原始數(shù)據(jù)。

但是我在網(wǎng)上找了很多播放pcm文件的代碼,很少找到即時播放傳過來的udp包的,一般都是播放本地的pcm或者解碼過來的穩(wěn)定的數(shù)據(jù)流。不能滿足我udp經(jīng)常丟包 導致播放停止的問題。

結(jié)合了很多別人的播放器和數(shù)據(jù)包的處理 自己改了一下 這個播放器能滿足我的需要了 希望能幫到有同樣需求的人。



AudioQueuePlay.h

~~~

//

//? AudioQueuePlay.h

//? udp

//

//? Created by? on 3/19/18.

//? Copyright ? 2018 . All rights reserved.

//

#import

#import

@interfaceAudioQueuePlay :NSObject

/**

?播放音頻pcm數(shù)據(jù) udp包傳過來的

?@paramdata pcm data

?*/

- (void)playWithData: (NSData*)data;

/**

?當播放不出聲音的時候 重啟一下播放器

?*/

- (void)resetPlay;

/**

?開始播放

?*/

- (void)startPlay;

/**

?停止播放

?*/

- (void)stopPlay;

@end

~~~

我們的需求是通過socket 傳輸udp語音包(pcm)和圖片實現(xiàn)視頻通話 但由于udp的不可靠性 網(wǎng)速慢的時候 播放聲音有問題 在網(wǎng)上找了好久的資料 終于找到能解決這個問題的代碼? 下面是我在網(wǎng)上看到的有意義的資料

?碼農(nóng)人生博客 比較詳細了 網(wǎng)易云音樂的大神

http://msching.github.io/blog/categories/audio/

?從這里我找到了怎么填充空數(shù)據(jù)包的方法? 贊

?https://my.oschina.net/xikan/blog/483929

?這個就是我現(xiàn)在的播放器來源 感謝? 結(jié)合第二個加空數(shù)據(jù)包的 就是我完整的例子啦

https://segmentfault.com/a/1190000010177336

~~~

#import "AudioQueuePlay.h"

#define MIN_SIZE_PER_FRAME5000//緩沖區(qū)大小? 如果一次性數(shù)據(jù)超出 會內(nèi)存溢出崩潰

#define QUEUE_BUFFER_SIZE3? ? ? //隊列緩沖個數(shù)

@interface AudioQueuePlay() {


? ? AudioQueueRefaudioQueue;? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //音頻播放隊列

? ? AudioStreamBasicDescription _audioDescription;

? ? AudioQueueBufferRefaudioQueueBuffers[QUEUE_BUFFER_SIZE];//音頻緩存

? ? BOOLaudioQueueBufferUsed[QUEUE_BUFFER_SIZE];? ? ? ? ? ? //判斷音頻緩存是否在使用

? ? NSLock*sysnLock;

? ? NSMutableData*tempData;

? ? OSStatusosState;

}

@property (nonatomic, assign) BOOL isRuning;

@end

@implementation AudioQueuePlay

- (instancetype)init

{

? ? self= [superinit];

? ? if(self) {

? ? ? ? sysnLock= [[NSLockalloc]init];

? ? ? ? // 播放PCM使用

? ? ? ? //設(shè)置音頻參數(shù) 這些參數(shù)根據(jù)傳過來的音頻參數(shù)自己設(shè)置 如果有偏差 可能會出現(xiàn)雜音或者卡頓的情況

? ? ? ? _audioDescription.mSampleRate = 22050;//采樣率

? ? ? ? _audioDescription.mFormatID = kAudioFormatLinearPCM;

? ? ? ? // 下面這個是保存音頻數(shù)據(jù)的方式的說明,如可以根據(jù)大端字節(jié)序或小端字節(jié)序,浮點數(shù)或整數(shù)以及不同體位去保存數(shù)據(jù)

? ? ? ? _audioDescription.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;

? ? ? ? //1單聲道 2雙聲道

? ? ? ? _audioDescription.mChannelsPerFrame = 1;

? ? ? ? //每一個packet一偵數(shù)據(jù),每個數(shù)據(jù)包下的楨數(shù),即每個數(shù)據(jù)包里面有多少楨

? ? ? ? _audioDescription.mFramesPerPacket = 1;

? ? ? ? //每個采樣點16bit量化 語音每采樣點占用位數(shù)

? ? ? ? _audioDescription.mBitsPerChannel = 16;

? ? ? ? _audioDescription.mBytesPerFrame = (_audioDescription.mBitsPerChannel / 8) * _audioDescription.mChannelsPerFrame;

? ? ? ? //每個數(shù)據(jù)包的bytes總數(shù),每楨的bytes數(shù)*每個數(shù)據(jù)包的楨數(shù)

? ? ? ? _audioDescription.mBytesPerPacket = _audioDescription.mBytesPerFrame * _audioDescription.mFramesPerPacket;


? ? ? ? // 使用player的內(nèi)部線程播放 新建輸出

? ? ? ? AudioQueueNewOutput(&_audioDescription,AudioPlayerAQInputCallback, (__bridgevoid*_Nullable)(self),nil,0,0, &audioQueue);


? ? ? ? // 設(shè)置音量

? ? ? ? AudioQueueSetParameter(audioQueue, kAudioQueueParam_Volume, 1.0);

? ? ? ? // 初始化需要的緩沖區(qū)

? ? ? ? for(inti =0; i

? ? ? ? ? ? audioQueueBufferUsed[i] = false;

? ? ? ? ? ? osState = AudioQueueAllocateBuffer(audioQueue, MIN_SIZE_PER_FRAME, &audioQueueBuffers[i]);

? ? ? ? }


? ? }

? ? return self;

}

//添加靜音包? 當來源音頻數(shù)據(jù)不足的時候 往里面添加靜音包

- (void)fillNullData{

? ? ? ? ? ? if(_isRuning) {

? ? ? ? ? ? ? ? BOOLisNull =YES;

? ? ? ? ? ? ? ? for(inti =0; i<3; i++) {

?? ? ? ? ? ? ? ? ? BOOLused =audioQueueBufferUsed[i];

? ? ? ? ? ? ? ? ? ? if(used) {

? ? ? ? ? ? ? ? ? ? ? ? isNull =NO;

? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? if(isNull) {

? ? ? ? ? ? ? ? ? ? //填空數(shù)據(jù)包

//? ? ? ? ? ? ? ? ? ? NSLog(@"填空數(shù)據(jù)包");

? ? ? ? ? ? ? ? ? ? NSMutableData*tmpData = [[NSMutableDataalloc]init];

? ? ? ? ? ? ? ? ? ? for(inti=0; i<600; i++) {

? ? ? ? ? ? ? ? ? ? ? ? [tmpDataappendBytes:"\x00"length:1];

? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? ? ? [selfplayWithData:tmpData];

? ? ? ? ? ? ? ? }

? ? ? ? ? ? }

}

- (void)startPlay{

? ? [self resetPlay];

}

- (void)stopPlay{

? ? _isRuning = NO;

? ? if (audioQueue) {

? ? ? ? AudioQueueStop(audioQueue,true);

? ? }

}

- (void)resetPlay {

? ? _isRuning = NO;

? ? if(audioQueue!=nil) {

? ? ? ? AudioQueueReset(audioQueue);

? ? ? ? //延遲執(zhí)行

? ? ? ? dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

? ? ? ? ? ? osState=AudioQueueStart(audioQueue,NULL);

? ? ? ? ? ? if(osState!=noErr) {

? ? ? ? ? ? ? ? NSLog(@"AudioQueueStart Error");

? ? ? ? ? ? }

? ? ? ? ? ? _isRuning=YES;

? ? ? ? });

? ? }

}

// 播放相關(guān)

-(void)playWithData:(NSData*)data {


? ? if(!_isRuning) {

? ? ? ? return;

? ? }

? ? [sysnLock lock];

? ? tempData = [NSMutableData new];

? ? [tempDataappendData:data];

? ? // 得到數(shù)據(jù)

? ? NSUInteger len = tempData.length;

? ? Byte*bytes = (Byte*)malloc(len);

? ? [tempDatagetBytes:byteslength: len];

? ? inti =0;

? ? while (true) {

? ? ? ? if (!audioQueueBufferUsed[i]) {

? ? ? ? ? ? audioQueueBufferUsed[i] = true;

? ? ? ? ? ? break;

? ? ? ? }else{

? ? ? ? ? ? i++;

? ? ? ? ? ? if(i >=QUEUE_BUFFER_SIZE) {

? ? ? ? ? ? ? ? i =0;

? ? ? ? ? ? }

? ? ? ? }

? ? }

? ? AudioQueueBufferRef buffer = audioQueueBuffers[i];

? ? buffer ->mAudioDataByteSize=? (unsignedint)len;

? ? // 把bytes的頭地址開始的len字節(jié)給mAudioData

? ? memcpy(audioQueueBuffers[i] ->mAudioData, bytes, len);

? ? free(bytes);

? ? AudioQueueEnqueueBuffer(audioQueue, audioQueueBuffers[i], 0, NULL);

? ? printf("本次播放數(shù)據(jù)大小: %lu \n", len);

? ? [sysnLock unlock];

}

// 回調(diào)回來把buffer狀態(tài)設(shè)為未使用

staticvoidAudioPlayerAQInputCallback(void* inUserData,AudioQueueRefaudioQueueRef,AudioQueueBufferRefaudioQueueBufferRef) {


? ? AudioQueuePlay* player = (__bridgeAudioQueuePlay*)inUserData;


? ? [playerresetBufferState:audioQueueRefand:audioQueueBufferRef];

//? ? NSLog(@"AudioPlayerAQInputCallback");

}

- (void)resetBufferState:(AudioQueueRef)audioQueueRef and:(AudioQueueBufferRef)audioQueueBufferRef {


? ? for(inti =0; i

? ? ? ? // 將這個buffer設(shè)為未使用

? ? ? ? if(audioQueueBufferRef ==audioQueueBuffers[i]) {

? ? ? ? ? ? audioQueueBufferUsed[i] = false;

? ? ? ? }

? ? }

? ? [self fillNullData];

}

// ************************** 內(nèi)存回收 **********************************

- (void)dealloc {


? ? if(audioQueue!=nil) {

? ? ? ? AudioQueueStop(audioQueue,true);

? ? }


? ? audioQueue = nil;

? ? sysnLock = nil;

}

@end

~~~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容