HTTP Range Requests 를 이용한 스프링 비디오 스트리밍
HTTP range requests
HTTP 범위 요청이란 HTTP 를 통해 일정한 부분을 서버에서 클라이언트로 보내는 것을 Accept하고 보내는 방법입니다. 범위를 알 수 있는 대형 미디어 파일을 나누어서 읽을 수 있습니다. 파일 다운로드 도중 일시정지와 다시 시작 기능에 유용한 점을 이용해 클라이언트에선 미디어파일을 재생, 일시정지, 다시시작을 가능하게 만들 수 있습니다. 예를 들어 4.5mb짜리 파일을 받겠다하면, 서버는 전체 범위(Content-Length
)와 일정 범위에 해당하는 파일을 bytes 로 내려보내줍니다.
참고
HTTP range requests
HTTP 범위 요청은 HTTP의 일정 부분만 서버에서 클라이언트로 보내도록 허락하는 것입니다. 부분 요청은 예를들어 대형 미디어나 파일 다운로드 도중 일시정지와 다시 시작 기능에 유용합니다.
developer.mozilla.org
HTML5 Video
Tag
HTML5 <video>
는 HTTP range request를 사용하는 대표적인 태그입니다. 비슷하게는 <audio>
도 존재합니다.<video>
속성 태그로는 <source>
와 <track>
를 사용할 수 있으며, <source>
는 데이터의 유형을 나타내고 <track>
은 재생 중 표시할 자막, 캡션 파일, 텍스트를 포함하는 파일을 표시할 수 있습니다.
참고
Spring Http Range Request 처리
Controller
@GetMapping("/videos/{name}")
public ResponseEntity<ResourceRegion> getVideo(@PathVariable String name,
@RequestHeader HttpHeaders headers) throws IOException {
log.info("getVideo");
UrlResource video = new UrlResource("classpath:" + videoLocation + "/" + name);
ResourceRegion region = resourceRegion(video, headers);
return ResponseEntity.status(HttpStatus.PARTIAL_CONTENT)
.contentType(MediaTypeFactory.getMediaType(video).orElse(MediaType.APPLICATION_OCTET_STREAM))
.body(region);
}
MediaTypeFactory
를 통해 파일의 content-type
의 유형을 가져오거나, 기본값으로 8 bit 스트림 유형인 APPLICATION_OCTET_STREAM
리턴합니다.
잘 보시면 body
에 ResouceRegion
객체를 담아 보내는데요. ResourceRegion
은 파일 객체의 Range
범위 만큼을 가져올 수 있는 스프링 코어 객체입니다.
ResourceRegion
private ResourceRegion resourceRegion(UrlResource video, HttpHeaders headers) throws IOException {
final long chunkSize = 1000000L;
long contentLength = video.contentLength();
HttpRange httpRange = headers.getRange().stream().findFirst().get();
if(httpRange != null) {
long start = httpRange.getRangeStart(contentLength);
long end = httpRange.getRangeEnd(contentLength);
long rangeLength = Long.min(chunkSize, end - start + 1);
return new ResourceRegion(video, start, rangeLength);
} else {
long rangeLength = Long.min(chunkSize, contentLength);
return new ResourceRegion(video, 0, rangeLength);
}
}
처음 요청(else
구문) 은 chunk 사이즈로 자른 시작값부터 청크 사이즈 만큼의 ResoureRegion
을 return
하고 그 다음 요청은 header
에 담긴 range
범위만큼 짤라 보냅니다.
- https://melgenek.github.io/spring-video-service
- https://developer.mozilla.org/ko/docs/Web/HTTP/Status/206
클라이언트 요청과 응답은 아래와 같습니다.
Request에서는 bytes=1557056 과 같이 범위 요청이 일어난 것을 볼 수 있습니다.
Response에서는 Content-Type 은 video/mp4로 리턴되고, 청크 싸이즈만큼 컨텐츠 사이즈가 정해진 것을 볼 수 있습니다.
서버는 각 요청을 지속적으로 받기 때문에 각기 다른 쓰레드로 처리된 것을 확인할 수 있습니다.
파일사이즈 만큼 클라이언트가 범위요청한 내역입니다. HTTP code 206 상태는 분활 스트리밍 상태에서 정상응답 코드입니다.
Github 주소
위 예제는 중요 파트만 잘라 놓았음으로 자세한 소스를 보고 싶으시다면 아래 링크를 참고하세요.
https://github.com/Derveljun/derveljun-video-streaming
Derveljun/derveljun-video-streaming
Contribute to Derveljun/derveljun-video-streaming development by creating an account on GitHub.
github.com