Cloud Recording 탐색 재생 장애 — moov, ENDLIST, DISCONTINUITY
녹화 파일을 중간 위치로 탐색할 때 멈추는 문제를 MP4 index, HLS playlist 종료 상태, timestamp discontinuity 관점에서 진단합니다. `ffprobe`, manifest 검사, ffmpeg remux가 각각 확인하거나 복구할 수 있는 범위를 구분하고, 원본 segment 누락처럼 후처리로 복구할 수 없는 경우도 명시합니다.
목차(21개 항목)
- 첫 가설 — 인덱스와 경계 정보부터 확인
패턴 ① MP4 moov atom이 끝에 있을 때
패턴 ② HLS m3u8의 ENDLIST 누락
패턴 ③ DISCONTINUITY 태그 누락
- 통합 진단 흐름
- Agora Cloud Recording 후처리 권장 파이프라인
- 빠른 체크리스트
- 한 줄 결론
- 관련 글
- 참고 자료
"Cloud Recording에서 받은 영상 — 처음부터 재생은 잘 되는데 중간으로 점프하면 무한 로딩, 또는 아예 재생 안 됨". 다운로드가 끝났어도 컨테이너 인덱스, 타임스탬프, 키프레임, 플레이리스트 상태를 각각 확인해야 합니다.
이 글은 그 인덱스 불완전성의 3가지 패턴을 분류하고, 각각의 진단·처방을 정리합니다.
첫 가설 — 인덱스와 경계 정보부터 확인
영상을 점프하려면 "이 시점은 파일의 어느 위치"라는 매핑 정보가 필요합니다. 그 인덱스가 없거나 손상돼도 순차 재생은 가능합니다(처음부터 데이터를 따라가기만 하면 되니까). 점프할 때만 실패합니다.
세 가지 인덱스 문제 패턴:
| 패턴 | 컨테이너 | 증상 트리거 |
|---|---|---|
| ① moov atom이 파일 끝에 있음 | MP4 | HTTP progressive download 시작이 늦거나 Range 요청이 추가됨 |
② #EXT-X-ENDLIST 누락 | HLS (m3u8) | 플레이어가 갱신 가능한 플레이리스트로 처리할 수 있음 |
③ #EXT-X-DISCONTINUITY 누락 | HLS | 코덱/해상도 변경 지점에서 디코더 초기화 실패 |
패턴 ① MP4 moov atom이 끝에 있을 때
구조
MP4는 두 부분으로 구성됩니다.
녹화 도중엔 전체 길이를 모르므로 인덱스를 끝에 붙이는 게 자연스럽습니다. 하지만 그 상태로 플레이어에 던지면:
- 로컬 파일: 플레이어가
moov를 한 번 읽은 뒤 인덱스를 이용 - HTTP progressive download: 파일 끝의
moov를 받기 위해 추가 Range 요청이 필요할 수 있어 시작이 늦어짐
진단
처방 — +faststart
-c copy: 재인코딩 없이 컨테이너만 다시 작성+faststart: moov를 파일 앞으로 옮김- 결과물: progressive download 시작 조건 개선. seek 성공 여부는 타임스탬프와 키프레임 상태에도 좌우됨
패턴 ② HLS m3u8의 ENDLIST 누락
정상 m3u8 구조
누락 시 증상
#EXT-X-ENDLIST가 없으면 플레이어는 플레이리스트가 계속 갱신될 수 있다고 판단합니다.
- 마지막 세그먼트에 도달한 뒤 플레이리스트를 다시 요청할 수 있음
- seek 가능 범위는 현재 플레이리스트가 제공하는 미디어 시간 범위와 플레이어 구현에 따라 달라짐
녹화가 비정상 종료되면 종종 발생합니다. Agora Cloud Recording에서도 stop API 누락·서버 크래시·storage upload 실패 같은 케이스에서 ENDLIST가 안 붙을 수 있습니다.
진단
처방
녹화가 완전히 종료됐음을 확인한 뒤 — 한 줄 추가:
진행 중인 EVENT/라이브 플레이리스트에 임의로 추가하면 이후 세그먼트를 잘라낸 것으로 보일 수 있습니다. 녹화 종료와 업로드 완료를 먼저 확인하세요.
근본 — mp4로 변환:
ffmpeg이 현재 플레이리스트에 나열된 세그먼트를 읽어 단일 MP4로 정리합니다. 누락된 세그먼트나 잘못된 타임스탬프까지 자동 복구하는 것은 아닙니다.
패턴 ③ DISCONTINUITY 태그 누락
개념
HLS에서 파일 형식, 트랙 구성·식별자, 타임스탬프 시퀀스가 바뀌면 #EXT-X-DISCONTINUITY가 필요합니다. 인코딩 파라미터나 인코딩 시퀀스가 바뀌는 지점에도 이 태그 사용이 권고됩니다.
이 태그 없이 segment 특성만 바뀌면:
- 처음부터 재생: segment 1→2→3 순차 진행이라 디코더가 알아서 재초기화 시도
- 점프: 특성 다른 segment로 바로 갔는데 m3u8엔 변경 표시 없음 → 디코더가 이전 segment 기준으로 가정 → 초기화 실패 → 무한 로딩
발생 시나리오 (Cloud Recording 맥락)
| 트리거 | 무엇이 바뀌나 |
|---|---|
| 실제 출력 트랙 구성이 바뀜 | audio-only ↔ audio+video 등 |
| 누군가 해상도 변경 | width/height/bitrate |
| 합성 출력의 인코딩 파라미터가 바뀜 | 코덱·해상도·타임스탬프 시퀀스 등 |
| Cloud Recording의 High Availability mechanism 작동 | 녹화 서버 자동 전환으로 m3u8이 둘로 쪼개짐 (bak0, bak1 … prefix) |
진단
각 segment의 width/height/codec/framerate가 다르면 그 사이에 DISCONTINUITY가 있어야 합니다.
처방
가장 확실 — 재인코딩으로 정규화:
- 재인코딩하면 모든 frame이 균일한 코덱/해상도로 통일됨 → discontinuity 자체가 사라짐
- 처리 시간은 하드웨어·코덱·프리셋에 따라 측정
- seek 간격을 일정하게 하려면 프레임레이트에 맞춰 IDR 간격과 scene-cut 정책도 함께 설정
Agora HA bak 파일 합치기: HA mechanism이 작동하면 fault processing center가 90초 이내에 새 녹화 서버로 전환하고, 그 시점부터의 인덱스를 담은 새 m3u8(bak0, bak1 …)을 생성합니다. Agora는 이 원본 m3u8과 bak m3u8을 단일 mp4로 합쳐주는 자체 transcoder script를 제공합니다(composite/mix mode 한정). Manage Recorded Files 문서의 다운로드 링크 참조. 단순 ffmpeg concat이 아니라 경계에 discontinuity를 적절히 처리해줍니다.
통합 진단 흐름
Agora Cloud Recording 후처리 권장 파이프라인
매번 클라이언트 쪽에서 이 문제를 만나는 게 싫다면, 결과물을 그대로 전달하지 말고 표준 후처리 파이프라인을 한 번 거치는 걸 권장합니다.
핵심 옵션 의미:
| 옵션 | 의미 |
|---|---|
-c copy | 재인코딩 없이 컨테이너만 변경 (빠름, 화질 손실 없음) |
-movflags +faststart | moov atom을 앞으로 |
-fflags +genpts | DTS가 있을 때 누락된 PTS 생성. 입력 옵션이므로 -i 앞에 배치 |
-g 60 -keyint_min 60 | 최대 GOP와 최소 keyint 설정. 정확한 60프레임 주기는 frame rate·scene-cut 설정도 필요 (#30 참고) |
실무 팁: Agora 문서상 composite recording은
recordingFileConfig.avFileType: ["hls", "mp4"]로 MP4를 함께 생성할 수 있습니다. 전달 전 실제 파일의 atom 순서와 seek 동작을 검사하고, 필요한 경우에만+faststart후처리를 적용하세요.
빠른 체크리스트
받은 결과물에서 점프 이슈가 있을 때, 5분 안에 원인을 좁히는 순서:
한 줄 결론
점프 실패는 한 가지 원인으로 단정하지 않습니다. moov 위치, ENDLIST, DISCONTINUITY를 먼저 확인하고, 이어서 타임스탬프·키프레임·누락 세그먼트를 검사합니다.
관련 글
- #11 Cloud Recording 녹화 파일 저장 — S3 — 후처리 대상인 원본 파일이 어디에 어떤 형태로 떨어지는지
- #15 M3U8과 TS — HLS Deep Dive — ENDLIST/DISCONTINUITY가 왜 필요한지 playlist 구조부터
- #17 FFmpeg 실전 파이프라인 — Agora 녹화 VOD — 본문 후처리 파이프라인의 전체 버전
- #16 FFmpeg 컨테이너/코덱/스트림 — moov atom·
-c copy·재컨테이닝의 컨테이너 기초 - #30 x264 실전 옵션 — ref/tune/keyint — 재인코딩 시
-g/keyint로 점프 성능을 보장하는 키프레임 전략 - #31 라이브 스트림 상태 점검 — ffprobe/gstreamer — segment 특성 비교에 쓴 ffprobe 진단 심화
참고 자료
- Cloud Recording — Manage recorded files (Agora Docs) — MP4 출력, HA의
bak<n>명명 규칙과 transcoder script - Cloud Recording — Core concepts (Agora Docs) — 현재 Cloud Recording 개념과 출력 구조
- ffmpeg(1) — movflags
faststart(FFmpeg Documentation) — moov atom을 파일 앞으로 옮기는+faststart의 공식 설명 - RFC 8216 — HTTP Live Streaming: EXT-X-ENDLIST / EXT-X-DISCONTINUITY / EXT-X-PLAYLIST-TYPE — HLS playlist 태그의 표준 정의와 VOD/Live 구분 의미
- QuickTime File Format — Movie Atom (moov) (Apple Developer) — MP4/QuickTime 인덱스인 moov atom의 구조
- ffmpeg -fflags genpts / -movflags (FFmpeg Formats Documentation) — 파이프라인에서 쓴
+genpts·muxer 플래그의 1차 레퍼런스