objective-c - 在 AVAssetReader 中设置时间范围会导致卡住

标签 objective-c ios avassetreader

所以,我正在尝试对以前录制的音频(来自 AVAsset)进行简单计算,以创建波形视觉效果。我目前通过对一组样本进行平均来做到这一点,样本的大小是通过将音频文件大小除以我想要的波形分辨率来确定的。

这一切都很好,除了一个问题......它太慢了。在 3GS 上运行,处理一个音频文件大约需要播放它所花费时间的 3%,这是很慢的方式(例如,处理一个 1 小时的音频文件大约需要 2.5 分钟)。我已尝试尽可能优化该方法,但它不起作用。我将发布我用来处理文件的代码。也许有人可以提供帮助,但我真正想要的是一种无需遍历每个字节即可处理文件的方法。因此,假设给定 2,000 的分辨率,我想访问该文件并在 2,000 个点中的每一个点进行采样。我认为这会快得多,特别是如果文件较大。但我知道获取原始数据的唯一方法是以线性方式访问音频文件。有任何想法吗?这是我用来处理文件的代码(注意,所有类变量都以“_”开头):

所以我已经完全改变了这个问题。我迟迟才意识到 AVAssetReader 有一个用于“寻找”的 timeRange 属性,这正是我正在寻找的(参见上面的原始问题)。此外,这个问题已经被问到并得到了回答(我只是之前没有找到它)并且我不想重复问题。但是,我仍然遇到问题。我的应用程序会卡住一段时间,然后在我尝试 copyNextSampleBuffer 时最终崩溃。我不确定发生了什么。我似乎没有处于任何类型的递归循环中,它只是永远不会从函数调用中返回。检查日志显示给我这个错误:

Exception Type:  00000020
Exception Codes: 0x8badf00d
Highlighted Thread:  0

Application Specific Information:
App[10570] has active assertions beyond permitted time: 
{(
    <SBProcessAssertion: 0xddd9300> identifier: Suspending process: App[10570] permittedBackgroundDuration: 10.000000 reason: suspend owner pid:52 preventSuspend  preventThrottleDownCPU  preventThrottleDownUI 
)}

我在应用程序上使用了一个时间分析器,是的,它只是坐在那里,只进行了最少的处理。无法完全弄清楚发生了什么。重要的是要注意,如果我不设置 AVAssetReader 的 timeRange 属性,则不会发生这种情况。我已经检查过,timeRange 的值是有效的,但由于某种原因设置它会导致问题。这是我的处理代码:

- (void) processSampleData{
    if (!_asset || CMTimeGetSeconds(_asset.duration) <= 0) return;
    NSError *error = nil;
    AVAssetTrack *songTrack = _asset.tracks.firstObject;
    if (!songTrack) return;
    NSDictionary *outputSettingsDict = [[NSDictionary alloc] initWithObjectsAndKeys:
                                        [NSNumber numberWithInt:kAudioFormatLinearPCM],AVFormatIDKey,
                                        [NSNumber numberWithInt:16], AVLinearPCMBitDepthKey,
                                        [NSNumber numberWithBool:NO],AVLinearPCMIsBigEndianKey,
                                        [NSNumber numberWithBool:NO],AVLinearPCMIsFloatKey,
                                        [NSNumber numberWithBool:NO],AVLinearPCMIsNonInterleaved,
                                        nil];

    UInt32 sampleRate = 44100.0; 
    _channelCount = 1;

    NSArray *formatDesc = songTrack.formatDescriptions;
    for(unsigned int i = 0; i < [formatDesc count]; ++i) {
        CMAudioFormatDescriptionRef item = (__bridge_retained CMAudioFormatDescriptionRef)[formatDesc objectAtIndex:i];
        const AudioStreamBasicDescription* fmtDesc = CMAudioFormatDescriptionGetStreamBasicDescription (item);
        if(fmtDesc ) { 
            sampleRate = fmtDesc->mSampleRate;
            _channelCount = fmtDesc->mChannelsPerFrame;
        }
        CFRelease(item);
    }

    UInt32 bytesPerSample = 2 * _channelCount; //Bytes are hard coded by AVLinearPCMBitDepthKey
    _normalizedMax = 0;
    _sampledData = [[NSMutableData alloc] init];

    SInt16 *channels[_channelCount];
    char *sampleRef;
    SInt16 *samples;
    NSInteger sampleTally = 0;
    SInt16 cTotal;
    _sampleCount = DefaultSampleSize * [UIScreen mainScreen].scale;
    NSTimeInterval intervalBetweenSamples = _asset.duration.value / _sampleCount;
    NSTimeInterval sampleSize = fmax(100, intervalBetweenSamples / _sampleCount);
    double assetTimeScale = _asset.duration.timescale;
    CMTimeRange timeRange = CMTimeRangeMake(CMTimeMake(0, assetTimeScale), CMTimeMake(sampleSize, assetTimeScale));

    SInt16 totals[_channelCount];
    @autoreleasepool {
        for (int i = 0; i < _sampleCount; i++) {
            AVAssetReader *reader = [AVAssetReader assetReaderWithAsset:_asset error:&error];
            AVAssetReaderTrackOutput *trackOutput = [AVAssetReaderTrackOutput assetReaderTrackOutputWithTrack:songTrack outputSettings:outputSettingsDict];
            [reader addOutput:trackOutput];
            reader.timeRange = timeRange;
            [reader startReading];
            while (reader.status == AVAssetReaderStatusReading) {
                CMSampleBufferRef sampleBufferRef = [trackOutput copyNextSampleBuffer];
                if (sampleBufferRef){
                    CMBlockBufferRef blockBufferRef = CMSampleBufferGetDataBuffer(sampleBufferRef);
                    size_t length = CMBlockBufferGetDataLength(blockBufferRef);
                    int sampleCount = length / bytesPerSample;
                    for (int i = 0; i < sampleCount ; i += _channelCount) {
                        CMBlockBufferAccessDataBytes(blockBufferRef, i * bytesPerSample, _channelCount, channels, &sampleRef);
                        samples = (SInt16 *)sampleRef;
                        for (int channel = 0; channel < _channelCount; channel++)
                            totals[channel] += samples[channel];
                        sampleTally++;
                    }
                    CMSampleBufferInvalidate(sampleBufferRef);
                    CFRelease(sampleBufferRef);
                }
            }
            for (int i = 0; i < _channelCount; i++){
                cTotal = abs(totals[i] / sampleTally);
                if (cTotal > _normalizedMax) _normalizedMax = cTotal;
                [_sampledData appendBytes:&cTotal length:sizeof(cTotal)];
                totals[i] = 0;
            }
            sampleTally = 0;
            timeRange.start = CMTimeMake((intervalBetweenSamples * (i + 1)) - sampleSize, assetTimeScale); //Take the sample just before the interval
        }

    }
    _assetNeedsProcessing = NO;
}

最佳答案

我终于明白为什么了。显然,您可以为 AVAssetReader 的 timeRange 指定某种“最小”持续时间。我不确定这个最小值到底是多少,高于 1,000 但低于 5,000。最小值可能会随着 Assets 的持续时间而变化……老实说我不确定。相反,我保持持续时间(无穷大)不变,只是更改了开始时间。我没有处理整个样本,而是只复制一个缓冲区 block ,处理它,然后寻找下一次。我仍然在使用代码时遇到问题,但如果我无法弄清楚,我会把它作为另一个问题发布。

关于objective-c - 在 AVAssetReader 中设置时间范围会导致卡住,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10458160/

相关文章:

objective-c - CoreMediaIO,错误更新的属性 kCMIODevicePropertyDeviceIsRunningSomewhere

iphone - NSMutableArray 似乎过早释放

ios - UITextView 链接点击识别延迟

iphone - 从区域设置特定字符串获取 NSDecimalNumber?

objective-c - ld : library not found for -lz. 1.2.3

ios - 使用 UINavigationController 作为窗口的 Root View 时如何构建应用程序

ios - 正在调用委托(delegate)方法但未正确执行

ios - 在 iOS 上以 30fps 播放 200fps 视频

ios - 如何在解码 video.mp4 时获取 iOS 中每个视频帧的时间戳

ios - 结合视频文件与 AVAssetWriter