audioconverter

不同格式音频间的转换。

iOS平台提供了AudioConverter这个接口来实现转换,这个接口在内存实现转换,不需要写文件(ExtAudioFile是对文件的操作,内部使用audioconverter来转换)。

下面的例子演示从pcm转aac的实现(比如录音出来的数据保存成aac的需求)。

adts aac每一个packet都是独立可播放的包(同mp3),每一个packet带packet信息(包括packet长度、采样率、声道数等),整个文件不带文件头信息。

m4a格式:{packet-table}{audio_data}{trailer},头信息之后就是音频裸数据,不带packet信息。

audioconverter转换出来的都是音频裸数据,至于组合成adts-aac还是苹果的m4a(ipod容器),由程序决定。


typedef struct

{

    void *source;

    UInt32 sourceSize;

    UInt32 channelCount;

    AudioStreamPacketDescription *packetDescriptions;

}FillComplexInputParam;


OSStatus audioConverterComplexInputDataProc(  AudioConverterRef               inAudioConverter,

                                            UInt32*                         ioNumberDataPackets,

                                            AudioBufferList*                ioData,

                                            AudioStreamPacketDescription**  outDataPacketDescription,

                                            void*                           inUserData)

{

    FillComplexInputParam* param = (FillComplexInputParam*)inUserData;

    if (param->sourceSize <= 0) {

        *ioNumberDataPackets = 0;

        return -1;

    }

    ioData->mBuffers[0].mData = param->source;

    ioData->mBuffers[0].mNumberChannels = param->channelCount;

    ioData->mBuffers[0].mDataByteSize = param->sourceSize;

    *ioNumberDataPackets = 1;

    param->sourceSize = 0;

    param->source = NULL;

    return noErr;

}


typedef struct _tagConvertContext {

    AudioConverterRef converter;

    int samplerate;

    int channels;

}ConvertContext;


// init

void* convert_init(int sample_rate, int channel_count)

{

    AudioStreamBasicDescription sourceDes;

    memset(&sourceDes, 0, sizeof(sourceDes));

    sourceDes.mSampleRate = sample_rate;

    sourceDes.mFormatID = kAudioFormatLinearPCM;

    sourceDes.mFormatFlags = kLinearPCMFormatFlagIsPacked | kLinearPCMFormatFlagIsSignedInteger;

    sourceDes.mChannelsPerFrame = channel_count;

    sourceDes.mBitsPerChannel = 16;

    sourceDes.mBytesPerFrame = sourceDes.mBitsPerChannel/8*sourceDes.mChannelsPerFrame;

    sourceDes.mBytesPerPacket = sourceDes.mBytesPerFrame;

    sourceDes.mFramesPerPacket = 1;

    sourceDes.mReserved = 0;

    

    AudioStreamBasicDescription targetDes;

    memset(&targetDes, 0, sizeof(targetDes));

    targetDes.mFormatID = kAudioFormatMPEG4AAC;

    targetDes.mSampleRate = sample_rate;

    targetDes.mChannelsPerFrame = channel_count;

    UInt32 size = sizeof(targetDes);

    AudioFormatGetProperty(kAudioFormatProperty_FormatInfo, 0, NULL, &size, &targetDes);

    

    AudioClassDescription audioClassDes;

    memset(&audioClassDes, 0, sizeof(AudioClassDescription));

    AudioFormatGetPropertyInfo(kAudioFormatProperty_Encoders, sizeof(targetDes.mFormatID), &targetDes.mFormatID, &size);

    int encoderCount = size / sizeof(AudioClassDescription);

    AudioClassDescription descriptions[encoderCount];

    AudioFormatGetProperty(kAudioFormatProperty_Encoders, sizeof(targetDes.mFormatID), &targetDes.mFormatID, &size, descriptions);

    for (int pos = 0; pos < encoderCount; pos ++) {

        if (targetDes.mFormatID == descriptions[pos].mSubType && descriptions[pos].mManufacturer == kAppleSoftwareAudioCodecManufacturer) {

            memcpy(&audioClassDes, &descriptions[pos], sizeof(AudioClassDescription));

            break;

        }

    }

    

    ConvertContext *convertContex = malloc(sizeof(ConvertContext));

    OSStatus ret = AudioConverterNewSpecific(&sourceDes, &targetDes, 1, &audioClassDes, &convertContex->converter);

    if (ret == noErr) {

        AudioConverterRef converter = convertContex->converter;

        

        tmp = kAudioConverterQuality_High;

        AudioConverterSetProperty(converter, kAudioConverterCodecQuality, sizeof(tmp), &tmp);

        

        UInt32 bitRate = 96000;

        UInt32 size = sizeof(bitRate);

        ret = AudioConverterSetProperty(converter, kAudioConverterEncodeBitRate, size, &bitRate);

    }

    else {

        free(convertContex);

        convertContex = NULL;

    }

    

    return convertContex;

}


// converting

void convert(void* convertContext, void* srcdata, int srclen, void** outdata, int* outlen)

{

    ConvertContext* convertCxt = (ConvertContext*)convertContext;

    if (convertCxt && convertCxt->converter) {

        UInt32 theOuputBufSize = srclen;  

        UInt32 packetSize = 1;

        void *outBuffer = malloc(theOuputBufSize);

        memset(outBuffer, 0, theOuputBufSize);

        

        AudioStreamPacketDescription *outputPacketDescriptions = NULL;

        outputPacketDescriptions = (AudioStreamPacketDescription*)malloc(sizeof(AudioStreamPacketDescription) * packetSize);

        

        FillComplexInputParam userParam;

        userParam.source = srcdata;

        userParam.sourceSize = srclen;

        userParam.channelCount = convertCxt->channels;

        userParam.packetDescriptions = NULL;

        

        OSStatus ret = noErr;

        

        AudioBufferList* bufferList = malloc(sizeof(AudioBufferList));

        AudioBufferList outputBuffers = *bufferList;

        outputBuffers.mNumberBuffers = 1;

        outputBuffers.mBuffers[0].mNumberChannels = convertCxt->channels;

        outputBuffers.mBuffers[0].mData = outBuffer;

        outputBuffers.mBuffers[0].mDataByteSize = theOuputBufSize;

        ret = AudioConverterFillComplexBuffer(convertCxt->converter, audioConverterComplexInputDataProc, &userParam, &packetSize, &outputBuffers, outputPacketDescriptions);

        if (ret == noErr) {

            if (outputBuffers.mBuffers[0].mDataByteSize > 0) {

                

                NSData* rawAAC = [NSData dataWithBytes:outputBuffers.mBuffers[0].mData length:outputBuffers.mBuffers[0].mDataByteSize];

                *outdata = malloc([rawAAC length]);

                memcpy(*outdata, [rawAAC bytes], [rawAAC length]);

                *outlen = (int)[rawAAC length];

#if 1

                int headerLength = 0;

                char* packetHeader = newAdtsDataForPacketLength((int)[rawAAC length], convertCxt->samplerate, convertCxt->channels, &headerLength);

                NSData* adtsPacketHeader = [NSData dataWithBytes:packetHeader length:headerLength];

                free(packetHeader);

                NSMutableData* fullData = [NSMutableData dataWithData:adtsPacketHeader];

                [fullData appendData:rawAAC];

                

                NSFileManager *fileMgr = [NSFileManager defaultManager];

                NSString *filepath = [NSHomeDirectory() stringByAppendingFormat:@"/Documents/test%p.aac", convertCxt->converter];

                NSFileHandle *file = nil;

                if (![fileMgr fileExistsAtPath:filepath]) {

                    [fileMgr createFileAtPath:filepath contents:nil attributes:nil];

                }

                file = [NSFileHandle fileHandleForWritingAtPath:filepath];

                [file seekToEndOfFile];

                [file writeData:fullData];

                [file closeFile];

#endif

            }

        }

        

        free(outBuffer);

        if (outputPacketDescriptions) {

            free(outputPacketDescriptions);

        }

    }

}


// uninit

....


/**

 *  Add ADTS header at the beginning of each and every AAC packet.

 *  This is needed as MediaCodec encoder generates a packet of raw

 *  AAC data.

 *

 *  Note the packetLen must count in the ADTS header itself.

 *  See: http://wiki.multimedia.cx/index.php?title=ADTS

 *  Also: http://wiki.multimedia.cx/index.php?title=MPEG-4_Audio#Channel_Configurations

 **/

//- (NSData*) adtsDataForPacketLength:(NSUInteger)packetLength {

char* newAdtsDataForPacketLength(int packetLength, int samplerate, int channelCount, int* ioHeaderLen) {

    int adtsLength = 7;

    char *packet = malloc(sizeof(char) * adtsLength);

    // Variables Recycled by addADTStoPacket

    int profile = 2//AAC LC

    //39=MediaCodecInfo.CodecProfileLevel.AACObjectELD;

    int freqIdx = freqIdxForAdtsHeader(samplerate);

    int chanCfg = channelIdxForAdtsHeader(channelCount);  //MPEG-4 Audio Channel Configuration.

    NSUInteger fullLength = adtsLength + packetLength;

    // 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;

    *ioHeaderLen = adtsLength;

    return packet;

}




评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值