2012-05-05 12 views
1

所以,我想對先前錄製的音頻(來自AVAsset)做一個簡單的計算,以創建一個波形可視化。我目前通過對一組樣本進行平均來實現這一點,其大小通過將音頻文件大小除以我想要的波形的分辨率來確定。在AVAssetReader中設置時間範圍導致凍結

這一切工作正常,除了一個問題....它太慢了。在3GS上運行時,處理音頻文件需要花費大約3%的時間來播放音頻文件,這會降低速度(例如,處理1小時的音頻文件需要大約2.5分鐘)。我試圖儘可能優化該方法,但它不起作用。我將發佈我用來處理文件的代碼。也許有人能夠提供幫助,但是我真正想要的是一種處理文件的方法,無需查看每一個字節。所以說,如果分辨率爲2,000,我想訪問該文件並在2000點的每個點上進行抽樣。我認爲這會更快,特別是如果文件較大。但我知道獲取原始數據的唯一方法是以線性方式訪問音頻文件。有任何想法嗎?這裏是我用來處理文件的代碼(注意,所有類變量都以'_'開頭):

所以我完全改變了這個問題。我遲到了,意識到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; 
} 

回答

1

我終於明白了原因。顯然,您可以爲AVAssetReader的timeRange指定某種「最小」持續時間。我不確定這個最小值是多少,大於1,000但小於5,000。有可能最小的變化與資產的持續時間...老實說,我不知道。相反,我將持續時間(無窮大)保持不變,只是簡單地改變了開始時間。我沒有處理整個樣本,而是隻複製一個緩衝塊,處理它,然後再尋找下一個。我仍然遇到了代碼問題,但如果我無法弄清楚,我會將其作爲另一個問題發佈。

相關問題