iOS音频流仅适用于**部分**蓝牙设备?

iOS音频流仅适用于**部分**蓝牙设备?,ios,iphone,audio,bluetooth,streaming,Ios,Iphone,Audio,Bluetooth,Streaming,我正在开发iOS应用程序,它将与iOS 6/7兼容,并从网站上传输音频.mp3文件 我已经使用以下代码实现了这一点: -(NSString*)documentsFolder { NSString* dataPath = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents"]; if (![[NSFileManager defaultManager] fileExistsAtPath:dataPath])

我正在开发iOS应用程序,它将与iOS 6/7兼容,并从网站上传输音频.mp3文件

我已经使用以下代码实现了这一点:

-(NSString*)documentsFolder
{
    NSString* dataPath =  [NSHomeDirectory() stringByAppendingPathComponent:@"Documents"];
    if (![[NSFileManager defaultManager] fileExistsAtPath:dataPath])
        [[NSFileManager defaultManager] createDirectoryAtPath:dataPath withIntermediateDirectories:NO attributes:nil error:NULL];
    return dataPath;
}

-(NSString*)createURLFile:(NSString*)songURL
{
   NSString* M3U_FILE = @"song.m3u";
   NSString* path = [NSString stringWithFormat:@"%@",[[self documentsFolder] stringByAppendingPathComponent:M3U_FILE]];
    if([[NSFileManager defaultManager] createFileAtPath:path contents:nil attributes:nil])
    {
      NSFileHandle* outFile = [NSFileHandle fileHandleForWritingAtPath:path];
        if(outFile != nil)
      {
         NSData* buffer = [songURL dataUsingEncoding:NSUTF8StringEncoding];
         [outFile writeData:buffer];
         return path;
      }
   }
    return nil;
}


- (void)createStreamer
{
    // Remove any previous references.
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    // Create a new player.
    NSString* fileURL = [self createURLFile:self.aSong.songpath];
    self.songPlayer = [[AVPlayer alloc]initWithURL:[NSURL fileURLWithPath:fileURL]];
    NSAssert(self.songPlayer != nil, @"NIL AVPlayer Created!!!");
    // Observer for when the song ends...
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(playerItemDidReachEnd:)
                                                 name:AVPlayerItemDidPlayToEndTimeNotification
                                               object:[self.songPlayer currentItem]];
    [[UIApplication sharedApplication] setIdleTimerDisabled: YES];
}
我将.mp3文件的url存储在本地.m3u文件中,并使用该文件加载AVPlayer。在iOS的早期版本中,我被告知AVPlayer将首先加载歌曲,然后播放,而不是立即流式播放。虽然这在iOS 6/7中似乎不是真的(歌曲几乎立即开始流式播放),但正在创建.m3u文件,以防不这样做会产生任何问题

有了它,一个环路就可以监控AVPlayer的状态,几秒钟后,音频就可以毫无问题地在手机上播放了

出于测试目的,我在播放歌曲的页面上设置了MPVolumeView:

MPVolumeView *volumeView = [[[MPVolumeView alloc] initWithFrame:CGRectMake(0, 0, 310, 20)] autorelease];
volumeView.center = CGPointMake(160,62);
[volumeView sizeToFit];
[self.view addSubview:volumeView];
原因是,如果蓝牙作为音频输出源连接,音量滑块也会显示一个指示灯,并允许我更改手机和蓝牙设备之间的音频路由。到目前为止,一切顺利

我通过蓝牙将手机连接到我的Jawbox Jambone,启动歌曲的AVPlayer,歌曲就如预期的那样从Jawbox中传出。音量控制装置有一个小的“带箭头的矩形”,表示我可以切换音频输出,事实上,在播放歌曲时,我可以在手机和颌骨盒之间切换。

当我试图将它连接到汽车上时,问题就出现了。我在这方面有两个经验:

  • 汽车已与手机配对,用于拨打/接听电话。当我上车时,手机甚至显示它已配对。但是,当我使用相同的代码播放相同的音频文件时,它们只能从手机中发出。音量滑块根本不显示“bluetooth route”(蓝牙路线)指示灯(就像它不将汽车识别为音频输出路线一样)
  • 在另一辆车上,音频是从另一个应用程序(一些广播流应用程序)传输的。另一个应用程序已停止,而此应用程序已启动。音频开始播放上面测试的同一首歌曲,但在一两秒钟后停止。同样,音量滑块上没有显示此时已连接蓝牙
  • 有人能给我解释一下为什么音频可以很好地流到一个蓝牙设备而不能流到另一个设备吗


    我的应用程序配置文件中是否遗漏了允许通过蓝牙向汽车传输音频的内容(授权?)

    我非常确定MPVolumeView只能处理符合较新低功耗蓝牙规范的蓝牙设备。。。(蓝牙低能量或BLE)

    我知道手机应用程序不使用MPVolumeView,可能其他音频播放器也不使用。。您可能需要研究CoreBluetooth并实现自己的:(祝您好运。github上可能有一个解决方案。

    GIT上有一个项目。 Play iOS项目是一款在iPhone/iPad上运行的流媒体播放客户端。它支持后台音频以及媒体键。 它支持:

    • 溪流
    • 显示当前正在播放的曲目
    • 背景音频
    • 锁屏相册艺术与播放控件
    • AirPlay(以及蓝牙)流媒体。支持发送元数据 和专辑艺术
    你可以下载这个项目。
    我还没有在车上测试过这个蓝牙音频播放器。希望它能对您有所帮助。

    设计成扬声器的蓝牙扬声器不会有问题

    然而,汽车通常是“电话”蓝牙扬声器,只接受“电话”类型的通信

    我的猜测是,你必须设置一个“电话音频”连接,将传入的音频传输到void,并将传出的音乐流作为电话信号来欺骗它


    请注意,信号质量可能会降低,而且可能不会有解决办法。

    在第一个示例中,您的汽车可能只是一个远程播放器。您需要注册这样的远程事件(考虑使用AVAudioPlayer,而不是AVPlayer)

    设置音频会话以识别蓝牙音频路由:

    - (BOOL)prepareAudioSession {
    
        // deactivate existing session
    NSError *setCategoryError = nil;
    NSError *activationError = nil;
    
    BOOL success = [[AVAudioSession sharedInstance] setActive:NO error: nil];
    if (!success) {
        NSLog(@"deactivationError");
    }
    
        // set audio session category AVAudioSessionCategoryPlayAndRecord options AVAudioSessionCategoryOptionAllowBluetooth
    success = [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionAllowBluetooth error:&setCategoryError];
    if (!success)
    {
        NSLog(@"setCategoryError %@",setCategoryError);
    }
    
        // activate audio session
    success = [[AVAudioSession sharedInstance] setActive:YES error: &activationError];
    if (!success) {
        NSLog(@"activationError");
    }
    
    return success;
    
    }

    您可以查看以下路线:

     AVAudioSessionRouteDescription *mAVASRD = audioSession.currentRoute;
    NSLog(@"the array is %@",mAVASRD.outputs);
    
    for (int ctr = 0; ctr < [mAVASRD.outputs count]; ctr++)
    {
        AVAudioSessionPortDescription *myPortDescription = [mAVASRD.outputs objectAtIndex:ctr];
        NSLog(@"the type is %@",myPortDescription.portType);
        NSLog(@"the name is %@",myPortDescription.portName);
        NSLog(@"the UID is %@",myPortDescription.UID);
        NSLog(@"the data sources are %@",myPortDescription.dataSources);
    }
    
    avaudiosessionrouteddescription*mAVASRD=audioSession.currentRoute;
    NSLog(@“数组为%@”,mAVASRD.outputs);
    对于(int ctr=0;ctr<[mAVASRD.outputs count];ctr++)
    {
    AVAudioSessionPortDescription*myPortDescription=[mAVASRD.outputs objectAtIndex:ctr];
    NSLog(@“类型为%@”,myPortDescription.portType);
    NSLog(@“名称为%@”,myPortDescription.portName);
    NSLog(@“UID是%@”,myPortDescription.UID);
    NSLog(@“数据源为%@”,myPortDescription.dataSources);
    }
    
    然后初始化AVAudioPlayer并打开RemoteControlEvents(您可以使用车上的控制台发送播放/暂停/等)

    [[UIApplication sharedApplication]开始接收RemoteControlEvents];

    然后在堆栈溢出问题中实现类似于AVAudioPlayer的委托方法的方法,以捕获接收到的事件,并在代码中做出相应的反应:

    在场景2中,当您将一个应用程序移到后台(无线流应用程序)并启动应用程序时,问题的原因可能与此相同-您的应用程序必须识别音频的蓝牙路由

    顺便说一下,对于电话和Siri,iOS使用了不同的蓝牙通道,这是遥控器的默认设置(这是我为您的汽车描述的)

    当您设置此路由和远程控制事件时,您还可以获得一个额外的副产品-您的应用程序将可以从锁定屏幕上进行控制。请查看此Apple技术说明,将您的应用程序配置为在后台播放,如果这也是您在屏幕锁定时需要执行的操作:技术QA文档QA1668

    最后,通过您添加集成