본문 바로가기

Web.d

[Javascript] 웹 브라우저에서 녹화하여 서버로 보내기 (MediaRecorder / Blob)

반응형

영상 파일을 이용하여 사람의 감정을 분석하는 ML을 구성하기 위해,

웹브라우저에서 비디오 데이터를 저장하고 서버로 보내야 했다.

 

[Fig 1] 웹페이지

 

[Fig 1]과 같이 사용자의 실시간 카메라 화면을 보여주고,

대화할 때의 영상을 녹화하여 저장하도록 한다.

 


구현할 기능

  • 웹브라우저에서 실시간 카메라 구현
  • 카메라에 찍히는 화면을 녹화(시작, 종료)
  • ( 녹화된 영상을 서버로 POST 설명 )

 


기능 구현

0. 프로세스

해당 프로세스는 다음과 같다

 

[Fig 2] 녹화본 저장 프로세스

 

  1. MediaStream Recording API로, 해당 stream을 가져온다
  2. MediaStream을 매개변수로, MediaRecorder 생성자를 호출한다
  3. MediaRecorder.ondataavailable 이벤트가 호출될 때마다 전달 받는 영상 데이터를 배열에 쌓는다
  4. 녹화를 중지하면, 모인 영상 데이터로 Blob을 생성하는 핸들러를 MediaRecorder.onstop 이벤트에 등록한다 / MediaRecorder.start() 를 호출해 녹화 시작 / MediaRecorder.stop() 을 호출해 녹화를 중지한다
  5. 4번에서 Blob으로 생성한 URL 데이터를 ajax를 이용해 서버로 POST 한다

 

1. 카메라로부터 입력 받기

녹화할 때 카메라의 화면을 바로 확인할 수 있도록 웹페이지에 출력하도록 구현한다.

(html video 태그로 해당 화면을 확인하도록 한다.)

 

MediaDevice.getUserMedia 메소드를 통해 유저의 카메라, 마이크 등을 기기로부터 입력 받을 수 있다.

-> https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia

사용자가 권한 요청을 수락하면, 비디오 트랙을 포함한 MediaStream을 전달하는 Promise 객체를 리턴한다.

 

//html
//<video class="user-video">

const videoTag = document.querySelector(".user-video");
let videoMediaStream = null;

navigator.mediaDevices
  .getUserMedia({ // constraints
    audio: false,
    video: {
      width: 360,
      height: 240,
    },
  })
  .then(function (mediaStream) {
    videoTag.srcObject = mediaStream;
    videoTag.onloadedmetadata = function() {
        videoTag.play();
      };
    videoMediaStream = mediaStream;
  };

 

해당 mediaStream을 확인하면 다음과 같이 확인된다 (퍼옴)

 

[Fig 3] console.log(mediaStream);

 

video 태그의 (HTMLVideoElement의) srcObject 속성을 통해 mediaStream을 가져올 수 있고,

메타데이터가 로드되는 onloadmeta 이벤트 호출 이후 video 태그의 play() 메소드를 호출하여 실시간으로 웹페이지에서 보여준다.

해당 mediaStream은 이후에 쓰이므로 videoMediaStream 변수에 넣어준다.

 

2~4. 녹화하여 저장하기

1) MediaStream의 매개변수MediaRecorder 생성자를 호출합니다.

-> https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder/MediaRecorder

생성자의 다른 매개변수mimeType을 설정하는데, 브라우저마다 지원하는 mimeType에는 차이가 있습니다. (mimeType을 설정하기 전에 MediaRecorder.isTypeSupported브라우저가 녹화를 지원하는 MIME type인지 확인할 수 있습니다.)


2) MediaRecorder.ondataavailable 이벤트가 호출될 때마다 전달받는 영상 데이터를 배열에 쌓도록 핸들러를 작성하여 등록합니다.

ondataavailable 이벤트는 데이터가 준비될 때마다 호출되며, 이벤트는 미디어 데이터를 data 속성으로 가집니다.

-> https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder/ondataavailable


3) 녹화를 중지하면 MediaRecorder.onstop 이벤트가 호출됩니다.

이벤트가 호출되었을 때 2번 과정에서 모인 영상 데이터로 Blob을 생성하는 핸들러 onstop 이벤트에 등록합니다.

-> https://developer.mozilla.org/ko/docs/Web/API/Blob

생성된 Blob을 매개변수로 URL.createObjectURL 메서드를 호출하면 URL로 활용할 수 있도록 string을 반환합니다.

반환받은 값은 이후 과정에서 서버 POST, 다운로드, 미리보기 기능 등에 사용됩니다.

코드에는 없지만 revokeObjectURL메소드를 호출하여 생성된 url이 참조하는 object가 할당된 메모리를 해제해야 합니다.

그렇지 않으면 메모리 누수 문제가 발생할 수 있습니다.


4) MediaRecorder.start() 를 호출하여 녹화 시작


5) 녹화 중지 버튼을 누르면 MediaRecorder.stop()을 호출 -> 녹화 중지하여 MediaRecorder.onstop 이벤트 호출

 

let videoRecorder = null;
let recordedVideoURL = null;
let videoBlob = null;

const VideoCaptureStart = () => {
  if(navigator.mediaDevices.getUserMedia) {
    console.log("video capture start");
    
    let videoData = [];
  
    // 1) MediaStream을 매개변수로 MediaRecorder 생성자를 호출
    // webm만?????
    videoRecorder = new MediaRecorder(videoMediaStream, {
      mimeType: "video/webm; codecs=vp9"
    });
  
    // 2) 전달받는 데이터를 처리하는 이벤트 핸들러 등록
    videoRecorder.ondataavailable = event => {
      if(event.data?.size > 0){
        videoData.push(event.data);
      }
    }
    
    // 3) 녹화 중지 이벤트 핸들러 등록
    videoRecorder.onstop = () => {
      videoBlob = new Blob(videoData, {type: "video/webm"});
      recordedVideoURL = window.URL.createObjectURL(videoBlob);
      
      // 이벤트 실행 시에 서버로 파일 POST
      sendAvi(videoBlob);
      console.log("video capture end");
    }
    
    // 4) 녹화 시작
    videoRecorder.start();
  }
};
const VideoCaptureEnd = () => {
  if(videoRecorder){
    // 5) 녹화 중지
    videoRecorder.stop();
    videoRecorder = null;

    // our final videoBlob
    // sendAvi(videoBlob);
    // -> 이벤트의 비동기로 인해 순서가 꼬이므로 이벤트 발생 시에 선언한다
  }
};

 

코드에서와 같이 URL로 받아와 생성한 Blob을 매개변수로,

avi 확장자로 POST하는 sendAvi 메소드를 호출한다

 

5. ajax를 이용해 서버로 POST 하기

$.ajax() 로 비동기식 Ajax를 이용하여 HTTP 요청을 전송한다.

-> https://developer.mozilla.org/ko/docs/Web/Guide/AJAX/Getting_Started

-> https://api.jquery.com/jquery.ajax/

 

const sendAvi = blob => {
  if (blob == null) return;
  
  let filename = new Date().toString() + ".avi";
  const file = new File([blob], filename);

  let fd = new FormData();
  fd.append("fname", filename);
  fd.append("file", file);

  $.ajax({
    url: "~~~url~~~",
    type: "POST",
    contentType: false,
    processData: false,
    data: fd,
    success: function (data, textStatus) {
      if (data != null) {
        setUserResponse(data);
        send(data);
      }
    },
    error: function (errorMessage) {
      setUserResponse("");
      console.log("Error" + errorMessage);
    },
  }).done(function (data) {
    console.log(data);
  });
}

 


마치며

다행히도 유투브 클론코딩과 같은 영상 녹화 기능에 대한 자료들이 많았다..

브라우저의 webRTC에 대해 좀 더 알 수 있었던 시간이였다

공부할 게 산더미 ..,

 

 


참고 자료

https://medium.com/watcha/%EC%9B%B9%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80%EC%97%90%EC%84%9C-%EB%8F%99%EC%9E%91%ED%95%98%EB%8A%94-%EB%85%B9%ED%99%94%EC%95%B1-%EB%A7%8C%EB%93%A4%EA%B8%B0-70142ce28994

 

웹브라우저에서 동작하는 녹화앱 만들기

웹브라우저에서 녹화 기능을 구현하는 방법을 간단한 예제와 함께 소개합니다.

medium.com

https://developers.google.com/web/updates/2016/01/mediarecorder

 

Record Audio and Video with MediaRecorder  |  Web  |  Google Developers

The MediaRecorder API enables you to record audio and video from a web app. It's available now in Firefox and in Chrome for Android and desktop.

developers.google.com

https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia

 

MediaDevices.getUserMedia() - Web APIs | MDN

The MediaDevices.getUserMedia() method prompts the user for permission to use a media input which produces a MediaStream with tracks containing the requested types of media.

developer.mozilla.org

https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder/MediaRecorder

 

MediaRecorder() - Web APIs | MDN

The MediaRecorder() constructor creates a new MediaRecorder object that will record a specified MediaStream.

developer.mozilla.org

https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder/ondataavailable

 

MediaRecorder.ondataavailable - Web APIs | MDN

The MediaRecorder.ondataavailable event handler (part of the MediaStream Recording API) handles the dataavailable event, letting you run code in response to Blob data being made available for use.

developer.mozilla.org

https://developer.mozilla.org/ko/docs/Web/API/Blob

 

Blob - Web API | MDN

Blob 객체는 파일류의 불변하는 미가공 데이터를 나타냅니다.

developer.mozilla.org

 https://developer.mozilla.org/ko/docs/Web/Guide/AJAX/Getting_Started

 

Ajax 시작하기 - 웹 개발자 안내서 | MDN

본 문서는 AJAX의 기본을 익힐수 있도록 해주며, 두 가지 간단한 훈련용 예제를 제공합니다.

developer.mozilla.org

 https://api.jquery.com/jquery.ajax/

 

jQuery.ajax() | jQuery API Documentation

Description: Perform an asynchronous HTTP (Ajax) request. The $.ajax() function underlies all Ajax requests sent by jQuery. It is often unnecessary to directly call this function, as several higher-level alternatives like $.get() and .load() are available

api.jquery.com


- 211027) sendAvi 함수 위치 변경

반응형