npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

movie-editor-lib

v1.0.0-beta.6

Published

movie editor

Downloads

1

Readme

movie-editor-lib

背景

原有的数字人生成方式,是通过drml去驱动数字人,然后生成数字人视频,但是这种方式局限性比较大,一是只涉及数字人,二是drml驱动是一种线编形式的视频编辑方式

那么非线编是什么呢?非线编全程为非线性编辑,即剪切、复制、粘贴素材无须在存储介质上重新安排他们;非线性的主要目标是提供对原素材任意部分的随机存取、修改和处理

最开始是服务型非线编平台有需求,服务型是云端数字人方案,数字人为远端视频,演艺型直播平台现在的方案是本地驱动ue客户端

视频编辑器核心主要分为两部分,一是非线编轴操作,二是视频预览界面

使用

<MovieEditorKit
    className="demo-wrapper"
    ref={editorRef}
    aspect={resolution[0] / resolution[1]}
    tracks={tracks as TrackType[]}
    onFocus={handleFocus}
    onChange={handleChange}
    onSave={handleSave}
>
    <UEPreview>
        <DigitalHumanView className="demo-streaming" />
    </UEPreview>
    <ZoomController />
    <TimeLine />
    <BlockLine />
</MovieEditorKit>

1. MovieEditorKit

| 参数名 | 类型 | 备注 | | -------- | ----------------------------------------------- | ---------------------- | | ref | MovieEditorHandler | 编辑器方法暴露 | | aspect | Number | 画幅比例,比如16/9 | | tracks | TrackType[] | 核心数据结构 | | onFocus | (clip?: TrackClipType) => void | 选中事件 | | onChange | (tracks: TrackType[], duration: number) => void | tracks变更 | | onSave | (tracks: TrackType[], duration: number) => void | 触发保存事件(ctrl+s) | | onEnd | () => void | 预览播放完成 |

MovieEditorKit这一层是整个视频编辑器的核心,所有数据的通信以及处理都在这一层,它给外部暴露了编辑器的相关方法MovieEditorHandler

| 方法名 | 类型 | 备注 | | ------- | -------------------------------------- | ---------------------- | | set | (obj: Partial) => void | 更新某个clip | | update | (tracks: TrackType[]) => void; | 更新整个tracks | | prepare | (obj: Partial) => void; | 拖拽新增某个clip前调用 | | reset | () => void; | 拖拽新增某个clip后调用 | | seek | (time: number) => void; | 跳转到预览的某一时间 | | play | () => void; | 开始预览播放 | | pause | () => void; | 暂停预览播放 |

但是视频编辑器的相关方法,除了外部调用以为,还有内部调用,比如demo里的组件,可以通过开发自定义组件,将自定义组件放在

MovieEditorKit内部,然后通过useContext获取相关方法

| 方法名 | 类型 | 备注 | | -------------- | ------------------------------------------------------------ | ------------------------------ | | tracks | TrackType[] | 核心数据结构 | | aspect | Number | 画幅比例,比如16/9 | | duration | number | 视频时长 | | focusId | number | 命中的clipId | | focusClip | TrackClipType | 命中的clip | | focusBindClips | Set | 命中的clip的绑定clips | | currentTime | number | 当前预览播放到的时间 | | playing | boolean | 当前预览播放状态 | | zoomConfig | ZoomConfig {zoom?: number; minZoom?: number; maxZoom?: number; onZoomChange?: ((zoom: number |((prev: number)=>number)) => void); } | 当前缩放比以及轴状态 | | virtualClip | VirtualClipType (TrackClipType & {point: Point; target: string; }) | 拖动或跳轴时,处于动态时的clip | | onClipChange | (clip?: Partial, isFocus?: boolean) => void; | clip触发变更 | | onTrackChange | (track: TrackType) => void; | tracks触发变更 | | dispatch | React.Dispatch | 其余数据状态变化触发 |

通过以上暴露的方法,基本就能对视频编辑器进行完整的操作

下面我们就开始讲最核心的部分,也就是数据结构,视频编辑器操作处理的数据

2. Tracks

通过ts的定义,可以看到,我们的tracks定义为TrackType,本质是我们提供的各种类型的Track

export type TrackType = VideoTrack | ImageTrack | AudioTrack | TextTrack | DigitalHumanTrack | FragmentTrack;

目前,我们提供的Track的类型为Video视频、Image图片、Audio音频、Text文本、DigitalHuman数字人、Fragment片段

具体的结构,我们直接看定义

// track
export interface MovieTrack {
    name: string;
    id: string;
    color?: string;
    // 是否磁吸,连续
    attraction?: boolean;
    isMainTrack?: boolean;
    masterId?: string;
    associatedId?: string;
    // 操作
    operations: Partial<MovieTrackOperation>;
}
export interface DigitalHumanTrack extends MovieTrack {
    type: ElementType.DIGITAL_HUMAN;
    digitalhuman: DigitalHumanType,
    position: Position;
    clips: DigitalHumanClip[];
}
export interface VideoTrack extends MovieTrack {
    type: ElementType.VIDEO;
    position: Position;
    clips: (VideoClip | ImageClip)[];
}
export interface ImageTrack extends MovieTrack {
    type: ElementType.IMAGE;
    position: Position;
    clips: ImageClip[];
}
export interface AudioTrack extends MovieTrack {
    type: ElementType.AUDIO;
    clips: AudioClip[];
}
export interface TextTrack extends MovieTrack {
    type: ElementType.TEXT;
    position: Position;
    source: Omit<TextStyle, 'argument'>;
    clips: TextClip[];
}
export interface FragmentTrack extends MovieTrack {
    type: ElementType.FRAGMENT;
    position: Position;
    clips: TrackClipType[];
}

这里可以看到,这里主要定义了Track的类型,并且每个Track内的数据结构也不一样

通过MovieTrack可以看到,track本身的一些功能

  • attraction:是否吸附连续轴

  • isMainTrack:是否主轴,目前还没用,之后做一些辅助线需要

  • masterId、associatedId: 关联轴

track上的position、operations不是表示track的位置信息和操作,是这个轴上的clip的默认信息,就是当clip添加的时候,如果没有设置相关的信息,那么就会从track上获取

这里讲一下比较复杂的FragmentTrack,它表达一种非常特殊的track类型,可以拖入所有类型的clip,即不进行track类型的限制,并且两种特殊类型的clip也只能在这个track下使用,具体的clip下面讲解

export type TrackClipType = DigitalHumanClip | VideoClip | ImageClip | TextClip | AudioClip | FragmentClip | CustomClip;

以上是所有的clip类型,基础的clip属性有

export interface MovieClip {
    // 标识符
    id: string;
    // 来自轴id
    from: string;
    // 起始时间
    offset: number;
    // 素材起始时间
    in: number;
    // 素材结束时间
    out: number;
    // 素材原本时长
    duration: number;
    // 预览位置信息
    position: Position;
    // 是否固定起始位置
    offsetFixed?: boolean;
    // 素材内容
    source: {
        // 素材地址
        url: string;
        // 预览图
        thumbnail?: string;
    };
    // 关联轴-主轴id
    masterId?: string;
    // 关联轴-副轴id
    associatedId?: string;
    // 关联轴-clip预览
    associate?: TrackClipType[];
    // 操作
    operations: Partial<MovieTrackOperation>;
}
export interface DigitalHumanClip extends Omit<MovieClip, 'source'> {
    type: ElementType.DIGITAL_HUMAN;
    digitalhuman: DigitalHumanType;
    source: DigitalHumanSource;
}
export interface VideoClip extends MovieClip {
    type: ElementType.VIDEO;
}
export interface ImageClip extends MovieClip {
    type: ElementType.IMAGE;
}
export interface AudioClip extends Omit<MovieClip, 'position'> {
    type: ElementType.AUDIO;
}
export interface TextClip extends Omit<MovieClip, 'source'> {
    type: ElementType.TEXT;
    source: {
        content: ElementStructure[];
        style: React.CSSProperties;
    },
}
export interface FragmentClip extends Omit<MovieClip, 'source'> {
    type: ElementType.FRAGMENT;
    source: {
        config: TrackType[];
        // 预览图
        thumbnail?: string;
        title?: string;
        duration?: number;
    },
}
export interface CustomClip extends Omit<MovieClip, 'source'> {
    type: ElementType.CUSTOM;
    source: any;
    onPreview: (clip: CustomClip) => (() => void);
    onRender: (clip: CustomClip) => ReactNode | void;
}

3. UE驱动

端渲染方案中,比较大的区别是在于Preview预览的部分,这里为了保持双方的独立性,新开发了UEPreview组件,在使用的时候,可以根据实际情况进行使用

主要区别在于驱动方式,一个是组件的渲染,一个是去驱动ue

4. 皮肤

    --theme-bg: #2D2E2E;
    --preview-bg: #000;
    --preview-content-bg: #D9DEE4;

    --text-color: #F0F0F0;

    --control-slider: #BDBDBD;
    --control-slider-solid: #00CCCC;
    --control-bg: #3D3D3D;
    --control-color: #ABABAB;;

    --timeline-pointer-color: #fff;
    --timeline-pointer-width: 1.5px;
    --timeline-pointer-size: 8px;

    --track-title-color: #666666;
    --track-title-icon-color: #F0F0F0;
    --track-title-width: 48px;
    --track-title-postion: center;

    --track-ruler-height: 30px;
    --track-margin: 4px;
    --track-height: 20px;
    --track-bg: #1D1E1F;
    --track-disabled-bg: none;
    --track-enabled-bg: rgba(86, 98, 239, .2);
    --track-preview-height: 80px;

    --track-block-width: 160px;
    --track-block-height: 90px;
    --track-block-bg: #1D1E1F;

    --clip-bg: #848DF1;
    --clip-shadow: normal;
    --clip-border: 1px solid #FFFFFF;
    --clip-border-radius: 4px;
    --clip-focus-bg: normal;
    --clip-btn-bg: #FFFFFF;
    --clip-btn-length: 2px;
    --clip-btn-content: '';

    --clip-menu-hover-bg: #3D3D3D;
    --clip-menu-hover-color: #FAFAFA;


    --preview-edit-border-width: 1px;
    --preview-edit-border: var(--preview-edit-border-width) solid #fff;
    --editor-btn-width: 10px;
    --editor-btn-color: #fff;
    --editor-btn-radius: 50%;