You need to enable JavaScript to run this app.
导航
SDK素材管理
最近更新时间:2025.04.08 20:29:43首次发布时间:2025.04.08 17:46:49
我的收藏
有用
有用
无用
无用
资源导入和配置

交付物列表

文件名称文件资源说明使用位置
EffectResourcetransitions.bundle转场资源轨道编辑转场
ModelResource.bundle模型资源各个特效依赖的模型
tone.bundle变声资源预览编辑变声面板
sound.bundle音效资源轨道编辑添加音效
sticker.bundle信息化贴纸资源预览编辑信息化贴纸面板
ve_effect.bundle特效资源预览编辑特效面板
ComposeMakeup.bundle美颜资源拍摄美颜面板
StickerResource.bundle人脸贴纸资源拍摄贴纸面板、预览编辑人脸贴纸面板
fonts.bundle字体资源预览编辑文字面板
music.bundle音乐资源拍摄、预览编辑、轨道编辑音乐面板

filter.bundle
ve_filter.bundle

滤镜资源

拍摄、预览编辑滤镜面板

Resource_icons素材相关icon所有资源对应的icon,面板展示时使用
Panel_configs配置文件各个面板层级关系配置,面板展示时使用
RemoteResource客户自行部署按需下载时使用用于服务器部署,素材内置时不需要关注
License鉴权文件用于应用鉴权

配置文件以及对应面板详细说明

EffectOne配置文件及面板对应关系

资源配置「在线&按需下载」

使用场景

  • 场景:点击某个功能项时才下载对应资源

  • 优点:减小包体;避免一次下载所有资源带来的长耗时问题

移动端配置

  • 资源包 EffectResource 无需内置到 App 工程

  • 在使用任何工程之前,配置EOResourceConfig,完成网络相关初始化

参数名类型含义建议值备注
resourceSavePathString素材下载后本地路径EOUtils.pathUtil.internalResource()

sysLanguage

String

素材展示名称的语言,默认支持简体中文(zh)和英文(en),其他语言需要自行在config中适配

Locale.getDefault().language

netWorkerINetWorker网络请求代理DefaultNetWorker()DefaultNetWorker,实现在EOQuickInithelper中,使用OKHTTP去发起网络请求

requestBuilder

IRequestBuilder

构建网络请求的代理

根据客户自己的场景来实现:

  • 获取下载素材的请求

  • 获取在线图标的请求

  • 获取预览视频(剪同款)

  • 获取面板config的请求

interface IRequestBuilder { fun buildDownloadResourceRequest(relativePath: String, isModel: Boolean, node: Any?): RequestInfo fun getIconUrl(buildInIcon: String): String?
fun getVideoUrl(buidInVideo: String): String fun buildPanelConfigRequest(panelKey: String): RequestInfo }

  • 通过IRequestBuilder来定义获取config/素材zip/预览图的相关请求

  • 在配置各个页面的面板时,使用OnlineResourceLoader代替EOQuickInitHelper中的DefaultLocalResourceLoader,示例代码如下

class EOQuickInitHelper {
    ...
    fun initApplication(application: Application) {
        ...

        // 指定素材文件保存目录、素材使用语言
        val config = EOResourceConfig.Builder()
            .setResourceSavePath(EOUtils.pathUtil.internalResource())
            .setSysLanguage(Locale.getDefault().language)
                        .setNetWorker(DefaultNetWorker())  // 使用在线素材时需要该配置
            .setRequestBuilder(object: IRequestBuilder)
//            .setDomainConfig(EORemoteAddressConfig())  //已废弃,如果以前使用了该接口,可以改成使用下方的setRequestBuilder(EORemoteAddressConfig())
//            .setRequestBuilder(DomainRequestBuilder(EORemoteAddressConfig()))
            .build()

        // 素材sdk初始化
        EOResourceManager.init(config)
        
        ...
    }
    
    private fun initConfigs() {

        //拍摄模块整体配置
        EffectOneConfigList.configure(RecorderInitConfig()) {
                        it.finishAction = {  activity, list, musicItem ->
                val options = ActivityOptionsCompat.makeCustomAnimation(activity, 0, 0)
                EditorMainActivity.startEditorActivityFromRecord(activity, list.map  {  resourceItem ->
                    com.volcengine.easyeditor.api.MediaItem(
                        path = resourceItem.path,
                        type = if (resourceItem.type == RecordMediaType.VIDEO) MediaType.VIDEO  else MediaType.IMAGE,
                        duration = resourceItem.duration,
                        speed = resourceItem.speed
                    )
                }  as ArrayList<com.volcengine.easyeditor.api.MediaItem>, options.toBundle(), musicItem)
            }
                        it.albumConfig = AlbumConfig(
                allEnable = true,
                imageEnable = true,
                videoEnable = true,
                maxSelectCount = EffectOneSdk.albumMaxSelectedCount,
                //maxDuration Unit:ms
                maxDuration = 60 * 60 * 1000L,
                finishClazz = StartEditorFinishImpl::class.java
            )
        }
        
        val loader = OnlineResourceLoader()
        //编辑和拍摄通用面板配置
        //滤镜面板
        EffectOneConfigList.configure(FilterUIConfig()) {
                        it.resourceLoader = loader
        }
        //人脸特效面板
        EffectOneConfigList.configure(EOBaseStickerUIConfig()) {
                        it.resourceLoader = loader
        }
        //音乐面板
        EffectOneConfigList.configure(EOBaseMusicUIConfig()) {
                        it.resourceLoader = loader
        }

        //拍摄专用面板配置
        //美颜面板
        EffectOneConfigList.configure(BeautyUIConfig()) {
                        it.resourceLoader = loader
            it.defaultBeautyAction = {
                EOResourceManager.syncLoadConfigByPanelKey(EOResourcePanelKey.RECORDER_BEAUTY.value, true).tabs
            }
                }
        //合拍面板
        EffectOneConfigList.configure((RecorderDuetUIConfig())) {
                        it.resourceLoader = loader
            it.defaultDuetAction = {
                EOResourceManager.syncLoadConfigByPanelKey(EOResourcePanelKey.RECORDER_DUET.value, true).tabs
            }
                }

        //编辑专用面板配置
        //信息贴纸面板
        EffectOneConfigList.configure(InfoStickerUIConfig()) {
                        it.resourceLoader = loader
        }
        //文字面板
        EffectOneConfigList.configure(TextStickerUIConfig()) {
                        it.resourceLoader = loader
        }
        //视频特效面板
        EffectOneConfigList.configure(EffectPanelUIConfig()) {
                        it.resourceLoader = loader
        }
        //音频特效面板
        EffectOneConfigList.configure(AudioFilterUIConfig()) {
                        it.resourceLoader = loader
        }
        //转场面板
        EffectOneConfigList.configure(TransitionUIConfig()) {
                        it.resourceLoader = loader
        }
    }
}

服务端配置

  • 部署配置文件

    • 基于RequestBuilder的配置,从客户端的请求中,获取 panel_key 字段信息,得到请求的配置文件名。

      fun buildPanelConfigRequest(panelKey: String): RequestInfo
      
    • 参数含义:

      • panleKey:对应的面板key,如获取到的 panleKey 是 "recorder_sticker",则表示获取的配置文件是 recorder_sticker.json。此时,Server将该文件的内容返回给客户端
    • 所有的配置文件位于 交付文档中的Panel_configs 文件夹内

    • 请求行为行为在DefaultNetWorker.execute(requestInfo: RequestInfo)中实现

    • 示例请求如下

      data class RequestInfo(
          val domain: String? = "https://your panel host address",
          val path: String? = "/recorder_beauty",
          val requestType: RequestType = RequestType.GET,//对应api请求
          val headerMap: Map<String, String>? = null,
          val body: ByteArray? = null,
          val contentType: String? = null
      )
      
      //返回示例
      {
          "data": {
              "tabs": [
                  {
                      "uniqueId": "d0001",
                      "titleDict": {
                          "en": "2D",
                          "zh": "2D"
                      },
                      "subItems": [
                          {
                              "uniqueId": "d0002",
                              "titleDict": {
                                  "en": "Autumn Leaves",
                                  "zh": "金秋叶舞"
                              },
                              "builtInIcon": "eo_resource_jinqiuyewu_7048762992175349791.png",
                              "md5": "F18FDFFD6DB904FFBE465909F0867959",
                              "relativePath": "StickerResource.bundle/jinqiuyewu_7048762992175349791.zip",
                              "param": {
                                  "path": "StickerResource.bundle/jinqiuyewu_7048762992175349791"
                              },
                              "requirements": [
                                  {
                                      "relativePath": "ttfacemodel/algo_ggl1pqh_v11.1.model",
                                      "md5": "F7360859FEB0EA5DF58DA9860EF9738F"
                                  },
                                  {
                                      "relativePath": "ttfacemodel/algo_ggl1pqhlhmg7p_v14.0.model",
                                      "md5": "D342795D110E4230666FB41B413AA638"
                                  },
                                  {
                                      "relativePath": "ttfaceattrmodel/algo_ggl1pqhl4hpkg6_v5.0.model",
                                      "md5": "194F4C038CD35A4E07FF378DE97B8709"
                                  },
                                  {
                                      "relativePath": "ttfaceattrmodel/algo_ggl1pqhlpgg754kgh_v8.0.model",
                                      "md5": "8CDD9000E4DE6111E21B148B3BFA2D5B"
                                  },
                                  {
                                      "relativePath": "ttfaceattrmodel/algo_ggl1pqhlpgg754kghlgt4_v7.0.model",
                                      "md5": "4FFDB764F2EC8876B615A6865330E96C"
                                  },
                                  {
                                      "relativePath": "ttfaceattrmodel/algo_ggl1pqhlpgg754kghlhmg7p_v3.0.model",
                                      "md5": "EA8D1F0BCEE6B8DC4DE8AC2693C85FE2"
                                  },
                                  {
                                      "relativePath": "ttfaceattrmodel/algo_ggl1pqhlpgg754kghlhmi_v1.0.model",
                                      "md5": "93BB53B5394184A0495A278F7A801C3A"
                                  },
                                  {
                                      "relativePath": "ttfaceattrmodel/algo_ggl1pqhlpgg754kghlpah_v2.0.model",
                                      "md5": "9EB2F0B32B36C71220AE4645144596DA"
                                  },
                                  {
                                      "relativePath": "ttfaceattrmodel/algo_ggl4hpkg6lpgg7e_v2.0.model",
                                      "md5": "2C2DC971E4F7567D09D26F870965043B"
                                  },
                                  {
                                      "relativePath": "handmodel/algo_ggl0pcvl4tml7ha_v12.0.model",
                                      "md5": "9D85DE071360FDF2EF16BB89D7153B6C"
                                  },
                                  {
                                      "relativePath": "handmodel/algo_ggl0pcvl75ca_v1.1.model",
                                      "md5": "1588A926866EF9A5367F23ADD9F0C2A5"
                                  },
                                  {
                                      "relativePath": "handmodel/algo_ggl0pcvl97_v3.2.model",
                                      "md5": "A55AFDBBD45B1C84E33E2EA3A6859368"
                                  },
                                  {
                                      "relativePath": "handmodel/algo_ggl0pcvlahugk7h_v11.0.model",
                                      "md5": "D5B8890589D61D738EB3AC050AC3716A"
                                  },
                                  {
                                      "relativePath": "handmodel/algo_ggl0pcvlahugk7hlgt4_v11.2.model",
                                      "md5": "068E6D58EE05EA0250C2E96852DEBAA7"
                                  },
                                  {
                                      "relativePath": "handmodel/algo_ggl0pcvlsi_v6.0.model",
                                      "md5": "130183E2A6880A627AFF4E86874E580B"
                                  },
                                  {
                                      "relativePath": "handmodel/algo_ggl0pcvlsibv_v2.0.model",
                                      "md5": "97807B3D18A6D77D86E8DF96CEA386C7"
                                  },
                                  {
                                      "relativePath": "handmodel/algo_ggl0pcvluha_v2.0.model",
                                      "md5": "DDDC9206CD2C70310C803E226E0F0A02"
                                  },
                                  {
                                      "relativePath": "handmodel/algo_ggl0pcvlvhg_v11.0.model",
                                      "md5": "208FF7DC0FE017C029D32D38646AC4F2"
                                  }
                              ],
                              "tipsDict": {
                                  "en": "Take out your hand",
                                  "zh": "伸出手掌"
                              }
                          }
                      ]
                  }
              ]
          }
      }
      
  • 部署特效和模型资源

    • 基于RequestBuilder的配置,从客户端的请求中,得到需要下载的文件的相对路径,并将文件流式返回给客户端供其下载

      fun buildDownloadResourceRequest(relativePath: String, ext: panelKey: Sting, isModel: Boolean, node: Any?): RequestInfo
      
    • 参数含义

      • relativePath:对应上一条,PanelConfigRequest中服务端返回的PanelConfig里每个素材item对应的相对路径。接入方可以根据该路径,将素材zip包部署到服务器或cdn上。

      • isModel:是否是模型

      • node:对应每个item的详细属性,无需关注

    • 需要部署的所有资源位于 RemoteResource 文件夹内

    • 下载行为行为在DefaultNetWorker.execute(requestInfo: RequestInfo)中实现

    • 示例请求如下:

      data class RequestInfo(
          val domain: String? = "https://your resource host address",
          val path: String? = "/StickerResource.bundle/jinqiuyewu_7048762992175349791.zip",//对应config.json中的一个item的relativepath
          val requestType: RequestType = RequestType.FILE,//对应下载
          val headerMap: Map<String, String>? = null,
          val body: ByteArray? = null,
          val contentType: String? = null
      )
      //返回对应zip包的下载数据
      
    • 下载行为在DefaultNetWorker.execute(requestInfo: RequestInfo)中实现

  • 部署 icon

    • 基于RequestBuilder的配置,获取图标地址

      fun getIconUrl(buildInIcon: String, panelKey: Sting): String?
      
    • 需要部署的 icon 位于 Resource_icons 文件夹内

    • 示例如下:如config中的builtInIcon为"eo_resource_jinqiuyewu_7048762992175349791.png",返回对应的图标在线地址:http://your icon host/eo_resource_jinqiuyewu_7048762992175349791.png

    • 图片的具体下载行为,在EOQuickInitHelper中的DefaultImageLoader中利用Glide完成

  • 部署预览video(若需要)

    • 基于RequestBuilder的配置,获取预览视频地址

      fun getVideoUrl(builtInVideo: String, panelKey: String): String
      
    • 需要部署的 视频 位于 Resource_videos 文件夹内

    • 示例如下:如config中的builtInVideo为"eo_resource_mengwa.mp4",返回对应的视频在线地址:http://your icon host/eo_resource_mengwa.mp4

交付后,不要进行任何资源包的改动、解压、压缩等操作,否则会导致下载失败

Mock在线素材

为了方便理解,下方提供一个简单的mock server 和 RequestBuilder,用来演示如何部署在线素材(环境为mac系统,python3,保证测试手机和电脑在相同的wifi环境下)

  1. 下载技术支持同学提供交付产物中的素材包,在本地解压,解压后的目录大概类似

~/Downloads/fd9d70fc2b743113eb1cf40f9cc4093c

  1. 在本地启动一个简易文件server,以python3为例(如果本地没有python环境,请自行配置)

cd ~/Downloads/fd9d70fc2b743113eb1cf40f9cc4093c //进入到解压后的资源目录
python3 -m http.server 8000 //在本机的ip上8000端口,启动SimpleHttpServer
  1. 获取本机的ip地址

ipconfig getifaddr en0 
//或者使用ifconfig 查看电脑的ip
  1. 成功即可通过http请求,访问电脑上的交付产物文件夹,类似


到此,完成了server数据的mock

  1. 在EO中设置自定义RequestBuilder

之后,在EO的demo中,配置一个对应的RequestBuilder,domain = "http://10.78.55.150:8000/Android"

//domain为你本地的ip:port/文件地址,参考上文的server,domain="http://10.78.55.150:8000/Android"
class MockOnlineRequestBuilder(private var domain:  String?):  IRequestBuilder  {
        init {
                domain = domain?.removeSuffix("/")
        }
    private  val panelPathFormat =  "/EffectResource/Panel_configs/%s.json" // 面板json路径
    private  val iconPathFormat =  "/EffectResource/Resource_icons/%s" // 图标路径
    private  val videoPathFormat =  "/EffectResource/Resource_videos/%s" //    视频路径
    private  val resourceFormat =  "/RemoteResource/%s" // 素材资源路径
    private  val modelFormat =  "/RemoteResource/ModelResource.bundle/%s" // 模型资源路径
    override  fun  buildDownloadResourceRequest(
        relativePath:  String,
        isModel:  Boolean,
        node:  Any?,
        panelKey:  String
        ):  RequestInfo  {
        return if  (isModel) {
            RequestInfo(domain, modelFormat.format(relativePath),  RequestType.FILE)
                }  else  {
            RequestInfo(domain, resourceFormat.format(relativePath),  RequestType.FILE)
                }

        }

    override  fun  getIconUrl(buildInIcon:  String,  panelKey:  String):  String  {
        return  domain + iconPathFormat.format(buildInIcon)
        }

    override  fun  getVideoUrl(builtInVideo:  String,  panelKey:  String):  String  {
        //使用 mock server时,需要关闭播放缓存,否则无法正确加载视频
                val cutsameConfig =  InnerEffectOneConfigList.getConfig<CutSameConfig>(null)
                cutsameConfig?.usePlayCacheServer  =  false
        return  domain + videoPathFormat.format(builtInVideo)
        }

    override  fun  buildPanelConfigRequest(panelKey:  String):  RequestInfo  {
                val newPanelKey =  if  (panelKey ==  EOResourcePanelKey.CUTSAME_TEMPLATES.value) {
            "eo_cutsame"
                }  else  {
                        panelKey
                }
        return RequestInfo(domain, panelPathFormat.format(newPanelKey))
        }

}

并在RequetBuilder配置给EO,具体方法如下:
在EOQuickInitHelper中,修改initApplication方法

override  fun  initApplication(application: Application) {
    ...
    resourceLoader = OnlineResourceLoader.instance //使用在线素材加载
    val  config = EOResourceConfig.Builder()
        .setResourceSavePath(EOUtils.pathUtil.internalResource())
        .setSysLanguage(Locale.getDefault().language)
    .setNetWorker(DefaultNetWorker()) // 使用在线素材时需要该配置
    .setRequestBuilder(MockOnlineRequestBuilder("your mock server domain")) //使用自定义的RequestBuilder
    .build()
    ...
}
  1. 运行Demo app

运行Demo App,并清除app的缓存,避免之前有离线加载的素材影响测试。然后可以根据之前运行python 服务器的terminal输出的日志,来判断是否访问了mock server上的资源。Server端日志大概如下:

资源导入和配置「在线&一次性下发」

使用场景

  • 场景:客户端一次性将资源、模型、icon、配置文件下载

  • 优点:减小包体;服务端部署简单;网络交互少

移动端配置

  • 客户端自行进行下载,在使用任何 SDK 工程之前,确保所有资源下载完成并解压到了自定义目录TARGET_PATH

  • 进行如下代码配置

// 需要在EOQuickInitHelper initConfigs()里添加读取面板配置文件的实现,参考如下:
DefaultLocalResourceLoader.instance.localConfigsLoader = object : LocalConfigsLoader {
    private val configsPathMap : Map<String, String> = mutableMapOf<EOResourcePanelKey, String>().apply  {
        put(EOResourcePanelKey.PREVIEW_EDITOR_EFFECT, "${TARGET_PATH}/preview_editor_effect.json")
        put(EOResourcePanelKey.PREVIEW_EDITOR_FILTER, "${TARGET_PATH}/preview_editor_filter.json")
        put(EOResourcePanelKey.PREVIEW_EDITOR_FONT, "${TARGET_PATH}/preview_editor_font.json")
        put(EOResourcePanelKey.PREVIEW_EDITOR_INFO_STICKER, "${TARGET_PATH}/preview_editor_info_sticker.json")
        put(EOResourcePanelKey.PREVIEW_EDITOR_MUSIC, "${TARGET_PATH}/eo_music.json")
        put(EOResourcePanelKey.PREVIEW_EDITOR_STICKER, "${TARGET_PATH}/preview_editor_sticker.json")
        put(EOResourcePanelKey.PREVIEW_EDITOR_VOICE, "${TARGET_PATH}/preview_editor_tone.json")
        put(EOResourcePanelKey.RECORDER_BEAUTY, "${TARGET_PATH}/recorder_beauty.json")
        put(EOResourcePanelKey.RECORDER_FILTER, "${TARGET_PATH}/recorder_filter.json")
        put(EOResourcePanelKey.RECORDER_MUSIC, "${TARGET_PATH}/eo_music.json")
        put(EOResourcePanelKey.RECORDER_STICKER, "${TARGET_PATH}/recorder_sticker.json")
        put(EOResourcePanelKey.TRACK_EDITOR_MUSIC, "${TARGET_PATH}/eo_music.json")
        put(EOResourcePanelKey.TRACK_EDITOR_SOUND, "${TARGET_PATH}/track_editor_sound.json")
        put(EOResourcePanelKey.TRACK_EDITOR_TRANSITION, "${TARGET_PATH}/track_editor_transition.json")
    }.map  {
                it.key.value to  it.value
    }.toMap()
    override fun loadConfigs(panelKey: String, type: String): String? {
        val value = configsPathMap.getValue(panelKey) ?: return null
        // 从下载的配置文件中读取面板配置文件内容
        return EOUtils.fileUtil.readJsonFile("${TARGET_PATH}/${value}")
    }
}

val config = EOResourceConfig.Builder()
    .setResourceSavePath(${TARGET_PATH}) // 配置资源保存路径,用户可自定义
    .build()

// 素材sdk初始化
EOResourceManager.init(config)

// 拉取面板配置时localOnly设置为true (一次性下载对素材sdk来讲也是属于离线资源)
EOResourceManager.syncLoadConfigByPanelKey(id,  localOnly)

// 下载素材
EOResourceManager.syncLoadResourceByItem(item)

服务端配置

  • 对齐客户端网络请求,提供资源包下载

建议资源包下载格式为:直接将 EffectResource 压缩成 zip,作为下载的资源

资源导入和配置「内置」

使用场景

  • 场景:资源包均内置到工程中

  • 优点:离线使用,无需服务器部署

移动端配置

  • 将 EffectResource 文件夹直接放置到 assets 里,打包到 App 中

  • 把Resource_icons中的图标全部拷贝到res/drawable-xxhdpi

  • 在使用任何 SDK 特效之前,做好如下配置

// 需要在EOQuickInitHelper initConfigs()里添加读取面板配置文件的实现,参考如下:
DefaultLocalResourceLoader.instance.localConfigsLoader = object : LocalConfigsLoader {
    private val configsPathMap : Map<String, String> = mutableMapOf<EOResourcePanelKey, String>().apply  {
        put(EOResourcePanelKey.PREVIEW_EDITOR_EFFECT, "Panel_configs/preview_editor_effect.json")
        put(EOResourcePanelKey.PREVIEW_EDITOR_FILTER, "Panel_configs/preview_editor_filter.json")
        put(EOResourcePanelKey.PREVIEW_EDITOR_FONT, "Panel_configs/preview_editor_font.json")
        put(EOResourcePanelKey.PREVIEW_EDITOR_INFO_STICKER, "Panel_configs/preview_editor_info_sticker.json")
        put(EOResourcePanelKey.PREVIEW_EDITOR_MUSIC, "Panel_configs/eo_music.json")
        put(EOResourcePanelKey.PREVIEW_EDITOR_STICKER, "Panel_configs/preview_editor_sticker.json")
        put(EOResourcePanelKey.PREVIEW_EDITOR_VOICE, "Panel_configs/preview_editor_tone.json")
        put(EOResourcePanelKey.RECORDER_BEAUTY, "Panel_configs/recorder_beauty.json")
        put(EOResourcePanelKey.RECORDER_FILTER, "Panel_configs/recorder_filter.json")
        put(EOResourcePanelKey.RECORDER_MUSIC, "Panel_configs/eo_music.json")
        put(EOResourcePanelKey.RECORDER_STICKER, "Panel_configs/recorder_sticker.json")
        put(EOResourcePanelKey.TRACK_EDITOR_MUSIC, "Panel_configs/eo_music.json")
        put(EOResourcePanelKey.TRACK_EDITOR_SOUND, "Panel_configs/track_editor_sound.json")
        put(EOResourcePanelKey.TRACK_EDITOR_TRANSITION, "Panel_configs/track_editor_transition.json")
    }.map  {
                it.key.value to  it.value
    }.toMap()
    override fun loadConfigs(panelKey: String, type: String): String? {
        val value = configsPathMap.getValue(panelKey) ?: return null
       // 从assets读取面板配置文件内容
        return EOUtils.fileUtil.readJsonFromAssets(AppSingleton.instance, "${EOUtils.pathUtil.assetsResourcePath}/${value}")
    }
}

val config = EOResourceConfig.Builder()
    .setResourceSavePath(EOUtils.pathUtil.internalResource()) // 配置资源保存路径
    .build()

// 素材sdk初始化
EOResourceManager.init(config)

// 拉取面板配置时localOnly设置为true
EOResourceManager.syncLoadConfigByPanelKey(id,  localOnly)

// 下载素材
EOResourceManager.syncLoadResourceByItem(item)

资源导入和配置「部分内置+部分在线」

使用场景

  • 场景:部分资源内置,来加快资源加载速度;其余资源部署到线上,减小包体

  • 优点:实现加载速度和包体之间的平衡

移动端配置

  • 参考「内置」场景,将需要内置的资源包放置到工程中打包

  • 设置网络相关参数、内置资源包路径、下载资源包路径

// 需要在EOQuickInitHelper initConfigs()里添加读取面板配置文件的实现,参考如下:
DefaultLocalResourceLoader.instance.localConfigsLoader = object : LocalConfigsLoader {
    private val configsPathMap : Map<String, String> = mutableMapOf<EOResourcePanelKey, String>().apply  {
        put(EOResourcePanelKey.PREVIEW_EDITOR_EFFECT, "Panel_configs/preview_editor_effect.json")
        put(EOResourcePanelKey.PREVIEW_EDITOR_FILTER, "Panel_configs/preview_editor_filter.json")
        put(EOResourcePanelKey.PREVIEW_EDITOR_FONT, "Panel_configs/preview_editor_font.json")
        put(EOResourcePanelKey.PREVIEW_EDITOR_INFO_STICKER, "Panel_configs/preview_editor_info_sticker.json")
        put(EOResourcePanelKey.PREVIEW_EDITOR_MUSIC, "Panel_configs/eo_music.json")
        put(EOResourcePanelKey.PREVIEW_EDITOR_STICKER, "Panel_configs/preview_editor_sticker.json")
        put(EOResourcePanelKey.PREVIEW_EDITOR_VOICE, "Panel_configs/preview_editor_tone.json")
        put(EOResourcePanelKey.RECORDER_BEAUTY, "Panel_configs/recorder_beauty.json")
        put(EOResourcePanelKey.RECORDER_FILTER, "Panel_configs/recorder_filter.json")
        put(EOResourcePanelKey.RECORDER_MUSIC, "Panel_configs/eo_music.json")
        put(EOResourcePanelKey.RECORDER_STICKER, "Panel_configs/recorder_sticker.json")
        put(EOResourcePanelKey.TRACK_EDITOR_MUSIC, "Panel_configs/eo_music.json")
        put(EOResourcePanelKey.TRACK_EDITOR_SOUND, "Panel_configs/track_editor_sound.json")
        put(EOResourcePanelKey.TRACK_EDITOR_TRANSITION, "Panel_configs/track_editor_transition.json")
    }.map  {
                it.key.value to  it.value
    }.toMap()
    override fun loadConfigs(panelKey: String, type: String): String? {
        val value = configsPathMap.getValue(panelKey) ?: return null
        // 从assets读取面板配置文件内容
        return EOUtils.fileUtil.readJsonFromAssets(AppSingleton.instance, "${EOUtils.pathUtil.assetsResourcePath}/${value}")
    }
}

//配置网络下载,参考上文在线&按需下载
// 指定素材文件保存目录、素材使用语言
val config = EOResourceConfig.Builder()
        .setResourceSavePath(EOUtils.pathUtil.internalResource())
        .setSysLanguage(Locale.getDefault().language)
        .setNetWorker(DefaultNetWorker())  // 使用在线素材时需要该配置
        .setRequestBuilder(object:  IRequestBuilder)
        .build()

// 素材sdk初始化
EOResourceManager.init(config)

// 拉取面板配置时localOnly设置为false
EOResourceManager.syncLoadConfigByPanelKey(id,  localOnly)

// 下载素材
EOResourceManager.syncLoadResourceByItem(item)

服务端配置

  • 根据部署情况,参考「在线&按需下发」或者「在线&一次性下发」在服务端部署资源

使用三方信息贴纸

EffectOne支持客户使用三方信息贴纸,参考如下文档EffectOne支持第三方信息贴纸

代码介绍

关键类介绍

EOResourceManager

EffectOneSdk需要使用素材来完成贴纸、特效、滤镜等效果,为了方便使用提供了素材SDK(EOResourceManager),方便接入方管理和配置素材。对于绝大部分场景,接入方直接使用ResourceManager来管理素材即可。
EOResourceManager主要提供的能力介绍:

  • 同步/异步拷贝离线素材接口

  • 同步/异步拉取并解析面板配置json接口

  • 同步/异步拉取素材包能力,包含自动拉取依赖模型、自动融合在线/离线素材、单/多组素材一次拉取能力

IEOResourceItem

EffectOne内部,所有功能(如滤镜、贴纸等)都依赖素材实现。IEOResourceItem提供了描述各种素材的通用接口,客户可以根据需求,自定义实现IEOResourceItem,来定制化EffectOneSDK拍摄和编辑页的各面板中可选元素的Tab、顺序、图标、名称等。默认情况下,EOResourceManager会根据各面板的config.json,自动构造对应的ResourceItem,客户无需关注。

参数名类型含义是否必须
uniqueIdString当前素材唯一ID
titleString素材的标题(语言类型为用户设置)
iconString在线素材图标url
builtInIconString离线素材图标文件名
defaultOnInt素材是否开启
absPathString素材本地保存的绝对路径
subItemsList<IEOResourceItem>当前素材的子项结构
paramsMap<String, Any>?素材使用时需要用的参数
extraMap<String, String>?音乐相关素材需要用到的额外参数

OnlineResourceLoader

EffectOneSDK的每个面板,都需要接入方在宿主内实现素材加载的逻辑,如果需要使用在线素材,可以参考该OnlineResourceLoader

class OnlineResourceLoader: ResourceLoader {
    override suspend fun loadResourceList(id: String, type: String): List<IEOResourceItem> {
        return EOResourceManager.syncLoadConfigByPanelKey(id, false).tabs
    }

    override suspend fun loadResourceItem(item: IEOResourceItem): IEOResourceItem? {
        return if (EOResourceManager.syncLoadResourceByItem(item)?.resValue == 0) {
            EOResourceManager.syncLoadResourceByItem(item)?.resourceItem
        } else {
            null
        }
    }

    override fun checkResourceExist(item: IEOResourceItem): Boolean {
        return EOResourceManager.resourceLoaded(item)
    }
}

DefaultLocalResourceLoader

如使用离线素材,可以参考DefaultLocalResourceLoader

/**
  * 素材SDK加载本地素材的ResourceLoader
  */
class DefaultLocalResourceLoader  :  ResourceLoader  {

    var  localConfigsLoader =  object  : LocalConfigsLoader {
        private val  configsPathMap : Map<String, String> = mutableMapOf<EOResourcePanelKey, String>().apply  {
            put(EOResourcePanelKey.PREVIEW_EDITOR_EFFECT,  "Panel_configs/preview_editor_effect.json")
                        put(EOResourcePanelKey.PREVIEW_EDITOR_FILTER,  "Panel_configs/preview_editor_filter.json")
                        put(EOResourcePanelKey.PREVIEW_EDITOR_FONT,  "Panel_configs/preview_editor_font.json")
                        put(EOResourcePanelKey.PREVIEW_EDITOR_INFO_STICKER,  "Panel_configs/preview_editor_info_sticker.json")
                        put(EOResourcePanelKey.PREVIEW_EDITOR_MUSIC,  "Panel_configs/eo_music.json")
                        put(EOResourcePanelKey.PREVIEW_EDITOR_STICKER,  "Panel_configs/preview_editor_sticker.json")
                        put(EOResourcePanelKey.PREVIEW_EDITOR_VOICE,  "Panel_configs/preview_editor_tone.json")
                        put(EOResourcePanelKey.RECORDER_BEAUTY,  "Panel_configs/recorder_beauty.json")
                        put(EOResourcePanelKey.RECORDER_FILTER,  "Panel_configs/recorder_filter.json")
                        put(EOResourcePanelKey.RECORDER_MUSIC,  "Panel_configs/eo_music.json")
                        put(EOResourcePanelKey.RECORDER_STICKER,  "Panel_configs/recorder_sticker.json")
                        put(EOResourcePanelKey.TRACK_EDITOR_MUSIC,  "Panel_configs/eo_music.json")
                        put(EOResourcePanelKey.TRACK_EDITOR_SOUND,  "Panel_configs/track_editor_sound.json")
                        put(EOResourcePanelKey.TRACK_EDITOR_TRANSITION,  "Panel_configs/track_editor_transition.json")
                        put(EOResourcePanelKey.RECORDER_DUET,  "Panel_configs/recorder_duet.json")
        }.map  {
                        it.key.value to  it.value
        }.toMap()
        override fun loadConfigs(panelKey:  String, type:  String): String? {
            val  value = configsPathMap.getValue(panelKey) ?:  return null
            return  EOUtils.fileUtil.readJsonFromAssets(AppSingleton.instance,  "${EOUtils.pathUtil.assetsResourcePath}/${value}")
                }

        }
    companion object  {
        val  instance = DefaultLocalResourceLoader()
        }

    override suspend fun loadResourceList(id:  String, type:  String): List<IEOResourceItem> {
        if  (!EOResourceManager.hasLocalConfigJson(id)) {
                        localConfigsLoader.loadConfigs(id, type)?.let  {
                EOResourceManager.setLocalConfigJson(id,  it)
            }
        }
        return  EOResourceManager.syncLoadConfigByPanelKey(id,  true).tabs
        }

    override suspend fun loadResourceItem(item:  IEOResourceItem): IEOResourceItem? {
        return  EOResourceManager.syncLoadResourceByItem(item)?.resourceItem
        }

    override fun checkResourceExist(item:  IEOResourceItem):  Boolean  {
        return  EOResourceManager.resourceLoaded(item)
        }

}

interface LocalConfigsLoader  {
    fun loadConfigs(panelKey:  String, type:  String): String?

}

接口代码

interface IEOResourceItem  {
    val  uniqueId: String
    var  title: String
    var  tips: String?
    var  icon: String
    var  builtInIcon: String

    //0:关闭,1打开
    var  defaultOn:  Int
    var  absPath: String
    var  subItems: List<IEOResourceItem>?
    var  params: Map<String, Any>?
    var  extra: Map<String, String>?

}