文件名称 | 文件 | 资源说明 | 使用位置 |
---|---|---|---|
EffectResource | transitions.bundle | 转场资源 | 轨道编辑转场 |
ModelResource.bundle | 模型资源 | 各个特效依赖的模型 | |
tone.bundle | 变声资源 | 预览编辑变声面板 | |
sound.bundle | 音效资源 | 轨道编辑添加音效 | |
sticker.bundle | 信息化贴纸资源 | 预览编辑信息化贴纸面板 | |
ve_effect.bundle | 特效资源 | 预览编辑特效面板 | |
ComposeMakeup.bundle | 美颜资源 | 拍摄美颜面板 | |
StickerResource.bundle | 人脸贴纸资源 | 拍摄贴纸面板、预览编辑人脸贴纸面板 | |
fonts.bundle | 字体资源 | 预览编辑文字面板 | |
music.bundle | 音乐资源 | 拍摄、预览编辑、轨道编辑音乐面板 | |
filter.bundle | 滤镜资源 | 拍摄、预览编辑滤镜面板 | |
duet.bundle | 合拍资源 | 合拍模块 | |
cutsame.bundle | 剪同款资源 | 剪同款模块 | |
Resource_icons | 素材相关icon和封面图 | 所有资源对应的icon,面板展示时使用 | |
Resource_videos | 剪同款预览视频 | 剪同款用 | |
Panel_configs | 配置文件 | 各个面板层级关系配置,面板展示时使用 | |
RemoteResource | 客户自行部署按需下载时使用 | 用于服务器部署,素材内置时不需要关注 | |
License | 鉴权文件 | 用于应用鉴权 |
场景:点击某个功能项时才下载对应资源
优点:减小包体;避免一次下载所有资源带来的长耗时问题
资源包 EffectResource 无需内置到 App 工程
自定义类实现接口EOResourceRequestBuilderProtocol,分别定义拉取配置请求、下载资源请求、icon加载url。在适当时机,SDK内部会调用该方法,获取对应请求
@protocol EOResourceRequestBuilderProtocol <NSObject> @required // 自定义请求对应面板配置数据的 Request // 在请求面板配置时,会调用这个方法来获取 Request,随后发起请求 - (NSURLRequest * _Nullable)buildConfigURLRequestByPanelKey:(NSString * _Nullable)panelKey resourceInfo:(NSDictionary * _Nullable)resource; // 自定义图片资源的请求url string // 在加载每一张icon时,会调用该方法获取url。如果返回空值,那么SDK内部会在尝试在本地路径下获取图片文件 - (NSString * _Nullable)iconUrlString:(NSString * _Nullable)iconName panelKey:(NSString * _Nullable)panelKey; // 自定义视频资源请求url string // 获取视频url时,会优先使用该接口获取url - (NSString * _Nullable)videoUrlString:(NSString * _Nullable)videoName panelKey:(NSString * _Nullable)panelKey; // 自定义资源下载的 Request // 资源下载时,对调用该方法,获取下载 Request - (NSURLRequest * _Nullable)buildResourceDownloadRequestByRelativePath:(NSString * _Nullable)relativePath isModel:(BOOL)isModel resourceInfo:(NSDictionary * _Nullable)resource; @end class EOCustomRequestBuilderImpl: NSObject, EOResourceRequestBuilderProtocol { ... }
EOInjectContainer.shared().resourceRequestBuilderImpl = EOCustomRequestBuilderImpl()
EOSDK.setResourceBaseDir(EOSDK.getEODocumentRootDir() + "/download_dir")
EOSDK.useRemoteConfig(true, useRemoteResource: true)
服务端需要部署的资源包括
素材和模型资源:使用交付素材包中RemoteResource文件夹下的资源
配置文件:使用交付素材包EffectResource中Panel_configs下的资源
icon:使用交付素材包EffectResource中Resource_icons中的资源
根据客户端在 EOResourceRequestBuilderProtocol 实现类中的定义分别部署配置文件、资源、icon图标,确保请求和资源一一对应
TIPS
提供一个最简单的部署方式供参考:
将交付道具包中的 RemoteResource、EffectResource/Panel_configs、EffectResource/Resource_icons 分别上传至文件服务器
客户端在 EOResourceRequestBuilderProtocol 实现类中,分别指向对应资源即可
交付后,不要进行任何资源包的改动、解压、压缩等操作,否则会导致下载失败
场景:客户端一次性将资源、模型、icon、配置文件下载
优点:减小包体;服务端部署简单;网络交互少
资源包 EffectResource 无需内置到 App 工程
客户端自行进行下载,在使用任何 SDK 工程之前,确保所有资源下载、解压完毕
进行如下代码配置
let downloadDir = EOSDK.getEODocumentRootDir() + "/download_dir" // 设置资源下载解压后的目标路径 EOSDK.setResourceBaseDir(EOSDK.defaultResourceDir(downloadDir)) // 设置配置文件下载解压后的目标路径 EOSDK.setResourceDefaultBuiltInConfig(EOSDK.defaultPanelConfigDir(downloadDir)) // 使用在线模式 EOSDK.useRemoteConfig(true, useRemoteResource: true)
建议资源包下载格式为:直接将 EffectResource 压缩成 zip,作为下载的资源
场景:资源包均内置到工程中
优点:离线使用,无需服务器部署
将 EffectResource 文件夹直接放置到 EOLocalResources.bundle 里,打包到 App 中
在使用任何 SDK 特效之前,做好如下配置
// 设置资源根路径 EOSDK.setResourceBaseDir(EOSDK.defaultResourceDir(localBundle().bundlePath)) // 设置配置文件根路径 EOSDK.setResourceDefaultBuiltInConfig(EOSDK.defaultPanelConfigDir(localBundle().bundlePath))
场景:部分资源内置,来加快资源加载速度;其余资源部署到线上,减小包体
优点:实现加载速度和包体之间的平衡
参考「在线&按需下发」配置在线请求参数
参考「内置」场景,将需要内置的资源包放置到工程中打包,设置内置资源路径
// 设置内置资源路径 EOSDK.setBuiltInResourceDir(localBundle().bundlePath)
// 设置资源根路径 EOSDK.setResourceBaseDir(EOSDK.defaultResourceDir(localBundle().bundlePath))
// 设置配置文件根路径 EOSDK.setResourceDefaultBuiltInConfig(EOSDK.defaultPanelConfigDir(localBundle().bundlePath))
// 使用在线模式 EOSDK.useRemoteConfig(true, useRemoteResource: true)
// 设置外部资源路径, let resourcePath = "外部资源路径" EOSDK.setBuiltInResourceRootPath(resourcePath)
配置拍摄模块中用户录制的视频片段的缓存路径
private func setCustomRecoderCacheDir() { let documentsDirectory = FileManager.default.temporaryDirectory let customFolderPath = documentsDirectory.appendingPathComponent("CustomRecoderCacheDir") EOSDK.setCustomRecoderCacheDir(customFolderPath.path) }
鉴权逻辑用于控制SDK功能的可用性,拥有对应的授权才能使用相应的功能。
类名 | 字段与方法 | 类型 | 说明 | 备注 |
---|---|---|---|---|
EOAuthorizationConfig | isOnline | BOOL | True 表示在线鉴权,False表示离线鉴权 | |
isOversea | BOOL | True表示使用海外鉴权服务,False表示使用国内鉴权服务 | ||
licensePathForOffline | NSString* | 离线证书的绝对路径 | ||
licenseTokenForOnline | NSString* | 在线证书的token | ||
EOAuthorization | + (instancetype)sharedInstance; | 获取EOAuthorization对象实例 | ||
-(void)makeAuthWithConfig:(EOAuthorizationConfig*)config completionHandler:(EOAuthCompletionHandler)completionHandler; | 进行鉴权,鉴权结果通过EOAuthCompletionHandler获取(其中在线鉴权是异步的) | |||
- typedef void (^EOAuthCompletionHandler)(BOOL success, NSString* errMsg); | 获取鉴权结果,其中success表示鉴权是否成功,当失败时,通过errMsg获取错误信息 | 可根据错误信息排查错误或者发给技术顾问协助排查 |
如果选择离线鉴权方式,鉴权证书通常会跟随交付产物一同交付给客户,且通常放置在EOLocalResources\Resources\License\XXX.licbag ,找到该文件并在app的启动阶段添加鉴权逻辑,例如:
let config = EOAuthorizationConfig { initializer in //鉴权方式是离线鉴权 initializer.isOnline = false //设置离线证书所在的路径 initializer.licensePathForOffline = //证书文件的路径 } //开始鉴权 EOAuthorization.sharedInstance().makeAuth(with: config) {isSuccess, errMsg in self.isAuthSucceeded = isSuccess //鉴权失败打印错误码 if !self.isAuthSucceeded { print(errMsg) } } }
离线鉴权证书更新需要客户自行处理。
如果选择在线鉴权方式,需要根据客户所在区域(国内或海外)申请相应的鉴权服务token,在app的启动阶段添加鉴权逻辑,例如:
let config = EOAuthorizationConfig { initializer in //鉴权方式是在线鉴权 initializer.isOnline = true //使用海外鉴权服务 initializer.isOversea = true //设置在线鉴权的token initializer.licenseTokenForOnline = //token } //开始鉴权 EOAuthorization.sharedInstance().makeAuth(with: config) {isSuccess, errMsg in self.isAuthSucceeded = isSuccess //鉴权失败打印错误码 if !self.isAuthSucceeded { print(errMsg) } } }
在线鉴权证书更新无需客户处理。
使用构造器模式,config 所有对外暴露的属性均为 readonly。统一通过对应的 initializer 来初始化。
EORecorderConfig 为配置容器,是拍摄页初始化参数。所有子配置均在该类下:
类名 | 字段与方法 | 类型 | 说明 | 备注 |
---|---|---|---|---|
EORecorderConfig | modelPath | NSString | 拍摄器使用的模型文件路径,默认值 EOBaseKit.modelResourceDir | |
cameraPosition | AVCaptureDevicePosition | 拍摄器默认摄像头,默认前置 AVCaptureDevicePositionFront | ||
videoResolution | EORecorderVideoResolution | 拍摄器采集分辨率,有 540p/720p/1280p/4k 4种选择 采集分辨率与硬件设备有关,部分旧设备前置摄像头只能支持到 720p iPhone 7 以及之后的 iPhone 默认值 EORecorderVideoResolution1080p,旧设备默认值EORecorderVideoResolution720p | ||
recorderViewControllerConfig | EORecorderViewControllerConfig | 拍摄页 ViewController 配置类,可进行 UI 相关配置,例如侧边栏设置(详见 2. 侧边栏) | ||
- (instancetype)initWithBlock:(void (^)(EORecorderConfigInitializer *))block | 初始化函数,使用构造器模式,config 所有对外暴露的属性均为 readonly。统一通过对应的 initializer 来初始化。使用详见下面代码样例。 |
类定义:
@interface EORecorderConfig : NSObject // 拍摄器使用的模型文件路径,默认值 EOBaseKit.modelResourceDir @property (nonatomic, copy, readonly) NSString *modelPath; // 拍摄器默认摄像头,默认前置 AVCaptureDevicePositionFront @property (nonatomic, assign, readonly) AVCaptureDevicePosition cameraPosition; // 拍摄器采集分辨率,有 540p/720p/1280p/4k 4种选择 // 采集分辨率与硬件设备有关,部分旧设备前置摄像头只能支持到 720p // iPhone 7 以及之后的 iPhone 默认值 EORecorderVideoResolution1080p, // 旧设备默认值 EORecorderVideoResolution720p @property (nonatomic, assign, readonly) EORecorderVideoResolution videoResolution; // 拍摄页 ViewController 配置类 @property (nonatomic, strong, readonly) EORecorderViewControllerConfig *recorderViewControllerConfig; // 初始化函数 - (instancetype)initWithBlock:(void (^)(EORecorderConfigInitializer *))block; @end
设置模型文件路径:
let config = EORecorderConfig { initializer in // 设置模型文件路径 initializer.modelPath = EOBaseKit.modelResourceDir() }
设置拍摄页 ViewController,拍摄一级页面设置类:
类名 | 字段与方法 | 类型 | 说明 | 备注 |
---|---|---|---|---|
EORecorderViewControllerConfig | sideBarConfig | EORecorderSideBarConfig | 侧边栏配置类 | |
musicBarHidden | BOOL | 隐藏音乐栏,默认值 NO | ||
stickerButtonHidden | BOOL | 隐藏人脸贴纸按钮,默认值 NO | ||
albumButtonHidden | BOOL | 隐藏相册按钮,默认值 NO | ||
-(instancetype)initWithInitializer:(EORecorderViewControllerConfigInitializer *)initializer | 初始化函数 |
类定义:
@interface EORecorderViewControllerConfig : NSObject // 侧边栏配置类 @property (nonatomic, strong, readonly) EORecorderSideBarConfig *sideBarConfig; /// Top middle music bar visible /// default `NO` @property (nonatomic, assign) BOOL musicBarHidden; /// Bottom left sticker button visible /// default `NO` @property (nonatomic, assign) BOOL stickerButtonHidden; /// Bottom right sticker button visible /// default `NO` @property (nonatomic, assign) BOOL albumButtonHidden; // 初始化函数 - (instancetype)initWithInitializer:(EORecorderViewControllerConfigInitializer *)initializer; @end
侧边栏设置类:
类名 | 字段与方法 | 类型 | 说明 | 备注 |
---|---|---|---|---|
EORecorderSideBarConfig | itemKeys | NSMutableArray | 侧边栏元素 key 数组,默认提供下面 6 个元素,支持调整展示顺序,暂不支持新增自定义元素 默认值 @[ EORecordBarItemRotateCameraKey, // 翻转摄像头 EORecordBarItemFlashKey, // 闪光灯 EORecordBarItemTimerKey, // 倒计时 EORecordBarItemFiltersKey, // 滤镜 EORecordBarItemBeautyKey, // 美颜 EORecordBarItemSpeedKey, // 倍速 ] | |
unfoldCount | NSUInteger | 从顶部开始计数,不折叠的元素数量 | ||
foldEnabled | BOOL | 侧边栏折叠功能开关,默认值 YES |
类定义:
@interface EORecorderSideBarConfig : NSObject // 侧边栏元素 key 数组,默认提供下面 6 个元素,支持调整展示顺序,暂不支持新增自定义元素 // 默认值 @[ // EORecordBarItemRotateCameraKey, // 翻转摄像头 // EORecordBarItemFlashKey, // 闪光灯 // EORecordBarItemTimerKey, // 倒计时 // EORecordBarItemFiltersKey, // 滤镜 // EORecordBarItemBeautyKey, // 美颜 // EORecordBarItemSpeedKey, // 倍速 // ] @property (nonatomic, strong) NSMutableArray<EORecordSideBarItemKey> *itemKeys; // 从顶部开始计数,不折叠的元素数量 @property (nonatomic, assign) NSUInteger unfoldCount; /// Control the fold mode for sidebar /// default is `YES` @property (nonatomic, assign) BOOL foldEnabled; @end
使用例子:
let config = EORecorderConfig { initializer in initializer.configRecorderViewController { recorderVCInitializer in // 调整侧边栏功能按钮顺序 recorderVCInitializer.sideBarConfig.itemKeys.exchangeObject(at: 0, withObjectAt: 1) } }
回调协议:
协议名/类名 | 字段与方法 | 类型 | 说明 | 备注 |
---|---|---|---|---|
EORecorderViewControllerDelegate | - (void)recorderViewController:(EORecorderViewController *)recorderViewController didFinishRecordingMediaWithInfo:(EORecordInfo *)info | 拍摄完成回调,回调数据封装在EORecordInfo 实例中 | 这里可以取到录制的视频(非编辑) | |
- (void)recorderViewControllerDidCancel:(EORecorderViewController *)recorderViewController | 拍摄页关闭回调。如果实现了这个方法,SDK 内部不会 dismiss,需要调用方处理 | |||
-(void)recorderViewControllerDidTapAlbum:(EORecorderViewController *)recorderViewController | 拍摄页相册按钮回调 | |||
EORecordInfo | mediaAssets | NSArray<id | media 数据 | |
backgroundMusic | id | 拍摄页选择的背景音乐 | ||
source | EORecordInfoSource | 数据来源,相机/相册 | ||
coverImage | UIImage * | mediaAssets 中第一个数据的第一帧图片 |
协议定义:
@protocol EORecorderViewControllerDelegate <NSObject> @optional - (void)recorderViewController:(EORecorderViewController *)recorderViewController didFinishRecordingMediaWithInfo:(EORecordInfo *)info; - (void)recorderViewControllerDidCancel:(EORecorderViewController *)recorderViewController; - (void)recorderViewControllerDidTapAlbum:(EORecorderViewController *)recorderViewController; @end
使用例子:
extension EffectOneModule: EORecorderViewControllerDelegate { func recorderViewControllerDidTapAlbum(_ recorderViewController: EORecorderViewController) { self.recorderVC = recorderViewController guard let resourcePicker = EOInjectContainer.shared().resourcePickerSharedImpl else { return } resourcePicker.pickResourcesFromRecorder { [weak self] resources, error, cancel in guard !resources.isEmpty else { return; } let info = EORecordInfo() info.mediaAssets = resources info.source = .album if let presenter = self?.topMostViewController() { self?.showEditorViewController(info: info, presenter: presenter) } } } func recorderViewController(_ recorderViewController: EORecorderViewController, didFinishRecordingMediaWith info: EORecordInfo) { self.recorderVC = recorderViewController self.showEditorViewController(info: info, presenter: recorderViewController) } }
SDK 默认使用 present 方式展示 UI,V1.1.0 新增 navigation push 支持。代码样例:
EORecorderViewController.requestMediaAuth { [weak self] granted in guard granted else { self?.showToast("Request media auth failed") return } guard let strongSelf = self else { return } do { let recorderViewController = try EORecorderViewController(config: config) recorderViewController.delegate = strongSelf strongSelf.parentVC.navigationController?.pushViewController(recorderViewController, animated: true) } catch { self?.showToast("show recorder failed: \(error)") } }
合拍功能
功能介绍:
合拍功能是选择一个本地视频之后,调起相机功能,并通过一个资源包来控制合拍视频与相机画面在预览视图中的布局。本地视频会在开拍时开始播放,拍摄结束后停止播放,合拍图像会跟相机图像在相关布局下一起记录到拍摄的视频中。
前置条件
需要有本地视频和合拍布局资源包。
本地视频可以从服务器下载也可以在相册选取。
资源包需要提前下载到本地,bundle名字为 duet.bundle ,如有采购交付资源中会包含。
相机权限及麦克风权限
接口介绍
从相册选取视频
EOResourcePickerImpl
//1 EOInjectContainer.shared().resourcePickerSharedImpl //2 - (void)pickVideoForDuetWithCompletion:(nonnull EOResourcePickerCompletion)completion;
创建合拍控制器
EODuetViewController:
+ (void)startDuetWithConfig:(EORecorderConfig *)config presenter:(UIViewController *)presenter duetVideoURL:(NSURL *)videoURL delegate:(id<EORecorderViewControllerDelegate> _Nullable)delegate completion:(void (^)(NSError * _Nullable error))completion;
EORecorderConfig:
musicBarHidden 属性会被设置为 true;
sideConfig 中会增加 barItemMicKey 和 barItemDuetLayoutKey;其中 barItemMicKey 用于增加麦克风的开关,barItemDuetLayoutKey 用于增加合拍布局的入口。
let config = EORecorderConfig { initializer in // config recorder if needed initializer.configRecorderViewController { recorderVCInitializer in // config recoderViewController if needed recorderVCInitializer.musicBarHidden = true if let stickerButtonHidden: Bool = DebugConfig.value(forKey: "recorderStickerButtonHidden") { recorderVCInitializer.stickerButtonHidden = stickerButtonHidden } if let albumButtonHidden: Bool = DebugConfig.value(forKey: "recorderAlbumButtonHidden") { recorderVCInitializer.albumButtonHidden = albumButtonHidden } var sideConfig = EORecorderSideBarConfig() sideConfig.itemKeys = [EORecordSideBarItemKey.barItemRotateCameraKey, EORecordSideBarItemKey.barItemFlashKey, EORecordSideBarItemKey.barItemMicKey, EORecordSideBarItemKey.barItemDuetLayoutKey, EORecordSideBarItemKey.barItemTimerKey, EORecordSideBarItemKey.barItemFiltersKey, EORecordSideBarItemKey.barItemBeautyKey, EORecordSideBarItemKey.barItemSpeedKey,] recorderVCInitializer.sideBarConfig = sideConfig } }
接入样例
相册选取
public func showDuetViewController() { if !self.isAuthSucceeded { self.showToast("authority is failed!,please check it.") return } EOAlbumHelper.eoCheckAlbumAuthorized { [weak self] in guard let resourcePicker = EOInjectContainer.shared().resourcePickerSharedImpl else { return } resourcePicker.pickVideoForDuet(completion: { [weak self] resources, error, cancel in if let resorce = resources.first { self?.gotoDuetViewController(withVideoURL: resorce.url!) } }) } restrictedOrDenied: { [weak self] in self?.showAlert(withTitle: NSLocalizedString("eo_home_camera_album_unauth_alert_title", comment: ""), message: NSLocalizedString("eo_home_camera_album_unauth_alert_message", comment: ""), cancelTitle: NSLocalizedString("eo_home_camera_unauth_alert_cancel", comment: ""), confirmTitle: NSLocalizedString("eo_home_camera_unauth_alert_confirm", comment: ""), cancelHandler: nil, confirmHandler: { if #available(iOS 10.0, *) { UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!, options: [:]) { success in // Completion handler } } else { UIApplication.shared.openURL(URL(string: UIApplication.openSettingsURLString)!) } }) } }
进入合拍
public func gotoDuetViewController(withVideoURL videoURL:URL) { if !self.isAuthSucceeded { self.showToast("authority is failed!,please check it.") return } updateImageEditingEnabledIfNeeded() let config = EORecorderConfig { initializer in // config recorder if needed initializer.configRecorderViewController { recorderVCInitializer in // config recoderViewController if needed recorderVCInitializer.musicBarHidden = true if let stickerButtonHidden: Bool = DebugConfig.value(forKey: "recorderStickerButtonHidden") { recorderVCInitializer.stickerButtonHidden = stickerButtonHidden } if let albumButtonHidden: Bool = DebugConfig.value(forKey: "recorderAlbumButtonHidden") { recorderVCInitializer.albumButtonHidden = albumButtonHidden } var sideConfig = EORecorderSideBarConfig() sideConfig.itemKeys = [EORecordSideBarItemKey.barItemRotateCameraKey, EORecordSideBarItemKey.barItemFlashKey, EORecordSideBarItemKey.barItemMicKey, EORecordSideBarItemKey.barItemDuetLayoutKey, EORecordSideBarItemKey.barItemTimerKey, EORecordSideBarItemKey.barItemFiltersKey, EORecordSideBarItemKey.barItemBeautyKey, EORecordSideBarItemKey.barItemSpeedKey,] recorderVCInitializer.sideBarConfig = sideConfig } } EODuetViewController.startDuet(with: config, presenter: self.parentVC,duetVideoURL:videoURL, delegate: self, completion: { [weak self] error in if let err = error as? NSError, let presenter = self?.parentVC { self?.showErrorAlert(err, presenter: presenter) } }) }
EOEditorConfig
使用构造器模式,config 所有对外暴露的属性均为 readonly。统一通过对应的 initializer 来初始化。
EOEditorConfig 为配置容器,是编辑页初始化参数。所有子配置均在该类下:
类名 | 字段与方法 | 类型 | 说明 | 备注 |
---|---|---|---|---|
EOEditorConfig | videoEditorConfig | EOVideoEditorViewControllerConfig | 编辑页 ViewController 配置类,可进行 UI 相关配置,例如侧边栏设置(详见 b. 侧边栏) | |
- (instancetype)initWithBlock:(void (^)(EOEditorConfigInitializer *))block; | 初始化函数,使用构造器模式,config 所有对外暴露的属性均为 readonly。统一通过对应的 initializer 来初始化。使用详见下面代码样例。 | |||
类定义:
@interface EOEditorConfig : NSObject @property (nonatomic, strong, readonly) EOVideoEditorViewControllerConfig *videoEditorConfig; - (instancetype)initWithBlock:(void (^)(EOEditorConfigInitializer *))block NS_DESIGNATED_INITIALIZER; @end
设置编辑页ViewController,编辑一级页面设置类:
类名 | 字段与方法 | 类型 | 说明 | 备注 |
---|---|---|---|---|
EOVideoEditorViewControllerConfig | sideBarConfig | EOEditorSideBarConfig | 侧边栏配置类 | |
musicBarHidden | BOOL | 隐藏音乐栏,默认值 NO | ||
textAndInfoStickerMaxLimit | NSInteger | 文字和信息贴纸最大使用数量,默认值30 | ||
soundEffectMaxLimit | NSInteger | 音效最大使用数量,默认值30 | ||
- (instancetype)initWith:(EOVideoEditorViewControllerConfigInitializer *)initializer; | 初始化函数 |
类定义:
@interface EOVideoEditorViewControllerConfig : NSObject @property (nonatomic, strong, readonly) EOEditorSideBarConfig *sideBarConfig; @property (nonatomic, assign) BOOL musicBarHidden; @property (nonatomic, assign, readonly) NSInteger textAndInfoStickerMaxLimit; @property (nonatomic, assign, readonly) NSInteger soundEffectMaxLimit; - (instancetype)initWith:(EOVideoEditorViewControllerConfigInitializer *)initializer; @end
EOEditorSceneConfig
EOEditorSceneConfig用于配置进入编辑页的场景:
类名 | 字段与方法 | 类型 | 说明 | 备注 |
---|---|---|---|---|
EOEditorSceneConfig | draftModel | EOSDKDraftModel | 草稿模型,用于从草稿再次进入编辑的场景。 | |
resources | NSArray<id | 用于拍摄或者从相册选择素材后进入编辑的场景 | ||
backgroundMusic | id | 拍摄场景下选择的BGM | ||
coverImage | UIImage | 拍摄场景下首帧画面,优化跳转体验 | ||
previewContentMode | EOEditorPreviewContentMode | 视频显示模式 | ||
ratio | EOEditorVideoRatio | 配置视频画布比例,默认为EOEditorVideoRatio9_16。 EOEditorVideoRatioOriginal :基于第一个素材的比例 |
如果从草稿恢复,可以给draftModel赋值
如果从拍摄页进入编辑,可以将从拍摄页得到的媒体数据设置给resources/backgroundMusic/coverImage这几个字段
@interface EOEditorSceneConfig : NSObject /// Restore from draft @property (nonatomic, strong) EOSDKDraftModel *draftModel; /// Resources from Recorder or Album @property (nonatomic, copy) NSArray<id<EOMediaAssetProtocol>> *resources; /// Selected BGM Resource from Recorder @property (nonatomic, strong, nullable) id<EOResourceProtocol> backgroundMusic; /// First resource thumbnail from Recorder @property (nonatomic, strong, nullable) UIImage *coverImage; /// Default is `EOEditorPreviewContentModeAspectFit` @property (nonatomic, assign) EOEditorPreviewContentMode previewContentMode; @end
侧边栏设置类:
类名 | 字段与方法 | 类型 | 说明 | 备注 |
---|---|---|---|---|
EOEditorSideBarConfig | itemKeys | NSMutableArray | 侧边栏元素 key 数组,默认提供下面 7 个元素,支持调整展示顺序,暂不支持新增自定义元素 默认值 @[ EOEditorBarItemKeyClip, // 剪辑 EOEditorBarItemKeyText, // 文本 EOEditorBarItemKeySticker, // 信息贴纸 EOEditorBarItemKeyFilter, // 滤镜 EOEditorBarItemKeyEffect, // 特效 EOEditorBarItemKeyFaceSticker, // 人脸特效 | |
unfoldCount | NSUInteger | 从顶部开始计数,不折叠的元素数量 | ||
foldEnabled | BOOL | 侧边栏折叠功能开关,默认值 YES |
@interface EOEditorSideBarConfig : NSObject /// Array of keys that presenting right side bar items. /// Could change the order. Custom new items not supported yet. /// default order: [ /// EOEditorBarItemKeyClip, /// EOEditorBarItemKeyText, /// EOEditorBarItemKeySticker, /// EOEditorBarItemKeyFilter, /// EOEditorBarItemKeyEffect, /// EOEditorBarItemKeyFaceSticker, /// EOEditorBarItemKeyVoice /// ] @property (nonatomic, strong) NSMutableArray<EOEditorBarItemKey> *itemKeys; /// The number of side bar items that would not be folded, /// count from the first object of `itemKeys` /// default is `5` @property (nonatomic, assign) NSUInteger unfoldCount; /// Control the fold mode for sidebar /// default is `YES` @property (nonatomic, assign) BOOL foldEnabled; @end
使用例子:
let config = EOEditorConfig { initializer in initializer.configVideoEditorViewController { editorVCInitializer in // 调整侧边栏功能按钮顺序 editorVCInitializer.sideBarConfig.itemKeys.exchangeObject(at: 0, withObjectAt: 1) } } let sceneConfig = EOEditorSceneConfig() sceneConfig.resources = info.mediaAssets sceneConfig.backgroundMusic = info.backgroundMusic sceneConfig.coverImage = info.coverImage // 如果想使用本地资源直接进入编辑,也可以如下构造一个遵循EOMediaAssetProtocol协议的模型,并设置给sceneConfig.resources // let asset = EOMediaAssetDefaultImpl() // asset.url = URL(fileURLWithPath: Bundle.main.path(forResource: "localVideo", ofType: "mp4") ?? ""); // sceneConfig.resources = [asset] EOVideoEditorViewController.startEditor(with: config, sceneConfig: sceneConfig, presenter: presenter, delegate: self) { [weak self, weak presenter] error in if let err = error as? NSError, let vc = presenter { self?.showErrorAlert(err, presenter: vc) } };
V1.1.0 新增图片编辑功能,默认开启。全局配置类:
类名 | 字段与方法 | 类型 | 说明 | 备注 |
---|---|---|---|---|
EOBusinessConfig | imageEditingEnabled | BOOL | 图片编辑功能开关,默认值 YES |
@interface EOBusinessConfig : NSObject /// Single image could be editing as image mode or shot video mode, /// set `YES` to let editor process and export the input image as image, otherwise as video. /// default `YES` @property (nonatomic, assign) BOOL imageEditingEnabled; @end
使用例子:
// disable image editing EOSDK.businessConfig().imageEditingEnabled = false
SDK 默认使用 present 方式展示 UI,V1.1.0 新增 navigation push 支持。代码样例:
if let editorViewController = EOVideoEditorViewController(config: config, sceneConfig: sceneConfig) { editorViewController.delegate = self presenter.navigationController?.pushViewController(editorViewController, animated: true) } else { self.showToast("show editor failed") }
草稿页面
V1.1.0 接口变更,草稿页面不在sdk内部调起编辑页,需要调用者实现EODraftBoxControllerDelegate,收到draftBoxController:didSelectDraft: 消息后自行调起
@protocol EODraftBoxControllerDelegate <NSObject> @optional - (void)draftBoxControllerDidCancel:(EODraftBoxController *)draftBoxController; - (void)draftBoxController:(EODraftBoxController *)draftBoxController didSelectDraft:(EOSDKDraftModel *)draftModel; @end @interface EODraftBoxController : UIViewController @property (nonatomic, weak) id<EODraftBoxControllerDelegate> delegate; + (void)presentDraftVCDelegate:(id<EODraftBoxControllerDelegate>)delegate; //presentVC 基于该控制器进行present,presentVC需要为置顶VC + (void)presentDraftVCDelegate:(id<EODraftBoxControllerDelegate>)delegate presentVC:(UIViewController *)presentVC; @end
实现delegate例子
extension EffectOneModule: EODraftBoxControllerDelegate { func draftBoxController(_ draftBoxController: EODraftBoxController, didSelectDraft draftModel: EOSDKDraftModel) { self.showEditorViewController(.draft(draftModel), presenter: draftBoxController) } }
调起方式
//第一种方式 EODraftBoxController.presentDraftVCDelegate(self) //第二种方式 EODraftBoxController.presentDraftVCDelegate(self, presentVC: viewController)
该组件是EffectOneSDK提供的一个独立组件,支持多选、单选、图片预览、视频预览、图片裁剪等功能,可用于其他需要相册业务中。
/// 调用相册 /// - 参数: /// - config: 相册配置 /// - completion: 相册调用结束回调 - (void)pickResourceWithConfig:(DAKResourcePickerConfig *)config completion:(DAKResourcePickerCompletion)completion;
部分属性说明如下
属性 | 类型 | 描述说明 | 默认值 | 注意 |
---|---|---|---|---|
hintText | NSString | 相册列表底部文案 | {zh}:你可以选择图片和视频 | 使用的是文本key为:eo_album_tips |
minDuration | NSUInteger | 最小视频时长,单位是毫秒 | 默认是 100 毫秒 | |
maxDuration | NSUInteger | 最大视频时长,单位是毫秒 | 默认是 60 * 60 * 1000 毫秒 | |
maxSelectCount | NSUInteger | 选择相册资源的最大个数 | 默认是 35 | |
minSelectCount | NSUInteger | 选择相册资源的最小个数 | 默认是 1 | |
imageEnable | BOOL | 是否允许选择图片 | 默认 YES | |
videoEnable | BOOL | 是否允许选择视频 | 默认 YES | |
allEnable | BOOL | 是否允许选择图片和视频 | 默认 YES | |
showGif | BOOL | 是否显示gif图 | 默认 YES | |
singleSelect | BOOL | 是否是单选 | 默认 NO | |
canRepeat | BOOL | 相册资源是否允许重复选择 | 默认 YES | |
gifMaxFileSize | CGFloat | gif图文件支持的最大值,单位:MB | 默认150MB | |
gifPixelWidthMax | CGFloat | gif图分辨率宽度支持的最大值 | 默认1920 | |
gifPixelHeightMax | CGFloat | gif图分辨率长度支持的最大值 | 默认1920 |
/// 相册列表底部文案 @property (nonatomic, copy) NSString *hintText; /// 最小视频时长,单位是毫秒,默认是 100 毫秒 @property (nonatomic, assign) NSUInteger minDuration; /// 最大视频时长,单位是毫秒, 默认是 60 * 60 * 1000 毫秒 @property (nonatomic, assign) NSUInteger maxDuration; /// 选择相册资源的最大个数, 默认是 35 @property (nonatomic, assign) NSUInteger maxSelectCount; /// 选择相册资源的最小个数, 默认是 1 @property (nonatomic, assign) NSUInteger minSelectCount; /// 是否允许选择图片, 默认YES @property (nonatomic, assign) BOOL imageEnable; /// 是否允许选择视频, 默认YES @property (nonatomic, assign) BOOL videoEnable; /// 是否允许选择图片和相册, 默认YES @property (nonatomic, assign) BOOL allEnable; /// 是否显示gif图, 默认YES @property (nonatomic, assign) BOOL showGif; /// 是否是单选, 默认 NO @property (nonatomic, assign) BOOL singleSelect; /// 相册资源是否允许重复选择,默认YES @property (nonatomic, assign) BOOL canRepeat; /// Maximum size of GIF image memory,unit is MB, default max is 150MB,that is 150 * 1024 * 1024 bytes @property (nonatomic, assign) CGFloat gifMaxFileSize; /// Maximum pixel width of GIF image, default max is 1920 @property (nonatomic, assign) CGFloat gifPixelWidthMax; /// Maximum pixel height of GIF image, default max is 1920 @property (nonatomic, assign) CGFloat gifPixelHeightMax;
使用到函数说明
函数名 | 描述说明 | 拓展 |
---|---|---|
-(DVEAlbumAssetMediaType)type; | 获取相册资源的类型 | 返回 DVEAlbumAssetMediaType 枚举类型,DVEAlbumAssetMediaTypeVideo 是 视频类型 |
-(NSData *)imageData; | 相册资源二进制数据 | |
-(NSURL *)videoURL; | 相册资源的地址路径 |
使用属性说明
属性 | 描述说明 | 拓展 |
---|---|---|
type | 获取相册资源的类型 | 返回 DVEAlbumAssetMediaType 枚举类型,DVEAlbumAssetMediaTypeVideo 是 视频类型 |
imageData | 相册资源二进制数据 | |
videoURL | 相册资源的地址路径 |
// 首选声明DAKResourcePicker对象 private lazy var resourcePicker = { return DAKResourcePicker() }() // 调用相册 let config = DAKResourcePickerConfig() //不支持单选 config.singleSelect = false //选择视频、图片 config.pickType = .imageVideo resourcePicker.pickResource(with: config) { [weak self] resources in //用户根据自己业务逻辑处理相册回调结果 }
导出界面考虑到客户一般都会定制,因此将这部分代码以开源的形式放入sample中共客户参考和复用(参考EffectOneSDK交付清单),代码中演示了sdk关于导出的接口调用;当然也可以在你自己的导出组件中直接调用相应的接口,详细的接口说明见文档 视频导出接口介绍
// 清理素材资源,会把通过{EOSDK#setResourceBaseDir}设置的文件夹下的内容删除 [EOSDK clearResource];
EffectOneSDK是包含UI的音视频拍摄与编辑SDK,其UI支持部分定制能力,包括更换主题色、文案与icon。
EffectOne SDK中所有可替换的颜色都可以在色卡中找到对应的key和色值。
SDK 内部优先读取自定义颜色,没有自定义的使用内部默认颜色,可通过以下API进行自定义主题色设置。颜色名字必须与色卡表格定义的一致。(备注:dark
和 light
为颜色风格预留字段,当前版本只使用了dark
,暂不支持切换,自定义设置填写相同的色值即可。)
EOSDK.setColorDictionary([ "Primary" : [ "dark" : "FF0ECDEB", //ARGB "light": "FF0ECDEB" ], "Secondary" : [ "dark" : "FF0ECDEB", "light": "FF0ECDEB" ], ]);
参考各模块UI标注
SDK依据文案对应的key进行替换,注意不要修改key名称。
(1)直接修改本地文案文件:打开目录EOLocalResources\BuiltInResource\Strings,找到EffectOne-en.strings和EffectOne-zh.strings,参考各模块UI标注 进行修改
(2)通过代码设置:SDK内部优先读取代码设置的文案,参考各模块UI标注 找到要替换的文案的key,示例如下:
//如果需要增加其他语言,可通过以下函数获取语言的key,例如中文"zh" func getLanguageCode() -> String? { let currentLanguage = Locale.preferredLanguages.first let components = currentLanguage?.components(separatedBy: "-") return components?.first } EOSDK.setLocalizationDictionary([ "zh" : [ "eo_previeweditor_pop_discard" : "你好", "eo_previeweditor_pop_discard_change": "你好" ], "en" : [ "eo_previeweditor_pop_discard" : "hello", "eo_previeweditor_pop_discard_change": "hello" ], ]);
SDK依据icon文件的名称进行替换,注意不要修改icon名称。
(1)直接替换本地Icon文件:打开目录EOLocalResources\BuiltInResource\Icons,EOLocalResources\BuiltInResource\Albums和EOLocalResources\BuiltInResource\Animations,参考各模块UI标注 找到对应的文件进行替换,
(2)通过代码设置:SDK内部优先读取代码设置的icon,参考各模块UI标注 找到要替换的icon的名称,示例如下:
var imageDictionary: [String: UIImage] = [:] let image1 = UIImage(named: "image1") imageDictionary["eo_album_list_bar_back"] = image1! let image2 = UIImage(named: "image2") imageDictionary["eo_album_list_delete"] = image2! EOSDK.setIconDictionary(imageDictionary);