由于項目需要 自己用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
~~~