홈 › 6장 아카이브와 AI 메타데이터

6.3 구간 복원(Partial Restore) - 필요한 바이트만 읽는 방법

1시간짜리 파일에서 1분만 필요할 때 전체를 복원하지 않고 구간만 읽는 구간 복원의 원리, 키프레임·GOP 경계와 인덱스로 읽을 바이트 범위를 계산하는 실험, 지원 포맷 확인의 중요성을 설명합니다.

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

#왜 구간 복원이 필요한가

아카이브에서 1시간짜리 영상(원본 XDCAM 50Mbps 기준 약 22GB)이 필요한 구간은 단 1분일 수 있습니다. 전체를 테이프에서 읽어 전송하고 저장하면 시간과 네트워크, 스토리지를 낭비합니다.

방식 읽는 양 시간
전체 복원 22GB 수십 분
구간 복원 약 0.4GB (1분) 수 분

구간 복원(Partial Restore, Partial File Restore, Partial Retrieve 등으로도 부릅니다)은 요청된 구간만 읽어서 새 파일로 만들어 주는 기능입니다. 뉴스 보도처럼 "그때 그 장면 10초"를 빨리 가져와야 하는 현장에서 아카이브 솔루션을 고르는 핵심 기준이 되기도 합니다.

#원리: 시간 → 바이트 위치

구간 복원의 핵심은 "3초부터 5초까지" 같은 시간 구간을 "파일의 몇 번째 바이트부터 몇 번째 바이트까지"로 바꾸는 것입니다. 이를 위해 프레임 위치를 알려 주는 인덱스가 필요합니다.

  • MXF: 파일 안에 Index Table이 있습니다(2장). 프레임 번호에 해당하는 바이트 오프셋을 알려 줍니다.
  • MP4/MOV: moov의 샘플 테이블(stts, stsz, stco)이 같은 역할을 합니다.
  • MPEG-TS: 전체 인덱스가 없어, 별도로 스캔해서 위치 인덱스를 만들어 두어야 합니다.

아카이브할 때 이 위치 정보를 미리 추출해서 DB에 저장해 두면, 복원할 때 테이프를 앞뒤로 감지 않고 필요한 위치로 바로 이동할 수 있습니다.

#키프레임 경계 문제

위치만 알아서는 부족합니다. 2장에서 설명한 것처럼 P/B 프레임은 앞의 I 프레임이 있어야 디코딩됩니다. 그러므로 3초 지점의 프레임을 쓰려면 그 이전의 가장 가까운 I 프레임부터 읽어야 하고, 구간의 끝도 다음 I 프레임 직전까지 읽어야 완결된 GOP가 됩니다.

요청 구간:              [3.00 ─────────── 5.00]
GOP 경계:   | 2.80 | 3.20 | 3.60 | 4.00 | 4.40 | 4.80 | 5.20 |
실제 읽기:  [2.80 ─────────────────────────────── 5.20 직전]

#실험: 읽을 바이트 범위 계산

아래 명령으로 만든 10초짜리 MPEG-2 4:2:2 50Mbps MXF(GOP N=12, B프레임 2장, 29.97fps, 약 62.3MB)에서 FFprobe의 패킷 정보(pos, size, 키프레임 플래그)를 이용해 읽어야 하는 범위를 계산했습니다.

ffmpeg -f lavfi -i testsrc2=size=1920x1080:rate=30000/1001 -t 10 -c:v mpeg2video -pix_fmt yuv422p \
  -b:v 50M -minrate 50M -maxrate 50M -bufsize 17M -g 12 -bf 2 -flags +ildct+ilme -top 1 -f mxf xd.mxf
import json
import subprocess
import sys


def packets(path):
    cmd = ['ffprobe', '-v', 'error', '-select_streams', 'v:0', '-show_entries',
           'packet=pts_time,pos,size,flags', '-of', 'json', path]
    out = subprocess.run(cmd, capture_output=True, text=True, check=True).stdout
    pk = [p for p in json.loads(out)['packets'] if p.get('pos') not in (None, 'N/A')]
    for p in pk:
        p['pts'] = float(p['pts_time'])
        p['pos'] = int(p['pos'])
        p['size'] = int(p['size'])
        p['key'] = 'K' in p['flags']
    return sorted(pk, key=lambda p: p['pos'])


def byte_range(pk, t_in, t_out):
    keys = [i for i, p in enumerate(pk) if p['key']]
    start_i = max(i for i in keys if pk[i]['pts'] <= t_in)     # t_in 이하의 마지막 키프레임
    after = [i for i in keys if pk[i]['pts'] > t_out]
    end_i = (after[0] - 1) if after else len(pk) - 1            # t_out 이후 첫 키프레임 직전
    start = pk[start_i]['pos']
    end = pk[end_i]['pos'] + pk[end_i]['size']
    return start, end, pk[start_i]['pts'], pk[end_i]['pts']

실행 결과는 다음과 같습니다.

키프레임 위치(pts): 0.0, 0.4, 0.801, ... 2.803, 3.203, 3.604, ... (12프레임 ≈ 0.4초 간격)

요청 구간      : 3.00s ~ 5.00s (2.00초)
읽기 시작      : pts 2.803s 키프레임부터, 다음 키프레임(5.205s) 직전까지
바이트 범위    : 16,646,144 ~ 31,722,654  (15,076,510 bytes)
비디오 스트림 전체 대비: 24.3%

요청 구간      : 3.50s ~ 3.60s (0.10초)
읽기 시작      : pts 3.203s 키프레임부터, 다음 키프레임(3.604s) 직전까지
바이트 범위    : 19,159,040 ~ 21,671,070  (2,512,030 bytes)
비디오 스트림 전체 대비: 4.0%

해석합니다.

  • 2초 구간 요청은 비디오 데이터의 24.3%를 읽습니다. 10초 중 2초면 20%이지만, 앞뒤를 GOP 경계에 맞추느라 실제로는 약 2.4초(2.803~5.205초)를 읽습니다.
  • 0.1초(3프레임) 구간을 요청해도 완결된 GOP를 읽어야 하므로 약 0.4초 분량인 2.5MB(4.0%)가 필요했습니다. 요청 길이보다 GOP 단위로 올림됩니다. 요청이 아주 짧아도 최소 1개 GOP분은 읽습니다.
  • Open GOP에 주의하세요. 이 파일에서는 각 키프레임 바로 뒤에 오는 B프레임 2장의 표시 시각(pts)이 키프레임보다 앞섭니다(0.4초 키프레임 뒤에 pts 0.33, 0.37인 프레임). 이 프레임들은 앞 GOP의 마지막 P프레임을 참조하므로, 키프레임부터만 잘라 내면 앞 2프레임을 정상적으로 디코딩할 수 없습니다. 구간 복원 기능은 이런 프레임을 버리거나 앞 GOP 일부를 함께 읽어야 합니다.
  • 이 계산에서 오디오와 부가 데이터는 제외했습니다. 실제 복원은 영상, 오디오, 타임코드를 모두 포함한 새 파일을 만들어야 합니다(MXF라면 헤더 메타데이터와 인덱스도 새로 생성).

#지원 포맷 확인이 중요한 이유

구간 복원을 처음 도입하는 프로젝트에서 흔한 실수는 "구간 추출 지원"이라는 말만 믿는 것입니다. 실제로는 다음 중 일부만 지원하는 경우가 많습니다.

확인 항목 질문
컨테이너 MXF OP1a? OP-Atom? MOV? MP4?
코덱·프로파일 XDCAM HD(35/50Mbps), D-10(IMX), DNxHD, ProRes, AVC-Intra, H.264 ...
해상도·프레임레이트 HD만? UHD도? 인터레이스?
오디오 구성 모노 N트랙? 몇 채널까지?
결과 파일 원본과 같은 포맷으로 생성? 타임코드는 유지?
프레임 정확도 GOP 경계 단위? 프레임 단위(재인코딩 포함)?

RFP(제안요청서)에는 필요한 코덱·컨테이너·프로파일 이름을 구체적으로 적고, 도입 전에 실제 소재 샘플로 구간 복원 테스트를 해야 합니다. 새로운 코덱 규격이 방송 현장에 도입된 시점에는 해당 제품이 아직 지원하지 않을 수 있습니다.

#구간 복원 설계 체크리스트

  1. 아카이브 시 인덱스 추출: 프레임 위치 정보를 추출해 DB 또는 별도 파일로 보관한다.
  2. 요청 단위: 타임코드 in/out을 받아 프록시와 같은 타임코드 체계로 변환한다(1장, 2장).
  3. GOP 정책: 정확한 프레임 컷이 필요하면 경계 GOP를 재인코딩할지(스마트 렌더링) 결정한다.
  4. 결과 검증: 복원된 파일이 의도한 구간인지(시작 타임코드, 길이) 분석 도구로 확인한다(3장).
  5. 여유(핸들): 편집을 위해 구간 앞뒤로 몇 초의 여유를 포함하는 옵션을 제공한다.

#정리

  • 구간 복원은 시간 구간을 바이트 범위로 바꾸어 필요한 만큼만 읽는 기능이며, 대용량 아카이브에서 시간·비용을 크게 줄입니다.
  • 키프레임/GOP 경계 때문에 요청보다 조금 더 읽고(실험에서 0.1초 요청에 약 2.5MB, 4.0%), 읽은 데이터를 유효한 파일로 재구성하는 로직은 포맷마다 별도 구현이 필요합니다.
  • 제품 선택과 RFP에서는 지원 코덱·컨테이너를 명시하고 실제 샘플로 테스트하세요.