Edit videos

更新时间:
复制 MD 格式

The short video SDK provides various video editing features. You can import a combination of video and image assets and apply editing effects, such as filters, dubbing, time effects, and Picture-in-Picture (PiP). This topic describes how to edit videos on iOS using the short video SDK.

Supported editions

Edition

Supported

Professional Edition

All features are supported.

Standard Edition

Partially supported. Supports all features except for captions, animated stickers, and MVs.

Basic Edition

Not supported.

Related classes

Operation

Class name

Feature

Initialization

AliyunEditor

Core video editing class.

Start editing

AliyunIClipConstructor

Video source manager.

AliyunClip

Media clip.

AEPVideoTrack

Main track in the project configuration.

AEPVideoTrackClip

Main clip in the project configuration.

Preview control

AliyunIPlayer

Playback protocol.

AliyunIPlayerCallback

Playback status callback protocol.

Set video effects

AliyunEffectFilter

Filter effect model class.

AliyunEffectTimeFilter

Time effect model class.

AliyunTransitionEffect

Base class for transition effects.

AliyunEffectMV

MV effect model class.

AliyunFilterManager

Filter manager for managing Lookup Table (LUT) and static filters.

AliyunLutFilterController

LUT filter controller.

AliyunLutFilter

LUT filter model.

AliyunShaderFilterController

Static filter controller.

AliyunShaderFilter

Static filter model.

Set music and sound effects

AliyunEffectMusic

Music.

AliyunEffectDub

Dubbing.

AEPAudioTrack

Audio track in the project configuration.

AEPAudioTrackClip

Audio track clip in the project configuration.

Set Picture-in-Picture (PiP)

AliyunPipManager

PiP management class.

AliyunPipTrackController

PiP track controller.

AliyunPipClipController

PiP clip controller.

AliyunPipClip

PiP data model.

AEPPipVideoTrackClip

PiP clip in the project configuration.

AEPPipVideoTrack

PiP track in the project configuration.

AliyunClipAugmentationInfo

Image enhancement information.

AliyunClipAudioInfo

Audio-related information.

AliyunPureColorBorderInfo

Border-related information.

Set captions and stickers

AliyunStickerManager

Sticker and caption manager.

AliyunCaptionStickerController

Caption controller.

AliyunGifStickerController

Animated sticker controller.

AliyunImageStickerController

Static graph controller

AliyunCaptionSticker

Caption data model.

AliyunGifSticker

Animated sticker data model.

AliyunImageSticker

Static graph data model

AEPGifStickerTrack

Animated sticker track in the project configuration.

AEPImageStickerTrack

Static sticker track in the project configuration.

AEPCaptionTrack

Caption track in the project configuration.

Draft box

AliyunEditorProject

Project configuration.

AliyunDraft

Draft object.

AliyunDraftManager

Local draft manager.

AliyunDraftLoadTask

Draft resource loading task.

AliyunDraftProjectUploadTask

Draft upload task.

AEPSource

Resource object.

AEPResourceModel

Specific resource model in a loading task.

Other settings

AliyunICanvasView

Doodle canvas view.

AliyunIPaint

Brush.

Video editing process

Stage

Flow

Description

Sample code

Basic

1

Create and initialize the editor.

Initialization

2

Dynamically crop videos, change video sources, and adjust video transition times and effects during editing.

Video management

3

Set up the editor for preview playback.

Preview control

Advanced

4

Set filters, transitions, and MV effects.

Set video effects

5

Set background music, dubbing, and sound effects.

Set music and sound effects

6

Set up PiP.

Set Picture-in-Picture (PiP)

7

Set captions, word art, text bubbles, and stickers.

Set captions and stickers

8

Edit videos from the draft box, or save edited videos to the draft box.

Draft box

9

Set up doodles.

Other settings

Initialization

You can create and initialize the editor. For more information about the parameters used in the code, see the API documentation referenced in Related classes.

// 1. Task path: A task is generated for each media editing state. Provide a path to store the task during editor initialization. The task must be initialized first.
NSString *taskPath = @"xxx"; // taskPath is the path of the imported video, which is usually imported from local media resources or the draft box.

// 2. Preview view: After you set a preview view, each operation during the editing process is displayed on the preview view in real time.
UIView *preview = xxx;

// 3. Instantiate
AliyunEditor *editor = [[AliyunEditor alloc] initWithPath:taskPath preview:preview];
            
Note

To ensure that the preview matches the final output, set the aspect ratio of your preview view to match the resolution of the output video.

Video management

Videos and images in the video editor are managed by the AliyunIClipConstructor video source manager. After you modify videos or images using AliyunIClipConstructor, the changes take effect only after you call [editor startEdit].

For more information about the parameters used in the code, see the API documentation referenced in Related classes.

// 1. Get the video source manager.
id<AliyunIClipConstructor> contructor = [editor getClipConstructor];

// 2. Add a clip.
// 2.1 Stop editing first.
[editor stopEdit]; 
// 2.2 Create a video clip.
AliyunClip *clip = [[AliyunClip alloc] initWithVideoPath:videoPath startTime:2 duration:6 animDuration:0];
// 2.3.1 Append the video clip to the end.
[contructor addMediaClip:clip];
// 2.3.2 Or, add the video clip to a specified index.
[contructor addMediaClip:clip atIndex:1];
// 2.4 Start editing.
[editor startEdit]; 

// 3. Update a clip (replace a clip).
[contructor updateMediaClip:clip atIndex:1];

// 4. Delete a clip.
// 4.1 Delete the last clip.
[contructor deleteLastMediaClip];
// 4.2 Delete a clip at a specified index.
[contructor deleteMediaClipAtIndex:1];
// 4.3 Delete all clips.
[contructor deleteAllMediaClips];

// 5. Get all current clips.
NSArray<AliyunClip *> *mediaClips = contructor.mediaClips;

Preview control

During video editing, you can perform playback control operations on the current video, such as playing, pausing, and retrieving the current duration. For more information about the parameters used in the code, see the API documentation referenced in Related classes.

Playback control

// Get the preview player.
id<AliyunIPlayer> player = [editor getPlayer];

//Start playback.    
[player play];

//Resume playback.
[player resume];

// Pause playback.
[player pause];

// Seek to a specified time.
[player seek:timeInSecond];

// Mute.
[editor setMute:YES];

// Set the volume.
[editor setVolume:50];

//Get the current stream time - not affected by time effects.
double currentTime = [player getCurrentStreamTime];

// Get the current playback time - affected by time effects.
double currentTime = [player getCurrentTime];

// Get the stream duration - not affected by time effects.
double duration = [player getStreamDuration];

// Get the total playback duration - affected by time effects.
double duration = [player getDuration];

Playback callback

// Listen for playback status.
editor.playerCallback = self;

// Protocol
- (void)playerDidEnd {
    // Playback finished callback.
}

- (void)playProgress:(double)playSec streamProgress:(double)streamSec {
    // Playback progress callback.
}

- (void)playError:(int)errorCode {
    // Playback error callback.
}
            

Set video effects

You can apply video effects, such as filters, transitions, MVs, and time effects. For more information about the parameters used in the code, see the API documentation referenced in Related classes.

Filters

  • Filters are categorized as LUT filters, static filters, and animated filters.

    • LUT filter: Uses a lookup table (LUT) to replace pixels.

    • Static filter: Calculates pixels using a shading language. Animated effects are not supported.

    • Animated filter: Calculates pixels using a shading language and supports animated effects.

  • You can create custom filters. For more information, see Filters and transitions.

LUT filters

// 1. Add a LUT filter.
AliyunLutFilterController *controller = [[editor getFilterManager] applyLutFilterWithPath:path intensity:intensity];
if (!controller) {
    // Failed to add the LutFilter.
}

// 2. Update the intensity.
controller.model.intensity = 0.5;

// 3. Delete the LUT filter.
[[editor getFilterManager] removeFilter:controller];

Static filters

// 1. Add a static filter.
AliyunShaderFilterController *controller = [[editor getFilterManager] applyShadeFilterWithPath:path];
if (!controller) {
    // Failed to add the static filter.
}

// 2. Delete the static filter.
[[editor getFilterManager] removeFilter:controller];

Animated filters

// 1. Add an animated filter.
AliyunEffectFilter *animationFilter = [[AliyunEffectFilter alloc] initWithFile:filterFolder];
animationFilter.startTime = 2;
animationFilter.endTime = 10;
// ... For other properties, see the API documentation.
[editor applyAnimationFilter:animationFilter];

// 2. Modify the animated filter.
animationFilter.startTime = 4;
animationFilter.endTime = 12;
// ... For other properties, see the API documentation.
[editor updateAnimationFilter:animationFilter];

// 3. Delete the animated filter.
[editor removeAnimationFilter:animationFilter];

Transitions

  • You can create custom transitions. For more information, see Filters and transitions.

  • The short video SDK provides the following transition effects: AliyunTransitionEffectTypeCircle (circle open), AliyunTransitionEffectTypeFade (fade in and fade out), AliyunTransitionEffectTypePolygon (pentagon), AliyunTransitionEffectTypeShuffer (shutter), and AliyunTransitionEffectTypeTranslate (translate). For more information about the parameters, see AliyunTransitionEffectType.

Transition position

A transition must be set between two clips. Therefore, you cannot add a transition if you have only one video clip. The transition position index starts from 0. The following example shows the meaning of the position:

[----Video Clip A----] [----Video Clip B----] [----Video Clip C----]...[----Video Clip N----]
                     ^                ^                 ^
 Position:           0                1                N-1 -->

Set a transition

//Add
//Note: Before using this API, you must call [editor stopEdit], then call this API, and then call [editor startEdit] for the changes to take effect.
AliyunTransitionEffect *transition = [[AliyunTransitionEffect alloc] initWithPath:transitionFolder];
[editor applyTransition:transition atIndex:0];

//Update
//Note: The duration of the transition cannot exceed the duration of the shorter of the two adjacent video clips.
transition.overlapDuration = 1;
// ... For other properties, see the API documentation.
[editor updateTransition:transition atIndex:0];

//Delete
[editor removeTransitionAtIndex:0];

MV

You can create custom MVs. For more information, see MV.

// Add an MV.
AliyunEffectMV *mv = [[AliyunEffectMV alloc] initWithFile:mvFolder];
[editor applyMV:mv];

// Mute the MV.
[editor removeMVMusic];

// Delete the MV.
[editor removeMV];

Time effects

The short video SDK provides the following time effects: TimeFilterTypeSpeed (speed ramping), TimeFilterTypeRepeat (repeat), and TimeFilterTypeInvert (reverse). For more information about the parameters, see TimeFilterType.

Note

When you set reverse playback, if the video's group of pictures (GOP) is too large (for example, 35), you must first transcode the video before you can apply the reverse effect. You can retrieve the GOP size using AliyunNativeParser. You can transcode the video by cropping it.

// 1. Add a time effect.
// 1.1 Speed ramping
AliyunEffectTimeFilter *timeFilter = [[AliyunEffectTimeFilter alloc] init];
timeFilter.type = TimeFilterTypeSpeed;
timeFilter.param = 0.67;
timeFilter.startTime = 2;
timeFilter.endTime = 10;
// ...For other properties, see the API documentation.
[editor applyTimeFilter:timeFilter];

// 1.2 Repeat
AliyunEffectTimeFilter *timeFilter = [[AliyunEffectTimeFilter alloc] init];
timeFilter.type = TimeFilterTypeRepeat;
timeFilter.param = 3; // For example, repeat 3 times.
timeFilter.startTime = 2;
timeFilter.endTime = 10;
// ...For other properties, see the API documentation.
[editor applyTimeFilter:timeFilter];

// 1.3 Reverse
AliyunEffectTimeFilter *timeFilter = [[AliyunEffectTimeFilter alloc] init];
timeFilter.type = TimeFilterTypeInvert;
// ...For other properties, see the API documentation.
[editor applyTimeFilter:timeFilter];


// 2. Delete the time effect.
[editor removeTimeFilter:timeFilter];

Set music and sound effects

You can add music and dubbing and apply various sound effects to the audio, such as fade-in, fade-out, and voice changing effects. For more information about the parameters used in the code, see the API documentation referenced in Related classes.

Music

Music includes background music and dubbing. Background music is not affected by time effects, such as speed ramping, repeat, or reverse playback. Dubbing is affected by time effects.

Background music

// 1. Add background music.
AliyunEffectMusic *music =[[AliyunEffectMusic alloc] initWithFile:musicFilePath];
music.duration = 3;
music.audioMixWeight = 50; // Volume (mix weight)
// ... For other properties, see the API documentation.
[editor applyMusic:music];

// 2. Delete the background music.
[editor removeMusic:music];

Dubbing

// 1. Add dubbing.
AliyunEffectDub *dub =[[AliyunEffectDub alloc] initWithFile:dubFilePath];
dub.startTime = 2;
dub.audioMixWeight = 50; // Volume (mix weight)
dub.audioDenoiseWeight = 50; // Denoising level
// ... For other properties, see the API documentation.
[editor applyDub:dub];

// 2. Delete the dubbing.
[editor removeDub:dub];

Sound effects

  • Fade-in and fade-out: Supports AliyunAudioFadeShapeLinear (linear curve) and AliyunAudioFadeShapeSin (sine function curve). For more information about the parameters, see AliyunAudioFadeShape.

  • Voice changing effects: For more information about the parameters, see AliyunAudioEffectType.

    • AliyunAudioEffectLolita (lively female voice)

    • AliyunAudioEffectUncle (husky male voice)

    • AliyunAudioEffectReverb (reverb)

    • AliyunAudioEffectEcho (echo)

    • Alibaba Cloud Audio Effect Robot (robot)

    • Big Devil (AliyunAudioEffectBigDevil)

    • Alibaba Cloud Audio Effect Minions (Minions)

    • AliyunAudioEffectDialect (dialect)

Fade-in and fade-out

// 1. Specify the fade-in effect before adding audio (fade-out is similar, using the fadeOut property).
AliyunAudioFade *fadeIn = [[AliyunAudioFade alloc] init];
fadeIn.shape = AliyunAudioFadeShapeLinear;
fadeIn.duration = 2;
music.fadeIn = fadeIn; // Set for dubbing in the same way.

// 2. Modify the fade-in effect after adding audio (fade-out is similar, using the function setAudioFadeOutShape:duration:streamId:).
[editor setAudioFadeInShape:AliyunAudioFadeShapeLinear duration:2 streamId:music.effectVid];

// 3. Delete the fade-in effect after adding audio (fade-out is similar, using the function removeAudioFadeOutWithStreamId:).
[editor removeAudioFadeInWithStreamId:music.effectVid]; // Call in the same way for dubbing.

Voice changing

// 1. Set the voice changing effect before adding audio.
AliyunAudioEffect *audioEffect = [[AliyunAudioEffect alloc] init];
audioEffect.type = AliyunAudioEffectLolita;
audioEffect.weight = 50;
[dub.audioEffects addObject:audioEffect]; // Note: Only one effect can be added at a time.

// 2. Set the voice changing effect after adding audio.
[editor setAudioEffect:AliyunAudioEffectLolita weight:50 streamId:dub.effectVid];

// 3. Delete the voice changing effect after adding audio.
[editor removeAudioEffect:AliyunAudioEffectLolita streamId:dub.effectVid];

Set Picture-in-Picture (PiP)

The Picture-in-Picture (PiP) feature lets you add one or more PiP tracks on top of the existing main track.

  • Main track: The default track on the editing page. There is only one main track, which can contain multiple video streams.

  • PiP track: You can add multiple PiP tracks and set their properties, such as position, scaling, and rotation. When you create a PiP effect, a PiP track is created by default. A PiP effect can be moved between different PiP tracks.

For more information about the parameters used in the code, see the API documentation referenced in Related classes.

//Get the PiP manager. The PiP manager is responsible for adding, deleting, modifying, and querying PiP effects.
AliyunPipManager * pipManager = [editor getPipManager];


//Add a PiP effect.
// 1. Add a PiP track to the top layer and add the PiP clip to that track.
// 1.1 Add a video clip.
NSError *error = nil;
AliyunPipClipController *pipClipController = [pipManager addClipWithType:AliyunPipClipTypeVideo path:xxVideoPath error:&error];
// 1.2 Add an image clip.
NSError *error = nil;
AliyunPipClipController *pipClipController = [pipManager addClipWithType:AliyunPipClipTypeImage path:xxImagePath error:&error];

// 2. Insert a PiP effect into a specified PiP track.
// 2.1 Create a PiP clip.
AliyunPipClip *pipClip = [[AliyunPipClip alloc] initWithClipType:AliyunPipClipTypeVideo clipPath:xxxVideoPath];
// ... For more PiP clip settings, see the document: https://alivc-demo-cms.alicdn.com/versionProduct/doc/shortVideo/iOS_cn/Classes/AliyunPipClip.html

// 2.2 Insert into the specified track.
NSError *error = nil;
AliyunPipClipController *pipClipController = [pipManager addClipWithModel:pipClip toTrack:pipManager.trackControllers.firstObject error:&error]; // For example, add to the first track.


//Delete a PiP effect.
NSError *error = nil;
[pipManager removePipClipController:pipClipController error:&error];


//Switch PiP tracks.
[pipManager movePipClipController:pipController toTrack:pipManager.trackControllers.firstObject withStartTime:0]; // For example, move to the start of the first track.


//Modify a PiP clip.
// For example, directly modify the PiP position.
pipController.clip.center = CGPointMake(100, 100);

// For example, batch modify the position, size, and rotation.
[pipController beginEdit];
pipController.clip.center = CGPointMake(100, 100);
pipController.clip.scale = 0.7;
pipController.clip.rotation = M_PI_2;
[pipController endEdit];

//Click test.
// For example, get the topmost PiP clip at a touch point at the current time.
double currentTime = [[editor getPlayer] getCurrentTime];
AliyunPipClipController *pipClipController = [pipManager hitTest:touchPoint withTime:currentTime];

Set captions and stickers

  • Captions and stickers are managed by the AliyunStickerManager. The states of captions and stickers are managed by the AliyunCaptionStickerController and the AliyunStickerController, respectively. Both the caption controller and the sticker controller inherit from the base rendering controller AliyunRenderBaseController. Therefore, the logic for modifying captions or stickers is the same as the logic for modifying the properties of base elements.

  • The short video SDK also provides word art and text bubble effects that are based on captions. You can create custom word art and text bubbles. For more information about the specifications and methods, see Word art and Animated stickers.

  • Stickers are categorized as static stickers and animated stickers. Animated stickers are similar to text bubbles but do not contain a text part. You can create custom animated stickers. For more information about the specifications and methods, see Animated stickers.

  • For more information about the parameters used in the code, see the API documentation referenced in Related classes.

Manager

  • The manager is responsible for adding, deleting, modifying, and querying captions and stickers. For more information about the parameters, see AliyunStickerManager.

  • Both captions and stickers inherit from the AliyunRenderModel rendering model and have the properties of base rendering elements. You can find the controller for the topmost caption or sticker at a specific time based on the click position in the preview coordinate system.

//Get the manager.
AliyunStickerManager *stickerManager = [editor getStickerManager];

//Add
// For example, add a caption.
AliyunCaptionStickerController *captionController = [stickerManager addCaptionText:@"Hello" bubblePath:nil startTime:0 duration:5];

//Delete
[stickerManager remove:gifController];

//Find
// For example, get the topmost caption or sticker at a touch point at the current time.
double currentTime = [[editor getPlayer] getCurrentTime];
AliyunRenderBaseController *controller = [stickerManager findControllerAtPoint:touchPoint atTime:currentTime];

Caption

//Add a caption.
AliyunCaptionStickerController *captionController = [stickerManager addCaptionText:@"Hello" bubblePath:nil startTime:0 duration:5];

//Modify the caption.
[captionController beginEdit];
captionController.model.text = xxx;
captionController.model.outlineWidth = 3;
captionController.model.outlineColor = UIColor.redColor;
// ... For other property modifications, see the AliyunCaptionSticker API documentation.
[captionController endEdit];

Word art

// Apply word art.
captionController.model.fontEffectTemplatePath = fontEffectFolder; // Directory of the word art effect resource package.
// Cancel word art.
captionController.model.fontEffectTemplatePath = nil;

Text bubble

// Apply a bubble.
captionController.model.resourePath = bubbleEffectFolder; // Directory of the text bubble effect resource package.
// Cancel the bubble.
captionController.model.resourePath = nil;

Animated sticker

//Add an animated sticker.
AliyunGifStickerController *gifController = [stickerManager addGif:gifFilePath startTime:0 duration:5];


//Modify the animated sticker.
[gifController beginEdit];
gifController.gif.center = xxx;
// ...For other property modifications, see the AliyunGifSticker API documentation.
[gifController endEdit];

Static sticker

//Add a static sticker.
AliyunImageStickerController *imageController = [stickerManager addImage:imageFilePath startTime:0 duration:5];

//Modify the static sticker.
[imageController beginEdit];
imageController.image.center = xxx;
// ...For other property modifications, see the AliyunImageSticker API documentation.
[imageController endEdit];

Draft box

Each edit generates an editing task that is recorded as a project configuration. If you do not want to immediately export the current editing result, you can save the editing state as a draft. You can then load the draft to restore the previous editing state and continue editing. For more information about the parameters used in the code, see the API documentation referenced in Related classes.

Project configuration

The editing state is described by a draft object in a timeline structure. Each editing action is reflected as a state change in this project configuration. We recommend that you associate the project configuration with your editing interface. When you restore a draft, you can use this project configuration to restore the interface state that corresponds to the editing state.

// Get the project configuration object during editing.
AliyunEditorProject *project = [editor getEditorProject];

Draft object

An editing state can be saved as an AliyunDraft draft object. You can save the current editing state as follows:

// 0. Before saving as a draft, define where the draft should be saved. We provide a draft management object as a container for draft storage management.
AliyunDraftManager *draftManager; // For an introduction to draft management, see the documentation below.

// 1. Specify the draft title to save it. You must specify where to save it. A draft object is returned.
AliyunDraft *draft = [editor saveToDraft:draftManager withTitle:@"your draft title"];

// 2.1 Do not specify a draft title (the title of the previous draft is used).
AliyunDraft *draft = [editor saveToDraft:draftManager];

// 2.2 You can modify the title after obtaining the draft object.
[draft renameTitle:@"your draft title"];

To better identify a draft and help you develop local or cloud draft services, the short video SDK provides an ID to identify a draft. You can call an API to set this ID to your service's identifier.

[draft changeProjectId:@"your custom project id"];

Draft Thumbnail

During the editing process, a suitable thumbnail is automatically generated. The thumbnail is usually the first frame of the video. If the thumbnail is unsuitable, you can use an API to replace it with a custom thumbnail.

[editor updateCover:yourCoverImage];

// You can also set it to nil to have one automatically generated for you (default).
[editor updateCover:nil];

You can also change the thumbnail after you obtain the draft object.

[draft updateCover:yourCoverImage];
Note

If you need a custom draft thumbnail, set it as soon as possible. After a custom draft thumbnail is specified, the thumbnail content is no longer automatically updated. Therefore, setting the thumbnail early reduces unnecessary update actions and improves editing performance.

Draft management

Important

Because draft management parses all managed draft objects during initialization, it incurs a certain amount of performance overhead. We recommend that you use it as a global object and avoid creating and destroying it frequently.

//Initialization
//For better user isolation, use an ID as the identifier for draft management. We recommend that you use the user's identifier as the identifier for draft management.
NSString *draftManagerId; 
//Instantiate
AliyunDraftManager *draftManager = [[AliyunDraftManager alloc] initWithId:draftManagerId];


//Draft list

// Get the draft list.
NSArray<AliyunDraft *> *draftList = draftManager.draftList;
// You can also listen for changes to the draft list.
draftManager.delegate = yourListener;
// Implement the AliyunDraftManagerDelegate protocol.
// - (void) onAliyunDraftManager:(AliyunDraftManager *)mgr listDidChange:(NSArray<AliyunDraft *> *)list;


//Delete a draft.
[draftManager deleteDraft:targetDraft];


//Copy a draft.
[draftManager copyDraft:fromDraft toPath:newDraftTaskPath withTitle:@"your new draft title"];

Draft loading

For performance and storage considerations, not all resources used in the editing process are copied to the task directory. Therefore, before you restore an editing state from a draft, you must ensure that all required resources are ready by handling the resource loading tasks. Pay special attention to the following two types of resources:

  • Media resources in the album: Ensure that you have read permissions before you start editing.

  • Dynamically customized font resources: Ensure that the corresponding font is registered in the system, or at least in the current app session, before you start editing.

[draft load:^(NSArray<AliyunDraftLoadTask *> *tasks) {
    for (AliyunDraftLoadTask *task in tasks) {
        // Process the resource loading task.
        // 1. Get the resource model of the current task.
        AEPResourceModel *resource = task.resource;

        // 2. Process...
        // For handling more resource types, see AliyunDraftLoader.m in the official demo.

        // 3. Mark the processing result.
        // 3.1.1 Success: Ignore the result.
        [task onIgnore];
        // 3.1.2 Success: The properties of the resource model need to be modified.
        AEPSource *resultSoruce = [resource.source createWithPath:@"result path"]; // For example, the resource path has changed after loading.
        [task onSuccess:resultSoruce];

        // 3.2.1 Failed: Delete the corresponding node and continue loading.
        [task onFailToRemove]; // For example, if the font for a caption cannot be loaded, choose to delete the caption and continue loading.
        // 3.2.2 Failed: Mark the entire load as failed. Use the error below as the reason for the overall load failure.
        NSError *error = xxxx; // You need to provide the specific reason for the load failure.
        [task onFailToStopWithError:error]; // When this failure occurs, actively stop other loading tasks.
    }
} completion:^(NSString *taskPath, AliyunEditorBaseProject *project, NSError *error) {
    if (!taskPath || !project || error) {
        // Handle load failure...
        // Generally, error.localizedDescription will contain the detailed reason for failure.
        return;
    }

    // Load successful.
    // You can use taskPath to create an Editor to get the restored editing state. For more information, see the initialization document for video editing.
}];

Draft upload

The editing state consists mainly of the project configuration and resources. Therefore, you can implement cloud drafts by synchronizing these two parts to the cloud. For more information about the parameters, see AliyunDraftProjectUploadTask.

For performance and storage considerations, not all resources used in editing are copied by default. However, the draft records the descriptions of all used resources. To better index resources, various methods are provided to describe a resource, such as AEPSource.path (local path) and AEPSource.sourceId (resource ID). For more information, see AEPSource. During the loading, uploading, and downloading of draft resources, in addition to providing resource descriptions, information is also provided, such as the node object where the currently loaded resource is located and the timeline module to which it belongs. For more information, see AEPResourceModel.

Uploading a draft involves the following two steps:

  1. Upload all resources used in editing. This process modifies the resource descriptions in the project configuration.

  2. Upload the project configuration file with the modified resource descriptions.

[draft uploadWithResourceUploader:^(NSArray<AliyunDraftLoadTask *> *tasks) {
    for (AliyunDraftLoadTask *task in tasks) {
        // Process the upload task.
        // 1. Get the resource model of the current task.
        AEPResourceModel *resource = task.resource;

        // 2. Process...
        // For handling more resource types, see AliyunDraftLoader.m in the official demo.

        // 3. Mark the processing result.
        // 3.1.1 Success: Ignore the result.
        [task onIgnore]; // For example, built-in service resources do not need to be uploaded.
        // 3.1.2 Success: The properties of the resource model need to be modified.
        AEPSource *resultSoruce = [resource.source createWithURL:@"resource URL"]; // For example, get a network link after uploading the resource.
        [task onSuccess:resultSoruce];

        // 3.2.1 Failed: Delete the corresponding node and continue uploading.
        [task onFailToRemove]; // For example, if the font for a caption fails to upload, choose to delete the caption and continue uploading others.
        // 3.2.2 Failed: Mark the entire upload as failed. Use the error below as the reason for the overall upload failure.
        NSError *error = xxx; // You need to provide the specific reason for the upload failure.
        [task onFailToStopWithError:error]; // When this failure occurs, actively stop other upload tasks.
    }
} projectUploader:^(AliyunDraftProjectUploadTask *projTask) {
    // Add cloud draft processing.
    // 1.1 Get the project configuration file path of the current draft.
    NSString *projectFilePath = projTask.projectFilePath;
    // 1.2 Process the project configuration file.
    NSString *projectUrl = [yourUploader upload:projectFilePath]; // For example, upload the configuration file and return the network path.

    // 2.1 Synchronize the cloud draft.
    NSString *projectId = [yourApi addCloudDraft:projectUrl]; // For example, call your business service to return an identifier ID.
    // 2.2 Update the project ID of the draft.
    [projTask.draft changeProjectId:projectId];
    
    // 3. Mark the processing result.
    // 3.1 Success
    [projTask onSuccess];
    // 3.2 Failed
    NSError *error = xxx; // You need to provide the reason for the failure.
    [projTask onFailWithError:error];
} completion:^(NSError *error) {
    if (error) {
        // Handle upload failure...
        return;
    }
        
    // Handle upload success...
}];

Draft download

Because the project configuration of a draft records the descriptions of all used resources, you can enumerate all resources by providing the project configuration file. You can then complete the draft download by synchronizing the resources to your local device.

Similar to saving a draft, you need to determine where to save the downloaded draft. Therefore, the download API is defined in the local draft manager AliyunDraftManager.

// 0. Get the project configuration file and project ID corresponding to the cloud draft through your business service.
NSString *projectFilePath = xxx;
NSString *projectId = xxx;

// 1. Download the draft.
[draftManager downloadDraftWithProjectFile:projectFilePath  resourceDownloader:^(NSArray<AliyunDraftLoadTask *> *tasks) {
    for (AliyunDraftLoadTask *task in tasks) {
        // Process the download task.
        // 1. Get the resource.
        AEPResourceModel *resource = task.resource;

        // 2. Process...
        NSString *localPath = [yourDownloader download:resource.source.URL]; // For example, network download.

        // 3. Mark the processing result.
        AEPSource *localSource = [resource.source createWithPath:localPath]; // For example, downloaded to local.
        [task onSuccess:localSource];
        // For more result markings, see the loading task.
    }
} completion:^(AliyunDraft *draft, NSError *error) {
    if (error || !draft) {
        // Handle download failure...
        return;
    }

    // Handle download success...
    [draft changeProjectId:projectId]; // We recommend synchronizing the cloud ID.
    // Other processing...
}];

Other settings

Doodle

The short video SDK provides a set of doodle APIs that include the canvas and brush. The entire doodle operation is handled by the AliyunICanvasView doodle canvas view. For more information about the parameters used in the code, see the API documentation referenced in Related classes.

// 1. Create a brush.
AliyunIPaint *paint = [[AliyunIPaint alloc]initWithLineWidth:5.0 lineColor:UIColor.whiteColor];
// 2. Add a doodle view.
AliyunICanvasView *paintView = [[AliyunICanvasView alloc]initWithFrame:CGRectMake(0, 0, 100, 100) paint:paint];
[yourView addSubview:paintView];

// 3. Doodle.
// Touch the view to doodle.

// 4. Other operations.
// 4.1 Undo one step.
[paintView undo];
// 4.2 Redo one step.
[paintView redo];
// 4.3 Undo all operations in this doodle session.
[paintView undoAllChanges];
// 4.4 Clear all lines (cannot be recovered).
[paintView remove];

// 5. Complete the doodle.
UIImage *image = [paintView complete];
NSString *paintPath = xxx;
[UIImagePNGRepresentation(image) writeToFile:paintPath atomically:YES];

// 6. Add to the editor.
AliyunEffectImage *paintImage = [[AliyunEffectImage alloc] initWithFile:paintPath];
[editor applyPaint:paintImage linesData:paintView.lines];

// 7. Delete the doodle.
[editor removePaint:paintImage];