이젠 영상을 재생할 수 있는 video 원소를 추가할 때가 되었다. index.html에 id가 stream인 video 원소를 추가한다.

1
2
3
4
5
6
...
<body>
<h1>MSE-remind</h1>
<video id="stream" muted></video>
</body>
...

muted를 설정한 이유는 autoplay 정책으로 인해 muted 설정이 되어 있지 않으면 재생이 되지 않는 경우가 있다. 어차피 지금은 비디오만 재생하는 것에 초점을 맞추고 있기 때문에 muted로 기본 설정한다. 정책에 대한 내용은 다음 링크를 참조한다.

https://developer.chrome.com/blog/autoplay?hl=ko

Chrome의 자동재생 정책  |  Blog  |  Chrome for Developers이미지 썸네일 삭제

Chrome의 자동재생 정책  |  Blog  |  Chrome for Developers

Chrome의 새로운 자동재생 정책을 통해 우수한 사용자 경험을 위한 권장사항을 알아보세요.

developer.chrome.com

video 원소에 영상을 재생하기 위해서는 src 프로퍼티를 설정해야 한다. 일반적으로는 로컬 파일이나 URL 정보가 들어가지만, 여기에서는 특별한 타입을 사용하게 된다. MediaSource의 인스턴스를 기반으로 만들어진 오브젝트 URL이다. 이것을 이용하면 미디어 소스를 통해 입력되는 스트림을 video 원소를 통해 재생을 할 수 있게 된다. 이 기능은 Media Source Extensions(MSE)라는 API를 통해 지원된다.

미디어 소스는 미디어 스트림의 조각을 나타내는 추상적인 개념이다. 미디어 데이터, 타임라인, 코덱 정보 등의 요소로 구성이 된다.

시선을 main.js로 옮기자. 앞서 index.html에 추가한 video 원소를 가져오고, MediaSource 인스턴스를 하나 생성하고, 이를 이용해서 video 원소의 src 프로퍼티를 설정한다.

1
2
3
const videoEl = document.getElementById("stream");
const mediaSource = new MediaSource();
videoEl.src = URL.createObjectURL(mediaSource);

MediaSource 인스턴스가 생성되고 video 원소에 연결이 되면, sourceopen이라는 이벤트가 발생한다. 이 시점에 비디오 스트림을 입력받을 소스 버퍼를 미디어 소스에 추가해 준다. 이것의 타입이 SourceBuffer이다. SourceBuffer를 추가해 줄 때는 mime 타입을 인자로 넣어서 어떤 미디어용으로 사용할 것인지를 알려줘야 한다. 비디오 형태고 h.264형태의 데이터를 입력해 줄 것이다. 라이브 영상 데이터이기 때문에 순서대로 입력될 것이며, 이 SourceBuffer의 mode는 “sequence”로 설정해 주면 된다.

이전에 웹소켓 접속을 위해 io() 함수를 호출했던 위치도 여기로 옮기도록 하자. 접속을 요청하면서 초기화 프레그먼트를 요청하는 명령인 start message를 보내도록 한다.

1
2
3
4
5
6
7
mediaSource.addEventListener("sourceopen", function (e) {
console.log("sourceopen: " + mediaSource.readyState);
buffer = mediaSource.addSourceBuffer(mime);
buffer.mode = "sequence";
socket = io(null, { transports: ["websocket"] });
socket.emit("message", "start");
});

이번엔 서버 코드인 index.js로 이동하자. 클라이언트에서 start message를 보내도록 했기 때문에 이에 대한 처리가 필요하다. socket의 message 이벤트에 대해 switch로 작성해서 start 처리를 한다. start 메시지를 받았을 때는 mp4frag를 통해 검출된 초기화 프레그먼트를 보내주도록 한다. 없다면 에러를 메시지를 보낸다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
io.on("connection", (socket) => {
console.log("connection");

function start() {
if (mp4frag.initialization) {
socket.emit("segment", mp4frag.initialization);
} else {
socket.emit("message", "init segment not ready yet, reload page");
}
}

socket.on("message", (msg) => {
switch (msg) {
case "start":
start();
break;
}
});
});

이제 추가로 해야 할 작업은 서버에서 보내주는 초기화 프레그먼트와 세그먼트 데이터를 받아서 소스 버퍼에 추가해 주는 것이 필요하다. 이 내용에 대한 설명은 다음 글에서 계속하고자 한다.

작업한 코드는 다음 위치에 업데이트했다.

https://github.com/moonyl/mse-remind/tree/v0.4.0

GitHub - moonyl/mse-remind at v0.4.0이미지 썸네일 삭제

GitHub - moonyl/mse-remind at v0.4.0

Contribute to moonyl/mse-remind development by creating an account on GitHub.

github.com