13 min read

영상과 음원의 표준에 대해 딥다이브

영상과 음원의 표준에 대해 딥다이브

회사에서 6개월 정도 근무하면서 주로 영상데이터나 음성데이터에 대해 다루는 작업을 꽤 진행해왔었는데, 잠시간 다른 작업을 도맡아 하게 되면서 기존에 했던 작업들 중 꽤 흥미로웠던걸 정리해두면 좋을거같아서 글을 발행해본다.

회사와 별개로 그냥 개인적인 작업하다가 발견한 것도 겸사겸사 같이 적어보도록 하겠음.

wav파일을 다루기

올해에 @monbox 학우가 일본여행 갔다오는 김에 앨범을 이것저것 대리구매해주게 되어서, 갑자기 앨범 보유분이 확 늘어났다.

그간 앨범은 flac음원 형태로 저장하고 있었지만, 실물앨범의 CD에서 딴 음성은 무압축 코덱인 wav형태로 저장하는게 보통 제일 무난한 픽이라 생각해서 사진에 있는 10개정도의 앨범을 전부 wav로 리핑해서 홈서버에 넣고 메타데이터를 채우려 시도해보았는데,

음악 관리 서버를 찾기 위한 여정과 gonic
음악을 자주 듣는 편이라서, 이것저것 긁어모은 음원파일들이 꽤나 많아졌습니다. 이 음원들을 서버에다 다 때려박아놓고 관리하고 있는데, 최종적으로는 gonic 이라는 프로그램에 정착해서 음악을 이걸로 관리하고 있습니다. plex / jellyfin 처음에는 단순하게 영상과 음원 둘다 하나에서 관리하면 더 편하지 않을까? 를 이유로 plex, jellyfin을 둘 다 사용해 보았습니다. plex는 유료 구독이라는게 맘에 안들어서

에서부터 잘 사용하고 있던 음악 서버 gonic에서 해당 wav파일들을 전혀 읽지 못하는 문제가 발생했다.

음성 파일과 메타데이터

일반적으로 mp3나 flac에 대한 메타데이터 파일은 어디에 쓰이게 될까? 정답은 둘이 서로 다른 방법을 사용해 기록하게 된다.

mp3 파일의 경우 ID3이라는 표준을 사용하며, 그 중에서도 ID3v2.3 표준을 현재는 가장 폭넓게 사용하고 있다.

파일의 제일 앞에 가변 길이의 데이터를 삽입해서 데이터를 기록할 수 있는데, 가변이라는 말에서 알 수 있듯 어떤걸 써야한다는+용량의 제약이 딱히 없고 일종의 딕셔너리마냥 임의의 태그를 만들어서 붙일수있다. 일종의 딕셔너리 같은거라 생각하면 좋다. 벌써여기부터 머리가 아파진다...

Tag Field Mappings – Mp3tag Documentation
Overview of all available tag fields, their names in Mp3tag, and how they are mapped to the internal structures of the different tag formats. Mp3tag is the universal Tag Editor.

일반적으로 쓰이는 태그들의 목록은 위 링크와 같은데 대층 그냥 앨범 아티스트 트랙번호 제목 등 일반적으로 노래에 기대하는것들 생각하면 된다.

물론 내가 사용하는 플레이어가 어떤 태그를 읽어줄지는 구현체를 까보지 않는한 믿음과 신뢰 그리고 기도의 영역이다.

문제는 구버전 ID3 태그의 경우 shift-jiseuc-kr 을 사용했기 때문에, 특히 보컬로이드 앨범을 구매한 나의 경우 구버전을 사용하면 무조건 피곤해지게 되고, 최소 utf-16 을 지원하는 ID3 v2.3 이상을 사용하는것이 정신건강에 좋다.


이에 비해 flac은 Vorbis Comment라는 별도 표준을 사용하고 있으며, 이것 역시 딕셔너리 Key=Value쌍을 사용해 기록하지만 utf-8을 지원하므로 다국어 태깅이 매우 간편하다는 장점이 있다. 사실상 이것만이 좀 써먹을만한 표준이라 말할 수 있겠다.


자 이제 문제의 wav 확장자를 보자. 둘중 어느방법을 표준으로 사용할 것 같은가?

정답은 둘 다 아니다! 이미 아무도 사용하지 않게된 표준을 지나 이것저것 마개조를 거쳐, mp3에서 사용되는 ID3 태그를 그냥 메타데이터용 공간에 대충 집어넣고 플레이어가 읽기를 기도하는것이 준 표준으로 잡히게 되었다.

태그 에디터들은 보통 wav에 대해 ID3 규격으로 태그를 쓰는것을 암묵적으로 허용하고 있다.

  • 내가 사용하고자 하는 사용처인 gonic은 이 태그를 읽지 못해서,
  • 혹은 구현체를 직접 찾아본 결과 읽는척만하고 읽을생각을 전혀 하지 않아서,
  • 거기에 하필 다국어지원조차 부실해서 한국어를 읽으면 웹어셈블리가 패닉으로 터1져버리기 때문에,

gonic서버에서는 wav에 한국어/일본어 메타데이터를 기록한 모든 트랙을 읽을수 없게 되었다. 물론 이 서버는 철저히 ^표준^ 을 지켜서 구현되어있으므로 탓할 수 없다...


그러므로, wav로 모든 앨범을 리핑했던 나에게는 야속하게도 flac으로 전부 다시 리핑해서 메타데이터를 다시 채우는 과정을 진행해야만 했다.

바이브코딩의 은총과 함께 vocadb에서 메타데이터를 따와서 자동으로 적재해주는 매크로를 간단히 작성해서 이 문제를 어느정도 해결할 수 있었다. 현재는 비공개상태로 되어있는데 언젠가는 좀더 다듬어보고 공개할지도

mp4, webm과 스트림

회사에서 산업기능요원으로 복무하며 그간 해오던 일 중의 하나는 aws mediaconvert의 기능 확장으로부터 시작한 자체적인 트랜스코딩용 레포지토리를 개선하고 영상처리 버그를 잡는 일이었다. 사용자의 웹캠 / 카메라로부터 녹화된 스트림을 가공해서 불안정한 영상을 어느정도 정리하고 재생에 쓸만한 퀄리티로 만드는 작업을 했다.

처음에는 webm으로, 현재는 mp4형식으로 녹화 및 일련의 처리를 해주게 됬는데, 이러한 변경을 거치게 된 경위를 설명하기 위해서는 webm의 기록방식과 mp4의 그것에 대해 먼저 다루어야만 한다.

webm의 내부 스트림 구조

webm의 구조는 제일 앞에 EBML header가 위치해서 코덱 종류나 해상도같은 제일 중요한 종류의 메타데이터들을 먼저 저장하고, 각각의 seek은 영상 Cluster에 대한 일종의 포인터 같은 느낌으로 빠른검색을 "도와주기" 위해 존재한다.

그리고 영상 데이터 영역인 cluster 안에는 사진에서 볼 수 있듯 timecode값을 전부 기록하고 있기 때문에, 특정 시점으로 이동하면 해당 클러스터만 열어보면 된다.

즉 일부 cluster 블럭이 손상을 입더라도 저 timecode값을 읽어 언제 다음 청크를 불러와야하는지 알 수있다는 것이고, 이러한 특징이 스트리밍에 강점을 지니는 확장자로서 기능하게 한다.


mp4의 내부 스트림 구조

이와 다르게 mp4에는 영상 본문에 해당하는 곳에 언제 재생해야하는지 찍는 데이터가 없고, 우리가 일반적으로 보게 되는 CFR 고정 프레임 레이트를 가진 영상과 다르게 카메라스트림은 VFR(가변 프레임레이트)인 경우가 왕왕 있으므로 각각의 프레임이 얼마나 긴지에 대한 값 역시 고정되어있지 않아서,

mp4는 영상 청크 일부분이 날아가면 뒷부분의 영상을 "언제부터" 재생해야하는지 판단할 수 없으므로, 정상적으로 보기 힘들다.

webm에서 mp4로 건너간 이유

용량이 큰 영상을 클라단에서 업로드하기 위해서, aws에서 기본 제공하는 멀티파트 업로드 기능을 사용하다가 특정 파트의 업로드가 실패하는 경우 전혀 복구나 재시도 로직을 기대할 수 없어서, 업로드도 그냥 여러 10초단위의 영상(앞으로 청크라 칭함)으로 끊어서 s3에 개별 업로드 처리하도록 작업을 했었다.

이때 각각의 청크가 다른 청크에 대해 의존성을 가지지 않으려면 어차피 모든 개별 청크에 EBML header가 들어가게 되는데, 이럴거라면 webm이 가지는 장점이 희석되고, 오히려 조금 더 strict한 구조를 가지고 있고 깨지지 않았을 경우의 신뢰성이 더 높은 mp4로 건너가자 라는 결정을 내려 작업을 하게 되었다.


영상의 스트림 순서

문제는 여기서 시작하는데, 영상 파일은 보통 위의 사진에서 보듯 트랙을 구분해 저장하게 된다. 예를들어 한 영화나 애니메이션의 더빙본과 원본을 하나의 파일로 관리할 수 있는 확장자라는 뜻이고, 하나의 비디오 트랙에 여러 오디오 트랙이 붙거나 할 수 있다는 뜻이다.

이를 유연하게 지원하기 위해서일지, mp4에서의 비디오트랙과 오디오트랙은 동일한 규격으로 저장되며, 그 순서를 구분하지도 않는다. 즉 어떤 영상을 열었을때 0번 트랙이 비디오일지 오디오일지 모른다는 소리다...

당연히 녹화하는쪽도 0번 트랙이 비디오일지 오디오일지는 신경쓰지 않고 그냥 먼저 기기가 붙어서 녹화시작하는것부터 0번 1번을 순서대로 배정하기 때문에,

10초단위의 청크로 끊어서 녹화하고 개별적으로 업로드하는 환경에서는 오디오트랙 비디오트랙이 뒤죽박죽이 되어 존재할 수 있다!

물론 일반적인 경우 먼저붙은건 먼저끊어지고 다시 먼저붙으므로 스트림 순서가 유지되나, 환경문제로 이게 뒤집히는 경우 10초로 끊어진 여러 영상들을 하나로 합치는 과정에서 음성청크를 영상으로 해석하려하고 영상청크를 음성으로 해석하려해서 대차게 깨지는 경우가 자주 발생했었다.

해결법으로는 그냥 간단하게 트랜스코딩 전에 모든 각 10초짜리 파일의 청크 순서를 하나로 정렬해놓도록 전처리 하면 된다.

결론

영상과 음성은 굉장히 많은 보정 로직이 이미 곳곳에서 쓰이고 있어서, 그 안쪽을 들여다보면

안은 생각보다 더 더러운걸

이라는 생각을 한다... 또한 이런 이해할 수 없는 표준들이 모여서 유튜브가 굴러가고 트위치가 굴러가고 치지직이 굴러가는걸 보면서 참 대단하다는 생각을 가지게 된 것 같다. 나는 실시간영상처리를 잘 할 자신이 전혀 없다...

ffmpeg 라이브러리 자체의 오류도 직접 두눈으로 확인해보고 대응해보는 등(비록 이미 beta버전에서 해결된 내용이었지만) 영상/음성 처리에 대해서는 꽤 깊게 업무하면서 다뤄봤던것 같다 흠.

기왕 뭔가 이렇게 딥다이브한김에 이걸 써먹어볼 방법이 없을까 라는 생각을 좀 하고 있는데, 영상과 음성과 관련한 사이드프로젝트 아이디어가 있다면은 적극적으로 제안을주시면은 참으로감사할것같읍니다...