Swift中如何使用AVPlayer播放IPFS加密视频URL?
解决IPFS视频在AVPlayer无法播放但WebView可播放的问题
问题分析
WebView能正常播放是因为浏览器会自动处理HTTP请求头、重定向或加密视频的加载逻辑,而AVPlayer默认不会携带必要的请求头,也不会自动处理某些IPFS网关的特殊响应规则。
解决方案
1. 添加自定义HTTP请求头(核心解决方法)
Moralis的IPFS网关可能需要特定请求头(如Origin、User-Agent)才能返回正确的视频流,通过AVAssetResourceLoader拦截请求并添加头:
步骤1:实现资源加载代理
class ResourceLoaderDelegate: NSObject, AVAssetResourceLoaderDelegate { func resourceLoader(_ resourceLoader: AVAssetResourceLoader, shouldWaitForLoadingOfRequestedResource loadingRequest: AVAssetResourceLoadingRequest) -> Bool { guard let requestURL = loadingRequest.request.url else { return false } // 构建带自定义头的请求 var modifiedRequest = URLRequest(url: requestURL) // 替换为你的App域名,模拟WebView的请求来源 modifiedRequest.addValue("https://your-app-domain.com", forHTTPHeaderField: "Origin") // 匹配WebView的User-Agent,避免网关拦截 modifiedRequest.addValue("Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148", forHTTPHeaderField: "User-Agent") // 发起网络请求并返回数据给AVPlayer let task = URLSession.shared.dataTask(with: modifiedRequest) { data, response, error in defer { loadingRequest.finishLoading() } if let error = error { loadingRequest.finishLoading(with: error) return } guard let httpResponse = response as? HTTPURLResponse, let data = data else { loadingRequest.finishLoading(with: NSError(domain: "ResourceLoaderError", code: -1, userInfo: nil)) return } // 告诉AVPlayer视频的类型和长度 loadingRequest.contentInformationRequest?.contentType = httpResponse.allHeaderFields["Content-Type"] as? String loadingRequest.contentInformationRequest?.contentLength = httpResponse.expectedContentLength loadingRequest.dataRequest?.respond(with: data) } task.resume() return true } }
步骤2:使用代理加载视频
// 初始化资源代理和AVAsset let videoURL = URL(string: "https://ipfs.moralis.io:2053/ipfs/QmUjWnQZVVNmTSVak2QDhTxMkn3dPQozxawa1sm3jE5bLr")! let asset = AVURLAsset(url: videoURL) let resourceDelegate = ResourceLoaderDelegate() asset.resourceLoader.setDelegate(resourceDelegate, queue: DispatchQueue.global()) // 创建PlayerItem和Player let playerItem = AVPlayerItem(asset: asset) playerItem.addObserver(self, forKeyPath: "status", options: [.new], context: nil) let player = AVPlayer(playerItem: playerItem) // 配置PlayerLayer并播放 let playerLayer = AVPlayerLayer(player: player) playerLayer.frame = self.avplayerUIView.bounds self.avplayerUIView.layer.addSublayer(playerLayer) player.play()
2. 完善ATS配置
确保App允许访问非标准HTTPS端口(2053),在Info.plist中添加:
<key>NSAppTransportSecurity</key> <dict> <key>NSExceptionDomains</key> <dict> <key>moralis.io</key> <dict> <key>NSIncludesSubdomains</key> <true/> <key>NSTemporaryExceptionAllowsPorts</key> <array> <string>2053</string> </array> <key>NSTemporaryExceptionMinimumTLSVersion</key> <string>TLSv1.2</string> </dict> </dict> </dict>
3. 调试错误原因
完善KVO监听,获取AVPlayer的具体错误信息:
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { guard keyPath == "status", let item = object as? AVPlayerItem else { super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context) return } switch item.status { case .readyToPlay: print("视频准备完成") case .failed: print("播放失败:\(item.error?.localizedDescription ?? "未知错误")") // 可打印完整错误信息排查 print("错误详情:\(item.error ?? NSError())") case .unknown: print("视频状态未知") @unknown default: break } }
4. 处理DRM加密情况
如果视频是DRM加密的,WebView可能自动集成了DRM插件,而AVPlayer需要配置AVContentKeySession处理解密。可以先通过asset.hasProtectedContent判断是否为加密视频,再添加DRM逻辑。
内容的提问来源于stack exchange,提问作者NickCoder
相关产品推荐
相关产品推荐

