Skip to content

MP4 轉 M3U8 教學:FFmpeg HLS 切片與 AES-128 加密

HLS(HTTP Live Streaming)是 Apple 開發的串流協定,也是主流影片傳輸方式。YouTube、Netflix、Twitch 都用 HLS 串流。這篇介紹用 FFmpeg 把 MP4 轉成 M3U8 的完整流程,包含切片、加密、多解析度。

HLS 基本原理

HLS 把影片切成小片段(TS 檔案),用 M3U8 播放清單管理。播放器依播放清單順序下載片段播放。

input.mp4 → [切片] → output.m3u8 + segment_001.ts + segment_002.ts + ...

HLS 元件

  • M3U8 播放清單:記錄片段順序和位置
  • TS 片段:實際影片資料(MPEG-TS 容器)
  • 主播放清單:多解析度時參照各解析度子播放清單

HLS 優點

  • 基於 HTTP,CDN 友善
  • 自適應位元率(ABR)
  • 跨平台相容(Safari 原生支援)
  • 可加密保護

基本 MP4 轉 M3U8

bash
ffmpeg -i input.mp4 \
  -c:v libx264 -c:a aac \
  -f hls \
  -hls_time 6 \
  -hls_playlist_type vod \
  -hls_segment_filename "segment_%03d.ts" \
  output.m3u8

參數說明:

  • -c:v libx264:H.264 視訊編碼
  • -c:a aac:AAC 音訊編碼
  • -f hls:HLS 輸出格式
  • -hls_time 6:每個片段 6 秒
  • -hls_playlist_type vod:VOD(點播)模式
  • -hls_segment_filename "segment_%03d.ts":片段命名模式
  • output.m3u8:播放清單輸出

生成的 M3U8 範例

text
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:6
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-PLAYLIST-TYPE:VOD
#EXTINF:6.000000,
segment_000.ts
#EXTINF:6.000000,
segment_001.ts
#EXTINF:6.000000,
segment_002.ts
...
#EXT-X-ENDLIST

無重新編碼切片

來源 MP4 已經是 H.264/AAC,可以用串流複製模式避免重新編碼:

bash
ffmpeg -i input.mp4 \
  -c copy \
  -f hls \
  -hls_time 6 \
  -hls_playlist_type vod \
  -hls_segment_filename "segment_%03d.ts" \
  output.m3u8

-c copy 直接複製串流,速度最快、無品質損失。

AES-128 加密

1. 生成密鑰和 IV

bash
# 生成 16 位元組密鑰
openssl rand 16 > enc.key

# 生成 16 位元組 IV
openssl rand 16 > enc.iv

# 確認密鑰內容
xxd enc.key
xxd enc.iv

2. 建立 key_info 檔案

建立 key_info 文字檔:

https://example.com/enc.key
/path/to/enc.key
/path/to/enc.iv
  • 第一行:播放器存取密鑰的 URL
  • 第二行:FFmpeg 讀取的本地密鑰檔案路徑
  • 第三行:IV 檔案路徑

3. 用 FFmpeg 加密切片

bash
ffmpeg -i input.mp4 \
  -c:v libx264 -c:a aac \
  -f hls \
  -hls_time 6 \
  -hls_playlist_type vod \
  -hls_key_info_file key_info \
  -hls_segment_filename "segment_%03d.ts" \
  output.m3u8

4. 加密 M3U8 範例

text
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:6
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-PLAYLIST-TYPE:VOD
#EXT-X-KEY:METHOD=AES-128,URI="https://example.com/enc.key",IV=0x1234567890abcdef1234567890abcdef
#EXTINF:6.000000,
segment_000.ts
#EXTINF:6.000000,
segment_001.ts
...
#EXT-X-ENDLIST

#EXT-X-KEY 標籤告訴播放器用 AES-128 解密,從指定 URI 取得密鑰。

注意: AES-128 是基本保護,播放器仍能取得密鑰。真要防盜用 DRM(Widevine、FairPlay)。詳見常見影片下載問題 Q&A 的 DRM 部分。

多解析度 HLS

1. 生成多解析度串流

bash
ffmpeg -i input.mp4 \
  -filter_complex "[0:v]split=3[v1][v2][v3]; \
    [v1]scale=1920:1080[v1out]; \
    [v2]scale=1280:720[v2out]; \
    [v3]scale=854:480[v3out]" \
  -map "[v1out]" -map 0:a -c:v:0 libx264 -b:v:0 5000k \
  -map "[v2out]" -map 0:a -c:v:1 libx264 -b:v:1 2500k \
  -map "[v3out]" -map 0:a -c:v:2 libx264 -b:v:2 1000k \
  -c:a aac -b:a 128k \
  -f hls \
  -hls_time 6 \
  -hls_playlist_type vod \
  -master_pl_name master.m3u8 \
  -var_stream_map "v:0,a:0 v:1,a:1 v:2,a:2" \
  -hls_segment_filename "v%v/segment_%03d.ts" \
  "v%v/out.m3u8"

2. 主播放清單 master.m3u8

text
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-STREAM-INF:BANDWIDTH=5500000,RESOLUTION=1920x1080
v0/out.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=2800000,RESOLUTION=1280x720
v1/out.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=1200000,RESOLUTION=854x480
v2/out.m3u8

播放器依網路狀況選擇最佳解析度。

Web 伺服器設定

Nginx 設定

nginx
server {
    listen 80;
    server_name example.com;
    
    location /hls/ {
        types {
            application/vnd.apple.mpegurl m3u8;
            video/mp2t ts;
        }
        
        # CORS 設定(跨網域播放需要)
        add_header Access-Control-Allow-Origin *;
        add_header Access-Control-Allow-Headers 'Range';
        
        # 目錄
        root /var/www/hls;
        
        # 啟用 Range 請求
        add_header Accept-Ranges bytes;
    }
}

Apache 設定

apache
<IfModule mod_mime.c>
    AddType application/vnd.apple.mpegurl .m3u8
    AddType video/mp2t .ts
</IfModule>

<IfModule mod_headers.c>
    <Location /hls>
        Header set Access-Control-Allow-Origin "*"
        Header set Access-Control-Allow-Headers "Range"
    </Location>
</IfModule>

HTML 播放器

Safari 原生播放

html
<video controls>
  <source src="https://example.com/hls/output.m3u8" type="application/vnd.apple.mpegurl">
</video>

Safari 原生支援 HLS,直接用 <video> 標籤。

其他瀏覽器用 hls.js

html
<script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
<video id="video" controls></video>
<script>
  const video = document.getElementById('video');
  const url = 'https://example.com/hls/output.m3u8';
  
  if (Hls.isSupported()) {
    const hls = new Hls();
    hls.loadSource(url);
    hls.attachMedia(video);
  } else if (video.canPlayType('application/vnd.apple.mpegurl')) {
    video.src = url;
  }
</script>

詳見 瀏覽器影片播放原理

進階設定

1. 片段長度最佳化

bash
# 6 秒片段(一般 VOD)
-hls_time 6

# 2 秒片段(低延遲直播)
-hls_time 2

# 用關鍵幀對齊片段
-hls_time 6 -force_key_frames "expr:gte(t,n_forced*6)"

2. 直播串流

bash
# 直播模式(持續更新 M3U8)
ffmpeg -i input.mp4 \
  -c:v libx264 -c:a aac \
  -f hls \
  -hls_time 2 \
  -hls_list_size 5 \
  -hls_flags delete_segments \
  -hls_segment_filename "live_%03d.ts" \
  output.m3u8
  • -hls_list_size 5:M3U8 只保留最近 5 個片段
  • -hls_flags delete_segments:自動刪除舊片段

3. SegmentTimeline 精確控制

bash
ffmpeg -i input.mp4 \
  -c:v libx264 -c:a aac \
  -f hls \
  -hls_time 6 \
  -hls_playlist_type vod \
  -hls_flags independent_segments \
  -hls_segment_type mpegts \
  -hls_segment_filename "segment_%03d.ts" \
  output.m3u8

詳見 H.264 vs H.265 vs AV1 編碼器比較

常見問題

1. Safari 播放不了

原因: HEVC 編碼或音訊不相容

解決方案: 用 H.264 視訊和 AAC 音訊

2. 跨網域播放失敗

原因: CORS 設定問題

解決方案: 確認伺服器 CORS 設定

3. 片段下載失敗

原因: URL 路徑錯誤

解決方案: 確認 M3U8 中的片段 URL

4. 加密播放失敗

原因: 密鑰 URL 不可存取

解決方案: 確認密鑰 URL 可存取,CORS 設定正確

總結

MP4 轉 M3U8 的核心:

  • 基本切片: ffmpeg -i input.mp4 -c copy -f hls -hls_time 6 output.m3u8
  • 無重新編碼:-c copy
  • 加密: AES-128(-hls_key_info_file
  • 多解析度: 主播放清單 + 子播放清單
  • Web 伺服器: 設定 MIME 類型和 CORS

快速參考:

  • 基本切片: ffmpeg -i input.mp4 -c copy -f hls -hls_time 6 -hls_segment_filename "seg_%03d.ts" out.m3u8
  • 加密: -hls_key_info_file key_info
  • 多解析度: -var_stream_map + -master_pl_name
  • 直播: -hls_list_size + -hls_flags delete_segments

參考資料

Last updated: