summaryrefslogtreecommitdiff
path: root/apps/web/src/services/renderer/nodes/video-node.ts
blob: b6911df7fff6b15ffa3d55ae7f47160dc3ac9b95 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
import type { CanvasRenderer } from "../canvas-renderer";
import { BaseNode } from "./base-node";
import { videoCache } from "@/services/video-cache/service";

const VIDEO_EPSILON = 1 / 1000;

export interface BaseMediaNodeParams {
	file: File;
	duration: number;
	timeOffset: number;
	trimStart: number;
	trimEnd: number;
	x?: number;
	y?: number;
	width?: number;
	height?: number;
	opacity?: number;
}

export interface VideoNodeParams extends BaseMediaNodeParams {
	mediaId: string;
}

export class VideoNode extends BaseNode<VideoNodeParams> {
	private getVideoTime(time: number) {
		return time - this.params.timeOffset + this.params.trimStart;
	}

	private isInRange(time: number) {
		const videoTime = this.getVideoTime(time);
		return (
			videoTime >= this.params.trimStart - VIDEO_EPSILON &&
			videoTime < this.params.trimStart + this.params.duration
		);
	}

	async render({ renderer, time }: { renderer: CanvasRenderer; time: number }) {
		await super.render({ renderer, time });

		if (!this.isInRange(time)) {
			return;
		}

		const videoTime = this.getVideoTime(time);
		const frame = await videoCache.getFrameAt({
			mediaId: this.params.mediaId,
			file: this.params.file,
			time: videoTime,
		});

		if (frame) {
			renderer.context.save();

			if (this.params.opacity !== undefined) {
				renderer.context.globalAlpha = this.params.opacity;
			}

			if (
				this.params.x !== undefined &&
				this.params.y !== undefined &&
				this.params.width !== undefined &&
				this.params.height !== undefined
			) {
				renderer.context.drawImage(
					frame.canvas,
					this.params.x,
					this.params.y,
					this.params.width,
					this.params.height,
				);
			} else {
				renderer.context.drawImage(
					frame.canvas,
					0,
					0,
					renderer.width,
					renderer.height,
				);
			}

			renderer.context.restore();
		}
	}
}