M4A 檔案格式
MPEG-4 音訊,通常是 AAC 編碼,裝在 ISO-BMFF 容器裡。
- MIME 類型
- audio/mp4
- 副檔名
- .m4a
- 分類
- 音訊
Magic number(檔案簽章)
| 0x00000004 | 66747970 | ftyp | ISO-BMFF 'ftyp' box |
| 0x00000008 | 4D344120 | M4A | brand 'M4A ' |
關於 M4A 格式
M4A 是裝在 MP4 容器裡的 AAC(或 ALAC)音訊,也是 iTunes 與 Apple Music 下載所用的格式。它是 ISO-BMFF 檔案,'ftyp' 品牌識別為 'M4A '。ISO-BMFF 是一棵 box 樹,每個 box 是一個大端序 32 位元大小加一個四字元類型,整套結構文法就這樣而已 — 'ftyp' 宣告這個檔案講的是哪種方言,'moov' 是讓它播得動的索引,'mdat' 裝著編碼後的音框。因為索引和資料是分開的兩個 box,一個 M4A 可以逐位元完好無缺,卻只因為掉了錯的那一頭就完全播不出來。
檔頭結構,一個位元組一個位元組看
| 位移 | 長度 | 欄位 | 意義 |
|---|---|---|---|
| 0 | 4 | ftyp box size | 大端序,而這件事值得多停留一下:ISO-BMFF 檔案裡所有東西都是大端序,跟 RIFF 完全相反。典型值是 00 00 00 1C,也就是 28 個位元組。大小是 1 代表類型欄位後面跟著 64 位元的 largesize;大小是 0 代表這個 box 一路延伸到檔案結尾。 |
| 4 | 4 | Box type 'ftyp' | 66 74 79 70。它坐在位移 4 而不是 0,是因為大小排在前面 — 這就是為什麼每個 MP4 家族的偵測器都是往內四個位元組去比對,也是為什麼這個家族裡沒有任何格式的 magic number 落在檔案最前面。 |
| 8 | 4 | Major brand | 4D 34 41 20 — 字母 M4A 加一個尾隨空格,因為品牌識別永遠是四個字元。光是這個欄位,就是 M4A、MP4('isom' 或 'mp42')、M4V、3GP('3gp4')、AVIF('avif')和 HEIC('heic')之間唯一的差別。 |
| 12 | 4 | Minor version | 一個大端序的版本號,是品牌識別的版本、不是檔案格式的版本。Apple 血統的檔案傳統上帶 00 00 02 00、也就是 512,而規格要讀取端忽略它,不要把它當成相容性門檻。 |
| 16 | variable | Compatible brands | 一串四位元組的品牌識別,把 ftyp box 剩下的空間填滿 — 常見的是 'M4A '、'isom'、'iso2'。看不懂主要品牌識別的播放器,只要認得這串清單裡任何一個,就仍然可以播這個檔案 — 新格式能繼續被舊軟體打開,靠的就是這個。 |
| variable | 8 | Next box header | 從 ftyp 的大小所說的位置開始 — 一個 major brand 加三個 compatible brands 這種常見組合就是 28 — 一樣是四個位元組的大小接四個位元組的類型。真實檔案接下來放的是 'free'、'moov' 或 'mdat',所以 ftyp box 之後沒有任何位移是固定的。 |
| variable | variable | 'moov' box | 索引:軌列表、時間刻度、長度、編碼設定,以及把每個音框對應到位元組位移與時間戳的樣本表。沒有它什麼都播不了,而它合法地可以放在檔案的任一端。 |
| variable | variable | 'mdat' box | 編碼後的音框本身,內部沒有任何索引 — 它就是一堆位元組,只有 'moov' 裡的樣本表才賦予它們意義。它自己的位置也不固定:28 個位元組的 ftyp 後面接一個 8 位元組的 'free' box,'mdat' 就從位移 36 開始,'moov' 則從 mdat 的長度用完的地方接下去。 |
| variable | 4 | Codec four-character code inside 'moov' | 埋在樣本描述裡:AAC 是 'mp4a',Apple Lossless 是 'alac'。在一個品牌識別寫著 M4A 的檔案裡兩者都合法,所以品牌識別告訴你的是容器方言,而這個欄位才告訴你音訊究竟是什麼。 |
怎麼跟長得像的格式分開
| 容易搞混的 | 怎麼分 |
|---|---|
| MP4 | 前八個位元組一個不差,之後的結構也一個不差。位移 8 的品牌識別就是全部差別:'M4A ' 對上 'isom' 或 'mp42'。更麻煩的是,很多純音訊檔案是用 'isom' 品牌識別寫出來的,所以光靠品牌識別無法證明裡面沒有影片 — 要證明就得走進 'moov' 去數軌。 |
| QuickTime Movie | QuickTime 正是 ISO-BMFF 標準化的來源,所以 box 文法是同一套。現代的 .mov 位移 8 帶的是 71 74 20 20('qt ',尾隨兩個空格),而且通常在 ftyp 後面就有一個 'wide' box — 那是 MP4 的寫入端不會產出的東西。這個組合才是可靠的判別點。 |
| 3GP | 又是同一個容器,位移 8 的品牌識別是 '3gp4'、'3gp5' 或 '3gpp'。實務上的差別在允許裝什麼:3GP 的設定檔把編碼限縮在行動網路談定的那些,所以一個結構上完全合法的 MP4,仍然可能被只認 3GP 的解碼器拒收。 |
| ADTS AAC | 裸的 .aac 檔案完全沒有 box。它是一串裸的 ADTS 音框,每一框以一個十二位元的同步字開頭,所以檔案開頭是 FF F1 或 FF F9。裡面的音訊可能就是 M4A 裝的那條 AAC 位元串流,但沒有 ftyp、沒有 moov、也沒有 seek 表,它就完全是另一種檔案了。 |
| MP3 | 老是被搞混,因為兩者都是「你買來的壓縮音訊」,但 MP3 是一串裸音框,開頭是一個 'ID3' 標籤或一個音框同步,而且它沒有容器、沒有樣本表,除了 ID3 標籤之外也沒有標準辦法帶封面圖。它前十六個位元組跟 ISO-BMFF 的 box 沒有半點相似。 |
常見的壞法
- 索引在錯的那一頭,加上下載被截斷。除非特別要求搬家,編碼器都是先寫 'mdat'、最後才寫 'moov',所以一個停在 95% 的下載裝著幾乎全部的音訊、卻一點索引都沒有,結果是任何東西都播不出哪怕一秒。跑一次 faststart 把 'moov' 搬到前面,正是串流伺服器堅持要這麼做的原因。
- 'mdat' 前面被插入或刪掉位元組。樣本表存的是絕對的檔案位移,所以在前面黏一個 ID3 標籤、或把多出來的前綴剝掉,都會一次讓每一個音框指標失去同步。跟 ZIP 不同 — ZIP 的讀取端還能自己算出偏移量 — MP4 完全不會給你任何位移已經移動的訊號,播出來就是雜訊或什麼都沒有。
- box 大小小於 8,或在檔案中間出現 0。相信這個欄位的剖析器,不是在一個長度為零的 box 上永遠迴圈,就是走出緩衝區之外;經過強化的讀取端會直接拒收任何小於 8 的大小,而這是媒體函式庫上經典的 fuzzing 目標。
- 四 GB 的 chunk 位移之牆。一般的 chunk 位移表是 32 位元的,所以超過四 GB 的檔案必須改用 64 位元的變體。從來不寫它的寫入端,產出的檔案後段位移會繞回去,於是一段長錄音播到一半就突然壞掉。
- 文字模式傳輸。編碼音框內部的換行轉換,不會影響 box 樹的剖析,所以檔案會報出正確的長度和編碼,然後播出一片靜電雜訊 — 中介資料一點事都沒有,因為損壞全在 'mdat' 裡面。
這個格式現在還在用嗎
現在買來的和串流聽的音樂,實際上就是 M4A。Apple 把它做成 iTunes 的下載格式,而 MP4 容器裡的 AAC 如今是 Apple 生態系、YouTube 的音軌、多數 podcast 訂閱源,以及兩大手機平台語音備忘錄的預設;同一個容器裡的 ALAC 則負責無損那一層。它幾乎在所有新東西上取代了 MP3,因為同樣位元率下 AAC 明顯更好,也因為這個容器帶來的是真正的中介資料、章節和封面圖,而不是硬掛在尾巴上的一個標籤。它不是第一選擇的場合是任何對延遲敏感、或走開放技術棧的東西:Ogg 或 WebM 裡的 Opus 在低位元率下贏過 AAC,而且沒有授權包袱,所以語音聊天和 WebRTC 都往那邊走了。後製工作則根本沒有人在編輯 M4A — 大家是解碼成 WAV、編輯完再編碼一次,因為 AAC 是有損的,每重編一次傷害就疊一層。
在這個站上打開 M4A 檔
這些全部在你的瀏覽器裡跑,檔案不會離開你的電腦。
相關格式
容易和 M4A 搞混的格式,可以比對它們的開頭位元組。