화면 캡처 API 소개 - 브라우저에서 QR 코드 검색


표지 사진 작성자Lianhao Qu

앞말


이 작은 글에서 우리는 화면 캡처 API를 토론할 것이다.이를 "신규"API라고 부르기는 어렵다. 규범은 2014년으로 거슬러 올라갈 수 있기 때문이다.그러나 여전히 브라우저 지원이 부족하더라도 개인 프로젝트에서 실험을 하거나 사용하는 것은 흥미로운 일인 것 같다. 왜냐하면 개인 프로젝트에서 각종 브라우저를 지원하는 것은 요구가 아니기 때문이다.
다음은 학습을 시작하기 위한 tldr 링크입니다.
  • Full spec
  • MDN Usage Guide
  • Demo of what we are about to implement
  • 링크가 작동을 멈추면 최종 제품이 어떻게 작동해야 합니까?

    건설을 시작합시다.

    왜?


    최근에 나는 QR코드를 사용하는 특정 인터넷 응용 프로그램에 대한 생각이 생겼다.물리적 세계에서 복잡한 데이터를 전송하기에 적합하지만, 모바일 장치를 가리킬 수 있지만, 데스크톱 장치의 화면에 놓고 이 장치에 정보를 인코딩해야 할 때 쉽게 사용할 수 없습니다.이미지나 캡처를 저장하고 식별 서비스를 찾아 캡처를 업로드해야 합니다.심심했어
    일부 공급업체, 예를 들어 1Password는 데스크톱에서 QR코드를 사용하는 방법을 찾았는데 이런 방법은 재미있고 간단하며 좀 신기하다.만약 당신이 그것에 익숙하지 않다면, 그들은 투명한 모드 창이 화면에 나타날 것이다.너는 그것을 너의 QR코드에 끌고 가서 펑 소리를 내라!계정을 추가했습니다!아니면 다른 거.이것은 그것의 모습이다.

    아주 깔끔해요.그러나 브라우저 창에 그 밑에 있는 어떤 것도 포착할 수 없습니다.아니면 저희가 할 수 있을까요?

    getDisplayMedia 입력


    응, 좀 닮았어.화면 캡처 API와 유일한 구성원getDisplayMedia이 역할을 하는 곳입니다.그것은 약간 getUserMedia 같지만 카메라가 아닌 사용자의 화면에 사용된다.불행하게도 브라우저는 이 API에 대한 지원이 널리 보급되지 않았지만, MDN에 따르면 Firefox, Chrome, Edge(비표준적인 위치를 가진 방법) + Edge Mobile과... Opera for Android는 화면 캡처 API를 지원한다.
    이 대배우로 구성된 회사에는 특이한 모바일 사용자 에이전트가 있다.
    이제 API 자체는 매우 간단합니다.작동 방식은 getUserMedia 과 같지만 화면에서 비디오 피드백을 캡처하거나 정의된 디스플레이 표면 중 하나에서 비디오 피드백을 캡처할 수 있습니다.
  • 모니터(전체 화면)
  • 특정 응용 프로그램의 창 또는 모든 창
  • 문서 형식의 브라우저입니다.Chrome에서는 개별 탭을 엽니다.FF에서 이 옵션이 부족한 것 같습니다
  • 이것은 우리가 그 중 어느 하나에서 동영상을 캡처하여 요약하고 필요에 따라 분석할 수 있다는 것을 의미한다.실시간 텍스트 식별과 수정, 구글 번역 카메라와 같은 일을 하는 것은 아마도 다른 멋진 일일 것이다.나는 발명 부분을 독자에게 남겨 둘 것이다.가장 중요한 것은 많은 다른 브라우저 API와 달리 우리는 브라우저 내부에 완전히 국한되지 않았다는 것이다.

    연결


    그래서 우리는 실시간 화면 포착의 힘을 장악했다.우리는 어떻게 그것을 이용합니까?
    우리는 <video><canvas> 및 일부 JS 풀을 사용할 것이다.이 절차는 높은 수준에서 다음과 같습니다.
  • 유입<video>
  • 설정된 새로 고침률을 사용하여 프레임 <video> 에서 <canvas> 으로 그리기
  • 사용ImageData으로부터<canvas>캡처getImageData
  • 이것은 좀 이상하게 들릴 수도 있지만, 내가 알기로는 이것은 매우 유행하는 방법이며, 우리의 다른 친구들getUserMedia과 함께 카메라에서 동영상을 얻는 데도 자주 쓰인다.
    모든 시작 흐름과 프레임 캡처를 생략하는 설정 코드 - 의미 있는 부분은 다음과 같습니다.
    async function run() {
      const video = document.createElement('video');
      const canvas = document.createElement('canvas');
      const context = canvas.getContext('2d');
    
      const displayMediaOptions = {
        video: {
          cursor: "never"
        },
        audio: false
      }
    
      video.srcObject = await navigator.mediaDevices.getDisplayMedia(displayMediaOptions);
    
      const videoTrack = video.srcObject.getVideoTracks()[0];
      const { height, width } = videoTrack.getSettings();
    
      context.drawImage(video, 0, 0, width, height);
      return context.getImageData(0, 0, width, height);
    }
    
    await run();
    
    앞에서 말한 바와 같이 우리는 여기에서 <video><canvas> 을 창설하고 CanvasRenderingContext2D 을 얻는다.
    그리고 포획 요청을 정의합니다constraints.그리 많지 않다.우리는 커서도 필요 없고, 오디오도 필요 없다.비록 본문을 작성할 때, 화면 포획 중의 오디오 포획을 지원하는 사람은 아무도 없었다.
    그 후에 우리는 결과MediaStream를 우리의 <video>에 연결할 것이다.getDisplayMedia 약속이 되돌아오기 때문에 예시 코드에서 기다리십시오.
    마지막으로 우리는 영상 궤적에서 실제 영상 전송 차원을 얻어 프레임을 캔버스에 그리고 이를 ImageData로 추출한다.
    현재, 실제 장면에서, 특정한 데이터가 프레임에 나타나거나 특정한 데이터를 연속적으로 조작하기를 기다리는 것이 아니라, 순환 중에 프레임을 처리하기를 원할 수도 있습니다.경고가 몇 개 있습니다.
    '배경에서 어떤 일을 연속적으로 순환 처리한다'고 언급했을 때 가장 먼저 떠오르는 것은 requestAnimationFrame일 것이다.불행히도 이런 상황에서는 올바른 선택이 아니다.봐라, 탭이 백그라운드에 들어가면 브라우저는 종종 rAF 순환을 멈추는데, 이것이 바로 모든 작업이 발생하는 곳이다.
    따라서 우리는 rAF가 아닌 낡은 setInterval 을 사용할 것이다.문제가 하나 더 있어도배경의 setInterval 순환 도로는 1000ms당 한 번 이상 운행할 수 없습니다.그러나 나는 이것이 대다수 목적에 있어서 이미 충분하다고 생각한다.
    현재 짐작한 바와 같이 프레임은 모든 처리 파이프로 보낼 수 있습니다.우리의 사례 중 - tojsQR.그것은 사용하기에 매우 간단하다. ImageData, 너비, 높이만 제공하면 이미지에 QR코드가 있으면 식별 데이터가 있는 JS 대상을 얻을 수 있다.그래서 당신은 간단한
    const imageData = await run();
    const code = jsQR(imageData.data, streamWidth, streamHeight);
    
    다 끝났어!

    그것을 싸라


    나는 그것을 npm 모듈로 포장하면 매우 좋을 것이라고 생각한다. 이렇게 하면 스스로 모든 물건을 설치할 필요가 없다.이제 간단합니다. 데이터를 순환에서 제공하는 리셋으로 보내고, 포획 사이의 간격을 설정하는 추가 옵션만 필요합니다.확장 기능이 의미가 있는지 확인해 보겠습니다.
    가방 이름은 stream-display: NPM | Github 입니다.
    핵심 모듈에는 해상도가 포함되어 있지 않으므로 직접 가져가십시오.이 라이브러리를 사용하면 작성해야 할 모든 코드는 다음과 같은 목적을 달성할 수 있습니다.
    const callback = imageData => {...} // do whatever with those images
    const capture = new StreamDisplay(callback); // specify where the ImageData will go
    await capture.startCapture(); // when ready
    capture.stopCapture(); // when done
    
    내가 창작한 이 글의 배후를 보여주기 위해this little demo.CodePen 형식의 빠른 실험도 제공한다.그것은 상술한 모듈을 사용한다.

    테스트에 대한 설명


    이 코드들로 라이브러리를 만들어서 이 API에 의존하는 코드를 어떻게 테스트해야 할지 생각하게 했다.
    나는 단지 몇 개의 작은 테스트를 실행하기 위해 50MB의 헤드 없는 크롬을 다운로드하는 것을 피하고 최종적으로 tape 을 사용하고 모든 내용을 수동으로 시뮬레이션하고 싶다.처음에는 무미건조해 보일 수도 있지만, 결국은 다음과 같은 몇 가지를 비웃기만 하면 된다.
  • document 및 DOM 요소.사용했습니다 jsdom
  • jsdom에서 실현되지 않은 방법 - HTMLMediaElement#play, HTMLCanvasElement#getContextnavigator.mediaDevices#getDisplayMedia
  • 시공 연속체.나는 엔진 뚜껑 아래에서 useFakeTimers 를 호출했다.그것은 lolex, setInterval 및 기타 시간 기반의 모든 대체품을 갖추고 있어 신기한 시간 리모컨을 통해 정확하게 제어할 수 있다.밀리초를 뛰어넘고 다음 타이머로 넘어가고 다음 똑딱거리는 소리로 넘어가면 너는 말할 수 있다.단, 경고가 필요합니다. 만약 jsdom 전에 사용자 정의 타이머를 사용한다면, jsdom가 시간을 바탕으로 어떤 일을 초기화하려고 하기 때문에 전 세계가 동결될 것입니다.
  • 나는 추적이 필요한 모든 허위 방법을 실현하기 위해sinon을 사용한다.다른 방법은 일반 JS 함수를 사용합니다.물론, 당신은 이미 가장 익숙한 도구를 사용할 수 있습니다.최종 결과는 도서관의gitrepo에서 볼 수 있다.그것은 보기 싫을 수도 있지만, 일하는 것 같으니, 너에게 생각을 주어야 한다.

    서양 농민 결론


    그것은 본고에서 처음에 보여준 데스크톱 해결 방안만큼 우아하지는 않지만, 나는 웹이 최종적으로 실현될 것이라고 믿는다.브라우저가 창을 통해 제대로 볼 수 있기를 바랄 뿐이다. 브라우저는 적당한 보호를 받을 것이며, 이 기능들을 완전히 제어할 것이다.하지만 스크린 공유 API를 통해 화면을 공유할 때마다 누군가가 화면의 모든 내용을 해석할 수 있기 때문에 받아들일 수 있는 범위를 넘어서는 내용을 공유하지 말고 암호 관리자에서 멀리 떨어지지 않도록 주의하세요.
    어쨌든, 나는 네가 오늘 새로운 기교를 배웠으면 좋겠다.만약 당신이 다른 생각이 있다면, 이것은 응용할 수 있습니다. - 공유하세요.다음까지!

    좋은 웹페이지 즐겨찾기