音視訊開發——音訊流解碼播放(五)
阿新 • • 發佈:2019-01-11
iOS音視訊開發相關文章:
在iOS中,Core Audio提供的一套軟體介面來處理音訊,支援錄音、播放、聲音效果、格式轉換、檔案流解析等。現在常用的場景是網路傳輸過來的音訊流播放,在Core Audio中,可以使用Audio Queue或者OpenAL實現。
audio queue可以錄音或播放,它的作用主要是:
·連線音訊的硬體部分
·管理記憶體
·對於壓縮的音訊格式,能使用編解碼codec
·調節錄音與播放
下面重點看看audio queue對於音訊流的處理:
圖片看上去挺複雜,但主要的步驟就兩步:
1、將音訊資料送入buffer
2、資料播放後,再給對應的buffer補充資料
這就好比運輸隊有三輛車(類比3個buffer),分別給車裝滿貨。把貨物運到目的地後車上的貨物卸下來,然後回去重新裝上貨。audio queue中的操作主要是裝載音訊的buffer,將buffer中存的資料(AudioQueueBufferRef)要一定格式組裝好,送入buffer後就能自動播放聲音。
瞭解這點,進一步理解就容易理解多了。
詳細的介紹:http://blog.csdn.net/sqc3375177/article/details/38532207
在本例中,應用從第三方的庫(tutk平臺)中得到音訊傳輸資料G711 mu-law,然後轉換成pcm格式,通過Audio Queue播放。
1、音訊資料格式轉換:G711 mu-law轉pcm
int G711Decode(char* pRawData,const unsigned char* pBuffer, int nBufferSize) { short *out_data = (short*)pRawData; int i = 0; for(; i<nBufferSize; i++) { // 參考文件:http://bbs.csdn.net/topics/360024000 // out_data[i] = (short)ulaw2linear((unsigned char)pBuffer[i]); int v = ulaw2linear((unsigned char)pBuffer[i]); out_data[i] = v < -32768 ? -32768 : v > 32767 ? 32767 : v; } return nBufferSize * 2; }
int ulaw2linear(unsigned char u_val)
{
int t;
/* Complement to obtain normal u-law value. */
u_val = ~u_val;
/*
* Extract and bias the quantization bits. Then
* shift up by the segment number and subtract out the bias.
*/
t = ((u_val & QUANT_MASK) << 3) + BIAS;
t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT;
return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS));
}
2、audio queue播放:- (void)play:(void*)pcmData length:(unsignedint)length送入資料及長度即可
#import "PCMDataPlayer.h"
//audio queue執行流程:
//1、新建AudioQueueNewOutput,設定每個buffer用完後的回撥,新建audio queue buffer(AudioQueueAllocateBuffer)
//2、外界傳來一幀音訊資料,找到閒置的一個buffer,將資料塞到buffer裡,然後enqueue到audio queue中
@implementation PCMDataPlayer
- (id)init
{
self = [super init];
if (self) {
[self reset];
}
return self;
}
- (void)dealloc
{
if (audioQueue != nil) {
AudioQueueStop(audioQueue, true);
}
audioQueue = nil;
sysnLock = nil;
NSLog(@"PCMDataPlayer dealloc...");
}
static void AudioPlayerAQInputCallback(void* inUserData, AudioQueueRef outQ, AudioQueueBufferRef outQB)
{
PCMDataPlayer* player = (__bridge PCMDataPlayer*)inUserData;
[player playerCallback:outQB];
}
- (void)reset
{
[self stop];
sysnLock = [[NSLock alloc] init];
///設定音訊引數
audioDescription.mSampleRate = 8000; //取樣率
audioDescription.mFormatID = kAudioFormatLinearPCM;
audioDescription.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
audioDescription.mChannelsPerFrame = 1; ///單聲道
audioDescription.mFramesPerPacket = 1; //每一個packet一偵資料
audioDescription.mBitsPerChannel = 16; //每個取樣點16bit量化
audioDescription.mBytesPerFrame = (audioDescription.mBitsPerChannel / 8) * audioDescription.mChannelsPerFrame;
audioDescription.mBytesPerPacket = audioDescription.mBytesPerFrame;
AudioQueueNewOutput(&audioDescription, AudioPlayerAQInputCallback, (__bridge void*)self, nil, nil, 0, &audioQueue); //使用player的內部執行緒播放
//初始化音訊緩衝區
for (int i = 0; i < QUEUE_BUFFER_SIZE; i++) {
int result = AudioQueueAllocateBuffer(audioQueue, MIN_SIZE_PER_FRAME, &audioQueueBuffers[i]); ///建立buffer區,MIN_SIZE_PER_FRAME為每一偵所需要的最小的大小,該大小應該比每次往buffer裡寫的最大的一次還大
// NSLog(@"AudioQueueAllocateBuffer i = %d,result = %d", i, result);
}
NSLog(@"PCMDataPlayer reset");
}
- (void)stop
{
if (audioQueue != nil) {
AudioQueueStop(audioQueue, true);
AudioQueueReset(audioQueue);
}
audioQueue = nil;
}
- (void)play:(void*)pcmData length:(unsigned int)length
{
if (audioQueue == nil || ![self checkBufferHasUsed]) {
[self reset];
AudioQueueStart(audioQueue, NULL);
}
[sysnLock lock];
AudioQueueBufferRef audioQueueBuffer = NULL;
while (true) {
audioQueueBuffer = [self getNotUsedBuffer];
if (audioQueueBuffer != NULL) {
break;
}
usleep(1000);
}
audioQueueBuffer->mAudioDataByteSize = length;
Byte* audiodata = (Byte*)audioQueueBuffer->mAudioData;
for (int i = 0; i < length; i++) {
audiodata[i] = ((Byte*)pcmData)[i];
}
AudioQueueEnqueueBuffer(audioQueue, audioQueueBuffer, 0, NULL);
// NSLog(@"PCMDataPlayer play dataSize:%d", length);
[sysnLock unlock];
}
- (BOOL)checkBufferHasUsed
{
for (int i = 0; i < QUEUE_BUFFER_SIZE; i++) {
if (YES == audioQueueUsed[i]) {
return YES;
}
}
// NSLog(@"PCMDataPlayer 播放中斷............");
return NO;
}
- (AudioQueueBufferRef)getNotUsedBuffer
{
for (int i = 0; i < QUEUE_BUFFER_SIZE; i++) {
if (NO == audioQueueUsed[i]) {
audioQueueUsed[i] = YES;
// NSLog(@"PCMDataPlayer play buffer index:%d", i);
return audioQueueBuffers[i];
}
}
return NULL;
}
- (void)playerCallback:(AudioQueueBufferRef)outQB
{
for (int i = 0; i < QUEUE_BUFFER_SIZE; i++) {
if (outQB == audioQueueBuffers[i]) { //現存的buffer與回撥的buffer一致,就表示沒用過
audioQueueUsed[i] = NO;
}
}
}
@end
相關程式碼:音訊解碼播放程式碼這部分是針對TUTK平臺進行開發,歡迎從事TUTK智慧攝像頭開發的小夥伴加入QQ群交流:331753091