2014-01-18 57 views
6

我正在开发iOS应用程序,它将与iOS 6/7兼容并从网站流式传输音频.mp3文件。iOS音频流仅适用于**某些**蓝牙设备?

已经获得这样的使用下面的代码工作:

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

我存储在本地m3u格式文件.MP3文件的URL并用它来加载了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. 在另一辆车中,音频是从另一个应用程序(某些无线电流应用程序)流式传输的。另一个应用程序已停止,并开始了这一个。音频开始播放上面测试过的同一首歌曲,但在一两秒钟后停止播放。同样,此时蓝牙连接的音量滑块上没有指示符。

有人可以向我解释为什么音频可以流出到一个蓝牙设备而不是另一个?

我在配置文件中遗漏了什么(权利?),可以让它通过蓝牙将音频流式传输到汽车上?

回答

0

我敢肯定MPVolumeView只能解决其符合新的低功耗蓝牙规格...(蓝牙低能量或BLE)蓝牙设备...

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

3

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

  • 流Shoutcast的流
  • 显示当前播放的曲目
  • 背景音频
  • 锁屏的专辑封面&播放控制
  • 的AirPlay流媒体(带蓝牙一起)。支持发送元数据 和专辑封面

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

+0

我会检查这一个出来......谢谢。 – FuzzyBunnySlippers

0

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

但是,汽车通常是一个“电话”蓝牙扬声器,只会接受“电话”类型的通信。

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

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

+0

我已经看到类似这样的解决方案,总是与降级音频的警告。我不认为这会是一个好的解决方案......但感谢您的反馈。 – FuzzyBunnySlippers

+0

是的,但请记住,您必须处理汽车的局限性。 大多数汽车都会通过汽车扬声器设置蓝牙,甚至可以在通话进行时使音乐静音,但是大多数汽车并非设计用于使用音频信号,而是使用电话信号。 您将不得不针对已购买带蓝牙播放功能的汽车收音机的观众。 – Tschallacka

+1

问题在于,当他们在手机上收听时,他们会听到高质量的音频。因此,通过这种方式,他们可以通过蓝牙在手机上获得“良好的音频”,在汽车中获得“糟糕的音频”。当他们可以插入一个MP3播放器,并获得良好的音频(可能来自他们的iPhone)。所以如果我不得不在没有蓝牙音频和坏音频之间进行选择,他们会将其与“良好的音频”进行比较,我宁愿它是“没有蓝牙音频”而不是“音质不好”的投诉流。 – FuzzyBunnySlippers

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(你可以使用console在你的车送戏/ p澳洲英语的/ etc)

[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];

然后实现像在这个堆栈溢出问题捕获接收事件AVAudioPlayer委托方法并在代码中做出相应的反应:

AVAudioPlayer on Lock Screen

在场景2,当您将一个应用移动到背景(无线电流应用)并启动您的应用时,问题的可能罪魁祸首是同样的原因 - 您的应用必须识别用于音频的蓝牙路由。

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

当您设置此路线和远程控制事件时,您也会获得额外的副产品 - 您的应用程序将从锁定屏幕控制。请查看Apple的技术说明,将您的应用配置为在后台播放,也可以在屏幕锁定时执行以下操作:技术质量检查文档QA1668

最后,为了通过蓝牙路由增加集成度,看看MPNowPlayingInfoCenter--把标题艺术家的作品和其他好东西放在车载显示屏上的锁屏和大多数蓝牙屏幕上。

+0

我也建议你研究AV要求以处理路线变化以及路线通知。你的应用会感谢你。 – thebdog