2013-10-27 56 views
3

我正在製作一個基於CoreAudio的FLAC播放器,並且遇到了AudioQueues的調皮問題。 我初始化我這樣的東西(用下劃線開頭的變量是實例變量):在44.1千赫CoreAudio AudioQueue停止問題

_flacDecoder = FLAC__stream_decoder_new(); 

    AudioStreamBasicDescription asbd = { 
     .mFormatID = kAudioFormatLinearPCM, 
     .mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked, 
     .mSampleRate = 44100, 
     .mChannelsPerFrame = 2, 
     .mBitsPerChannel = 16, 
     .mBytesPerPacket = 4, 
     .mFramesPerPacket = 1, 
     .mBytesPerFrame = 4, 
     .mReserved = 0 
    }; 

    AudioQueueNewOutput(&asbd, HandleOutputBuffer, (__bridge void *)(self), CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 0, &_audioQueue); 

    for (int i = 0; i < kNumberBuffers; ++i) { 
     AudioQueueAllocateBuffer(_audioQueue, 0x10000, &_audioQueueBuffers[i]); 
    } 

    AudioQueueSetParameter(_audioQueue, kAudioQueueParam_Volume, 1.0); 

16位立體聲PCM,非常基本的設置。 kNumberBuffers是3,每個緩衝區是0x10000字節。 我填充緩存與這些回調:

static void HandleOutputBuffer(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer){ 

    FLACPlayer * self = (__bridge FLACPlayer*)inUserData; 

    UInt32 largestBlockSizeInBytes = self->_currentStreamInfo.max_blocksize * self->_currentStreamInfo.channels * self->_currentStreamInfo.bits_per_sample/8; 

    inBuffer->mAudioDataByteSize = 0; 
    self->_buffer = inBuffer; 

    while(inBuffer->mAudioDataByteSize <= inBuffer->mAudioDataBytesCapacity - largestBlockSizeInBytes){ 
     FLAC__bool result = FLAC__stream_decoder_process_single(self->_flacDecoder); 
     assert(result); 

     if(FLAC__stream_decoder_get_state(self->_flacDecoder) == FLAC__STREAM_DECODER_END_OF_STREAM){ 
      AudioQueueStop(self->_audioQueue, false); 
      break; 
     } 
    } 

    AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, NULL); 
} 

static FLAC__StreamDecoderWriteStatus flacDecoderWriteCallback(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data){ 

    FLACPlayer * self = (__bridge FLACPlayer *)client_data; 

    assert(frame->header.bits_per_sample == 16); // TODO 

    int16_t * bufferWritePosition = (int16_t*)((uint8_t*)self->_buffer->mAudioData + self->_buffer->mAudioDataByteSize); 
    for(int i = 0; i < frame->header.blocksize; i++){ 
     for(int j = 0; j < frame->header.channels; j++){ 
      *bufferWritePosition = (int16_t)buffer[j][i]; 
      bufferWritePosition++; 
     } 
    } 

    int totalFramePayloadInBytes = frame->header.channels * frame->header.blocksize * frame->header.bits_per_sample/8; 
    self->_buffer->mAudioDataByteSize += totalFramePayloadInBytes; 

    return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; 
} 

static void flacDecoderMetadataCallback(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data){ 

    FLACPlayer * self = (__bridge FLACPlayer*) client_data; 

    if(metadata->type == FLAC__METADATA_TYPE_STREAMINFO){ 
     self->_currentStreamInfo = metadata->data.stream_info; 
    } 
} 

基本上當隊列請求一個新的緩衝區,我填的是緩衝區中的FLAC__stream_decoder,那麼我入隊了。就像其他人一樣。當libFLAC告訴我我已經到達文件末尾時,我告訴AudioQueue異步停止,直到它消耗了所有緩衝區的內容爲止。然而,播放完畢之前,播放停止了一小會兒。如果我刪除此行:

AudioQueueStop(self->_audioQueue, false); 

一切工作正常;音頻播放端到端,雖然我的隊列一直運行直到時間結束。如果我把上面一行是:

AudioQueueStop(self->_audioQueue, true); 

然後回放立即停止/同步,因爲你會從蘋果公司的文檔中獲得:

如果傳遞真實,停止立即發生(即, 同步)。如果通過false,函數將立即返回 ,但音頻隊列不會停止,直到播放隊列緩衝區 或記錄(即異步停止)。音頻隊列 根據需要調用回調,直到隊列實際停止。

我的問題是: - 我做錯了什麼? - 如何才能播放我的音頻直到結束,並正確關閉隊列?

回答

0

當然,這個東西幾個小時後掙扎,我已經找到了解決方案分鐘張貼這個問題後... 的問題是,AudioQueue不關心緩衝區調用AudioQueueStop後排隊( ...,假)。所以現在我喂這樣的隊列,一切正常般的魅力:

static void HandleOutputBuffer(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer){ 

    FLACPlayer * self = (__bridge FLACPlayer*)inUserData; 

    UInt32 largestBlockSizeInBytes = self->_currentStreamInfo.max_blocksize * self->_currentStreamInfo.channels * self->_currentStreamInfo.bits_per_sample/8; 

    inBuffer->mAudioDataByteSize = 0; 
    self->_buffer = inBuffer; 

    bool shouldStop = false; 

    while(inBuffer->mAudioDataByteSize <= inBuffer->mAudioDataBytesCapacity - largestBlockSizeInBytes){ 
     FLAC__bool result = FLAC__stream_decoder_process_single(self->_flacDecoder); 
     assert(result); 

     if(FLAC__stream_decoder_get_state(self->_flacDecoder) == FLAC__STREAM_DECODER_END_OF_STREAM){ 
      shouldStop = true; 
      break; 
     } 
    } 

    AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, NULL); 
    if(shouldStop){ 
     AudioQueueStop(self->_audioQueue, false); 
    } 
} 
+0

沒了感覺 - 你的意思是說 」 ...... AudioQueue並不關心排隊緩衝區** *後*調用AudioQueueStop ....「。 我認爲隊列停止之前入隊的每個緩衝區都將被播放。 – helioz

+0

你是對的隊友! Mea culpa –