我正在尝试下载&播放远程mp3。
基本上:
一切都运行得很好 - 下载是由AFHTTPRequestOperation
执行的,它将mp3直接下载到文件中,每次 1000字节左右都会获得进度更新。
问题是我不确切知道何时开始播放
AVAudioPlayer
。
AVAudioPlayer
可播放未完成的mp3,但如果没有足够的数据,则会触发 fatalError(EXC_BREAKPOINT)并且我的应用崩溃
- 它甚至没有返回错误。
我事先知道mp3的持续时间,我可以从中确定约。像 - (sizeDownloaded/totalSize)*duration
那样下载了多少秒的播放,但这只是愚蠢的,有时会失败(我猜这是因为文件开头有ID3标签,它可以包含巨大的图像我的近似值毫无意义)。
我目前的代码:
op.setDownloadProgressBlock({bytesRead, totalBytesRead, totalBytesExpectedToRead in
let approxBytesForOneSecond = totalBytesExpectedToRead/Int64(downloadableItem.length)
let approxSecondsDownloaded = totalBytesRead/approxBytesForOneSecond
if approxSecondsDownloaded > kMinimumSecondsDownloadedForPlay { //min. secs = 5
var error: NSError?
self.player = AVAudioPlayer(contentsOfURL: url, error: &error) // This is the line where I sometimes get EXC_Breakpoint. The reason for this is that the mp3 is not playable yet ...
if (error != nil) {
// Error initializing item - never called
} else {
// Item initialized OK -> Play
}
}
})
那么,是否有一些可靠的方法来确定文件是否可播放?
我知道提高播放开始的限制会有所帮助,它似乎并不合适。并且“等到下载完成”不是答案...... :)
非常感谢!
答案 0 :(得分:1)
知道mp3文件的比特率可以让你估计你需要多少文件才能安全地开始播放,但是谁知道如果文件在打开后文件发生变化,AVAudioPlayer
将会做什么?
如果您在完成播放后不需要保留mp3,则可以从AVAudioPlayer
(只能播放本地文件)切换到AVPlayer
,这也可以播放流式传输的文件来自互联网:
var url = NSURL(string:"http://YOUR_URL")
player = AVPlayer(URL:url)
player.play()
只要有足够的数据可用于持续播放,它就会播放。
如果您想保留AVPlayer
正在播放的流式字节,事情会变得更复杂,但可以通过this blog post
AVAssetResourceLoader
来实现
<强>更新强>
如果要在后台下载多个文件,请执行此操作!您仍然可以通过AVPlayer
将数据移至AVAssetResourceLoader
,AVPlayer
将决定何时有足够的数据进行播放(您可以播放的是什么意思?)
答案 1 :(得分:1)
我明白了。如果文件不可播放(无论出于何种原因),此函数将返回错误。在我的测试中,对于完全没有音频数据的不完整文件,它成功返回false,如果至少有一些东西要播放,则为true(无论文件是否完整)。在我看来,这与AVAudioPlayer / AVPlayer使用的功能相同。它在Obj-C中我并没有迅速重写它,但我不确定它是否会被泄漏...
+ (BOOL) isFilePlayable:(NSURL*)url {
// Try opening audiofile -> if it's playable it will open, if not, it will return error
AudioFileID audioFileID;;
OSErr err;
err = AudioFileOpenURL((__bridge CFURLRef)url,
kAudioFileReadPermission,
0,
&audioFileID);
if (err != noErr) {
NSLog(@"Couldn't open audio file...");
return NO;
}
AudioFileClose(audioFileID);
return YES;
}