MP3文件格式解析

1, MP3簡介

MP3是今天一種常見的音樂格式,但恐怕除了工作要求之外,有興趣對此進行研究的人恐怕不多。所以,當我打算做MP3解碼方面的工作時,在找資料時也頗費了一番周折,同時也覺得很有趣。所以想在這里分享一下自己的心得,做一個總結性的介紹。這樣有興趣的同志也可以對此有一個大概了解,盡快入門。

MP3是MPEG-1 Audio Layer 3的簡稱,是當今比較流行的一種數字音頻編碼和有損壓縮格式(有Layer 3,也必然有Layer1和Layer2,也就是MP1和MP2,但不在本文討論范圍之內)。MP3技術的應該可以用來大幅度的降低音頻文件存儲所需要的空間。它丟掉脈沖編碼調制(PCM)音頻數據中對人類聽覺不重要得數據,從而達到了較高的壓縮比(高達12:1-10:1)。簡單地說,MP3在編碼時先對音頻文件進行頻譜分析,然后用過濾器濾掉噪音電平,接著通過量化的方式將剩下的每一位打散排列,最后形成有較高壓縮比的MP3文件,并使壓縮后的文件在回放時也能夠達到比較接近原音源的效果。

MP3的音頻質量取決于它的Bitrate和Sampling frequency,以及編碼器質量。MP3的典型速度介于每秒128到320kb之間。采樣頻率也有44.1,48和32 kHz三種頻率,比較常見的是采用CD采樣頻率——44.1kHz。常用的編碼器是LAME,它完全遵循LGPL的MP3編碼器,有著良好的速度和音質。

?

2,????? MP3文件格式

用一個二進制查看器(比如Ultra-Edit)打開一個MP3文件,就能看到一大堆看似雜亂無序的數據。但只要用心了解就會知道,其實,這一切都是有規律可循的。

MP3文件是由幀(frame)構成,幀是MP3文件的最小組成單位。每幀都包含幀頭,并可以計算幀的長度。根據幀的性質不同,文件主要分為三個部分,ID3v2標簽幀,數據幀和ID3v1標簽幀。并非每個MP3文件都有ID3v2,但是數據幀和ID3v1幀是必須的。ID3v2在文件頭,以字符串“ID3”為標志,包含了演唱者,作曲,專輯等信息,長度不固定,擴展了ID3V1的信息量。ID3v1在文件結尾,以字符串“TAG”為標記,其長度是固定的128個字節,包含了演唱者、歌名、專輯、年份等信息。

I, ID3V2

ID3V2到現在一共有四個版本,但流行的播放軟件一般只支持第三版,既ID3V2.3。每個ID3V2.3 的標簽都一個標簽頭和若干個標簽幀或一個擴展標簽頭組成。關于曲目的信息如標題、作者等都存放在不同的標簽幀中,擴展標簽頭和標簽幀并不是必要的,但每個標簽至少要有一個標簽幀。標簽頭和標簽幀一起順序存放在MP3 文件的首部。

標簽頭

長度為10個字節,位于文件首部,其數據結構如下:

char Header[3]; /* 字符串 "ID3" */

char Ver;?????? /* 版本號ID3V2.3 就記錄3 */

char Revision; /* 副版本號此版本記錄為0 */

char Flag;???? /* 存放標志的字節,這個版本只定義了三位,很少用到,可以忽略 */

char Size[4]; /* 標簽大小,除了標簽頭的10 個字節的標簽幀的大小 */

標簽大小為四個字節,但每個字節只用低7位,最高位不使用,恒為0,其格式如下:
0xxxxxxx 0xxxxxxx 0xxxxxxx 0xxxxxxx

計算公式如下:

ID3V2_frame_size = (int)(Size[0] & 0x7F) << 21
???????????????? | (int)(Size[1] & 0x7F) << 14
????????????????? | (int)(Size[2] & 0x7F) << 7
????????????????? | (int)(Size[3] & 0x7F) + 10;


標簽幀

每個標簽幀都有一個10字節的幀頭和至少一個字節的不固定長度的內容組成。它們是順序存放在文件中,由各自特定的標簽頭來標記幀的開始。其幀的結構如下:

char FrameID[4];?? /*用四個字符標識一個幀,說明其內容 */

char Size[4];????? /* 幀內容的大小,不包括幀頭,不得小于1 */

char Flags[2];???? /* 存放標志,只定義了6 位,此處不再說明 */

常用幀標識:

TIT2:標題
TPE1:作者
TALB:專輯
TRCK: 音軌,格式:N/M,N表示專輯中第幾首,M為專輯中歌曲總數
TYER:年份
TCON:類型
COMM:備注,格式:“eng/0備注內容”,其中eng表示所使用的語言
幀大小為四個字節所表示的整數大小。


II, ID3V1

其數據結構如下:

char Header[3];??? /* 標簽頭必須是"TAG"否則認為沒有標簽 */
char Title[30];??? /* 標題 */
char Artist[30];?? /* 作者 */
char Album[30];??? /* 專集 */
char Year[4];????? /* 出品年代 */
char Comment[28]; /* 備注 */
char reserve;????? /* 保留 */
char track;;?????? /* 音軌 */
char Genre;??????? /* 類型 */

其實,關于最后31個字節還存在另外一個版本,就是30個字節的Comment和一個字節的Genre.

有了上述的這些信息,我們就可以自己寫代碼,從MP3文件中抓取信息以及修改文件名了。但是,如果真的想寫一個播放軟件,還是需要讀它的數據幀,并進行解碼。


III, 數據幀

數據幀往往有多個,至于有多少,由文件大小和幀大小來決定。每個幀都有一個四字節長的幀頭,接下來可能有兩個字節的CRC校驗,其存在由幀頭中的具體信息決定。接著就是幀的實體數據,也就是MAIN_DATA了。

A,幀頭結構如下:

位置???? 長度???? 描述
(BIT)? (BITS)
————————————————————————————
31-19?? 12?????? Frame sync(0xFFF)
18/17??? 2??????? Layer, 00 – reserved, 01 – Layer III
???????????????????????? 10 – Layer II, 11 - Layer I
16?????? 1???????? protection_bit, 0 意味著受CRC保護,幀頭后面跟16位的CRC。
15-12??? 4??????? bitrate_index, 比特率
11-10??? 2??????? sampling_frequency,??? 00 – 44.1KHz, 01 – 48KHz
???????????????????????????????????????? 10 – 32 KHz,? 11 – 保留
9??????? 1??????? padding_bit,1 意味著幀里包含padding位,僅當采樣頻率為44.1KHz時發生。
8??????? 1??????? private_bit
7-6???? 2??????? mode,??? 00-stereo,??????? 01-joint stereo(intensity stereo and/or ms_stereo)
?????????????????????????? 11- dual_channel, 11 – single_channel
5-4????? 2??????? mode_extension,在Layer III中表示使用了哪一種joint stereo編碼方式。
??????????????????????????? Intensity_stereo?? ms_stereo
?????????????????? 00??????????? off??????????????? off
?????????????????? 01??????????? on???????????????? off
?????????????????? 10??????????? off??????????????? on
?????????????????? 11??????????? on???????????????? on
3??????? 1??????? copyright,1 表示受版權保護。
2??????? 1??????? original,0表示該bitstream是一個copy,1表示是original.
1-0????? 2??????? emphasis,表示會使用哪一種de-emphasis。
?????????????????? 00 - no emphasis,???? 01 – 50/15 microsec. Emphasis
?????????????????? 10 – reserved,??????? 11 – CCITT J.17

1)????? 無論幀長是多少,每幀的播放時間都是26ms

2)????? 數據幀大小:

FrameSize = 144 * Bitrate / SamplingRate + PaddingBit
當144 * Bitrate / SamplingRate不能被8整除,則加上相應的paddingBit.


B,MAIN_DATA:

MP3的granule包含18 * 32個subband采樣。每個數據幀含有兩個granule的數據,其內容結如下:
?????? - main_data_end pointer
?????? - side info for both granules (scfsi)
?????? - side info granule 1
?????? - side info granule 2
?????? - scalefactors and Huffman code data granule 1
?????? - scalefactors and Huffman code data granule 2

主要數據里包含了scalefactors, Huffman encoded data和ancillary information。其內容不再詳敘,可以參考MP3 SPEC-IS0 11172-3 AUDIO PART。我們一般用的都是立體聲,scfsi的長度為32個字節。

這里要解釋的一個概念就是位流――bitstream。我們平常接觸到的數據都是整數,最小的單位就是byte后者char。雖然我們也會用一個字節里的不同位來表示不同的含義,但總的來說,我們在出來數據的時候還是把它當作一個個字節看待。但對MP3這種數據格式來說,這是行不通的。在解碼時,它的數據輸入就是一個個比特流。其中一個或幾個比特會是你的采樣數據或者信息編碼。你需要從整個MAIN_DATA里提取你所需要的以BIT為單位的參數和輸入信號,從而進行解碼。所以我們需要一個子程序,getbit(n),也就是從緩沖中提取所需要的位,并形成一個新的整數,作為我們的輸出。

C,LAME標簽幀

可是,當你真的打開一個MP3文件的時候,你會發現,很奇怪,很多時候第一個數據幀的幀頭后面的32個字節居然都為0,這是為什么呢,這么奇怪的解碼信息該如何解釋?找到MP3 INFO TAG REV SPECIFICATION的網站,我才明白,原來第一幀并不是真正的數據幀,而是LAME編碼的標志幀。

這里又要牽涉到兩個概念:CBR和VBR。CBR表示比特率不變,也就是每幀的長度是一致的,它以字符串“INFO”為標記。VBR是Variable BitRate的簡稱,也就是每幀的比特率和幀的長度是變化的,它以字符串“Xing”為標記。同時,它還存放了MP3文件里幀的總個數,和100個字節的播放總時間分段的幀的INDEX,還有其他一些參數,這被稱為Zone A,傳統Xing VBR標簽數據,共120個字節。

在二進制文本編輯器里我們還可看到一個字符串“LAME”,并且后面清楚地跟著版本號。這就是20個字節的Zone B初始LAME信息,表示該文件是用LAME編碼技術。接下來一直到該幀結束就是Zone C-LAME標簽。

3,????? 相關資料

× Mp3 Info Tag rev 1 specifications - draft 0

× MP3 文件格式

× MP3 SPEC-IS0 11172-3 AUDIO PART?

本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/zp752963831/archive/2008/11/25/3368845.aspx

本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/zp752963831/archive/2008/11/25/3368845.aspx

本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/zp752963831/archive/2008/11/25/3368845.aspx

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/455131.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/455131.shtml
英文地址,請注明出處:http://en.pswp.cn/news/455131.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

懶漢式(線程安全,同步代碼塊兒)

懶漢式(線程安全,同步代碼塊兒) 代碼演示 // 懶漢式 (線程安全_ 同步放法_) class Singleton{private static Singleton instance;private Singleton() {}// 提供一個靜態的公有方法 加入了同步處理的代碼// 解決線程安全問題// 即懶漢式// 我們在這里加一個關鍵字 synchroni…

同余定理證明

轉載于:https://www.cnblogs.com/cmyg/p/7206474.html

非常抱歉,

非常抱歉&#xff0c;好長一段時間沒寫了&#xff0c;但是我在博客園偶爾還會寫寫這個行業必須不斷地學才不會被淘汰&#xff0c;幸好我是主動接受知識&#xff0c;我喜歡這個。。。。。。。。。。轉載于:https://blog.51cto.com/52770825/1962949

乘基取整法是什么_深入理解計算機系統(六):進制間的轉換原理

目錄1、進制的介紹2、二進制轉換成其他進制3、十進制轉換成其他進制4、十六進制轉換成其他進制5、總結上一篇博客我們講解了信息的在計算機中是如何存儲以及如何表示的。但是對于各個進制的轉換只是一筆帶過了&#xff0c;后來作者仔細研究了進制轉換的原理&#xff0c;發現還是…

單例設計模式-雙重檢查

雙重檢查 雙重檢查應用實例 代碼演示: class Singleton{private static volatile Singleton singleton;private Singleton(){}// 提供一個靜態公有方法public static Singleton getInstance() {if (instance null) {synchronized (Singleton.class) {if (instance null) {…

symbian系統開發教程(一)

第一章&#xff1a;Symbian OS簡介作者&#xff1a;謝興 enigma19971hotmail.com---轉載需注明出處 下載word文檔1.1. Symbian系統簡介當前有很多手機運行Symbian OS&#xff0c;數量甚至超出您的想象。到目前為止已經有超過7500 萬、100 多種型號的手機運行Sym…

小談c#數據庫存取圖片的方式

第一種方式 文件夾與數據庫配合 /// <summary>/// 上傳圖片/// </summary>/// <param name"FUSShopURL">FileUpload對象</param>/// <param name"UpladURL">圖片要放到的目錄名稱</param>/// <returns>如果Fi…

c#課程設計簡單題目_《C#項目案例》課程設計題目

1《C#項目案例》課程設計題目一、課程設計的基本目標1、通過本次課程設計&#xff0c;熟練掌握開發語言C#和開發環境——.NET。開發語言的熟練需要通過編寫一定長度的代碼(1000&#xff5e;2000行代碼)才能達到&#xff0c;開發環境的熟練需要反復的程序調試訓練。2、加深對軟件…

筆記吧

map的第一個為關鍵值&#xff0c;只可讀&#xff0c;不可寫。第二個為對應值&#xff1b;vector的消除有很大的困擾&#xff0c;感覺存在很大的局限性&#xff1b;問題——不能夠復雜話&#xff0c;一個問題先從較淺的方面想起&#xff0c;其實在現有基礎和請況下&#xff0c;不…

單例設計模式-靜態內部類

靜態內部類 靜態內部類應用實例 代碼演示 package com.atguigu.principle.singleton.type07;/** * author victor * site https://victorfengming.github.io/ * company XDL * project java_mode * package com.atguigu.principle.singleton.type07 * created 2021-02-02 20…

Symbian系統開發教程(二)

第二章&#xff1a;數據類型與語法 作者&#xff1a;謝興 enigma19971hotmail.com 轉載需注明出處 下載word版本 Symbian系統已經提供了一套已經定義好的內置的數據類型。為了保證你的代碼是編譯器無關的&#xff0c;應當使用下面symbian系統提供的數…

design php 如何使用ant_Ant Design Pro初探—添加面包屑

Ant Design Pro初探—添加面包屑上次咱們說了新建頁面&#xff01;頁面建成了&#xff0c;開始填充頁面 新建面頁面不會的看上一片文章:Ant Design Pro初探—新建頁面首先是需要添加家面包屑&#xff0c;上代碼&#xff1a;import React, { PureComponent } from react;import …

C++主要操作符重載的定義和總結

C中預定義的運算符的操作對象只能是基本數據類型&#xff0c;實際上&#xff0c;對于很多用戶自定義類型&#xff0c;也需要有類似的運算操作。例如&#xff1a; class complex { public: complex(double r0.0,double I0.0){realr;imagI;} void display(); private: do…

原生JS數組去重的幾種方法

有時候我們做項目的時候往往會需要把數組里面一些重復的項去掉&#xff0c;但是原生JS有排序&#xff0c;有篩選等等&#xff0c;但是就是沒有數組去重怎么辦呢&#xff1f;這能怎么辦&#xff0c;自己手動實現嘛。&#xff08;以下代碼直接在原型上添加的的方法&#xff0c;為…

單例設計模式-枚舉

枚舉 枚舉應用實例 代碼演示 package com.atguigu.principle.singleton.type08;/** */ public class SingletonTest08 {public static void main(String[] args) {Singleton instance Singleton.INSTANCE;Singleton instance2 Singleton.INSTANCE;System.out.println(inst…

拋物線交點式公式_拋物線交點式

以下是范文網www zhuodaoren com 分享的6 已知拋物線與軸兩交點在軸同側&#xff0c;它們的距離的平方等于&#xff0c;則的值為( )&#xff0c;希望能幫助到大家!(一)6 已知拋物線與軸兩交點在軸同側&#xff0c;它們的距離的平方等于&#xff0c;則的值為( )函數與一元二次方…

學習的動力

要有自學的意識&#xff0c;這是一個知識不斷更新、不斷涌現的時代&#xff0c;大學里的很多知識是過時的&#xff0c;就算入校時是熱門行業的但很可能四年后畢業找工作時已經變成了夕陽產業&#xff0c;學習是一種能力&#xff0c;但首先是一種態度&#xff0c;一個人想在快速…

java面向對象之父類的引用指向子類的對象

1 package Text; 2 3 public class Job { 4 public void dowork(){ 5 6 } 7 } 1 package Text; 2 3 public class Luosidao extends Job{ 4 public void dowork(){ 5 System.out.println("螺絲刀擰螺絲。。。"); 6 } 7 } 1 packa…

單例模式在JDK應用的源碼分析

單例模式在JDK應用的源碼分析 單例模式在jdk中的源碼分析 在我們JDK中,java.lang.Runtime就是經典的單例模式(惡漢式) 代碼分析Debug源碼代碼說明 public class Runtime {private static Runtime currentRuntime new Runtime();/*** Returns the runtime object associate…

lisp語言cond和if套用_LISP - 決策

決策結構需要程序員指定一個或多個條件由程序進行評估或測試&#xff0c;以及要執行的語句或語句如果條件被確定為true&#xff0c;如果條件被確定為false那么選擇要執行其他語句。下面是在大多數編程語言中一個典型的決策結構的一般形式為&#xff1a;LISP提供了以下類型的決策…