11import { RawImage } from "./image.js" ;
2+ import { apis } from "../env.js" ;
23
34export class RawVideoFrame {
45
@@ -42,55 +43,52 @@ export class RawVideo {
4243/**
4344 * Loads a video.
4445 *
45- * @param {string|Blob|HTMLVideoElement } url The video to process.
46+ * @param {string|Blob|HTMLVideoElement } src The video to process.
4647 * @param {Object } [options] Optional parameters.
4748 * @param {number } [options.num_frames=null] The number of frames to sample uniformly.
48- * If provided, the video is seeked to the desired positions rather than processing every frame.
4949 * @param {number } [options.fps=null] The number of frames to sample per second.
50- * If provided (and num_frames is null), the video is seeked at fixed time intervals.
5150 *
52- * @returns {Promise<RawVideo> } The video
51+ * @returns {Promise<RawVideo> } The loaded video.
5352 */
54- export async function load_video ( url , { num_frames = null , fps = null } = { } ) {
53+ export async function load_video ( src , { num_frames = null , fps = null } = { } ) {
54+ if ( ! apis . IS_BROWSER_ENV ) {
55+ throw new Error ( "`load_video` is currently only supported in browser environments." ) ;
56+ }
57+
58+ // TODO: Support efficiently loading all frames using the WebCodecs API.
59+ // Specfically, https://developer.mozilla.org/en-US/docs/Web/API/VideoDecoder
60+ if ( num_frames == null && fps == null ) {
61+ throw new Error ( "Either num_frames or fps must be provided." ) ;
62+ }
63+
5564 const frames = [ ] ;
5665
57- const video = document . createElement ( 'video' ) ;
58- if ( typeof url === 'string' ) {
59- video . src = url ;
60- } else if ( url instanceof Blob ) {
61- video . src = URL . createObjectURL ( url ) ;
62- } else if ( url instanceof HTMLVideoElement ) {
63- video . src = url . src ;
66+ const video = document . createElement ( "video" ) ;
67+ video . crossOrigin = "anonymous" ;
68+ video . muted = true ; // mute to allow autoplay and seeking
69+
70+ if ( typeof src === 'string' ) {
71+ video . src = src ;
72+ } else if ( src instanceof Blob ) {
73+ video . src = URL . createObjectURL ( src ) ;
74+ } else if ( src instanceof HTMLVideoElement ) {
75+ video . src = src . src ;
6476 } else {
6577 throw new Error ( "Invalid URL or video element provided." ) ;
6678 }
67- video . crossOrigin = "anonymous" ;
68- video . muted = true ; // mute to allow autoplay
69-
7079 // Wait for metadata to load to obtain duration
71- await new Promise ( ( resolve ) => {
72- video . onloadedmetadata = resolve ;
73- } ) ;
80+ await new Promise ( ( resolve ) => video . onloadedmetadata = resolve ) ;
7481
7582 if ( video . seekable . start ( 0 ) === video . seekable . end ( 0 ) ) {
7683 // Fallback: Download entire video if not seekable
7784 const response = await fetch ( video . src ) ;
7885 const blob = await response . blob ( ) ;
7986 video . src = URL . createObjectURL ( blob ) ;
80-
81- await new Promise ( ( resolve ) => {
82- video . onloadedmetadata = resolve ;
83- } ) ;
87+ await new Promise ( ( resolve ) => video . onloadedmetadata = resolve ) ;
8488 }
8589
8690 const duration = video . duration ;
8791
88- // Build an array of sample times based on num_frames or fps
89- let sampleTimes = [ ] ;
90- if ( num_frames == null && fps == null ) {
91- throw new Error ( "Either num_frames or fps must be provided." ) ;
92- }
93-
9492 let count , step ;
9593 if ( num_frames != null ) {
9694 count = num_frames ;
@@ -100,11 +98,13 @@ export async function load_video(url, { num_frames = null, fps = null } = {}) {
10098 count = Math . floor ( duration / step ) ;
10199 }
102100
103- for ( let i = 0 ; i < count ; i ++ ) {
101+ // Build an array of sample times based on num_frames or fps
102+ let sampleTimes = [ ] ;
103+ for ( let i = 0 ; i < count ; ++ i ) {
104104 sampleTimes . push ( num_frames === 1 ? duration / 2 : i * step ) ;
105105 }
106106
107- const canvas = document . createElement ( ' canvas' ) ;
107+ const canvas = document . createElement ( " canvas" ) ;
108108 canvas . width = video . videoWidth ;
109109 canvas . height = video . videoHeight ;
110110 const ctx = canvas . getContext ( "2d" , { willReadFrequently : true } ) ;
0 commit comments