iOS 音频硬编码AAC

关于一些参数的含义和音频采集参考:
iOS音频AudioStreamBasicDescription设置
iOS 音频AudioComponentDescription类型设置
iOS 音频采集

创建一个编码器

- (void)createAudioConvert{
    AudioConverterRef m_converter;
    
    //输入音频的相关属性
    AudioStreamBasicDescription inputFormat = {0};
    inputFormat.mSampleRate = 44100;
    inputFormat.mFormatID = kAudioFormatLinearPCM;
    inputFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked;
    inputFormat.mChannelsPerFrame = 2;
    inputFormat.mFramesPerPacket = 1;
    inputFormat.mBitsPerChannel = 16;
    inputFormat.mBytesPerFrame = inputFormat.mBitsPerChannel / 8 * inputFormat.mChannelsPerFrame;
    inputFormat.mBytesPerPacket = inputFormat.mBytesPerFrame * inputFormat.mFramesPerPacket;
    
    // 这里开始是输出音频格式
    AudioStreamBasicDescription outputFormat = {0};
    outputFormat.mSampleRate = inputFormat.mSampleRate;       // 采样率保持一致
    outputFormat.mFormatID = kAudioFormatMPEG4AAC;            // AAC编码 kAudioFormatMPEG4AAC kAudioFormatMPEG4AAC_HE_V2
    outputFormat.mChannelsPerFrame = inputFormat.mChannelsPerFrame;// 声道数保持一致
    outputFormat.mFramesPerPacket = 1024;                     // AAC一帧是1024个字节
    
    //设置输出格式,编码方式
    const OSType subtype = kAudioFormatMPEG4AAC;
    AudioClassDescription requestedCodecs[2] = {
        {
            kAudioEncoderComponentType,
            subtype,
            kAppleSoftwareAudioCodecManufacturer
        },
        {
            kAudioEncoderComponentType,
            subtype,
            kAppleHardwareAudioCodecManufacturer
        }
    };
    
    //初始化m_converter
    OSStatus result = AudioConverterNewSpecific(&inputFormat, &outputFormat, 2, requestedCodecs, &m_converter);
    
    // AAC并不是随便的码率都可以支持。比如如果PCM采样率是44100Hz,那么码率可以设置64000bps,如果是16000,可以设置为32000bps。
    UInt32 outputBitrate = 64000;
    UInt32 propSize = sizeof(outputBitrate);
    if(result == noErr) {
        //设置码率
        result = AudioConverterSetProperty(m_converter, kAudioConverterEncodeBitRate, propSize, &outputBitrate);
    }
   
    ///到此,我们取得了编码器 m_converter
   
}

开始编码

接上篇,采集器采集的数据,在封装成NSData之后,传入编码器进行编码

///开始积累需要编码的数据
- (void)encodeAudioData:(nullable NSData*)audioData timeStamp:(uint64_t)timeStamp{
    ///当待编码数据到达一定量时候进行编码
    ///[self bufferLength] 为了方便理解这么写,其实是一个常量
    if(leftLength + audioData.length >= [self bufferLength]){
        ///发送数据去编码
        NSInteger totalSize = leftLength + audioData.length;
        NSInteger encodeCount = totalSize/[self bufferLength];
        char *totalBuf = malloc(totalSize);
        char *p = totalBuf;
        
        memset(totalBuf, (int)totalSize, 0);
        memcpy(totalBuf, leftBuf, leftLength);
        memcpy(totalBuf + leftLength, audioData.bytes, audioData.length);
        
        for(NSInteger index = 0;index < encodeCount;index++){
            [self encodeBuffer:p timeStamp:timeStamp];
            p += [self bufferLength];
        }
        leftLength = totalSize%[self bufferLength];
        memset(leftBuf, 0, [self bufferLength]);
        memcpy(leftBuf, totalBuf + (totalSize -leftLength), leftLength);
        
        free(totalBuf);
    }else{
        ///< 积累数据
        memcpy(leftBuf+leftLength, audioData.bytes, audioData.length);
        leftLength = leftLength + audioData.length;
    }
}

开始进行编码

//开始编码
- (void)encodeBuffer:(char*)buf timeStamp:(uint64_t)timeStamp{
    AudioBuffer inBuffer;
    inBuffer.mNumberChannels = 1;
    inBuffer.mData = buf;
    inBuffer.mDataByteSize = (UInt32)[self bufferLength];
    
    AudioBufferList buffers;
    buffers.mNumberBuffers = 1;
    buffers.mBuffers[0] = inBuffer;
    
    // 初始化一个输出缓冲列表
    AudioBufferList outBufferList;
    outBufferList.mNumberBuffers = 1;
    outBufferList.mBuffers[0].mNumberChannels = inBuffer.mNumberChannels;
    outBufferList.mBuffers[0].mDataByteSize = inBuffer.mDataByteSize;   // 设置缓冲区大小
    outBufferList.mBuffers[0].mData = aacBuf;           // 设置AAC缓冲区
    UInt32 outputDataPacketSize = 1;
    if (AudioConverterFillComplexBuffer(m_converter, audioInputDataProc, &buffers, &outputDataPacketSize, &outBufferList, NULL) != noErr) {
        return;
    }
    //编码后的数据
    //outBufferList.mBuffers[0].mData
    //size
    //outBufferList.mBuffers[0].mDataByteSize
    //timeStamp
    //timeStamp
    //转为NSData
    //[NSData dataWithBytes:outBufferList.mBuffers[0].mData length:outBufferList.mBuffers[0].mDataByteSize]
    //ADTS 头
    //NSData *adts = [self adtsData:2 rawDataLength:outBufferList.mBuffers[0].mDataByteSize];
    


}
OSStatus audioInputDataProc(AudioConverterRef inConverter, UInt32 *ioNumberDataPackets, AudioBufferList *ioData, AudioStreamPacketDescription * *outDataPacketDescription, void *inUserData) {
    //AudioConverterFillComplexBuffer 编码过程中,会要求这个函数来填充输入数据,也就是原始PCM数据
    AudioBufferList bufferList = *(AudioBufferList *)inUserData;
    ioData->mBuffers[0].mNumberChannels = 1;
    ioData->mBuffers[0].mData = bufferList.mBuffers[0].mData;
    ioData->mBuffers[0].mDataByteSize = bufferList.mBuffers[0].mDataByteSize;
    return noErr;
}

另外,获取到的数据默认是没有ADTS头的,如果需要添加,调用以下代码

- (NSData *)adtsData:(NSInteger)channel rawDataLength:(NSInteger)rawDataLength {
    int adtsLength = 7;
    char *packet = malloc(sizeof(char) * adtsLength);
    // Variables Recycled by addADTStoPacket
    int profile = 2;  //AAC LC
    //39=MediaCodecInfo.CodecProfileLevel.AACObjectELD;
    NSInteger freqIdx = [self sampleRateIndex:self.configuration.audioSampleRate];  //44.1KHz
    int chanCfg = (int)channel;  //MPEG-4 Audio Channel Configuration. 1 Channel front-center
    NSUInteger fullLength = adtsLength + rawDataLength;
    // fill in ADTS data
    packet[0] = (char)0xFF;     // 11111111     = syncword
    packet[1] = (char)0xF9;     // 1111 1 00 1  = syncword MPEG-2 Layer CRC
    packet[2] = (char)(((profile-1)<<6) + (freqIdx<<2) +(chanCfg>>2));
    packet[3] = (char)(((chanCfg&3)<<6) + (fullLength>>11));
    packet[4] = (char)((fullLength&0x7FF) >> 3);
    packet[5] = (char)(((fullLength&7)<<5) + 0x1F);
    packet[6] = (char)0xFC;
    NSData *data = [NSData dataWithBytesNoCopy:packet length:adtsLength freeWhenDone:YES];
    return data;
}

- (NSInteger)sampleRateIndex:(NSInteger)frequencyInHz {
    NSInteger sampleRateIndex = 0;
    switch (frequencyInHz) {
        case 96000:
            sampleRateIndex = 0;
            break;
        case 88200:
            sampleRateIndex = 1;
            break;
        case 64000:
            sampleRateIndex = 2;
            break;
        case 48000:
            sampleRateIndex = 3;
            break;
        case 44100:
            sampleRateIndex = 4;
            break;
        case 32000:
            sampleRateIndex = 5;
            break;
        case 24000:
            sampleRateIndex = 6;
            break;
        case 22050:
            sampleRateIndex = 7;
            break;
        case 16000:
            sampleRateIndex = 8;
            break;
        case 12000:
            sampleRateIndex = 9;
            break;
        case 11025:
            sampleRateIndex = 10;
            break;
        case 8000:
            sampleRateIndex = 11;
            break;
        case 7350:
            sampleRateIndex = 12;
            break;
        default:
            sampleRateIndex = 15;
    }
    return sampleRateIndex;
}

其他参数:

{
    char *leftBuf;
    char *aacBuf;
    NSInteger leftLength;
    AudioConverterRef m_converter;
}
- (instancetype)init{
    if (self = [super init]) {
        /*leftBuf aacBuf销毁的时候需要释放 free(leftBuf) free(aacBuf)*/
        if (!leftBuf) {
            leftBuf = malloc([self bufferLength]);
        }
        if (!aacBuf) {
            aacBuf = malloc([self bufferLength]);
        }
    
        leftLength = 0;
    }
    return self;
}
- (NSUInteger)bufferLength{
    /* 1024 * 2 * 声道数 */
    return 1024 * 2 * 2;
}

Demo地址整理后奉上。
有其他不明白的,可以留言,看到就会回复。
如果喜欢,请帮忙点赞。支持转载,转载请附原文链接。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容