编辑视频

短视频SDK提供视频编辑与导出功能,支持视频图片素材混合导入、提供滤镜、配音、时间特效等丰富的编辑效果。

版本支持

版本

是否支持

专业版

支持所有功能。

标准版

部分支持,支持除了字幕、动态贴纸、MV以外的其他功能。

基础版

不支持。

相关类功能

操作

类名

功能

初始化

AliyunIEditor

视频编辑核心类。

AliyunEditorFactory

工厂类。

视频管理

AliyunIClipConstructor

视频源管理器。

AliyunImageClip

图片片段。

AliyunVideoClip

视频片段。

设置视频特效

EffectBean

滤镜及MV的Model。

EffectFilter

动效滤镜Model。

TransitionBase

转场Model。

设置画中画

AliyunIPipManager

画中画管理类,负责画中画的增删改查。

AliyunIPipController

画中画控制器,设置画中画的开始时间,结束时间。

AliyunIPipTrack

轨道信息获取。

AliyunILayoutController

布局控制器,对画中画移动、缩放、旋转、透明度等设置。

AliyunIAnimationController

动画控制器,可以对画中画做帧动画。

AliyunIAudioController

声音控制器,控制画中画音量,降噪、音效、淡入淡出等。

AliyunIAugmentationController

画面调节控制器,可以调节饱和度、亮度、对比度等。

设置字幕及动态贴纸

AliyunPasterManager

字幕及动态贴纸管理器。

AliyunPasterControllerCompoundCaption

字幕控制器。

AliyunPasterController

动态贴纸控制器。

草稿箱

AliyunEditorProject

工程配置。

AliyunDraftManager

草稿管理。

AliyunDraft

草稿。

AliyunDraftResourceLoader

资源加载器。

AliyunDraftResourceUploader

资源上传器。

AliyunDraftResourceDownloader

资源下载器。

AliyunDraftResTask

草稿资源处理任务。

编辑视频流程

阶段

流程

说明

示例代码

基础

1

创建并初始化编辑器。

初始化

2

在编辑过程中动态裁剪视频、动态更换视频源、动态调整视频转场时间及转场效果。编辑核心类是AliyunIEditor类。

视频管理

3

设置编辑器的预览播放。

预览控制

进阶

4

设置滤镜、转场及MV特效。

设置视频特效

5

设置背景音乐、配音及音效。

设置音乐及音效

6

设置画中画。

设置画中画

7

设置字幕、花字、文字气泡及动态贴纸。

设置字幕及动态贴纸

8

支持编辑草稿箱中的视频、或将编辑完成的视频保存至草稿箱。

草稿箱

9

设置时间特效、水印及涂鸦。

其他设置

初始化

创建并初始化编辑器。代码中需要使用的参数详情,请参考接口文档。接口链接请参见相关类功能

//1. 实例化
//configPath为导入视频的地址,为草稿箱地址或者AliyunIImport.generateProjectConfigure()重新生成后的地址
Uri uri = Uri.parse(configPath); 

AliyunIEditor editor = AliyunEditorFactory.creatAliyunEditor(uri, null);

//2. 初始化
//初始化并设置预览窗口
editor.init(surfaceView, context);


//3. 销毁,当不再使用时需要调用销毁接口
editor.onDestroy()

视频管理

视频编辑器里的视频或图片最终由视频源管理器AliyunIClipConstructor统一管理,通过AliyunIClipConstructor修改编辑器里的视频或图片后,需要手动调用AliyunIEditor.applySourceChange()应用更新的视频或图片。

代码中需要使用的参数详情,请参考接口文档。接口链接请参见相关类功能

视频源管理操作

//1.获取视频源管理器
AliyunIClipConstructor contructor = AliyunIEditor.getSourcePartManager();


//[可选] 2.添加视频或图片

//在时间轴最后添加视频或图片,其中视频用AliyunVideoClip,图片用AliyunImageClipAliyunImageClip
contructor.addMediaClip(AliyunClip clip);

//在指定位置的添加视频或图片,其中视频用AliyunVideoClip,图片用AliyunImageClipAliyunImageClip
contructor.addMediaClip(int index, AliyunClip clip);

//[可选] 3.删除视频或图片

//删除最后一个视频或图片
contructor.deleteMediaClip();

//删除某一个指定的视频或图片
contructor.deleteMediaClip(int index);

//[可选] 4.替换视频或图片
//替换指定位置视频或图片
contructor.updateMediaClip(int index, AliyunClip clip);

//替换所有的视频或图片
contructor.updateAllClips(List<AliyunClip> clips);


//5.应用更新源:视频源操作完成后,要调用该方法应用更新,操作才会生效
AliyunIEditor.applySourceChange();

其他相关操作

//交换视频源顺序    
contructor.swap(int pos1, int pos2);

//获取当前视频源个数    
contructor.getMediaPartCount();

//获取当前视频源列表    
contructor.getAllClips();

预览控制

在视频编辑过程中,提供一系列对当前视频的播放控制操作,如播放、暂停、获取当前时长等。代码中需要使用的参数详情,请参考接口文档。接口链接请参见相关类功能

//开始播放    
AliyunIEditor.play();

//继续播放
AliyunIEditor.resume();

// 暂停播放
AliyunIEditor.pause();

// 跳转到指定位置
AliyunIEditor.seek(long time);

// 设置静音播放
AliyunIEditor.setAudioSilence(boolean silence);

// 设置音量    
AliyunIEditor.setVolume(int volume);

// 设置视频显示模式    
AliyunIEditor.setDisplayMode(VideoDisplayMode mode);

// 填充模式下设置填充背景色
AliyunIEditor.setFillBackgroundColor(int color);

//获取当前流的位置 - 不受时间特效影响
AliyunIEditor.getCurrentStreamPosition();

// 获取当前播放的位置 - 受时间特效影响
AliyunIEditor.getCurrentPlayPosition();

// 获取流时长 - 不受时间特效影响
AliyunIEditor.getStreamDuration();

// 获取播放总时长 - 受时间特效影响
AliyunIEditor.getDuration();

设置视频特效

目前支持设置的视频特效包括滤镜、转场和MV,支持自定义制作,制作方法请参见滤镜及转场MV代码中需要使用的参数详情,请参考接口文档。接口链接请参见相关类功能

  • 滤镜分为lut滤镜、静态滤镜和动效滤镜。

    • lut滤镜:使用Lookup Table方式进行像素替换。

    • 静态滤镜:通过编写着色语言方式进行像素计算,不支持带动画效果。Bean为EffectBean,接口参数请参考EffectBean

    • 动效滤镜:通过编写着色语言方式进行像素计算,带动画效果。Bean为EffectFilter,接口参数请参考EffectFilter

  • 目前短视频SDK提供的转场效果包括:TransitionCircle(圆形打开)、TransitionFade(淡入淡出)、TransitionFiveStar(五角星)、TransitionShutter(百叶窗)、TransitionTranslate(平移)。

lut滤镜

LUTEffectBean bean = new LUTEffectBean();
bean.setPath("image_01.png");
bean.setIntensity(1.f);

//添加lut滤镜
mAliyunIEditor.applyLutFilter(bean);
//删除lut滤镜
mAliyunIEditor.applyLutFilter(null);

静态滤镜

EffectBean effect = new EffectBean();
effect.setId(id)
effect.setSource(new Souce(filePath));

//添加滤镜
AliyunIEditor.applyFilter(effect);
//删除滤镜
AliyunIEditor.applyFilter(new EffectBean());

动效滤镜

EffectFilter effectFilter = new EffectFilter(new Souce(filePath));
effectFilter.setStartTime(startTime);
effectFilter.setDuration(duration);

//添加动效滤镜
AliyunIEditor.addAnimationFilter(effectFilter);

//删除指定动效滤镜
AliyunIEditor.removeAnimationFilter(effectFilter);
//删除所有动效滤镜
AliyunIEditor.clearAllAnimationFilter();

转场

//1.设置转场
//设置一个转场,index为转场位置,从0开始记,取消转场的话,transition传null即可
AliyunEditor.setTransition(int index, TransitionBase transition); 

//设置批量转场,取消转场的话,transition传null即可
AliyunEditor.setTransition(Map<Integer, TransitionBase> transitions); //设置多个转场


//2.更新转场
//从某个转场更新成另一个转场,transition不允许为null
AliyunEditor.updateTransition(int index, TransitionBase transition); 

MV

支持自定义MV,MV的制作规范请参见MV

//添加MV
AliyunIEditor.applyMV(EffectBean effect);

//删除MV
AliyunIEditor.applyMV(null);

设置音乐及音效

音乐

音乐分为背景音乐和配音。背景音乐不受时间特效影响(变速、重复、倒放等),而配音会受到时间特效的影响。代码中需要使用的参数详情,请参考接口文档。接口链接请参见相关类功能

EffectBean musicBean = new EffectBean();
musicBean.setId(effectInfo.id);
musicBean.setSource(effectInfo.getSource());

                    
//切换音乐seek到0清音乐缓存,避免响一声
musicBean.setStartTime(startTime);
musicBean.setDuration(Integer.MAX_VALUE);//设置为最大时长
musicBean.setStreamStartTime(streamStartTime);
musicBean.setStreamDuration(streamDuration);

int audioSteamId;
//1.添加背景音乐/配音
audioSteamId = AliyunIEditor.applyMusic(musicBean);//音乐
AliyunIEditor.applyDub(EffectBean effect);//配音

//2.删除背景音乐/配音
AliyunIEditor.removeMusic(EffectBean effect);//音乐
AliyunIEditor.removeDub(EffectBean effect);//配音

//3.调整背景音乐/配音与原音的比重
AliyunIEditor.applyMusicMixWeight(int audioSteamId, int weight);

//4.调整指定音频流音量
//背景音乐、配音、原音,详细请查看API文档
AliyunIEditor.applyMusicWeight(int audioSteamId, int weight);

//5.指定音频流降噪
AliyunIEditor.denoise(int audioSteamId, boolean needDenoise);

音效

短视频SDK支持对每路音频流设置音效,目前提供的音效包括如下:

  • AudioEffectType.EFFECT_TYPE_LOLITA(萝莉)

  • AudioEffectType.EFFECT_TYPE_REVERB(混响)

  • AudioEffectType.EFFECT_TYPE_UNCLE(大叔)

  • AudioEffectType.EFFECT_TYPE_ECHO(回声)

  • AudioEffectType.EFFECT_TYPE_ROBOT(机器人)

  • AudioEffectType.EFFECT_TYPE_BIG_DEVIL(大魔王)

  • AudioEffectType.EFFECT_TYPE_MINIONS(小黄人)

  • AudioEffectType.EFFECT_TYPE_DIALECT(方言)

//1. 设置音效
//AudioEffectType的更多内容请查看API文档
int audioEffect(int audioSteamId, AudioEffectType type, int weight);

//2. 删除音效
//音效支持叠加操作,想要切换音效需要先删除上次设置的音效。
int removeAudioEffect(int audioSteamId, AudioEffectType type);

设置画中画

画中画功能允许在现有主轨道的基础上,添加一个或者多个画中画。

  • 主轨道:编辑页面默认轨道,有且仅有一个主轨道,一个主轨道可以有多个视频流。

  • 画中画:允许添加多个画中画,画中画允许设置位置,缩放,旋转等。创建画中画默认创建一个画中画轨道。画中画可以在不同画中画轨道中移动。

说明

短视频SDK对画中画的个数未做限制,但建议同一时刻画面中最好不要超过3个画中画,具体限制个数由业务方自己做决定。

代码中需要使用的参数详情,请参考接口文档。接口链接请参见相关类功能

//从编辑主接口获取画中画管理类
AliyunIPipManager pipManager = mAliyunIEditor.getPipManager();

//增加画中画
mAliyunIEditor.puase(); //先暂停
long current = mAliyunIEditor.getCurrentPlayPosition(); //获取当前的播放时间点
AliyunIPipManager pipManager = mAliyunIEditor.getPipManager();
AliyunIPipController pipController = pipManager.createNewPip("流文件地址");
pipController.setTimelineStartTime(current) //从轨道当前时间点开始
                .setClipStartTime(0) //画中画视频本身开始时间
                .apply();  //应用生效


//删除画中画
mAliyunIEditor.puase(); //先暂停
AliyunIPipManager pipManager = mAliyunIEditor.getPipManager();
pipManager.removePip(pipController);


//修改布局:AliyunILayoutController
mAliyunIEditor.puase(); //先暂停
AliyunILayoutController layoutController = pipController.getLayoutController();  // 获取布局控制器
layoutController.setRotation(3.14)  //设置旋转弧度 0~3.14
                .setScale(0.3)  //设置缩放
                .setPosition(0.5, 0.5)  //设置位置居中
                .apply();

//获取画中画画布中的位置
RectF rectf = pipController.getPipRectFInCurrentScreen(); //获得画中画所在的矩形区域
int width = mSurfaceView.getWidth() * rectf.width();  //画中画在画布中的实际宽度
int height = mSurfaceView.getWidth() * rectf.height();  //画中画在画布中的实际高度


//修改声音相关:AliyunIAudioController
mAliyunIEditor.puase(); //先暂停
AliyunIAudioController audioController = pipController.getAudioController(); //获取声音控制器
audioController.setVolume(100) //设置音量最大
                .setAudioEffect(AudioEffectType.EFFECT_TYPE_LOLITA)  //设置声音特效为萝莉音
                .apply();  //应用


//设置画中画帧动画:AliyunIAnimationController
mAliyunIEditor.puase(); //先暂停
AliyunIAnimationController animationController = pipController.getAnimationController();
if (mActionTranslate == null) { //添加位移动画
    mActionTranslate = new ActionTranslate();
    mActionTranslate.setFromPointX(-1);
    mActionTranslate.setFromPointY(-1);
    mActionTranslate.setToPointX(1);
    mActionTranslate.setToPointY(1);
    mActionTranslate.setStartTime(pipController.getTimeLineStartTimeInMillis() * 1000);
    mActionTranslate.setDuration(pipController.getClipDurationInMillis() * 1000);
    animationController.addFrameAnimation(mActionTranslate);
} else {  //删除位移动画
    animationController.removeFrameAnimation(mActionTranslate);
    mActionTranslate = null;
}


//画面调节:AliyunIAugmentationController
mAliyunIEditor.puase(); //先暂停
AliyunIAugmentationController augmentationController = pipController.getAugmentationController();   //获取画面调节控制器
augmentationController.setVignette(0~1) //设置暗角
                        .setSharpness(0~1)  //设置锐化程度
                        .setSaturation(0~1)  //设置保护度
                        .apply();  //应用

//获取轨道信息:AliyunIPipTrack
//方式一: 从管理类获取所有轨道
AliyunIPipManager pipManager = mAliyunIEditor.getPipManager();
List<AliyunIPipTrack> pipTrackList = pipManager.getPipTracks(); //获取所有画中画轨道

//方式二: 获取当前控制器所在轨道
AliyunIPipTrack pipTrack = pipController.getOwnerTrack();
List<AliyunIPipController> pipControllers = pipTrack.getPipClips(); //获取当前轨道下所有画中画片段控制器

设置字幕及动态贴纸

字幕及动态贴纸统一通过AliyunPasterManager进行管理,通过AliyunPasterManager获取到对应的AliyunIPasterController进行相应的操作。代码中需要使用的参数详情,请参考接口文档。接口链接请参见相关类功能

基于字幕,短视频SDK还提供了花字及气泡文字的特效。花字、气泡文字及动态贴纸的制作请参见花字动图

字幕

添加/删除字幕

Source fontSouce = null;
long startTime = 0L;
long duration = 20000L;

//1. 添加字幕
AliyunPasterControllerCompoundCaption captionController = pasterManager.addCaptionWithStartTime('输入文字', null, fontSouce, startTime,duration);

//2. 删除字幕
controller.remove()

更新字幕属性

AliyunPasterControllerCompoundCaption包含了字幕的所有操作,详细请查看接口说明。每次更新完字幕属性后,必须调用AliyunPasterControllerCompoundCaption.apply()。

//设置颜色
captionController.setColor(AliyunColor color);
//设置字体
captionController.setFontPath(ISouce fontPath);
//应用以上更新
captionController.apply();

花字

Source fontEffectSource(fontEffectFolder);

//1. 应用花字
captionController.setFontEffectTemplate(fontEffectSource)

//2. 取消花字
captionController.setFontEffectTemplate(null)

文字气泡

Source bubbleEffectSource(bubbleEffectFolder);
//1. 应用气泡
captionController.setBubbleEffectTemplate(bubbleEffectSource)

//2. 取消气泡
captionController.setBubbleEffectTemplate(null)

动态贴纸

添加动态贴纸

AliyunPasterController pasterController = pasterManager.addPasterWithStartTime(Source path, long startTime, long duration);

设置动态贴纸属性

动态贴纸使用与字幕有所差异,动态贴纸是Android去展示的动画效果,因此设置贴纸的属性需要在Android实现UI(AliyunPasterBaseView),定义动态贴纸的大小、宽高、旋转角度等属性。由于是在平台层也同时实现了AliyunPasterBaseView,因此,提供了从渲染层展示、隐藏动态贴纸的操作,避免两者重叠。

//必须调用
pasterController.setPasterView(AliyunPasterBaseView pasterView);

//显示贴纸
pasterController.editCompleted();

//隐藏贴纸
pasterController.editStart();

贴纸效果图预览

//设置预览
pasterController.createPasterPlayer(TextureView view);

//同时手动触发播放/停止预览效果
protected void playPasterEffect() {
        TextureView pv = new TextureView(mPasterView.getContext());
        animPlayerView = mController.createPasterPlayer(pv);
        ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT);
        ViewGroup vg = (ViewGroup) mPasterView.getContentView();
        vg.addView(pv, 0, lp);

    }

    
    protected void stopPasterEffect() {
        ViewGroup vg = (ViewGroup) mPasterView.getContentView();
        vg.removeViewAt(0);
        animPlayerView = null;
    }

删除动态贴纸

pasterController.removePaster();

草稿箱

代码中需要使用的参数详情,请参考接口文档。接口链接请参见相关类功能

工程配置信息获取

//初始化
AliyunIEditor.init(SurfaceView surfaceView, Context context);
//获取配置信息
AliyunEditorProject project = AliyunIEditor.getEditorProject();

初始化草稿管理器

AliyunDraftManager draftManager = AliyunDraftManager.getInstance(context);

获取草稿列表

//异步获取草稿列表
AliyunDraftManager.getInstance(getContext())
                          .getDraftListByAsync(new AliyunDraftListCallback() {
                              @Override
                              public void onFailure(final String msg) {
                                  //获取列表异常
                              }

                              @Override
                              public void onSuccess(final List<AliyunDraft> draftList) {
                                  //草稿列表回调
                              }
                          });

根据草稿ID删除草稿

//删除草稿,draft为草稿列表单个item(草稿列表通过草稿列表接口获取)
AliyunDraftManager.getInstance(v.getContext()).deleteDraft(draft.getId());

草稿重命名

//重命名,draft为草稿列表单个item(草稿列表通过草稿列表接口获取)
AliyunDraftManager.getInstance(v.getContext()).rename(draft.getId(), newName);

草稿复制

//复制后会返回新草稿,draft为草稿列表单个item(草稿列表通过草稿列表接口获取)
AliyunDraft newDraft = AliyunDraftManager.getInstance(v.getContext()).copy(draft.getId());

草稿加载

//draft为草稿列表单个item(草稿列表通过草稿列表接口获取)
AliyunDraftManager.getInstance(v.getContext()).preLoadDraft(draft, new AliyunDraftResourceLoader() {

                @Override
                public void onHandleResourceTasks(final List<AliyunDraftResTask> tasks) {
                    //缺少相关资源,返回需要处理的资源任务,必须对任务进行处理,可选项:修复、忽略、删除
                    HashMap<String, List<AliyunDraftResTask>> map = new HashMap<>();
                    for (AliyunDraftResTask task : tasks) {
                        if (task.getSource() != null && !StringUtils.isEmpty(task.getSource().getURL())) {
                            if (map.containsKey(task.getSource().getURL())) {
                                map.get(task.getSource().getURL()).add(task);
                            } else {
                                List<AliyunDraftResTask> list = new ArrayList<>();
                                list.add(task);
                                map.put(task.getSource().getURL(), list);
                            }
                        } else {
                            //必须对任务进行处理,可选项:修复、忽略、删除
                            if (task.getResModuleType() == AliyunResModuleType.MAIN_VIDEO) {
                                task.getSource().setPath(EditorCommon.SD_DIR + "svideo_res/image/aliyun_svideo_failed.jpg");
                                task.onHandleCallback(task.getSource());
                            } else if(task.getResModuleType() == AliyunResModuleType.TRANSITION) {
                                //删除
                                task.onRemove();
                            } else {
                                //忽略
                                task.onIgnore();
                            }
                        }
                        for (final Map.Entry<String, List<AliyunDraftResTask>> entry : map.entrySet()) {
                            //key为资源地址,Value为对应资源地址需要处理的任务
                            final List<AliyunDraftResTask> list = entry.getValue();
                            try {
                                final String url = entry.getKey();
                                //判断是否是平台资源
                                if (url.startsWith(AlivcResUtil.SCHEME)) {
                                    //平台资源加载回调封装
                                    AlivcResUtil.LoadCallback callback = new AlivcResUtil.LoadCallback() {
                                        @Override
                                        public void onSuccess(String path) {
                                            for (AliyunDraftResTask task : list) {
                                                Source source = task.getSource();
                                                source.setPath(path);
                                                task.onHandleCallback(source);
                                            }
                                        }

                                        @Override
                                        public void onFailure(String type, String msg) {
                                            Log.d("CloudDraft", "loadRes>Failure>type>" + type + ">msg>" + msg);
                                            for (AliyunDraftResTask task : list) {
                                                task.onIgnore();
                                            }
                                        }
                                    };
                                    //加载平台资源,具体代码看demo
                                    AlivcResUtil.loadRes(context, url, callback);
                                } else {
                                    //下载用户资源,具体代码看demo
                                    downloadRes(url, new File(item.getEditorProjectUri()).getParent(), list);
                                }
                            } catch (Exception e) {
                                //出错
                                for (AliyunDraftResTask item : list) {
                                    item.onIgnore();
                                }
                            }
                        }
                    }
                }

                @Override
                public void onFailure(final String msg) {
                    //预加载失败
                    Toast.makeText(v.getContext(), "预加载失败", Toast.LENGTH_SHORT).show();
                }

                @Override
                public void onSuccess() {
                    //预加载处理成功即可进入编辑界面,draft为草稿列表单个item(草稿列表通过草稿列表接口获取),通过draft.getEditorProjectUri()来加载草稿
                    EditorActivity.startEdit(v.getContext(), draft);
                }
            });

上传草稿

上传草稿需配合草稿服务端,服务端简单代码示例下载

//draft为草稿列表单个item(草稿列表通过草稿列表接口获取)
AliyunDraftManager.getInstance(context)
                          .uploadDraft(draft, new AliyunDraftResourceUploader() {
                              @Override
                              public void onHandleResourceTasks(final List<AliyunDraftResTask> tasks) {
                                  //需要处理的上传资源任务
                                  HashMap<String, List<AliyunDraftResTask>> map = new HashMap<>();
                                  //过滤重复资源
                                  for (AliyunDraftResTask task : tasks) {
                                      if (task.getSource() == null) {
                                          task.onIgnore();
                                          continue;
                                      }
                                      //URL为空或者不以alivc_resource开头需要做上传处理
                                      String url = task.getSource().getURL();
                                      if (StringUtils.isEmpty(url) || !url.startsWith("alivc_resource")) {
                                          if (map.containsKey(task.getSource().getPath())) {
                                              map.get(task.getSource().getPath()).add(task);
                                          } else {
                                              List<AliyunDraftResTask> list = new ArrayList<>();
                                              list.add(task);
                                              map.put(task.getSource().getPath(), list);
                                          }
                                      } else {
                                          //忽略出错
                                          task.onIgnore();
                                      }
                                  }
                                  for (Map.Entry<String, List<AliyunDraftResTask>> entry : map.entrySet()) {
                                      try {
                                          for (AliyunDraftResTask task : tasks) {
                                              Source source = task.getSource();
                                              //上传成功后回调远程地址
                                              source.setURL();
                                              task.onHandleCallback(source);
                                          }
                                      } catch (Exception e) {
                                          //忽略出错
                                          List<AliyunDraftResTask> list = entry.getValue();
                                          for (AliyunDraftResTask item:list){
                                              item.onIgnore();
                                          }
                                      }
                                  }
                              }

                              @Override
                              public void onSuccess(final String projectPath, String coverUrl) {
                                  //资源全部上传成功后,返回工程配置地址和封面地址
                                  //可自行处理上传到云端,其他用户就可以通过工程配置地址恢复草稿到编辑状态
                              }

                              @Override
                              public void onFailure(final String msg) {
                                  Toast.makeText(context,"备份失败",Toast.LENGTH_SHORT).show();
                              }
                          });

下载草稿

下载草稿需配合草稿服务端,服务端简单代码示例下载

//根据草稿工程配置下载草稿相关资源,file为工程配置文件(草稿备份后从服务器下载下来的)
AliyunDraftManager.getInstance(context).downloadDraft(file, new AliyunDraftResourceDownloader() {
            @Override
            public void onHandleResourceTasks(final String projectDir, final List<AliyunDraftResTask> tasks) {
                //待处理的草稿资源任务,需要下载草稿里面包含的资源
                HashMap<String, List<AliyunDraftResTask>> map = new HashMap<>();
                //过滤重复资源
                for (AliyunDraftResTask task : tasks) {
                    if (task.getSource() == null || StringUtils.isEmpty(task.getSource().getURL())) {
                        task.onIgnore();
                    } else if (map.containsKey(task.getSource().getURL())) {
                        map.get(task.getSource().getURL()).add(task);
                    } else {
                        List<AliyunDraftResTask> list = new ArrayList<>();
                        list.add(task);
                        map.put(task.getSource().getURL(), list);
                    }
                }
                for (final Map.Entry<String, List<AliyunDraftResTask>> entry : map.entrySet()) {
                    final List<AliyunDraftResTask> list = entry.getValue();
                    try {
                        final String url = entry.getKey();
                        //这里需要根据url下载草稿资源
                        for (AliyunDraftResTask task : list) {
                            Source source = task.getSource();
                            //下载完成后回调资源本地地址
                            source.setPath(path);
                            //如果是MV则解压出ID赋值给Source供显示还原
                            if (task.getResModuleType() == AliyunResModuleType.MV) {
                                try {
                                    source.setId(Uri.parse(url).getQueryParameter("gid"));
                                }catch (Exception ignored){
                                }
                            }
                            task.onHandleCallback(source);
                        }
                    } catch (Exception e) {
                        //出错
                        for (AliyunDraftResTask item : list) {
                            item.onIgnore();
                        }
                    }
                }

            }

            @Override
            public void onSuccess(final AliyunDraft draft) {
                //资源全部下载完成后设置服务端ProjectID到本地草稿用来关联云端草稿
                AliyunDraftManager.getInstance(context).setProjectId(draft.getId(), projectId);
                Toast.makeText(context,"成功恢复到本地",Toast.LENGTH_SHORT).show();
                //恢复成功后就可以在本地草稿列表查看
            }

            @Override
            public void onFailure(final String msg) {
                Toast.makeText(context,"恢复到本地失败",Toast.LENGTH_SHORT).show();
            }
        });

其他设置

代码中需要使用的参数详情,请参考接口文档。接口链接请参见相关类功能

时间特效

// 1.变速
//3.7.0版本开始变速接口可以针对多段视频/图片添加
int effectId;
effectId = AliyunIEditor.rate(float rate, long startTime, long duration, boolean needOriginDuration);

// 2.反复
effectId = AliyunIEditor.repeat(int times, long startTime, long duration, boolean needOriginDuration);

//3.倒放
//特别注意:对于GOP大于5的视频需要先转码,否则将无法倒播。对于视频GOP的检查可以用NativeParser.getMaxGopSize()来获取。转码时将GOP SIZE设置为1,即CropParam.setGop(1);
effectId = AliyunIEditor.invert();


// 删除时间特效
AliyunIEditor.deleteTimeEffect(effectId);

水印

水印分为普通水印及片尾水印。普通水印会贯穿整个视频流时长,而片尾水印会在视频流结尾处添加水印。

//水印例子 水印的大小为 :水印图片的宽高和显示区域的宽高比,注意保持图片的比例,不然显示不完全
//水印的位置为 :以水印图片中心点为基准,显示区域宽高的比例为偏移量,0,0为左上角,1,1为右下角

//普通水印
AliyunIEditor.applyWaterMark(String imgPath, float sizeX, float sizeY, float posX, float posY);

//片尾水印
AliyunIEditor.addTailWaterMark(String imagePath, float sizeX, float sizeY, float posX, float posY, long durationUs);

涂鸦

短视频SDK封装了一套涂鸦接口,包含画板、画笔等,整个涂鸦操作由涂鸦控制器(AliyunICanvasController)完成。

  • 画板:涂鸦对应的UI交互View,可以添加到UI交互的ViewGroup。

  • 画笔:一个android.graphics.Paint对象,开发者可以从外部设置,也可以使用默认画笔。

// 获取涂鸦控制器
int width = 600;
int height = 800
AliyunICanvasController controller = AliyunIEditor.obtainCanvasController( context, width, height);


// 获取涂鸦画板
View canvasView = controller.getCanvas();

// 进行涂鸦

// 应用涂鸦
controller.applyPaintCanvas();

// 释放资源
controller.release();

//*******
//其他操作
//*******

// 撤销上一笔
controller.undo();

// 清除画布
controller.clear();

// 移除涂鸦

controller.removeCanvas();

// 判断是否由涂鸦
controller.hasCanvasPath();