-
Notifications
You must be signed in to change notification settings - Fork 27
WebCodecs
<↗>로 표시한 부분은 다소 기술적인 내용을 포함하고 있습니다. 부담스럽거나 궁금하지 않다면 <↘> 표시가 나오는 부분으로 건너뛰세요.
여러 장의 이미지를 빠르게 전환하며 보면 그것은 마치 움직이는 것처럼 보인다. 즉, timestamp
또는 framerate
를 가지는 이미지의 집합체를 비디오라고 부를 수 있겠다. 픽셀마다 RGB(+A)값을 가지는 이미지는 용량이 굉장히 커서, 이를 그대로 비디오에 저장하면 비디오의 용량 또한 굉장히 커진다.
용량이 크면 저장과 전송에 있어 불리하기 때문에, 이미지들을 어떤 과정을 거쳐서 압축(인코딩)한 상태로 가지고 있다가, 재생할 때 압축을 풀어(디코딩)서 보는 방법이 널리 사용되고 있다. 인코딩을 수행하는 것을 인코더, 디코딩을 수행하는 것을 디코더라고 하며, 이 둘을 보통 같이 사용하기 때문에 (en)co(der) + dec(oder)을 따서 코덱(codec
)이라고 부른다.
동영상 압축 기법은 크게 프레임 간의 상관관계를 이용하는 것과 프레임 내 공간적인 상관관계를 이용하는 것으로 나눠 볼 수 있다. 아래는 H264 코덱을 기준으로 설명한다.
사람이 달리는 모습이 담긴 동영상이 있다고 할 때, 사람은 연속적으로 움직이므로 어떤 한 프레임과 그 바로 다음 프레임은 크게 다르지 않을 것이다. 따라서, 다음 프레임을 직접 저장하지 않고, 기준 프레임(keyframe
)으로부터의 변화(delta frame
)를 저장하는 것이 유리하다.
<↗>
이 때, 다음 프레임의 특정 부분(block
)이 기준 프레임의 어떤 부분과 비슷한 지 알고 있다면 저장해야 할 변화의 폭은 더욱 줄어든다. 예를 들어, 기준 프레임의 (0, 0) x (16, 16)
블록이 다음 프레임의 (3, 2) x (19, 18)
블록과 가장 유사도가 높다면, (3, 2)라는 motion vector
와 함께 블록의 차이를 저장할 수 있다.
motion estimation
은 현재 프레임이 이전 프레임의 어떤 블록으로부터 온 것인지를 추정하는 과정이며, motion compensation
은 움직입 추정의 부정확성을 부가적인 정보로부터 보상하는 과정이다. 이 과정은 인코딩에서 가장 많은 계산을 필요로 하며, 또한 상당한 비율로 크기를 줄여 준다.
단순 프레임 차이와 ME 적용 시의 차이 |
<↘>
색을 나타내는 방법에는 여러 가지가 있다. 빛의 삼원색으로 색을 표현한 RGB 색 공간, RGB의 가중치 합인 휘도(luminance
, Y)와 파란색의 차이(CB) 및 빨간색의 차이(CR)로 색을 표현한 YCBCR 색 공간 등이 있다.
사람의 눈은 색차보다 휘도에 더 민감하기 때문에, 여러 픽셀의 색차를 하나의 값으로 저장하는 chroma subsampling을 적용하면 이미지의 용량을 줄이면서도 차이가 별로 느껴지지 않도록 할 수 있다.
<↗>
이 때 여러 픽셀의 색차를 어떤 방식으로 추출하는지를 비율로 명시할 수 있는데, 보통 J : a : b로 표시한다. J는 표본 영역의 가로 길이 (보통 4픽셀 사용), a는 첫 행의 J픽셀에서 추출하는 표본 개수, b는 다음 행에서 첫 행과 비교해 추출하는 변화 표본 개수이다. 예를 들어, ISO/IEC MPEG나 H264 등의 영상 코덱 표준에서 사용하는 4:2:0은 4픽셀에서 2개 표본을 추출하고, 두 번째 행에 대해서는 변화를 추출하지 않으므로 4x2 영역에서에서 2개의 값을 추출한다.
동영상의 경우, 이미지가 아닌 이미지(프레임)의 차이를 다루므로 휘도 대신 휘도의 변화(luma
, Y')와 색차(chroma
)로 저장한다.
<↘>
프레임 간 차이에는 색이 주위와 비슷한 부분도 있고, 많이 달라지는 부분도 있다. 사람의 눈은 넓은 공간 상의 미세한(저주파) 밝기 변화를 잘 감지하지만 고주파의 밝기 변화를 정확히 감지하지는 못 하므로, 고주파 부분의 크기를 약간 줄여도 된다. 이를 이용한 JPEG
와 같은 이미지 손실 압축 표준은 동영상 압축에도 적용할 수 있다.
<↗>
JPEG 압축에서 chroma subsampling 이후 이미지의 각 채널은 여러 블록(8x8)으로 잘리며, 색은 이산 코사인 변환(DCT)을 통해 주파수 형태로 변환된다. 그 후, 양자화라는 과정을 통해 저주파 성분은 작은 값으로 나눠서 비교적 온전하게 보존하고, 고주파 성분은 큰 값으로 나눠서 0 또는 작은 값으로 만든다. 오직 양자화만 데이터 손실이 일어나는 과정이다. 이 시점에서 대부분의 값은 0으로 바뀌었기 때문에, 엔트로피 코딩(지그재그로 값을 읽고 허프만 부호화)을 적용하여 더욱 용량을 줄일 수 있다.
<↘>
기존에는 비디오를 브라우저에서 프레임, 또는 인코딩 된 덩어리 단위로 다룰 때 WebAssembly로 비디오 코덱을 가져와서 사용했다. 대부분의 브라우저에 이미 내장된 코덱을 활용할 수 있도록, WebCodecs API를 제공하고자 한다. 비디오 편집기, 비디오 컨퍼런스 및 스트리밍과 같이 미디어 컨텐츠의 전반적인 가공 과정을 다룰 필요가 있을 때 유용할 것이다.
Web Codecs API의 규격을 정리해놓은 문서로 unofficial draft기 때문에 내용이 예고 없이 변경될 수 있으며, 설명이나 예시가 다소 부실한 편이다(2020/12/20 기준).
Codec: AudioDecoder, AudioEncoder, VideoDecoder, VideoEncoder 인스턴스를 가리킴
Key Frame: 인코딩된 프레임 중 디코딩이 다른 프레임에 의존하지 않는 것
요약하면, 큐를 이용하여 기존의 작업이 완수되기 전에 새로운 작업을 예약할 수 있다.
constructor({ EncodedVideoChunkOutputCallback output, WebCodecsErrorCallback error })
- 아래 인자를 받아서 객체 생성
- 프레임 인코딩이 완료되었을 때 실행할 output 콜백
- 에러가 발생했을 때 실행할 error 콜백
configure({ DOMString codec, unsigned long long bitrate, width, height })
- 객체 생성 후 코덱을 설정 (필수)
- 유효한 codec string이 필요함 (
vp8
,vp09.02.31.12
등) - 문서에는 언급되어 있지 않지만
framerate
도 필수로 포함해야 함
- 유효한 codec string이 필요함 (
encode(frame, options)
- AudioFrame 또는 VideoFrame을 받아 인코딩
-
options
는 VideoEncoder에만 있으며,keyframe
인지 여부를 포함하는 객체 형태
-
flush()
- Queue에 있는 모든 작업을 수행한 후 resolve되는
Promise
반환
reset()
- 모든 상태(
config
,control message queue
,pending callback
) 초기화
close()
- 실행 중인 작업을 모두 종료하고 system resource 반환
- 영구적
Encoder와 거의 유사하나, encode(frame)
대신 decode(encodedChunk)
함수가 있으며, config 형태가 약간 다르다.
구체적인 동작 흐름이 궁금하다면 읽어볼 만 하며, 사용하는 데 있어 완벽하게 이해할 필요는 없다.
각 codec 설정을 할 때 필요한 config의 데이터의 구조를 설명한다. 유효한 코덱에 대해서도 최근 예시가 추가되었다.
type
('key'
또는 'delta'
), timestamp
(unsigned long long
, microsecond 단위), data
(ArrayBuffer
)로 구성
- EncodedVideoChunk의 경우 선택적으로
duration
을 가질 수 있음
AudioFrame
과 VideoFrame
모두 timestamp와 내부 buffer에 저장된 데이터를 가지고 있으며, 디코딩되어 용량이 크기 때문에 다 사용한 후 system resource를 반납하는 함수도 있다. VideoFrame
의 구조는 픽셀 형태인 format
(현재 I420
만 지원)과 크기 정보 (coded
, crop
, display
)를 포함하여 더 구체적으로 정해져 있다. 또한, VideoFrame
은 VideoEncoder
에서 자동으로 파괴(destroy()
)되기 때문에 필요하다면 백업할 수 있도록 clone()
함수도 지원한다.
VideoFrame
은 ImageBitmap
을 인자로 하여 생성할 수도 있고, createImageBitmap()
함수를 통해 ImageBitmap
을 생성하는 것도 가능하다.
constructor(MediaStreamTrack track)
-
HTMLVideoElement.captureStream()
이나await MediaDevices.getUserMedia()
등으로MediaStream
을 얻어.getVideoTracks()[0]
으로MediaStreamTrack
을 받을 수 있음
start(VideoFrameOutputCallback callback)
-
VideoFrame
을 얻으면 실행할 콜백을 등록- <video> element로부터 생성한 경우 영상이 재생 중이어야 프레임을 얻을 수 있음
-
MediaStreamTrack.applyContraints()
로 framerate을 설정했더라도, 실제로 디코딩된 프레임이 방출되는 속도는 이보다 느릴 수 있음
stop()
- 위에서 등록한 콜백을 삭제
state
-
started
,stopped
,ended
중의 값을 가짐
Web Codecs의 예시 코드와, 인터페이스에 대한 비교적 구체적인 설명이 포함되어 있는 문서이다. 예시로 캔버스에서 애니메이션을 렌더링하고, 이를 VideoTrackReader
로 읽어 VideoEncoder
로 encoding한 뒤 VideoDecoder
로 decoding하는 과정을 구현해 놓았다.
실제 사용에서는 encoding과 decoding 사이에 네트워크 통신 과정을 포함해야 할 것이다. 네트워크로 전송하는 데 있어 데이터의 포맷이나 분할하는 방식까지 제공하지는 않고, 이런 식으로 이루어질 것이다 정도의 느낌만 있는 pseudocode가 작성되어 있다.