Skip to content

WebCodecs

joh16 edited this page Dec 20, 2020 · 1 revision

배경 지식

<↗>로 표시한 부분은 다소 기술적인 내용을 포함하고 있습니다. 부담스럽거나 궁금하지 않다면 <↘> 표시가 나오는 부분으로 건너뛰세요.

인코딩/디코딩

개요

여러 장의 이미지를 빠르게 전환하며 보면 그것은 마치 움직이는 것처럼 보인다. 즉, 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으로 바뀌었기 때문에, 엔트로피 코딩(지그재그로 값을 읽고 허프만 부호화)을 적용하여 더욱 용량을 줄일 수 있다.

<↘>

Web Codecs

개요

기존에는 비디오를 브라우저에서 프레임, 또는 인코딩 된 덩어리 단위로 다룰 때 WebAssembly로 비디오 코덱을 가져와서 사용했다. 대부분의 브라우저에 이미 내장된 코덱을 활용할 수 있도록, WebCodecs API를 제공하고자 한다. 비디오 편집기, 비디오 컨퍼런스 및 스트리밍과 같이 미디어 컨텐츠의 전반적인 가공 과정을 다룰 필요가 있을 때 유용할 것이다.

Web Codecs API의 규격을 정리해놓은 문서로 unofficial draft기 때문에 내용이 예고 없이 변경될 수 있으며, 설명이나 예시가 다소 부실한 편이다(2020/12/20 기준).

Codec: AudioDecoder, AudioEncoder, VideoDecoder, VideoEncoder 인스턴스를 가리킴

Key Frame: 인코딩된 프레임 중 디코딩이 다른 프레임에 의존하지 않는 것

요약하면, 큐를 이용하여 기존의 작업이 완수되기 전에 새로운 작업을 예약할 수 있다.

Encoder (Audio, Video)

constructor({ EncodedVideoChunkOutputCallback output, WebCodecsErrorCallback error })

  • 아래 인자를 받아서 객체 생성
    • 프레임 인코딩이 완료되었을 때 실행할 output 콜백
    • 에러가 발생했을 때 실행할 error 콜백

configure({ DOMString codec, unsigned long long bitrate, width, height })

  • 객체 생성 후 코덱을 설정 (필수)
    • 유효한 codec string이 필요함 (vp8, vp09.02.31.12 등)
    • 문서에는 언급되어 있지 않지만 framerate도 필수로 포함해야 함

encode(frame, options)

  • AudioFrame 또는 VideoFrame을 받아 인코딩
    • options는 VideoEncoder에만 있으며, keyframe인지 여부를 포함하는 객체 형태

flush()

  • Queue에 있는 모든 작업을 수행한 후 resolve되는 Promise 반환

reset()

  • 모든 상태(config, control message queue, pending callback) 초기화

close()

  • 실행 중인 작업을 모두 종료하고 system resource 반환
    • 영구적

Decoder (Audio, Video)

Encoder와 거의 유사하나, encode(frame) 대신 decode(encodedChunk) 함수가 있으며, config 형태가 약간 다르다.

구체적인 동작 흐름이 궁금하다면 읽어볼 만 하며, 사용하는 데 있어 완벽하게 이해할 필요는 없다.

각 codec 설정을 할 때 필요한 config의 데이터의 구조를 설명한다. 유효한 코덱에 대해서도 최근 예시가 추가되었다.

Encoded Media: Chunks (Audio, Video)

type ('key' 또는 'delta'), timestamp (unsigned long long, microsecond 단위), data (ArrayBuffer)로 구성

  • EncodedVideoChunk의 경우 선택적으로 duration을 가질 수 있음

Raw Media: Frames (Audio, Video)

AudioFrameVideoFrame 모두 timestamp와 내부 buffer에 저장된 데이터를 가지고 있으며, 디코딩되어 용량이 크기 때문에 다 사용한 후 system resource를 반납하는 함수도 있다. VideoFrame의 구조는 픽셀 형태인 format(현재 I420만 지원)과 크기 정보 (coded, crop, display)를 포함하여 더 구체적으로 정해져 있다. 또한, VideoFrameVideoEncoder에서 자동으로 파괴(destroy())되기 때문에 필요하다면 백업할 수 있도록 clone()함수도 지원한다.

VideoFrameImageBitmap을 인자로 하여 생성할 수도 있고, 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가 작성되어 있다.

Clone this wiki locally