유저가 업로드한 mp4 파일의 썸네일 추출하는 작업을 진행하며 알게 되었던 것들을 정리한 글이다.
createObjectURL, revokeObjectURL
썸네일을 추출하기 위해선 video element를 생성해 src를 유저가 업로드한 파일로 지정해야 했다.
이때 업로드한 파일에 접근 가능한 URL을 만들기 위해 URL.createObjectURL()와 URL.revokeObjectUrl()을 사용했다.
URL.createObjectURL()
URL.createObjectURL() 정적 메서드는 주어진 객체를 가리키는 URL을 DOMString으로 반환합니다. 해당 URL은 자신을 생성한 창의 document가 사라지면 함께 무효화됩니다. 객체 URL을 해제하려면 revokeObjectURL을 호출하세요.
URL.revokeObjectURL()
URL.createObjectURL() 정적 메서드는 주어진 객체를 가리키는 URL을 DOMString으로 반환합니다. 해당 URL은 자신을 생성한 창의 document가 사라지면 함께 무효화됩니다.
정리하자면 특정 객체를 가리키는 URL을 생성하고 싶을 때 URL.createObjectURL() 사용하고, 해당 URL 더이상 사용하지 않을 URL.createObjectURL()로 메모리에서 해제해 주는 것이다.
비디오 썸네일 추출하기
URL.createObjectURL()을 이용했으면 URL.revokeObjectURL()로 메모리에서 해제해줘야 한다는 건 알았다. 당시 링크의 예제를 참고했었는데, 이 예제는 img element를 사용한 예제로 load 이벤트 발생시 revoke 시켜주었다. 이 예제처럼 데이터가 로드된 후 해제하면 되겠거니 생각했다. video element에는 loadeddata라는 이벤트라는 게 있다는 걸 알고는 바로 코드를 짜기 시작했다.
당시 짰던 코드가 대략 이런 식으로 loadeddata event 발생 시 바로 URL.createObjectURL()를 실행했다.
const videoObjUrl = URL.createObjectURL(file);
video.setAttribute('src', videoObjUrl);
video.load();
video.addEventListener('loadeddata',
() => {
URL.revokeObjectURL(videoObjUrl);
video.currentTime = Math.min(video.duration, 0.1); //썸네일로 추출할 시간 설정
video.addEventListener('seeked',
() => {
... // 썸네일 생성 코드
}
})
이렇게 하면 될 줄 알았는데요…
썸네일도 생성되어 보이고 문제가 없는 줄만 알았다. 콘솔의 빨간 줄을 보기 전까진…
해당 메시지는 blob을 참조할 수 없다고 알려주고 있었다. 썸네일은 잘 표시되고 있는데? load 되고 revoke 시키는 건데 뭐가 문제인가? 왜 못 찾는 건데??
원인은 loadeddata 이벤트를 잘 모르고 써서였다.
HTMLMediaElement: loadeddata event
로드된 데이터 이벤트는 미디어의 현재 재생 위치에서 프레임 로딩이 완료될 때(대개 첫 번째 프레임) 발생합니다.
당시의 나는 loadeddata event가 발생했을 때는 이미 데이터가 다 로드된 상태라고 생각하고 있었다. (지금 보니 멋대로 착각했다는 말이 더 맞을 것 같다.) 문서를 보고 나서야 초반 프레임만 로딩됐을 때 URL을 메모리에서 해제해 버렸고, 그렇게 로딩이 다 되지 않은 상태에서 재생 위치를(currentTime을) 바꾸려고 했기에 에러가 발생했던 것이라는 걸 알았다. 로딩이 완료된 0.1초쯤의 프레임을 썸네일로 만들었으니 겉보기엔 정상적으로 동작했던 것이었다. currentTime을 5초로 설정하니 바로 썸네일이 추출되지 않았다.
그러니까 단순 loadeddata란 말을 제멋대로 믿고 코드를 짠 나의 탓… 작은 걸 쓰더라도 제대로 알고 써야 한다. (썸네일 추출이 되면서도 에러가 떴던 이유는 video의 currentTime을 지정할 때 발생하는 seeking 과정을 좀 더 찾아봐야 알 것 같다.)
추가로 궁금해서 video element를 document 내에 위치시키고 loadeddata event가 발생하자마자 revoke 시켰더니 영상 재생이 아주 잠깐 됐다가 더 이상 재생이 되지 않는 것을 확인할 수 있었다.
createObjectUrl, 이제 <video>에는 쓰지 않는다?
미디어 스트림 객체 URL
구 Media Source 명세에서는 <video> 요소에 스트림을 부착하려면 MediaStream (en-US)의 객체 URL을 생성했어야 했습니다. 이제 이런 과정은 필수가 아니며, 브라우저도 지원을 중단하고 있습니다.아직 미디어 요소에 createObjectURL()을 사용해 스트림을 부착하고 있다면, srcObject에 MediaStream을 직접 설정하도록 코드를 수정해야 합니다.
mdn 문서를 보며 이 글을 정리하다 이 부분을 뒤늦게 발견했는데 createObjectUrl 대신 srcObject 사용을 권장하는 내용이었다. 둘의 차이가 뭔지, 어떤 것이 더 좋은지는 후에 더 분석해볼 예정...
MediaStream 사용시에 해당하는 내용으로 위와 같이 사용할 땐 해당하지 않았다. 후속 정리글
참고 문서
URL.createObjectURL()
https://developer.mozilla.org/ko/docs/Web/API/URL/createObjectURL_static
URL.createObjectURL() - Web API | MDN
URL.createObjectURL() 정적 메서드는 주어진 객체를 가리키는 URL을 DOMString으로 반환합니다. 해당 URL은 자신을 생성한 창의 document가 사라지면 함께 무효화됩니다.
developer.mozilla.org
URL.revokeObjectURL()
https://developer.mozilla.org/ko/docs/Web/API/URL/revokeObjectURL_static