LINE 트랜스코딩 서버 아키텍처 개선기 – 1

들어가며

안녕하세요. LINE에서 트랜스코딩(transcoding) 서버 개발과 운영 업무를 담당하고 있는 백승훈입니다. 우리는 생활 속에서 다양한 방식으로 동영상을 생산하고 소비하고 있습니다. 자신의 스마트폰으로 직접 동영상을 촬영하기도 하고, 누군가가 메신저로 공유해 준 동영상을 감상하기도 하고, 인터넷에 공개된 동영상을 다운로드하기도 합니다. 그런데 이렇게 다양한 곳에서 다양한 방식으로 얻은 동영상이 간혹 재생되지 않는 경우가 있습니다. 재생할 수 없는 이유에는 여러 가지가 있을 수 있지만, 대부분 스마트폰의 코덱이나 성능의 제약이 문제가 됩니다. LINE에서는 이런 문제를 어떻게 해결하고 있을까요?

LINE 트랜스코딩 서버에서는 모든 LINE 사용자가 문제없이 동영상을 시청할 수 있도록 가장 범용적인 포맷으로 동영상을 변환하고 있습니다. 또한 더 적은 데이터 요금으로 동영상을 즐길 수 있고, 인터넷 환경이 좋지 않은 곳에서도 잘 재생될 수 있도록 동영상 품질을 최적화하고 있습니다. 그 외에도 동영상이 필요한 LINE의 여러 서비스에 다양한 기능들을 제공하고 있는데요. 이번 글에서는 전 세계의 LINE 사용자가 발생시킨 동영상 트래픽을 처리하고 있는 LINE 트랜스코딩 서버의 아키텍처에 대해 이야기하겠습니다.

 

일반적인 동영상 트랜스코딩 과정

LINE 트랜스코딩 서버에 대해 이야기하기 전에, 동영상 트랜스코딩이 무엇이고 어떤 과정으로 진행되는지 간략하게 살펴보겠습니다. 동영상 트랜스코딩은 원본 동영상 파일을 다른 포맷의 동영상 파일로 변환하는 과정을 의미합니다. 원본 동영상의 코덱을 변경하거나 영상 혹은 오디오의 품질을 변경할 때, 영상 또는 음성에 효과를 추가하는 등 여러 가지 이유로 동영상 트랜스코딩 작업을 진행합니다. 스마트폰이 대중화되기 전인 2000년 대 초, 중반에 디지털 기기를 좋아하셨던 분들은 PMP(Portable Media Player)를 알고 계실 텐데요. 당시에는 휴대용 기기에서 사용할 수 있는 동영상 코덱이 제한적이었기 때문에, 컴퓨터에 저장된 동영상을 PMP에서 재생하기 위해선 제작사가 제공한 변환 프로그램을 거쳐야만 재생할 수 있었습니다. 바로 그 작업이 동영상 트랜스코딩 작업입니다. 아래는 트랜스코딩이 진행되는 과정을 간략하게 도식화한 그림입니다. 

위 그림에서 모래시계는 과정별로 어느 정도의 시간이 소요되는지를 표시한 것입니다. 비디오(video)를 처리하는 데 다른 과정에 비해 많은 시간이 소요된다는 것을 확인할 수 있습니다. 만약 동영상의 해상도가 높거나, 재생 길이가 길거나, 초당 프레임 수가 많다면 더욱 많은 시간이 필요합니다. 실제로 제가 사용하는 서버에서, 해상도가 1280×720 픽셀이고 길이가 90초 정도이며 초당 프레임 수가 30인 동영상을 단일 CPU 코어에서 트랜스코딩하면 ‘비디오 디코딩(decoding) + 비디오 인코딩(encoding)’엔 약 3분 40초, ‘오디오 디코딩 + 오디오 인코딩’엔 약 2.5초가 소요됩니다. 이를 통해 ‘동영상 트랜스코딩에선 비디오를 처리하는 데 많은 시간이 소요된다’라는 것을 알 수 있습니다.

 

LINE 트랜스코딩 서버 소개

LINE 트랜스코딩 서버는 LINE의 모든 서비스에 동영상 트랜스코딩과 동영상 프로세싱 기능을 제공하기 위한 서버 플랫폼입니다. 2016년에 프로젝트를 시작하여, 동영상 파일을 가공하고 균일한 품질을 제공하는 것을 목적으로 현재까지 개발하고 있습니다. 2019년 12월 평일을 기준으로 하루에 약 2천만 건 이상의 동영상을 처리하고 있으며, 주말이나 전 세계 방방곡곡에서 행사가 열릴 때는 이보다 훨씬 많은 동영상 업로드를 처리하고 있습니다. 내부에선 LINE 트랜스코딩 서버를 ‘리코더(Licoder, LIne TransCODER)’라는 이름으로 부르고 있는데요. 이 블로그에서도 LINE 트랜스코딩 서버를 리코더라는 명칭으로 설명하겠습니다.

리코더는 사용자가 업로드한 동영상을 여러 형태로 가공할 수 있는 기능과 인터페이스를 제공하고 있는데요. 리코더가 어떤 기능을 제공하고 있는지 좀 더 자세히 알아보겠습니다.

 

리코더가 제공하는 기능

 

동영상 트랜스코딩 

리코더는 비디오의 품질을 결정하는 여러 가지 옵션을 제공하며, 생성된 비디오를 MP4나 HLS 포맷으로 제공합니다.

 

점진적 다운로드(Progressive Downloadable) MP4

리코더에서 동영상을 MP4 포맷으로 트랜스코딩할 경우, 점진적 다운로드가 가능한 MP4 파일을 생성합니다. 점진적 다운로드를 설명하기에 앞서 일반적인 MP4 파일의 구조를 설명하겠습니다.

우리가 일반적으로 부르는 MP4(MPEG-4 Part 14)는 ISOBMFF 파일 구조에 기반하고 있습니다. ISOBMFF 파일 구조는 ‘box’라고 불리는 구조체로 구성되어 있는데요. 여기에 미디어의 트랙 정보나, 비디오 또는 오디오 데이터를 저장할 수 있습니다. MP4 파일은 미디어의 메타데이터를 저장하고 있는 ‘moov box’와 실제 인코딩된 데이터를 저장하고 있는 ‘mdat box’로 구성되어 있습니다. 이때 메타데이터를 저장하기 위해 필요한 정보는 인코딩이 끝난 후에야 계산할 수 있기 때문에, moov box는 mdat box 다음에 위치하게 됩니다.

이제 점진적 다운로드 MP4에 대해 설명하겠습니다. 동영상 플레이어에서 MP4 파일을 재생하기 위해서는, 먼저 동영상 파일의 메타데이터를 찾은 후에 인코딩된 오디오와 비디오의 데이터를 읽어 디코딩하는 과정을 수행해야 합니다. 따라서 동영상 플레이어는 moov box에 저장된 메타데이터를 찾아 mdat box에 저장되어 있는 오디오 데이터의 시작 위치나 비디오 데이터의 위치를 알아낸 뒤, mdat box에 저장된 데이터를 읽어 디코딩 과정을 거쳐 화면에 비디오를 출력하거나 스피커를 통해 음성을 출력합니다.

그런데 만약 스트리밍 서비스를 통해 MP4 파일을 재생하는 경우라면 어떨까요? 동영상을 재생하기 위해선 먼저 moov box를 찾아야 합니다. 그런데 동영상 플레이어는 동영상 파일의 어느 위치에 moov box가 있는지 알 수 없으므로 moov box가 발견될 때까지 파일을 다운로드하게 됩니다. 아마 전체 동영상 파일을 다운로드한 후에야 moov box를 찾을 수 있을 테고, 그 후 위에서 설명한 과정을 거친 뒤 동영상 파일을 재생할 수 있을 것입니다. 이렇게 되면 동영상의 일부분만 보고 싶더라도 전체 동영상 파일을 다운로드해야 하기 때문에 스트리밍 서비스에서 이런 MP4 파일을 재생하는 것은 비효율적입니다. 그래서 MP4 파일 전체를 다운로드하지 않더라도 재생할 수 있도록 만든 것이 바로 ‘점진적 다운로드 MP4’ 파일입니다. 아래 그림은 일반적인 MP4 파일의 구조와 점진적 다운로드 MP4 파일의 구조입니다.

기존의 MP4 구조와 비교해 달라진 점은, moov box를 동영상 파일의 앞부분으로 이동했다는 점입니다. 그리고 moov box의 위치를 이동하면서 moov box에 저장되어 있는 오디오 데이터의 시작 위치, 비디오 데이터의 시작 위치를 변경해 줍니다. 이렇게 moov box의 위치를 변경하게 되면 파일의 처음 일부분을 읽어 동영상 파일을 재생할 수 있게 됩니다. 스트리밍 서비스의 경우 동영상 파일 전체를 다운로드하지 않아도 재생을 시작할 수 있고, 또한 탐색(seek) 기능을 사용할 수 있어 사용자가 원하는 위치에서 영상을 시청할 수 있다는 장점이 있습니다. 이러한 이유로 리코더는 사용자에게 점진적 다운로드가 가능한 MP4 파일을 제공하고 있습니다.

 

HLS(Http Live-Streaming)

리코더는 동영상이 중심이 되는 서비스에서 많이 사용하는 HLS 파일 포맷을 지원합니다. HLS에서는 ABR(Adaptive Bit Rate) 기능을 제공하고 있어서 다양한 네트워크 환경이나 단말에서 최적의 화질로 비디오를 제공할 수 있습니다. 또한 저작권 등의 이유로 콘텐츠를 보호할 필요가 있는 서비스를 위해서 암호화 기능도 지원합니다.

 

대표 장면 추출

동영상 파일에서 원하는 위치의 프레임을 단일 이미지 파일로 추출하는 기능을 제공합니다. 복수의 이미지로 추출하는 기능도 같이 제공하고 있는데요. 특정 주기마다 이미지를 추출하는 방식과, 원하는 수만큼 추출하는 기능을 제공하고 있습니다. 최근에는 동영상의 특정 위치에 해당하는 프레임을 이미지로 추출할 때 검은색 이미지나 의미 없는 이미지가 추출되는 것을 피하고자, 원본 동영상에서 의미가 있는 부분을 찾아 이미지 파일로 추출하는 기능을 제공하고 있습니다.

 

애니메이션 제작

리코더는 동영상 파일을 애니메이션 이미지 파일로 제작하는 기능을 제공합니다. 대표적으로 많이 사용하고 있는 APNG 파일과 GIF 파일을 만들 수 있으며, 아래에서 소개할 ‘편집’ 기능을 사용하면 동영상의 일부 구간을 선택하여 애니메이션 이미지 파일을 만들 수 있고, 리코더에서 정의한 SJPEG(Sprite JPEG) 이미지 파일을 만들 수도 있습니다.

SJPEG 이미지 파일은 LINE 메신저의 친구 목록에서 동영상 프로필의 미리 보기 용도로 사용하기 위해 만들어졌습니다. LINE 메신저의 친구 목록을 보면 친구가 설정한 프로필 이미지나 동영상을 작은 화면을 통해 볼 수 있는데요. 모바일의 성능 한계로 한 화면에서 여러 명의 동영상 프로필을 재생할 수 없는 문제가 있었습니다. 그래서 동영상을 재생하는 대신 애니메이션 이미지를 사용하여 동영상 프로필의 미리 보기를 볼 수 있도록 하였는데요. 기존의 애니메이션 이미지 포맷은 파일 크기가 커지거나 화질의 열화가 발생하는 단점이 있어 이를 개선하고자 만든 것이 SJPEG 이미지 파일입니다. 

SJPEG 이미지 파일은 연속된 여러 개의 이미지를 하나의 큰 이미지에 타일처럼 붙여 JPEG 파일로 만듭니다. 그리고 타일로 붙여진 이미지들을 순서대로 재생하기 위한 정보를 JPEG 파일의 헤더에 포함시켜서 클라이언트에서 애니메이션을 정상적으로 재생할 수 있도록 만듭니다. 아래는 SJPEG 이미지 파일의 구조와 예시입니다. 

JPEG 파일 포맷에서 해상도를 표현하는 필드의 크기가 2 bytes이므로 가로 또는 세로 해상도의 최댓값은 64,335 픽셀이 됩니다. SJPEG 파일을 만들 때는 프레임을 세로 방향으로 배치하고 있습니다. 만약 프레임의 세로 길이가 65,535 픽셀을 초과하면 가로 방향으로 두 줄 이상의 프레임을 배치하게 됩니다. 이럴 때는 가로 방향으로 프레임을 배치한 다음에 세로 방향으로 내려가 프레임을 채우는 방식을 사용하고 있습니다. 이렇게 세로 방향 기준으로 프레임을 배치하면 SJPEG을 사용하는 클라이언트에서 이미지를 빠르게 로딩할 수 있는 장점이 있습니다. 클라이언트에서 애니메이션 이미지를 보여주기 위해서는 저장된 프레임이 디코딩되어야 하는데요. 만약 SJPEG에 포함된 프레임이 가로 방향 기준으로 배치되어 있다면, 첫 번째 프레임을 읽기 위해서 모든 가로 방향에 해당하는 프레임을 읽어야 합니다. 하지만 세로 방향 기준으로 된 SJPEG을 디코딩할 땐, 첫 번째 프레임을 읽기 위해서 첫 번째 프레임에 해당하는 데이터만 읽으면 되기 때문에 이미지 로딩에 유리하다고 볼 수 있습니다. 

아래는 앞서 설명한 가로 방향 배치와 세로 방향 배치를 도식화한 그림입니다. 애니메이션을 보여주기 위해 프레임을 디코딩해야 하는 상황에서 디코더의 스캔 범위를 나타내고 있습니다.  

1번 예시처럼 가로 방향 기준으로 프레임을 배치하면 첫 번째 프레임을 읽기 위해 가로 방향에 해당하는 프레임을 읽어야 합니다. 1번 프레임부터 4번 프레임까지, 총 16KB(kilobyte)를 읽어야 첫 번째 프레임을 디코딩할 수 있으므로 이미지 로딩 속도에 지연이 발생할 수 있습니다. 반면 2번 예시처럼 프레임을 세로로 배치하면 4KB만 읽으면 첫 번째 프레임을 재생할 수 있으므로 1번 예시에 비해 로딩 속도가 빠르다는 이점이 있습니다. 그래서 리코더에서는 SJPEG 이미지 파일을 만들 때 프레임을 세로 방향 기준으로 배치하고 있습니다.

클라이언트에서 SJPEG 이미지 파일을 재생하기 위해서는 각 프레임의 해상도나 재생 속도, 프레임의 총개수 등의 정보가 필요합니다. 그래서 아래와 같이 SJPEG 파일을 만들 때 JPEG 포맷 헤더의 Comment(COM) Marker에 애니메이션 재생과 관련된 정보를 저장합니다.

COM 정보는 JPEG 포맷의 SOI(Start Of Image) Marker 다음 위치에 저장합니다. 클라이언트에서는 COM Marker를 사용해서 이미지 파일에 저장된 타일을 순차적으로 재생할 수 있습니다. 

아래 표는 동일한 동영상을 APNG와 GIF, SJPEG 방식으로 생성한 결과입니다. 가장 우측에 있는 SJPEG을 살펴보면, APNG 파일 크기의 약 10%의 크기로 애니메이션을 보여줄 수 있으며, GIF와 대비해선 약 32%(일반 화질은 50%)의 파일 크기로 애니메이션을 보여줄 수 있습니다.

APNG (Animated PNG)GIF (High Quality)GIF (Normal Quality)SJPEG
이미지(프레임 당 256×144 픽셀)
파일 크기(byte)1,700,094561,727356,912176,970
비고SJPEG 파일은 일반 웹 브라우저에서는 재생이 불가능하여 비교를 위해 APNG 파일로 대체했습니다.

SJPEG은 다른 포맷에 비해 화질 열화가 크게 발생하지 않고, 파일 크기 또한 작기 때문에 네트워크 환경이 좋지 않은 곳에서 미리 보기 영상으로 사용하기에 적합합니다. 하지만 SJPEG 이미지 파일에도 단점은 존재합니다. 첫 번째는 SJPEG을 사용하기 위해서는 클라이언트에서 별도 개발이 필요하다는 점입니다. 두 번째는, 전체 애니메이션 이미지를 메모리에 한 번에 로딩해야 하기 때문에 시스템 메모리가 많이 필요하다는 점입니다. 이 때문에 저사양 모바일 단말에선 메모리가 부족해 재생하지 못하는 문제가 발생할 수 있습니다. 이 문제를 해결하기 위해서 내부적으로 JPEG Partial Decoding 기능을 검토하고 있습니다. 이 기능은 이미지 전체가 아닌 일부분만 로드하기 때문에 메모리 절약에 도움이 될 것으로 기대하고 있습니다.

 

편집

구간 설정(trim)

사용자가 리코더로 요청을 보낼 때 원하는 구간을 입력할 수 있습니다. 시작 시간과 끝 시간, 또는 시작에서 몇 퍼센트만큼 떨어진 위치와 끝에서 몇 퍼센트만큼 떨어진 위치와 같은 방식으로 구간을 지정할 수 있습니다. 입력된 구간은 동영상 트랜스코딩이나 장면 추출, 애니메이션 제작 작업을 수행할 때 사용합니다.

해상도 조절(resize)

리코더에서는 사용자가 업로드한 동영상의 해상도를 원하는 해상도로 변경할 수 있는 기능을 제공하고 있습니다. 사용자는 목표 해상도를 정하고 해상도 조절 옵션을 선택해 원하는 형태의 동영상 파일을 만들 수 있습니다. 원본 동영상의 해상도가 사용자의 목표 해상도보다 낮은 경우를 위해 업샘플링(Up-Sampling) 옵션도 제공하고 있습니다.

옵션설명해상도 변경 예시
(원본 해상도: 1920×1920, 목표 해상도: 1280×720)
Default원본 동영상의 해상도 비율을 유지720×720
Stretch원본 동영상의 해상도 비율을 유지하지 않음1280×720
Padding원본 동영상의 해상도 비율을 유지하기 위해 비율이 맞지 않은 공간에 검은색 배경 추가1280×720 (좌우로 검은색 화면이 추가됨)
WidthBased목표 해상도의 가로 길이에 맞춰 원본 해상도의 비율을 유지1280×1280
HeightBased목표 해상도의 세로 길이에 맞춰 원본 해상도의 비율을 유지720×720
ScaleCrop목표 해상도의 가로, 세로 중 작은 축에 기준을 맞추어 원본 해상도의 비율에 맞게 가운데 자름1280×720
워터마크(overlay)

동영상 화면의 특정 위치에 다른 영상을 배치할 수 있는 기능을 제공하고 있습니다. 현재는 주로 LINE 로고를 배치하는 역할로 사용하고 있고, LINE LIVE 서비스에서 이 기능을 활용하고 있습니다. 아래 예시를 보면 화면의 우측 상단에 로고 이미지가 배치된 것을 볼 수 있습니다.

Original FrameOverlay Frame

 

인터페이스

리코더는 앞서 소개한 기능들을 사용자가 편리하게 사용할 수 있도록 아래와 같은 인터페이스를 제공합니다.

 

커맨드 파이프라인

리코더는 작업의 기본이 되는 단위로 커맨드(command)를 사용합니다. 커맨드는 입력 동영상이 어느 경로에 있는지, 어떤 작업을 수행할지, 생성된 결과물을 어디에 저장할지 등 리코더의 작업에 필요한 내용을 포함하고 있습니다. 앞서 ‘리코더가 제공하는 기능’ 단락에서 소개한 내용을 커맨드에 입력할 수 있으며, 사용자는 리코더에 요청을 보낼 때 여러 개의 커맨드를 입력할 수 있습니다.

리코더는 내부적으로 시스템의 리소스를 최대한 사용하기 위해 커맨드 파이프라인을 만들어 커맨드별로 독립적으로 작업을 수행합니다. 커맨드 파이프라인에서는 커맨드별로 필요한 작업을 선별하여 수행합니다. 커맨드 파이프라인에서 수행되는 작업은 크게 3가지인데요. 원본 동영상을 확인하는 작업(Pre Transcoding)과 트랜스코딩(Main Transcoding), 그리고 마지막으로 후처리 작업과 결과물 파일을 확인하고 원격 저장소에 저장하는 작업(Post Transcoding)으로 분류할 수 있습니다.

먼저 Pre-Transcoding 단계에서 외부 저장소에 저장된 동영상을 다운로드하고 파일을 분석하여 동영상의 세부 정보를 알아낸 뒤, 동영상의 세부 정보와 사용자가 원하는 품질 정보를 분석하여 Main-Transcoding 단계에서 어떤 일을 할지 결정합니다. 이후 Main-Transcoing 단계에서는 Pre-Transcoding 단계에서 결정된 내용을 기반으로 트랜스코딩을 수행합니다. 마지막으로 Post-Transcoding 단계에서는 Main-Transcoding 단계에서 생성된 파일을 후처리한 다음, 파일이 요구 사항에 맞게 잘 만들어졌는지 확인합니다. 결과물 파일을 확인한 후에는 원격 저장소의 지정된 위치에 파일을 저장하고, 작업이 완료되었음을 알려주는 콜백 메시지를 특정 위치에 저장한 뒤 커맨드 파이프라인을 마무리합니다. 저장된 콜백 메시지는 아래에서 설명할 알림(notify) 항목을 거쳐 사용자에게 전달됩니다.

 

프리셋(preset)

리코더를 사용하는 서비스에서 필요한 결과물의 형식이 고정되어 있거나, 혹은 서비스에 특화된 기능이 필요한 경우엔 개발 중 상호 합의하여 프리셋을 설정할 수 있습니다. 프리셋을 설정하고 나면, 사용자는 트랜스코딩 서버에 요청을 보낼 때 원본 동영상의 파일 경로와 프리셋의 이름을 전달하면 됩니다. 그러면 프리셋을 처리하는 모듈에서 커맨드 파이프라인을 자동으로 구성합니다. 또한 리코더를 중단하지 않고도 프리셋 설정을 변경할 수 있기 때문에 서비스 측의 긴급한 수정이 필요한 경우에 대응할 수 있습니다. 사용자는 커맨드 파이프라인을 직접 구축할 필요 없이 간단하게 트랜스코딩 서버의 기능을 사용할 수 있고, 프리셋으로 설정된 파이프라인을 분석하여 트래픽을 예측할 수 있으므로 사용자들에게 프리셋 방식의 리코더 사용을 추천하고 있습니다.

 

알림(notify)

사용자가 트랜스코딩 요청을 보낸 후 처리가 완료될 때까지 응답을 기다려야 한다면 사용자는 그동안 다른 작업을 못하는 상태가 됩니다. 이는 사용자의 리소스를 낭비하는 것이기 때문에 이를 방지하고자 알림 기능을 제공하고 있습니다. 사용자가 트랜스코딩을 요청하면 즉시 응답을 주고, 실제 트랜스코딩이 완료되었을 때 사용자가 지정한 HTTP 경로로 요청이 완료되었음을 알려줍니다. 사용자가 부득이한 이유로 리코더의 완료 알림을 받지 못할 경우엔, 리코더는 이 메시지를 임시 공간에 잠시 보관하고 일정 시간 이후 다시 알림을 발송하여 메시지가 누락되지 않도록 보장하고 있습니다. 사용자는 리코더에 요청을 보낼 때 알림을 설정할 수 있는데요. 커맨드별로 완료 알림을 받거나, 전체 요청의 처리가 모두 완료되었을 때 알림을 받을 수도 있습니다. 완료 알림을 받게 된 시점에는 원격 저장소에 결과물이 저장된 상태이기 때문에 바로 저장소에 접근하여 결과물 파일을 사용할 수 있습니다.

 

마치며

이번 글에서는 일반적인 트랜스코딩 과정과 함께 전 세계의 LINE 사용자가 발생시킨 동영상 트래픽을 처리하고 있는 LINE의 트랜스코딩 서버, 리코더를 소개하고 그 기능과 인터페이스에 대해 말씀드렸습니다. 다음 편에서는 LINE이 다양한 국가에서 급격하게 성장하면서, 그에 따라 리코더의 아키텍처가 어떻게 변화했는지 공유하려고 합니다. 많이 기대해 주세요! 

Related Post