FFmpeg 操作 M3U8 串流:切片、合併、轉碼與直播推流進階
哈囉,我回來了。最近又掉進串流媒體的坑裡折騰,跟 M3U8 這個格式徹底較上勁了。FFmpeg 到底有多神我不吹,但每次遇到新場景都得狂翻文件是真的。乾脆把自己常碰的幾種玩法記下來,當備忘,也給需要的人做個參考。
先承認,我第一次見到 .m3u8 完全不知道是什麼,心想找個影片出來怎麼是一堆 .ts 小檔案。後來才知道這是蘋果的 HLS 直播協定,說穿了就是個索引加一堆切片。不過呢,如果你只是臨時想看某個 m3u8 連結裡的內容,真的不用像我一樣裝 FFmpeg 折騰命令列,直接丟給 M3U8Player(https://m3u8player.link/)那個線上網站就能播,省心省力。但如果你跟我一樣想自己處理、下載、轉碼或者推流,那才需要繼續往下看。
先說切片
把自己手邊的影片切成 HLS 串流這個操作最日常。我通常這樣寫:
ffmpeg -i input.mp4 -c copy -hls_time 10 -hls_list_size 0 -hls_segment_filename "seg%d.ts" playlist.m3u8
幾個參數解釋一下:-hls_time 10 是每 10 秒一切,-hls_list_size 0 表示保留所有切片(直播的話一般設個數字只留最近幾個),-hls_segment_filename 自訂檔案名稱,不然預設會變成 output%03d.ts 那種。-c copy 不重新編解碼,速度飛快。不過有時候原影片的音訊串流不規範,直接複製出來沒聲音,得補一個 -bsf:a aac_adtstoasc,但普通 mp4 切片一般不用。
有次我切一個長影片,忘了改輸出目錄,結果檔案名稱把上一批直接覆蓋了,血的教訓。建議大家每次切片規規矩矩丟到新資料夾裡。
合併下載
現在不少線上影片直接給你一個 m3u8 位址,想拉下來變成 mp4 很簡單:
ffmpeg -i "https://...playlist.m3u8" -c copy -bsf:a aac_adtstoasc output.mp4
一行搞定,預設會選最高碼率。但有防盜連的時候要加個 -user_agent "Mozilla/5.0",不然一直 403。有次我排查了半天才發現是預設 user-agent 被封了,換了個 UA 秒解。
如果拿到的是多碼率 master playlist(裡面套了好幾個不同解析度的串流),直接用上面的命令會只挑一個最高的。想選特定的版本,可以先跑 ffmpeg -i "master.m3u8" 看資訊,它會顯示出 #EXT-X-STREAM-INF 下面各個串流的索引號,然後用 -map 0:v:0 -map 0:a:0? 指定要哪個串流的影片和音訊,最後合併成單一檔案。
轉碼進階
有時候原始 m3u8 解析度太高或者格式不對,想重新壓制一下再輸出成新的 m3u8:
ffmpeg -i "input.m3u8" -vf scale=1280:720 -c:v libx264 -crf 23 -c:a aac -hls_time 10 out.m3u8
輸入直接是 m3u8 連結,ffmpeg 會邊下邊處理。注意這樣速度慢、硬碟暫存佔用大,最好先合併成一個暫存檔案再切片。不過一次搞定也挺方便。
如果要搞自適應多碼率,一般需要先把原影片轉碼成不同解析度和碼率的版本,各自切片,再手寫一個 master playlist。ffmpeg 本身不能一步生成自適應串流,得靠腳本編排。
轉碼裡有個坑:不同碼率之間切片要對齊關鍵影格,不然點擊切換解析度時畫面會花一下。我習慣加個 -g 48,讓每 48 影格一個關鍵影格,大概 2 秒間隔(按 24fps 算),再配合同樣時長的 -hls_time 2,這樣切片點就整整齊齊了。
直播推流進階
這個我純屬測試好玩。純 FFmpeg 直接推 HLS 到本地目錄:
ffmpeg -re -i input.mp4 -c copy -f hls -hls_time 4 -hls_list_size 10 -hls_flags delete_segments live.m3u8
-re 是強制按原影格率讀取(否則會狂奔)。這樣會在當前目錄生成不斷刷新的 m3u8 和 ts 檔案,再用 nginx 把目錄暴露出去,就是一個簡單的直播源了。適合小範圍自己測。
更常見的做法是先推 RTMP 到伺服器(比如用 nginx-rtmp-module),再由伺服端轉成 HLS。但如果你不想搭伺服器,ffmpeg 也可以直接推流到雲端平台,參數大同小異。
之前我跑本地推流,播放器老是緩衝,後來把 -hls_time 壓到 2 秒,-hls_list_size 只留 3 個,延遲明顯降了。但切片越短對伺服器壓力越大,自己取捨。
還有一個坑是時間戳記:如果你循環推同一個檔案(比如加 -stream_loop -1),時間戳記可能會重置導致 HLS 串流中斷。解決辦法是加 -fflags +genpts 或者用 -re 配正確的時間基底。這個我折騰了兩小時才搞定。
其實我現在直播大部分直接開 OBS,但自動化寫腳本的時候還是得靠 FFmpeg 硬上,沒輒。
好了,關於 M3U8 和 FFmpeg 的常用操作就這些。每個玩法就那麼幾個核心參數,但組合起來千變萬化。對了如果你只是想快速預覽一個 m3u8 看看自己生成對不對,我偶爾也會用上面的 M3U8Player 那個網站丟進去看一眼,省得再開本地播放器。反正大家多試試就熟了,有什麼新坑也歡迎交流。