首先判斷MP3文件中是否含有ID3V2的標籤,關於ID3V2的格式有一堆的說法api
我嘛,不怎麼關心,所以只攻專輯圖片,也就是判斷是否包含APIC這個標識app
找到這個標識其實也就是和解析普通文件同樣,每一個像APIC標識的東西,都有幀標識頭和幀標識內容組成,有頭中得出幀內容的實際長度便可,而後將數據讀出就行啦函數
可是對於APIC標識,讀出了內容,直接保存爲文件並不能顯示圖片,由於這個幀內容的前面一部分用於記錄圖片的類型,以後還有一部分空數據,這裏就須要些判斷this
我用的mp3文件大部分都是jpeg的圖片,在APIC幀內容中開頭就是image/jpeg,這種內容,因爲JPEG圖片以0xFFD8開始,所以只要判斷幀內容中以0xFFD8開始便可獲取真正的圖片數據,固然了,其餘的數據格式,我就沒有判斷了,如今附上C++代碼圖片
#include <QCoreApplication>
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <string.h>
int ReadAIPCFromMP3(const char* path);
int main(int argc, char *argv[])
{
ReadAIPCFromMP3("/Users/apple/Downloads/Avicii - The Nights.mp3");
return 0;
//QCoreApplication a(argc, argv);
//return a.exec();
printf("cannot open this mp3\n");
//return 0;
}
bool isFrameAPIC(const char* cID3V2Fra_head)
{
// 在win7中vs2012下,沒發現strncasecmp函數,否則不須要兩次判斷
// 固然了,這種判斷也能夠逐字節判斷
if ((strncmp(cID3V2Fra_head, "APIC", 4) == 0) ||
(strncmp(cID3V2Fra_head, "apic", 4) == 0))
return true;
return false;
}
int calcID3V2Len(const char* cID3V2_head)
{
int len = (cID3V2_head[6] & 0x7f) << 21
| (cID3V2_head[7] & 0x7f) << 14
| (cID3V2_head[8] & 0x7f) << 7
| (cID3V2_head[9] & 0x7f);
return len;
}
int calclID3V2FraLength(const char* cID3V2Fra_head)
{
int len = (int)(cID3V2Fra_head[4] * 0x100000000 +
cID3V2Fra_head[5] * 0x10000 +
cID3V2Fra_head[6] * 0x100 +
cID3V2Fra_head[7]);
return len;
}
bool isJPEG(const char* data)
{
// JPEG格式數據的起始爲0xFFD8,固然了,結束也有標誌,可是這裏不須要了
if (((unsigned char)data[0] == 0xFF) &&
((unsigned char)data[1] == 0xD8))
return true;
return false;
}
int ReadAIPCFromMP3(const char* path)
{
FILE* fp = fopen(path, "rb");
if (!fp)
{
printf("cannot open this mp3\n");
return -1;
}
// 這裏的ID3V2以及幀標識的大小由標準規定均爲10個字節
// 這裏其實應該是做爲字節存儲,用unsigned char更好,這裏就簡單用char替代吧
char cID3V2_head[10] = {0}, cID3V2Fra_head[10] = {0};
long ID3V2_len = 0, lID3V2Fra_length = 0;
char* cID3V2Fra = NULL;
// 讀取幀頭,這裏就是爲了判斷是不是ID3V2的標籤頭
fread(cID3V2_head, 10, 1, fp);
if ((cID3V2_head[0] == 'I' || cID3V2_head[0] == 'i') &&
(cID3V2_head[1] == 'D' || cID3V2_head[1] == 'd') &&
cID3V2_head[2] == '3')
{
// 獲取ID3V2標籤的長度
ID3V2_len = calcID3V2Len(cID3V2_head);
}
bool hasAPIC = false;
// 這裏來逐個讀取幀標識,這裏的專輯圖片即保存在APIC標識裏
while ((ftell(fp) + 10) <= ID3V2_len)
{
// 這裏每一個幀標識的長度也爲10,因爲每一個幀標識的存儲的數據的長度不一
// 每次要讀取出來,進行運算獲取真正數據長度
memset(cID3V2Fra_head, 0, 10);
fread(cID3V2Fra_head, 10, 1, fp);
lID3V2Fra_length = (cID3V2Fra_head[4] * 0x100000000 +
cID3V2Fra_head[5] * 0x10000 +
cID3V2Fra_head[6] * 0x100 +
cID3V2Fra_head[7]);
// 這裏判斷是否爲專輯圖片的幀標識
if (isFrameAPIC(cID3V2Fra_head))
{
cID3V2Fra = (char*)calloc(lID3V2Fra_length, 1);
if (cID3V2Fra != NULL)
{
hasAPIC = true;
fread(cID3V2Fra, lID3V2Fra_length, 1, fp);
}
break;
}
else
{
// 移動到下一幀標識
fseek(fp, lID3V2Fra_length, SEEK_CUR);
}
}
fclose(fp);
// 到這裏若是,專輯圖片要麼讀出,要麼就不存在
if (hasAPIC)
{
// 這裏整個數據的前面一部分數據是用來記錄專輯圖片的格式
// 例如 image/jpeg image/png等,這裏大部分的專輯圖片都是jpeg的
// 所以這裏簡單的只判斷jpeg的格式,除去圖片格式,數據前部依然有些是空數據
// 所以以jpeg的標識來定位圖片數據的起始
int start = 0;
while (start < lID3V2Fra_length)
{
if (isJPEG(cID3V2Fra + start))
break;
++start;
}
// 是以JPEG格式存在,則存儲爲jpeg的文件
if (start != lID3V2Fra_length)
{
// 這裏沒有錯誤處理,從簡
FILE* jpegFP = fopen("/Users/apple/Downloads/test.jpeg", "wb");
fwrite(cID3V2Fra + start, lID3V2Fra_length - start, 1, jpegFP);
fclose(jpegFP);
}
}
return 0;
}