-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
7b5c4b8
commit 071bf6d
Showing
6 changed files
with
247 additions
and
127 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
67 changes: 41 additions & 26 deletions
67
packages/live-streaming/frontend/src/media/streamer/convertGPUFrameToCPUFrame.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,36 +1,51 @@ | ||
// Create a persistent offscreen canvas and its context | ||
const offscreen = new OffscreenCanvas(1, 1); | ||
const ctx = offscreen.getContext("2d"); | ||
import pDefer, { DeferredPromise } from "p-defer"; | ||
|
||
// Create a persistent worker (adjust the path as needed) | ||
const frameWorker = new Worker(new URL("./frameWorker.js", import.meta.url)); | ||
|
||
// We'll assign a unique id to each conversion request. | ||
let nextMessageId = 0; | ||
const pendingRequests = new Map<number, DeferredPromise<VideoFrame>>(); | ||
|
||
// Listen for responses from the worker. | ||
frameWorker.addEventListener("message", (event: MessageEvent) => { | ||
const { id, cpuFrame, error } = event.data; | ||
const deferred = pendingRequests.get(id); | ||
if (!deferred) return; | ||
if (error) { | ||
deferred.reject(new Error(error)); | ||
} else { | ||
deferred.resolve(cpuFrame); | ||
} | ||
pendingRequests.delete(id); | ||
}); | ||
|
||
/** | ||
* Convert a GPU-backed frame to a CPU-accessible one using the reusable offscreen canvas. | ||
* The canvas is resized if needed. | ||
* Convert a GPU-backed VideoFrame to a CPU-accessible VideoFrame. | ||
* | ||
* This function sends the provided VideoFrame (source) along with the dimensions | ||
* obtained from the video element (videoRef) to a worker. The worker draws the | ||
* frame on its offscreen canvas and returns a new VideoFrame. | ||
* | ||
* @param videoRef - The HTMLVideoElement (used to obtain dimensions). | ||
* @param source - The GPU-backed VideoFrame to convert. | ||
* @returns A promise that resolves to a CPU-accessible VideoFrame. | ||
*/ | ||
export const convertGPUFrameToCPUFrame = ( | ||
videoRef: HTMLVideoElement, | ||
source: VideoFrame | ||
) => { | ||
// Resize canvas if dimensions don't match | ||
if ( | ||
offscreen.width !== videoRef.videoWidth || | ||
offscreen.height !== videoRef.videoHeight | ||
) { | ||
offscreen.width = videoRef.videoWidth; | ||
offscreen.height = videoRef.videoHeight; | ||
} | ||
|
||
// Draw the source (video element or VideoFrame) onto the offscreen canvas | ||
ctx.drawImage(source, 0, 0, offscreen.width, offscreen.height); | ||
|
||
// Get an ImageBitmap from the canvas | ||
const bitmap = offscreen.transferToImageBitmap(); | ||
): Promise<VideoFrame> => { | ||
const deferred = pDefer<VideoFrame>(); | ||
const messageId = nextMessageId++; | ||
pendingRequests.set(messageId, deferred); | ||
|
||
// Create a new VideoFrame from the ImageBitmap | ||
const cpuFrame = new VideoFrame(bitmap, { timestamp: source.timestamp }); | ||
// Get the desired dimensions from the video element. | ||
const width = videoRef.videoWidth; | ||
const height = videoRef.videoHeight; | ||
|
||
// Clean up the bitmap (it's no longer needed) | ||
bitmap.close(); | ||
// Post the message to the worker. | ||
// Transfer the VideoFrame (source) to avoid a copy. | ||
frameWorker.postMessage({ id: messageId, source, width, height }, [source]); | ||
|
||
source.close(); | ||
return cpuFrame; | ||
return deferred.promise; | ||
}; |
Oops, something went wrong.