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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 20:02:31