iOS中AVPlayer对应ExoPlayer的BandwidthMeter是什么?如何获取下载速率?
AVPlayer中类似ExoPlayer BandwidthMeter的下载速率估算方案
嘿,作为同时踩过ExoPlayer和AVPlayer坑的开发者,我太懂你想找直接等价组件的心情了——可惜AVFoundation并没有像BandwidthMeter那样封装好的现成工具,但我们可以用AVFoundation提供的API自己实现类似的实时/历史下载速率统计功能,下面给你拆解两种实用方案:
一、实时速率估算:用AVAssetResourceLoader拦截请求统计
这是最接近ExoPlayer BandwidthMeter实时监测逻辑的方案,通过拦截AVPlayer的资源加载请求,手动统计下载字节数和耗时,计算实时速率:
- 核心思路:给AVAsset设置
resourceLoader代理,在每个资源请求的生命周期里记录开始时间、累计下载字节数,动态计算速率 - 优势:能拿到实时的下载速率,适合做码流自适应、加载状态展示等场景
- 简单实现示例:
class BandwidthMonitor: NSObject, AVAssetResourceLoaderDelegate { private var activeRequests: [UUID: (startTime: Date, totalBytes: Int64)] = [:] func resourceLoader(_ resourceLoader: AVAssetResourceLoader, shouldWaitForLoadingOfRequestedResource loadingRequest: AVAssetResourceLoadingRequest) -> Bool { let requestID = UUID() activeRequests[requestID] = (startTime: Date(), totalBytes: 0) // 发起实际网络请求 let task = URLSession.shared.dataTask(with: loadingRequest.request) { [weak self] data, response, error in guard let self = self, let data = data else { loadingRequest.finishLoading(with: error) self?.activeRequests.removeValue(forKey: requestID) return } // 更新累计字节数 var requestStats = self.activeRequests[requestID]! requestStats.totalBytes += Int64(data.count) self.activeRequests[requestID] = requestStats // 计算实时速率(转换为MB/s) let elapsed = Date().timeIntervalSince(requestStats.startTime) let speedMBps = Double(requestStats.totalBytes) / elapsed / 1024 / 1024 print("当前请求实时速率:\(String(format: "%.2f", speedMBps)) MB/s") // 将数据返回给AVPlayer loadingRequest.dataRequest?.respond(with: data) loadingRequest.finishLoading() self.activeRequests.removeValue(forKey: requestID) } task.resume() return true } } // 使用方式 let asset = AVURLAsset(url: yourVideoURL) let monitor = BandwidthMonitor() asset.resourceLoader.setDelegate(monitor, queue: DispatchQueue.global()) let playerItem = AVPlayerItem(asset: asset) let player = AVPlayer(playerItem: playerItem)
二、历史速率统计:用AVPlayerItem的访问日志
如果只需要平均下载速率或者历史加载数据,可以直接用AVPlayerItem自带的访问日志获取官方统计的信息:
- 核心思路:通过
playerItem.accessLog()拿到AVPlayerItemAccessLog,从中提取numberOfBytesTransferred(总传输字节数)和durationWatched(观看时长)等字段计算平均速率 - 优势:无需额外拦截请求,代码更简洁;但只能拿到历史统计,无法实时监测
- 示例代码:
// 每秒更新一次平均速率 Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak player] _ in guard let player = player, let currentItem = player.currentItem else { return } guard let accessLog = currentItem.accessLog() else { return } let totalBytes = accessLog.events.reduce(0) { $0 + $1.numberOfBytesTransferred } let totalDuration = accessLog.events.reduce(0.0) { $0 + $1.durationWatched } if totalDuration > 0 { let avgSpeedMBps = Double(totalBytes) / totalDuration / 1024 / 1024 print("平均下载速率:\(String(format: "%.2f", avgSpeedMBps)) MB/s") } }
总结对比ExoPlayer的BandwidthMeter
ExoPlayer的BandwidthMeter本质也是基于请求拦截和字节统计实现的,只是做了更完善的封装(比如滑动窗口过滤瞬时波动、多请求合并统计等)。如果你需要更精准的实时速率,可以在AVAssetResourceLoader的基础上,给统计逻辑加上滑动窗口(比如只统计最近3秒的下载数据),这样能避免速率的剧烈波动。
内容的提问来源于stack exchange,提问作者Matan Marciano
相关产品推荐
相关产品推荐

