我正在开发一个iOS应用程序(目前专门针对iPhone),要求应用程序仅从iPhone内置麦克风录制音频(即使插入了耳机/耳机),以及在耳机播放(假设现在插入耳机)。
我想知道目前是否可以使用可用的API?如果是这样,有人可以说明我该如何做到这一点?
谢谢!
答案 0 :(得分:8)
我相信这个问题的答案是'不'。我使用iPhone 4和新到iOS 4 AVFoundation进行实验,重点关注AVCaptureDevice类。
我在应用程序中添加了以下内容:
NSLog(@"%@", [AVCaptureDevice devices]);
因此要求列出可用于捕获音频和/或视频的所有设备。没有插入耳机,我得到:
(
"Back Camera",
"Front Camera",
"iPhone Microphone"
)
耳机插入后,我得到:
(
"Back Camera",
"Front Camera",
Headphones
)
因此,只要耳机可用,iPhone麦克风就会从可用的AVCaptureDevices列表中删除。为了进一步探讨这一点,我添加了一些代码来获取可用音频设备的AVCaptureDevice实例并打印其唯一ID。对于将自己标识为“iPhone麦克风”的设备和将自己标识为“耳机”的设备,我得到了:
com.apple.avfoundation.avcapturedevice.built-in_audio:0
在我看来,很明显两个设备不能具有相同的唯一ID,所以显然它是同一设备改变其状态。尽管AVCaptureDevices在设置状态方面有很多东西,但它仅限于焦点,曝光,闪光和白平衡等视觉效果。
答案 1 :(得分:4)
看起来真的不可能。
我的目标是将输出发送到蓝牙耳机并记录输入。 到目前为止,我可以看到,我最好的选择是:“PlayAndRecord + AllowBluetoothInput”属性 (iphone 4,诺基亚BH-214耳机)
重要的是,根据Apple文档,当音频路径发生变化时,您必须重新覆盖音频类别!
这是我的ROUTE CHANGE LISTENER方法,它打印: RouteChangeReasons,outputRoute,audioRout :
void RouteChangeListener(void *inClientData,
AudioSessionPropertyID inID,
UInt32 inDataSize,
const void *inData) {
if (inID == kAudioSessionProperty_AudioRouteChange) {
NSLog(@"]-----------------[ Audio Route Change ]--------------------[");
// ************************************************************************************************
// Check route change reason **********************************************************************
// ************************************************************************************************
CFDictionaryRef routeDict = (CFDictionaryRef)inData;
NSNumber* reasonValue = (NSNumber*)CFDictionaryGetValue(routeDict, CFSTR(kAudioSession_AudioRouteChangeKey_Reason));
int reason = [reasonValue intValue];
if (reason == kAudioSessionRouteChangeReason_OldDeviceUnavailable) {
NSLog(@"] Logic: audio route change reason: OldDeviceUnavailable");
}else if (reason == kAudioSessionRouteChangeReason_NewDeviceAvailable ) {
NSLog(@"] Logic: audio route change reason: NewDeviceAvailable");
}else if (reason == kAudioSessionRouteChangeReason_Unknown ) {
NSLog(@"] Logic: audio route change reason: Unknown");
}else if (reason == kAudioSessionRouteChangeReason_CategoryChange ) {
NSLog(@"] Logic: audio route change reason: CategoryChange");
}else if (reason == kAudioSessionRouteChangeReason_Override ) {
NSLog(@"] Logic: audio route change reason: Override");
}else if (reason == kAudioSessionRouteChangeReason_WakeFromSleep ) {
NSLog(@"] Logic: audio route change reason: WakeFromSleep");
}else if (reason == kAudioSessionRouteChangeReason_NoSuitableRouteForCategory ) {
NSLog(@"] Logic: audio route chang reasone: NoSuitableRouteForCategory");
}
// ************************************************************************************************
// Check output type ******************************************************************************
// ************************************************************************************************
CFDictionaryRef currentRouteDescriptionDictionary = nil;
UInt32 dataSize = sizeof(currentRouteDescriptionDictionary);
AudioSessionGetProperty(kAudioSessionProperty_AudioRouteDescription, &dataSize, ¤tRouteDescriptionDictionary);
if (currentRouteDescriptionDictionary) {
CFArrayRef outputs = CFDictionaryGetValue(currentRouteDescriptionDictionary, kAudioSession_AudioRouteKey_Outputs);
if(CFArrayGetCount(outputs) > 0) {
CFDictionaryRef currentOutput = CFArrayGetValueAtIndex(outputs, 0);
CFStringRef outputType = CFDictionaryGetValue(currentOutput, kAudioSession_AudioRouteKey_Type);
if ( (CFStringCompare(outputType, kAudioSessionOutputRoute_AirPlay, 0) == kCFCompareEqualTo) ) { // if Airplay
NSLog(@"] Logic: output changed to Airplay");
}
else if ( (CFStringCompare(outputType, kAudioSessionOutputRoute_BluetoothA2DP, 0) == kCFCompareEqualTo) ) { // if Bluetooth A2DP
NSLog(@"] Logic: output changed to A2DP");
// Mix with others category
UInt32 doSetProperty = 1;
AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryMixWithOthers,sizeof(doSetProperty),&doSetProperty);
// Bluetooth support enable
UInt32 allowBluetoothInput = 1;
AudioSessionSetProperty (kAudioSessionProperty_OverrideCategoryEnableBluetoothInput,sizeof (allowBluetoothInput),&allowBluetoothInput);
}
else if ( (CFStringCompare(outputType, kAudioSessionOutputRoute_BluetoothHFP, 0) == kCFCompareEqualTo) ) { // if Bluetooth HFP
NSLog(@"] Logic: output changed to HFP");
// Mix with others category
UInt32 doSetProperty = 1;
AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryMixWithOthers,sizeof(doSetProperty),&doSetProperty);
// Bluetooth support enable
UInt32 allowBluetoothInput = 1;
AudioSessionSetProperty (kAudioSessionProperty_OverrideCategoryEnableBluetoothInput,sizeof (allowBluetoothInput),&allowBluetoothInput);
}
else if ( (CFStringCompare(outputType, kAudioSessionOutputRoute_LineOut, 0) == kCFCompareEqualTo) ) { // if Line Out
NSLog(@"] Logic: output changed to Line Out");
}
else if ( (CFStringCompare(outputType, kAudioSessionOutputRoute_Headphones, 0) == kCFCompareEqualTo) ) { // if Headphones
NSLog(@"] Logic: output changed to Headphone");
// Mix with others category
UInt32 doSetProperty = 1;
AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryMixWithOthers,sizeof(doSetProperty),&doSetProperty);
// Bluetooth support disable
UInt32 allowBluetoothInput = 0;
AudioSessionSetProperty (kAudioSessionProperty_OverrideCategoryEnableBluetoothInput,sizeof (allowBluetoothInput),&allowBluetoothInput);
}
else if ( (CFStringCompare(outputType, kAudioSessionOutputRoute_BuiltInSpeaker, 0) == kCFCompareEqualTo) ) { // if Built In Speaker
NSLog(@"] Logic: output changed to Built In Speaker");
// Mix with others category
UInt32 doSetProperty = 1;
AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryMixWithOthers,sizeof(doSetProperty),&doSetProperty);
}
else if ( (CFStringCompare(outputType, kAudioSessionOutputRoute_USBAudio, 0) == kCFCompareEqualTo) ) { // if USB audio
NSLog(@"] Logic: output changed to USB Audio");
}
else if ( (CFStringCompare(outputType, kAudioSessionOutputRoute_HDMI, 0) == kCFCompareEqualTo) ) { // if HDMI
NSLog(@"] Logic: output changed to HDMI");
}
else if ( (CFStringCompare(outputType, kAudioSessionOutputRoute_BuiltInReceiver, 0) == kCFCompareEqualTo) ) { // if Built in Reciever
NSLog(@"] Logic: output changed to Built in Reciever");
// Mix with others category
UInt32 doSetProperty = 1;
AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryMixWithOthers,sizeof(doSetProperty),&doSetProperty);
}
else { // Unknown audio type
NSLog(@"] Logic: WARNING: Unknown audio type: %@",(NSString*)outputType);
}
}
}
// ************************************************************************************************
// Check audio route ******************************************************************************
// ************************************************************************************************
UInt32 routeSize = sizeof(CFStringRef);
CFStringRef route;
AudioSessionGetProperty(kAudioSessionProperty_AudioRoute, &routeSize, &route);
NSLog(@"] Logic: the audio route is: %@",(NSString*)route);
// ************************************************************************************************
NSLog(@"]--------------------------[ ]-----------------------------[");
}
}
答案 2 :(得分:3)
由于苹果从7.0开始重新改变音频系统,我将在这里发布更新代码:
#pragma mark Route change listener
// *********************************************************************************************************
// *********** Route change listener ***********************************************************************
// *********************************************************************************************************
-(void)routeChanged:(NSNotification*)notification {
NSLog(@"]-----------------[ Audio Route Change ]--------------------[");
AVAudioSession *session = [AVAudioSession sharedInstance];
//AVAudioSessionRouteDescription* prevRoute = [[notification userInfo] objectForKey:AVAudioSessionRouteChangePreviousRouteKey];
// Reason
NSInteger reason = [[[notification userInfo] objectForKey:AVAudioSessionRouteChangeReasonKey] integerValue];
switch (reason) {
case AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory:
NSLog(@"] Audio Route: The route changed because no suitable route is now available for the specified category.");
break;
case AVAudioSessionRouteChangeReasonWakeFromSleep:
NSLog(@"] Audio Route: The route changed when the device woke up from sleep.");
break;
case AVAudioSessionRouteChangeReasonOverride:
NSLog(@"] Audio Route: The output route was overridden by the app.");
break;
case AVAudioSessionRouteChangeReasonCategoryChange:
NSLog(@"] Audio Route: The category of the session object changed.");
break;
case AVAudioSessionRouteChangeReasonOldDeviceUnavailable:
NSLog(@"] Audio Route: The previous audio output path is no longer available.");
break;
case AVAudioSessionRouteChangeReasonNewDeviceAvailable:
NSLog(@"] Audio Route: A preferred new audio output path is now available.");
break;
case AVAudioSessionRouteChangeReasonUnknown:
NSLog(@"] Audio Route: The reason for the change is unknown.");
break;
default:
NSLog(@"] Audio Route: The reason for the change is very unknown.");
break;
}
// Output
AVAudioSessionPortDescription *output = [[session.currentRoute.outputs count]?session.currentRoute.outputs:nil objectAtIndex:0];
if ([output.portType isEqualToString:AVAudioSessionPortLineOut]) {
NSLog(@"] Audio Route: Output Port: LineOut");
}
else if ([output.portType isEqualToString:AVAudioSessionPortHeadphones]) {
NSLog(@"] Audio Route: Output Port: Headphones");
}
else if ([output.portType isEqualToString:AVAudioSessionPortBluetoothA2DP]) {
NSLog(@"] Audio Route: Output Port: BluetoothA2DP");
}
else if ([output.portType isEqualToString:AVAudioSessionPortBuiltInReceiver]) {
NSLog(@"] Audio Route: Output Port: BuiltInReceiver");
}
else if ([output.portType isEqualToString:AVAudioSessionPortBuiltInSpeaker]) {
NSLog(@"] Audio Route: Output Port: BuiltInSpeaker");
}
else if ([output.portType isEqualToString:AVAudioSessionPortHDMI]) {
NSLog(@"] Audio Route: Output Port: HDMI");
}
else if ([output.portType isEqualToString:AVAudioSessionPortAirPlay]) {
NSLog(@"] Audio Route: Output Port: AirPlay");
}
else if ([output.portType isEqualToString:AVAudioSessionPortBluetoothLE]) {
NSLog(@"] Audio Route: Output Port: BluetoothLE");
}
else {
NSLog(@"] Audio Route: Output Port: Unknown: %@",output.portType);
}
// Input
AVAudioSessionPortDescription *input = [[session.currentRoute.inputs count] ? session.currentRoute.inputs:nil objectAtIndex:0];
if ([input.portType isEqualToString:AVAudioSessionPortLineIn]) {
NSLog(@"] Audio Route: Input Port: LineIn");
}
else if ([input.portType isEqualToString:AVAudioSessionPortBuiltInMic]) {
NSLog(@"] Audio Route: Input Port: BuiltInMic");
}
else if ([input.portType isEqualToString:AVAudioSessionPortHeadsetMic]) {
NSLog(@"] Audio Route: Input Port: HeadsetMic");
}
else if ([input.portType isEqualToString:AVAudioSessionPortBluetoothHFP]) {
NSLog(@"] Audio Route: Input Port: BluetoothHFP");
}
else if ([input.portType isEqualToString:AVAudioSessionPortUSBAudio]) {
NSLog(@"] Audio Route: Input Port: USBAudio");
}
else if ([input.portType isEqualToString:AVAudioSessionPortCarAudio]) {
NSLog(@"] Audio Route: Input Port: CarAudio");
}
else {
NSLog(@"] Audio Input Port: Unknown: %@",input.portType);
}
NSLog(@"]--------------------------[ ]-----------------------------[");
}
请记住添加观察者,因为音频会话的委托也已弃用:
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector(audioInterruption:)
name: AVAudioSessionInterruptionNotification
object: nil];
PS:你不需要在这里重置类别(如在6.0中)
答案 3 :(得分:2)
我非常有信心通过您的应用程序的音频会话可以实现:
音频会话是中介 您的应用程序和iOS之间。每 iPhone应用程序只有一个 音频会话。您将其配置为 表达你的应用程序的音频 意图。首先,你回答一些问题 关于你如何想要你的问题 申请表现:
- 您希望如何申请 响应中断,例如a 打电话?
- 你打算混合你的 应用程序的声音来自 其他正在运行的应用程序,或者你 打算让他们沉默?
- 应该如何 您的应用程序响应音频 路由更改,例如,当用户 插入或拔下耳机?
使用 手头的答案,你使用音频 会话接口(声明于 AudioToolbox / AudioServices.h)来 配置你的音频会话和你的 应用
挖掘这些文档:
让我知道它是怎么回事!
答案 4 :(得分:2)
这是不可能的,我试图弄清楚路由改变了监听器(使用AudioSession)。我的结果是:由于Apple提供的categories,您无法单独配置输入或输出。我尝试* PlayAndRecord,当我配对一个蓝牙设备时,路由改变如下:
old route : HeadsetBT
new route : SpeakerAndMicrophone
事实上,我的蓝牙不是耳机,只是扬声器......所以对我来说没有解决方案。
答案 5 :(得分:-1)
我发现,当使用AirPod pro耳机并尝试录制语音邮件问候语时,即使耳机已完全连接并在使用中,手机仍使用了内置麦克风。