diff --git a/examples/thumbnail-generation/thumbnail-generation.ts b/examples/thumbnail-generation/thumbnail-generation.ts index ea7716c..7211fed 100644 --- a/examples/thumbnail-generation/thumbnail-generation.ts +++ b/examples/thumbnail-generation/thumbnail-generation.ts @@ -1,6 +1,7 @@ -import { Input, ALL_FORMATS, BlobSource, UrlSource, CanvasSink } from 'mediabunny'; +import { Input, ALL_FORMATS, BlobSource, UrlSource, VideoSampleCursor } from 'mediabunny'; import SampleFileUrl from '../../docs/assets/big-buck-bunny-trimmed.mp4'; +import { canvasTransformer } from '../../src/cursors.js'; (document.querySelector('#sample-file-download') as HTMLAnchorElement).href = SampleFileUrl; const selectMediaButton = document.querySelector('#select-file') as HTMLButtonElement; @@ -69,16 +70,23 @@ const generateThumbnails = async (resource: File | string) => { (_, i) => firstTimestamp + i * (lastTimestamp - firstTimestamp) / THUMBNAIL_COUNT, ); - // Create a CanvasSink for extracting resized frames from the video track - const sink = new CanvasSink(videoTrack, { - width: Math.floor(width * window.devicePixelRatio), - height: Math.floor(height * window.devicePixelRatio), - fit: 'fill', + // Create a cursor to extract frames from the video + const cursor = new VideoSampleCursor(videoTrack, { + transform: canvasTransformer({ + width: Math.floor(width * window.devicePixelRatio), + height: Math.floor(height * window.devicePixelRatio), + fit: 'fill', + }), }); + // Queue all cursor commands at once, including closing the cursor; this allows it to optimize access patterns + const promises = timestamps.map(x => cursor.seekTo(x)); + void cursor.close(); + // Iterate over all thumbnail canvases let i = 0; - for await (const wrappedCanvas of sink.canvasesAtTimestamps(timestamps)) { + for (const promise of promises) { + const wrappedCanvas = await promise; const container = thumbnailElements[i]!; if (wrappedCanvas) { diff --git a/src/cursors.ts b/src/cursors.ts index b8174b5..4b86eba 100644 --- a/src/cursors.ts +++ b/src/cursors.ts @@ -930,7 +930,6 @@ export abstract class SampleCursor< this._lastPendingRequest = pendingRequest; this._pumpGate.open(); - deferred.execute(); // Waiting for the return would be too long return res.set(await request.promise); @@ -987,6 +986,7 @@ export abstract class SampleCursor< } this._lastPendingRequest = pendingRequest; + this._pumpGate.open(); lock.release(); // Waiting for the return would be too long return res.set(await request.promise); diff --git a/src/isobmff/isobmff-demuxer.ts b/src/isobmff/isobmff-demuxer.ts index df46f17..e50f3b6 100644 --- a/src/isobmff/isobmff-demuxer.ts +++ b/src/isobmff/isobmff-demuxer.ts @@ -2368,7 +2368,10 @@ abstract class IsobmffTrackBacking implements InputTrackBacking { } async getFirstTimestamp() { - const firstPacket = await this.getFirstPacket({ metadataOnly: true }); + const result = new ResultValue(); + await this.getFirstPacket(result, { metadataOnly: true }); + + const firstPacket = result.value; return firstPacket?.timestamp ?? 0; }