在iOS上将PCM(CMSampleBufferRef)编码为AAC-如何设置频率和比特率?

new9mtju  于 2023-01-03  发布在  iOS
关注(0)|答案(1)|浏览(239)

我想将PCM(从AVCaptureAudioDataOutputSampleBufferDelegate开始的CMSampleBufferRef)编码为AAC。
当第一个CMSampleBufferRef到达时,我根据文档将两个(in/out)AudioStreamBasicDescription都设置为“out

AudioStreamBasicDescription inAudioStreamBasicDescription = *CMAudioFormatDescriptionGetStreamBasicDescription((CMAudioFormatDescriptionRef)CMSampleBufferGetFormatDescription(sampleBuffer));

AudioStreamBasicDescription outAudioStreamBasicDescription = {0}; // Always initialize the fields of a new audio stream basic description structure to zero, as shown here: ...
outAudioStreamBasicDescription.mSampleRate = 44100; // The number of frames per second of the data in the stream, when the stream is played at normal speed. For compressed formats, this field indicates the number of frames per second of equivalent decompressed data. The mSampleRate field must be nonzero, except when this structure is used in a listing of supported formats (see “kAudioStreamAnyRate”).
outAudioStreamBasicDescription.mFormatID = kAudioFormatMPEG4AAC; // kAudioFormatMPEG4AAC_HE does not work. Can't find `AudioClassDescription`. `mFormatFlags` is set to 0.
outAudioStreamBasicDescription.mFormatFlags = kMPEG4Object_AAC_SSR; // Format-specific flags to specify details of the format. Set to 0 to indicate no format flags. See “Audio Data Format Identifiers” for the flags that apply to each format.
outAudioStreamBasicDescription.mBytesPerPacket = 0; // The number of bytes in a packet of audio data. To indicate variable packet size, set this field to 0. For a format that uses variable packet size, specify the size of each packet using an AudioStreamPacketDescription structure.
outAudioStreamBasicDescription.mFramesPerPacket = 1024; // The number of frames in a packet of audio data. For uncompressed audio, the value is 1. For variable bit-rate formats, the value is a larger fixed number, such as 1024 for AAC. For formats with a variable number of frames per packet, such as Ogg Vorbis, set this field to 0.
outAudioStreamBasicDescription.mBytesPerFrame = 0; // The number of bytes from the start of one frame to the start of the next frame in an audio buffer. Set this field to 0 for compressed formats. ...
outAudioStreamBasicDescription.mChannelsPerFrame = 1; // The number of channels in each frame of audio data. This value must be nonzero.
outAudioStreamBasicDescription.mBitsPerChannel = 0; // ... Set this field to 0 for compressed formats.
outAudioStreamBasicDescription.mReserved = 0; // Pads the structure out to force an even 8-byte alignment. Must be set to 0.

AudioConverterRef

AudioClassDescription audioClassDescription;
memset(&audioClassDescription, 0, sizeof(audioClassDescription));
UInt32 size;
NSAssert(AudioFormatGetPropertyInfo(kAudioFormatProperty_Encoders, sizeof(outAudioStreamBasicDescription.mFormatID), &outAudioStreamBasicDescription.mFormatID, &size) == noErr, nil);
uint32_t count = size / sizeof(AudioClassDescription);
AudioClassDescription descriptions[count];
NSAssert(AudioFormatGetProperty(kAudioFormatProperty_Encoders, sizeof(outAudioStreamBasicDescription.mFormatID), &outAudioStreamBasicDescription.mFormatID, &size, descriptions) == noErr, nil);
for (uint32_t i = 0; i < count; i++) {

    if ((outAudioStreamBasicDescription.mFormatID == descriptions[i].mSubType) && (kAppleSoftwareAudioCodecManufacturer == descriptions[i].mManufacturer)) {

        memcpy(&audioClassDescription, &descriptions[i], sizeof(audioClassDescription));

    }
}
NSAssert(audioClassDescription.mSubType == outAudioStreamBasicDescription.mFormatID && audioClassDescription.mManufacturer == kAppleSoftwareAudioCodecManufacturer, nil);
AudioConverterRef audioConverter;
memset(&audioConverter, 0, sizeof(audioConverter));
NSAssert(AudioConverterNewSpecific(&inAudioStreamBasicDescription, &outAudioStreamBasicDescription, 1, &audioClassDescription, &audioConverter) == 0, nil);

然后,我将每个CMSampleBufferRef转换为原始AAC数据。

AudioBufferList inAaudioBufferList;
CMBlockBufferRef blockBuffer;
CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(sampleBuffer, NULL, &inAaudioBufferList, sizeof(inAaudioBufferList), NULL, NULL, 0, &blockBuffer);
NSAssert(inAaudioBufferList.mNumberBuffers == 1, nil);

uint32_t bufferSize = inAaudioBufferList.mBuffers[0].mDataByteSize;
uint8_t *buffer = (uint8_t *)malloc(bufferSize);
memset(buffer, 0, bufferSize);
AudioBufferList outAudioBufferList;
outAudioBufferList.mNumberBuffers = 1;
outAudioBufferList.mBuffers[0].mNumberChannels = inAaudioBufferList.mBuffers[0].mNumberChannels;
outAudioBufferList.mBuffers[0].mDataByteSize = bufferSize;
outAudioBufferList.mBuffers[0].mData = buffer;

UInt32 ioOutputDataPacketSize = 1;

NSAssert(AudioConverterFillComplexBuffer(audioConverter, inInputDataProc, &inAaudioBufferList, &ioOutputDataPacketSize, &outAudioBufferList, NULL) == 0, nil);

NSData *data = [NSData dataWithBytes:outAudioBufferList.mBuffers[0].mData length:outAudioBufferList.mBuffers[0].mDataByteSize];

free(buffer);
CFRelease(blockBuffer);

inInputDataProc()实施:

OSStatus inInputDataProc(AudioConverterRef inAudioConverter, UInt32 *ioNumberDataPackets, AudioBufferList *ioData, AudioStreamPacketDescription **outDataPacketDescription, void *inUserData)
{
    AudioBufferList audioBufferList = *(AudioBufferList *)inUserData;

    ioData->mBuffers[0].mData = audioBufferList.mBuffers[0].mData;
    ioData->mBuffers[0].mDataByteSize = audioBufferList.mBuffers[0].mDataByteSize;

    return  noErr;
}

现在,data保存了我的原始AAC,我将其 Package 到带有适当ADTS头的ADTS帧中,这些ADTS帧的序列是可播放的AAC文档。
但是我并不像我想的那样理解这段代码。一般来说,我不理解音频......我只是在博客、论坛和文档后面写了它,花了很长时间,现在它可以工作了,但是我不知道为什么以及如何改变一些参数。所以这里是我的问题:
1.我需要在硬件编码器占用期间使用此转换器(通过AVAssetWriter)。这就是为什么我通过AudioConverterNewSpecific()而不是AudioConverterNew()制作SW转换器。但是现在设置outAudioStreamBasicDescription.mFormatID = kAudioFormatMPEG4AAC_HE;不起作用。找不到AudioClassDescription。即使mFormatFlags设置为0。使用kAudioFormatMPEG4AAC会损失什么(kMPEG4Object_AAC_SSR)超过kAudioFormatMPEG4AAC_HE?我应该使用什么进行直播?kMPEG4Object_AAC_SSR还是kMPEG4Object_AAC_Main
1.如何正确更改采样率?例如,如果我将outAudioStreamBasicDescription.mSampleRate设置为22050或8000,音频播放速度会减慢。我将ADTS头中的采样频率索引设置为与outAudioStreamBasicDescription.mSampleRate相同的频率。
1.如何改变比特率?ffmpeg -i显示了以下信息:Stream #0:0: Audio: aac, 44100 Hz, mono, fltp, 64 kb/s。例如如何将其更改为16 kbps?当我降低频率时,比特率会降低,但我相信这不是唯一的方法?而且无论如何,正如我在2中提到的,降低频率会损坏播放。
1.如何计算buffer的大小?现在我将其设置为uint32_t bufferSize = inAaudioBufferList.mBuffers[0].mDataByteSize;,因为我相信压缩格式不会比未压缩格式大...但这是不是太多了?
1.如何正确设置ioOutputDataPacketSize?如果我的文档正确,我应该设置为UInt32 ioOutputDataPacketSize = bufferSize / outAudioStreamBasicDescription.mBytesPerPacket;,但mBytesPerPacket是0。如果我设置为0,AudioConverterFillComplexBuffer()返回错误。如果我设置为1,它的工作,但我不知道为什么...
1.在inInputDataProc()中有3个“out”参数。我只设置了ioData。我是否还应该设置ioNumberDataPacketsoutDataPacketDescription?为什么以及如何设置?

iq3niunx

iq3niunx1#

在将音频传送到AAC转换器之前,您可能需要使用重新采样音频单元更改原始音频数据的采样率。否则,AAC标头和音频数据之间将不匹配。

相关问题