解决腾讯pag不能播放线上资源

前言

PAG 是腾讯多媒体技术委员会下 AVGenerator Oteam开源协同小组自主研发的一套完整的动效工作流解决方案,和业界常用的动效工作流解决方案相比,PAG支持的 AE 特性更多,覆盖的平台更广(Android、iOS、Web、macOS、Windows和Linux),性能方面也做了深层次的优化,支持文本和占位图编辑替换,可以与视频编辑场景紧密结合。

PAG的优势

这里简单总结一下PAG的优势

1. 相同动效相对Lottie体积更小

2. 支持文本和占位图的编辑和替换

目前,绝大部分的视频编辑App,在贴纸字幕这块的实现都分为两种类型,要么贴纸有丰富的动效,但不可编辑;要么可以编辑文本,但只有静态或者简单的动效,大大束缚了设计师,降低了视频的整体观感。而PAG方案让贴纸有精美动效的前提下,还可以保持强大的编辑性,让使用者的个性化元素得到更好的呈现。

3. 支持播放声音

4. 性能更好

基于 C++ 和 OpenGL 硬件加速渲染,除了能做到两端渲染完全一致外,应用了游戏渲染里的大量的优化经验,从中间渲染数据到局部位图的多级缓存架构,每帧渲染耗时平均可以做到Lottie的50%左右。

已应用的部分软件

问题和解决方案

目前官方的libpag库只支持播放本地PAG资源,不支持网络PAG资源,为此这里提供一个可播放网络PAG资源的方案LXYPAGPlayerSDK

核心代码

1. 使用

1.1 创建LXYPAGView

ini 复制代码
 lazy var pagView :LXYPAGView = {
        let pagView = LXYPAGView()
        return pagView
    }()

1.2 播放线上资源

arduino 复制代码
 //MARK: - 播放线上资源
    private func playOnlineResource(){
        let config = LXYPAGConfig()
        config.resourceStr =  "http://xxx.pag"
        pagView.frame = CGRect(x: 0, y: 0, width: 300, height: 300)
        self.pagView.playAnim(config)
    }

2. LXPAGView内部实现

2.1 public func playAnim(_ config: LXYPAGConfig)方法中通过判断config.resourceStr是否以http开头判断是否是网络资源,如果是本地资源则调用 playLocalPathAnim(config)播放。,如果没有则进入网络下载环节

arduino 复制代码
 if config.resourceStr.hasPrefix("http") == false {
           //本地数据
           playLocalPathAnim(config)
           return
       }

2.2 如果传入的config.resourceStr是网络地址,则通过缓存单例LXYPAGCacheManager查询本地是否已经有缓存数据

arduino 复制代码
let cacheKey =  LXYPAGCacheManager.shareInstance.cacheKey(config.resourceStr)
      let cachePath = LXYPAGCacheManager.shareInstance.filepath(cacheKey)
      if cachePath.count > 0 && FileManager.default.fileExists(atPath: cachePath) {
          //本地有则拿本地数据进行播放
          config.resourceStr = cachePath
          playLocalPathAnim(config)
          return
      }

2.3 本地查询没有缓存怎进入下载环节

swift 复制代码
LXYPAGManager.shareInstance.downloader.loadData(config.resourceStr) { locationPath, err in
            if err != nil {
                self.delegate?.playErroronView(self, err)
                return
            }
            guard let locationPath = locationPath else{
                return
            }
            do{
                let targetPath = try LXYPAGCacheManager.shareInstance.moveTempData((locationPath as NSURL).path, cacheKey)
                if let targetPath = targetPath{
                    config.resourceStr = targetPath
                    self.playLocalPathAnim(config)
                }
            }catch let error{
                self.delegate?.playErroronView(self, error as NSError)
            }
        }
        
    }
    

2.4 playLocalPathAnim(_ config: LXYPAGConfig)实现

scss 复制代码
   //MARK: - 播放PAG
    private func playLocalPathAnim(_ config: LXYPAGConfig){
        if isPlaying() {
            stopPlay()
        }
        let path = config.resourceStr
        if path.count == 0 || FileManager.default.fileExists(atPath: path) == false {
            let error = NSError.localResourceNotExist(path)
            delegate?.playErroronView(self, error)
            return
        }
        pagFile = PAGFile.load(path)
        //动态替换资源,未实现
        pagView.setProgress(0)
        customizePlay()
        //播放音效,未实现
        pagView.play()
        //第一次播放时检查缓存大小,如果超出则删除
        if checkLocalCaches {
            LXYPAGCacheManager.shareInstance.removeFolderIfExceedsSize()
            checkLocalCaches = true
        }
    }
    

3. LXYDownloader里的下载逻辑

3.1 根据传入的url去队列中查询该下载路径下的下载任务是否已经存在队列中,如果存在再不重新发起网络请求,只是把这次请求的回调downloadCallback存放到队列queue中

swift 复制代码
    guard let url = URL(string: urlStr) else {
            let error = NSError.pathNotFound(urlStr)
            downloadCallback?(nil,error)
            return nil
        }
        let questTask = queue.queryTask(urlStr)
        if let questTask = questTask {
            //在下载队列中找到对应的下载请求
            if let downloadCallback = downloadCallback {
                queue.updateQueueItemCallback(urlStr, callback: downloadCallback)
            }
            return questTask
        }
    

3.2 下载完成后通过url去查询callbacks并让对应的quesTastTask出列

swift 复制代码
     let requestTask = self.session.downloadTask(with: url) {
            [weak self] (localPath, respon, error ) in
            guard let self = self else {
                return
            }
                var err: NSError?
                if let respon = respon as? HTTPURLResponse{
                    let statusCode = respon.statusCode
                    if statusCode < 200 || statusCode > 300 {
                        err = NSError.downloadStatueError(urlStr, statueCode: statusCode)
                        
                    }
                }
            let callbacks = queue.dequeueCallback(urlStr)
            guard let callbacks = callbacks else {
                return
            }
                for itemCallback in callbacks{

                    itemCallback(localPath,err)
                }
            
        }
    

常用使用场景介绍

创建LXYPAGView

ini 复制代码
 lazy var pagView :LXYPAGView = {
        let pagView = LXYPAGView()
        return pagView
    }()
  1. 播放线上资源
arduino 复制代码
 //MARK: - 播放线上资源
    private func playOnlineResource(){
        let config = LXYPAGConfig()
        config.resourceStr =  "http://xxx.pag"
        pagView.frame = CGRect(x: 0, y: 0, width: 300, height: 300)
        self.pagView.playAnim(config)
    }
  1. 区间播放
php 复制代码
    //MARK: - 区间播放
    private func playInFrame(){
        let config = LXYPAGConfig()
        if let path = Bundle.main.path(forResource: "like", ofType: ".pag") {
            config.resourceStr = path
            config.InFramePlayRange = NSRange(location: 30, length: 100)
        }
        pagView.frame = CGRect(x: 0, y: 0, width: 300, height: 300)
        self.pagView.playAnim(config)
    }
  1. 循环+变速播放
ini 复制代码
    private func playInFrame(){
        let config = LXYPAGConfig()
        if let path = Bundle.main.path(forResource: "login_page_animation", ofType: ".pag") {
            config.resourceStr = path
            config.speed = 2
            config.loop = 0
        }
        pagView.frame = CGRect(x: 0, y: 0, width: 300, height: 300)
        self.pagView.playAnim(config)
    }

主要流程

githut地址

github.com/liang152091...

引用文章

baijiahao.baidu.com/s?id=167924... baijiahao.baidu.com/s?id=167843...

相关推荐
库奇噜啦呼1 小时前
【iOS】Core Data
ios
2501_9151063216 小时前
TraceEagle 代理抓包教程 本机和手机的 HTTPS 抓包方法
网络协议·计算机网络·网络安全·ios·adb·https·udp
云逸_21 小时前
Cinder:基于 Swift Macro 与 Mach-O Section 的解耦注册机制
ios·swift
末代iOS程序员华仔1 天前
从 Figma 到上架:使用 Codex 与 MCP 全流程实现 AI 聊天 App
flutter·ios·swift
极客猴子1 天前
苹果手机怎么快速总结长文本?iOS录音APP实测对比
ios·智能手机
一牛1 天前
Swift 循环引用
macos·ios
迷雾悠悠2 天前
企业微信iPad协议聚合聊天系统:从原理到实战开发指南
ios·企业微信·ipad
LDyun_2 天前
.2026主流云手机测评,8款机型高压挂机实测
ios·智能手机·安卓·玩游戏
pop_xiaoli2 天前
【iOS】YYModel源码学习
学习·ios·objective-c·cocoa
霍霍哈嗨2 天前
swiftUI框架基础
ios·swiftui·swift