简体   繁体   中英

Reading audio samples via AVAssetReader

How do you read audio samples via AVAssetReader? I've found examples of duplicating or mixing using AVAssetReader, but those loops are always controlled by the AVAssetWriter loop. Is it possible just to create an AVAssetReader and read through it, getting each sample and throwing the int32 of each audio sample into an array?

Thanks.

To expand on @amrox's answer, you can get an AudioBufferList from the CMBlockBufferRef, eg

CMItemCount numSamplesInBuffer = CMSampleBufferGetNumSamples(buffer);

AudioBufferList audioBufferList;

CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(
      buffer,
      NULL,
      &audioBufferList,
      sizeof(audioBufferList),
      NULL,
      NULL,
      kCMSampleBufferFlag_AudioBufferList_Assure16ByteAlignment,
      &buffer
    );

for (int bufferCount=0; bufferCount < audioBufferList.mNumberBuffers; bufferCount++) {
  SInt16* samples = (SInt16 *)audioBufferList.mBuffers[bufferCount].mData;
  for (int i=0; i < numSamplesInBuffer; i++) {
    // amplitude for the sample is samples[i], assuming you have linear pcm to start with
  }
}

//Release the buffer when done with the samples 
//(retained by CMSampleBufferGetAudioBufferListWithRetainedblockBuffer)
CFRelease(buffer); 
AVAssetReader *reader   = [[AVAssetReader alloc] initWithAsset:asset error:&error];
AVAssetTrack  *track    = [[asset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0];
NSDictionary  *settings = @{ AVFormatIDKey : [NSNumber numberWithInt:kAudioFormatLinearPCM] };

AVAssetReaderTrackOutput *readerOutput = [AVAssetReaderTrackOutput assetReaderTrackOutputWithTrack:track
                                                                                    outputSettings:settings];

[reader addOutput:readerOutput]; 
[reader startReading];

CMSampleBufferRef sample = [readerOutput copyNextSampleBuffer];

while ( sample )
{
   sample = [readerOutput copyNextSampleBuffer];

    if ( ! sample )
    {
       continue;
    }

    CMBlockBufferRef buffer = CMSampleBufferGetDataBuffer(sample);

    size_t  lengthAtOffset;
    size_t  totalLength;
    char   *data;

    if ( CMBlockBufferGetDataPointer( buffer, 0, &lengthAtOffset, &totalLength, &data ) != noErr )
    {
        NSLog(@"error!");
        break;
    }

    // do something with data...

    CFRelease(sample);
}

The answers here are not generic. The call to CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer might fail when the AudioBufferList needs to be sized differently. When having non-intererleaved samples as example.

The correct way is to call CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer twice. The first call queries the size needed for the AudioBufferList and second one actually fills the AudioBufferList .

size_t bufferSize = 0;
CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(
    sampleBuffer,
    &bufferSize,
    NULL,
    0,
    NULL,
    NULL,
    0,
    NULL
);

AudioBufferList *bufferList = malloc(bufferSize);
CMBlockBufferRef blockBuffer = NULL;
CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(
    sampleBuffer,
    NULL,
    bufferList,
    bufferSize,
    NULL,
    NULL,
    kCMSampleBufferFlag_AudioBufferList_Assure16ByteAlignment,
    &blockBuffer
);

// handle audio here

free(bufferList);
CFRelease(blockBuffer);

In a real world example you must perform error handling and also you should not malloc every frame, instead cache the AudioBufferList .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM