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

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

问题描述

好吧,我的工作,有一个障碍,我需要让我的iOS应用的蓝牙音频支持一个有趣的项目。

我在障碍是,我根本无法甚至开始让连接的蓝牙音频设备的列表。虽然我的iPhone 5S认识我的耳机(A〜3 - 4岁的 LG HBM-230 ,为precise),并通过它播放音频的电话, BOTH 外部附件和CoreBluetooth是给我什么有用的,当我查询两种。

我立足我自己的code关闭问题及放大器;答案我发现无论是<一个href=\"http://stackoverflow.com/questions/10178293/how-to-get-list-of-available-bluetooth-devices\">CoreBluetooth和<一个href=\"http://stackoverflow.com/questions/19998078/corebluetooth-cannot-find-devices-but-ios-can\">External附件框架。

在我的code只是试图 scanForPeripheralsWithServices:无为的任何的蓝牙设备,其中设置 - >蓝牙说是可见的,连接后,低于code根本就没有拿出一个单一的命中超越在控制台中的 CBCentralManagerStatePoweredOn 的消息。

和此行中我的​​code(凭有效EAAccessoryManager实例)

 的NSArray * connectedDevices = [self.eAAccessoryManager connectedAccessories]

也回来了零数组。

还有什么我是做错了什么?

B.T.W,我做了可作为GitHub的项目这code。

  @implementation BluetoothManager+(BluetoothManager *)sharedInstance
{
    静态dispatch_once_t preD = 0;
    __strong静态ID _bluetoothMGR =零;    dispatch_once(安培; preD,^ {
        _bluetoothMGR = [[BluetoothManager的alloc]初始化];
    });    返回_bluetoothMGR;
} - (ID)的init
{
    自= [超级初始化]
    如果(个体经营)
    {
        dispatch_queue_t centralQueue = dispatch_queue_create(com.yo.mycentral,DISPATCH_QUEUE_SERIAL);        //我们是否尝试这种对无(主队列),或这个单独的线程队列中,仍然没有得到结果
        self.cbManager = [[CBCentralManager页头] initWithDelegate:自队列:centralQueue选项:无];
    }
    返回自我;
}//这将打击....如果我这个实例在厦门国际银行文件的故事板
- (无效)awakeFromNib
{
    如果(!self.cbManager)
        self.cbManager = [[CBCentralManager页头] initWithDelegate:自队列:无选项:无];
} - (无效)centralManager:(CBCentralManager *)中央didDiscoverPeripheral:(CBPeripheral *)周边advertisementData:(NSDictionary的*)advertisementData RSSI(NSNumber的*){RSSI    的NSLog(@嘿,我找到%@,[advertisementData描述]);
} - (无效)centralManager:(CBCentralManager *)中央didRetrieveConnectedPeripherals:(NSArray的*)外设
{
    的NSLog(@我检索连接的外设);
} - (无效)centralManager:(CBCentralManager *)中央didRetrievePeripherals:(NSArray的*){外设
    的NSLog(@这就是它!);
} - (无效)centralManagerDidUpdateState:(CBCentralManager *){中心
    * NSString的messtoshow;    开关(central.state){
        案例CBCentralManagerStateUnknown:
        {
            messtoshow = @状态未知,更新迫在眉睫。
            打破;
        }
        案例CBCentralManagerStateResetting:
        {
            messtoshow = @与系统服务的连接一度丢失,更新迫在眉睫。
            打破;
        }
        案例CBCentralManagerStateUnsupported:
        {
            messtoshow = @该平台不支持蓝牙低功耗
            打破;
        }
        案例CBCentralManagerStateUnauthorized:
        {
            messtoshow = @应用程序是无权使用蓝牙低耗能;
            打破;
        }
        案例CBCentralManagerStatePoweredOff:
        {
            messtoshow = @蓝牙目前已关闭。
            打破;
        }
        案例CBCentralManagerStatePoweredOn:
        {
            messtoshow = @蓝牙正在通电并可以使用。
            *的NSDictionary选项= [NSDictionary的dictionaryWithObjectsAndKeys:[NSNumber的numberWithBool:YES],CBCentralManagerScanOptionAllowDuplicatesKey,零]            [_cbManager scanForPeripheralsWithServices:无选项:选择];            打破;
        }    }
    的NSLog(@%@,messtoshow);
}@结束


解决方案

首先,你需要配置你的应用程序音频会话,以便支持音频蓝牙连接。你可以这样做的,例如,您的应用程序委托 - (BOOL)应用:(*的UIApplication)的应用didFinishLaunchingWithOptions:(NSDictionary的*)launchOptions方法。请确保您链接AVFoundation框架和进口的标头将使用它。

 #进口&LT; AVFoundation / AVFoundation.h&gt;在.H //到位[自prepareAudioSession]; //从申请didFinishLaunchingWithOptions称为 - (BOOL)$ P $ {ppareAudioSession     //关闭会话
     BOOL成功= [[AVAudioSession sharedInstance] SETACTIVE:没有错误:无];
     如果(!成功){
         的NSLog(@deactivati​​onError);
     }     //设置音频会话类别AVAudioSessionCategoryPlayAndRecord选项AVAudioSessionCategoryOptionAllowBluetooth
     成功= [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionAllowBluetooth错误:无];
     如果(!成功){
         的NSLog(@setCategoryError);
     }     //激活音频会议
     成功= [[AVAudioSession sharedInstance] SETACTIVE:YES错误:无];
     如果(!成功){
         的NSLog(@activati​​onError);
     }返回成功;

}

每个应用程序都有一个音频会话单可以配置。会议类别和模式(在这个例子中我没有设置模式,以便其恢复为默认模式)申报您的应用程序的意图,以你想怎么音频路由进行处理。它遵循的最后在胜利的一个重要规则。这意味着,如果用户插入的耳机或在这种情况下,一个蓝牙设备是免提外围设备(HFP),系统会自动音频路由到耳机或蓝牙设备。在使用者身体的动作被用于确定音频路由。但是,如果你想给用户提供的路线列表苹果推荐使用MPVolumeView类。

添加MPVolumeView可以放在一个UIViewController一个例子子类viewDidLoad方法。

 #进口&LT;的MediaPlayer / MediaPlayer.h&GT; //发生在.H
// prefered方式使用MPVolumeView用户选择的音频路线
self.view.backgroundColor =的UIColor clearColor]
的CGRect frameForMPVV = CGRectMake(50.0,50.0,100.0,100.0);
MPVolumeView * routeView = [[MPVolumeView页头] initWithFrame:方法frameForMPVV];
[routeView setShowsVolumeSlider:NO];
[routeView setShowsRouteButton:YES];
[self.view addSubview:routeView];

由于iOS的7,你可以得到所有的输入这样的

  // portDesc.portType例如可以是 -  BluetoothHFP,MicrophoneBuiltIn,MicrophoneWired
的NSArray * availInputs = [[AVAudioSession sharedInstance] availableInputs];
诠释计数= [availInputs计数]
对于(INT K = 0; K&LT;计数; k ++){
    AVAudioSessionPortDescription * portDesc = [availInputs objectAtIndex:K];
    的NSLog(@输入%I端口类型%@,K + 1,portDesc.portType);
    的NSLog(@输入%I端口名称%@,K + 1,portDesc.portName);
}

您会感兴趣的端口类型为BluetoothHFP。该物业PORTNAME通常是制造商/型号这是你会显示给用户的内容。 (我曾与一个非LE蓝牙摩托罗拉恐龙检查这个和它的作品)

由于在最后在胜利规定,你需要观察这两个通知(iOS版7含税)。一个处理中断(如电话或报警),第二被通知的路由变化。路线更改通知是一个涉及到这个问题。

  [NSNotificationCenter defaultCenter]的addObserver:自我
                                         选择:@选择(myInterruptionSelector :)
                                             名称:AVAudioSessionInterruptionNotification
                                           对象:无];
[NSNotificationCenter defaultCenter]的addObserver:自我
                                         选择:@选择(myRouteChangeSelector :)
                                             名称:AVAudioSessionRouteChangeNotification
                                           对象:无];

有关的iOS 6.x的,你可以阅读AVA​​udioSession的currentRoute属性myRouteChange内:选择来获得新的途径,为耳机或蓝牙设备连接时,这将被调用

   - (无效)myRouteChangeSelector:(NSNotification *)通知{
 AVAudioSessionRouteDescription * currentRoute = [[AVAudioSession sharedInstance] currentRoute];
      NSArray的* inputsForRoute = currentRoute.inputs;
      NSArray的* outputsForRoute = currentRoute.outputs;
      AVAudioSessionPortDescription * outPortDesc = [outputsForRoute objectAtIndex:0];
      的NSLog(@当前外港类型%@,outPortDesc.portType);
      AVAudioSessionPortDescription * inPortDesc = [inputsForRoute objectAtIndex:0];
      的NSLog(@目前的运行轨迹类型%@,inPortDesc.portType);

}

任何版本的iOS&LT; 6.0你所需要的'的现在去precated 的'AudioSessionServices类。这个类是一个C API,与其通知,它可以让你添加属性侦听器。

我会完成在这张票据 - 你不总是得到你想要从系统中想要的。有中断处理的通知,观察和检查需要很多的错误。我觉得这是一个很好的问题,我希望这阐明了什么是你正在努力实现的一些情况。

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

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.

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

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.

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

NSArray * connectedDevices = [self.eAAccessoryManager connectedAccessories];

also comes back with a nil array.

What could I be doing wrong?

B.T.W., I've made this code available as a GitHub project.

@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

解决方案

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;

}

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.

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];

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);
}

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)

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];

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);

}

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天全站免登陆