昨天講的是最單純的情況:一份 M3U8 清單裡直接列著片段網址。但實務上常常打開一份 M3U8,裡面沒有任何 #EXTINF 或片段網址,只有幾行指向「其他 M3U8 清單」的連結——第一次遇到這種情況,很容易誤以為解析邏輯壞了。
一份典型的 Master playlist 長這樣:
#EXTM3U
#EXT-X-STREAM-INF:BANDWIDTH=1280000,RESOLUTION=640x360
low/index.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=2560000,RESOLUTION=1280x720
high/index.m3u8
這份清單本身沒有任何片段,low/index.m3u8 跟 high/index.m3u8 才是真正的 Media playlist。
async def get_playlist(url: str):
while True:
content = await fetch(url)
parsed = parse_m3u8(content, url)
if len(parsed.segments) > 0:
return parsed # 這是 Media playlist,可以直接用
# 沒有片段,代表這是 Master playlist,取第一個版本繼續往下解析
url = resolve_url(parsed.playlists[0])
這段邏輯的關鍵判斷準則很單純:解析出來的清單裡有沒有實際的片段。有,代表已經是最終的 Media playlist;沒有,代表拿到的是 Master playlist,要挑一個版本繼續往下解析,直到真的拿到片段為止。
要說明的是,「用有沒有片段來判斷該不該再往下繞」是這裡採用的實作判斷,不是規格條文本身逐字定義的演算法——規格只定義了 Master/Media playlist 各自該有的標籤與結構,怎麼從解析結果推導出「這是哪一種、下一步該怎麼做」,是實作者依據結構特性自己導出的邏輯。
如果程式碼沒意識到有兩種清單,看到「解析結果裡片段數量是零」,很容易誤判成「這個內容沒有片段可下載」,直接跳過、留下一份空的輸出,而不是繼續往下解析。這種問題不會拋例外、不會當機,只會安靜地產出一份不完整的結果——是最難被發現的那種錯誤。
你處理過的資料格式裡,有沒有類似「一層索引指向另一層索引」的巢狀結構?你的解析邏輯有沒有明確處理「已經到底層」跟「還要再往下繞一層」這兩種情況?
明天進入加密片段:AES-128 加密的片段,金鑰跟初始向量(IV)從哪裡取得。