summaryrefslogtreecommitdiff
path: root/apps/web/src/media/mediabunny.ts
diff options
context:
space:
mode:
Diffstat (limited to 'apps/web/src/media/mediabunny.ts')
-rw-r--r--apps/web/src/media/mediabunny.ts84
1 files changed, 61 insertions, 23 deletions
diff --git a/apps/web/src/media/mediabunny.ts b/apps/web/src/media/mediabunny.ts
index 47ffaeb1..3c949b35 100644
--- a/apps/web/src/media/mediabunny.ts
+++ b/apps/web/src/media/mediabunny.ts
@@ -1,43 +1,81 @@
-import { Input, ALL_FORMATS, BlobSource } from "mediabunny";
+import {
+ Input,
+ ALL_FORMATS,
+ BlobSource,
+ VideoSampleSink,
+ type VideoCodec,
+} from "mediabunny";
import { createTimelineAudioBuffer } from "@/media/audio";
import type { SceneTracks } from "@/timeline";
import type { MediaAsset } from "@/media/types";
import { TICKS_PER_SECOND } from "@/wasm";
+import { renderThumbnailDataUrl } from "./thumbnail";
-export async function getVideoInfo({
- videoFile,
-}: {
- videoFile: File;
-}): Promise<{
+export type VideoFileData = {
duration: number;
width: number;
height: number;
fps: number;
hasAudio: boolean;
-}> {
+ codec: VideoCodec | null;
+ canDecode: boolean;
+ thumbnailUrl: string | null;
+};
+
+export async function readVideoFile({
+ file,
+}: {
+ file: File;
+}): Promise<VideoFileData> {
const input = new Input({
- source: new BlobSource(videoFile),
+ source: new BlobSource(file),
formats: ALL_FORMATS,
});
- const duration = await input.computeDuration();
- const videoTrack = await input.getPrimaryVideoTrack();
+ try {
+ const duration = await input.computeDuration();
+ const videoTrack = await input.getPrimaryVideoTrack();
- if (!videoTrack) {
- throw new Error("No video track found in the file");
- }
+ if (!videoTrack) {
+ throw new Error("No video track found in the file");
+ }
+
+ const canDecode = await videoTrack.canDecode();
+ const packetStats = await videoTrack.computePacketStats(100);
+ const audioTrack = await input.getPrimaryAudioTrack();
- const packetStats = await videoTrack.computePacketStats(100);
- const fps = packetStats.averagePacketRate;
- const audioTrack = await input.getPrimaryAudioTrack();
+ let thumbnailUrl: string | null = null;
+ if (canDecode) {
+ const sink = new VideoSampleSink(videoTrack);
+ const frame = await sink.getSample(1);
+ if (frame) {
+ try {
+ thumbnailUrl = renderThumbnailDataUrl({
+ width: videoTrack.displayWidth,
+ height: videoTrack.displayHeight,
+ draw: ({ context, width, height }) => {
+ frame.draw(context, 0, 0, width, height);
+ },
+ });
+ } finally {
+ frame.close();
+ }
+ }
+ }
- return {
- duration,
- width: videoTrack.displayWidth,
- height: videoTrack.displayHeight,
- fps,
- hasAudio: audioTrack !== null,
- };
+ return {
+ duration,
+ width: videoTrack.displayWidth,
+ height: videoTrack.displayHeight,
+ fps: packetStats.averagePacketRate,
+ hasAudio: audioTrack !== null,
+ codec: videoTrack.codec,
+ canDecode,
+ thumbnailUrl,
+ };
+ } finally {
+ input.dispose();
+ }
}
const SAMPLE_RATE = 44100;