解决腾讯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 分钟前
App渠道来源追踪方案全面分析(iOS/Android/鸿蒙)
android·ios·harmonyos
2401_8658548816 小时前
iOS应用想要下载到手机上只能苹果签名吗?
后端·ios·iphone
HackerTom1 天前
iOS用rime且导入自制输入方案
ios·iphone·rime
良技漫谈1 天前
Rust移动开发:Rust在iOS端集成使用介绍
后端·程序人生·ios·rust·objective-c·swift
2401_852403551 天前
高效管理iPhone存储:苹果手机怎么删除相似照片
ios·智能手机·iphone
星际码仔2 天前
【动画图解】是怎样的方法,能被称作是 Flutter Widget 系统的核心?
android·flutter·ios
emperinter2 天前
WordCloudStudio:AI生成模版为您的文字云创意赋能 !
图像处理·人工智能·macos·ios·信息可视化·iphone
关键帧Keyframe2 天前
音视频面试题集锦第 8 期
ios·音视频开发·客户端
pb82 天前
引入最新fluwx2.5.4的时候报错
flutter·ios
袁代码2 天前
Swift 开发教程系列 - 第4章:函数与闭包
ios·swift·ios开发