關於mp3文件結構解析的問題

最近在用qt5編寫播放器時遇到個問題,在Windows端需要解析音頻文件音譜來繪製曲線圖,但發現Windows端的庫要麼太老、要麼太弱雞、要麼得自己編譯,無奈花了一天時間在下載、編譯taglib、學習ffmpeg路上,到最後都是以動態庫調用時出現未知函數告終(mingw_gcc),原來qt本身就有對這些的封裝,可採用qmediaplaer代替他們,只不過需要安裝解碼器纔行,但qmediaplaer並不支持ffmpeg、taglib的音頻文件直接操作功能,這不得已自己重寫一版。。。

參考文章
MP3文件結構解析(超詳細)
要一個解析MP3的代碼要C語言的 要自己寫的不要網上的
Inside the MP3 Codec - Page 11
lets-build-mp3-decoder
Reading MP3 files [closed]

C語言結構體爲

ID3V2

//起始位置:0x0-0x9
struct ID3V2Header{
	char Header[3];    /*必須爲“ID3”否則認爲標籤不存在*/
	char Ver[1];         /*版本號ID3V2.3 就記錄3*/
	char Revision[1];     /*副版本號此版本記錄爲0*/
	char Flag[1];        /*標誌字節,只使用高三位,其它位爲0 */
	char Size[4];      /*標籤大小*/
};
//起始位置:0x10-0x19
struct ID3V2INFO{
	char ID[4]; /*標識幀,說明其內容,例如作者/標題等*/
	char Size[4]; /*幀內容的大小,不包括幀頭,不得小於1*/
	char Flags[2]; /*標誌幀,只定義了6 位*/
};


ID3V1

//起始位置:-128
struct ID3V1HEAD{
	char tag[3];//tag佔用3字節
	char musicname[30];//音樂標籤佔30字節
	char artist[30];//歌曲作者佔30字節
	char album[30];//發行專輯佔30字節
	char year[4];//發行年份佔4字節
	char generic[30];//一些音樂的介紹以及其他雜七雜八內容佔30字節
	char type[1];//類型,佔1字節
};

part1:ID3v1版本可以通過音樂文件末尾倒數第128字節開始進行數據截取,這也是最簡單的

//用於解析mp3文件,採用id3v1取倒數128固定位置數據
MP3INFO MediaTools::toMp3Info(QString path) {
  int part = 128;
  QString tag, musicname, artist, album, year, generic, musictype;
  QFile f(path);
  f.open(QFile::ReadOnly);//以只讀方式打開文件
  f.seek(f.size() - part);//從倒數128開始讀取數據
  part = part - 3;
  tag = QString::fromLocal8Bit(f.readLine(4));//前3字節爲tag標籤,這裏爲什麼用4,因爲這是qt
  f.seek(f.size() - part);
  part = part - 30;//這裏減去30,是爲了給下面往後移動30字節讀取內容
  musicname = QString::fromLocal8Bit(f.readLine(31));
  f.seek(f.size() - part);
  part = part - 30;
  artist = QString::fromLocal8Bit(f.readLine(31));
  f.seek(f.size() - part);
  part = part - 30;
  album = QString::fromLocal8Bit(f.readLine(31));
  f.seek(f.size() - part);
  part = part - 4;
  year = QString::fromLocal8Bit(f.readLine(5));
  f.seek(f.size() - part);
  part = part - 30;
  generic = QString::fromLocal8Bit(f.readLine());
  f.seek(f.size() - 1);
  musictype = QString::fromLocal8Bit(f.readLine(1));
  f.close();
  //返回mp3info對象,可以用結構體代替
  return MP3INFO(tag, musicname, artist, album, year, generic, musictype);
}

part2:ID3v2.3版本在國內只有原理講解文章與一些開源第三方庫可供參考
我這裏實現了一版非常爛的demo,望各位懂這方面的大佬幫忙指點指點

void MediaTools::seekFiles(int &len, QFile &f) {
  QByteArray by;//創建一個字節數組對象
  f.seek(len);//設置文件讀取起始位置
  //這個爲音樂文件裏0x21起始頭部的幀信息頭部,佔4字節
  qDebug() << "seekFiles  0 ::" << QString::fromLocal8Bit(f.readLine(5));
  f.seek(len + 4);
  //這個爲音樂文件裏幀內容大小
  by = f.read(4);
  qDebug() << "seekFiles  1 ::" << by;
  f.seek(len + 4 + 4);
  qDebug() << "seekFiles  2 ::" << QString::fromLocal8Bit(f.readLine(3));
  int FSize;
  //幀內容大小計算公式
  FSize = by[0] * 0x100000000 + by[1] * 0x10000 + by[2] * 0x100 + by[3];
  qDebug() << "seekFiles  fsize ::" << FSize;
  f.seek(len + 11);
  qDebug() << "seekFiles  data ::" << QString::fromLocal8Bit(f.readLine());
  len = len + 10 + FSize;
  qDebug() << "next positi ::: " << len << "\n";
}

//第一次調用位置
void MediaTools::Mp3ID3V2_3(QString path) {
  QFile f(path);
  f.open(QFile::ReadOnly);
  QByteArray by;
  //讀取0x0-0x9位置的數據
  qDebug() << "0 ::" << QString::fromLocal8Bit(f.readLine(4));//tag標識
  f.seek(3);
  qDebug() << "1 ::" << QString::fromLocal8Bit(f.readLine(2));
  f.seek(4);
  qDebug() << "2 ::" << QString::fromLocal8Bit(f.readLine(2));
  f.seek(5);
  qDebug() << "3 ::" << QString::fromLocal8Bit(f.readLine(2));
  f.seek(6);
  //幀數據內容大小,這裏的大小爲所有幀的內容大小,也就是說,在這個地址往後偏移1位就是音頻數據了
  by = f.read(4);
  qDebug() << "4 ::" << by;
  int len = 10;
  int total_size;

  total_size = (by[0] & 0x7F) * 0x200000 + (by[1] & 0x7F) * 0x400 +
               (by[2] & 0x7F) * 0x80 + (by[3] & 0x7F);
  qDebug() << total_size;
  //通過不斷的調用來獲取音頻文件裏TIT2、TPE1、TALB等內容信息
  seekFiles(len, f);
  seekFiles(len, f);
  seekFiles(len, f);
  seekFiles(len, f);
  seekFiles(len, f);
  seekFiles(len, f);
  seekFiles(len, f);
  seekFiles(len, f);

  f.close();
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章