Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Converting an AudioBufferList to a CMSampleBuffer Produces Unexpected Results

I'm trying to convert an AudioBufferList that I get from an Audio Unit into a CMSampleBuffer that I can pass into an AVAssetWriter to save audio from the microphone. This conversion works, in that the calls I'm making to perform the transformation don't fail, but recording ultimately does fail, and I'm seeing some output in the logs that seems to be cause for concern.

The code I'm using looks like this:

- (void)handleAudioSamples:(AudioBufferList*)samples numSamples:(UInt32)numSamples hostTime:(UInt64)hostTime {
    // Create a CMSampleBufferRef from the list of samples, which we'll own
    AudioStreamBasicDescription monoStreamFormat;
    memset(&monoStreamFormat, 0, sizeof(monoStreamFormat));
    monoStreamFormat.mSampleRate = 48000;
    monoStreamFormat.mFormatID = kAudioFormatLinearPCM;
    monoStreamFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked | kAudioFormatFlagIsNonInterleaved;
    monoStreamFormat.mBytesPerPacket = 2;
    monoStreamFormat.mFramesPerPacket = 1;
    monoStreamFormat.mBytesPerFrame = 2;
    monoStreamFormat.mChannelsPerFrame = 1;
    monoStreamFormat.mBitsPerChannel = 16;

    CMFormatDescriptionRef format = NULL;
    OSStatus status = CMAudioFormatDescriptionCreate(kCFAllocatorDefault, &monoStreamFormat, 0, NULL, 0, NULL, NULL, &format);
    if (status != noErr) {
        // really shouldn't happen
        return;
    }

    CMSampleTimingInfo timing = { CMTimeMake(1, 48000), kCMTimeZero, kCMTimeInvalid };

    CMSampleBufferRef sampleBuffer = NULL;
    status = CMSampleBufferCreate(kCFAllocatorDefault, NULL, false, NULL, NULL, format, numSamples, 1, &timing, 0, NULL, &sampleBuffer);
    if (status != noErr) {
        // couldn't create the sample buffer
        PTKLogError(@"Failed to create sample buffer");
        CFRelease(format);
      return;
    }

    // add the samples to the buffer
    status = CMSampleBufferSetDataBufferFromAudioBufferList(sampleBuffer,
                                                            kCFAllocatorDefault,
                                                            kCFAllocatorDefault,
                                                            0,
                                                            samples);
    if (status != noErr) {
        PTKLogError(@"Failed to add samples to sample buffer");
        CFRelease(sampleBuffer);
        CFRelease(format);
        return;
    }

    NSLog(@"Original sample buf size: %ld for %d samples from %d buffers, first buffer has size %d", CMSampleBufferGetTotalSampleSize(sampleBuffer), numSamples, samples->mNumberBuffers, samples->mBuffers[0].mDataByteSize);
    NSLog(@"Original sample buf has %ld samples", CMSampleBufferGetNumSamples(sampleBuffer));

As I mentioned, the code doesn't seem to be failing, per se, but AVAssetWriter doesn't like it, and the CMSampleBuffer that I'm creating seems to have size 0, based on the fact that the following log entries are being logged:

2015-07-09 19:34:00.710 xxxx[1481:271334] Original sample buf size: 0 for 1024 samples from 1 buffers, first buffer has size 2048
2015-07-09 19:34:00.710 xxxx[1481:271334] Original sample buf has 1024 samples

Oddly, the sample buffer reports that it has 1024 samples, but size 0. The original AudioBufferList has 2048 bytes of data, which is what I'd expect for 1024 2-byte samples.

Am I doing something wrong in terms of the way I'm initializing and populating the CMSampleBuffer?

like image 855
Jim Wong Avatar asked Nov 09 '22 09:11

Jim Wong


1 Answers

It turns out that the fact that the sample size was coming back as 0 was a red herring. Once I cleaned up a few things--notably, I set the timestamp correctly, like so:

uint64_t timeNS = (uint64_t)(hostTime * _hostTimeToNSFactor);
CMTime presentationTime = CMTimeMake(timeNS, 1000000000);
CMSampleTimingInfo timing = { CMTimeMake(1, 48000), presentationTime, kCMTimeInvalid };

recording started working.

So, in the event that someone else is thrown off by the reportedly 0 sample buffer size, be aware that this is OK, at least in the case in which you're feeding the data into an AVAssetWriter.

like image 70
Jim Wong Avatar answered Nov 14 '22 22:11

Jim Wong