采集方式
- 音频采集可以参考上次的视频采集方式,在里面添加相应的音频采集即可,只不过可定制型不强。(视频采集)
- 使用audioUnit,可定制型强,本文主要介绍这种方式。
实现方式
详细介绍参考(iOS 音频AudioComponentDescription类型设置)
AudioComponentDescription acd;
acd.componentType = kAudioUnitType_Output;//类型输出
//acd.componentSubType = kAudioUnitSubType_VoiceProcessingIO;//
acd.componentSubType = kAudioUnitSubType_RemoteIO;//
acd.componentManufacturer = kAudioUnitManufacturer_Apple;//ios固定这么写,mac不同
acd.componentFlags = 0;
acd.componentFlagsMask = 0;
//用来描述音频组件
AudioComponent component = AudioComponentFindNext(NULL, &acd);
//用来表示特定音频组件的实例
AudioComponentInstance componetInstance;
OSStatus status = noErr;
//创建实例
status = AudioComponentInstanceNew(component, &componetInstance);
UInt32 flagOne = 1;
//打开IO,1 是麦克风,0 是扬声器
AudioUnitSetProperty(componetInstance, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &flagOne, sizeof(flagOne));
设置输出音频属性
AudioStreamBasicDescription desc = {0};
desc.mSampleRate = 44100;//采样率
desc.mFormatID = kAudioFormatLinearPCM;//类型交错存储PCM
desc.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked;
desc.mChannelsPerframe = 2;//声道数
desc.mframesPerPacket = 1;// 非压缩数据,固定填1
desc.mBitsPerChannel = 16;//每声道占的位数
desc.mBytesPerframe = desc.mBitsPerChannel / 8 * desc.mChannelsPerframe;
desc.mBytesPerPacket = desc.mBytesPerframe * desc.mframesPerPacket;
设置采集回调
AURenderCallbackStruct cb;//采集回调
cb.inputProcRefCon = (__bridge void *)(self);
cb.inputProc = handleInputBuffer;
status = AudioUnitSetProperty(componetInstance, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &desc, sizeof(desc));//设置流格式
status = AudioUnitSetProperty(componetInstance, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 1, &cb, sizeof(cb));//设置回调
初始化
AVAudioSession *session = [AVAudioSession sharedInstance];
NSError *error;
//设置采样率
[session setPreferredSampleRate:44100 error:&error];
//设置类型
[session setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker | AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers error:&error];
//
[session setActive:YES withOptions:kAudioSessionSetActiveFlag_NotifyOthersonDeactivation error:&error];
[session setActive:YES error:&error];
//创建一个线程
taskQueue = dispatch_queue_create("com.mt.audioCapture", NULL);
开始采集
- (void)startRecord{
dispatch_async(taskQueue, ^{
NSLog(@"开始录音");
//每次设置一下状态,防止别的地方被修改或者出现其他情况
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker | AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers error:nil];
AudioOutputUnitStart(self->componetInstance);
});
}
结束采集
- (void)endRecord{
dispatch_async(taskQueue, ^{
NSLog(@"停止录音");
AudioOutputUnitStop(self->componetInstance);
});
}
采集的回调,数据获取
static OSStatus handleInputBuffer(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberframes,
AudioBufferList *ioData) {
@autoreleasepool {
Test *source = (__bridge Test *)inRefCon;
if (!source) {
return -1;
}
AudioBuffer buffer;
buffer.mData = NULL;
buffer.mDataByteSize = 0;
buffer.mNumberChannels = 1;
AudioBufferList bufferList;
bufferList.mNumberBuffers = 1;
bufferList.mBuffers[0] = buffer;
static int64_t get_audio_base_timesss = 0;
Float64 currentTime = CMTimeGetSeconds(CMClockMakeHostTimeFromSystemUnits(inTimeStamp->mHostTime));
if (get_audio_base_timesss == 0) {
get_audio_base_timesss = currentTime;
}
int64_t pts = (int64_t)((currentTime - get_audio_base_timesss) * 1000);
///获取数据
OSStatus status = AudioUnitRender(source->componetInstance,
ioActionFlags,
inTimeStamp,
inBusNumber,
inNumberframes,
&bufferList);
if ((NO)) {
for (int i = 0; i < bufferList.mNumberBuffers; i++) {
AudioBuffer ab = bufferList.mBuffers[i];
memset(ab.mData, 0, ab.mDataByteSize);
}
}
if (status == noErr) {
// //音频数据
// bufferList.mBuffers[0].mData
// //音频长度
// bufferList.mBuffers[0].mDataByteSize
// //pts
// pts
// //转为NSData
// [NSData dataWithBytes:bufferList.mBuffers[0].mData length:bufferList.mBuffers[0].mDataByteSize]
}
return noErr;
}
}
Demo地址整理后奉上。
有其他不明白的,可以留言,看到就会回复。
如果喜欢,请帮忙点赞。支持转载,转载请附原文链接。