Skip to content

Commit

Permalink
improve: video export and move it to its own controller
Browse files Browse the repository at this point in the history
  • Loading branch information
zenkyuv committed Jan 3, 2024
1 parent 28b7944 commit 619fcec
Show file tree
Hide file tree
Showing 11 changed files with 207 additions and 206 deletions.
180 changes: 0 additions & 180 deletions s/context/controllers/timeline/tools/VideoExport/worker.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import {XClip, XTimeline} from "../../types.js"
import {FFmpegHelper} from "../../helpers/FFmpegHelper/helper.js"
import {FileSystemHelper} from "../../helpers/FileSystemHelper/helper.js"
import {AnyEffect, XTimeline} from "../timeline/types.js"
import {FFmpegHelper} from "./helpers/FFmpegHelper/helper.js"
import {FileSystemHelper} from "./helpers/FileSystemHelper/helper.js"

export class VideoExport {
#worker = new Worker(new URL("./worker.js", import.meta.url), {type: "module"})
#worker = new Worker(new URL("./worker/worker.js", import.meta.url), {type: "module"})
#file: Uint8Array | null = null
#FFmpegHelper = new FFmpegHelper()
#FileSystemHelper = new FileSystemHelper()

constructor() {
this.#worker.addEventListener("message", async (msg: MessageEvent<{chunks: Uint8Array, type: string}>) => {
if(msg.data.type === "export-end") {
console.log(msg.data.chunks.length)
const binary_container_name = "raw.h264"
await this.#FFmpegHelper.write_binary_into_container(msg.data.chunks, binary_container_name)
await this.#FFmpegHelper.mux(binary_container_name, "test.mp4")
Expand All @@ -26,15 +27,17 @@ export class VideoExport {
}

async export_start(timeline: XTimeline) {
const sorted_clips = this.#sort_clips_by_track(timeline.clips)
this.#worker.postMessage({clips: sorted_clips})
console.log(timeline.effects)
const sorted_effects = this.#sort_effects_by_track(timeline.effects)
this.#worker.postMessage({effects: sorted_effects})
}

#sort_clips_by_track(clips: XClip[]) {
const sorted_clips = clips.sort((a, b) => {
#sort_effects_by_track(effects: AnyEffect[]) {
console.log(effects)
const sorted_effects = [...effects].sort((a, b) => {
if(a.track < b.track) return 1
else return -1
})
return sorted_clips
return sorted_effects
}
}
Binary file not shown.
18 changes: 18 additions & 0 deletions s/context/controllers/video-export/worker/bin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// function get_overlapping_effects(effect: Xeffect, effects: Xeffect[]): Overlapping[] {
// let overlapping: Overlapping[] = []
// const excluded = effects.filter(c => c.id !== effect.id)

// excluded.forEach((c) => {
// const from = Math.max(effect.start_at_position, c.start_at_position)
// const to = Math.min(effect.start_at_position + effect.duration, c.start_at_position + c.duration)
// if(from <= to)
// overlapping.push({effect, between: [from, to]})
// })

// return overlapping
// }
//
// interface Overlapping {
// effect: effectX
// between: V2
// }
Original file line number Diff line number Diff line change
@@ -1,28 +1,22 @@
import {Status} from "../../../../../context/controllers/timeline/types.js"
import MP4Box, {MP4File, MP4Info, MP4MediaTrack} from "../../../../../tools/mp4boxjs/mp4box.adapter.js"
import MP4Box, {MP4File, MP4Info, MP4MediaTrack} from "../../../../../../tools/mp4boxjs/mp4box.adapter.js"

type SetStatus = (status: Status, message: string) => void
type OnChunk = (chunk: EncodedVideoChunk) => void
type OnConfig = (config: VideoDecoderConfig) => void


export class MP4Demuxer {
#onConfig: OnConfig
#onChunk: OnChunk
#setStatus: SetStatus
#file: MP4File

constructor(uri: string, {onConfig, onChunk, setStatus}: {onConfig: OnConfig, onChunk: OnChunk, setStatus: SetStatus}) {
constructor(uri: string, {onConfig, onChunk}: {onConfig: OnConfig, onChunk: OnChunk}) {
this.#onConfig = onConfig
this.#onChunk = onChunk
this.#setStatus = setStatus

this.#file = MP4Box.createFile()
this.#file.onError = error => setStatus("demux", error)
this.#file.onReady = this.#onReady.bind(this)
this.#file.onSamples = this.#onSamples.bind(this)

const fileSink = new MP4FileSink(this.#file, setStatus)
const fileSink = new MP4FileSink(this.#file)
fetch(uri).then(response => {
response.body!.pipeTo(new WritableStream(fileSink, {highWaterMark: 2}))
})
Expand All @@ -44,17 +38,15 @@ export class MP4Demuxer {
}

#onReady(info: MP4Info) {
this.#setStatus("demux", "Ready")
const track = info.videoTracks[0]

this.#onConfig({
codec: track.codec.startsWith('vp08') ? 'vp8' : track.codec,
codedHeight: track.video.height,
codedWidth: track.video.width,
description: this.#description(track),
optimizeForLatency: true
});

this.#file.flush()
this.#file.setExtractionOptions(track.id)
this.#file.start()
}
Expand All @@ -67,18 +59,17 @@ export class MP4Demuxer {
duration: 1e6 * sample.duration / sample.timescale,
data: sample.data
}))
this.#file.flush()
}
}
}

class MP4FileSink {
#setStatus: SetStatus
#file: MP4File
#offset = 0

constructor(file: MP4File, setStatus: SetStatus) {
constructor(file: MP4File) {
this.#file = file
this.#setStatus = setStatus
}

write(chunk: ArrayBuffer) {
Expand All @@ -88,13 +79,11 @@ class MP4FileSink {
//@ts-ignore
buffer.fileStart = this.#offset
this.#offset += buffer.byteLength
this.#setStatus("fetch", (this.#offset / (1024 ** 2)).toFixed(1) + " MiB")!
//@ts-ignore
this.#file!.appendBuffer(buffer)
}

close() {
this.#setStatus("fetch", "Done")
this.#file.flush()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import {AnyEffect} from "../../../timeline/types.js"
import {sort_effects_by_track} from "./sort_effects_by_track.js"

export function get_effects_at_timestamp(effects: AnyEffect[], timestamp: number) {
const filtered_effects = effects.filter(effect => effect.start_at_position <= timestamp && effect.start_at_position + effect.duration >= timestamp)
const sorted_by_track = sort_effects_by_track(filtered_effects)
return sorted_by_track.length > 0 ? sorted_by_track : undefined
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import {AnyEffect} from "../../../timeline/types.js"

export function sort_effects_by_track(effects: AnyEffect[]) {
// so that effects on first track draw on top of things that are on second track
const sorted_effects = effects.sort((a, b) => {
if(a.track < b.track) return 1
else return -1
})
return sorted_effects
}
Loading

0 comments on commit 619fcec

Please sign in to comment.