
您考虑过多少次下载正在收听的FM流? 还是您试图找到一种解决方案,该方案如何下载没有确定持续时间并可以在Internet上播放的实时音频流? 好吧,我们一起将在本文中找到解决方案。
现在开始真正的交易,我们将遵循一些步骤,我们将解决在完成此任务时出现的问题。

- 当我们使用无限音频URL初始化
AVPlayer
时,它将开始播放它。 但是,如果您尝试从AVPlayer
对象中获取AVAssetTrack
,则会得到一个空数组。 - 现在,如果您没有音轨,那么究竟如何捕获它并下载音轨? 您不能使用
MTAudioProcessingTapRef
轻按并下载音频轨道。 - 可以想到使用
InputStream
来简单地使用URL初始化流并读取其字节。 但是……这又是不可能的,因为一旦您尝试读取缓冲区,InputStream
就会遇到结尾。
但是,请稍等,为什么我一直在告诉您我们将如何失败……然后应以不同的方式命名本文,您不能下载实时音频流,但不是! 所以,现在我们将探索我们的解决方案……准备吧!

- 从要播放的位置准备好URL并下载音频流:
让 url = URL(字符串:“您的网址”)!
2.现在从该URL创建一个AVPlayerItem
:
playerItem = CachingPlayerItem(URL:URL,recordingName:recordingName ??“ default.mp3”)//// CachingPlayerItem是一个自定义类,是AVPlayerItem的子类。
使用url初始化CachingPlayerItem
对象将根据给定的url准备一个AVURLAsset
。 初始化CachingPlayerItem
整个初始化函数如下:
初始化 (网址:URL,customFileExtension:字符串?,录音名称:字符串) (“不支持不使用方案的网址”)} self .recordingName = recordingName self .url = url self .initialScheme = scheme 如果 让 ext = customFileExtension {urlWithCustomScheme.deletePathExtension()urlWithCustomScheme.appendPathExtension(ext) self .customFileExtension = ext} 让资产= AVURLAsset(URL:urlWithCustomScheme)asset.resourceLoader.setDelegate(resourceLoaderDelegate,队列:DispatchQueue.main) 超级 .init(资产:资产,automaticLoadedAssetKeys: nil )resourceLoaderDelegate.owner = self addObserver( self ,forKeyPath:“ status”,选项:NSKeyValueObservingOptions.new,上下文: nil )NotificationCenter.default.addObserver( self ,选择器: #selector (playbackStalledHandler),名称:NSNotification.Name.AVPlayerIte mPlaybackStalled,对象: self )}
3.初始化AVPlayerItem
之后,从中创建一个AVPlayer
对象,以便它可以启动从给定url加载音频流的过程:
player = AVPlayer(playerItem:playerItem)player.automaticallyWaitsToMinimizeStalling = false
如果您查看init方法,则设置asset.resourceLoader
的委托,因此将调用以下AVAssetResourceLoaderDelegate
函数:
func resourceLoader( _ resourceLoader:AVAssetResourceLoader,shouldWaitForLoadingOfRequestedResource loadingRequest:AVAssetResourceLoadingRequest)->布尔{ 如果 playFromFromData { //没有要加载的内容。 } else if session == nil { //如果我们正在播放URL,则需要下载文件。 //我们仅在第一次请求时才开始加载文件。 警卫队 让 initialUrl =所有者?.url 其他 {fatalError(“内部不一致”)} startDataRequest(使用:initialUrl)} pendingRequests.insert(loadingRequest)processPendingRequests() 返回 true }
一旦资源加载开始,我们就调用func startDataRequest(with url: URL)
方法。
func startDataRequest(带有url:URL){ var recordingName =“ default.mp3” 如果 让 recording =所有者?。recordingName{recordingName =记录} fileURL = 尝试 ! FileManager.default.url(for:.documentDirectory,in:.userDomainMask,properFor: nil ,create: false ).appendingPathComponent(recordingName) let配置= URLSessionConfiguration.defaultconfiguration.requestCachePolicy = .reloadIgnoringLocalAndRemoteCacheDatasession = URLSession(配置:配置,委托: 自我 : ,proxyQueue: nil )session?.dataTask(with:url).resume()outputStream = OutputStream(url:fileURL,append: true )outputStream?.schedule(in:RunLoop.current,forMode:RunLoopMode.defaultRunLoopMode)outputStream ?.打开()}
现在,当我们使用给定的URL启动dataTask
时,我们将开始将数据字节接收到委托函数中:
func urlSession( _ session:URLSession,dataTask:URLSessionDataTask,didReceive数据:Data)
4.现在我们开始接收实时音频流…😰。 最后一个难题是存储音频流。 这是最简单的部分。 我们只是创建一个OutputStream
对象,将其打开,然后在上面的委托函数中附加要接收的字节,就这样,我们保存了实时音频流的所需部分。
func urlSession( _ session:URLSession,dataTask:URLSessionDataTask,didReceive data:Data){ 让 bytesWritten = data.withUnsafeBytes {outputStream?.write($ 0,maxLength:data.count)} print(“写入的字节数:\(bytesWritten!)到\(fileURL)“)}
我们已经成功下载了实时音频流的所需部分,该部分没有确定的长度通过Internet进行。
最终产品看起来像这样:

您可以在我的GitHub上找到源代码。 谢谢阅读!
在社交媒体平台上关注我们: Facebook:
facebook.com/AppCodamobile/Twitter:
twitter.com/AppCodaMobileInstagram:
instagram.com/AppCodadotcom