Spaces:
Sleeping
Sleeping
| "use client" | |
| import { ClapProject, ClapSegment } from "@aitube/clap" | |
| import { PromiseResponseType, waitPromisesUntil } from "@/lib/utils/waitPromisesUntil" | |
| import { VideoLoop } from "./video-loop" | |
| import { generateVideo } from "./generateVideo" | |
| import { useStore } from "@/app/state/useStore" | |
| export async function resolve(segment: ClapSegment, clap: ClapProject): Promise<JSX.Element> { | |
| const { prompt } = segment | |
| // this is were the magic happen, and we create our buffer of N videos | |
| // we need to adopt a conservative approach, ideally no more than 3 videos in parallel per user | |
| const numberOfParallelRequests = 3 | |
| // the playback speed is the second trick: | |
| // it allows us to get more "value" per video (a run time of 2 sec instead of 1) | |
| // at the expense of a more sluggish appearance (10 fps -> 5 fps, if you pick a speed of 0.5) | |
| const playbackSpeed = 0.5 | |
| // running multiple requests in parallel increase the risk that a server is down | |
| // we also cannot afford to wait for all videos for too long as this increase latency, | |
| // so we should keep a tight execution window | |
| // with current resolution and step settings, | |
| // we achieve 3463.125 rendering time on a A10 Large | |
| const maxRenderingTimeForAllVideos = 5000 | |
| // this is how long we wait after we received our first video | |
| // this represents the variability between rendering time | |
| // | |
| // this parameters helps us "squeeze" the timeout, | |
| // if the hardware settings changed for instance | |
| // this is a way to say "how, this is faster than I expected, other videos should be fast too then" | |
| // | |
| // I've noticed it is between 0.5s and 1s with current settings | |
| // so let's a a slightly larger value | |
| const maxWaitTimeAfterFirstVideo = 1500 | |
| let playlist: string[] = [] | |
| try { | |
| // console.log(`resolveVideo: generating video for: ${prompt}`) | |
| const promises: Array<Promise<string>> = [] | |
| for (let i = 0; i < numberOfParallelRequests; i++) { | |
| // TODO use the Clap segments instead to bufferize the next scenes, | |
| // otherwise we just clone the current segment, which is not very interesting | |
| promises.push(generateVideo({ | |
| prompt, | |
| width: clap.meta.width, | |
| height: clap.meta.height, | |
| token: useStore.getState().jwtToken, | |
| mode: "object-uri" // it's better for videos withing Chrome, apparently | |
| })) | |
| } | |
| const results = await waitPromisesUntil(promises, maxWaitTimeAfterFirstVideo, maxRenderingTimeForAllVideos) | |
| playlist = results | |
| .filter(result => result?.status === PromiseResponseType.Resolved && typeof result?.value === "string") | |
| .map(result => result?.value || "") | |
| // console.log(`resolveVideo: generated ${assetUrl}`) | |
| } catch (err) { | |
| console.error(`resolveVideo failed: ${err}`) | |
| return <></> | |
| } | |
| // note: the latent-video class is not used for styling, but to grab the component | |
| // from JS when we need to segment etc | |
| return ( | |
| <VideoLoop | |
| className="latent-video object-cover h-full" | |
| playbackSpeed={playbackSpeed} | |
| playlist={playlist} | |
| /> | |
| ) | |
| } |