You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 02:03:21