up vote9down voteaccepted

+50

"Audio Recorder" is indeed a very simple tweak. The author tried to obfuscate important parts of his tweak (which function is being hooked), but here is what I found.

Tweak basically hooks just one function - AudioConverterConvertComplexBuffer from AudioToolbox.framework. Tweak is loaded in mediaserverd daemon at startup.

First, we need to find out when we should start recording because AudioConverterConvertComplexBuffer is called even when you just playing regular audio files. To achieve that tweak is listening to kCTCallStatusChangeNotification notification from CTTelephonyCenter.

Second, AudioConverterConvertComplexBuffer implementation. I didn't finished it yet so I will post what I have so far. Here is somewhat working example that will get you started.

Helper class to keep track of AudioConverterRef - ExtAudioFileRef pairs

@interface ConverterFile : NSObject

@property (nonatomic, assign) AudioConverterRef converter;
@property (nonatomic, assign) ExtAudioFileRef file;
@property (nonatomic, assign) BOOL failedToOpenFile; @end @implementation ConverterFile
@end

ConverterFile objects container

NSMutableArray* callConvertersFiles = [[NSMutableArray alloc] init];

AudioConverterConvertComplexBuffer original implementation

OSStatus(*AudioConverterConvertComplexBuffer_orig)(AudioConverterRef, UInt32, const AudioBufferList*, AudioBufferList*);

AudioConverterConvertComplexBuffer hook declaration

OSStatus AudioConverterConvertComplexBuffer_hook(AudioConverterRef inAudioConverter, UInt32 inNumberPCMFrames, const AudioBufferList *inInputData, AudioBufferList *outOutputData);

Hooking

MSHookFunction(AudioConverterConvertComplexBuffer, AudioConverterConvertComplexBuffer_hook, &AudioConverterConvertComplexBuffer_orig);

AudioConverterConvertComplexBuffer hook definition

OSStatus AudioConverterConvertComplexBuffer_hook(AudioConverterRef inAudioConverter, UInt32 inNumberPCMFrames, const AudioBufferList *inInputData, AudioBufferList *outOutputData)
{
//Searching for existing AudioConverterRef-ExtAudioFileRef pair
__block ConverterFile* cf = nil;
[callConvertersFiles enumerateObjectsUsingBlock:^(ConverterFile* obj, NSUInteger idx, BOOL *stop){
if (obj.converter == inAudioConverter)
{
cf = obj;
*stop = YES;
}
}]; //Inserting new AudioConverterRef
if (!cf)
{
cf = [[[ConverterFile alloc] init] autorelease];
cf.converter = inAudioConverter;
[callConvertersFiles addObject:cf];
} //Opening new audio file
if (!cf.file && !cf.failedToOpenFile)
{
//Obtaining input audio format
AudioStreamBasicDescription desc;
UInt32 descSize = sizeof(desc);
AudioConverterGetProperty(cf.converter, kAudioConverterCurrentInputStreamDescription, &descSize, &desc); //Opening audio file
CFURLRef url = CFURLCreateWithFileSystemPath(NULL, (CFStringRef)[NSString stringWithFormat:@"/var/mobile/Media/DCIM/Call%u.caf", [callConvertersFiles indexOfObject:cf]], kCFURLPOSIXPathStyle, false);
ExtAudioFileRef audioFile = NULL;
OSStatus result = ExtAudioFileCreateWithURL(url, kAudioFileCAFType, &desc, NULL, kAudioFileFlags_EraseFile, &audioFile);
if (result != 0)
{
cf.failedToOpenFile = YES;
cf.file = NULL;
}
else
{
cf.failedToOpenFile = NO;
cf.file = audioFile; //Writing audio format
ExtAudioFileSetProperty(cf.file, kExtAudioFileProperty_ClientDataFormat, sizeof(desc), &desc);
}
CFRelease(url);
} //Writing audio buffer
if (cf.file)
{
ExtAudioFileWrite(cf.file, inNumberPCMFrames, inInputData);
} return AudioConverterConvertComplexBuffer_orig(inAudioConverter, inNumberPCMFrames, inInputData, outOutputData);
}

This is roughly how it's done in the tweak. But why it's done like that? When phone call is in progress AudioConverterConvertComplexBuffer_hook will be called continuously. But inAudioConverter argument will be different. I've found that there can be more than nine different inAudioConverter objects passed to our hook during one phone call. They will have different audio formats so we can't write everything in one file. This is why we building array of AudioConverterRef-ExtAudioFileRef pairs - to keep track of what is being saved to where. This code will create as many file as there is AudioConverterRef objects. All files will containt different audio - one or two will be the speaker sound. Others - microphone. I've tested this code on iPhone 4S with iOS 6.1 and it works. Unfortunately, call recording on 4S can be done only when speaker is turned on. There is no such limitation on iPhone 5. This is mentioned in tweak's description.

Only thing left is to find out how we can find just two specific inAudioConverter objects - one for speaker audio and one for microphone. Everything else is not a problem.

And one last thing - mediaserverd process is sandboxed so as our tweak. We can't save files anywhere we want. This is why I chose that file path - it can be written even from the inside of the sandbox.

PS Even though I've posted this code credit has to go to Elias Limneos. He's done it.

iOS 后台录音Tweak实现参考--stackoverflow的更多相关文章

  1. iOS 直播-实现后台录音并推流

    iOS 直播-实现后台录音并推流 从一个月前开始开始接收公司的直播类app.到今天为止测试都已接近尾声,但是产品哥哥加了一个要求,就是在app进入后台后也实时保证录音并且推流. 刚听到这个的时候我也是 ...

  2. iOS后台运行

    http://www.cocoachina.com/bbs/read.php?tid=149564 文一 我从苹果文档中得知,一般的应用在进入后台的时候可以获取一定时间来运行相关任务,也就是说可以在后 ...

  3. iOS 后台处理

    iOS 后台处理的常见用途 1.进入后台时候删除资源:应用处于挂起状态的时候所占用的资源越少,该应用被iOS终止的风险就越低.通过从内存中清理那些易于重新创建的资源,可以增加应用驻留内存的机会,因此可 ...

  4. iOS后台定位实现

    iOS后台定位实现 (2013-01-24 16:43:12)     工作中碰到一个定位的应用场景:app需要在后台运行,实时上传用户地理位置.   苹果对iOS的规范性在提升了app的品质的同时也 ...

  5. iOS 后台运行 类型

    iOS后台运行,需要有特定的类型才可以进行.这些内容并不是一直不变的,苹果也在逐步的更新这些内容. 本文内容是2015年11月03日时苹果支持的后台运行类型. 这是官方连接地址 其中较为重要的是下面这 ...

  6. iOS AVAudioRecorder 录音频率、声道、位数配置 wav格式

    iOS AVAudioRecorder 录音频率.声道.位数配置 #pragma mark 录音设置 - (void)setUP_VOICE_RECOARDER { NSError *error = ...

  7. IOS 后台执行

    在IOS后台执行是本文要介绍的内容,大多数应用程序进入后台状态不久后转入暂停状态.在这种状态下,应用程序不执行任何代码,并有可能在任意时候从内存中删除.应用程序提供特定的服务,用户可以请求后台执行时间 ...

  8. iOS 后台运行实现 --备用

    文一 我从苹果文档中得知,一般的应用在进入后台的时候可以获取一定时间来运行相关任务,也就是说可以在后台运行一小段时间. 还有三种类型的可以运行在后以,1.音乐2.location 3.voip 文二 ...

  9. iOS 后台定位被拒注意事项

    iOS 后台定位被拒的原因很简单就是没有达到苹果对后台定位的要求. 本地要求: 1.在plist文件中添加字段 "Privacy - Location Always Usage Descri ...

  10. iOS 后台持续定位详解(支持ISO9.0以上)

    iOS 后台持续定位详解(支持ISO9.0以上) #import <CoreLocation/CoreLocation.h>并实现CLLocationManagerDelegate 代理, ...

随机推荐

  1. mysql 必知必会整理—表[十一]

    前言 简单整理一下表和视图. 正文 MySQL不仅用于表数据操纵,而且还可以用来执行数据库和表的所有操作,包括表本身的创建和处理. 一般有两种创建表的方法: 使用具有交互式创建和管理表的工具 表也可以 ...

  2. docker 应用篇————docker安装[二]

    前言 这其实是去年的一篇blog,忘了写了.本来我想先发一下理论的,但是水平.... 正文 如果你不熟悉linux,而是使用windows,那么你可以这样下载windows桌面版或者说你在这之前完全不 ...

  3. 第壹課-Install:Mirth Connect在Win10下的安装步骤

    1.安装JDK,推荐安装JDK8 64位,版本jdk-8u201-windows-x64.exe. 安装JDK后,同时必须配置win10的系统环境变量[示例如下]: JAVA_HOME : F:\Ja ...

  4. 力扣744(java&python)- 寻找比目标字母大的最小字母(简单)

    题目: 给你一个排序后的字符列表 letters ,列表中只包含小写英文字母.另给出一个目标字母 target,请你寻找在这一有序列表里比目标字母大的最小字母. 在比较时,字母是依序循环出现的.举个例 ...

  5. 5G新基建 边缘计算乘风破浪

    作者 | 张羽辰(同昭)阿里云交付专家 导读:如今,几乎所有的事情都离不开软件,当你开车时,脚踩上油门,实际上是车载计算机通过力度感应等计算输出功率,最终来控制油门,你从未想过这会是某个工程师的代码. ...

  6. DLF +DDI 一站式数据湖构建与分析最佳实践

    简介: 本文由阿里云数据湖构建 DLF 团队和 Databricks 数据洞察团队联合撰写,旨在帮助您更深入地了解阿里云数据湖构建(DLF)+Databricks 数据洞察(DDI)构建一站式云上数据 ...

  7. dotnet 警惕 C# 的 is var 写法

    本文将和大家介绍 C# 语言设计里面,我认为比较坑的一个语法.通过 is var 的写法,会让开发者误以为 null 是不被包含的,然而事实是在这里的 var 是被赋予含义的,将被允许 null 通过 ...

  8. WPF 对接 Vortice 调用 WIC 加载图片

    本文将告诉大家如何通过 Vortice 库从底层的方式使用 WIC 层加载本地图片文件,解码为 IWICBitmap 图片,然后将 IWICBitmap 图片交给 WPF 进行渲染 本文的前置博客:W ...

  9. QT MySQL连接自动断开

    参考链接 MySQL链接10天后自动断开解决方案:<https://blog.csdn.net/xiaoxiao133/article/details/123006881 方式一 QT中可以通过 ...

  10. FFmpeg开发笔记(十七)Windows环境给FFmpeg集成字幕库libass

    ​libass是一个适用于ASS和SSA格式(Advanced Substation Alpha/Substation Alpha)的字幕渲染器,支持的字幕类型包括srt.ass等,凡是涉及到给视频画 ...