← 블로그로 돌아가기

HLS / m3u8 스트림의 일부만 다운로드하는 방법 (전체가 아닌)

m3u8/HLS 스트림에서 특정 시간 범위를 추출하는 방법 — ffmpeg 검색, 세그먼트 인덱스 계산, 브라우저 클립 방법

3시간짜리 HLS VOD가 있고 47분에서 52분만 원한다고 가정해보세요. 전체 매니페스트를 다운로드하면 수백 개의 세그먼트, 기가바이트의 데이터, 그리고 비디오 편집기에서 5분 정도 자르는 작업이 필요합니다. 이건 잘못된 접근입니다. HLS는 작고 독립적으로 가져올 수 있는 청크로 구성되어 있기 때문에, 시간 윈도우 외부에 있는 것을 다운로드할 이유가 없습니다. 단지 어떤 청크가 겹치는지 파악하면 됩니다.

이 글은 이 주제에서 더 기술적인 것입니다. 이미 HLS와 m3u8이 대략 무엇인지 알고 있다고 가정합니다. 모른다면 먼저 m3u8 / HLS 스트림을 다운로드하는 방법을 읽어서 전체 프로토콜을 이해한 후에 돌아오세요. 여기서의 목표는 더 좁습니다: 미디어 플레이리스트와 [start, end] 시간 범위가 주어졌을 때, 필요한 바이트만 얻는 것입니다.

세 가지 방법을 다룰 것입니다 — 매니페스트 URL에 대한 ffmpeg 검색, 범위 인식 CLI 다운로더, 그리고 겹치는 세그먼트만 가져오는 브라우저 내 클립 확장 — 그리고 이 모든 것을 작동하게 하는 세그먼트 인덱스 계산입니다.

섹션 다운로드가 가능한 이유

HLS 미디어 플레이리스트는 기본적으로 지속 시간이 있는 세그먼트의 순서 있는 목록입니다. 실제 플레이리스트를 단순화하면 다음과 같습니다:

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:6
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-PLAYLIST-TYPE:VOD

#EXTINF:6.000,
segment000.ts
#EXTINF:6.000,
segment001.ts
#EXTINF:6.000,
segment002.ts
#EXTINF:6.000,
segment003.ts
...
#EXTINF:4.200,
segment842.ts
#EXT-X-ENDLIST

핵심 줄은 #EXTINF입니다. 이것은 그 다음에 오는 세그먼트의 정확한 지속 시간을 선언합니다. 플레이어가 세그먼트를 순서대로 연결하기 때문에, 세그먼트 _n_의 시작 시간은 단순히 그 앞의 모든 #EXTINF 지속 시간의 합입니다. 세그먼트 0은 [0, 6)을 포함하고, 세그먼트 1은 [6, 12)을, 세그먼트 2는 [12, 18)을 포함합니다.

이 특성이 부분 다운로드를 가능하게 합니다. 윈도우 [T_start, T_end)를 얻으려면 전체 플레이리스트가 필요한 것이 아니라 시간 범위가 그 윈도우와 겹치는 인접한 세그먼트들의 연속 실행이 필요합니다. 첫 번째 겹치는 세그먼트 전의 모든 것과 마지막 겹치는 세그먼트 후의 모든 것은 건너뛸 수 있는 불필요한 것입니다.

이로부터 두 가지 결과가 즉시 따라옵니다:

  1. 임의의 프레임에서 자유롭게 자를 수 없습니다. 세그먼트는 키프레임 경계에서 시작합니다 (잘 형성된 스트림에서 각 세그먼트는 새로운 IDR 프레임으로 열립니다). 복사만 하는 재인코딩 없는 추출을 원한다면, 가장 저렴한 자르기 지점은 세그먼트 경계입니다. 더 미세한 정확도는 경계 세그먼트를 재인코딩해야 합니다.
  2. 수학은 #EXTINF 값에만 의존합니다, 세그먼트 파일 이름, 바이트 오프셋, 또는 CDN이 숨기는 것에는 영향을 받지 않습니다. 미디어 플레이리스트를 읽을 수 있으면 세그먼트 범위를 직접 계산할 수 있습니다.

시간 범위를 세그먼트 인덱스로 매핑하기: 실제 예제

세그먼트가 균일하게 6초이고 10:00부터 15:00 — 즉, [600s, 900s) —를 원한다고 가정하세요.

  • 첫 번째 겹치는 세그먼트 인덱스: floor(600 / 6) = 100. 세그먼트 100은 [600, 606)을 포함합니다.
  • 마지막 겹치는 세그먼트 인덱스: ceil(900 / 6) - 1 = 149. 세그먼트 149는 [894, 900)을 포함합니다.

따라서 100부터 149까지 — 843개 모두가 아닌 50개 세그먼트, 300초가 필요합니다. 정확히 그것들을 연결하면, 10:00에 정확히 시작하는 (600이 세그먼트 경계이기 때문에) 그리고 15:00에 정확히 끝나는 깔끔한 5분 클립을 얻습니다.

실제 플레이리스트는 완벽하게 균일한 지속 시간을 거의 가지지 않습니다 — 마지막 세그먼트는 짧고, 광고 삽입 지점은 타이밍을 재설정하고, 라이브 인코더는 표류합니다. 따라서 강력한 접근법은 상수로 나누지 말고 — 축적하세요:

# Pseudocode: time range -> segment index range
t = 0.0
first = last = None
for i, dur in enumerate(extinf_durations):
    seg_start, seg_end = t, t + dur
    if seg_end > T_start and first is None:
        first = i                      # first segment that reaches into the window
    if seg_start < T_end:
        last = i                       # keeps advancing while segments still overlap
    t = seg_end
# fetch segments[first .. last] inclusive

이 루프는 아래의 모든 도구가 내부적으로 정확히 수행하는 것입니다. 메서드 간의 차이는 그들이 당신을 위해 처리하는 주변 작업의 정도입니다 — 인증, 키 가져오기, 오디오/비디오 쌍 맞추기, URL 서명.

방법 1: m3u8 URL에 대한 ffmpeg 검색

ffmpeg는 HLS를 기본적으로 이해하므로, 가장 직접적인 부분 다운로드는 매니페스트 URL에 검색과 지속 시간을 전달하는 것입니다:

ffmpeg -ss 00:10:00 -t 00:05:00 -i "https://example.com/video/720p/playlist.m3u8" -c copy clip.mp4

-ss는 시작 (10:00으로 검색), -t는 캡처할 지속 시간 (5분), -c copy는 패킷을 재인코딩 없이 직접 스트리밍합니다. -c copy를 사용하면 ffmpeg은 키프레임 경계에서만 자를 수 있으므로, 실제 클립은 10:00에 있거나 그 직전의 키프레임부터 시작합니다 — 보통 우리 예제의 세그먼트 100의 시작입니다. 이것은 빠르고 손실 없는 캡처를 원할 때 정확히 원하는 것입니다.

-ss before -i vs after -i

-i에 대한 -ss의 위치는 여기서 가장 중요한 세부 사항이며, 속도와 정확도를 맞바꿉니다:

  • -ss before -i (입력 검색) — ffmpeg는 디코딩 전에 디먹서 수준에서 검색합니다. HLS의 경우, 이는 다운로드할 필요가 없는 전체 세그먼트를 건너뛸 수 있다는 의미입니다. 빠르고, 네트워크 저렴하며, 섹션을 캡처하기 위한 올바른 기본값입니다. 정확도는 가장 가까운 키프레임까지입니다.
  • -ss after -i (출력 검색) — ffmpeg는 처음부터 읽고, 디코딩하고, 타임스탬프에 도달할 때까지 프레임을 버립니다. 프레임 정확하지만, 시작점까지 모든 것을 가져와야 하고 (종종 디코딩도) 합니다. 3시간 VOD의 10:00부터 시작하는 클립의 경우, 이것은 느리고 목적을 위반합니다.

빠른 섹션 다운로드를 위해 -ss-i 전에 유지하세요. 인 포인트에서 프레임 정확도가 필요하다면, 일반적인 방법은 대상 직전까지 입력 검색 (빠름) 하고 작은 나머지를 출력 검색 (정확) 하는 것인데, 이것은 헤드를 재인코딩해야 합니다:

# Fast input seek to ~9:58, then frame-accurate trim to 10:00, re-encoding only the boundary
ffmpeg -ss 00:09:58 -i "https://example.com/video/720p/playlist.m3u8" \
  -ss 00:00:02 -t 00:05:00 -c:v libx264 -c:a aac clip.mp4

-c copy 속도를 잃지만, 전체 클립이 아닌 재인코딩하는 초만큼만입니다.

솔직한 주의 사항들

매니페스트에 대한 ffmpeg은 스트림이 도달 가능하고 서명되지 않았을 때 올바른 도구입니다. 그렇지 않을 때는 빨리 복잡해집니다:

  • 만료되는 서명된 URL. 많은 CDN은 60–300초에 만료되는 토큰으로 매니페스트 (그리고 때때로 각 세그먼트)에 서명합니다. DevTools에서 URL을 복사하고 ffmpeg이 긴 클립을 처리하는 동안 토큰이 만료되면, 다운로드 중간에 403 오류를 얻습니다. 5분 복사의 경우 이것은 종종 괜찮습니다; 더 길거나 타이밍을 놓친 것은 실제 문제입니다.

  • AES-128 암호화. 플레이리스트가 #EXT-X-KEY:METHOD=AES-128,URI="key.bin"을 전달하면, ffmpeg은 자동으로 키를 가져와서 적용합니다 — 키 URL이 가진 요청 컨텍스트로 도달 가능한 경우에만. 키 엔드포인트가 쿠키나 헤더를 필요로 하고 순수한 ffmpeg 호출이 보내지 않으면, 복호화가 실패합니다.

  • 별도의 오디오 및 비디오 매니페스트. 최신 HLS는 보통 오디오와 비디오를 별도의 미디어 플레이리스트로 분할합니다. 비디오 매니페스트만 검색하면 무음 클립을 얻습니다. 둘 다 입력으로 전달하고 둘 다 검색해야 합니다:

    ffmpeg \
      -ss 00:10:00 -t 00:05:00 -i "https://example.com/video/720p/playlist.m3u8" \
      -ss 00:10:00 -i "https://example.com/audio/en/playlist.m3u8" \
      -c copy -bsf:a aac_adtstoasc clip.mp4

    (무음 클립 실패 모드에 대한 자세한 내용은 HLS 다운로드: 오디오 및 비디오가 분리되어 있음을 참조하세요.)

  • Referer / 사용자 정의 헤더. 원본을 기반으로 CDN을 게이트하는 것은 헤더를 명시적으로 전달해야 합니다. 예: -headers "Referer: https://example.com/watch". 잘못 설정하면 모든 세그먼트 요청이 403입니다.

이것은 스크립팅의 경우 거래 차단기가 아닙니다 — ffmpeg은 이미 URL, 헤더, 키를 정렬했고 반복 가능하고 자동화 가능한 자르기를 원할 때 타의 추종을 불허합니다. 스트림이 로그인 뒤에 있거나 회전하는 서명이 있을 때는 단순히 정말 까다로워집니다. 왜냐하면 브라우저가 무료로 생성한 요청 컨텍스트를 손으로 재구성하고 있기 때문입니다.

방법 2: 범위 인식 CLI 다운로더 (N_m3u8DL-RE, yt-dlp)

ffmpeg 검색 플래그 대신 세그먼트/시간 공간에서 작업하려면, 전용 HLS 다운로더가 플레이리스트의 부분 집합을 선택할 수 있습니다.

N_m3u8DL-RE는 범위 선택을 직접 노출합니다. --custom-range 플래그는 시간 또는 세그먼트 범위를 수용하고 해당 세그먼트만 다운로드합니다 — 이것은 정확히 “겹치는 실행을 가져오기” 작업이며, 당신을 위해 완료됩니다:

# Time range
N_m3u8DL-RE "https://example.com/master.m3u8" --custom-range "00:10:00-00:15:00" --save-name clip

# Or by segment index
N_m3u8DL-RE "https://example.com/master.m3u8" --custom-range "100-149" --save-name clip

또한 마스터 플레이리스트를 해석하고, 오디오와 비디오 트랙을 쌍 지으며, AES-128 키를 적용하여 대부분의 ffmpeg 통증을 제거합니다. 트레이드오프는 설치할 다른 바이너리이고 자르기 정확도는 세그먼트 단위입니다 — 전체 세그먼트를 얻으므로 클립은 각 끝의 가장 가까운 세그먼트 경계에 스냅합니다.

yt-dlp는 더 넓은 도구이지만 범위 지원은 더 좁으며 솔직하게 말할 가치가 있습니다. --download-sections 플래그는 실제 추출기가 있는 사이트 (가장 유명하게는 YouTube를 장/타임스탐프 범위를 통해)에서 잘 작동합니다:

yt-dlp --download-sections "*00:10:00-00:15:00" -f "bv*+ba/b" "https://www.example.com/watch?v=..."

그러나 raw .m3u8 URL의 경우, --download-sections는 전체 스트림을 다운로드하고 ffmpeg으로 자르는 것으로 자주 폴백합니다. yt-dlp의 일반 HLS 처리가 항상 네이티브 세그먼트 범위 가져오기를 수행하지는 않기 때문입니다. 따라서 디스크 자르기 노력을 절약하지만 반드시 대역폭을 절약하지는 않습니다. 실제로 무엇을 가져왔는지 확인한 후에 윈도우만 캡처했다고 가정하세요.

두 도구 모두 순수한 ffmpeg과 같은 맹점을 공유합니다: 공개, 서명되지 않은 스트림에서 깔끔하게 작동합니다. 인증 차단 또는 짧은 만료 시간 서명 매니페스트를 가리키면, 손으로 DevTools에서 쿠키, 헤더, 새로 서명된 URL을 복사하는 것으로 돌아옵니다.

방법 3: OFA를 사용하여 브라우저에서 클립하기 (CLI 없음, 세션 상속)

위의 모든 방법은 하나의 근본적인 문제를 공유합니다: 매니페스트와 세그먼트는 특정 인증된 브라우저 세션에 제공되었고, 명령줄 도구는 그 세션이 아닙니다. 요청 컨텍스트를 역엔지니어링해야 합니다 — 쿠키, Referer, 서명된 쿼리 문자열, 키 엔드포인트의 인증 — 그리고 1분 안에 만료되는 토큰을 경주합니다.

그 방법은 세션이 이미 있는 페이지 내에서 자르기를 수행하는 것입니다. Video Downloader One-for-AllClip & Trim Download (v1.1.38, 2026년 6월에 추가됨)는 정확히 이것을 수행합니다:

  1. 페이지를 열고 플레이어가 스트림을 정상적으로 로드하게 합니다.
  2. OFA의 클립 보기를 엽니다 — 감지된 HLS/DASH 스트림의 검색 가능한 미리 보기를 얻습니다.
  3. 인 포인트와 아웃 포인트를 정확한 범위로 드래그합니다 (예: 10:00 → 15:00).
  4. 다운로드합니다. OFA는 선택한 범위와 겹치는 세그먼트만 가져옵니다 — 이 글의 맨 위에서 축적 및 선택 로직을 실행합니다 — 그리고 이들을 단일 MP4로 먹스합니다.

Chrome/Edge 확장으로 탭 내에서 실행되기 때문에, 이것은:

  • 플레이어의 인증을 상속합니다 — 쿠키, 서명된 URL, Referer는 페이지가 이미 보낸 것이므로, 로그인 차단 및 짧은 만료 스트림은 아무것도 복사하지 않고도 작동합니다.
  • AES-128을 투명하게 처리합니다, 페이지의 자체 요청 컨텍스트로 키를 가져옵니다.
  • 별도의 오디오 및 비디오 매니페스트를 자동으로 쌍을 짓습니다, 따라서 클립에는 소리가 있습니다 — -bsf:a aac_adtstoasc 주문을 기억할 필요가 없습니다.
  • 당신의 윈도우만 가져옵니다, 전체 VOD가 아니므로, 3시간 스트림에서 5분 클립은 대략 5분 가치의 데이터를 다운로드합니다.

Clip & Trim은 Free 및 Paid 플랜 모두에서 사용 가능합니다. 자르기 정확도는 경계에서 세그먼트 단위입니다 (N_m3u8DL-RE와 동일한 제약 — 복사만 하는 추출은 키프레임이 이미 있는 곳에서만 자를 수 있음). 일반적인 2–6초 세그먼트의 경우, 설정한 표시에서 수 초 이내에 도달합니다. 정확한 프레임 인/아웃이 필요하다면, 여기서 약간 더 큰 클립을 가져가고 방법 1의 출력 검색 ffmpeg 패턴으로 로컬에서 최종 프레임 정확한 자르기를 수행하세요.

기본 HLS 엔진의 제품 세부 사항은 HLS 다운로더 페이지에 있고, 매니페스트가 고정 목록이 아닌 슬라이딩 윈도우인 라이브 (비 VOD) 윈도우 캡처의 경우, 라이브 스트림 녹음기를 참조하세요.

이것은 또한 평행 가이드 비디오의 일부를 다운로드하는 방법이 일반적인 경우를 권장하는 경로이며, 긴 스트림에서 단일 하이라이트를 원할 때도 동일한 접근 방식이 적용됩니다 — 해당 특정 시나리오의 경우 Twitch VOD를 다운로드하는 방법을 참조하세요.

어떤 방법을, 언제

방법CLI 필요인증/쿠키 처리AES-128 처리프레임 정확도범위만 가져오기
ffmpeg -ss/-t수동 (헤더/쿠키를 손으로)예 (키 URL에 도달 가능한 경우)기본적으로 키프레임; 재인코딩으로 프레임 정확예 (입력 검색으로)
N_m3u8DL-RE --custom-range수동세그먼트 단위
yt-dlp --download-sections부분 (추출기 종속)다양; 종종 전체 가져오기 후 자르기추출기가 네이티브 범위를 할 때만
OFA Clip & Trim아니오예 (페이지 세션 상속)예 (투명)세그먼트 단위

솔직한 요약:

  • ffmpeg스크립팅 및 자동화를 위한 최고의 도구입니다. 공개 또는 자체 서비스 스트림에서 — 반복 가능하고, 조합 가능하고, GUI 없으며, 모든 플래그를 제어합니다. 인증 벽이나 60초 토큰과 싸울 때는 최악의 도구입니다.
  • N_m3u8DL-RE는 ffmpeg 검색 플래그의 미묘함 없이 명시적 범위 선택을 원하고 스트림이 잠겨 있지 않을 때 가장 좋은 CLI 중간 지점입니다.
  • OFA Clip & Trim은 스트림이 로그인이 필요하거나 빠르게 만료되는 서명된 URL을 배포할 때, 또는 단순히 터미널을 건드리고 싶지 않을 때 최고의 도구입니다 — 인증된 브라우저 세션을 절대 떠나지 않기 때문입니다.

권리와 DRM에 대한 참고

저장할 권리가 있는 콘텐츠만 다운로드하세요 — 자신의 업로드, 사용 허가를 받은 자료, 또는 소스의 약관이 허용하는 콘텐츠. 그리고 범위를 명확히 하기 위해: 이 전체 글은 명확한 (또는 AES-128 암호화) HLS에 대한 것입니다. 키가 플레이어에게 전달되기 때문에 복호화할 수 있습니다. Widevine, PlayReady, 또는 FairPlay로 보호된 스트림은 다른 것입니다 — 콘텐츠 키는 하드웨어 기반 CDM을 절대 떠나지 않으며, 브라우저 내 확장이나 CLI는 추출할 수 없으며, OFA는 이들을 지원하지 않습니다. 이들이 보이면, 여기서 섹션 다운로드 기법은 적용되지 않습니다.

결론

부분 다운로드는 HLS 플레이리스트가 타이밍된 세그먼트의 목록이기 때문에 가능합니다: #EXTINF 지속 시간을 합산하고, [start, end] 윈도우와 겹치는 인접한 실행을 찾고, 그 실행만 가져오세요. ffmpeg, N_m3u8DL-RE, yt-dlp는 모두 이 수학을 내부적으로 수행하며 열린 스트림에서 훌륭하며 — 스크립팅과 배치 작업을 위해 선택하세요. 스트림이 로그인 뒤에 있거나 URL이 초 단위로 만료될 때, 마찰은 수학이 아니라 브라우저의 세션을 재현하는 것입니다 — 이것은 정확히 Video Downloader One-for-All으로 페이지 내에서 클립하는 것이 모든 인증 차단 콘텐츠에 대해 저항의 경로인 이유입니다.