范文健康探索娱乐情感热点
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

iOS实时音频采集与播放

  1、前言
  在iOS中有很多方法可以进行音视频采集。如 AVCaptureDevice, AudioQueue以及Audio Unit。其中 Audio Unit是最底层的接口,它的优点是功能强大,延迟低; 而缺点是学习成本高,难度大。对于一般的iOS应用程序,AVCaptureDevice和AudioQueue完全够用了。但对于音视频直播,最好还是使用 Audio Unit 进行处理,这样可以达到最佳的效果,著名的 WebRTC 就使用的 Audio Unit 做的音频采集与播放。今天我们就重点介绍一下Audio Unit的基本知识和使用。
  下图是 Audio Unit在 iOS架构中所处的位置:
  2、基本概念
  在介绍 Audio Unit 如何使用之前,先要介绍一下Audio Unit的基本概念,这样更有利于我们理解对它的使用。 Audio Unit的种类 Audio Units共可分为四大类,並可细分为七种,可参考下表: Audo Unit 的内部结构 参考下图,Audio Unit 内部结构分为两大部分,Scope 与Element。其中 scope 又分三种,分別是 input scope, output scope, global scope。而 element 则是 input scope 或 output scope 內的一部分。
  Audio Unit 的输入与输出 下图是一个 I/O type 的 Audio Unit,其输入为麦克风,其输出为喇叭。这是一个最简单的Audio Unit使用范例。 The input element is element 1 (mnemonic device: the letter "I" of the word "Input" has an appearance similar to the number 1) The output element is element 0 (mnemonic device: the letter "O" of the word "Output" has an appearance similar to the number 0) 3、使用流程概要描述音频元件 kAudioUnitType_Output kAudioUnitSubType_RemoteIO kAudioUnitManufacturerApple 使用 AudioComponentFindNext(NULL,  &descriptionOfAudioComponent) 获得 AudioComponent。 AudioComponent有点像生产 Audio Unit 的工厂。 使用 AudioComponentInstanceNew(ourComponent, &audioUnit) 获得 Audio Unit 实例。 使用 AudioUnitSetProperty函数为录制和回放开启IO。 使用 AudioStreamBasicDescription 结构体描述音频格式,并使用AudioUnitSetProperty进行设置。 使用 AudioUnitSetProperty 设置音频录制与放播的回调函数。 分配缓冲区。 初始化 Audio Unit。 启动 Audio Unit。
  C++音视频学习资料免费获取方法:关注音视频开发T哥  ,点击「链接」即可免费获取2023年最新 C++音视频开发进阶独家免费学习大礼包! 4、初始化
  初始化看起来像下面这样。我们有一个 AudioComponentInstance 类型的成员变量,它用于存储 Audio Unit。
  下面的音频格式用16位表式一个采样。 #define kOutputBus 0#define kInputBus 1// ...OSStatus status; AudioComponentInstance audioUnit;// 描述音频元件AudioComponentDescription desc; desc.componentType = kAudioUnitType_Output; desc.componentSubType = kAudioUnitSubType_RemoteIO; desc.componentFlags = 0; desc.componentFlagsMask = 0; desc.componentManufacturer = kAudioUnitManufacturer_Apple;// 获得一个元件AudioComponent inputComponent = AudioComponentFindNext(NULL, &desc);// 获得 Audio Unitstatus = AudioComponentInstanceNew(inputComponent, &audioUnit); checkStatus(status);// 为录制打开 IOUInt32 flag = 1; status = AudioUnitSetProperty(audioUnit,                                kAudioOutputUnitProperty_EnableIO,                                kAudioUnitScope_Input,                                kInputBus,                               &flag,                                sizeof(flag)); checkStatus(status);// 为播放打开 IOstatus = AudioUnitSetProperty(audioUnit,                                kAudioOutputUnitProperty_EnableIO,                                kAudioUnitScope_Output,                                kOutputBus,                               &flag,                                sizeof(flag)); checkStatus(status);// 描述格式audioFormat.mSampleRate         = 44100.00; audioFormat.mFormatID           = kAudioFormatLinearPCM; audioFormat.mFormatFlags        = kAudioFormatFlagIsSignedInteger |                                   kAudioFormatFlagIsPacked; audioFormat.mFramesPerPacket    = 1; audioFormat.mChannelsPerFrame   = 1; audioFormat.mBitsPerChannel = 16; audioFormat.mBytesPerPacket = 2; audioFormat.mBytesPerFrame      = 2;// 设置格式status = AudioUnitSetProperty(audioUnit,                                kAudioUnitProperty_StreamFormat,                                kAudioUnitScope_Output,                                kInputBus,                                &audioFormat,                                sizeof(audioFormat)); checkStatus(status); status = AudioUnitSetProperty(audioUnit,                                kAudioUnitProperty_StreamFormat,                                kAudioUnitScope_Input,                                kOutputBus,                                &audioFormat,                                sizeof(audioFormat)); checkStatus(status);// 设置数据采集回调函数AURenderCallbackStruct callbackStruct; callbackStruct.inputProc = recordingCallback; callbackStruct.inputProcRefCon = self; status = AudioUnitSetProperty(audioUnit,                                kAudioOutputUnitProperty_SetInputCallback,                                kAudioUnitScope_Global,                                kInputBus,                                &callbackStruct,                                sizeof(callbackStruct)); checkStatus(status);// 设置声音输出回调函数。当speaker需要数据时就会调用回调函数去获取数据。// 它是 "拉" 数据的概念。callbackStruct.inputProc = playbackCallback; callbackStruct.inputProcRefCon = self; status = AudioUnitSetProperty(audioUnit,                                kAudioUnitProperty_SetRenderCallback,                                kAudioUnitScope_Global,                                kOutputBus,                               &callbackStruct,                                sizeof(callbackStruct)); checkStatus(status);// 关闭为录制分配的缓冲区(我们想使用我们自己分配的)flag = 0; status = AudioUnitSetProperty(audioUnit,                              kAudioUnitProperty_ShouldAllocateBuffer,                             kAudioUnitScope_Output,                              kInputBus,                             &flag,                              sizeof(flag));// 初始化status = AudioUnitInitialize(audioUnit); checkStatus(status);
  开启 Audio Unit OSStatus status = AudioOutputUnitStart(audioUnit); checkStatus(status);
  关闭 Audio Unit OSStatus status = AudioOutputUnitStop(audioUnit); checkStatus(status);
  结束 Audio Unit AudioComponentInstanceDispose(audioUnit);5、录制回调static OSStatus recordingCallback(void *inRefCon,                                    AudioUnitRenderActionFlags *ioActionFlags,                                    const AudioTimeStamp *inTimeStamp,                                    UInt32 inBusNumber,                                    UInt32 inNumberFrames,                                    AudioBufferList *ioData) {    // TODO:     // 使用 inNumberFrames 计算有多少数据是有效的     // 在 AudioBufferList 里存放着更多的有效空间      AudioBufferList *bufferList; //bufferList里存放着一堆 buffers,                                  //buffers的长度是动态的。        // 获得录制的采样数据      OSStatus status;      status = AudioUnitRender([audioInterface audioUnit],                               ioActionFlags,                               inTimeStamp,                               inBusNumber,                               inNumberFrames,                               bufferList);     checkStatus(status);    // 现在,我们想要的采样数据已经在                            // bufferList中的buffers中了。     DoStuffWithTheRecordedAudio(bufferList);    return noErr; }6、播放回调static OSStatus playbackCallback(void *inRefCon,                              AudioUnitRenderActionFlags *ioActionFlags,                              const AudioTimeStamp *inTimeStamp,                              UInt32 inBusNumber,                              UInt32 inNumberFrames,                              AudioBufferList *ioData) {         // Notes: ioData 包括了一堆 buffers      // 尽可能多的向ioData中填充数据,记得设置每个buffer的大小要与buffer匹配好。     return noErr; }7、结束
  Audio Unit可以做很多非常棒的的工作。如混音,音频特效,录制等等。它处于 iOS 开发架构的底层,特别合适于音视频直播这种场景中使用。
  我们今天介绍的只是 Audio Unit众多功能中的一小点知识,但这一点点知识对于我来说已经够用了。对于那些想了解更多Audio Unit的人,只好自行去google了。
  "知识无穷尽,只取我所需"。这就是我的思想,哈!
  原文链接:iOS 实时音频采集与播放

王者荣耀戈娅首款新皮肤全面曝光,又一款6元皮肤即将上架王者荣耀早已官宣未来的伴生皮肤将会全面升级,之前上线的戈娅就一直没有伴生皮肤,直到新赛季开启,也就是三个月之后,戈娅终于迎来自己的首款皮肤危途狂花。今日,戈娅的伴生勇者皮肤正式曝光首播5集收视破亿,评分高达8。3,观众直言剧情精彩完全不够看最近几个月,可以说是古偶剧百花齐放的时段了,先有杨洋赵露思的且试天下,袁冰妍郑业成的祝卿好后有虞书欣王鹤棣的苍兰诀,杨紫成毅的沉香如屑。以及待播的玉骨遥安乐传。首播5集收视破亿,评靳东凭底线再次大火,不仅演技获赞,还被观众取了个外号这几个月出圈的作品还是以偶像剧居多,像是某酷的两人的小森林其实内容不够深层次,但看着虞书欣和张彬彬两大高颜值主演谈恋爱还是非常养眼。271在苍兰诀结束之后又有新爆款剧请君播出,该剧红色警戒核战争印度导弹部队盘点,三哥用实力证明自己能歌善舞核武器这个玩意,以目前人类文明的科技等级来看,确实是无可替代的确保长久和平的神器。五常国家虽然都拥有相当数量和规模的核武库,不过英国在这其中显得有点特殊,因为他们的首相即便按下了发驻港部队海空装备完成升级换代,配备056A轻护武直20解放军驻港部队代表的是中国正规武装力量对香港的防务承诺,是中国对香港行使主权的象征和保证。在香港未回归祖国怀抱之前,中英两国政府就针对解放军是否进驻香港问题进行了多轮激烈谈判,起初深一度走进泉州村BA在这里,有100种打篮球的理由入夜的东店村灯光球场,热闹非凡,球场旁的三层洋楼上早已站满观众。这个位于福建省泉州市石狮市锦尚镇东店村的篮球场,距离最近的海边不过一公里的路程。即便入秋,夜晚的微风还是能带来一些海毛主席身边的警卫员为何时常更换?并非失去信任,原因让人落泪作为从那个战火纷飞的岁月中走过来的老革命,毛主席的安全一直都是重中之重,他身边的警卫员也都是个中翘楚。不过毛主席与别人不同,他不会像其他人那样把警卫员培养成自己的心腹常年守卫在自己请君首播差评一片,理由出奇一致,男女主角的高颜值也救不了千呼万唤的请君空降播出,这部由李沁和任嘉伦主演的奇幻爱情剧,一早就被粉丝和网友期待。一方面很多粉丝磕男女主角的颜值,一方面也期待看到两人合作能否擦出奇妙的火花,一方面对奇幻题材的爱96年台湾富家女与邓小平警卫员订婚,女方母亲台湾娶妻要100万读者朋友们,在阅读文章之前,辛苦您动动小手点击一下关注,我们将持续更新历史故事,既方便您后续的阅读,又可以与志同道合的读友进行讨论,感谢您的支持。执子之手,与子偕老。人生苦短,想要感知自然抓住灵感放下手中的诗篇,带着想象走进自然。化作蝶,去感受那花鸟缠绵,春意盎然。化作蝉,去感受那风和日丽,绿水青山。化作雁,去感受那红叶满山,金色的秋天。化作鹰,去感受那雪月空明,风雨雷电。歌手老狼朗读晃晃悠悠晃晃悠悠(节选)作者石康朗诵老狼阿莱,我承认我爱你,尽管我们在一起时我很少提及它。阿莱,并不是我爱你这件事本身叫我痛苦,而是另外一件事,即你仍然存在着这件事,想到你我共同生活在世间
星二代里的颜值,你们最喜欢哪一个基因这个东西,真的太玄妙了有的集家族缺点于一身,避开美貌有的则拿到基因头奖,青出于蓝而胜于蓝。比如现今娱乐圈的星二代,好多颜值都超能打许曦文Elly小S的大女儿许曦文Elly,20一品大员被咸丰判斩首,行刑前儿子你回去打包行李,我不会死清朝时期,有一位一品大员因为在朝中无人替他说话,再加上皇帝对其宠爱有加,树大招风使得自己的死对头,抓住了一点小错将他送入法场。这位一品大员在送往刑场前还信誓旦旦认为自己不会死,但是蒋经国逝世后,五个儿子死了四个,唯一活着的认祖归宗艰难1975年4月5日晚上十一点左右,台北市郊士林官邸人影攒动,屋子里不时传出医生和护士跑动的声音,此时蒋介石正躺在病床上,由于突发心脏病,医生们正在全力抢救蒋介石的生命,当天晚上蒋介1990年孙立人病逝,临终前拉着儿子的手交代不葬大陆,棺不入土前言孙立人在人生的最后阶段,不过才堪堪享受了2年多的自由时光。在1988年以前,他一直过着被人监视没有人身自由的软禁生活。而长达33年的软禁时光,让他从一个在战场上杀伐果断的将军变上市仅4个月跌至2549元,12GB256GB4800mAh,荣耀旗舰售价亲民大家好,我是唐三,与以前相比,现在大家在选购手机上也慢慢有了自己的看法,以前的手机数码市场中,哪怕是旗舰机,多多少少都会出现卡顿的情况,这就导致很长一段时间,大家在选购手机上或多或新照片曝光!NASA航天器撞击小行星,出现高亮放射光线NASA公布航天器撞击小行星的三个不同视图海外网9月30日电美国航空航天局(NASA)26日利用轨道航天器,对一颗近地小行星实施了动能撞击。据美联社报道,当地时间29日,NASA公消失的三峡1917年老照片带我们重回梦中三峡1925年甘博与夫人西德尼戴维甘博(SidneyDavidGamble,18901968年),社会经济学家摄影家,致力于中国城镇和乡村社会经济问题的调查研究。19081932年,甘落选秀砍174!打爆雄鹿两大中锋,灰熊捡宝了!新版黑熊上线在NBA里,一眼看上去具备超级巨星的新星,无非都要有锋线身高后卫速度运动能力十足的身体素质,但是在NBA里也有一些动态天赋很一般的超级巨星,像约基奇东契奇就是这样的类型,过去凭借突把心思用在打磨自己上自古以来,凡是才华出众能力超强的人,比常人经受的磨难多,甚至寿命也不会太长。因为拥有任何高于平常的东西,都是造物主特赐的恩典,需要特别谦卑的心来承载。如果一个人被赐予了超越常人的恩西安出土的李建成陵墓墓室内刻有55个字,暴露了李世民的小心思说到唐朝,不得不提及开国皇室一族,都是人们耳熟能详的人物,尤其是开创了中国历史上著名贞观之治的唐太宗李世民。一方面,他是千古一帝。登基二十三年来,经过李世民君臣的不懈努力,这一时期人到中年遇到迟来的真爱,是幸运还是劫难?如果俩个有家的中年男女相爱了,心里到底有多难受?人到了中年,遇见了自己喜欢的人,怎么办?再怎么喜欢又不能更进一步,爱他就背叛了爱人,没有道德。不爱他呢,又对不起自己的内心。想保持距