Video를 Range Stream이 아닌 그냥 파일 Stream으로 내보낼 경우 비디오 재생을 할 때 시간 이동이 안된다. 따라서 Range Stream 방식을 하려면
- http status가 206(Partial Content)일 것
- 헤더에 range가 필수로 있어야 할 것
- 브라우저가 range시작 값을 보내주는데 청크사이즈를 계산해서 start/end를 알려주고 보내줄 것
- 비디오 총 길이와 청크 사이즈도 필요하다.
을 갖추면 브라우저에서 비디오로 인식하고 알아서 제어한다.
먼저 브라우저의 비디오 요청 헤더를 보면 아래와 같다.
Chrome


Chrome의 경우는 Start는 있지만 End값이 없다. 서버에서 청크 크기만큼 짤라서 줘도 되고 비디오 전체를 줘도 지원한다는 얘기다.
Safari

Safari의 경우 첫번째 요청으로 Range: bytes=0-1 딱 1바이트만큼 요청을 한다. 1바이트보다 더 준다면 미디어 형식으로 인식하지 않고 재생이 안된다.

첫번째 요청을 잘 받으면 두번째 요청에 0부터 파일 크기만큼 요청을 한다.

Safari에서 바이트 범위의 End가 파일 크기만큼 요청을 하지만 서버에서 청크 크기만큼 짤라서 보내줘도 인식을 잘 한다. 요청한 바이트 범위보다 부족하다면 추가로 계속 요청을 하니 바이트 범위의 Start값에만 맞춰서 잘 주면 된다.
위 요구사항들에 맞춰서 코드를 작성하면 아래와 같다. Stream만 있으면 다운로드가 안돼서 기본적인 Get요청일 상황도 있으니 같이 작성해 준다.
typescript
return으로 new Response의 데이터로 fs로 만든 ReadStream을 내보내주면 된다.

타입스크립트라면 타입 에러가 날텐데 videoStream as any 까지 해주면 됨.
위 코드는 Chrome 113, macOS Safari 16.3, iOS Safari 16.3에서 테스트 했고 정상 작동한다.
Safari에서 재생이 안되는 경우가 있는데 VP9, AV1, … 등 비디오 코덱의 미지원 문제이다. 소리만 안나오는 경우도 브라우저의 오디오 코덱 미지원 때문이다.