Skip to content

Core-SDK

Core-SDK is the SDK for the canvas rendering part, which can draw project data (JSON) onto the Canvas. It can be used for secondary development to create video-related mini-programs, H5, PC video editors, and other web-based online tools.

github demo

Usage:

js
import { VideoCoreSDK } from "@h5/videoCoreSDK";

const vc = new VideoCoreSDK({
  data: MovieData, // Required, project data
  workerPath: "assets", // Optional, reference directory for decode.worker.js, default is assets
  registerId: "", // Required, registration ID, bound to the domain name
  movieId: "movieID", // Unique identifier within the instance, cannot be the same for multiple instances
  EModuleEffectSourcePath: "/assets/effectcanvas/", // Effect resource module loading path
  resourceHost: "https://cdn.h5ds.com", // Host for resource loading
  scale: 1, // Optional, canvas scaling ratio, default is 1
  env: "preview", // Required, rendering mode: editor (edit mode, draggable for editing); preview (preview mode, can only preview, less memory consumption)
  currentTime: 0, // Optional, default start time is 0
  plugins: [], // Optional, extension plugins
});

const core = await vc.init();

/**
 * Assuming the project data is movieData, modify data -> update view
 */
// 1. Get the first element in the project data
const [element] = movieData.elements;

// 2. Modify the coordinates of the first element to [100, 200]
element.style.x = 100;
element.style.y = 200;

// 3. Update the view
core.update();

You can get the vc instance through new VideoCore(...), then get core through vc.init(). The core instance contains many properties and methods, which are introduced below one by one along with usage tips.

typescript
// VideoCoreSDK parameter description typescript
export interface IMovieProps {
  data: types.MovieData; // Project data
  workerPath?: string; // Worker address
  registerId?: string; // Registration ID
  movieId?: string; // Unique identifier within the instance, cannot be the same for multiple instances
  EModuleEffectSourcePath?: string; // Effect loading path
  scale?: number; // Canvas scaling ratio, default is 1
  resourceHost?: string; // Host for resource loading
  stopControl?: boolean; // Stop controller, editor uses controller by default, can disable controller in editor through this parameter
  useRecordManager?: boolean; // Use history records, history records are enabled by default
  plugins?: types.PluginConfig[]; // Extension plugins
  target?: HTMLElement | null; // DOM container where canvas is placed, default is body
  times?: [number, number]; // If this parameter is passed, only resources in this interval will be loaded, used with export
  env: types.Env; // Controlled by environment variables, env:  'editor' | 'preview' | 'export';
  server?: boolean; // Set to true for server-side rendering, default is false
  // Must preload resources when exporting MP4, no need to preload resources in the editor, so there is a parameter here to control whether resources need to be preloaded
  fetchSourceBeforeRender?: boolean; // Whether to cache resources before rendering, default is false
  currentTime?: number; // Time, can be used in editor and preivew, need to call update method in export
  // This method accepts a method to change currentTime, triggers currentTime change
  triggerCurrentTime?: (t: number) => void;
  // Select multiple elements in edit mode
  onSelectElements?: (elementIds: string[]) => void;
  // Execute after controller changes
  onControlChangeEnd?: (elementIds: string[]) => void;
  // Resource loading progress
  onSourceProgress?: (p: number) => void;
  // Single resource loading progress
  onEachSourceProgress?: (n: { src: string; p: number; total: number }) => void;
  // Pause event
  onPause?: () => void;
  // Execute after initialization succeeds
  initSuccess?: () => void;
  // Execute after rendering
  callback?: (c: Store) => void;
}

Property Description

Only the properties of the instance core are listed below, with the data type after the colon. Private properties all start with _.

.target?: HTMLElement | null;

DOM container where canvas is placed, default is body

.movieId: string

Unique identifier of the instance, can be a random string, default is 'movieId'

.workerPath: string

Worker address, default is /assets/worker/decode.worker.js, this worker is needed to decode video frames when exporting video

.currentTime: number

Current display time

.times: [number, number] | null

When exporting, you can pass times to indicate exporting a video for a specified period of time, in seconds

.controlModeType: 'editElement' | 'editMask'

Controller mode, divided into element edit mode and mask edit mode, only mask can be edited in mask edit mode

.bodyContainer: PIXI.Container

Body container, used to store PIXI elements

.captionContainer: PIXI.Container

Caption container, used to store caption elements

.data: types.MovieData

Animation JSON data

.events: events.EventsName

Instance of event names, events can be called by triggering event names

ts
// Update view
pubsub.publish(core.events.UPDATE_STAGE);

// Event names and descriptions:
events.UPDATE_STAGE; // Update view
events.VISIBLE_CONTROL; // Controller show/hide
events.STOP_GROUP_DRAG; // Disable group selection
events.SORT_CONTAINER; // Reorder container
events.UPDATE_CONTROL_TARGET; // Update controller after target size changes
events.UPDATE_CONTROL_LIST; // Controller update list
events.TRIGGER_CONTROL; // Trigger controller to select element
events.TRIGGER_CONTROL_NO_CALLBACK; // Only trigger controller, do not call onSelected callback
events.UPDATE_MOVIE; // Update Movie.tsx
events.CONTROL_CHANGE_END; // Controller end
events.CONTROL_CHANGE_START; // Controller start

.record: RecordObject

Collection of history record methods, this parameter is only available when env is editor

ts
export interface RecordItem {
  desc: string; // Description information
}

export interface RecordObject {
  add: (item: RecordItem) => void; // Add history record
  debounceAdd: (item: RecordItem) => void; // Add history record using debounce function
  redo: () => void;
  undo: () => void;
  manager: RecordManager; // Data management for history records
}

.resourceManage: ResourceManage

Resource manager instance, please refer to the class: packages\video-core\src\react-pixi\manage\resourceManage.ts

.movieSize: { width: number; height: number }

Video size

.app: Application | undefined

pixijs instance

.playing: boolean

Whether in playback mode, not in playback by default

.env: 'editor' | 'preview' | 'export'

Current operating environment, editor means in editor mode, preview means in preview mode, export means in export mode

.groups: types.BaseElement[][]

Data grouped by trackIndex

.zIndexObjects: Record<string, number>

Record element zIndex data for controller element usage

.cacheControlElementIds: string[]

Array of element IDs recorded by the controller

.usedMediaInfo: Record<string, types.UsedMediaItem>

Cache all media resources video & audio for audio synthesis

.elementReadyMark: Record<string, 'start' | 'success' | 'error'>

Record layer preparation success, used to handle asynchronous issues in react, tring can be elementId or other parameters

.renderAsyncMark: Record<string, 'start' | 'success' | 'error'>

Reference format: {elementId_time: 'start' | 'success' | 'error'}

The rendering process is asynchronous, used to detect whether rendering is successful. Before rendering, put the element's id and time into the object, after successful rendering, the time is null. If all rendering is completed, the mark should be {}, and if it hasn't been rendered after 1 second, no processing will be done. Therefore, to ensure performance issues, the rendering time of each frame must be controlled within 1 second

.controlElements: ControlChangeValues[]

Record data after controller changes

ts
export interface ControlChangeValues {
  elementId: string; // Target element ID
  x: number;
  y: number;
  alpha?: number;
  width: number;
  height: number;
  rotation: number;
}

Method Description

get hideLockData(): number

Hide and lock are for the entire track, return lock, hide show element records, this parameter should be set when calculating group

ts
return Record<number, { hide: boolean; lock: boolean }>;

getPixiContainerById(ids: string[]): PIXI.Container[]

Get pixi container objects by ids

pixiContainerToImageById(id: string): Promise<HTMLImageElement>

Find pixi container by id, then export as image

initHideLock(hideLock: Record<number, { hide: boolean; lock: boolean }>)

The hide and lock information of tracks in the timeline will be recorded in movieData._hideLock ={}, which needs to be initialized, the key in _hideLock is trackIndex

getHideLock(trackIndex: number): { hide: boolean;lock: boolean;}

Get track hide and lock parameters

setHideLock(params: { trackIndex: number; hide?: boolean; lock?: boolean }, updateMovie?: boolean)

Set hide and lock for the specified track (trackIndex), the second parameter indicates whether to update movie

changeControlMode(modeType: 'editElement' | 'editMask', id: string)

Switch edit mode, which are element edit and mask edit respectively.

getFrameItem(elementData: types.BaseElement): types.FrameItem | null

Get current frame data, if there is data at the current time node, return the corresponding frame data (error 0.1 seconds)

asyncFrameAnimateStatus(elementData: types.BaseElement, status: types.FrameItem | null)

Synchronize frame animation data with element status, can be understood as updating element display status through frame data

getFrameStatusByCurrentTime(elementData: types.BaseElement, currentTime?: number): types.FrameItem | null

Get frame status at specified time position, if the second parameter is not passed, use the time at the cursor position by default

getFrameStatus(elementData: types.BaseElement, relativeTime: number | null): types.FrameItem | null

Get frame status at specified time position

removeTrackMedias(trackIndex: number, resourceId: string)

Remove media data, called when deleting elements

checkSupportTransition(elementId: string): boolean

Determine whether the element supports inserting transition data, transitions can only be inserted between two adjacent video or image elements

removeInvalidTransition()

Remove invalid transition animations

play()

Play video

pause()

Pause video

sortZIndexNewData()

Reset zindex when saving, return new data, the reason for resetting zindex parameter is that when inserting new tracks, the subscript may be a decimal, reset will update it to integer

step(time: number)

Set video time

runAnimate()

Execute play operation, fps is 32 frames by default, the frequency of requestAnimationFrame may be 60fps, need to play through FPS. The reason for not using setTimeout is that setTimeout will cause large errors and cannot correct the time

updateControl(type: 'updateTarget' | 'updateList' | 'trigger' | 'triggerNoCallback' | 'visible' | 'stopGroupDrag', doms?: HTMLElement[] | SVGElement[] | string[] | { visible: boolean; elementId: string } | boolean)

Method to update the controller

updateTarget: Update controller after target size changes

updateList: Update controller after targets list changes

trigger: Trigger element to be selected

triggerNoCallback: Only trigger controller, do not call onSelected callback

visible: Show/hide controller { visible, elementId }

stopGroupDrag: Disable & enable group selection

ts
// Trigger controller to select elementId1
movie.updateControl("trigger", ["elementId1"]);

updateSortTracks()

Track sort update, need to call this method again to update the z-index issue in pixi

getTotalTime()

Get total duration of video

getElementDataByIds(ids: string[])

Get elements by ids

getElementDataByTypes(types: types.ElementType[], data?: types.MovieData): types.BaseElement[]

Get elements by type

getCloneData()

Get clone data of data, recalculate trackIndex to make it integer, starting from 1

addResource(url: string, styleSize?: types.StyleSize): Promise<types.Resource>

Add resource, resource will be synchronously added to resourceManage and cached

ts
export interface StyleSize {
  width: number;
  height: number;
}

addElementByResource(resource: types.Resource, params: { elementType: types.ElementType; time: number; trackIndex?: number; duration?: number })

Add element through resource data

ts
core.addElementByResource(resource, {
  elementType: "image", // Insert image
  time: 10, // Start from 10 seconds
  trackIndex: 2, // Insert into the second track
  duration: 5, // Display for 5 seconds
});

addElementNoSource( info: Record<string | 'attrs', any>, params: { elementType: types.ElementType; time: number; trackIndex?: number; duration?: number })

Add elements without resource, such as: text, effect, cutScene, etc.

update(time?: number)

Update Movie component, for example, if the _dirty parameter of an element is modified, this function can be called, or if the data structure is modified, this method can also be called. In export environment, use update to update component, because in the export process, rendering is an asynchronous process, need to judge whether rendering is successful

capture(): string

Capture current view screen, return base64 image

controlChangeStart(elementIds: string[])

This method is triggered when the controller is selected, no need to call it separately

controlChangeEnd(elementIds: string[])

Callback function when controller ends, no need to call it separately

destroy()

Destroy instance, memory recycling

Event Related

onControlChangeEnd: ((elementIds: string[]) => void)

Bind event for end of controller changes

onControlChangeStart: ((elementIds: string[]) => void)

Bind event for start of controller changes

onEachSourceProgress: (n: { src: string; p: number; total: number }) => void

This event is triggered for each resource loading progress

triggerCurrentTime: (t: number) => void

Event that triggers currentTime change

triggerRecordSelectElements: (ids: string[]) => void

Trigger element selection

React DEMO

jsx
import { useEffect, useRef, useState } from 'react';
import './App.less';

import { VideoCoreSDK } from '@video/core/src/react-pixi/CoreSDK.tsx';
import { data } from './data';
import { Slider } from '@douyinfe/semi-ui';

function App() {
  const videoCoreRef = useRef<any>(null);
  const [currentTime, setCurrentTime] = useState(0);
  const [duration, setDuration] = useState(0);

  useEffect(() => {
    // Ensure only one instance is created
    if (!videoCoreRef.current) {
      const vc = new VideoCoreSDK({
        data: data, // Required, project data
        target: document.getElementById('video-container')!, // Required, rendering target
        movieId: 'movieID', // Unique identifier within the instance, cannot be the same for multiple instances
        env: 'preview', // Required, rendering mode: editor (edit mode, draggable for editing); preview (preview mode, can only preview, less memory consumption)
        registerId: 'H5DS', // Required, registration ID, bound to the domain name
        resourceHost: 'https://cdn.h5ds.com',
        EModuleEffectSourcePath: '/assets/effectcanvas/', // Effect resource module loading path
        workerPath: 'assets', // Optional, reference directory for decode.worker.js, default is assets
        scale: 1, // Optional, canvas scaling ratio, default is 1
        currentTime, // Optional, default start time is 0
        plugins: [], // Optional, extension plugins
        triggerCurrentTime: (currentTime: number) => {
          setCurrentTime(currentTime);
        },
      });
      vc.init().then((core: any) => {
        videoCoreRef.current = core;
        setDuration(core.getTotalTime());
      });
    }

    // Cleanup function, destroy instance when component unmounts
    return () => {
      if (videoCoreRef.current) {
        // videoCoreRef.current.destroy();
        videoCoreRef.current = null;
      }
    };
  }, []);

  return (
    <div
      style={{
        width: '960px',
      }}
    >
      <div id="video-container"></div>
      <div>
        {!!duration && (
          <Slider
            value={currentTime}
            max={duration}
            step={0.01}
            onChange={value => videoCoreRef.current?.triggerCurrentTime(value)}
          />
        )}
      </div>
      <div className="list">
        <a
          onClick={() => {
            videoCoreRef.current?.play();
          }}
        >
          play
        </a>
        <a
          onClick={() => {
            videoCoreRef.current?.pause();
          }}
        >
          pause
        </a>
      </div>
    </div>
  );
}

export default App;

Powered by Sichuan AiQuWu Technology Co., Ltd. Shu ICP Bei 18034069 Hao.