如何在 iOS 中查找蓝牙音频设备 [英] how to find Bluetooth audio devices in iOS

查看:17
本文介绍了如何在 iOS 中查找蓝牙音频设备的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

好的,我正在做一个有趣的项目,但有一个障碍,我需要为我的 iOS 应用启用蓝牙音频支持.

Okay, I'm working on a fun project that has a hurdle where I need to enable Bluetooth audio support for my iOS app.

我遇到的障碍是我什至无法开始获取已连接蓝牙音频设备的列表.即使我的 iPhone 5S 可以识别我的耳机(大约 3 - 4 岁的 LG HBM-230,准确地说)并通过它播放音频以拨打电话,BOTH 外部附件和 CoreBluetooth 没有给我任何有用的东西查询两者.

The hurdle I'm at is that I simply can't even begin to get a list of connected Bluetooth audio devices. Even though my iPhone 5S recognizes my earpiece (a ~3 - 4 year old LG HBM-230, to be precise) and plays audio through it for phone calls, BOTH External Accessory and CoreBluetooth are giving me nothing useful when I query both.

我将自己的代码基于问题和我为 CoreBluetooth外部附件框架.

I'm basing my own code off questions & answers I found for both the CoreBluetooth and External Accessory frameworks.

当我的代码只是尝试scanForPeripheralsWithServices:nil"为任何蓝牙设备设置->蓝牙说是可见和连接时,下面的代码根本不会出现在控制台中的CBCentralManagerStatePoweredOn"消息之外点击一次.

When my code simply tries to "scanForPeripheralsWithServices:nil" for any Bluetooth devices which Settings->Bluetooth say are visible and connected, the below code simply is NOT coming up with a single hit beyond the "CBCentralManagerStatePoweredOn" message in the console.

我代码中的这一行(带有有效的 EAAccessoryManager 实例)

And this line in my code (with a valid EAAccessoryManager instance)

NSArray * connectedDevices = [self.eAAccessoryManager connectedAccessories];

也返回一个 nil 数组.

also comes back with a nil array.

我可能做错了什么?

顺便说一句,我已将此代码作为 GitHub 项目提供.

@implementation BluetoothManager

+ (BluetoothManager *)sharedInstance
{
    static dispatch_once_t pred = 0;
    __strong static id _bluetoothMGR = nil;

    dispatch_once(&pred, ^{
        _bluetoothMGR = [[BluetoothManager alloc] init];
    });

    return _bluetoothMGR;
}

- (id)init
{
    self = [super init];
    if(self)
    {
        dispatch_queue_t centralQueue = dispatch_queue_create("com.yo.mycentral", DISPATCH_QUEUE_SERIAL);

        // whether we try this on a queue of "nil" (the main queue) or this separate thread, still not getting results
        self.cbManager = [[CBCentralManager alloc] initWithDelegate:self queue:centralQueue options:nil];
    }
    return self;
}

// this would hit.... if I instantiated this in a storyboard of XIB file
- (void)awakeFromNib
{
    if(!self.cbManager)
        self.cbManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil options:nil];
}

- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI {

    NSLog(@"hey I found %@",[advertisementData description]);
}

- (void)centralManager:(CBCentralManager *)central didRetrieveConnectedPeripherals:(NSArray *)peripherals
{
    NSLog( @"I retrieved CONNECTED peripherals");
}

-(void)centralManager:(CBCentralManager *)central didRetrievePeripherals:(NSArray *)peripherals{
    NSLog(@"This is it!");
}

- (void)centralManagerDidUpdateState:(CBCentralManager *)central{
    NSString *messtoshow;

    switch (central.state) {
        case CBCentralManagerStateUnknown:
        {
            messtoshow=@"State unknown, update imminent.";
            break;
        }
        case CBCentralManagerStateResetting:
        {
            messtoshow=@"The connection with the system service was momentarily lost, update imminent.";
            break;
        }
        case CBCentralManagerStateUnsupported:
        {
            messtoshow=@"The platform doesn't support Bluetooth Low Energy";
            break;
        }
        case CBCentralManagerStateUnauthorized:
        {
            messtoshow=@"The app is not authorized to use Bluetooth Low Energy";
            break;
        }
        case CBCentralManagerStatePoweredOff:
        {
            messtoshow=@"Bluetooth is currently powered off.";
            break;
        }
        case CBCentralManagerStatePoweredOn:
        {
            messtoshow=@"Bluetooth is currently powered on and available to use.";
            NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], CBCentralManagerScanOptionAllowDuplicatesKey, nil];

            [_cbManager scanForPeripheralsWithServices:nil options:options];

            break;
        }   

    }
    NSLog(@"%@", messtoshow);
}

@end

推荐答案

首先,您需要配置应用程序音频会话以允许支持音频的蓝牙连接.例如,您可以在您的应用程序委托中执行此操作 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 方法.确保链接 AVFoundation 框架并导入将使用它的标头.

First you will need to configure your applications audio session to allow bluetooth connections that support audio. You can do this in, for example, your application delegates - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions method. Make sure you link the AVFoundation Framework and import in headers that will use it.

#import <AVFoundation/AVFoundation.h>// place in .h

[self prepareAudioSession];// called from application didFinishLaunchingWithOptions

- (BOOL)prepareAudioSession {

     // deactivate session
     BOOL success = [[AVAudioSession sharedInstance] setActive:NO error: nil];
     if (!success) {
         NSLog(@"deactivationError");
     }

     // set audio session category AVAudioSessionCategoryPlayAndRecord options AVAudioSessionCategoryOptionAllowBluetooth
     success = [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionAllowBluetooth error:nil];
     if (!success) {
         NSLog(@"setCategoryError");
     }

     // activate audio session
     success = [[AVAudioSession sharedInstance] setActive:YES error: nil];
     if (!success) {
         NSLog(@"activationError");
     }

return success;

}

每个应用程序都有一个可以配置的音频会话单例.会话类别和模式(在这个例子中我没有设置模式,所以它恢复到默认模式)声明你的应用程序意图你希望如何处理音频路由.它遵循最后胜利的重要规则.这意味着如果用户插入耳机或在这种情况下是免提外围设备 (HFP) 的蓝牙设备,系统将自动将音频路由到耳机或蓝牙设备.用户的身体动作用于确定音频路由.但是,如果您希望向用户提供可用路线列表,Apple 建议使用 MPVolumeView 类.

Every application has an audio session singleton that you can configure. The sessions category and mode (in this example I did not set the mode so it reverts to the default mode) declare your applications intentions as to how you would like audio routing to be handled. It follows an important rule of last in wins. This means that if the user plugs in a headset or in this case a bluetooth device that is a hands free peripheral (HFP) the system will automatically route the audio to the headset or bluetooth device. The users physical actions are used to determine audio routing. However if you wish to give the user a list of available routes Apple recommend using MPVolumeView class.

添加 MPVolumeView 的示例可以放在 UIViewController 子类的 viewDidLoad 方法中.

An example for adding MPVolumeView could be put in a UIViewController subclasses viewDidLoad method.

#import <MediaPlayer/MediaPlayer.h> // place in .h
// prefered way using MPVolumeView for user selecting audio routes
self.view.backgroundColor = [UIColor clearColor];
CGRect frameForMPVV = CGRectMake(50.0, 50.0, 100.0, 100.0);
MPVolumeView *routeView = [[MPVolumeView alloc] initWithFrame:frameForMPVV];
[routeView setShowsVolumeSlider:NO];
[routeView setShowsRouteButton:YES];
[self.view addSubview: routeView];

从 iOS 7 开始,您可以获得所有这样的输入

As of iOS 7 you can get all inputs like this

// portDesc.portType could be for example - BluetoothHFP, MicrophoneBuiltIn, MicrophoneWired
NSArray *availInputs = [[AVAudioSession sharedInstance] availableInputs];
int count = [availInputs count];
for (int k = 0; k < count; k++) {
    AVAudioSessionPortDescription *portDesc = [availInputs objectAtIndex:k];
    NSLog(@"input%i port type %@", k+1, portDesc.portType);
    NSLog(@"input%i port name %@", k+1, portDesc.portName);
}

您可能感兴趣的端口类型是BluetoothHFP".portName 属性通常是制造商/型号,这是您将向用户显示的内容.(我已经用非 LE 蓝牙摩托罗拉恐龙检查过这个,它可以工作)

The portType you would be interested in is "BluetoothHFP". The portName property typically is the manufacturer/model which is what you would show to the user. (I've checked this with a non-LE bluetooth Motorola dinosaur and it works)

由于最后胜利规则,您需要观察这两个通知(包括 iOS 7).一个用于处理中断(例如电话或警报),第二个用于通知路线更改.路由更改通知是与此问题相关的通知.

Because of the last in wins rule you will need to observe these two notifications (iOS 7 included). One to handle interruptions (such as phone calls or an alarm) and the second to be notified of route changes. Route change notifications is the one related to this question.

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(myInterruptionSelector:)
                                             name:AVAudioSessionInterruptionNotification
                                           object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(myRouteChangeSelector:)
                                             name:AVAudioSessionRouteChangeNotification
                                           object:nil];

对于 iOS 6.x,您可以在 myRouteChange: 选择器中读取 AVAudioSession 的 currentRoute 属性以获取新路由,因为这将在连接耳机或蓝牙设备时被调用.

For iOS 6.x you could read the currentRoute property of AVAudioSession inside the myRouteChange: selector to get the new route, as this will get called when a headset or bluetooth device is connected.

- (void)myRouteChangeSelector:(NSNotification*)notification {
 AVAudioSessionRouteDescription *currentRoute = [[AVAudioSession sharedInstance] currentRoute];
      NSArray *inputsForRoute = currentRoute.inputs;
      NSArray *outputsForRoute = currentRoute.outputs;
      AVAudioSessionPortDescription *outPortDesc = [outputsForRoute objectAtIndex:0];
      NSLog(@"current outport type %@", outPortDesc.portType);
      AVAudioSessionPortDescription *inPortDesc = [inputsForRoute objectAtIndex:0];
      NSLog(@"current inPort type %@", inPortDesc.portType);
}

任何 iOS 版本 <在 6.0 中,您将需要现已弃用"AudioSessionServices 类.此类是一个 C api,它允许您添加属性侦听器而不是通知.

Any iOS version < 6.0 you'll need the 'now deprecated' AudioSessionServices class. This class is a C api that instead of notifications it allows you to add property listeners.

我会在这个笔记上结束 - 你并不总是从系统中得到你想要的.需要观察中断处理通知并需要进行大量错误检查.我认为这是一个非常好的问题,我希望这能阐明您正在努力实现的目标.

I'll finish on this note - YOU DONT ALWAYS GET WHAT YOU WANT from the system. There are interruption handling notifications to observe and lots of error checking needed. I think this is a really good question and I hope this sheds some light on what it is you are trying to achieve.

这篇关于如何在 iOS 中查找蓝牙音频设备的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆