ffmpeg

hrj2233·2021년 11월 23일
1

webassembly

목록 보기
1/1

webm에서 mp4로 변환할거임.
왜냐하면 모든 기기들이 webm을 이해하지는 못하기 때문.
mp4는 모두 이해할 수 있음.
비디오의 썸네일도 추출할거임.
ffmpeg는 비디오나 오디오 같은, 어떤 종류의 미디어 파일들을 다룰 수가 있음.
비디오를 압축하거나 비디오 포맷을 변환해야 하거나 비디오에서 오디오를 추출하고 싶거나
비디오에서 스크린샷을 찍고 싶거나 등등 할 수 있음.
웹 어셈블리도 이용할 거임.
웹어셈블리는 개방형 표준. 기본적으로 웹사이트가 매우 빠른 코드를 실행 할 수 있게 해줌.
유튜브는 업로된 비디오를 그들의 비싼 서버에서 변환할 거임.
ffmpeg.wasm으로 사용자의 브라우저에서 비디오를 변환할 거임.
즉 사용자(컴퓨터)의 처리 능력을 사용할 거임.

npm install @ffmpeg/ffmpeg @ffmpeg/core

import { createFFmpeg, fetchFile } from '@ffmpeg/ffmpeg';

const actionBtn = document.getElementById('actionBtn');
const video = document.getElementById('preview');

let stream;
let recorder;
let videoFile;

const files = {
  input: 'recording.webm',
  output: 'output.mp4',
  thumb: 'thumbnail.jpg',
};

const downloadFile = (fileUrl, fileName) => {
  const a = document.createElement('a');
  a.href = fileUrl;
  // download는 url을 저장하게 해줌.
  a.download = fileName;
  document.body.appendChild(a);
  a.click();
};

const handleDownload = async () => {
  actionBtn.removeEventListener('click', handleDownload);

  actionBtn.innerText = 'Transcoding...';

  actionBtn.disabled = true;

  const ffmpeg = createFFmpeg({
    log: true,
    corePath: '/static/ffmpeg-core.js',
  });

  // load를 await하는 이유는 사용자가 소프트웨어를 사용할 것이기 때문.
  await ffmpeg.load();

  // writeFile은 ffmpeg의 가상의 세계에 파일을 생성해줌.
  ffmpeg.FS('writeFile', files.input, await fetchFile(videoFile));

  // -i는 input, 인풋:recording.webm, -r은 rate, 60은 영상 초당 60프레임으로 인코딩, 아웃풋:output.mp4
  await ffmpeg.run('-i', files.input, '-r', '60', files.output);

  // -ss는 영상의 특정 시간대로 갈 수 있게 해줌.
  // thumbnail.jpg은 파일시스템(FS)의 메모리에 만들어짐.
  // 영상의 1초로 가서 1장의 스크린샷을 찍음.
  await ffmpeg.run(
    '-i',
    files.input,
    '-ss',
    '00:00:01',
    '-frames:v',
    '1',
    files.thumb
  );

  // readFile의 return 값은 Uint8Array(양의정수 배열)
  // Uint8Array(양의정수 배열)은 수많은 숫자들로 표현된 자바스크립트 방식의 파일
  const mp4File = ffmpeg.FS('readFile', files.output);
  const thumbFile = ffmpeg.FS('readFile', files.thumb);

  //blob은 자바스크립트 세계의 파일 같은 거(binary 정보를 가지고 있는 파일)
  //blob은 immutable, raw data인 파일과 같은 객체
  //ArrayBuffer는 raw binary data를 나타내는 object
  //binary data를 사용하고 싶다면 buffer를 사용해야 함.
  //binary data로 mp4 파일 만듬
  const mp4Blob = new Blob([mp4File.buffer], { type: 'video/mp4' });
  const thumbBlob = new Blob([thumbFile.buffer], { type: 'image/jpg' });

  const mp4Url = URL.createObjectURL(mp4Blob);
  const thumbUrl = URL.createObjectURL(thumbBlob);

  downloadFile(mp4Url, 'MyRecording.mp4');
  downloadFile(thumbUrl, 'MyThumbnail.jpg');

  // 브라우저가 계속 가지고 있으면 속도에 문제가 되니까 메모리에서 삭제.
  ffmpeg.FS('unlink', files.input);
  ffmpeg.FS('unlink', files.output);
  ffmpeg.FS('unlink', files.thumb);

  URL.revokeObjectURL(mp4Url);
  URL.revokeObjectURL(thumbUrl);
  URL.revokeObjectURL(videoFile);

  actionBtn.disabled = false;
  actionBtn.innerText = 'Record Again';
  actionBtn.addEventListener('click', handleStart);
};

const handleStart = async () => {
  actionBtn.innerText = 'Recording';
  actionBtn.disabled = true;
  actionBtn.removeEventListener('click', handleStart);

  // MediaRecorder는 말 그대로 녹화해줄 수 있게 도와줌.
  recorder = new MediaRecorder(stream, { mimeType: 'video/webm' });
  // ondataavailable는 이벤트 핸들러
  recorder.ondataavailable = (e) => {
    // createObjectURL은 브라우저 메모리에서만 가능한 URL을 만들어줌.
    // 웹사이트 상에 존재하는 URL처럼 보이지만 실제로는 없음.
    // 파일을 가리키고 있는 url이라고 생각하면 됨.
    // 브라우저가 그 파일에 접근할 수 있는 url을 주는거.
    videoFile = URL.createObjectURL(event.data);
    video.srcObject = null;
    video.src = videoFile;
    video.loop = true;
    video.play();
    actionBtn.innerText = 'Download';
    actionBtn.disabled = false;
    actionBtn.addEventListener('click', handleDownload);
  };
  recorder.start();
  setTimeout(() => {
    // stop 함수는 종료후 ondataavailable가 실행되게 만들어짐.
    recorder.stop();
  }, 5000);
};

const init = async () => {
  // 사용자의 navigator에서 카메라와 오디오를 가져다줌.
  stream = await navigator.mediaDevices.getUserMedia({
    audio: false,
    video: {
      width: 1024,
      height: 576,
    },
  });
  // 비디오 미리보기 기능 구현.
  // srcObject는 MediaStream, MediaSource, Blob, File을 실행할때 video에 주는 무언가를 의미
  // url은 src, (MediaStream, MediaSource, Blob, File)은 srcObejct
  video.srcObject = stream;
  video.play();
};

init();

actionBtn.addEventListener('click', handleStart);

0개의 댓글