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. 报表如何批量导出成 excel 文件

    需求说明 报表展现后可以通过工具栏中的导出按钮将当前展现的报表导出成 excel 文件,但是在实际使用中通常会要求报表不需要展现,直接通过一些操作将报表导出成 excel 文件,并且往往会要求批量导出 ...

  2. Locust、Jemter、Loadrunner三种工具的分布式压测

    前言: 最近公司接了一个云上展会项目,合同里签订的是6w并发连接数和2w QPS的性能指标,还有监理单位,第三方评测单位. 唉,先吐槽一下,有了监理和评测,文档tmd一堆堆,严格按照软件开发来执行,写 ...

  3. Oracle 查询超级慢之buffer sort

    查询超级慢之buffer sort 在视图中增加了一个临时表作为一个数据源进行id和名称的转换,没加的时候一秒不到,加了以后14秒,感觉有点问题,于是打开了解释计划看了下,发现这个buffer sor ...

  4. 【笔记】rocketMQ了解

    记录rocketMQ 忘了从哪儿保存的图了 原图链接:https://www.jianshu.com/p/2838890f3284

  5. HarmonyOS NEXT应用开发之深色模式适配

    介绍 本示例介绍在开发应用以适应深色模式时,对于深色和浅色模式的适配方案,采取了多种策略如下: 固定属性适配:对于部分组件的颜色属性,如背景色或字体颜色,若保持不变,可直接设定固定色值或引用固定的资源 ...

  6. [GPT] golang代码组织的核心思想

    1/golang代码组织的核心思想 Go语言(Golang)在代码组织上的核心思想是模块化和封装,主要体现在以下几个方面: 1.包(Packages): Go语言通过包(Packages)来组织代码, ...

  7. [Contract] 监听 MetaMask 网络变化, 账号切换

    为什么需要监听网络变化?目前在 MetaMask 中切换网络,网页会自动刷新,但是这一特性后面将停止使用. MetaMask: MetaMask will soon stop reloading pa ...

  8. 聊聊 dotnet 7 对 bool 与字符串互转的底层性能优化

    本文也叫 跟着 Stephen Toub 大佬学性能优化系列.大家都知道在 .NET 7 有众多的性能优化,其中就包括了对布尔和字符串互转的性能优化.在对布尔和字符串的转换的性能优化上,有着非常巧妙的 ...

  9. 2019-8-31-How-to-output-the-target-message-in-dotnet-build-command-line

    title author date CreateTime categories How to output the target message in dotnet build command lin ...

  10. 兼容ie8问题

    <!-- 让IE8/9支持媒体查询,从而兼容栅格 --><!--[if lt IE 9]><script src="https://cdn.staticfile ...