조각난 데이터는 재생되지만, 조각을 합친 파일은 재생이 안된다?
스트리밍 환경에서는 오디오 데이터를 작은 조각(Segment) 형태로 나눠 전송하고 브라우저에서는 이를 실시간으로 받아 재생한다.
이때 많이 사용되는 형식 중 하나가 fMP4 (Fragmented MP4) 이다.
그렇다면 이렇게 전송된 조각들을 모두 모아서 하나의 Blob 파일로 만들면 그 파일도 브라우저에서 재생이 될까?
정답은 ❌, 불가능하다.
왜냐하면 브라우저가 MSE(Media Source Extensions) 를 통해 스트리밍 데이터를 재생하는 방식과, 일반적으로 파일을 재생하는 방식은 근본적으로 구조가 다르기 때문이다.
구분 | 스트리밍 (MSE) | 일반 파일 재생 (파일 URL / <audio> 등) |
---|---|---|
데이터 입력 방식 | 조각(segment) 단위로 실시간 추가 | 완성된 파일 전체 |
메타데이터 요구 | init segment로 시작, 이후 조각 입력 | 전체 구조가 완전해야 함 (moov atom 등) |
내결함성 | 어느 정도 허용 (끊겨도 복구 가능) | 메타정보 누락 시 재생 실패 |
MSE는 스트리밍을 전제로 하기 때문에 덜 완전한 구조도 잘 처리할 수 있다.
하지만 Blob(파일)으로 저장한 fMP4는 정상적인 MP4 구조가 아니기 때문에 브라우저가 직접 재생할 수 없다.
const blob = new Blob([initSegment, ...mediaSegments], { type: 'audio/mp4; codecs=opus' });
이렇게 만든 Blob 파일은 겉보기엔 .mp4처럼 보여도 실제로는 조각만 합쳐놓은 것일 뿐이다.
[moov][atom](재생 정보를 담는 필수 메타데이터)이 없거나 잘못되어 있으면 브라우저는 이 파일을 완성된 MP4로 간주하지 못해 재생할 수 없다.
결국 MSE에서는 재생 가능한 데이터라도 일반적으로 저장하고 재생하려면 조각난 데이터를 읽고 정상적인 포맷으로 재구성하는 포맷 정리(remux) 과정이 필요하다.
remux를 통해 처리되는 것?
- 잘린 타임스탬프 복원
- 메타데이터 삽입
- 정상적인 컨테이너 구조 생성
즉, Remux는 압축된(인코딩된) 데이터 자체는 건드리지 않고 그 데이터를 새로운 컨테이너 포맷에 깔끔하게 다시 담는 작업이다. 이 과정을 통해 우리가 알고있는 재생가능한 온전한 오디오 파일이 생성된다.
모든 파일을 브라우저에서 재생할 수 있다면 참 행복할텐데!
하지만 세상은 그렇게 호락호락하지 않다...
브라우저가 지원하는 코덱과 컨테이너 조합이 제한돼있기 때문이다.
fMP4를 예시로 들면 remux과정을 통해 우리는 깔끔한 mp4 오디오파일을 얻을 수 있다. 하지만 이 파일을 그대로 브라우저에서 재생할 수 있을까?
답은 그럴 수도
있고, 아닐 수도
있다.
MP4라는 컨테이너는 재생 가능한 형식일 수 있지만, 그 안에 담긴 오디오 코덱은 얘기가 다르다. 가령 스트리밍에 최적화된 오디오 코덱인 Opus는 MP4와 조합될 경우 최악의 결과를 낳는다. 대부분의 브라우저가 이 조합을 지원하지 않기 때문이다.
[fMP4 + Opus] 조합은 스트리밍에는 최적화돼 있지만 파일로 저장해서 일반 플레이어로 재생하기엔 아주 까다로운 조합이다. 그렇기에 단순히 mp4형식의 파일로 remux하더라도 chrome같은 브라우저에선 재생이 불가능하다.
따라서 브라우저가 지원하는 코덱 + 컨테이너 조합을 정확히 이해하고 Remux 과정에서 그에 맞게 조합을 선택하는 것이 매우 중요하다.
예를 들어 [fMP4 + Opus] 조합이라면 컨테이너를 MP4 → WebM으로 바꾸거나
오디오 코덱을 Opus → AAC로 변환하면 대부분의 브라우저에서 재생이 잘 된다.
최근에 파일을 저장하려다 이 문제로 꽤 오래 헤맸는데, 오디오 데이터와 파일 구조의 기본 원리를 제대로 이해하고 있었다면 가볍게 해결할 수 있었을지도 모른다.
언제나 기본을 탄탄히 다져두는 게 중요하다는 걸 다시 한 번 느꼈다… (ㅠㅠ)