检查mp3是否可以在swift中播放

时间:2023-01-24 11:51:32

I'm trying to download & play remote mp3s.

我正在下载和播放远程mp3。

Basically:

基本上:

  1. User clicks the mp3,
  2. 用户单击mp3,
  3. Download starts on background
  4. 下载从背景开始
  5. and then, when it's possible, the playback starts.
  6. 然后,如果可能的话,回放就开始了。

Everything works almost fine - the download is performed by AFHTTPRequestOperation which is downloading the mp3 directly into a file and I get progress updates every 1000 Bytes or so.

一切都很好——下载是由AFHTTPRequestOperation执行的,AFHTTPRequestOperation直接将mp3下载到一个文件中,我每1000个字节就会得到进度更新。

The problem is I don't know exactly when to start playback via AVAudioPlayer.

问题是我不知道什么时候可以通过AVAudioPlayer开始回放。

AVAudioPlayer can play unfinished mp3, but if there's not enough data it fires fatalError (EXC_BREAKPOINT) and my app crashes

AVAudioPlayer可以播放未完成的mp3,但是如果没有足够的数据,它会触发fatalError (EXC_BREAKPOINT)和我的应用程序崩溃。

  • it doesn't even return an error.
  • 它甚至不会返回错误。

I know in advance the duration of the mp3 from which I can determine approx. how many seconds of playback are downloaded like - (sizeDownloaded/totalSize)*duration, but that's just silly and sometimes it fails (I'm guessing thanks to the fact that there's ID3 Tag in the beginning of the file and it can contain huge images my approximation is pointless).

我事先知道mp3的播放时间,这样我就可以知道大概的播放时间了。下载回放的时间有多少秒,比如- (sizeDownloaded/totalSize)*持续时间,但这很愚蠢,有时会失败(我猜,由于文件开头有ID3标记,它可以包含巨大的图像,我的估计是没有意义的)。

My current code:

我现在的代码:

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

So, is there's some solid way of finding out whether the file is playable or not?

那么,是否有可靠的方法来确定文件是否可播放?

I know that raising the limit for playback start would help, it just doesn't seem to be right. And "wait until download is finished" is not an answer... :)

我知道提高回放的极限会有帮助,但这似乎是不对的。“等到下载完成”不是答案……:)

Thanks a lot!

谢谢!

2 个解决方案

#1


1  

Knowing the bitrate of the mp3 file would let you estimate how much of the file you need to be able to safely begin playback, BUT who knows what AVAudioPlayer will do if its file changes after it has opened it?

知道mp3文件的比特率可以让您估计需要多少文件才能安全地开始回放,但是谁知道AVAudioPlayer在打开文件后会做什么呢?

If you don't need to keep the mp3 once you've finished playing it, you could switch from AVAudioPlayer, which can only play local files, to AVPlayer which can also play files streamed from the internet:

如果你在播放完mp3后不需要保存,你可以从只能播放本地文件的AVAudioPlayer切换到可以播放来自互联网的文件的AVPlayer:

 var url = NSURL(string:"http://YOUR_URL")
 player = AVPlayer(URL:url)
 player.play()

It will play as soon as enough data is available for sustained playback.

只要有足够的数据可以持续播放,它就会播放。

If you want to keep the streamed bytes that AVPlayer is playing, things get more complicated, but it can be achieved via AVAssetResourceLoader as detailed in this blog post

如果你想要保持AVPlayer播放的流数据,事情会变得更复杂,但是可以通过AVAssetResourceLoader实现,详情请见本文

UPDATE If you want to download multiple files in the background, do it! You can still hand the data off to AVPlayer via AVAssetResourceLoader and AVPlayer will decide when it has enough data for playback (is that what you mean by playable?)

如果你想在后台下载多个文件,请进行更新!你仍然可以通过AVAssetResourceLoader将数据移交给AVPlayer, AVPlayer将决定何时有足够的数据进行回放(这就是你所说的可播放的意思吗?)

#2


1  

I figured it out. This function returns error, if file is not playable (for whatever reason). In my tests it successfully returned false for incomplete files with absolutely no audio data, and true if there was at least something to play (no matter if the file wasn't complete). In my opinion this is the same function that is being used by AVAudioPlayer/AVPlayer. It's in Obj-C and i haven't rewrote it in swift yet as I wasn't sure if it won't be leaky...

我想出来。如果文件不能播放(无论出于什么原因),此函数将返回错误。在我的测试中,它成功地为完全没有音频数据的不完整文件返回false,如果至少有播放内容(无论文件是否不完整)返回true。在我看来,这和AVAudioPlayer/AVPlayer使用的功能是一样的。它在object - c中,我还没有用swift重写,因为我不确定它是否会泄露……

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

#1


1  

Knowing the bitrate of the mp3 file would let you estimate how much of the file you need to be able to safely begin playback, BUT who knows what AVAudioPlayer will do if its file changes after it has opened it?

知道mp3文件的比特率可以让您估计需要多少文件才能安全地开始回放,但是谁知道AVAudioPlayer在打开文件后会做什么呢?

If you don't need to keep the mp3 once you've finished playing it, you could switch from AVAudioPlayer, which can only play local files, to AVPlayer which can also play files streamed from the internet:

如果你在播放完mp3后不需要保存,你可以从只能播放本地文件的AVAudioPlayer切换到可以播放来自互联网的文件的AVPlayer:

 var url = NSURL(string:"http://YOUR_URL")
 player = AVPlayer(URL:url)
 player.play()

It will play as soon as enough data is available for sustained playback.

只要有足够的数据可以持续播放,它就会播放。

If you want to keep the streamed bytes that AVPlayer is playing, things get more complicated, but it can be achieved via AVAssetResourceLoader as detailed in this blog post

如果你想要保持AVPlayer播放的流数据,事情会变得更复杂,但是可以通过AVAssetResourceLoader实现,详情请见本文

UPDATE If you want to download multiple files in the background, do it! You can still hand the data off to AVPlayer via AVAssetResourceLoader and AVPlayer will decide when it has enough data for playback (is that what you mean by playable?)

如果你想在后台下载多个文件,请进行更新!你仍然可以通过AVAssetResourceLoader将数据移交给AVPlayer, AVPlayer将决定何时有足够的数据进行回放(这就是你所说的可播放的意思吗?)

#2


1  

I figured it out. This function returns error, if file is not playable (for whatever reason). In my tests it successfully returned false for incomplete files with absolutely no audio data, and true if there was at least something to play (no matter if the file wasn't complete). In my opinion this is the same function that is being used by AVAudioPlayer/AVPlayer. It's in Obj-C and i haven't rewrote it in swift yet as I wasn't sure if it won't be leaky...

我想出来。如果文件不能播放(无论出于什么原因),此函数将返回错误。在我的测试中,它成功地为完全没有音频数据的不完整文件返回false,如果至少有播放内容(无论文件是否不完整)返回true。在我看来,这和AVAudioPlayer/AVPlayer使用的功能是一样的。它在object - c中,我还没有用swift重写,因为我不确定它是否会泄露……

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