iOS Audio Streaming仅适用于**某些**蓝牙设备?

时间:2014-01-18 12:00:09

标签: ios iphone audio bluetooth streaming

我正在开发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中传出。音量控制有一个小的“带箭头的矩形”表示我可以切换音频输出,实际上,在播放歌曲时,我可以在手机和Jawbox之间切换。 幸福。

当我尝试将其连接到汽车时会出现问题。我有两个经验:

  1. 该车已与手机配对以拨打/接听电话。当我上车时,手机甚至表示它已配对。但是,当我使用相同的代码播放相同的音频文件时,它们只能从手机中取出。音量滑块根本不显示“蓝牙路线”指示器(就像它不能将汽车识别为音频输出路径一样)。
  2. 在另一辆车中,音频从另一个应用程序(一些无线电流媒体应用程序)流式传输。另一个应用程序停止了,这个开始了。音频开始播放上面测试的同一首歌,但在一两秒后停止播放。同样,此时蓝牙连接的音量滑块上没有指示灯。
  3. 有人可以向我解释为什么音频可以流式传输到一个蓝牙设备而不是另一个蓝牙设备吗?

    我的应用程序配置文件中是否遗漏了某些内容(权利?),以便通过蓝牙将音频流式传输到汽车上?

4 个答案:

答案 0 :(得分:3)

GIT有this项目。 Play iOS项目是Play的流媒体客户端,可在iPhone / iPad上运行。它支持背景音频以及背景时的媒体键。 它支持:

  • Streams shoutcast stream
  • 显示当前正在播放的曲目
  • 背景音频
  • 锁屏专辑封面&播放控件
  • AirPlay(连同蓝牙)流式传输。支持发送元数据 和专辑艺术

您可以下载项目here。 我没有在CAR蓝牙音频播放器上测试过这个。希望它对你有任何帮助。

答案 1 :(得分:2)

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

设置AudioSession以识别蓝牙音频路径:

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

然后初始化AVAudioPlayer并启用RemoteControlEvents(您可以使用汽车中的控制台发送播放/暂停/等)

[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];

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

AVAudioPlayer on Lock Screen

在方案2中,当您将一个应用程序移动到后台(无线电流媒体应用程序)并启动您的应用程序时,问题的可能原因是同样的原因 - 您的应用程序必须识别音频的蓝牙路由。 / p>

顺便打电话和Siri,iOS使用不同的蓝牙通道,默认为遥控器(这是我为你的车描述的那个)。

当您设置此路线和遥控事件时,您还会获得奖励副产品 - 您的应用程序将可以从锁定屏幕进行控制。查看Apple的此技术说明,将您的应用配置为在后台播放,如果这也是您在屏幕锁定时需要执行的操作:技术质量保证文档QA1668

最后,为了通过蓝牙路线增加集成,请查看MPNowPlayingInfoCenter - 将标题艺术家的作品和其他好东西放在锁定屏幕和汽车中显示该信息的大多数蓝牙屏幕上。

答案 2 :(得分:0)

我很确定MPVolumeView只能解决符合较新的低功耗蓝牙规格的蓝牙设备......(蓝牙低功耗或BLE)......

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

答案 3 :(得分:0)

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

然而,汽车通常是“手机”蓝牙音箱,只会接受“手机”类型的通讯。

我的猜测是,您必须通过设置“手机音频”连接并将传入的音频传输到空白,并将传出的音乐流作为电话信号来欺骗它。

请注意,信号质量可能会降低,并且可能无法解决此问题。