objective-c - 使用 AVAssetReader 和 timeRange 实时读取样本

标签 objective-c ios core-audio

以前我read使用 CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer 从完整音频文件中提取音频样本。现在我想使用范围做同样的事情(即我及时指定范围.. 根据时间读取一小段音频,然后返回并再次读取)。我想使用时间范围的原因是 b/c 我想控制每次读取的大小(以适应最大大小的数据包)。

出于某种原因,每次读取之间总是有一个碰撞。在我的代码中,您会注意到我每次设置时间范围时都会启动 AVAssetReader 并结束它,这是 b/c 在读取器启动后我无法动态调整时间范围(有关更多详细信息,请参见 here)。

难道开始和结束读者的成本太高,无法产生连续的实时体验吗?或者还有其他我不知道的方法吗?

另请注意,这种抖动或滞后会发生在我将时间间隔设置为任何时间点......这让我相信以我现在的方式开始和结束阅读器对于实时音频播放来说太昂贵了。

- (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);
    }


}

最佳答案

我知道会发生什么:-D 我花了将近一整天的时间才弄明白。

事实上,AVAssetReader 会淡入前 1024 个样本(可能更多)。这就是为什么您会听到抖动效果。

我通过读取我真正想要读取的位置之前的 1024 个样本来修复它,然后跳过那 1024 个样本。

我希望它也对你有用。

关于objective-c - 使用 AVAssetReader 和 timeRange 实时读取样本,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13210684/

相关文章:

objective-c - 从 fwrite 参数获取数据数组的内容 - 使用 Objective-C 打印数据

android - 将蓝牙设备与React-Native配对

ios - 如何保证进程在 iOS 中的准确时间启动

ios - 使用 AudioKit 模拟动态播放头(scratching/tunrtablism)

iphone - 修改Auritouch示例代码以从音频文件读取数据

ios - SQLite iOS 警告 : Comparison of constant 101 with expression of type BOOL is always false

objective-c - UITableView 的 indexPathForCell : returns always 0 as indexPath. 行

objective-c - 为什么 CGLayers 不混合?

ios - 如何隐藏具有纵横比自动布局约束的 UIImageView?

iphone - CFReadStream - 在后台启动