basst314 / ngx-webcam

A simple Angular webcam component / pure & minimal, no flash-fallback
https://basst314.github.io/ngx-webcam/?
MIT License
231 stars 104 forks source link

Camera does not stop even after component is destroyed #164

Open shaileshSwabhav opened 1 year ago

shaileshSwabhav commented 1 year ago

I am facing issue in angular v14. I have a component where in the ngx-webcam is used. I have hidden camera using css so that webcam is not visible on screen (video should not be visible to user as I am conducting there test). So when the user submits the test I am destroying the component and redirecting to other route. But the camera indicator does switch off.

component.html
<div class="hidden">
  <webcam [height]="400" [width]="400" [trigger]="triggerObservable" (imageCapture)="handleImage($event)"
    *ngIf="showWebcam" [allowCameraSwitch]="allowCameraSwitch" [switchCamera]="nextWebcamObservable" [imageQuality]="1"
    (initError)="handleInitError($event)" mirrorImage="never">
  </webcam>
</div>

component.css
.hidden {
  display: none;
}

I have used showWebcam flag and when componet is destroyed I have toggled it to false so that ngx-webcam component is destroyed.

ngOnDestroy(): void {
  this.showWebcam = false
}

I need to know if there is something wrong with the code or is there something that I am missing.

Thank you!

pul87 commented 1 year ago

Hi @shaileshSwabhav I have the same issue, did you find a way to handle it?

Thank you!

pete-mcwilliams commented 1 year ago

Problem is the way the library is written, if you stop the component before it fully initialises (_video.play() is async and should be treated as a promise) it will continue to initialise after the close on destroy.

I get the feeling the library is not being maintained.

Will-at-FreedomDev commented 3 months ago

I had the same issue and believe I've come across a workaround.

Implement OnDestroy, assign the WebcamComponent and the video element to private properties, and in ngOnDestroy, pause and stop the media stream.

In my limited testing, it seems to work. We only use this component on one single page, but if we were going to reuse it I'd probably wrap the WebcamComponent in custom component to do this.


  private _webcamComponent: WebcamComponent;
  private _webcamVideoElement: HTMLVideoElement;

  @ViewChild(WebcamComponent) set webcamComponent(webcamComponent: WebcamComponent) {
    if (!webcamComponent && this._webcamComponent) {
      this.disableWebcam(); // if the webcam is destroyed via ngIf or other means then disable it here as well.
    }

    this._webcamComponent = webcamComponent;
    this._webcamVideoElement = webcamComponent?.nativeVideoElement?.nativeElement;
  }

  ngOnDestroy(): void {
    this.disableWebcam();
  }

  private disableWebcam() {
    if (this._webcamComponent) {
      if (this._webcamVideoElement) {
        this._webcamVideoElement.pause();
        this._webcamVideoElement.srcObject = null;
      }

      // hack: mediaStream is private, but we need to manually destroy it because it isn't being handled by the component;
      const mediaStream: MediaStream = (this._webcamComponent as any).mediaStream;
      mediaStream?.getTracks().forEach(track => track.stop());

      this._webcamComponent = null;
    }
  }
pete-mcwilliams commented 3 months ago

I suspect if your OnDestroy runs before the camera has completed initialising it will still be active after the OnDestroy completes, it's a race condition because the _video.play() should be treated as async by the library and notify you.

willherr commented 3 months ago

Thanks for the comment. It's probably not a robust workaround, but probably catches the majority of use cases. I'll update this thread if I have any obvious issues or updates to the workaround.