홈 › 3장 MediaInfo와 파일 분석

3.5 MXF 내부 구조 검사와 손상 파일 판별

MXF 구조 분석 도구(MXF Inspect)의 활용법과, KLV를 직접 읽어 파티션·Footer·RIP 유무로 파일 완결성을 점검하는 파이썬 스크립트를 소개합니다.

최종 수정 2026-10-28 · 읽는 시간 약 4분 · 운영 프리즘(Prism) · 편집 원칙

#왜 MXF를 따로 검사하는가

MediaInfo와 FFprobe는 MXF에서 코덱과 길이를 잘 읽어 주지만, 파일이 구조적으로 온전한지는 알려 주지 않는 경우가 많습니다. 방송 소재 교환에서는 다음과 같은 문제가 실제로 발생합니다.

  • 녹화 중 중단되어 Footer와 RIP가 없는 파일
  • 전송 중 잘려서 뒷부분이 사라진 파일
  • 파티션 포인터가 틀려서 일부 장비에서만 열리지 않는 파일
  • 인덱스 테이블이 없어서 탐색이 느리거나 불가능한 파일

이런 파일은 재생기에서는 열리는 것처럼 보여도 송출 서버나 편집 시스템에서 문제를 일으킵니다. 그래서 MXF 구조를 직접 보여 주는 도구가 필요합니다.

#MXF Inspect

MXF Inspect는 Myriadbits가 공개한 무료 오픈소스(LGPL) Windows 도구로, MXF 파일의 내부 구조를 트리와 오프셋 단위로 보여 줍니다. 영상을 재생하는 도구가 아니라 구조를 들여다보는 도구입니다. 프로젝트 소개에 나온 주요 기능은 다음과 같습니다.

  • 여러 MXF 파일을 동시에 열기, 수 GB 이상의 큰 파일 지원
  • 물리적 보기(파일 오프셋 순서)와 논리적 보기(객체 계층) 제공
  • 오프셋, 해석된 값, 원시 데이터(raw)를 함께 표시
  • 같은 종류의 객체로 이동하거나 특정 종류만 필터링
  • 검증 리포트: 파티션 일관성(이전/다음/Footer 파티션 위치 값이 올바른지), RIP 존재 및 유효성 점검

MXF 파일에서 무엇이 어디에 있는지 눈으로 확인할 수 있으므로, "왜 이 파일이 안 열리는가"를 조사할 때 가장 먼저 쓸 만한 도구입니다. 다만 개발자 스스로 밝히듯 SMPTE MXF 규격 전체를 구현한 것은 아니며 보증 없이 제공되는 도구입니다. 검증 결과는 참고 자료로 쓰고, 이상이 있을 때 원인을 사람이 확인하는 용도로 쓰는 것이 적절합니다. 라이선스(LGPL)를 확인한 뒤 사내 도구에 연동하세요.

#직접 KLV를 읽어 보기

도구 없이도 MXF의 큰 구조는 KLV를 순서대로 읽는 간단한 스크립트로 확인할 수 있습니다. 2장에서 설명한 대로 모든 데이터는 Key(16바이트) + Length(BER) + Value입니다. 아래 스크립트는 최상위 KLV를 훑으며 파티션 팩과 주요 요소를 분류해서 요약합니다.

import sys
from collections import Counter

PARTITION = {0x02: 'Header', 0x03: 'Body', 0x04: 'Footer'}
STATUS = {0x01: 'Open Incomplete', 0x02: 'Closed Incomplete',
          0x03: 'Open Complete', 0x04: 'Closed Complete'}
SMPTE = bytes.fromhex('060e2b34')


def read_ber(f):
    b = f.read(1)
    if not b:
        return None
    n = b[0]
    return n if n < 0x80 else int.from_bytes(f.read(n & 0x7F), 'big')


def classify(key):
    if key[:4] != SMPTE:
        return 'Unknown'
    group = key[8:13].hex()          # 0d01020101 = MXF 구조(파티션/프라이머/인덱스/RIP)
    if group == '0d01020101':
        kind = key[13]
        if kind in PARTITION:
            return f"{PARTITION[kind]} Partition Pack ({STATUS.get(key[14], '?')})"
        return {0x05: 'Primer Pack', 0x10: 'Index Table Segment',
                0x11: 'Random Index Pack'}.get(kind, 'Structure (other)')
    if key[4:8].hex() == '01010102' and key[8:13].hex() == '0301021001':
        return 'KLV Fill'
    if key[4:6].hex() == '0253':
        return 'Header Metadata set'
    if key[8:12].hex() == '0d010301':   # Generic Container
        return 'System Item' if key[4:6].hex() == '0205' else 'Essence element'
    return 'Other'


def scan(path):
    counts, layout = Counter(), []
    with open(path, 'rb') as f:
        while True:
            pos = f.tell()
            key = f.read(16)
            if len(key) < 16:
                break
            length = read_ber(f)
            if length is None:
                break
            name = classify(key)
            counts[name] += 1
            if 'Partition Pack' in name or name == 'Random Index Pack':
                layout.append((pos, name))
            f.seek(length, 1)
    return layout, counts


if __name__ == '__main__':
    layout, counts = scan(sys.argv[1])
    for pos, name in layout:
        print(f'{pos:>10}  {name}')
    print('--')
    for name, n in counts.most_common():
        print(f'{n:>6}  {name}')

파티션 팩의 키 14번째 바이트(0부터 세면 13)가 종류(02 Header, 03 Body, 04 Footer), 15번째 바이트가 상태(04 Closed Complete 등)입니다.

#정상 파일의 결과

2장에서 만든 5초짜리 OP1a MXF를 검사한 결과입니다.

         0  Header Partition Pack (Closed Complete)
      7168  Body Partition Pack (Closed Complete)
  23324160  Footer Partition Pack (Closed Complete)
  23327232  Random Index Pack
--
   605  KLV Fill
   600  Essence element
   150  System Item
    34  Header Metadata set
     1  Header Partition Pack (Closed Complete)
     1  Primer Pack
     1  Body Partition Pack (Closed Complete)
     1  Footer Partition Pack (Closed Complete)
     1  Index Table Segment
     1  Random Index Pack

해석: Header → Body → Footer → RIP 순서가 모두 있고, 프레임마다 System Item이 하나씩 있습니다(150프레임 = 150개). 인덱스 테이블도 있습니다.

#뒷부분이 잘린 파일의 결과

같은 파일의 앞 20MB만 남긴 파일입니다.

         0  Header Partition Pack (Closed Complete)
      7168  Body Partition Pack (Closed Complete)
--
   517  KLV Fill
   515  Essence element
   129  System Item
    34  Header Metadata set
     ...

Footer Partition과 RIP, Index Table이 없습니다. System Item도 129개로 줄었습니다. 그런데 주목할 점이 하나 있습니다.

그러면 MediaInfo는 어떨까요? 라이브러리의 IsTruncated 항목을 조회하면 다음과 같이 나왔습니다.

항목 정상 파일 잘린 파일
IsTruncated (General) (빈 값) Yes
FrameCount (Video) 150 129
Duration 5005 ms 5005 ms

Duration은 같지만 FrameCount와 IsTruncated가 다릅니다. 정리하면 MXF의 완결성은 다음 조합으로 점검해야 합니다.

  1. Footer Partition과 RIP가 존재하는가 (KLV 스캔)
  2. MediaInfo의 IsTruncated 가 Yes가 아닌가
  3. ffprobe -count_frames로 읽은 프레임 수가 헤더의 길이 × 프레임레이트와 일치하는가

#인제스트 완결성 검사 규칙 예시

규칙 실패 시 처리
Header, Footer, RIP가 모두 있다 격리 폴더로 이동, 담당자 알림
IsTruncated가 Yes가 아니다 재전송 요청
실제 프레임 수가 길이 × fps의 ±1프레임 이내 소재 등록 보류
OP-Atom 세트에서 영상/오디오 파일 개수가 맞다 누락 파일 대기 후 재확인

#정리

  • MXF 구조 검사는 Header, Body, Footer, RIP, Index Table의 존재와 일관성을 확인하는 일입니다.
  • MXF Inspect 같은 도구로 눈으로 확인하고, 자동화에는 KLV 스캔이나 MediaInfo IsTruncated를 사용합니다.
  • 헤더의 상태나 길이 값만으로는 파일이 온전하다고 판단할 수 없습니다. Footer/RIP와 실제 프레임 수까지 확인해야 합니다.

#참고 자료

링크는 2026-10-28에 열람이 되는 것을 확인했습니다. 규격과 도구 문서는 개정될 수 있으므로 적용하기 전에 최신 판을 확인하세요.