Performance: Offload MediaPipe pose detection to Web Worker#298
Performance: Offload MediaPipe pose detection to Web Worker#298shivansh31414 wants to merge 3 commits into
Conversation
|
Someone is attempting to deploy a commit to the somiljain2024-4175's projects Team on Vercel. A member of the Team first needs to authorize it. |
There was a problem hiding this comment.
Pull request overview
This PR refactors MediaPipe Pose landmark extraction to run in a dedicated module Web Worker, aiming to reduce main-thread load and improve UI responsiveness during continuous video processing.
Changes:
- Added a new
poseLandmarkWorkerthat initializes MediaPipe Pose inside a worker and processes transferredImageBitmapframes. - Updated
PoseServiceto spawn the module worker, transfer frames viapostMessage, and surface results back through anonResultscallback. - Introduced per-frame acknowledgement/timeout tracking in
PoseServiceusing apendingPromisesmap.
Reviewed changes
Copilot reviewed 2 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| src/workers/poseLandmarkWorker.ts | New worker that imports/initializes MediaPipe Pose and processes ImageBitmap frames off the main thread. |
| src/services/poseService.ts | Refactors pose processing to delegate work to the new module worker and relay results/timeouts back to callers. |
Comments suppressed due to low confidence (3)
src/services/poseService.ts:97
- If the
Promise.racetimes out, thependingPromisesentry for this frame is never removed, so a hung worker will leak entries indefinitely. Also,inProgressis only cleared in the catch path, which makes it easy to get stuck if the worker later responds unexpectedly. Ensure timeout handling deletes the pending entry and resets state in afinally, and consider terminating/restarting the worker on timeout to prevent permanent stalls.
const promise = new Promise<void>((resolve, reject) => {
this.pendingPromises.set(id, { resolve, reject });
// Transfer the ImageBitmap for zero-copy
try {
this.worker!.postMessage({ type: 'processFrame', frameId: id, imageBitmap: bitmap }, [bitmap]);
} catch (err) {
this.pendingPromises.delete(id);
reject(err);
}
});
// wait for processing to finish (or timeout)
const timeout = new Promise<void>((_, rej) => setTimeout(() => rej(new Error('pose worker timeout')), 2000));
await Promise.race([promise, timeout]);
} catch (error) {
console.error('PoseService.send error', error);
this.inProgress = false;
}
src/services/poseService.ts:56
worker.onerroronly logs. If the worker throws/crashes aftersend()setsinProgress = true,inProgressmay never be cleared and anypendingPromiseswill never resolve/reject, effectively deadlocking pose processing. Handleonerrorby rejecting/clearing all pending entries and resettinginProgress(and optionally recreating the worker).
this.worker.onerror = (e) => {
console.error('PoseService worker thrown error', e);
};
src/services/poseService.ts:109
close()terminates the worker but does not clear/reject anypendingPromisesor resetinProgress. Ifclose()is called while a frame is pending, callers awaitingsend()may hang and the service can remain stuck. Consider rejecting/clearingpendingPromises, resettinginProgress, and nulling the callback as part of shutdown.
close() {
if (this.worker) {
try {
this.worker.terminate();
} catch (e) {
console.warn('Error terminating pose worker', e);
}
this.worker = null;
}
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
@shivansh31414 plz correct the issues suggested by copilot |
|
okay |
Added PoseResultsPayload type for structured results and improved error handling.
|
done |
|
please check the pr |
|
@shivansh31414 plz solve these merge conflicts |
Fixes #256
📝 Description
Offload MediaPipe pose landmark extraction from the main thread into a module Web Worker. This change moves expensive frame processing into a background thread, preserving the public
poseServiceAPI (onResults()andsend(image)) while improving UI responsiveness during workout sessions.🔹 What has been changed?
src/services/poseService.tssend(image)now creates anImageBitmapand posts it to the worker;onResults(callback)receives worker results unchanged.frameIdtracking and short promise-based backpressure to avoid flooding the worker.src/workers/poseLandmarkWorker.ts(new)@mediapipe/poseinside the worker (dynamically imported).ImageBitmapframes, draws toOffscreenCanvas(when available), runspose.send({ image }), and posts{ frameId, results }or{ frameId, error }back.src/workers/poseWorker.ts(angle/math worker) — landmarks are still posted to it as before.🔹 Why are these changes needed?
MediaPipe running on the main thread can block rendering and lower FPS on constrained devices. Offloading landmark extraction to a worker reduces main-thread CPU load and improves smoothness and responsiveness during live tracking.
🛠️ Type of Change
🧪 Testing
npm run dev.poseService.onResults()receivesresultsposted from the worker.send()does not queue indefinitely; worker timeouts/logs on stalled frames.npm run build) — blocked by unrelated TypeScript error in an orthogonal file (see notes).Notes:
createImageBitmap,OffscreenCanvas. Verify on target browsers and provide fallbacks if you need older-browser support.ImageBitmapdirectly topose.send()ifOffscreenCanvaspath fails.🌐 Browsers / Environments Tested
📷 Screenshots / Demo
Please attach:
resultsposts📋 Checklist
npm run devnpm run build(blocked by unrelated TS error; see Notes)🛠️ Implementation Notes (for reviewers)
poseServiceAPI is unchanged — components likeWorkoutScreendo not require updates.createImageBitmap()is used for efficient transfer; the worker receives theImageBitmapas a transferable to avoid copying large frame buffers.@mediapipe/poseand setslocateFileto the CDN path so WASM/assets load correctly in the worker context.frameIdto results; this can be refined to a strict frame queue map if reviewers prefer.