@lumen5/framefusion 0.0.24 → 0.0.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,2 +1,2 @@
1
- "use strict";var S=(r,i,t)=>{if(!i.has(r))throw TypeError("Cannot "+t)};var e=(r,i,t)=>(S(r,i,"read from private field"),t?t.call(r):i.get(r)),h=(r,i,t)=>{if(i.has(r))throw TypeError("Cannot add the same private member more than once");i instanceof WeakSet?i.add(r):i.set(r,t)},a=(r,i,t,s)=>(S(r,i,"write to private field"),s?s.call(r,t):i.set(r,t),t),T=(r,i,t,s)=>({set _(n){a(r,i,n,t)},get _(){return e(r,i,s)}}),O=(r,i,t)=>(S(r,i,"access private method"),t);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const q=require("@antoinemopa/beamcoder"),L=require("canvas"),V=require("path"),W=require("node:https"),B=require("http"),H=require("tmp"),U=require("fs-extra");class G{static async create(i){throw new Error("Not implemented")}async init({inputFileOrUrl:i,outputFile:t,threadCount:s=8,endTime:n,interpolateFps:p,interpolateMode:f}){throw new Error("Not implemented")}get duration(){throw new Error("Not implemented")}get width(){throw new Error("Not implemented")}get height(){throw new Error("Not implemented")}async seekToPTS(i){throw new Error("Not implemented")}async getFrameAtTime(i){throw new Error("Not implemented")}async getImageDataAtTime(i){throw new Error("Not implemented")}async getFrameAtPts(i){throw new Error("Not implemented")}async seekToTime(i){throw new Error("Not implemented")}ptsToTime(i){throw new Error("Not implemented")}async readFrames({onFrameAvailable:i,flush:t=!0}={flush:!0,onFrameAvailable:()=>!0}){throw new Error("Not implemented")}async dispose(){throw new Error("Not implemented")}}class K extends Error{}var v,A,N,F;class X{constructor(i){h(this,v,void 0);h(this,A,void 0);h(this,N,void 0);h(this,F,void 0);a(this,v,i)}get filepath(){return e(this,N)}async download(){await new Promise((i,t)=>{const s=e(this,v),n=V.extname(s);a(this,F,H.fileSync({postfix:n}));try{const p=s.startsWith("https://")?W:B;a(this,A,p.get(s,f=>{const _=f.headers["content-type"];if(!_.includes("video")){const c=new Error(`Source ${s}, returned unsupported content type ${_}`);t(c);return}const o=U.createWriteStream(e(this,F).name);f.pipe(o),o.on("finish",()=>{o.close(),a(this,N,e(this,F).name),i()}),o.on("error",c=>{t(c)})})),e(this,A).on("error",f=>{f instanceof K||t(f)})}catch(p){t(p)}})}clear(){e(this,F)&&e(this,F).removeCallback(),e(this,v)&&a(this,v,void 0),e(this,A)&&a(this,A,null),e(this,N)&&a(this,N,void 0)}}v=new WeakMap,A=new WeakMap,N=new WeakMap,F=new WeakMap;const Y=({demuxer:r,streamIndex:i,threadCount:t})=>q.decoder({demuxer:r,width:r.streams[i].codecpar.width,height:r.streams[i].codecpar.height,stream_index:i,pix_fmt:r.streams[i].codecpar.format,thread_count:t}),j=async({stream:r,outputPixelFormat:i,interpolateFps:t,interpolateMode:s="fast"})=>{if(!r.codecpar.format)return null;let n=[`[in0:v]format=${r.codecpar.format}`];if(t)if(s==="high-quality")n=[...n,`minterpolate=fps=${t}`];else if(s==="fast")n=[...n,`fps=${t}`];else throw new Error(`Unexpected interpolation mode: ${s}`);const p=n.join(", ")+"[out0:v]";return q.filterer({filterType:"video",inputParams:[{name:"in0:v",width:r.codecpar.width,height:r.codecpar.height,pixelFormat:r.codecpar.format,timeBase:r.time_base,pixelAspect:r.sample_aspect_ratio}],outputParams:[{name:"out0:v",pixelFormat:i}],filterSpec:p})},$="video",J="rgba",Q=5;var l,m,P,w,g,y,E,b,u,k,D,R,I;const M=class extends G{constructor(){super(...arguments);h(this,R);h(this,l,null);h(this,m,null);h(this,P,null);h(this,w,[]);h(this,g,[]);h(this,y,null);h(this,E,null);h(this,b,8);h(this,u,0);h(this,k,0);h(this,D,0)}static async create(t){const s=new M;return await s.init(t),s}async init({inputFileOrUrl:t,threadCount:s=8}){if(a(this,b,s),t.startsWith("http")){const n=new X(t);await n.download(),t=n.filepath}if(a(this,l,await q.demuxer("file:"+t)),a(this,u,e(this,l).streams.findIndex(n=>n.codecpar.codec_type===$)),e(this,u)===-1)throw new Error(`File has no ${$} stream!`);a(this,P,await j({stream:e(this,l).streams[e(this,u)],outputPixelFormat:J}))}get duration(){return this.ptsToTime(e(this,l).streams[e(this,u)].duration)}get width(){return e(this,l).streams[e(this,u)].codecpar.width}get height(){return e(this,l).streams[e(this,u)].codecpar.height}async getFrameAtTime(t){const s=Math.floor(this._timeToPTS(t));return this._getFrameAtPts(s)}async getImageDataAtTime(t){const s=Math.floor(this._timeToPTS(t)),n=await this._getFrameAtPts(s);if(!n)return null;const p=this._resizeFrameData(n);return L.createImageData(p,n.width,n.height)}_timeToPTS(t){const s=e(this,l).streams[e(this,u)].time_base;return t*s[1]/s[0]}ptsToTime(t){const s=e(this,l).streams[e(this,u)].time_base;return t*s[0]/s[1]}get packetReadCount(){return e(this,k)}async _getFrameAtPts(t,s=0){a(this,k,0);const n=3,p=e(this,w).flat().some(c=>this.ptsToTime(Math.abs(t-c.pts))<n);(e(this,E)===null||e(this,E)>t||!p)&&(await e(this,l).seek({stream_index:0,timestamp:t+s,any:!1}),await O(this,R,I).call(this),a(this,y,null),a(this,g,[]),a(this,E,t),a(this,w,[]));let f=null,_=-1,o=null;if(e(this,w).length>0){const c=e(this,w).flat().find(d=>d.pts<=t);if(c){const d=e(this,w).flat().find(x=>x.pts>c.pts);if(_=c.pts,o=c,d&&d.pts>t||_===t)return a(this,E,t),o}}for(!e(this,y)&&e(this,g).length===0&&({packet:T(this,y)._,frames:T(this,g)._}=await this._getNextPacketAndDecodeFrames(),T(this,k)._++);(e(this,y)||e(this,g).length!==0)&&_<t;){if(e(this,g).length!==0){f=(await e(this,P).filter([{name:"in0:v",frames:e(this,g)}])).flatMap(x=>x.frames);const d=f.reverse().find(x=>x.pts<=t);if(!d)return o;if(e(this,w).unshift(f),e(this,w).length>2&&e(this,w).pop(),_=d==null?void 0:d.pts,!o||_<=t)a(this,E,t),o=d;else break}({packet:T(this,y)._,frames:T(this,g)._}=await this._getNextPacketAndDecodeFrames()),T(this,k)._++}if(!o){if(Q<e(this,D))throw Error("No matching frame found");const c=.1,d=this._timeToPTS(c);T(this,D)._++,o=await this._getFrameAtPts(t,s-d),o&&a(this,D,0)}return o}async _getNextPacketAndDecodeFrames(){const t=await this._getNextVideoStreamPacket();let s=null;t!==null&&e(this,m)?s=await e(this,m).decode(t):e(this,m)&&(s=await e(this,m).flush(),a(this,m,null));let n=[];return s&&s.frames.length!==0&&(n=s.frames),{packet:t,frames:n}}async _getNextVideoStreamPacket(){let t=await e(this,l).read();for(;t&&t.stream_index!==e(this,u);)if(t=await e(this,l).read(),t===null)return null;return t}_resizeFrameData(t){const n=t.width*t.height*4,p=new Uint8ClampedArray(n),f=t.linesize,_=t.data[0];for(let o=0;o<t.height;o++){const c=o*f,d=c+t.width*4,x=_.slice(c,d),z=o*t.width*4;p.set(x,z)}return p}async dispose(){e(this,m)&&(await e(this,m).flush(),a(this,m,null)),e(this,l).forceClose(),a(this,P,null),a(this,w,void 0),a(this,g,[]),a(this,y,null),a(this,E,null),a(this,u,0)}};let C=M;l=new WeakMap,m=new WeakMap,P=new WeakMap,w=new WeakMap,g=new WeakMap,y=new WeakMap,E=new WeakMap,b=new WeakMap,u=new WeakMap,k=new WeakMap,D=new WeakMap,R=new WeakSet,I=async function(){e(this,m)&&(await e(this,m).flush(),a(this,m,null)),a(this,m,Y({demuxer:e(this,l),streamIndex:e(this,u),threadCount:e(this,b)}))};exports.BeamcoderExtractor=C;
1
+ "use strict";var S=(r,i,t)=>{if(!i.has(r))throw TypeError("Cannot "+t)};var e=(r,i,t)=>(S(r,i,"read from private field"),t?t.call(r):i.get(r)),h=(r,i,t)=>{if(i.has(r))throw TypeError("Cannot add the same private member more than once");i instanceof WeakSet?i.add(r):i.set(r,t)},a=(r,i,t,s)=>(S(r,i,"write to private field"),s?s.call(r,t):i.set(r,t),t),F=(r,i,t,s)=>({set _(n){a(r,i,n,t)},get _(){return e(r,i,s)}}),M=(r,i,t)=>(S(r,i,"access private method"),t);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const q=require("@antoinemopa/beamcoder"),L=require("canvas"),V=require("path"),W=require("node:https"),B=require("http"),H=require("tmp"),U=require("fs-extra");class G{static async create(i){throw new Error("Not implemented")}async init({inputFileOrUrl:i,outputFile:t,threadCount:s=8,endTime:n,interpolateFps:p,interpolateMode:f}){throw new Error("Not implemented")}get duration(){throw new Error("Not implemented")}get width(){throw new Error("Not implemented")}get height(){throw new Error("Not implemented")}async seekToPTS(i){throw new Error("Not implemented")}async getFrameAtTime(i){throw new Error("Not implemented")}async getImageDataAtTime(i){throw new Error("Not implemented")}async getFrameAtPts(i){throw new Error("Not implemented")}async seekToTime(i){throw new Error("Not implemented")}ptsToTime(i){throw new Error("Not implemented")}async readFrames({onFrameAvailable:i,flush:t=!0}={flush:!0,onFrameAvailable:()=>!0}){throw new Error("Not implemented")}async dispose(){throw new Error("Not implemented")}}class K extends Error{}var A,y,N,x;class X{constructor(i){h(this,A,void 0);h(this,y,void 0);h(this,N,void 0);h(this,x,void 0);a(this,A,i)}get filepath(){return e(this,N)}async download(){await new Promise((i,t)=>{const s=e(this,A),n=V.extname(s);a(this,x,H.fileSync({postfix:n}));try{const p=s.startsWith("https://")?W:B;a(this,y,p.get(s,f=>{const _=f.headers["content-type"];if(!_.includes("video")){const c=new Error(`Source ${s}, returned unsupported content type ${_}`);t(c);return}const o=U.createWriteStream(e(this,x).name);f.pipe(o),o.on("finish",()=>{o.close(),a(this,N,e(this,x).name),i()}),o.on("error",c=>{t(c)})})),e(this,y).on("error",f=>{f instanceof K||t(f)}),e(this,y).on("timeout",()=>{e(this,y).destroy(),t(new Error(`Request to ${s} timed out`))})}catch(p){t(p)}})}clear(){e(this,x)&&e(this,x).removeCallback(),e(this,A)&&a(this,A,void 0),e(this,y)&&a(this,y,null),e(this,N)&&a(this,N,void 0)}}A=new WeakMap,y=new WeakMap,N=new WeakMap,x=new WeakMap;const Y=({demuxer:r,streamIndex:i,threadCount:t})=>q.decoder({demuxer:r,width:r.streams[i].codecpar.width,height:r.streams[i].codecpar.height,stream_index:i,pix_fmt:r.streams[i].codecpar.format,thread_count:t}),J=async({stream:r,outputPixelFormat:i,interpolateFps:t,interpolateMode:s="fast"})=>{if(!r.codecpar.format)return null;let n=[`[in0:v]format=${r.codecpar.format}`];if(t)if(s==="high-quality")n=[...n,`minterpolate=fps=${t}`];else if(s==="fast")n=[...n,`fps=${t}`];else throw new Error(`Unexpected interpolation mode: ${s}`);const p=n.join(", ")+"[out0:v]";return q.filterer({filterType:"video",inputParams:[{name:"in0:v",width:r.codecpar.width,height:r.codecpar.height,pixelFormat:r.codecpar.format,timeBase:r.time_base,pixelAspect:r.sample_aspect_ratio}],outputParams:[{name:"out0:v",pixelFormat:i}],filterSpec:p})},O="video",Q="rgba",Z=5;var l,m,P,w,g,E,T,R,u,k,D,b,I;const $=class extends G{constructor(){super(...arguments);h(this,b);h(this,l,null);h(this,m,null);h(this,P,null);h(this,w,[]);h(this,g,[]);h(this,E,null);h(this,T,null);h(this,R,8);h(this,u,0);h(this,k,0);h(this,D,0)}static async create(t){const s=new $;return await s.init(t),s}async init({inputFileOrUrl:t,threadCount:s=8}){if(a(this,R,s),t.startsWith("http")){const n=new X(t);await n.download(),t=n.filepath}if(a(this,l,await q.demuxer("file:"+t)),a(this,u,e(this,l).streams.findIndex(n=>n.codecpar.codec_type===O)),e(this,u)===-1)throw new Error(`File has no ${O} stream!`);a(this,P,await J({stream:e(this,l).streams[e(this,u)],outputPixelFormat:Q}))}get duration(){return this.ptsToTime(e(this,l).streams[e(this,u)].duration)}get width(){return e(this,l).streams[e(this,u)].codecpar.width}get height(){return e(this,l).streams[e(this,u)].codecpar.height}async getFrameAtTime(t){const s=Math.round(this._timeToPTS(t));return this._getFrameAtPts(s)}async getImageDataAtTime(t){const s=Math.round(this._timeToPTS(t)),n=await this._getFrameAtPts(s);if(!n)return null;const p=this._resizeFrameData(n);return L.createImageData(p,n.width,n.height)}_timeToPTS(t){const s=e(this,l).streams[e(this,u)].time_base;return t*s[1]/s[0]}ptsToTime(t){const s=e(this,l).streams[e(this,u)].time_base;return t*s[0]/s[1]}get packetReadCount(){return e(this,k)}async _getFrameAtPts(t,s=0){a(this,k,0);const n=3,p=e(this,w).flat().some(c=>this.ptsToTime(Math.abs(t-c.pts))<n);(e(this,T)===null||e(this,T)>t||!p)&&(await e(this,l).seek({stream_index:0,timestamp:t+s,any:!1}),await M(this,b,I).call(this),a(this,E,null),a(this,g,[]),a(this,T,t),a(this,w,[]));let f=null,_=-1,o=null;if(e(this,w).length>0){const c=e(this,w).flat().find(d=>d.pts<=t);if(c){const d=e(this,w).flat().find(v=>v.pts>c.pts);if(_=c.pts,o=c,d&&d.pts>t||_===t)return a(this,T,t),o}}for(!e(this,E)&&e(this,g).length===0&&({packet:F(this,E)._,frames:F(this,g)._}=await this._getNextPacketAndDecodeFrames(),F(this,k)._++);(e(this,E)||e(this,g).length!==0)&&_<t;){if(e(this,g).length!==0){f=(await e(this,P).filter([{name:"in0:v",frames:e(this,g)}])).flatMap(v=>v.frames);const d=f.reverse().find(v=>v.pts<=t);if(!d)return o;if(e(this,w).unshift(f),e(this,w).length>2&&e(this,w).pop(),_=d==null?void 0:d.pts,!o||_<=t)a(this,T,t),o=d;else break}({packet:F(this,E)._,frames:F(this,g)._}=await this._getNextPacketAndDecodeFrames()),F(this,k)._++}if(!o){if(Z<e(this,D))throw Error("No matching frame found");const c=.1,d=this._timeToPTS(c);F(this,D)._++,o=await this._getFrameAtPts(t,s-d),o&&a(this,D,0)}return o}async _getNextPacketAndDecodeFrames(){const t=await this._getNextVideoStreamPacket();let s=null;t!==null&&e(this,m)?s=await e(this,m).decode(t):e(this,m)&&(s=await e(this,m).flush(),a(this,m,null));let n=[];return s&&s.frames.length!==0&&(n=s.frames),{packet:t,frames:n}}async _getNextVideoStreamPacket(){let t=await e(this,l).read();for(;t&&t.stream_index!==e(this,u);)if(t=await e(this,l).read(),t===null)return null;return t}_resizeFrameData(t){const n=t.width*t.height*4,p=new Uint8ClampedArray(n),f=t.linesize,_=t.data[0];for(let o=0;o<t.height;o++){const c=o*f,d=c+t.width*4,v=_.slice(c,d),z=o*t.width*4;p.set(v,z)}return p}async dispose(){e(this,m)&&(await e(this,m).flush(),a(this,m,null)),e(this,l).forceClose(),a(this,P,null),a(this,w,void 0),a(this,g,[]),a(this,E,null),a(this,T,null),a(this,u,0)}};let C=$;l=new WeakMap,m=new WeakMap,P=new WeakMap,w=new WeakMap,g=new WeakMap,E=new WeakMap,T=new WeakMap,R=new WeakMap,u=new WeakMap,k=new WeakMap,D=new WeakMap,b=new WeakSet,I=async function(){e(this,m)&&(await e(this,m).flush(),a(this,m,null)),a(this,m,Y({demuxer:e(this,l),streamIndex:e(this,u),threadCount:e(this,R)}))};exports.BeamcoderExtractor=C;
2
2
  //# sourceMappingURL=framefusion.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"framefusion.cjs","sources":["../src/BaseExtractor.ts","../src/DownloadVideoURL.ts","../src/backends/beamcoder.ts"],"sourcesContent":["import type { ImageData } from 'canvas';\n\nimport type {\n ExtractorArgs,\n Frame,\n Extractor\n} from '../framefusion';\n\nexport class BaseExtractor implements Extractor {\n static async create(args: ExtractorArgs): Promise<Extractor> {\n throw new Error('Not implemented');\n }\n\n async init({\n inputFileOrUrl,\n outputFile,\n threadCount = 8,\n endTime,\n interpolateFps,\n interpolateMode,\n }: ExtractorArgs): Promise<void> {\n throw new Error('Not implemented');\n }\n\n get duration(): number {\n throw new Error('Not implemented');\n }\n\n get width(): number {\n throw new Error('Not implemented');\n }\n\n get height(): number {\n throw new Error('Not implemented');\n }\n\n async seekToPTS(targetPts: number) {\n throw new Error('Not implemented');\n }\n\n async getFrameAtTime(targetTime: number): Promise<Frame> {\n throw new Error('Not implemented');\n }\n\n async getImageDataAtTime(targetTime: number): Promise<ImageData> {\n throw new Error('Not implemented');\n }\n\n async getFrameAtPts(targetPts: number): Promise<Frame> {\n throw new Error('Not implemented');\n }\n\n async seekToTime(targetTime: number) {\n throw new Error('Not implemented');\n }\n\n /**\n * Convert a PTS (based on timebase) to PTS (in seconds)\n */\n ptsToTime(pts: number) {\n throw new Error('Not implemented');\n }\n\n async readFrames({\n onFrameAvailable,\n flush = true,\n }: {\n /**\n * Return true if we need to read more frames.\n */\n onFrameAvailable?: (frame: Frame) => Promise<boolean> | boolean;\n flush?: boolean;\n } = {\n flush: true,\n onFrameAvailable: () => true,\n }) {\n throw new Error('Not implemented');\n }\n\n async dispose() {\n throw new Error('Not implemented');\n }\n}\n","import path from 'path';\nimport https from 'node:https';\nimport type { ClientRequest } from 'http';\nimport http from 'http';\nimport tmp from 'tmp';\nimport fs from 'fs-extra';\n\nclass CancelRequestError extends Error { }\n\n/**\n * Downloads a video file from a given URL as a temporary file. When the object is cleared, the temporary file is\n * deleted.\n */\nexport class DownloadVideoURL {\n #url: string | undefined;\n #httpRequest: ClientRequest | undefined = undefined;\n #filepath: string | undefined = undefined;\n #tmpObj: tmp.FileResult | undefined = undefined;\n\n constructor(url: string) {\n this.#url = url;\n }\n\n /**\n * returns the filepath of the downloaded file. If the file has not been downloaded yet, it will be undefined\n */\n get filepath() {\n return this.#filepath;\n }\n\n /**\n * Downloads the file from the given URL. The file will be downloaded to a temporary file.\n */\n async download() {\n await new Promise<void>((resolve, reject) => {\n const source = this.#url;\n const extension = path.extname(source);\n this.#tmpObj = tmp.fileSync({ postfix: extension });\n try {\n const connectionHandler = source.startsWith('https://') ? https : http;\n this.#httpRequest = connectionHandler.get(source, (res) => {\n const contentType = res.headers['content-type'];\n if (!contentType.includes('video')) {\n const err = new Error(`Source ${source}, returned unsupported content type ${contentType}`);\n reject(err);\n return;\n }\n const writeStream = fs.createWriteStream(this.#tmpObj.name);\n res.pipe(writeStream);\n writeStream.on('finish', () => {\n writeStream.close();\n this.#filepath = this.#tmpObj.name;\n resolve();\n });\n writeStream.on('error', (e) => {\n reject(e);\n });\n });\n this.#httpRequest.on('error', (e) => {\n if (e instanceof CancelRequestError) {\n return;\n }\n reject(e);\n });\n }\n catch (e) {\n reject(e);\n }\n });\n }\n\n clear() {\n if (this.#tmpObj) this.#tmpObj.removeCallback();\n if (this.#url) this.#url = undefined;\n if (this.#httpRequest) this.#httpRequest = null;\n if (this.#filepath) this.#filepath = undefined;\n }\n}\n","import type {\n Packet,\n Demuxer,\n Decoder,\n Filterer,\n Frame\n} from '@antoinemopa/beamcoder';\nimport beamcoder from '@antoinemopa/beamcoder';\nimport type { ImageData } from 'canvas';\nimport { createImageData } from 'canvas';\nimport { BaseExtractor } from '../BaseExtractor';\nimport type { Extractor, ExtractorArgs, InterpolateMode } from '../../framefusion';\nimport { DownloadVideoURL } from '../DownloadVideoURL';\n\nconst VERBOSE = false;\n\nconst createDecoder = ({\n demuxer,\n streamIndex,\n threadCount,\n}: {\n demuxer: Demuxer;\n streamIndex: number;\n threadCount: number;\n}): Decoder => {\n return beamcoder.decoder({\n demuxer: demuxer,\n width: demuxer.streams[streamIndex].codecpar.width,\n height: demuxer.streams[streamIndex].codecpar.height,\n stream_index: streamIndex,\n pix_fmt: demuxer.streams[streamIndex].codecpar.format,\n thread_count: threadCount,\n });\n};\n\n/**\n * A filter to convert between color spaces.\n * An example would be YUV to RGB, for mp4 to png conversion.\n */\nconst createFilter = async({\n stream,\n outputPixelFormat,\n interpolateFps,\n interpolateMode = 'fast',\n}: {\n stream: beamcoder.Stream;\n outputPixelFormat: string;\n interpolateFps?: number;\n interpolateMode?: InterpolateMode;\n}): Promise<beamcoder.Filterer> => {\n if (!stream.codecpar.format) {\n return null;\n }\n\n let filterSpec = [`[in0:v]format=${stream.codecpar.format}`];\n\n if (interpolateFps) {\n if (interpolateMode === 'high-quality') {\n filterSpec = [...filterSpec, `minterpolate=fps=${interpolateFps}`];\n }\n else if (interpolateMode === 'fast') {\n filterSpec = [...filterSpec, `fps=${interpolateFps}`];\n }\n else {\n throw new Error(`Unexpected interpolation mode: ${interpolateMode}`);\n }\n }\n\n const filterSpecStr = filterSpec.join(', ') + '[out0:v]';\n\n VERBOSE && console.log(`filterSpec: ${filterSpecStr}`);\n\n return beamcoder.filterer({\n filterType: 'video',\n inputParams: [\n {\n name: 'in0:v',\n width: stream.codecpar.width,\n height: stream.codecpar.height,\n pixelFormat: stream.codecpar.format,\n timeBase: stream.time_base,\n pixelAspect: stream.sample_aspect_ratio,\n },\n ],\n outputParams: [\n {\n name: 'out0:v',\n pixelFormat: outputPixelFormat,\n },\n ],\n filterSpec: filterSpecStr,\n });\n};\n\nconst STREAM_TYPE_VIDEO = 'video';\nconst COLORSPACE_RGBA = 'rgba';\nconst MAX_RECURSION = 5;\n\n/**\n * A simple extractor that uses beamcoder to extract frames from a video file.\n */\nexport class BeamcoderExtractor extends BaseExtractor implements Extractor {\n /**\n * The demuxer reads the file and outputs packet streams\n */\n #demuxer: Demuxer = null;\n\n /**\n * The decoder reads packets and can output raw frame data\n */\n #decoder: Decoder = null;\n\n /**\n * Packets can be filtered to change colorspace, fps and add various effects. If there are no colorspace changes or\n * filters, filter might not be necessary.\n */\n #filterer: Filterer = null;\n\n /**\n * This is where we store filtered frames from each previously processed packet.\n * We keep these in chronological order. We hang on to them for two reasons:\n * 1. so we can return them if we get a request for the same time again\n * 2. so we can return frames close the end of the stream. When such a frame is requested we have to flush (destroy)\n * the encoder to get the last few frames. This avoids having to re-create an encoder.\n */\n #filteredFramesPacket: undefined[] | Array<Array<Frame>> = [];\n\n /**\n * This contains the last raw frames we read from the demuxer. We use it as a starting point for each new query. We\n * do this ensure we don't skip any frames.\n */\n #frames = [];\n\n /**\n * This contains the last packet we read from the demuxer. We use it as a starting point for each new query. We do\n * this ensure we don't skip any frames.\n */\n #packet: null | Packet = null;\n\n /**\n * The last target presentation timestamp (PTS) we requested. If we never requested a time(stamp) then this\n * value is null\n */\n #previousTargetPTS: null | number = null;\n\n /**\n * The number of threads to use for decoding\n */\n #threadCount = 8;\n\n /**\n * The index of the video stream in the demuxer\n */\n #streamIndex = 0;\n\n /**\n * The number of packets we've read from the demuxer to complete the frame query\n * @private\n */\n #packetReadCount = 0;\n\n /**\n * The number of times we've recursively read packets from the demuxer to complete the frame query\n * @private\n */\n #recursiveReadCount = 0;\n\n /**\n * Encoder/Decoder construction is async, so it can't be put in a regular constructor.\n * Use and await this method to generate an extractor.\n */\n static async create(args: ExtractorArgs): Promise<BeamcoderExtractor> {\n const extractor = new BeamcoderExtractor();\n await extractor.init(args);\n return extractor;\n }\n\n async init({\n inputFileOrUrl,\n threadCount = 8,\n }: ExtractorArgs): Promise<void> {\n this.#threadCount = threadCount;\n if (inputFileOrUrl.startsWith('http')) {\n VERBOSE && console.log('downloading url', inputFileOrUrl);\n const downloadUrl = new DownloadVideoURL(inputFileOrUrl);\n await downloadUrl.download();\n inputFileOrUrl = downloadUrl.filepath;\n VERBOSE && console.log('finished downloading');\n }\n this.#demuxer = await beamcoder.demuxer('file:' + inputFileOrUrl);\n this.#streamIndex = this.#demuxer.streams.findIndex(stream => stream.codecpar.codec_type === STREAM_TYPE_VIDEO);\n if (this.#streamIndex === -1) {\n throw new Error(`File has no ${STREAM_TYPE_VIDEO} stream!`);\n }\n this.#filterer = await createFilter({\n stream: this.#demuxer.streams[this.#streamIndex],\n outputPixelFormat: COLORSPACE_RGBA,\n });\n }\n\n async #createDecoder() {\n // It's possible that we need to create decoder multiple times during the lifecycle of this extractor so we\n // need to make sure we destroy the old one first if it exists\n if (this.#decoder) {\n await this.#decoder.flush();\n this.#decoder = null;\n }\n this.#decoder = createDecoder({\n demuxer: this.#demuxer as Demuxer,\n streamIndex: this.#streamIndex,\n threadCount: this.#threadCount,\n });\n }\n\n /**\n * This is the duration of the first video stream in the file expressed in seconds.\n */\n get duration(): number {\n return this.ptsToTime(this.#demuxer.streams[this.#streamIndex].duration);\n }\n\n /**\n * Width in pixels\n */\n get width(): number {\n return this.#demuxer.streams[this.#streamIndex].codecpar.width;\n }\n\n /**\n * Height in pixels\n */\n get height(): number {\n return this.#demuxer.streams[this.#streamIndex].codecpar.height;\n }\n\n /**\n * Get the beamcoder Frame for a given time in seconds\n * @param targetTime\n */\n async getFrameAtTime(targetTime: number): Promise<beamcoder.Frame> {\n VERBOSE && console.log(`getFrameAtTime time(s)=${targetTime}`);\n const targetPts = Math.floor(this._timeToPTS(targetTime));\n return this._getFrameAtPts(targetPts);\n }\n\n /**\n * Get imageData for a given time in seconds\n * @param targetTime\n */\n async getImageDataAtTime(targetTime: number): Promise<ImageData> {\n const targetPts = Math.floor(this._timeToPTS(targetTime));\n VERBOSE && console.log('targetTime', targetTime, '-> targetPts', targetPts);\n const frame = await this._getFrameAtPts(targetPts);\n if (!frame) {\n VERBOSE && console.log('no frame found');\n return null;\n }\n const rawData = this._resizeFrameData(frame);\n const image = createImageData(\n rawData,\n frame.width,\n frame.height\n ) as ImageData;\n return image;\n }\n\n /**\n * Get the presentation timestamp (PTS) for a given time in seconds\n */\n _timeToPTS(time: number) {\n const time_base = this.#demuxer.streams[this.#streamIndex].time_base;\n return time * time_base[1] / time_base[0];\n }\n\n /**\n * Get the time in seconds from a given presentation timestamp (PTS)\n */\n ptsToTime(pts: number) {\n const time_base = this.#demuxer.streams[this.#streamIndex].time_base;\n return pts * time_base[0] / time_base[1];\n }\n\n get packetReadCount() {\n return this.#packetReadCount;\n }\n\n /**\n * Get the frame at the given presentation timestamp (PTS)\n * @param targetPTS - the target presentation timestamp (PTS) we want to retrieve\n * @param SeekPTSOffset - the offset to use when seeking to the targetPTS. This is used when we have trouble finding\n * the targetPTS. We use it to further move away from the requested PTS to find a frame. The allows use to read\n * additional packets and find a frame that is closer to the targetPTS.\n */\n async _getFrameAtPts(targetPTS: number, SeekPTSOffset = 0): Promise<beamcoder.Frame> {\n VERBOSE && console.log('_getFrameAtPts', targetPTS, SeekPTSOffset, '-> duration', this.duration);\n this.#packetReadCount = 0;\n\n // seek and create a decoder when retrieving a frame for the first time or when seeking backwards\n // we have to create a new decoder when seeking backwards as the decoder can only process frames in\n // chronological order.\n // RE_SEEK_DELTA: sometimes, we are looking for a frame so far ahead that it's better to drop everything and seek.\n // Example: when we got a frame a 0 and request a frame at t = 30s just after, we don't want to start reading all packets\n // until 30s.\n const RE_SEEK_THRESHOLD = 3; // 3 seconds - typically we have keyframes at shorter intervals\n const hasFrameWithinThreshold = this.#filteredFramesPacket.flat().some(frame => {\n return this.ptsToTime(Math.abs(targetPTS - (frame as Frame).pts)) < RE_SEEK_THRESHOLD;\n });\n VERBOSE && console.log('hasPreviousTargetPTS', this.#previousTargetPTS === null, 'targetPTS is smaller', this.#previousTargetPTS > targetPTS, 'has frame within threshold', hasFrameWithinThreshold);\n if (this.#previousTargetPTS === null || this.#previousTargetPTS > targetPTS || !hasFrameWithinThreshold) {\n VERBOSE && console.log(`Seeking to ${targetPTS - SeekPTSOffset}`);\n\n await this.#demuxer.seek({\n stream_index: 0, // even though we specify the stream index, it still seeks all streams\n timestamp: targetPTS + SeekPTSOffset,\n any: false,\n });\n await this.#createDecoder();\n this.#packet = null;\n this.#frames = [];\n this.#previousTargetPTS = targetPTS;\n this.#filteredFramesPacket = [];\n }\n\n let filteredFrames = null;\n let closestFramePTS = -1;\n let outputFrame = null;\n\n // If we have previously filtered frames, get the frame closest to our targetPTS\n if (this.#filteredFramesPacket.length > 0) {\n const closestFrame = this.#filteredFramesPacket\n .flat()\n .find(f => (f as Frame).pts <= targetPTS) as Frame;\n\n if (closestFrame) {\n const nextFrame = this.#filteredFramesPacket\n .flat()\n .find(f => (f as Frame).pts > closestFrame.pts) as Frame;\n\n VERBOSE && console.log('returning previously filtered frame with pts', (closestFrame as Frame).pts);\n closestFramePTS = (closestFrame as Frame).pts;\n outputFrame = closestFrame;\n\n if ((nextFrame && nextFrame.pts > targetPTS) || (closestFramePTS === targetPTS)) {\n // We have a next frame, so we know the frame being displayed at targetPTS is the previous one,\n // which corresponds to outputFrame.\n this.#previousTargetPTS = targetPTS;\n return outputFrame;\n }\n }\n }\n\n // This is the first time we're decoding frames. Get the first packet and decode it.\n if (!this.#packet && this.#frames.length === 0) {\n ({ packet: this.#packet, frames: this.#frames } = await this._getNextPacketAndDecodeFrames());\n this.#packetReadCount++;\n }\n // Read packets until we have a frame which is closest to targetPTS\n while ((this.#packet || this.#frames.length !== 0) && closestFramePTS < targetPTS) {\n VERBOSE && console.log('packet si:', this.#packet?.stream_index, 'pts:', this.#packet?.pts, 'frames:', this.#frames?.length);\n VERBOSE && console.log('frames', this.#frames?.length, 'frames.pts:', this.#frames?.map(f => f.pts), '-> target.pts:', targetPTS);\n\n // packet contains frames\n if (this.#frames.length !== 0) {\n // filter the frames\n const filteredResult = await this.#filterer.filter([{ name: 'in0:v', frames: this.#frames }]);\n filteredFrames = filteredResult.flatMap(r => r.frames);\n VERBOSE && console.log('filteredFrames', filteredFrames.length, 'filteredFrames.pts:', filteredFrames.map(f => f.pts), '-> target.pts:', targetPTS);\n\n // get the closest frame to our target presentation timestamp (PTS)\n // Beamcoder returns decoded packet frames as follows: [1000, 2000, 3000, 4000]\n // If we're looking for a frame at 2500, we want to return the frame at 2000\n const closestFrame = filteredFrames.reverse().find(f => f.pts <= targetPTS);\n\n // The packet contains frames, but all of them have PTS larger than our a targetPTS (we looked too far)\n if (!closestFrame) {\n return outputFrame;\n }\n\n // store the filtered packet frames for later reuse\n this.#filteredFramesPacket.unshift(filteredFrames);\n if (this.#filteredFramesPacket.length > 2) {\n this.#filteredFramesPacket.pop();\n }\n\n closestFramePTS = closestFrame?.pts;\n VERBOSE && console.log('closestFramePTS', closestFramePTS, 'targetPTS', targetPTS);\n if (!outputFrame || closestFramePTS <= targetPTS) {\n VERBOSE && console.log('assigning outputFrame', closestFrame?.pts);\n this.#previousTargetPTS = targetPTS;\n outputFrame = closestFrame;\n }\n else {\n // break out of the loop if we've found the closest frame (and ensure we don't move to the next\n // packet by calling _getNextPacketAndDecodeFrames again) as this risks us getting a frame that is\n // after our targetPTS\n VERBOSE && console.log('breaking');\n break;\n }\n }\n // get the next packet and frames\n ({ packet: this.#packet, frames: this.#frames } = await this._getNextPacketAndDecodeFrames());\n\n // keep track of how many packets we've read\n this.#packetReadCount++;\n }\n\n // we read through all the available packets and frames, but we still don't have a frame. This can happen\n // when our targetPTS is to close to the end of the video. In this case, we'll try to seek further away from\n // the end of the video and try again. We've set up a MAX_RECURSION to prevent an infinite loop.\n if (!outputFrame) {\n if (MAX_RECURSION < this.#recursiveReadCount) {\n throw Error('No matching frame found');\n }\n const TIME_OFFSET = 0.1; // time offset in seconds\n const PTSOffset = this._timeToPTS(TIME_OFFSET);\n this.#recursiveReadCount++;\n outputFrame = await this._getFrameAtPts(targetPTS, SeekPTSOffset - PTSOffset);\n if (outputFrame) {\n this.#recursiveReadCount = 0;\n }\n }\n VERBOSE && console.log('read', this.packetReadCount, 'packets');\n\n return outputFrame;\n }\n\n /**\n * Get the next packet from the video stream and decode it into frames. Each frame has a presentation time stamp\n * (PTS). If we've reached the end of the stream and no more packets are available, we'll extract the last frames\n * from the decoder and destroy it.\n */\n async _getNextPacketAndDecodeFrames() {\n const packet = await this._getNextVideoStreamPacket();\n VERBOSE && console.log('packet pts:', packet?.pts);\n\n // extract frames from the packet\n let decodedFrames = null;\n if (packet !== null && this.#decoder) {\n decodedFrames = await this.#decoder.decode(packet as Packet);\n VERBOSE && console.log('decodedFrames', decodedFrames.frames.length, decodedFrames.frames.map(f => f.pts));\n }\n // we've reached the end of the stream\n else {\n if (this.#decoder) {\n VERBOSE && console.log('getting the last frames from the decoder');\n // flush the decoder -- this will return the last frames and destroy the decoder\n decodedFrames = await this.#decoder.flush();\n this.#decoder = null;\n }\n else {\n // we don't have a decoder, so we can't decode any more frames\n VERBOSE && console.log('no more frames to decode');\n }\n }\n\n let frames = [];\n if (decodedFrames && decodedFrames.frames.length !== 0) {\n frames = decodedFrames.frames;\n }\n VERBOSE && console.log(`returning ${frames.length} decoded frames`);\n\n return { packet, frames };\n }\n\n async _getNextVideoStreamPacket(): Promise<null | Packet> {\n VERBOSE && console.log('_getNextVideoStreamPacket');\n\n let packet = await this.#demuxer.read();\n while (packet && packet.stream_index !== this.#streamIndex) {\n packet = await this.#demuxer.read();\n if (packet === null) {\n VERBOSE && console.log('no more packets');\n return null;\n }\n }\n VERBOSE && console.log('returning packet', !!packet, 'pts', packet?.pts, 'si', packet?.stream_index);\n return packet as Packet;\n }\n\n _resizeFrameData(frame): Uint8ClampedArray {\n const components = 4; // 4 components: r, g, b and a\n const size = frame.width * frame.height * components;\n const rawData = new Uint8ClampedArray(size); // we should probably reuse this buffer\n const sourceLineSize = frame.linesize as unknown as number;\n // frame.data can contain multiple \"planes\" in other colorspaces, but in rgba, there is just one \"plane\", so\n // our data is in frame.data[0]\n const pixels = frame.data[0] as Uint8Array;\n\n // libav creates larger buffers because it makes their internal code simpler.\n // we have to trim a part at the right of each pixel row.\n for (let i = 0; i < frame.height; i++) {\n const sourceStart = i * sourceLineSize;\n const sourceEnd = sourceStart + frame.width * components;\n const sourceData = pixels.slice(sourceStart, sourceEnd);\n const targetOffset = i * frame.width * components;\n rawData.set(sourceData, targetOffset);\n }\n return rawData;\n }\n\n async dispose() {\n if (this.#decoder) {\n await this.#decoder.flush();\n this.#decoder = null;\n }\n this.#demuxer.forceClose();\n this.#filterer = null;\n this.#filteredFramesPacket = undefined;\n this.#frames = [];\n this.#packet = null;\n this.#previousTargetPTS = null;\n this.#streamIndex = 0;\n }\n}\n"],"names":["BaseExtractor","args","inputFileOrUrl","outputFile","threadCount","endTime","interpolateFps","interpolateMode","targetPts","targetTime","pts","onFrameAvailable","flush","CancelRequestError","DownloadVideoURL","url","__privateAdd","_url","_httpRequest","_filepath","_tmpObj","__privateSet","__privateGet","resolve","reject","source","extension","path","tmp","connectionHandler","https","http","res","contentType","err","writeStream","fs","e","createDecoder","demuxer","streamIndex","beamcoder","createFilter","stream","outputPixelFormat","filterSpec","filterSpecStr","STREAM_TYPE_VIDEO","COLORSPACE_RGBA","MAX_RECURSION","_BeamcoderExtractor","_createDecoder","_demuxer","_decoder","_filterer","_filteredFramesPacket","_frames","_packet","_previousTargetPTS","_threadCount","_streamIndex","_packetReadCount","_recursiveReadCount","extractor","downloadUrl","frame","rawData","createImageData","time","time_base","targetPTS","SeekPTSOffset","RE_SEEK_THRESHOLD","hasFrameWithinThreshold","__privateMethod","createDecoder_fn","filteredFrames","closestFramePTS","outputFrame","closestFrame","f","nextFrame","__privateWrapper","r","TIME_OFFSET","PTSOffset","packet","decodedFrames","frames","size","sourceLineSize","pixels","i","sourceStart","sourceEnd","sourceData","targetOffset","BeamcoderExtractor"],"mappings":"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"}
1
+ {"version":3,"file":"framefusion.cjs","sources":["../src/BaseExtractor.ts","../src/DownloadVideoURL.ts","../src/backends/beamcoder.ts"],"sourcesContent":["import type { ImageData } from 'canvas';\n\nimport type {\n ExtractorArgs,\n Frame,\n Extractor\n} from '../framefusion';\n\nexport class BaseExtractor implements Extractor {\n static async create(args: ExtractorArgs): Promise<Extractor> {\n throw new Error('Not implemented');\n }\n\n async init({\n inputFileOrUrl,\n outputFile,\n threadCount = 8,\n endTime,\n interpolateFps,\n interpolateMode,\n }: ExtractorArgs): Promise<void> {\n throw new Error('Not implemented');\n }\n\n get duration(): number {\n throw new Error('Not implemented');\n }\n\n get width(): number {\n throw new Error('Not implemented');\n }\n\n get height(): number {\n throw new Error('Not implemented');\n }\n\n async seekToPTS(targetPts: number) {\n throw new Error('Not implemented');\n }\n\n async getFrameAtTime(targetTime: number): Promise<Frame> {\n throw new Error('Not implemented');\n }\n\n async getImageDataAtTime(targetTime: number): Promise<ImageData> {\n throw new Error('Not implemented');\n }\n\n async getFrameAtPts(targetPts: number): Promise<Frame> {\n throw new Error('Not implemented');\n }\n\n async seekToTime(targetTime: number) {\n throw new Error('Not implemented');\n }\n\n /**\n * Convert a PTS (based on timebase) to PTS (in seconds)\n */\n ptsToTime(pts: number) {\n throw new Error('Not implemented');\n }\n\n async readFrames({\n onFrameAvailable,\n flush = true,\n }: {\n /**\n * Return true if we need to read more frames.\n */\n onFrameAvailable?: (frame: Frame) => Promise<boolean> | boolean;\n flush?: boolean;\n } = {\n flush: true,\n onFrameAvailable: () => true,\n }) {\n throw new Error('Not implemented');\n }\n\n async dispose() {\n throw new Error('Not implemented');\n }\n}\n","import path from 'path';\nimport https from 'node:https';\nimport type { ClientRequest } from 'http';\nimport http from 'http';\nimport tmp from 'tmp';\nimport fs from 'fs-extra';\n\nclass CancelRequestError extends Error { }\n\n/**\n * Downloads a video file from a given URL as a temporary file. When the object is cleared, the temporary file is\n * deleted.\n */\nexport class DownloadVideoURL {\n #url: string | undefined;\n #httpRequest: ClientRequest | undefined = undefined;\n #filepath: string | undefined = undefined;\n #tmpObj: tmp.FileResult | undefined = undefined;\n\n constructor(url: string) {\n this.#url = url;\n }\n\n /**\n * returns the filepath of the downloaded file. If the file has not been downloaded yet, it will be undefined\n */\n get filepath() {\n return this.#filepath;\n }\n\n /**\n * Downloads the file from the given URL. The file will be downloaded to a temporary file.\n */\n async download() {\n await new Promise<void>((resolve, reject) => {\n const source = this.#url;\n const extension = path.extname(source);\n this.#tmpObj = tmp.fileSync({ postfix: extension });\n try {\n const connectionHandler = source.startsWith('https://') ? https : http;\n this.#httpRequest = connectionHandler.get(source, (res) => {\n const contentType = res.headers['content-type'];\n if (!contentType.includes('video')) {\n const err = new Error(`Source ${source}, returned unsupported content type ${contentType}`);\n reject(err);\n return;\n }\n const writeStream = fs.createWriteStream(this.#tmpObj.name);\n res.pipe(writeStream);\n writeStream.on('finish', () => {\n writeStream.close();\n this.#filepath = this.#tmpObj.name;\n resolve();\n });\n writeStream.on('error', (e) => {\n reject(e);\n });\n });\n this.#httpRequest.on('error', (e) => {\n if (e instanceof CancelRequestError) {\n return;\n }\n reject(e);\n });\n\n this.#httpRequest.on('timeout', () => {\n this.#httpRequest.destroy();\n reject(new Error(`Request to ${source} timed out`));\n });\n }\n catch (e) {\n reject(e);\n }\n });\n }\n\n clear() {\n if (this.#tmpObj) this.#tmpObj.removeCallback();\n if (this.#url) this.#url = undefined;\n if (this.#httpRequest) this.#httpRequest = null;\n if (this.#filepath) this.#filepath = undefined;\n }\n}\n","import type {\n Packet,\n Demuxer,\n Decoder,\n Filterer,\n Frame\n} from '@antoinemopa/beamcoder';\nimport beamcoder from '@antoinemopa/beamcoder';\nimport type { ImageData } from 'canvas';\nimport { createImageData } from 'canvas';\nimport { BaseExtractor } from '../BaseExtractor';\nimport type { Extractor, ExtractorArgs, InterpolateMode } from '../../framefusion';\nimport { DownloadVideoURL } from '../DownloadVideoURL';\n\nconst VERBOSE = false;\n\nconst createDecoder = ({\n demuxer,\n streamIndex,\n threadCount,\n}: {\n demuxer: Demuxer;\n streamIndex: number;\n threadCount: number;\n}): Decoder => {\n return beamcoder.decoder({\n demuxer: demuxer,\n width: demuxer.streams[streamIndex].codecpar.width,\n height: demuxer.streams[streamIndex].codecpar.height,\n stream_index: streamIndex,\n pix_fmt: demuxer.streams[streamIndex].codecpar.format,\n thread_count: threadCount,\n });\n};\n\n/**\n * A filter to convert between color spaces.\n * An example would be YUV to RGB, for mp4 to png conversion.\n */\nconst createFilter = async({\n stream,\n outputPixelFormat,\n interpolateFps,\n interpolateMode = 'fast',\n}: {\n stream: beamcoder.Stream;\n outputPixelFormat: string;\n interpolateFps?: number;\n interpolateMode?: InterpolateMode;\n}): Promise<beamcoder.Filterer> => {\n if (!stream.codecpar.format) {\n return null;\n }\n\n let filterSpec = [`[in0:v]format=${stream.codecpar.format}`];\n\n if (interpolateFps) {\n if (interpolateMode === 'high-quality') {\n filterSpec = [...filterSpec, `minterpolate=fps=${interpolateFps}`];\n }\n else if (interpolateMode === 'fast') {\n filterSpec = [...filterSpec, `fps=${interpolateFps}`];\n }\n else {\n throw new Error(`Unexpected interpolation mode: ${interpolateMode}`);\n }\n }\n\n const filterSpecStr = filterSpec.join(', ') + '[out0:v]';\n\n VERBOSE && console.log(`filterSpec: ${filterSpecStr}`);\n\n return beamcoder.filterer({\n filterType: 'video',\n inputParams: [\n {\n name: 'in0:v',\n width: stream.codecpar.width,\n height: stream.codecpar.height,\n pixelFormat: stream.codecpar.format,\n timeBase: stream.time_base,\n pixelAspect: stream.sample_aspect_ratio,\n },\n ],\n outputParams: [\n {\n name: 'out0:v',\n pixelFormat: outputPixelFormat,\n },\n ],\n filterSpec: filterSpecStr,\n });\n};\n\nconst STREAM_TYPE_VIDEO = 'video';\nconst COLORSPACE_RGBA = 'rgba';\nconst MAX_RECURSION = 5;\n\n/**\n * A simple extractor that uses beamcoder to extract frames from a video file.\n */\nexport class BeamcoderExtractor extends BaseExtractor implements Extractor {\n /**\n * The demuxer reads the file and outputs packet streams\n */\n #demuxer: Demuxer = null;\n\n /**\n * The decoder reads packets and can output raw frame data\n */\n #decoder: Decoder = null;\n\n /**\n * Packets can be filtered to change colorspace, fps and add various effects. If there are no colorspace changes or\n * filters, filter might not be necessary.\n */\n #filterer: Filterer = null;\n\n /**\n * This is where we store filtered frames from each previously processed packet.\n * We keep these in chronological order. We hang on to them for two reasons:\n * 1. so we can return them if we get a request for the same time again\n * 2. so we can return frames close the end of the stream. When such a frame is requested we have to flush (destroy)\n * the encoder to get the last few frames. This avoids having to re-create an encoder.\n */\n #filteredFramesPacket: undefined[] | Array<Array<Frame>> = [];\n\n /**\n * This contains the last raw frames we read from the demuxer. We use it as a starting point for each new query. We\n * do this ensure we don't skip any frames.\n */\n #frames = [];\n\n /**\n * This contains the last packet we read from the demuxer. We use it as a starting point for each new query. We do\n * this ensure we don't skip any frames.\n */\n #packet: null | Packet = null;\n\n /**\n * The last target presentation timestamp (PTS) we requested. If we never requested a time(stamp) then this\n * value is null\n */\n #previousTargetPTS: null | number = null;\n\n /**\n * The number of threads to use for decoding\n */\n #threadCount = 8;\n\n /**\n * The index of the video stream in the demuxer\n */\n #streamIndex = 0;\n\n /**\n * The number of packets we've read from the demuxer to complete the frame query\n * @private\n */\n #packetReadCount = 0;\n\n /**\n * The number of times we've recursively read packets from the demuxer to complete the frame query\n * @private\n */\n #recursiveReadCount = 0;\n\n /**\n * Encoder/Decoder construction is async, so it can't be put in a regular constructor.\n * Use and await this method to generate an extractor.\n */\n static async create(args: ExtractorArgs): Promise<BeamcoderExtractor> {\n const extractor = new BeamcoderExtractor();\n await extractor.init(args);\n return extractor;\n }\n\n async init({\n inputFileOrUrl,\n threadCount = 8,\n }: ExtractorArgs): Promise<void> {\n this.#threadCount = threadCount;\n if (inputFileOrUrl.startsWith('http')) {\n VERBOSE && console.log('downloading url', inputFileOrUrl);\n const downloadUrl = new DownloadVideoURL(inputFileOrUrl);\n await downloadUrl.download();\n inputFileOrUrl = downloadUrl.filepath;\n VERBOSE && console.log('finished downloading');\n }\n this.#demuxer = await beamcoder.demuxer('file:' + inputFileOrUrl);\n this.#streamIndex = this.#demuxer.streams.findIndex(stream => stream.codecpar.codec_type === STREAM_TYPE_VIDEO);\n if (this.#streamIndex === -1) {\n throw new Error(`File has no ${STREAM_TYPE_VIDEO} stream!`);\n }\n this.#filterer = await createFilter({\n stream: this.#demuxer.streams[this.#streamIndex],\n outputPixelFormat: COLORSPACE_RGBA,\n });\n }\n\n async #createDecoder() {\n // It's possible that we need to create decoder multiple times during the lifecycle of this extractor so we\n // need to make sure we destroy the old one first if it exists\n if (this.#decoder) {\n await this.#decoder.flush();\n this.#decoder = null;\n }\n this.#decoder = createDecoder({\n demuxer: this.#demuxer as Demuxer,\n streamIndex: this.#streamIndex,\n threadCount: this.#threadCount,\n });\n }\n\n /**\n * This is the duration of the first video stream in the file expressed in seconds.\n */\n get duration(): number {\n return this.ptsToTime(this.#demuxer.streams[this.#streamIndex].duration);\n }\n\n /**\n * Width in pixels\n */\n get width(): number {\n return this.#demuxer.streams[this.#streamIndex].codecpar.width;\n }\n\n /**\n * Height in pixels\n */\n get height(): number {\n return this.#demuxer.streams[this.#streamIndex].codecpar.height;\n }\n\n /**\n * Get the beamcoder Frame for a given time in seconds\n * @param targetTime\n */\n async getFrameAtTime(targetTime: number): Promise<beamcoder.Frame> {\n VERBOSE && console.log(`getFrameAtTime time(s)=${targetTime}`);\n const targetPts = Math.round(this._timeToPTS(targetTime));\n return this._getFrameAtPts(targetPts);\n }\n\n /**\n * Get imageData for a given time in seconds\n * @param targetTime\n */\n async getImageDataAtTime(targetTime: number): Promise<ImageData> {\n const targetPts = Math.round(this._timeToPTS(targetTime));\n VERBOSE && console.log('targetTime', targetTime, '-> targetPts', targetPts);\n const frame = await this._getFrameAtPts(targetPts);\n if (!frame) {\n VERBOSE && console.log('no frame found');\n return null;\n }\n const rawData = this._resizeFrameData(frame);\n const image = createImageData(\n rawData,\n frame.width,\n frame.height\n ) as ImageData;\n return image;\n }\n\n /**\n * Get the presentation timestamp (PTS) for a given time in seconds\n */\n _timeToPTS(time: number) {\n const time_base = this.#demuxer.streams[this.#streamIndex].time_base;\n return time * time_base[1] / time_base[0];\n }\n\n /**\n * Get the time in seconds from a given presentation timestamp (PTS)\n */\n ptsToTime(pts: number) {\n const time_base = this.#demuxer.streams[this.#streamIndex].time_base;\n return pts * time_base[0] / time_base[1];\n }\n\n get packetReadCount() {\n return this.#packetReadCount;\n }\n\n /**\n * Get the frame at the given presentation timestamp (PTS)\n * @param targetPTS - the target presentation timestamp (PTS) we want to retrieve\n * @param SeekPTSOffset - the offset to use when seeking to the targetPTS. This is used when we have trouble finding\n * the targetPTS. We use it to further move away from the requested PTS to find a frame. The allows use to read\n * additional packets and find a frame that is closer to the targetPTS.\n */\n async _getFrameAtPts(targetPTS: number, SeekPTSOffset = 0): Promise<beamcoder.Frame> {\n VERBOSE && console.log('_getFrameAtPts', targetPTS, SeekPTSOffset, '-> duration', this.duration);\n this.#packetReadCount = 0;\n\n // seek and create a decoder when retrieving a frame for the first time or when seeking backwards\n // we have to create a new decoder when seeking backwards as the decoder can only process frames in\n // chronological order.\n // RE_SEEK_DELTA: sometimes, we are looking for a frame so far ahead that it's better to drop everything and seek.\n // Example: when we got a frame a 0 and request a frame at t = 30s just after, we don't want to start reading all packets\n // until 30s.\n const RE_SEEK_THRESHOLD = 3; // 3 seconds - typically we have keyframes at shorter intervals\n const hasFrameWithinThreshold = this.#filteredFramesPacket.flat().some(frame => {\n return this.ptsToTime(Math.abs(targetPTS - (frame as Frame).pts)) < RE_SEEK_THRESHOLD;\n });\n VERBOSE && console.log('hasPreviousTargetPTS', this.#previousTargetPTS === null, 'targetPTS is smaller', this.#previousTargetPTS > targetPTS, 'has frame within threshold', hasFrameWithinThreshold);\n if (this.#previousTargetPTS === null || this.#previousTargetPTS > targetPTS || !hasFrameWithinThreshold) {\n VERBOSE && console.log(`Seeking to ${targetPTS - SeekPTSOffset}`);\n\n await this.#demuxer.seek({\n stream_index: 0, // even though we specify the stream index, it still seeks all streams\n timestamp: targetPTS + SeekPTSOffset,\n any: false,\n });\n await this.#createDecoder();\n this.#packet = null;\n this.#frames = [];\n this.#previousTargetPTS = targetPTS;\n this.#filteredFramesPacket = [];\n }\n\n let filteredFrames = null;\n let closestFramePTS = -1;\n let outputFrame = null;\n\n // If we have previously filtered frames, get the frame closest to our targetPTS\n if (this.#filteredFramesPacket.length > 0) {\n const closestFrame = this.#filteredFramesPacket\n .flat()\n .find(f => (f as Frame).pts <= targetPTS) as Frame;\n\n if (closestFrame) {\n const nextFrame = this.#filteredFramesPacket\n .flat()\n .find(f => (f as Frame).pts > closestFrame.pts) as Frame;\n\n VERBOSE && console.log('returning previously filtered frame with pts', (closestFrame as Frame).pts);\n closestFramePTS = (closestFrame as Frame).pts;\n outputFrame = closestFrame;\n\n if ((nextFrame && nextFrame.pts > targetPTS) || (closestFramePTS === targetPTS)) {\n // We have a next frame, so we know the frame being displayed at targetPTS is the previous one,\n // which corresponds to outputFrame.\n this.#previousTargetPTS = targetPTS;\n return outputFrame;\n }\n }\n }\n\n // This is the first time we're decoding frames. Get the first packet and decode it.\n if (!this.#packet && this.#frames.length === 0) {\n ({ packet: this.#packet, frames: this.#frames } = await this._getNextPacketAndDecodeFrames());\n this.#packetReadCount++;\n }\n // Read packets until we have a frame which is closest to targetPTS\n while ((this.#packet || this.#frames.length !== 0) && closestFramePTS < targetPTS) {\n VERBOSE && console.log('packet si:', this.#packet?.stream_index, 'pts:', this.#packet?.pts, 'frames:', this.#frames?.length);\n VERBOSE && console.log('frames', this.#frames?.length, 'frames.pts:', this.#frames?.map(f => f.pts), '-> target.pts:', targetPTS);\n\n // packet contains frames\n if (this.#frames.length !== 0) {\n // filter the frames\n const filteredResult = await this.#filterer.filter([{ name: 'in0:v', frames: this.#frames }]);\n filteredFrames = filteredResult.flatMap(r => r.frames);\n VERBOSE && console.log('filteredFrames', filteredFrames.length, 'filteredFrames.pts:', filteredFrames.map(f => f.pts), '-> target.pts:', targetPTS);\n\n // get the closest frame to our target presentation timestamp (PTS)\n // Beamcoder returns decoded packet frames as follows: [1000, 2000, 3000, 4000]\n // If we're looking for a frame at 2500, we want to return the frame at 2000\n const closestFrame = filteredFrames.reverse().find(f => f.pts <= targetPTS);\n\n // The packet contains frames, but all of them have PTS larger than our a targetPTS (we looked too far)\n if (!closestFrame) {\n return outputFrame;\n }\n\n // store the filtered packet frames for later reuse\n this.#filteredFramesPacket.unshift(filteredFrames);\n if (this.#filteredFramesPacket.length > 2) {\n this.#filteredFramesPacket.pop();\n }\n\n closestFramePTS = closestFrame?.pts;\n VERBOSE && console.log('closestFramePTS', closestFramePTS, 'targetPTS', targetPTS);\n if (!outputFrame || closestFramePTS <= targetPTS) {\n VERBOSE && console.log('assigning outputFrame', closestFrame?.pts);\n this.#previousTargetPTS = targetPTS;\n outputFrame = closestFrame;\n }\n else {\n // break out of the loop if we've found the closest frame (and ensure we don't move to the next\n // packet by calling _getNextPacketAndDecodeFrames again) as this risks us getting a frame that is\n // after our targetPTS\n VERBOSE && console.log('breaking');\n break;\n }\n }\n // get the next packet and frames\n ({ packet: this.#packet, frames: this.#frames } = await this._getNextPacketAndDecodeFrames());\n\n // keep track of how many packets we've read\n this.#packetReadCount++;\n }\n\n // we read through all the available packets and frames, but we still don't have a frame. This can happen\n // when our targetPTS is to close to the end of the video. In this case, we'll try to seek further away from\n // the end of the video and try again. We've set up a MAX_RECURSION to prevent an infinite loop.\n if (!outputFrame) {\n if (MAX_RECURSION < this.#recursiveReadCount) {\n throw Error('No matching frame found');\n }\n const TIME_OFFSET = 0.1; // time offset in seconds\n const PTSOffset = this._timeToPTS(TIME_OFFSET);\n this.#recursiveReadCount++;\n outputFrame = await this._getFrameAtPts(targetPTS, SeekPTSOffset - PTSOffset);\n if (outputFrame) {\n this.#recursiveReadCount = 0;\n }\n }\n VERBOSE && console.log('read', this.packetReadCount, 'packets');\n\n return outputFrame;\n }\n\n /**\n * Get the next packet from the video stream and decode it into frames. Each frame has a presentation time stamp\n * (PTS). If we've reached the end of the stream and no more packets are available, we'll extract the last frames\n * from the decoder and destroy it.\n */\n async _getNextPacketAndDecodeFrames() {\n const packet = await this._getNextVideoStreamPacket();\n VERBOSE && console.log('packet pts:', packet?.pts);\n\n // extract frames from the packet\n let decodedFrames = null;\n if (packet !== null && this.#decoder) {\n decodedFrames = await this.#decoder.decode(packet as Packet);\n VERBOSE && console.log('decodedFrames', decodedFrames.frames.length, decodedFrames.frames.map(f => f.pts));\n }\n // we've reached the end of the stream\n else {\n if (this.#decoder) {\n VERBOSE && console.log('getting the last frames from the decoder');\n // flush the decoder -- this will return the last frames and destroy the decoder\n decodedFrames = await this.#decoder.flush();\n this.#decoder = null;\n }\n else {\n // we don't have a decoder, so we can't decode any more frames\n VERBOSE && console.log('no more frames to decode');\n }\n }\n\n let frames = [];\n if (decodedFrames && decodedFrames.frames.length !== 0) {\n frames = decodedFrames.frames;\n }\n VERBOSE && console.log(`returning ${frames.length} decoded frames`);\n\n return { packet, frames };\n }\n\n async _getNextVideoStreamPacket(): Promise<null | Packet> {\n VERBOSE && console.log('_getNextVideoStreamPacket');\n\n let packet = await this.#demuxer.read();\n while (packet && packet.stream_index !== this.#streamIndex) {\n packet = await this.#demuxer.read();\n if (packet === null) {\n VERBOSE && console.log('no more packets');\n return null;\n }\n }\n VERBOSE && console.log('returning packet', !!packet, 'pts', packet?.pts, 'si', packet?.stream_index);\n return packet as Packet;\n }\n\n _resizeFrameData(frame): Uint8ClampedArray {\n const components = 4; // 4 components: r, g, b and a\n const size = frame.width * frame.height * components;\n const rawData = new Uint8ClampedArray(size); // we should probably reuse this buffer\n const sourceLineSize = frame.linesize as unknown as number;\n // frame.data can contain multiple \"planes\" in other colorspaces, but in rgba, there is just one \"plane\", so\n // our data is in frame.data[0]\n const pixels = frame.data[0] as Uint8Array;\n\n // libav creates larger buffers because it makes their internal code simpler.\n // we have to trim a part at the right of each pixel row.\n for (let i = 0; i < frame.height; i++) {\n const sourceStart = i * sourceLineSize;\n const sourceEnd = sourceStart + frame.width * components;\n const sourceData = pixels.slice(sourceStart, sourceEnd);\n const targetOffset = i * frame.width * components;\n rawData.set(sourceData, targetOffset);\n }\n return rawData;\n }\n\n async dispose() {\n if (this.#decoder) {\n await this.#decoder.flush();\n this.#decoder = null;\n }\n this.#demuxer.forceClose();\n this.#filterer = null;\n this.#filteredFramesPacket = undefined;\n this.#frames = [];\n this.#packet = null;\n this.#previousTargetPTS = null;\n this.#streamIndex = 0;\n }\n}\n"],"names":["BaseExtractor","args","inputFileOrUrl","outputFile","threadCount","endTime","interpolateFps","interpolateMode","targetPts","targetTime","pts","onFrameAvailable","flush","CancelRequestError","DownloadVideoURL","url","__privateAdd","_url","_httpRequest","_filepath","_tmpObj","__privateSet","__privateGet","resolve","reject","source","extension","path","tmp","connectionHandler","https","http","res","contentType","err","writeStream","fs","e","createDecoder","demuxer","streamIndex","beamcoder","createFilter","stream","outputPixelFormat","filterSpec","filterSpecStr","STREAM_TYPE_VIDEO","COLORSPACE_RGBA","MAX_RECURSION","_BeamcoderExtractor","_createDecoder","_demuxer","_decoder","_filterer","_filteredFramesPacket","_frames","_packet","_previousTargetPTS","_threadCount","_streamIndex","_packetReadCount","_recursiveReadCount","extractor","downloadUrl","frame","rawData","createImageData","time","time_base","targetPTS","SeekPTSOffset","RE_SEEK_THRESHOLD","hasFrameWithinThreshold","__privateMethod","createDecoder_fn","filteredFrames","closestFramePTS","outputFrame","closestFrame","f","nextFrame","__privateWrapper","r","TIME_OFFSET","PTSOffset","packet","decodedFrames","frames","size","sourceLineSize","pixels","i","sourceStart","sourceEnd","sourceData","targetOffset","BeamcoderExtractor"],"mappings":"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"}
@@ -6,7 +6,7 @@ var e = (r, s, t) => (C(r, s, "read from private field"), t ? t.call(r) : s.get(
6
6
  if (s.has(r))
7
7
  throw TypeError("Cannot add the same private member more than once");
8
8
  s instanceof WeakSet ? s.add(r) : s.set(r, t);
9
- }, a = (r, s, t, i) => (C(r, s, "write to private field"), i ? i.call(r, t) : s.set(r, t), t), F = (r, s, t, i) => ({
9
+ }, a = (r, s, t, i) => (C(r, s, "write to private field"), i ? i.call(r, t) : s.set(r, t), t), T = (r, s, t, i) => ({
10
10
  set _(o) {
11
11
  a(r, s, o, t);
12
12
  },
@@ -15,9 +15,9 @@ var e = (r, s, t) => (C(r, s, "read from private field"), t ? t.call(r) : s.get(
15
15
  }
16
16
  }), I = (r, s, t) => (C(r, s, "access private method"), t);
17
17
  import S from "@antoinemopa/beamcoder";
18
- import { createImageData as V } from "canvas";
19
- import W from "path";
20
- import q from "node:https";
18
+ import { createImageData as L } from "canvas";
19
+ import V from "path";
20
+ import W from "node:https";
21
21
  import H from "http";
22
22
  import U from "tmp";
23
23
  import B from "fs-extra";
@@ -30,8 +30,8 @@ class G {
30
30
  outputFile: t,
31
31
  threadCount: i = 8,
32
32
  endTime: o,
33
- interpolateFps: f,
34
- interpolateMode: u
33
+ interpolateFps: u,
34
+ interpolateMode: f
35
35
  }) {
36
36
  throw new Error("Not implemented");
37
37
  }
@@ -80,14 +80,14 @@ class G {
80
80
  }
81
81
  class K extends Error {
82
82
  }
83
- var v, A, N, T;
83
+ var A, E, N, x;
84
84
  class X {
85
85
  constructor(s) {
86
- h(this, v, void 0);
87
86
  h(this, A, void 0);
87
+ h(this, E, void 0);
88
88
  h(this, N, void 0);
89
- h(this, T, void 0);
90
- a(this, v, s);
89
+ h(this, x, void 0);
90
+ a(this, A, s);
91
91
  }
92
92
  /**
93
93
  * returns the filepath of the downloaded file. If the file has not been downloaded yet, it will be undefined
@@ -100,36 +100,38 @@ class X {
100
100
  */
101
101
  async download() {
102
102
  await new Promise((s, t) => {
103
- const i = e(this, v), o = W.extname(i);
104
- a(this, T, U.fileSync({ postfix: o }));
103
+ const i = e(this, A), o = V.extname(i);
104
+ a(this, x, U.fileSync({ postfix: o }));
105
105
  try {
106
- const f = i.startsWith("https://") ? q : H;
107
- a(this, A, f.get(i, (u) => {
108
- const _ = u.headers["content-type"];
106
+ const u = i.startsWith("https://") ? W : H;
107
+ a(this, E, u.get(i, (f) => {
108
+ const _ = f.headers["content-type"];
109
109
  if (!_.includes("video")) {
110
110
  const c = new Error(`Source ${i}, returned unsupported content type ${_}`);
111
111
  t(c);
112
112
  return;
113
113
  }
114
- const n = B.createWriteStream(e(this, T).name);
115
- u.pipe(n), n.on("finish", () => {
116
- n.close(), a(this, N, e(this, T).name), s();
114
+ const n = B.createWriteStream(e(this, x).name);
115
+ f.pipe(n), n.on("finish", () => {
116
+ n.close(), a(this, N, e(this, x).name), s();
117
117
  }), n.on("error", (c) => {
118
118
  t(c);
119
119
  });
120
- })), e(this, A).on("error", (u) => {
121
- u instanceof K || t(u);
120
+ })), e(this, E).on("error", (f) => {
121
+ f instanceof K || t(f);
122
+ }), e(this, E).on("timeout", () => {
123
+ e(this, E).destroy(), t(new Error(`Request to ${i} timed out`));
122
124
  });
123
- } catch (f) {
124
- t(f);
125
+ } catch (u) {
126
+ t(u);
125
127
  }
126
128
  });
127
129
  }
128
130
  clear() {
129
- e(this, T) && e(this, T).removeCallback(), e(this, v) && a(this, v, void 0), e(this, A) && a(this, A, null), e(this, N) && a(this, N, void 0);
131
+ e(this, x) && e(this, x).removeCallback(), e(this, A) && a(this, A, void 0), e(this, E) && a(this, E, null), e(this, N) && a(this, N, void 0);
130
132
  }
131
133
  }
132
- v = new WeakMap(), A = new WeakMap(), N = new WeakMap(), T = new WeakMap();
134
+ A = new WeakMap(), E = new WeakMap(), N = new WeakMap(), x = new WeakMap();
133
135
  const Y = ({
134
136
  demuxer: r,
135
137
  streamIndex: s,
@@ -157,7 +159,7 @@ const Y = ({
157
159
  o = [...o, `fps=${t}`];
158
160
  else
159
161
  throw new Error(`Unexpected interpolation mode: ${i}`);
160
- const f = o.join(", ") + "[out0:v]";
162
+ const u = o.join(", ") + "[out0:v]";
161
163
  return S.filterer({
162
164
  filterType: "video",
163
165
  inputParams: [
@@ -176,10 +178,10 @@ const Y = ({
176
178
  pixelFormat: s
177
179
  }
178
180
  ],
179
- filterSpec: f
181
+ filterSpec: u
180
182
  });
181
183
  }, M = "video", Q = "rgba", Z = 5;
182
- var l, m, D, w, g, E, y, R, p, k, P, b, z;
184
+ var m, l, D, w, g, y, F, R, p, k, P, b, z;
183
185
  const $ = class extends G {
184
186
  constructor() {
185
187
  super(...arguments);
@@ -187,11 +189,11 @@ const $ = class extends G {
187
189
  /**
188
190
  * The demuxer reads the file and outputs packet streams
189
191
  */
190
- h(this, l, null);
192
+ h(this, m, null);
191
193
  /**
192
194
  * The decoder reads packets and can output raw frame data
193
195
  */
194
- h(this, m, null);
196
+ h(this, l, null);
195
197
  /**
196
198
  * Packets can be filtered to change colorspace, fps and add various effects. If there are no colorspace changes or
197
199
  * filters, filter might not be necessary.
@@ -214,12 +216,12 @@ const $ = class extends G {
214
216
  * This contains the last packet we read from the demuxer. We use it as a starting point for each new query. We do
215
217
  * this ensure we don't skip any frames.
216
218
  */
217
- h(this, E, null);
219
+ h(this, y, null);
218
220
  /**
219
221
  * The last target presentation timestamp (PTS) we requested. If we never requested a time(stamp) then this
220
222
  * value is null
221
223
  */
222
- h(this, y, null);
224
+ h(this, F, null);
223
225
  /**
224
226
  * The number of threads to use for decoding
225
227
  */
@@ -255,10 +257,10 @@ const $ = class extends G {
255
257
  const o = new X(t);
256
258
  await o.download(), t = o.filepath;
257
259
  }
258
- if (a(this, l, await S.demuxer("file:" + t)), a(this, p, e(this, l).streams.findIndex((o) => o.codecpar.codec_type === M)), e(this, p) === -1)
260
+ if (a(this, m, await S.demuxer("file:" + t)), a(this, p, e(this, m).streams.findIndex((o) => o.codecpar.codec_type === M)), e(this, p) === -1)
259
261
  throw new Error(`File has no ${M} stream!`);
260
262
  a(this, D, await J({
261
- stream: e(this, l).streams[e(this, p)],
263
+ stream: e(this, m).streams[e(this, p)],
262
264
  outputPixelFormat: Q
263
265
  }));
264
266
  }
@@ -266,26 +268,26 @@ const $ = class extends G {
266
268
  * This is the duration of the first video stream in the file expressed in seconds.
267
269
  */
268
270
  get duration() {
269
- return this.ptsToTime(e(this, l).streams[e(this, p)].duration);
271
+ return this.ptsToTime(e(this, m).streams[e(this, p)].duration);
270
272
  }
271
273
  /**
272
274
  * Width in pixels
273
275
  */
274
276
  get width() {
275
- return e(this, l).streams[e(this, p)].codecpar.width;
277
+ return e(this, m).streams[e(this, p)].codecpar.width;
276
278
  }
277
279
  /**
278
280
  * Height in pixels
279
281
  */
280
282
  get height() {
281
- return e(this, l).streams[e(this, p)].codecpar.height;
283
+ return e(this, m).streams[e(this, p)].codecpar.height;
282
284
  }
283
285
  /**
284
286
  * Get the beamcoder Frame for a given time in seconds
285
287
  * @param targetTime
286
288
  */
287
289
  async getFrameAtTime(t) {
288
- const i = Math.floor(this._timeToPTS(t));
290
+ const i = Math.round(this._timeToPTS(t));
289
291
  return this._getFrameAtPts(i);
290
292
  }
291
293
  /**
@@ -293,12 +295,12 @@ const $ = class extends G {
293
295
  * @param targetTime
294
296
  */
295
297
  async getImageDataAtTime(t) {
296
- const i = Math.floor(this._timeToPTS(t)), o = await this._getFrameAtPts(i);
298
+ const i = Math.round(this._timeToPTS(t)), o = await this._getFrameAtPts(i);
297
299
  if (!o)
298
300
  return null;
299
- const f = this._resizeFrameData(o);
300
- return V(
301
- f,
301
+ const u = this._resizeFrameData(o);
302
+ return L(
303
+ u,
302
304
  o.width,
303
305
  o.height
304
306
  );
@@ -307,14 +309,14 @@ const $ = class extends G {
307
309
  * Get the presentation timestamp (PTS) for a given time in seconds
308
310
  */
309
311
  _timeToPTS(t) {
310
- const i = e(this, l).streams[e(this, p)].time_base;
312
+ const i = e(this, m).streams[e(this, p)].time_base;
311
313
  return t * i[1] / i[0];
312
314
  }
313
315
  /**
314
316
  * Get the time in seconds from a given presentation timestamp (PTS)
315
317
  */
316
318
  ptsToTime(t) {
317
- const i = e(this, l).streams[e(this, p)].time_base;
319
+ const i = e(this, m).streams[e(this, p)].time_base;
318
320
  return t * i[0] / i[1];
319
321
  }
320
322
  get packetReadCount() {
@@ -329,40 +331,40 @@ const $ = class extends G {
329
331
  */
330
332
  async _getFrameAtPts(t, i = 0) {
331
333
  a(this, k, 0);
332
- const o = 3, f = e(this, w).flat().some((c) => this.ptsToTime(Math.abs(t - c.pts)) < o);
333
- (e(this, y) === null || e(this, y) > t || !f) && (await e(this, l).seek({
334
+ const o = 3, u = e(this, w).flat().some((c) => this.ptsToTime(Math.abs(t - c.pts)) < o);
335
+ (e(this, F) === null || e(this, F) > t || !u) && (await e(this, m).seek({
334
336
  stream_index: 0,
335
337
  // even though we specify the stream index, it still seeks all streams
336
338
  timestamp: t + i,
337
339
  any: !1
338
- }), await I(this, b, z).call(this), a(this, E, null), a(this, g, []), a(this, y, t), a(this, w, []));
339
- let u = null, _ = -1, n = null;
340
+ }), await I(this, b, z).call(this), a(this, y, null), a(this, g, []), a(this, F, t), a(this, w, []));
341
+ let f = null, _ = -1, n = null;
340
342
  if (e(this, w).length > 0) {
341
343
  const c = e(this, w).flat().find((d) => d.pts <= t);
342
344
  if (c) {
343
- const d = e(this, w).flat().find((x) => x.pts > c.pts);
345
+ const d = e(this, w).flat().find((v) => v.pts > c.pts);
344
346
  if (_ = c.pts, n = c, d && d.pts > t || _ === t)
345
- return a(this, y, t), n;
347
+ return a(this, F, t), n;
346
348
  }
347
349
  }
348
- for (!e(this, E) && e(this, g).length === 0 && ({ packet: F(this, E)._, frames: F(this, g)._ } = await this._getNextPacketAndDecodeFrames(), F(this, k)._++); (e(this, E) || e(this, g).length !== 0) && _ < t; ) {
350
+ for (!e(this, y) && e(this, g).length === 0 && ({ packet: T(this, y)._, frames: T(this, g)._ } = await this._getNextPacketAndDecodeFrames(), T(this, k)._++); (e(this, y) || e(this, g).length !== 0) && _ < t; ) {
349
351
  if (e(this, g).length !== 0) {
350
- u = (await e(this, D).filter([{ name: "in0:v", frames: e(this, g) }])).flatMap((x) => x.frames);
351
- const d = u.reverse().find((x) => x.pts <= t);
352
+ f = (await e(this, D).filter([{ name: "in0:v", frames: e(this, g) }])).flatMap((v) => v.frames);
353
+ const d = f.reverse().find((v) => v.pts <= t);
352
354
  if (!d)
353
355
  return n;
354
- if (e(this, w).unshift(u), e(this, w).length > 2 && e(this, w).pop(), _ = d == null ? void 0 : d.pts, !n || _ <= t)
355
- a(this, y, t), n = d;
356
+ if (e(this, w).unshift(f), e(this, w).length > 2 && e(this, w).pop(), _ = d == null ? void 0 : d.pts, !n || _ <= t)
357
+ a(this, F, t), n = d;
356
358
  else
357
359
  break;
358
360
  }
359
- ({ packet: F(this, E)._, frames: F(this, g)._ } = await this._getNextPacketAndDecodeFrames()), F(this, k)._++;
361
+ ({ packet: T(this, y)._, frames: T(this, g)._ } = await this._getNextPacketAndDecodeFrames()), T(this, k)._++;
360
362
  }
361
363
  if (!n) {
362
364
  if (Z < e(this, P))
363
365
  throw Error("No matching frame found");
364
366
  const c = 0.1, d = this._timeToPTS(c);
365
- F(this, P)._++, n = await this._getFrameAtPts(t, i - d), n && a(this, P, 0);
367
+ T(this, P)._++, n = await this._getFrameAtPts(t, i - d), n && a(this, P, 0);
366
368
  }
367
369
  return n;
368
370
  }
@@ -374,33 +376,33 @@ const $ = class extends G {
374
376
  async _getNextPacketAndDecodeFrames() {
375
377
  const t = await this._getNextVideoStreamPacket();
376
378
  let i = null;
377
- t !== null && e(this, m) ? i = await e(this, m).decode(t) : e(this, m) && (i = await e(this, m).flush(), a(this, m, null));
379
+ t !== null && e(this, l) ? i = await e(this, l).decode(t) : e(this, l) && (i = await e(this, l).flush(), a(this, l, null));
378
380
  let o = [];
379
381
  return i && i.frames.length !== 0 && (o = i.frames), { packet: t, frames: o };
380
382
  }
381
383
  async _getNextVideoStreamPacket() {
382
- let t = await e(this, l).read();
384
+ let t = await e(this, m).read();
383
385
  for (; t && t.stream_index !== e(this, p); )
384
- if (t = await e(this, l).read(), t === null)
386
+ if (t = await e(this, m).read(), t === null)
385
387
  return null;
386
388
  return t;
387
389
  }
388
390
  _resizeFrameData(t) {
389
- const o = t.width * t.height * 4, f = new Uint8ClampedArray(o), u = t.linesize, _ = t.data[0];
391
+ const o = t.width * t.height * 4, u = new Uint8ClampedArray(o), f = t.linesize, _ = t.data[0];
390
392
  for (let n = 0; n < t.height; n++) {
391
- const c = n * u, d = c + t.width * 4, x = _.slice(c, d), L = n * t.width * 4;
392
- f.set(x, L);
393
+ const c = n * f, d = c + t.width * 4, v = _.slice(c, d), q = n * t.width * 4;
394
+ u.set(v, q);
393
395
  }
394
- return f;
396
+ return u;
395
397
  }
396
398
  async dispose() {
397
- e(this, m) && (await e(this, m).flush(), a(this, m, null)), e(this, l).forceClose(), a(this, D, null), a(this, w, void 0), a(this, g, []), a(this, E, null), a(this, y, null), a(this, p, 0);
399
+ e(this, l) && (await e(this, l).flush(), a(this, l, null)), e(this, m).forceClose(), a(this, D, null), a(this, w, void 0), a(this, g, []), a(this, y, null), a(this, F, null), a(this, p, 0);
398
400
  }
399
401
  };
400
402
  let O = $;
401
- l = new WeakMap(), m = new WeakMap(), D = new WeakMap(), w = new WeakMap(), g = new WeakMap(), E = new WeakMap(), y = new WeakMap(), R = new WeakMap(), p = new WeakMap(), k = new WeakMap(), P = new WeakMap(), b = new WeakSet(), z = async function() {
402
- e(this, m) && (await e(this, m).flush(), a(this, m, null)), a(this, m, Y({
403
- demuxer: e(this, l),
403
+ m = new WeakMap(), l = new WeakMap(), D = new WeakMap(), w = new WeakMap(), g = new WeakMap(), y = new WeakMap(), F = new WeakMap(), R = new WeakMap(), p = new WeakMap(), k = new WeakMap(), P = new WeakMap(), b = new WeakSet(), z = async function() {
404
+ e(this, l) && (await e(this, l).flush(), a(this, l, null)), a(this, l, Y({
405
+ demuxer: e(this, m),
404
406
  streamIndex: e(this, p),
405
407
  threadCount: e(this, R)
406
408
  }));
@@ -1 +1 @@
1
- {"version":3,"file":"framefusion.es.js","sources":["../src/BaseExtractor.ts","../src/DownloadVideoURL.ts","../src/backends/beamcoder.ts"],"sourcesContent":["import type { ImageData } from 'canvas';\n\nimport type {\n ExtractorArgs,\n Frame,\n Extractor\n} from '../framefusion';\n\nexport class BaseExtractor implements Extractor {\n static async create(args: ExtractorArgs): Promise<Extractor> {\n throw new Error('Not implemented');\n }\n\n async init({\n inputFileOrUrl,\n outputFile,\n threadCount = 8,\n endTime,\n interpolateFps,\n interpolateMode,\n }: ExtractorArgs): Promise<void> {\n throw new Error('Not implemented');\n }\n\n get duration(): number {\n throw new Error('Not implemented');\n }\n\n get width(): number {\n throw new Error('Not implemented');\n }\n\n get height(): number {\n throw new Error('Not implemented');\n }\n\n async seekToPTS(targetPts: number) {\n throw new Error('Not implemented');\n }\n\n async getFrameAtTime(targetTime: number): Promise<Frame> {\n throw new Error('Not implemented');\n }\n\n async getImageDataAtTime(targetTime: number): Promise<ImageData> {\n throw new Error('Not implemented');\n }\n\n async getFrameAtPts(targetPts: number): Promise<Frame> {\n throw new Error('Not implemented');\n }\n\n async seekToTime(targetTime: number) {\n throw new Error('Not implemented');\n }\n\n /**\n * Convert a PTS (based on timebase) to PTS (in seconds)\n */\n ptsToTime(pts: number) {\n throw new Error('Not implemented');\n }\n\n async readFrames({\n onFrameAvailable,\n flush = true,\n }: {\n /**\n * Return true if we need to read more frames.\n */\n onFrameAvailable?: (frame: Frame) => Promise<boolean> | boolean;\n flush?: boolean;\n } = {\n flush: true,\n onFrameAvailable: () => true,\n }) {\n throw new Error('Not implemented');\n }\n\n async dispose() {\n throw new Error('Not implemented');\n }\n}\n","import path from 'path';\nimport https from 'node:https';\nimport type { ClientRequest } from 'http';\nimport http from 'http';\nimport tmp from 'tmp';\nimport fs from 'fs-extra';\n\nclass CancelRequestError extends Error { }\n\n/**\n * Downloads a video file from a given URL as a temporary file. When the object is cleared, the temporary file is\n * deleted.\n */\nexport class DownloadVideoURL {\n #url: string | undefined;\n #httpRequest: ClientRequest | undefined = undefined;\n #filepath: string | undefined = undefined;\n #tmpObj: tmp.FileResult | undefined = undefined;\n\n constructor(url: string) {\n this.#url = url;\n }\n\n /**\n * returns the filepath of the downloaded file. If the file has not been downloaded yet, it will be undefined\n */\n get filepath() {\n return this.#filepath;\n }\n\n /**\n * Downloads the file from the given URL. The file will be downloaded to a temporary file.\n */\n async download() {\n await new Promise<void>((resolve, reject) => {\n const source = this.#url;\n const extension = path.extname(source);\n this.#tmpObj = tmp.fileSync({ postfix: extension });\n try {\n const connectionHandler = source.startsWith('https://') ? https : http;\n this.#httpRequest = connectionHandler.get(source, (res) => {\n const contentType = res.headers['content-type'];\n if (!contentType.includes('video')) {\n const err = new Error(`Source ${source}, returned unsupported content type ${contentType}`);\n reject(err);\n return;\n }\n const writeStream = fs.createWriteStream(this.#tmpObj.name);\n res.pipe(writeStream);\n writeStream.on('finish', () => {\n writeStream.close();\n this.#filepath = this.#tmpObj.name;\n resolve();\n });\n writeStream.on('error', (e) => {\n reject(e);\n });\n });\n this.#httpRequest.on('error', (e) => {\n if (e instanceof CancelRequestError) {\n return;\n }\n reject(e);\n });\n }\n catch (e) {\n reject(e);\n }\n });\n }\n\n clear() {\n if (this.#tmpObj) this.#tmpObj.removeCallback();\n if (this.#url) this.#url = undefined;\n if (this.#httpRequest) this.#httpRequest = null;\n if (this.#filepath) this.#filepath = undefined;\n }\n}\n","import type {\n Packet,\n Demuxer,\n Decoder,\n Filterer,\n Frame\n} from '@antoinemopa/beamcoder';\nimport beamcoder from '@antoinemopa/beamcoder';\nimport type { ImageData } from 'canvas';\nimport { createImageData } from 'canvas';\nimport { BaseExtractor } from '../BaseExtractor';\nimport type { Extractor, ExtractorArgs, InterpolateMode } from '../../framefusion';\nimport { DownloadVideoURL } from '../DownloadVideoURL';\n\nconst VERBOSE = false;\n\nconst createDecoder = ({\n demuxer,\n streamIndex,\n threadCount,\n}: {\n demuxer: Demuxer;\n streamIndex: number;\n threadCount: number;\n}): Decoder => {\n return beamcoder.decoder({\n demuxer: demuxer,\n width: demuxer.streams[streamIndex].codecpar.width,\n height: demuxer.streams[streamIndex].codecpar.height,\n stream_index: streamIndex,\n pix_fmt: demuxer.streams[streamIndex].codecpar.format,\n thread_count: threadCount,\n });\n};\n\n/**\n * A filter to convert between color spaces.\n * An example would be YUV to RGB, for mp4 to png conversion.\n */\nconst createFilter = async({\n stream,\n outputPixelFormat,\n interpolateFps,\n interpolateMode = 'fast',\n}: {\n stream: beamcoder.Stream;\n outputPixelFormat: string;\n interpolateFps?: number;\n interpolateMode?: InterpolateMode;\n}): Promise<beamcoder.Filterer> => {\n if (!stream.codecpar.format) {\n return null;\n }\n\n let filterSpec = [`[in0:v]format=${stream.codecpar.format}`];\n\n if (interpolateFps) {\n if (interpolateMode === 'high-quality') {\n filterSpec = [...filterSpec, `minterpolate=fps=${interpolateFps}`];\n }\n else if (interpolateMode === 'fast') {\n filterSpec = [...filterSpec, `fps=${interpolateFps}`];\n }\n else {\n throw new Error(`Unexpected interpolation mode: ${interpolateMode}`);\n }\n }\n\n const filterSpecStr = filterSpec.join(', ') + '[out0:v]';\n\n VERBOSE && console.log(`filterSpec: ${filterSpecStr}`);\n\n return beamcoder.filterer({\n filterType: 'video',\n inputParams: [\n {\n name: 'in0:v',\n width: stream.codecpar.width,\n height: stream.codecpar.height,\n pixelFormat: stream.codecpar.format,\n timeBase: stream.time_base,\n pixelAspect: stream.sample_aspect_ratio,\n },\n ],\n outputParams: [\n {\n name: 'out0:v',\n pixelFormat: outputPixelFormat,\n },\n ],\n filterSpec: filterSpecStr,\n });\n};\n\nconst STREAM_TYPE_VIDEO = 'video';\nconst COLORSPACE_RGBA = 'rgba';\nconst MAX_RECURSION = 5;\n\n/**\n * A simple extractor that uses beamcoder to extract frames from a video file.\n */\nexport class BeamcoderExtractor extends BaseExtractor implements Extractor {\n /**\n * The demuxer reads the file and outputs packet streams\n */\n #demuxer: Demuxer = null;\n\n /**\n * The decoder reads packets and can output raw frame data\n */\n #decoder: Decoder = null;\n\n /**\n * Packets can be filtered to change colorspace, fps and add various effects. If there are no colorspace changes or\n * filters, filter might not be necessary.\n */\n #filterer: Filterer = null;\n\n /**\n * This is where we store filtered frames from each previously processed packet.\n * We keep these in chronological order. We hang on to them for two reasons:\n * 1. so we can return them if we get a request for the same time again\n * 2. so we can return frames close the end of the stream. When such a frame is requested we have to flush (destroy)\n * the encoder to get the last few frames. This avoids having to re-create an encoder.\n */\n #filteredFramesPacket: undefined[] | Array<Array<Frame>> = [];\n\n /**\n * This contains the last raw frames we read from the demuxer. We use it as a starting point for each new query. We\n * do this ensure we don't skip any frames.\n */\n #frames = [];\n\n /**\n * This contains the last packet we read from the demuxer. We use it as a starting point for each new query. We do\n * this ensure we don't skip any frames.\n */\n #packet: null | Packet = null;\n\n /**\n * The last target presentation timestamp (PTS) we requested. If we never requested a time(stamp) then this\n * value is null\n */\n #previousTargetPTS: null | number = null;\n\n /**\n * The number of threads to use for decoding\n */\n #threadCount = 8;\n\n /**\n * The index of the video stream in the demuxer\n */\n #streamIndex = 0;\n\n /**\n * The number of packets we've read from the demuxer to complete the frame query\n * @private\n */\n #packetReadCount = 0;\n\n /**\n * The number of times we've recursively read packets from the demuxer to complete the frame query\n * @private\n */\n #recursiveReadCount = 0;\n\n /**\n * Encoder/Decoder construction is async, so it can't be put in a regular constructor.\n * Use and await this method to generate an extractor.\n */\n static async create(args: ExtractorArgs): Promise<BeamcoderExtractor> {\n const extractor = new BeamcoderExtractor();\n await extractor.init(args);\n return extractor;\n }\n\n async init({\n inputFileOrUrl,\n threadCount = 8,\n }: ExtractorArgs): Promise<void> {\n this.#threadCount = threadCount;\n if (inputFileOrUrl.startsWith('http')) {\n VERBOSE && console.log('downloading url', inputFileOrUrl);\n const downloadUrl = new DownloadVideoURL(inputFileOrUrl);\n await downloadUrl.download();\n inputFileOrUrl = downloadUrl.filepath;\n VERBOSE && console.log('finished downloading');\n }\n this.#demuxer = await beamcoder.demuxer('file:' + inputFileOrUrl);\n this.#streamIndex = this.#demuxer.streams.findIndex(stream => stream.codecpar.codec_type === STREAM_TYPE_VIDEO);\n if (this.#streamIndex === -1) {\n throw new Error(`File has no ${STREAM_TYPE_VIDEO} stream!`);\n }\n this.#filterer = await createFilter({\n stream: this.#demuxer.streams[this.#streamIndex],\n outputPixelFormat: COLORSPACE_RGBA,\n });\n }\n\n async #createDecoder() {\n // It's possible that we need to create decoder multiple times during the lifecycle of this extractor so we\n // need to make sure we destroy the old one first if it exists\n if (this.#decoder) {\n await this.#decoder.flush();\n this.#decoder = null;\n }\n this.#decoder = createDecoder({\n demuxer: this.#demuxer as Demuxer,\n streamIndex: this.#streamIndex,\n threadCount: this.#threadCount,\n });\n }\n\n /**\n * This is the duration of the first video stream in the file expressed in seconds.\n */\n get duration(): number {\n return this.ptsToTime(this.#demuxer.streams[this.#streamIndex].duration);\n }\n\n /**\n * Width in pixels\n */\n get width(): number {\n return this.#demuxer.streams[this.#streamIndex].codecpar.width;\n }\n\n /**\n * Height in pixels\n */\n get height(): number {\n return this.#demuxer.streams[this.#streamIndex].codecpar.height;\n }\n\n /**\n * Get the beamcoder Frame for a given time in seconds\n * @param targetTime\n */\n async getFrameAtTime(targetTime: number): Promise<beamcoder.Frame> {\n VERBOSE && console.log(`getFrameAtTime time(s)=${targetTime}`);\n const targetPts = Math.floor(this._timeToPTS(targetTime));\n return this._getFrameAtPts(targetPts);\n }\n\n /**\n * Get imageData for a given time in seconds\n * @param targetTime\n */\n async getImageDataAtTime(targetTime: number): Promise<ImageData> {\n const targetPts = Math.floor(this._timeToPTS(targetTime));\n VERBOSE && console.log('targetTime', targetTime, '-> targetPts', targetPts);\n const frame = await this._getFrameAtPts(targetPts);\n if (!frame) {\n VERBOSE && console.log('no frame found');\n return null;\n }\n const rawData = this._resizeFrameData(frame);\n const image = createImageData(\n rawData,\n frame.width,\n frame.height\n ) as ImageData;\n return image;\n }\n\n /**\n * Get the presentation timestamp (PTS) for a given time in seconds\n */\n _timeToPTS(time: number) {\n const time_base = this.#demuxer.streams[this.#streamIndex].time_base;\n return time * time_base[1] / time_base[0];\n }\n\n /**\n * Get the time in seconds from a given presentation timestamp (PTS)\n */\n ptsToTime(pts: number) {\n const time_base = this.#demuxer.streams[this.#streamIndex].time_base;\n return pts * time_base[0] / time_base[1];\n }\n\n get packetReadCount() {\n return this.#packetReadCount;\n }\n\n /**\n * Get the frame at the given presentation timestamp (PTS)\n * @param targetPTS - the target presentation timestamp (PTS) we want to retrieve\n * @param SeekPTSOffset - the offset to use when seeking to the targetPTS. This is used when we have trouble finding\n * the targetPTS. We use it to further move away from the requested PTS to find a frame. The allows use to read\n * additional packets and find a frame that is closer to the targetPTS.\n */\n async _getFrameAtPts(targetPTS: number, SeekPTSOffset = 0): Promise<beamcoder.Frame> {\n VERBOSE && console.log('_getFrameAtPts', targetPTS, SeekPTSOffset, '-> duration', this.duration);\n this.#packetReadCount = 0;\n\n // seek and create a decoder when retrieving a frame for the first time or when seeking backwards\n // we have to create a new decoder when seeking backwards as the decoder can only process frames in\n // chronological order.\n // RE_SEEK_DELTA: sometimes, we are looking for a frame so far ahead that it's better to drop everything and seek.\n // Example: when we got a frame a 0 and request a frame at t = 30s just after, we don't want to start reading all packets\n // until 30s.\n const RE_SEEK_THRESHOLD = 3; // 3 seconds - typically we have keyframes at shorter intervals\n const hasFrameWithinThreshold = this.#filteredFramesPacket.flat().some(frame => {\n return this.ptsToTime(Math.abs(targetPTS - (frame as Frame).pts)) < RE_SEEK_THRESHOLD;\n });\n VERBOSE && console.log('hasPreviousTargetPTS', this.#previousTargetPTS === null, 'targetPTS is smaller', this.#previousTargetPTS > targetPTS, 'has frame within threshold', hasFrameWithinThreshold);\n if (this.#previousTargetPTS === null || this.#previousTargetPTS > targetPTS || !hasFrameWithinThreshold) {\n VERBOSE && console.log(`Seeking to ${targetPTS - SeekPTSOffset}`);\n\n await this.#demuxer.seek({\n stream_index: 0, // even though we specify the stream index, it still seeks all streams\n timestamp: targetPTS + SeekPTSOffset,\n any: false,\n });\n await this.#createDecoder();\n this.#packet = null;\n this.#frames = [];\n this.#previousTargetPTS = targetPTS;\n this.#filteredFramesPacket = [];\n }\n\n let filteredFrames = null;\n let closestFramePTS = -1;\n let outputFrame = null;\n\n // If we have previously filtered frames, get the frame closest to our targetPTS\n if (this.#filteredFramesPacket.length > 0) {\n const closestFrame = this.#filteredFramesPacket\n .flat()\n .find(f => (f as Frame).pts <= targetPTS) as Frame;\n\n if (closestFrame) {\n const nextFrame = this.#filteredFramesPacket\n .flat()\n .find(f => (f as Frame).pts > closestFrame.pts) as Frame;\n\n VERBOSE && console.log('returning previously filtered frame with pts', (closestFrame as Frame).pts);\n closestFramePTS = (closestFrame as Frame).pts;\n outputFrame = closestFrame;\n\n if ((nextFrame && nextFrame.pts > targetPTS) || (closestFramePTS === targetPTS)) {\n // We have a next frame, so we know the frame being displayed at targetPTS is the previous one,\n // which corresponds to outputFrame.\n this.#previousTargetPTS = targetPTS;\n return outputFrame;\n }\n }\n }\n\n // This is the first time we're decoding frames. Get the first packet and decode it.\n if (!this.#packet && this.#frames.length === 0) {\n ({ packet: this.#packet, frames: this.#frames } = await this._getNextPacketAndDecodeFrames());\n this.#packetReadCount++;\n }\n // Read packets until we have a frame which is closest to targetPTS\n while ((this.#packet || this.#frames.length !== 0) && closestFramePTS < targetPTS) {\n VERBOSE && console.log('packet si:', this.#packet?.stream_index, 'pts:', this.#packet?.pts, 'frames:', this.#frames?.length);\n VERBOSE && console.log('frames', this.#frames?.length, 'frames.pts:', this.#frames?.map(f => f.pts), '-> target.pts:', targetPTS);\n\n // packet contains frames\n if (this.#frames.length !== 0) {\n // filter the frames\n const filteredResult = await this.#filterer.filter([{ name: 'in0:v', frames: this.#frames }]);\n filteredFrames = filteredResult.flatMap(r => r.frames);\n VERBOSE && console.log('filteredFrames', filteredFrames.length, 'filteredFrames.pts:', filteredFrames.map(f => f.pts), '-> target.pts:', targetPTS);\n\n // get the closest frame to our target presentation timestamp (PTS)\n // Beamcoder returns decoded packet frames as follows: [1000, 2000, 3000, 4000]\n // If we're looking for a frame at 2500, we want to return the frame at 2000\n const closestFrame = filteredFrames.reverse().find(f => f.pts <= targetPTS);\n\n // The packet contains frames, but all of them have PTS larger than our a targetPTS (we looked too far)\n if (!closestFrame) {\n return outputFrame;\n }\n\n // store the filtered packet frames for later reuse\n this.#filteredFramesPacket.unshift(filteredFrames);\n if (this.#filteredFramesPacket.length > 2) {\n this.#filteredFramesPacket.pop();\n }\n\n closestFramePTS = closestFrame?.pts;\n VERBOSE && console.log('closestFramePTS', closestFramePTS, 'targetPTS', targetPTS);\n if (!outputFrame || closestFramePTS <= targetPTS) {\n VERBOSE && console.log('assigning outputFrame', closestFrame?.pts);\n this.#previousTargetPTS = targetPTS;\n outputFrame = closestFrame;\n }\n else {\n // break out of the loop if we've found the closest frame (and ensure we don't move to the next\n // packet by calling _getNextPacketAndDecodeFrames again) as this risks us getting a frame that is\n // after our targetPTS\n VERBOSE && console.log('breaking');\n break;\n }\n }\n // get the next packet and frames\n ({ packet: this.#packet, frames: this.#frames } = await this._getNextPacketAndDecodeFrames());\n\n // keep track of how many packets we've read\n this.#packetReadCount++;\n }\n\n // we read through all the available packets and frames, but we still don't have a frame. This can happen\n // when our targetPTS is to close to the end of the video. In this case, we'll try to seek further away from\n // the end of the video and try again. We've set up a MAX_RECURSION to prevent an infinite loop.\n if (!outputFrame) {\n if (MAX_RECURSION < this.#recursiveReadCount) {\n throw Error('No matching frame found');\n }\n const TIME_OFFSET = 0.1; // time offset in seconds\n const PTSOffset = this._timeToPTS(TIME_OFFSET);\n this.#recursiveReadCount++;\n outputFrame = await this._getFrameAtPts(targetPTS, SeekPTSOffset - PTSOffset);\n if (outputFrame) {\n this.#recursiveReadCount = 0;\n }\n }\n VERBOSE && console.log('read', this.packetReadCount, 'packets');\n\n return outputFrame;\n }\n\n /**\n * Get the next packet from the video stream and decode it into frames. Each frame has a presentation time stamp\n * (PTS). If we've reached the end of the stream and no more packets are available, we'll extract the last frames\n * from the decoder and destroy it.\n */\n async _getNextPacketAndDecodeFrames() {\n const packet = await this._getNextVideoStreamPacket();\n VERBOSE && console.log('packet pts:', packet?.pts);\n\n // extract frames from the packet\n let decodedFrames = null;\n if (packet !== null && this.#decoder) {\n decodedFrames = await this.#decoder.decode(packet as Packet);\n VERBOSE && console.log('decodedFrames', decodedFrames.frames.length, decodedFrames.frames.map(f => f.pts));\n }\n // we've reached the end of the stream\n else {\n if (this.#decoder) {\n VERBOSE && console.log('getting the last frames from the decoder');\n // flush the decoder -- this will return the last frames and destroy the decoder\n decodedFrames = await this.#decoder.flush();\n this.#decoder = null;\n }\n else {\n // we don't have a decoder, so we can't decode any more frames\n VERBOSE && console.log('no more frames to decode');\n }\n }\n\n let frames = [];\n if (decodedFrames && decodedFrames.frames.length !== 0) {\n frames = decodedFrames.frames;\n }\n VERBOSE && console.log(`returning ${frames.length} decoded frames`);\n\n return { packet, frames };\n }\n\n async _getNextVideoStreamPacket(): Promise<null | Packet> {\n VERBOSE && console.log('_getNextVideoStreamPacket');\n\n let packet = await this.#demuxer.read();\n while (packet && packet.stream_index !== this.#streamIndex) {\n packet = await this.#demuxer.read();\n if (packet === null) {\n VERBOSE && console.log('no more packets');\n return null;\n }\n }\n VERBOSE && console.log('returning packet', !!packet, 'pts', packet?.pts, 'si', packet?.stream_index);\n return packet as Packet;\n }\n\n _resizeFrameData(frame): Uint8ClampedArray {\n const components = 4; // 4 components: r, g, b and a\n const size = frame.width * frame.height * components;\n const rawData = new Uint8ClampedArray(size); // we should probably reuse this buffer\n const sourceLineSize = frame.linesize as unknown as number;\n // frame.data can contain multiple \"planes\" in other colorspaces, but in rgba, there is just one \"plane\", so\n // our data is in frame.data[0]\n const pixels = frame.data[0] as Uint8Array;\n\n // libav creates larger buffers because it makes their internal code simpler.\n // we have to trim a part at the right of each pixel row.\n for (let i = 0; i < frame.height; i++) {\n const sourceStart = i * sourceLineSize;\n const sourceEnd = sourceStart + frame.width * components;\n const sourceData = pixels.slice(sourceStart, sourceEnd);\n const targetOffset = i * frame.width * components;\n rawData.set(sourceData, targetOffset);\n }\n return rawData;\n }\n\n async dispose() {\n if (this.#decoder) {\n await this.#decoder.flush();\n this.#decoder = null;\n }\n this.#demuxer.forceClose();\n this.#filterer = null;\n this.#filteredFramesPacket = undefined;\n this.#frames = [];\n this.#packet = null;\n this.#previousTargetPTS = null;\n this.#streamIndex = 0;\n }\n}\n"],"names":["BaseExtractor","args","inputFileOrUrl","outputFile","threadCount","endTime","interpolateFps","interpolateMode","targetPts","targetTime","pts","onFrameAvailable","flush","CancelRequestError","DownloadVideoURL","url","__privateAdd","_url","_httpRequest","_filepath","_tmpObj","__privateSet","__privateGet","resolve","reject","source","extension","path","tmp","connectionHandler","https","http","res","contentType","err","writeStream","fs","e","createDecoder","demuxer","streamIndex","beamcoder","createFilter","stream","outputPixelFormat","filterSpec","filterSpecStr","STREAM_TYPE_VIDEO","COLORSPACE_RGBA","MAX_RECURSION","_BeamcoderExtractor","_createDecoder","_demuxer","_decoder","_filterer","_filteredFramesPacket","_frames","_packet","_previousTargetPTS","_threadCount","_streamIndex","_packetReadCount","_recursiveReadCount","extractor","downloadUrl","frame","rawData","createImageData","time","time_base","targetPTS","SeekPTSOffset","RE_SEEK_THRESHOLD","hasFrameWithinThreshold","__privateMethod","createDecoder_fn","filteredFrames","closestFramePTS","outputFrame","closestFrame","f","nextFrame","__privateWrapper","r","TIME_OFFSET","PTSOffset","packet","decodedFrames","frames","size","sourceLineSize","pixels","i","sourceStart","sourceEnd","sourceData","targetOffset","BeamcoderExtractor"],"mappings":"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"}
1
+ {"version":3,"file":"framefusion.es.js","sources":["../src/BaseExtractor.ts","../src/DownloadVideoURL.ts","../src/backends/beamcoder.ts"],"sourcesContent":["import type { ImageData } from 'canvas';\n\nimport type {\n ExtractorArgs,\n Frame,\n Extractor\n} from '../framefusion';\n\nexport class BaseExtractor implements Extractor {\n static async create(args: ExtractorArgs): Promise<Extractor> {\n throw new Error('Not implemented');\n }\n\n async init({\n inputFileOrUrl,\n outputFile,\n threadCount = 8,\n endTime,\n interpolateFps,\n interpolateMode,\n }: ExtractorArgs): Promise<void> {\n throw new Error('Not implemented');\n }\n\n get duration(): number {\n throw new Error('Not implemented');\n }\n\n get width(): number {\n throw new Error('Not implemented');\n }\n\n get height(): number {\n throw new Error('Not implemented');\n }\n\n async seekToPTS(targetPts: number) {\n throw new Error('Not implemented');\n }\n\n async getFrameAtTime(targetTime: number): Promise<Frame> {\n throw new Error('Not implemented');\n }\n\n async getImageDataAtTime(targetTime: number): Promise<ImageData> {\n throw new Error('Not implemented');\n }\n\n async getFrameAtPts(targetPts: number): Promise<Frame> {\n throw new Error('Not implemented');\n }\n\n async seekToTime(targetTime: number) {\n throw new Error('Not implemented');\n }\n\n /**\n * Convert a PTS (based on timebase) to PTS (in seconds)\n */\n ptsToTime(pts: number) {\n throw new Error('Not implemented');\n }\n\n async readFrames({\n onFrameAvailable,\n flush = true,\n }: {\n /**\n * Return true if we need to read more frames.\n */\n onFrameAvailable?: (frame: Frame) => Promise<boolean> | boolean;\n flush?: boolean;\n } = {\n flush: true,\n onFrameAvailable: () => true,\n }) {\n throw new Error('Not implemented');\n }\n\n async dispose() {\n throw new Error('Not implemented');\n }\n}\n","import path from 'path';\nimport https from 'node:https';\nimport type { ClientRequest } from 'http';\nimport http from 'http';\nimport tmp from 'tmp';\nimport fs from 'fs-extra';\n\nclass CancelRequestError extends Error { }\n\n/**\n * Downloads a video file from a given URL as a temporary file. When the object is cleared, the temporary file is\n * deleted.\n */\nexport class DownloadVideoURL {\n #url: string | undefined;\n #httpRequest: ClientRequest | undefined = undefined;\n #filepath: string | undefined = undefined;\n #tmpObj: tmp.FileResult | undefined = undefined;\n\n constructor(url: string) {\n this.#url = url;\n }\n\n /**\n * returns the filepath of the downloaded file. If the file has not been downloaded yet, it will be undefined\n */\n get filepath() {\n return this.#filepath;\n }\n\n /**\n * Downloads the file from the given URL. The file will be downloaded to a temporary file.\n */\n async download() {\n await new Promise<void>((resolve, reject) => {\n const source = this.#url;\n const extension = path.extname(source);\n this.#tmpObj = tmp.fileSync({ postfix: extension });\n try {\n const connectionHandler = source.startsWith('https://') ? https : http;\n this.#httpRequest = connectionHandler.get(source, (res) => {\n const contentType = res.headers['content-type'];\n if (!contentType.includes('video')) {\n const err = new Error(`Source ${source}, returned unsupported content type ${contentType}`);\n reject(err);\n return;\n }\n const writeStream = fs.createWriteStream(this.#tmpObj.name);\n res.pipe(writeStream);\n writeStream.on('finish', () => {\n writeStream.close();\n this.#filepath = this.#tmpObj.name;\n resolve();\n });\n writeStream.on('error', (e) => {\n reject(e);\n });\n });\n this.#httpRequest.on('error', (e) => {\n if (e instanceof CancelRequestError) {\n return;\n }\n reject(e);\n });\n\n this.#httpRequest.on('timeout', () => {\n this.#httpRequest.destroy();\n reject(new Error(`Request to ${source} timed out`));\n });\n }\n catch (e) {\n reject(e);\n }\n });\n }\n\n clear() {\n if (this.#tmpObj) this.#tmpObj.removeCallback();\n if (this.#url) this.#url = undefined;\n if (this.#httpRequest) this.#httpRequest = null;\n if (this.#filepath) this.#filepath = undefined;\n }\n}\n","import type {\n Packet,\n Demuxer,\n Decoder,\n Filterer,\n Frame\n} from '@antoinemopa/beamcoder';\nimport beamcoder from '@antoinemopa/beamcoder';\nimport type { ImageData } from 'canvas';\nimport { createImageData } from 'canvas';\nimport { BaseExtractor } from '../BaseExtractor';\nimport type { Extractor, ExtractorArgs, InterpolateMode } from '../../framefusion';\nimport { DownloadVideoURL } from '../DownloadVideoURL';\n\nconst VERBOSE = false;\n\nconst createDecoder = ({\n demuxer,\n streamIndex,\n threadCount,\n}: {\n demuxer: Demuxer;\n streamIndex: number;\n threadCount: number;\n}): Decoder => {\n return beamcoder.decoder({\n demuxer: demuxer,\n width: demuxer.streams[streamIndex].codecpar.width,\n height: demuxer.streams[streamIndex].codecpar.height,\n stream_index: streamIndex,\n pix_fmt: demuxer.streams[streamIndex].codecpar.format,\n thread_count: threadCount,\n });\n};\n\n/**\n * A filter to convert between color spaces.\n * An example would be YUV to RGB, for mp4 to png conversion.\n */\nconst createFilter = async({\n stream,\n outputPixelFormat,\n interpolateFps,\n interpolateMode = 'fast',\n}: {\n stream: beamcoder.Stream;\n outputPixelFormat: string;\n interpolateFps?: number;\n interpolateMode?: InterpolateMode;\n}): Promise<beamcoder.Filterer> => {\n if (!stream.codecpar.format) {\n return null;\n }\n\n let filterSpec = [`[in0:v]format=${stream.codecpar.format}`];\n\n if (interpolateFps) {\n if (interpolateMode === 'high-quality') {\n filterSpec = [...filterSpec, `minterpolate=fps=${interpolateFps}`];\n }\n else if (interpolateMode === 'fast') {\n filterSpec = [...filterSpec, `fps=${interpolateFps}`];\n }\n else {\n throw new Error(`Unexpected interpolation mode: ${interpolateMode}`);\n }\n }\n\n const filterSpecStr = filterSpec.join(', ') + '[out0:v]';\n\n VERBOSE && console.log(`filterSpec: ${filterSpecStr}`);\n\n return beamcoder.filterer({\n filterType: 'video',\n inputParams: [\n {\n name: 'in0:v',\n width: stream.codecpar.width,\n height: stream.codecpar.height,\n pixelFormat: stream.codecpar.format,\n timeBase: stream.time_base,\n pixelAspect: stream.sample_aspect_ratio,\n },\n ],\n outputParams: [\n {\n name: 'out0:v',\n pixelFormat: outputPixelFormat,\n },\n ],\n filterSpec: filterSpecStr,\n });\n};\n\nconst STREAM_TYPE_VIDEO = 'video';\nconst COLORSPACE_RGBA = 'rgba';\nconst MAX_RECURSION = 5;\n\n/**\n * A simple extractor that uses beamcoder to extract frames from a video file.\n */\nexport class BeamcoderExtractor extends BaseExtractor implements Extractor {\n /**\n * The demuxer reads the file and outputs packet streams\n */\n #demuxer: Demuxer = null;\n\n /**\n * The decoder reads packets and can output raw frame data\n */\n #decoder: Decoder = null;\n\n /**\n * Packets can be filtered to change colorspace, fps and add various effects. If there are no colorspace changes or\n * filters, filter might not be necessary.\n */\n #filterer: Filterer = null;\n\n /**\n * This is where we store filtered frames from each previously processed packet.\n * We keep these in chronological order. We hang on to them for two reasons:\n * 1. so we can return them if we get a request for the same time again\n * 2. so we can return frames close the end of the stream. When such a frame is requested we have to flush (destroy)\n * the encoder to get the last few frames. This avoids having to re-create an encoder.\n */\n #filteredFramesPacket: undefined[] | Array<Array<Frame>> = [];\n\n /**\n * This contains the last raw frames we read from the demuxer. We use it as a starting point for each new query. We\n * do this ensure we don't skip any frames.\n */\n #frames = [];\n\n /**\n * This contains the last packet we read from the demuxer. We use it as a starting point for each new query. We do\n * this ensure we don't skip any frames.\n */\n #packet: null | Packet = null;\n\n /**\n * The last target presentation timestamp (PTS) we requested. If we never requested a time(stamp) then this\n * value is null\n */\n #previousTargetPTS: null | number = null;\n\n /**\n * The number of threads to use for decoding\n */\n #threadCount = 8;\n\n /**\n * The index of the video stream in the demuxer\n */\n #streamIndex = 0;\n\n /**\n * The number of packets we've read from the demuxer to complete the frame query\n * @private\n */\n #packetReadCount = 0;\n\n /**\n * The number of times we've recursively read packets from the demuxer to complete the frame query\n * @private\n */\n #recursiveReadCount = 0;\n\n /**\n * Encoder/Decoder construction is async, so it can't be put in a regular constructor.\n * Use and await this method to generate an extractor.\n */\n static async create(args: ExtractorArgs): Promise<BeamcoderExtractor> {\n const extractor = new BeamcoderExtractor();\n await extractor.init(args);\n return extractor;\n }\n\n async init({\n inputFileOrUrl,\n threadCount = 8,\n }: ExtractorArgs): Promise<void> {\n this.#threadCount = threadCount;\n if (inputFileOrUrl.startsWith('http')) {\n VERBOSE && console.log('downloading url', inputFileOrUrl);\n const downloadUrl = new DownloadVideoURL(inputFileOrUrl);\n await downloadUrl.download();\n inputFileOrUrl = downloadUrl.filepath;\n VERBOSE && console.log('finished downloading');\n }\n this.#demuxer = await beamcoder.demuxer('file:' + inputFileOrUrl);\n this.#streamIndex = this.#demuxer.streams.findIndex(stream => stream.codecpar.codec_type === STREAM_TYPE_VIDEO);\n if (this.#streamIndex === -1) {\n throw new Error(`File has no ${STREAM_TYPE_VIDEO} stream!`);\n }\n this.#filterer = await createFilter({\n stream: this.#demuxer.streams[this.#streamIndex],\n outputPixelFormat: COLORSPACE_RGBA,\n });\n }\n\n async #createDecoder() {\n // It's possible that we need to create decoder multiple times during the lifecycle of this extractor so we\n // need to make sure we destroy the old one first if it exists\n if (this.#decoder) {\n await this.#decoder.flush();\n this.#decoder = null;\n }\n this.#decoder = createDecoder({\n demuxer: this.#demuxer as Demuxer,\n streamIndex: this.#streamIndex,\n threadCount: this.#threadCount,\n });\n }\n\n /**\n * This is the duration of the first video stream in the file expressed in seconds.\n */\n get duration(): number {\n return this.ptsToTime(this.#demuxer.streams[this.#streamIndex].duration);\n }\n\n /**\n * Width in pixels\n */\n get width(): number {\n return this.#demuxer.streams[this.#streamIndex].codecpar.width;\n }\n\n /**\n * Height in pixels\n */\n get height(): number {\n return this.#demuxer.streams[this.#streamIndex].codecpar.height;\n }\n\n /**\n * Get the beamcoder Frame for a given time in seconds\n * @param targetTime\n */\n async getFrameAtTime(targetTime: number): Promise<beamcoder.Frame> {\n VERBOSE && console.log(`getFrameAtTime time(s)=${targetTime}`);\n const targetPts = Math.round(this._timeToPTS(targetTime));\n return this._getFrameAtPts(targetPts);\n }\n\n /**\n * Get imageData for a given time in seconds\n * @param targetTime\n */\n async getImageDataAtTime(targetTime: number): Promise<ImageData> {\n const targetPts = Math.round(this._timeToPTS(targetTime));\n VERBOSE && console.log('targetTime', targetTime, '-> targetPts', targetPts);\n const frame = await this._getFrameAtPts(targetPts);\n if (!frame) {\n VERBOSE && console.log('no frame found');\n return null;\n }\n const rawData = this._resizeFrameData(frame);\n const image = createImageData(\n rawData,\n frame.width,\n frame.height\n ) as ImageData;\n return image;\n }\n\n /**\n * Get the presentation timestamp (PTS) for a given time in seconds\n */\n _timeToPTS(time: number) {\n const time_base = this.#demuxer.streams[this.#streamIndex].time_base;\n return time * time_base[1] / time_base[0];\n }\n\n /**\n * Get the time in seconds from a given presentation timestamp (PTS)\n */\n ptsToTime(pts: number) {\n const time_base = this.#demuxer.streams[this.#streamIndex].time_base;\n return pts * time_base[0] / time_base[1];\n }\n\n get packetReadCount() {\n return this.#packetReadCount;\n }\n\n /**\n * Get the frame at the given presentation timestamp (PTS)\n * @param targetPTS - the target presentation timestamp (PTS) we want to retrieve\n * @param SeekPTSOffset - the offset to use when seeking to the targetPTS. This is used when we have trouble finding\n * the targetPTS. We use it to further move away from the requested PTS to find a frame. The allows use to read\n * additional packets and find a frame that is closer to the targetPTS.\n */\n async _getFrameAtPts(targetPTS: number, SeekPTSOffset = 0): Promise<beamcoder.Frame> {\n VERBOSE && console.log('_getFrameAtPts', targetPTS, SeekPTSOffset, '-> duration', this.duration);\n this.#packetReadCount = 0;\n\n // seek and create a decoder when retrieving a frame for the first time or when seeking backwards\n // we have to create a new decoder when seeking backwards as the decoder can only process frames in\n // chronological order.\n // RE_SEEK_DELTA: sometimes, we are looking for a frame so far ahead that it's better to drop everything and seek.\n // Example: when we got a frame a 0 and request a frame at t = 30s just after, we don't want to start reading all packets\n // until 30s.\n const RE_SEEK_THRESHOLD = 3; // 3 seconds - typically we have keyframes at shorter intervals\n const hasFrameWithinThreshold = this.#filteredFramesPacket.flat().some(frame => {\n return this.ptsToTime(Math.abs(targetPTS - (frame as Frame).pts)) < RE_SEEK_THRESHOLD;\n });\n VERBOSE && console.log('hasPreviousTargetPTS', this.#previousTargetPTS === null, 'targetPTS is smaller', this.#previousTargetPTS > targetPTS, 'has frame within threshold', hasFrameWithinThreshold);\n if (this.#previousTargetPTS === null || this.#previousTargetPTS > targetPTS || !hasFrameWithinThreshold) {\n VERBOSE && console.log(`Seeking to ${targetPTS - SeekPTSOffset}`);\n\n await this.#demuxer.seek({\n stream_index: 0, // even though we specify the stream index, it still seeks all streams\n timestamp: targetPTS + SeekPTSOffset,\n any: false,\n });\n await this.#createDecoder();\n this.#packet = null;\n this.#frames = [];\n this.#previousTargetPTS = targetPTS;\n this.#filteredFramesPacket = [];\n }\n\n let filteredFrames = null;\n let closestFramePTS = -1;\n let outputFrame = null;\n\n // If we have previously filtered frames, get the frame closest to our targetPTS\n if (this.#filteredFramesPacket.length > 0) {\n const closestFrame = this.#filteredFramesPacket\n .flat()\n .find(f => (f as Frame).pts <= targetPTS) as Frame;\n\n if (closestFrame) {\n const nextFrame = this.#filteredFramesPacket\n .flat()\n .find(f => (f as Frame).pts > closestFrame.pts) as Frame;\n\n VERBOSE && console.log('returning previously filtered frame with pts', (closestFrame as Frame).pts);\n closestFramePTS = (closestFrame as Frame).pts;\n outputFrame = closestFrame;\n\n if ((nextFrame && nextFrame.pts > targetPTS) || (closestFramePTS === targetPTS)) {\n // We have a next frame, so we know the frame being displayed at targetPTS is the previous one,\n // which corresponds to outputFrame.\n this.#previousTargetPTS = targetPTS;\n return outputFrame;\n }\n }\n }\n\n // This is the first time we're decoding frames. Get the first packet and decode it.\n if (!this.#packet && this.#frames.length === 0) {\n ({ packet: this.#packet, frames: this.#frames } = await this._getNextPacketAndDecodeFrames());\n this.#packetReadCount++;\n }\n // Read packets until we have a frame which is closest to targetPTS\n while ((this.#packet || this.#frames.length !== 0) && closestFramePTS < targetPTS) {\n VERBOSE && console.log('packet si:', this.#packet?.stream_index, 'pts:', this.#packet?.pts, 'frames:', this.#frames?.length);\n VERBOSE && console.log('frames', this.#frames?.length, 'frames.pts:', this.#frames?.map(f => f.pts), '-> target.pts:', targetPTS);\n\n // packet contains frames\n if (this.#frames.length !== 0) {\n // filter the frames\n const filteredResult = await this.#filterer.filter([{ name: 'in0:v', frames: this.#frames }]);\n filteredFrames = filteredResult.flatMap(r => r.frames);\n VERBOSE && console.log('filteredFrames', filteredFrames.length, 'filteredFrames.pts:', filteredFrames.map(f => f.pts), '-> target.pts:', targetPTS);\n\n // get the closest frame to our target presentation timestamp (PTS)\n // Beamcoder returns decoded packet frames as follows: [1000, 2000, 3000, 4000]\n // If we're looking for a frame at 2500, we want to return the frame at 2000\n const closestFrame = filteredFrames.reverse().find(f => f.pts <= targetPTS);\n\n // The packet contains frames, but all of them have PTS larger than our a targetPTS (we looked too far)\n if (!closestFrame) {\n return outputFrame;\n }\n\n // store the filtered packet frames for later reuse\n this.#filteredFramesPacket.unshift(filteredFrames);\n if (this.#filteredFramesPacket.length > 2) {\n this.#filteredFramesPacket.pop();\n }\n\n closestFramePTS = closestFrame?.pts;\n VERBOSE && console.log('closestFramePTS', closestFramePTS, 'targetPTS', targetPTS);\n if (!outputFrame || closestFramePTS <= targetPTS) {\n VERBOSE && console.log('assigning outputFrame', closestFrame?.pts);\n this.#previousTargetPTS = targetPTS;\n outputFrame = closestFrame;\n }\n else {\n // break out of the loop if we've found the closest frame (and ensure we don't move to the next\n // packet by calling _getNextPacketAndDecodeFrames again) as this risks us getting a frame that is\n // after our targetPTS\n VERBOSE && console.log('breaking');\n break;\n }\n }\n // get the next packet and frames\n ({ packet: this.#packet, frames: this.#frames } = await this._getNextPacketAndDecodeFrames());\n\n // keep track of how many packets we've read\n this.#packetReadCount++;\n }\n\n // we read through all the available packets and frames, but we still don't have a frame. This can happen\n // when our targetPTS is to close to the end of the video. In this case, we'll try to seek further away from\n // the end of the video and try again. We've set up a MAX_RECURSION to prevent an infinite loop.\n if (!outputFrame) {\n if (MAX_RECURSION < this.#recursiveReadCount) {\n throw Error('No matching frame found');\n }\n const TIME_OFFSET = 0.1; // time offset in seconds\n const PTSOffset = this._timeToPTS(TIME_OFFSET);\n this.#recursiveReadCount++;\n outputFrame = await this._getFrameAtPts(targetPTS, SeekPTSOffset - PTSOffset);\n if (outputFrame) {\n this.#recursiveReadCount = 0;\n }\n }\n VERBOSE && console.log('read', this.packetReadCount, 'packets');\n\n return outputFrame;\n }\n\n /**\n * Get the next packet from the video stream and decode it into frames. Each frame has a presentation time stamp\n * (PTS). If we've reached the end of the stream and no more packets are available, we'll extract the last frames\n * from the decoder and destroy it.\n */\n async _getNextPacketAndDecodeFrames() {\n const packet = await this._getNextVideoStreamPacket();\n VERBOSE && console.log('packet pts:', packet?.pts);\n\n // extract frames from the packet\n let decodedFrames = null;\n if (packet !== null && this.#decoder) {\n decodedFrames = await this.#decoder.decode(packet as Packet);\n VERBOSE && console.log('decodedFrames', decodedFrames.frames.length, decodedFrames.frames.map(f => f.pts));\n }\n // we've reached the end of the stream\n else {\n if (this.#decoder) {\n VERBOSE && console.log('getting the last frames from the decoder');\n // flush the decoder -- this will return the last frames and destroy the decoder\n decodedFrames = await this.#decoder.flush();\n this.#decoder = null;\n }\n else {\n // we don't have a decoder, so we can't decode any more frames\n VERBOSE && console.log('no more frames to decode');\n }\n }\n\n let frames = [];\n if (decodedFrames && decodedFrames.frames.length !== 0) {\n frames = decodedFrames.frames;\n }\n VERBOSE && console.log(`returning ${frames.length} decoded frames`);\n\n return { packet, frames };\n }\n\n async _getNextVideoStreamPacket(): Promise<null | Packet> {\n VERBOSE && console.log('_getNextVideoStreamPacket');\n\n let packet = await this.#demuxer.read();\n while (packet && packet.stream_index !== this.#streamIndex) {\n packet = await this.#demuxer.read();\n if (packet === null) {\n VERBOSE && console.log('no more packets');\n return null;\n }\n }\n VERBOSE && console.log('returning packet', !!packet, 'pts', packet?.pts, 'si', packet?.stream_index);\n return packet as Packet;\n }\n\n _resizeFrameData(frame): Uint8ClampedArray {\n const components = 4; // 4 components: r, g, b and a\n const size = frame.width * frame.height * components;\n const rawData = new Uint8ClampedArray(size); // we should probably reuse this buffer\n const sourceLineSize = frame.linesize as unknown as number;\n // frame.data can contain multiple \"planes\" in other colorspaces, but in rgba, there is just one \"plane\", so\n // our data is in frame.data[0]\n const pixels = frame.data[0] as Uint8Array;\n\n // libav creates larger buffers because it makes their internal code simpler.\n // we have to trim a part at the right of each pixel row.\n for (let i = 0; i < frame.height; i++) {\n const sourceStart = i * sourceLineSize;\n const sourceEnd = sourceStart + frame.width * components;\n const sourceData = pixels.slice(sourceStart, sourceEnd);\n const targetOffset = i * frame.width * components;\n rawData.set(sourceData, targetOffset);\n }\n return rawData;\n }\n\n async dispose() {\n if (this.#decoder) {\n await this.#decoder.flush();\n this.#decoder = null;\n }\n this.#demuxer.forceClose();\n this.#filterer = null;\n this.#filteredFramesPacket = undefined;\n this.#frames = [];\n this.#packet = null;\n this.#previousTargetPTS = null;\n this.#streamIndex = 0;\n }\n}\n"],"names":["BaseExtractor","args","inputFileOrUrl","outputFile","threadCount","endTime","interpolateFps","interpolateMode","targetPts","targetTime","pts","onFrameAvailable","flush","CancelRequestError","DownloadVideoURL","url","__privateAdd","_url","_httpRequest","_filepath","_tmpObj","__privateSet","__privateGet","resolve","reject","source","extension","path","tmp","connectionHandler","https","http","res","contentType","err","writeStream","fs","e","createDecoder","demuxer","streamIndex","beamcoder","createFilter","stream","outputPixelFormat","filterSpec","filterSpecStr","STREAM_TYPE_VIDEO","COLORSPACE_RGBA","MAX_RECURSION","_BeamcoderExtractor","_createDecoder","_demuxer","_decoder","_filterer","_filteredFramesPacket","_frames","_packet","_previousTargetPTS","_threadCount","_streamIndex","_packetReadCount","_recursiveReadCount","extractor","downloadUrl","frame","rawData","createImageData","time","time_base","targetPTS","SeekPTSOffset","RE_SEEK_THRESHOLD","hasFrameWithinThreshold","__privateMethod","createDecoder_fn","filteredFrames","closestFramePTS","outputFrame","closestFrame","f","nextFrame","__privateWrapper","r","TIME_OFFSET","PTSOffset","packet","decodedFrames","frames","size","sourceLineSize","pixels","i","sourceStart","sourceEnd","sourceData","targetOffset","BeamcoderExtractor"],"mappings":"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"}
@@ -57,6 +57,10 @@ export class DownloadVideoURL {
57
57
  }
58
58
  reject(e);
59
59
  });
60
+ this.#httpRequest.on('timeout', () => {
61
+ this.#httpRequest.destroy();
62
+ reject(new Error(`Request to ${source} timed out`));
63
+ });
60
64
  }
61
65
  catch (e) {
62
66
  reject(e);
@@ -1 +1 @@
1
- {"version":3,"file":"DownloadVideoURL.js","sourceRoot":"","sources":["../../src/DownloadVideoURL.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"DownloadVideoURL.js","sourceRoot":"","sources":["../../src/DownloadVideoURL.ts"],"names":[],"mappings":"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"}
@@ -182,7 +182,7 @@ export class BeamcoderExtractor extends BaseExtractor {
182
182
  */
183
183
  async getFrameAtTime(targetTime) {
184
184
  VERBOSE && console.log(`getFrameAtTime time(s)=${targetTime}`);
185
- const targetPts = Math.floor(this._timeToPTS(targetTime));
185
+ const targetPts = Math.round(this._timeToPTS(targetTime));
186
186
  return this._getFrameAtPts(targetPts);
187
187
  }
188
188
  /**
@@ -190,7 +190,7 @@ export class BeamcoderExtractor extends BaseExtractor {
190
190
  * @param targetTime
191
191
  */
192
192
  async getImageDataAtTime(targetTime) {
193
- const targetPts = Math.floor(this._timeToPTS(targetTime));
193
+ const targetPts = Math.round(this._timeToPTS(targetTime));
194
194
  VERBOSE && console.log('targetTime', targetTime, '-> targetPts', targetPts);
195
195
  const frame = await this._getFrameAtPts(targetPts);
196
196
  if (!frame) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lumen5/framefusion",
3
- "version": "0.0.24",
3
+ "version": "0.0.26",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "docs": "typedoc framefusion.ts",