Skip to content

Commit 8fc5682

Browse files
committed
feat(mediapipe): update camera sdk to use web worker timer instead setTimeout when page inactive
1 parent 54af6dc commit 8fc5682

File tree

3 files changed

+59
-33
lines changed

3 files changed

+59
-33
lines changed

packages/paddlejs-mediapipe/camera/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ const option = {
2121
height?: number,
2222
// mirror or not
2323
mirror?: boolean,
24+
// if enable capture stream when page hidden, default is false
25+
enableOnInactiveState?: boolean,
2426
// canvas DOM
2527
targetCanvas?: HTMLCanvasElement,
2628
// video rendered successfully

packages/paddlejs-mediapipe/camera/README_cn.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ const option = {
2121
height?: number,
2222
// 是否镜像
2323
mirror?: boolean,
24+
// 当页面处于非激活态时,是否继续捕获 video frame,默认为 false
25+
enableOnInactiveState?: boolean,
2426
// 目标canvas DOM对象
2527
targetCanvas?: HTMLCanvasElement,
2628
// 视频流渲染成功

packages/paddlejs-mediapipe/camera/src/index.ts

Lines changed: 55 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,13 @@
33
* @author xxx
44
*/
55

6+
import 'hacktimer';
7+
68
interface cameraOption {
79
width?: number;
810
height?: number;
911
mirror?: boolean;
12+
enableOnInactiveState?: boolean;
1013
targetCanvas?: HTMLCanvasElement;
1114
onSuccess?: () => void;
1215
onError?: () => void;
@@ -15,7 +18,6 @@ interface cameraOption {
1518
switchError?: () => void;
1619
videoLoaded?: () => void;
1720
}
18-
1921
export default class Camera {
2022
constructor(videoElement: HTMLVideoElement, opt: Partial<cameraOption> = {}) {
2123
this.video = videoElement;
@@ -24,13 +26,14 @@ export default class Camera {
2426
this.visible = true;
2527
this.isCameraRunning = true;
2628
this.initVideoStream();
27-
this.registerVisiblityEvent();
29+
this.options.enableOnInactiveState && this.registerVisiblityEvent();
2830
}
2931

3032
private noop = () => {};
3133
/** 默认配置对象 */
3234
private defaultOption: cameraOption = {
3335
mirror: false,
36+
enableOnInactiveState: false,
3437
targetCanvas: null,
3538
onSuccess: this.noop,
3639
onError: this.noop,
@@ -170,43 +173,57 @@ export default class Camera {
170173
}
171174

172175
private videoRequestAnimationFrame() {
173-
const drawImage = () => {
174-
if (!this.isCameraRunning) {
175-
return;
176-
}
177-
if (this.context) {
178-
this.context.drawImage(this.video, 0, 0, this.video.width, this.video.height);
179-
}
180-
this.options.onFrame(this.video);
181-
if (this.visible) {
182-
this.requestAnimationId = requestAnimationFrame(drawImage);
183-
return;
184-
}
185-
this.setTimeoutHandler = setTimeout(() => {
186-
drawImage();
187-
}, 50);
188-
};
189-
drawImage();
176+
if (this.visible) {
177+
this.drawImageRAF();
178+
return;
179+
}
180+
this.drawImageST();
181+
}
182+
183+
private async drawImageRAF() {
184+
if (!this.isCameraRunning) {
185+
cancelAnimationFrame(this.requestAnimationId);
186+
this.requestAnimationId = null;
187+
return;
188+
}
189+
if (this.context) {
190+
this.context.drawImage(this.video, 0, 0, this.video.width, this.video.height);
191+
}
192+
await this.options.onFrame(this.video);
193+
this.requestAnimationId = requestAnimationFrame(() => {
194+
this.drawImageRAF();
195+
});
190196
}
191197

198+
private drawImageST() {
199+
if (!this.isCameraRunning) {
200+
clearTimeout(this.setTimeoutHandler);
201+
this.setTimeoutHandler = null;
202+
return;
203+
}
204+
if (this.context) {
205+
this.context.drawImage(this.video, 0, 0, this.video.width, this.video.height);
206+
}
207+
this.options.onFrame(this.video);
208+
this.setTimeoutHandler = setTimeout(() => {
209+
this.drawImageST();
210+
}, 50);
211+
};
212+
192213
public start() {
193214
this.video && this.video.play();
194215
if (this.requestAnimationId || this.setTimeoutHandler) {
195216
return;
196217
}
197218
this.isCameraRunning = true;
219+
this.visible = true;
198220
this.videoRequestAnimationFrame();
199221
}
200222

201223
public pause() {
202224
this.video && this.video.pause();
203225
this.isCameraRunning = false;
204-
if (this.requestAnimationId) {
205-
cancelAnimationFrame(this.requestAnimationId);
206-
clearTimeout(this.setTimeoutHandler);
207-
this.requestAnimationId = null;
208-
this.setTimeoutHandler = null;
209-
}
226+
this.cancelDrawImage();
210227
}
211228

212229
public switchCameras() {
@@ -226,20 +243,25 @@ export default class Camera {
226243
this.handleStream();
227244
}
228245

246+
private cancelDrawImage() {
247+
clearTimeout(this.setTimeoutHandler);
248+
this.setTimeoutHandler = null;
249+
cancelAnimationFrame(this.requestAnimationId);
250+
this.requestAnimationId = null;
251+
}
252+
229253
private registerVisiblityEvent() {
230254
document.addEventListener('visibilitychange', () => {
231255
if (!this.isCameraRunning) {
232256
return;
233257
}
234-
if (document.visibilityState === 'visible') {
235-
this.visible = true;
236-
clearTimeout(this.setTimeoutHandler);
237-
this.setTimeoutHandler = null;
238-
}
239-
else if (document.visibilityState === 'hidden') {
240-
this.visible = false;
241-
this.videoRequestAnimationFrame();
258+
this.visible = document.visibilityState === 'visible';
259+
this.cancelDrawImage();
260+
if (this.visible) {
261+
this.drawImageRAF();
262+
return;
242263
}
264+
this.drawImageST();
243265
});
244266
}
245267
}

0 commit comments

Comments
 (0)