3

以前は、 を使用して完全なオーディオ ファイルからオーディオ サンプルを読み取っCMSampleBufferGetAudioBufferListWithRetainedBlockBufferていました。今、範囲を使用して同じことをしたいと思います(つまり、範囲を時間で指定します..時間ごとにオーディオの小さなチャンクを読み取り、戻って再度読み取ります)。時間範囲を使用する理由は、b/c 各読み取りのサイズを制御したい (最大サイズのパケットに収まるようにするため) ためです。

何らかの理由で、各読み取りの間に常にバンプがあります。私のコードでは、時間範囲を設定するたびに AVAssetReader を開始して終了していることに気付くでしょう。これは、リーダーの開始後に時間範囲を動的に調整できないということです (詳細については、こちらを参照してください)。

リーダーの開始と終了は、継続的なリアルタイム エクスペリエンスを生み出すにはコストが高すぎるのではないでしょうか? または、私が気付いていないこれを行う他の方法はありますか?

また、このジッターまたはラグは、時間間隔を設定した時点で発生することに注意してください..これにより、私のようにリーダーを開始および終了することは、リアルタイムのオーディオ再生にはコストがかかりすぎると思います.

- (void) setupReader 
{
    NSURL *assetURL = [NSURL URLWithString:@"ipod-library://item/item.m4a?id=1053020204400037178"];   
    songAsset = [AVURLAsset URLAssetWithURL:assetURL options:nil];

    track = [songAsset.tracks objectAtIndex:0];     
    nativeTrackASBD = [self getTrackNativeSettings:track];

    // set CM time parameters
    assetCMTime = songAsset.duration;
    CMTimeReadDurationInSeconds = CMTimeMakeWithSeconds(1, assetCMTime.timescale);
    currentCMTime = CMTimeMake(0,assetCMTime.timescale); 
}

-(void)readVBRPackets
{
    // make sure assetCMTime is greater than currentCMTime
    while (CMTimeCompare(assetCMTime,currentCMTime) == 1 )
    {
        NSError * error = nil;
        reader = [[AVAssetReader alloc] initWithAsset:songAsset error:&error];
        readerOutput = [AVAssetReaderTrackOutput assetReaderTrackOutputWithTrack:track
                                                                  outputSettings:nil];

        [reader addOutput:readerOutput];
        reader.timeRange = CMTimeRangeMake(currentCMTime, CMTimeReadDurationInSeconds);

        [reader startReading];

        while ((sample = [readerOutput copyNextSampleBuffer])) {
            CMItemCount numSamples = CMSampleBufferGetNumSamples(sample);
            if (numSamples == 0) {
                continue;
            }

            NSLog(@"reading sample");               

            CMBlockBufferRef CMBuffer = CMSampleBufferGetDataBuffer( sample );                                                         
            AudioBufferList audioBufferList;  

            OSStatus err = CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(
                                                                               sample,
                                                                               NULL,
                                                                               &audioBufferList,
                                                                               sizeof(audioBufferList),
                                                                               NULL,
                                                                               NULL,
                                                                               kCMSampleBufferFlag_AudioBufferList_Assure16ByteAlignment,
                                                                               &CMBuffer
                                                                                   );



            const AudioStreamPacketDescription   * inPacketDescriptions;
            size_t                               packetDescriptionsSizeOut;
            size_t inNumberPackets;

            CheckError(CMSampleBufferGetAudioStreamPacketDescriptionsPtr(sample, 
                                                                         &inPacketDescriptions,
                                                                         &packetDescriptionsSizeOut),
                       "could not read sample packet descriptions");

            inNumberPackets = packetDescriptionsSizeOut/sizeof(AudioStreamPacketDescription);

            AudioBuffer audioBuffer = audioBufferList.mBuffers[0];


            for (int i = 0; i < inNumberPackets; ++i)
            {

                SInt64 dataOffset = inPacketDescriptions[i].mStartOffset;
                UInt32 packetSize   = inPacketDescriptions[i].mDataByteSize;            

                size_t packetSpaceRemaining;
                packetSpaceRemaining = bufferByteSize - bytesFilled;

                // if the space remaining in the buffer is not 
                // enough for the data contained in this packet
                // then just write it
                if (packetSpaceRemaining < packetSize)
                {
                    [self enqueueBuffer];           
                }

                // copy data to the audio queue buffer
                AudioQueueBufferRef fillBuf = audioQueueBuffers[fillBufferIndex];
                memcpy((char*)fillBuf->mAudioData + bytesFilled, 
                       (const char*)(audioBuffer.mData + dataOffset), packetSize);                                                                

                // fill out packet description
                packetDescs[packetsFilled] = inPacketDescriptions[i];
                packetDescs[packetsFilled].mStartOffset = bytesFilled;

                bytesFilled += packetSize;
                packetsFilled += 1;

                // if this is the last packet, then ship it
                size_t packetsDescsRemaining = kAQMaxPacketDescs - packetsFilled;
                if (packetsDescsRemaining == 0) {          
                    [self enqueueBuffer];              
                }                  
            }

            CFRelease(CMBuffer);
            CMSampleBufferInvalidate(sample);
            CFRelease(sample);
        }

        [reader cancelReading];
        reader = NULL;
        readerOutput = NULL;

        currentCMTime = CMTimeAdd(currentCMTime, CMTimeReadDurationInSeconds);
    }


}
4

1 に答える 1

5

私は何が起こるか知っています:-D それを理解するのに丸一日近くかかりました.

実際、AVAssetReader は最初の 1024 サンプル (おそらくもう少し) をフェードインします。これが、ジッター効果が聞こえる理由です。

本当に読み取りたい位置の前に 1024 サンプルを読み取り、その 1024 サンプルをスキップすることで修正しました。

それがあなたにとってもうまくいくことを願っています。

于 2012-11-07T23:51:29.010 に答える