Skip to content

Commit

Permalink
chore: transparent video pt-2 (#315)
Browse files Browse the repository at this point in the history
* chore: resolve lots of type stuff, clean up

* chore: naming, defaults in one spot

* chore: simplify
  • Loading branch information
hkonsti authored Oct 7, 2024
1 parent 0e7e4d5 commit d36b328
Show file tree
Hide file tree
Showing 9 changed files with 271 additions and 262 deletions.
34 changes: 26 additions & 8 deletions packages/core/src/app/Project.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import {
FfmpegExporterOptions,
ImageExporterOptions,
WasmExporterOptions,
} from '../exporter';
import {FfmpegExporterOptions, ImageExporterOptions} from '../exporter';
import type {Plugin} from '../plugin';
import {SceneDescription} from '../scenes';
import {CanvasColorSpace, Color, Vector2} from '../types';
import {Logger} from './Logger';

// TODO(refactor): check if we can get rid of this
export interface Versions {
core: string;
two: string | null;
Expand Down Expand Up @@ -36,9 +33,9 @@ export type ExporterSettings =
}
| {
name: '@revideo/core/wasm';
options: WasmExporterOptions;
};

// Project settings that are used internally
export interface ProjectSettings {
shared: {
background: Color;
Expand All @@ -57,11 +54,12 @@ export interface ProjectSettings {
};
}

// Project settings as they are provided by the user (can be serialized)
export interface UserProjectSettings {
shared: {
range: [number, number];
background: string | null;
size: {x: number; y: number};
background: string | null; // changed from Color to string
size: {x: number; y: number}; // changed from Vector2 to object
};
rendering: {
exporter: ExporterSettings;
Expand All @@ -75,6 +73,22 @@ export interface UserProjectSettings {
};
}

/**
* Settings that can be passed to the renderVideo / renderPartialVideo functions
*/

export type RenderVideoUserProjectSettings = {
range?: UserProjectSettings['shared']['range'];
background?: UserProjectSettings['shared']['background'];
size?: UserProjectSettings['shared']['size'];

exporter?: UserProjectSettings['rendering']['exporter'];
};

/**
* Settings that can be passed to the createProject function
*/

export type PartialUserProjectSettings = {
shared?: Partial<UserProjectSettings['shared']>;
rendering?: Partial<UserProjectSettings['rendering']>;
Expand Down Expand Up @@ -118,6 +132,10 @@ export interface UserProject {
settings?: PartialUserProjectSettings;
}

/**
* Internal project that includes legacy properties that can't be changed by the user
* as well as defaulted properties in case the user didn't provide them.
*/
export interface Project extends Omit<UserProject, 'settings'> {
name: string;
settings: ProjectSettings;
Expand Down
60 changes: 29 additions & 31 deletions packages/core/src/app/makeProject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,64 +8,62 @@ import {
createVersionObject,
} from './Project';

export const defaultUserProjectSettings: UserProjectSettings = {
shared: {
background: 'FFFFFF00',
range: [0, Infinity],
size: {x: 1920, y: 1080},
},
rendering: {
exporter: {
name: '@revideo/core/wasm',
},
fps: 30,
resolutionScale: 1,
colorSpace: 'srgb',
},
preview: {
fps: 30,
resolutionScale: 1,
},
};

export function makeProject(project: UserProject): Project {
// Don't delete, has side effects
// TODO(konsti): Figure out how to get rid of this
void DefaultPlugin;

const defaultSettings: UserProjectSettings = {
shared: {
background: 'FFFFFF00',
range: [0, Infinity],
size: {x: 1920, y: 1080},
},
rendering: {
exporter: {
name: '@revideo/core/wasm',
options: {
format: 'mp4',
},
},
fps: 30,
resolutionScale: 1,
colorSpace: 'srgb',
},
preview: {
fps: 30,
resolutionScale: 1,
},
};

const settings: UserProjectSettings = {
...defaultSettings,
const settings = {
...defaultUserProjectSettings,
...project.settings,
shared: {
...defaultSettings.shared,
...defaultUserProjectSettings.shared,
...project.settings?.shared,
},
rendering: {
...defaultSettings.rendering,
...defaultUserProjectSettings.rendering,
...project.settings?.rendering,
},
preview: {
...defaultSettings.preview,
...defaultUserProjectSettings.preview,
...project.settings?.preview,
},
};

const modifiedSettings = {
// Convert background and size to correct types
const convertedSettings = {
...settings,
shared: {
...settings.shared,
background: new Color(settings.shared.background ?? 'FFFFFF00'),
background: new Color(settings.shared.background!),
size: new Vector2(settings.shared.size),
},
};

return {
...project,
name: project.name ?? 'project',
settings: modifiedSettings,
settings: convertedSettings,
plugins: [],
logger: new Logger(),
versions: createVersionObject('0.5.9'),
Expand Down
28 changes: 14 additions & 14 deletions packages/core/src/exporter/FFmpegExporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,11 @@ export class FFmpegExporterClient implements Exporter {
public static readonly id = '@revideo/core/ffmpeg';
public static readonly displayName = 'Video (FFmpeg)';

public static async create(project: Project, settings: RendererSettings) {
return new FFmpegExporterClient(project, settings);
private readonly settings: RendererSettings;
private readonly exporterOptions: FfmpegExporterOptions;

public static async create(_: Project, settings: RendererSettings) {
return new FFmpegExporterClient(settings);
}

private static readonly response = new EventDispatcher<ServerResponse>();
Expand All @@ -61,23 +64,21 @@ export class FFmpegExporterClient implements Exporter {
}
}

public constructor(
private readonly project: Project,
private readonly settings: RendererSettings,
) {}
public constructor(settings: RendererSettings) {
if (settings.exporter.name !== FFmpegExporterClient.id) {
throw new Error('Invalid exporter');
}
this.settings = settings;
this.exporterOptions = settings.exporter.options;
}

public async start(): Promise<void> {
await this.invoke('start', this.settings);
}

public async handleFrame(canvas: HTMLCanvasElement): Promise<void> {
const format = (this.settings.exporter.options as FfmpegExporterOptions)
.format;
const blob = await new Promise<Blob | null>(resolve =>
canvas.toBlob(
resolve,
['proRes', 'webm'].includes(format) ? 'image/png' : 'image/jpeg',
),
canvas.toBlob(resolve, 'image/png'),
);

if (!blob) {
Expand Down Expand Up @@ -139,8 +140,7 @@ export class FFmpegExporterClient implements Exporter {
public async mergeMedia(): Promise<void> {
const outputFilename = this.settings.name;
const tempDir = `revideo-${this.settings.name}-${this.settings.hiddenFolderId}`;
const format = (this.settings.exporter.options as FfmpegExporterOptions)
.format;
const format = this.exporterOptions.format;

await fetch('/audio-processing/merge-media', {
method: 'POST',
Expand Down
10 changes: 3 additions & 7 deletions packages/core/src/exporter/WasmExporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,6 @@ import type {AssetInfo, RendererSettings} from '../app/Renderer';
import {Exporter} from './Exporter';
import {download} from './download-videos';

export interface WasmExporterOptions {
format: 'mp4';
}

export class WasmExporter implements Exporter {
public static readonly id = '@revideo/core/wasm';
public static readonly displayName = 'Video (Wasm)';
Expand Down Expand Up @@ -76,6 +72,7 @@ export class WasmExporter implements Exporter {
): Promise<void> {
await fetch('/audio-processing/generate-audio', {
method: 'POST',
// TODO: add type and validate on the other side
body: JSON.stringify({
tempDir: `revideo-${this.settings.name}-${this.settings.hiddenFolderId}`,
assets,
Expand All @@ -89,15 +86,14 @@ export class WasmExporter implements Exporter {
public async mergeMedia(): Promise<void> {
const outputFilename = this.settings.name;
const tempDir = `revideo-${this.settings.name}-${this.settings.hiddenFolderId}`;
const format = (this.settings.exporter.options as WasmExporterOptions)
.format;

await fetch('/audio-processing/merge-media', {
method: 'POST',
// TODO: add type and validate on the other side
body: JSON.stringify({
outputFilename,
tempDir,
format,
format: 'mp4',
}),
});
}
Expand Down
6 changes: 5 additions & 1 deletion packages/ffmpeg/src/ffmpeg-exporter-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,12 @@ export class FFmpegExporterServer {
private readonly format: FfmpegExporterOptions['format'];

public constructor(settings: FFmpegExporterSettings) {
if (settings.exporter.name !== '@revideo/core/ffmpeg') {
throw new Error('Invalid exporter');
}

this.settings = settings;
this.format = (settings.exporter.options as FfmpegExporterOptions).format;
this.format = settings.exporter.options.format;

this.jobFolder = path.join(
os.tmpdir(),
Expand Down
56 changes: 30 additions & 26 deletions packages/renderer/client/render.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {
Color,
Project,
RenderVideoUserProjectSettings,
Renderer,
UserProjectSettings,
Vector2,
getFullRenderingSettings,
} from '@revideo/core';
Expand All @@ -16,28 +16,24 @@ declare global {

/**
* Render the project.
* @param project - The project to render.
* @param range - The range of frames to render.
*/
export const render = async (
project: Project,
workerId: number,
totalNumOfWorkers: number,
hiddenFolderId: string,
projectRenderSettings: Partial<
UserProjectSettings['shared'] & UserProjectSettings['rendering']
>,
overwriteRenderSettings: RenderVideoUserProjectSettings,
) => {
try {
const renderer = new Renderer(project);

// Range calculation
const range =
overwriteRenderSettings.range ?? project.settings.shared.range;

const {firstGlobalFrame, lastGlobalFrame} =
await getGlobalFirstAndLastFrame(
project,
renderer,
projectRenderSettings.range?.[0] ?? 0,
projectRenderSettings.range?.[1] ?? Infinity,
);
await getGlobalFirstAndLastFrame(project, renderer, range[0], range[1]);

const {firstWorkerFrame, lastWorkerFrame} =
await getWorkerFirstAndLastFrame(
firstGlobalFrame,
Expand All @@ -46,29 +42,37 @@ export const render = async (
totalNumOfWorkers,
);

const fullRenderingSettings = getFullRenderingSettings(project);
const renderSettingsFromProject = getFullRenderingSettings(project);

// Overwrite settings with user provided settings
let background = renderSettingsFromProject.background;
if (overwriteRenderSettings.background) {
background = new Color(overwriteRenderSettings.background);
}

let size = renderSettingsFromProject.size;
if (overwriteRenderSettings.size) {
size = new Vector2(
overwriteRenderSettings.size.x,
overwriteRenderSettings.size.y,
);
}

const renderSettings = {
...fullRenderingSettings,
// Combine settings
const combinedSettings = {
...renderSettingsFromProject,
name: project.name,
hiddenFolderId: hiddenFolderId,
...projectRenderSettings,
...overwriteRenderSettings,
background,
size,
range: [
renderer.frameToTime(firstWorkerFrame),
renderer.frameToTime(lastWorkerFrame),
] as [number, number],
size: projectRenderSettings.size
? new Vector2(
projectRenderSettings.size.x,
projectRenderSettings.size.y,
)
: fullRenderingSettings.size,
background: projectRenderSettings.background
? new Color(projectRenderSettings.background)
: fullRenderingSettings.background,
};

await renderer.render(renderSettings);
await renderer.render(combinedSettings);
window.onRenderComplete();
} catch (e: any) {
window.onRenderFailed(e.message);
Expand Down
Loading

0 comments on commit d36b328

Please sign in to comment.