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

AVPlayer播放错误:FireStorage缓存视频偶发播放非目标内容问题

问题根因定位

这种偶现的播放错误缓存视频的问题,通常是以下几个原因导致的:

  1. 不同视频的本地缓存路径重复,导致新下载的视频覆盖了旧的缓存文件
  2. 后台并发下载未做线程控制,多个写入任务同时操作同一个文件导致内容错乱
  3. 播放逻辑同时使用自定义缓存组件和原生AVAsset,出现缓存资源复用错误
  4. 页面复用/旧播放实例未销毁,导致前一个视频的播放资源被复用

修复方案

  • 1. 确保本地缓存路径唯一

    检查filePathURL(for:)方法的实现,不要只用imageFileName作为唯一标识,拼接Exercise的唯一ID避免重名,示例修改:
    func filePathURL(for exercise: Exercise) -> URL? {
        let fileName = "\(exercise.id)_\(exercise.imageFileName)" // 拼接业务唯一ID避免重名
        let documentsDir = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
        return URL(fileURLWithPath: documentsDir).appendingPathComponent(fileName)
    }
    
  • 2. 下载队列改为串行避免并发写冲突

    你当前使用全局并发队列执行下载,同一时间多个下载任务同时写文件会导致内容错乱,自定义串行下载队列替换全局队列:
    // 全局定义串行下载队列
    private let videoDownloadQueue = DispatchQueue(label: "com.yourApp.videoDownloadSerialQueue", qos: .background)
    
    // 修改getFileWith里的队列调用
    func getFileWith(with url: URL, saveTo saveFilePathURL: URL) {
        videoDownloadQueue.async { // 替换原来的DispatchQueue.global().async
            print(saveFilePathURL.path)
            if let videoData = NSData(contentsOf: url) {
                try? videoData.write(to: saveFilePathURL, options: .atomic)
            } else {
                DispatchQueue.main.async {
                    let error = NSError(domain: "SomeErrorDomain", code: -2001, userInfo: ["description": "Can't download video"])
                    print(error.debugDescription)
                }
            }
        }
    }
    
  • 3. 修复播放逻辑的资源冲突

    你当前的播放代码同时初始化了CachingPlayerItem和原生AVPlayerItem,两个实例会同时读取同一资源,容易出现缓存冲突,直接用CachingPlayerItem初始化播放器,同时优先读取本地缓存文件:
    func startPlayingVideoOnDemand(url : URL, exercise: Exercise? = nil) {
        activityIndicatorView.startAnimating()
        activityIndicatorView.isHidden = false
        // 优先使用本地缓存文件url
        var targetUrl = url
        if let exercise = exercise, let localPath = filePathURL(for: exercise), FileManager.default.fileExists(atPath: localPath.path) {
            targetUrl = localPath
        }
        print(targetUrl)
        let cachingPlayerItem = CachingPlayerItem(url: targetUrl)
        cachingPlayerItem.delegate = self
        cachingPlayerItem.download()
        let player = AVPlayer(playerItem: cachingPlayerItem)
        player.automaticallyWaitsToMinimizeStalling = false
        // 播放前先销毁旧的播放器实例避免资源复用
        if let existingPlayer = self.player {
            existingPlayer.replaceCurrentItem(with: nil)
        }
        self.player = player
        initializeVideoLayer(for: player)
    }
    
  • 4. 新增缓存有效性校验

    下载完成后比对本地文件大小和服务器返回的文件大小,不一致则删除损坏缓存,避免读取错误内容:可以在写入文件前通过URLSession的dataTask获取响应头的Content-Length,写入完成后读取本地文件大小做比对,校验失败就删除本地文件触发重新下载。

内容的提问来源于stack exchange,提问作者user9261559

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 02:45:10