1. 程式人生 > >音視訊開發——音訊流解碼播放(五)

音視訊開發——音訊流解碼播放(五)

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