使用Swift在iOS中下载持续时间不确定的实时音频流

您考虑过多少次下载正在收听的FM流? 还是您试图找到一种解决方案,该方案如何下载没有确定持续时间并可以在Internet上播放的实时音频流? 好吧,我们一起将在本文中找到解决方案。

现在开始真正的交易,我们将遵循一些步骤,我们将解决在完成此任务时出现的问题。

  1. 当我们使用无限音频URL初始化AVPlayer时,它将开始播放它。 但是,如果您尝试从AVPlayer对象中获取AVAssetTrack ,则会得到一个空数组。
  2. 现在,如果您没有音轨,那么究竟如何捕获它并下载音轨? 您不能使用MTAudioProcessingTapRef轻按并下载音频轨道。
  3. 可以想到使用InputStream来简单地使用URL初始化流并读取其字节。 但是……这又是不可能的,因为一旦您尝试读取缓冲区, InputStream就会遇到结尾。

但是,请稍等,为什么我一直在告诉您我们将如何失败……然后应以不同的方式命名本文,您不能下载实时音频流,但不是! 所以,现在我们将探索我们的解决方案……准备吧!

  1. 从要播放的位置准备好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

如果您喜欢这篇文章,请单击👏按钮并分享以帮助其他人找到它! 随时在下面发表评论。