flac文件提取专辑封面手记
生活随笔
收集整理的這篇文章主要介紹了
flac文件提取专辑封面手记
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
博客遷移后整理發型這篇文章當時沒寫完,不補了,不過還是得說明一些東西
下面這部分代碼可用之處為從flac文件頭開始然后各種形式的大跳,最后到達專輯封面的數據塊,之后解析。
當時寫的時候不會寫圖片解析部分,于是照搬了ShadowPlayer中某部分的代碼,其有一特色為如果圖片部分代碼不認照樣會爆搜然后嘗試解析。實際上下面給出的代碼的圖片解析部分就是照搬的,而此處的正確做法恰恰不是如代碼所示,我之前自己嘗試能提取出圖片的原因也就是爆搜成功了。。。
于是如果看官想要研究真正能用的代碼,建議直接去看ShadowPlayer中此部分的代碼。請參見這里。
下面是之前寫的原文:
這是代碼(代碼中的注釋以及為了檢查運行狀態的奇怪提示沒刪,需要的話手動刪除):
1 /*
2 fLaC標簽圖片提取庫 Ver 0.0
3 Gary 于2014/8/1 下午決定亂搞
4 */
5
6 #ifndef _ShadowPowerOff_FLACPIC___
7 #define _ShadowPowerOff_FLACPIC___
8 #define _CRT_SECURE_NO_WARNINGS //安慰vs編譯器用
9 #ifndef NULL
10 #define NULL 0
11 #endif
12 #include <cstdio>
13 #include <cstdlib>
14 #include <memory.h>
15 #include <cstring>
16
17 typedef unsigned char byte;
18 using namespace std;
19
20 namespace spFLAC {
21 //fLaC標簽頭部結構體定義
22 struct FLACHeader //似乎這就不用寫成結構體咯,懶得改先用著
23 {
24 char identi[4];//fLaC頭部校驗,必須為“fLaC”否則認為不存在fLaC標簽
25 };
26
27 //fLaC標簽METADATA_BLOCK_HEADER結構體定義
28 struct FLACMetaDataHeader
29 { //MBFlagType共1bit+7bit=1byte
30 byte MBFlagType;//第一塊1bit用于描述此MetaBlock是(1)不是(0)挨著音頻塊兒
31 //第二塊7bit標志MetaBlock的種類的,其中6為PICTURE,別的用不著
32 byte size[3]; //MetaBlock的大小,不包含 METADATA_BLOCK_HEADER大小
33 };
34
35 //按照官方文檔的說法,圖片塊兒和id3v2的應該是一樣的,下面直接照搬電影同志的代碼
36 byte *pPicData = 0; //指向圖片數據的指針
37 int picLength = 0; //存放圖片數據長度
38 char picFormat[4] = {}; //存放圖片數據的格式(擴展名)
39
40 //檢測圖片格式,參數1:數據,參數2:指向存放文件格式(擴展名)的指針,返回值:是否成功(不是圖片則失敗)
41 bool verificationPictureFormat(char *data)
42 {
43 //支持格式:JPEG/PNG/BMP/GIF
44 byte jpeg[2] = { 0xff, 0xd8 };
45 byte png[8] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a };
46 byte gif[6] = { 0x47, 0x49, 0x46, 0x38, 0x39, 0x61 };
47 byte gif2[6] = { 0x47, 0x49, 0x46, 0x38, 0x37, 0x61 };
48 byte bmp[2] = { 0x42, 0x4d };
49 memset(&picFormat, 0, 4);
50 if (memcmp(data, &jpeg, 2) == 0)
51 {
52 strcpy(picFormat, "jpg");
53 }
54 else if (memcmp(data, &png, 8) == 0)
55 {
56 strcpy(picFormat, "png");
57 }
58 else if (memcmp(data, &gif, 6) == 0 || memcpy(data, &gif2, 6) == 0)
59 {
60 strcpy(picFormat, "gif");
61 }
62 else if (memcmp(data, &bmp, 2) == 0)
63 {
64 strcpy(picFormat, "bmp");
65 }
66 else
67 {
68 return false;
69 }
70
71 return true;
72 }
73
74 //安全釋放內存
75 void freePictureData()
76 {
77 if (pPicData)
78 {
79 delete pPicData;
80 }
81 pPicData = 0;
82 picLength = 0;
83 memset(&picFormat, 0, 4);
84 }
85
86 //將圖片提取到內存,參數1:文件路徑,成功返回true
87 bool loadPictureData(const char *inFilePath)
88 {
89 freePictureData();
90 FILE *fp = NULL; //初始化文件指針,置空
91 fp = fopen(inFilePath, "rb"); //以只讀&二進制方式打開文件
92 if (!fp) //如果打開失敗
93 {
94 fp = NULL;
95 return false;
96 }
97 fseek(fp, 0, SEEK_SET); //設文件流指針到文件頭部
98
99 //讀取
100 FLACHeader fLaCh; //創建一個FLACHeader結構體(即char[4] = "fLaC")
101 memset(&fLaCh, 0, 4); //內存填0,4個字節
102 fread(&fLaCh, 4, 1, fp); //把文件頭部4個字節寫入結構體內存
103
104 //文件頭識別
105 if (strncmp(fLaCh.identi, "fLaC", 4) != 0)
106 {
107 fclose(fp);
108 fp = NULL;
109 return false;//沒有fLaC標簽
110 }
111
112 //能運行到這里應該已經成功打開文件了
113 printf("是flac");
114 system("PAUSE");
115
116 FLACMetaDataHeader fLaCfh; //創建一個fLaCMetaBlockHeader結構體
117 memset(&fLaCfh, 0, 4); //共4byte,第一個字節上面說過了,后3bit記錄標簽實際內容(不含頭)大小
118
119 fread(&fLaCfh, 4, 1, fp); //將數據寫到fLaCMetaBlockHeader結構體中
120 int curDataLength = 4; //存放當前已經讀取的數據大小,剛才已經讀入4字節
121 while((fLaCfh.MBFlagType & 0x7F) != 6) //如果標簽不是6(即picture)則循環執行,
122 {
123 //計算幀數據長度
124 int frameLength = fLaCfh.size[0] * 0x10000 + fLaCfh.size[1] * 0x100 + fLaCfh.size[2];
125 fseek(fp, frameLength, SEEK_CUR); //向前跳躍到下一個幀頭
126 memset(&fLaCfh, 0, 4); //清除幀頭結構體數據
127 fread(&fLaCfh, 4, 1, fp); //重新讀取數據
128 curDataLength += frameLength + 4; //記錄當前所在的ID3標簽位置,以便退出循環
129 printf("剛剛打劫了⑨,沒掉出圖包\n");
130 if ((fLaCfh.MBFlagType & 0x80) == 0x80) return false;//不包含圖片標簽,完事.0x80 = 10000000
131 system("PAUSE");
132 printf("再來一次\n");
133 }
134
135 printf("正在處理掉落");
136 //計算一下當前圖片幀的數據長度
137 int frameLength = fLaCfh.size[0] * 0x10000 + fLaCfh.size[1] * 0x100 + fLaCfh.size[2];
138
139 /*
140 這是ID3v2.3圖片幀的結構:
141
142 <Header for 'Attached picture', ID: "APIC">
143 頭部10個字節的幀頭
144
145 Text encoding $xx
146 要跳過一個字節(文字編碼)
147
148 MIME type <text string> $00
149 跳過(文本 + /0),這里可得到文件格式
150
151 Picture type $xx
152 跳過一個字節(圖片類型)
153
154 Description <text string according to encoding> $00 (00)
155 跳過(文本 + /0),這里可得到描述信息
156
157 Picture data <binary data>
158 這是真正的圖片數據
159 */
160 int nonPicDataLength = 0; //非圖片數據的長度
161 fseek(fp, 1, SEEK_CUR); //信仰之躍
162 nonPicDataLength++;
163
164 char tempData[20] = {}; //臨時存放數據的空間
165 char mimeType[20] = {}; //圖片類型
166 int mimeTypeLength = 0; //圖片類型文本長度
167
168 fread(&tempData, 20, 1, fp);//取得一小段數據
169 fseek(fp, -20, SEEK_CUR); //回到原位
170
171 strcpy(mimeType, tempData); //復制出一個字符串
172 mimeTypeLength = strlen(mimeType) + 1; //測試字符串長度,補上末尾00
173 fseek(fp, mimeTypeLength, SEEK_CUR); //跳到此數據之后
174 nonPicDataLength += mimeTypeLength; //記錄長度
175
176 fseek(fp, 1, SEEK_CUR); //再一次信仰之躍
177 nonPicDataLength++;
178
179 int temp = 0; //記錄當前字節數據的變量
180 fread(&temp, 1, 1, fp); //讀取一個字節
181 nonPicDataLength++; //+1
182 while (temp) //循環到temp為0
183 {
184 fread(&temp, 1, 1, fp); //如果不是0繼續讀一字節的數據
185 nonPicDataLength++; //計數
186 }
187 //跳過了Description文本,以及末尾的\0
188
189 //非主流情況檢測
190 memset(tempData, 0, 20);
191 fread(&tempData, 8, 1, fp);
192 fseek(fp, -8, SEEK_CUR); //回到原位
193 //判斷40次,一位一位跳到文件頭
194 bool ok = false; //是否正確識別出文件頭
195 for (int i = 0; i < 40; i++)
196 {
197 //校驗文件頭
198 if (verificationPictureFormat(tempData))
199 {
200 ok = true;
201 break;
202 }
203 else
204 {
205 //如果校驗失敗嘗試繼續向后校驗
206 fseek(fp, 1, SEEK_CUR);
207 nonPicDataLength++;
208 fread(&tempData, 8, 1, fp);
209 fseek(fp, -8, SEEK_CUR);
210 }
211 }
212
213 if (!ok)
214 {
215 fclose(fp);
216 fp = NULL;
217 freePictureData();
218 return false; //無法識別的數據
219 }
220 //-----真正的圖片數據-----
221 picLength = frameLength - nonPicDataLength; //計算圖片數據長度
222 pPicData = new byte[picLength]; //動態分配圖片數據內存空間
223 memset(pPicData, 0, picLength); //清空圖片數據內存
224 fread(pPicData, picLength, 1, fp); //得到圖片數據
225 //------------------------
226 fclose(fp); //操作已完成,關閉文件。
227
228 return true;
229 }
230
231 //取得圖片數據的長度
232 int getPictureLength()
233 {
234 return picLength;
235 }
236
237 //取得指向圖片數據的指針
238 byte *getPictureDataPtr()
239 {
240 return pPicData;
241 }
242
243 //取得圖片數據的擴展名(指針)
244 char *getPictureFormat()
245 {
246 return picFormat;
247 }
248
249 bool writePictureDataToFile(const char *outFilePath)
250 {
251 FILE *fp = NULL;
252 if (picLength > 0)
253 {
254 fp = fopen(outFilePath, "wb"); //打開目標文件
255 if (fp) //打開成功
256 {
257 fwrite(pPicData, picLength, 1, fp); //寫入文件
258 fclose(fp); //關閉
259 return true;
260 }
261 else
262 {
263 return false; //文件打開失敗
264 }
265 }
266 else
267 {
268 return false; //沒有圖像數據
269 }
270 }
271
272 //提取圖片文件,參數1:輸入文件,參數2:輸出文件,返回值:是否成功
273 bool extractPicture(const char *inFilePath, const char *outFilePath)
274 {
275 FILE *fp = NULL; //初始化文件指針,置空
276 if (loadPictureData(inFilePath)) //如果取得圖片數據成功
277 {
278 if (writePictureDataToFile(outFilePath))
279 {
280 return true; //文件寫出成功
281 }
282 else
283 {
284 return false; //文件寫出失敗
285 }
286 }
287 else
288 {
289 return false; //無圖片數據
290 }
291 freePictureData();
292 }
293 }
294 #endif
調用方法(手動指定輸入文件路徑和輸出文件路徑,輸出文件的格式自己猜吧~ gcc編譯運行測試成功):
1 #include "fLaCPic.h"
2
3 int main(int argc, char* argv[])
4 {
5 using namespace spFLAC;
6 if (argc > 2)
7 {
8 extractPicture(argv[1], argv[2]);
9 }
10 else
11 {
12 printf("參數數量不足");
13 }
14 return 0;
15 }
以上代碼基于Shadow Player的ID3v2Pic.h頭文件改造編寫而成。由于flac格式的官方給出的說明文檔上有說圖片部分和id3v2是一樣的所以那部分直接照搬了,注釋也沒改。
總結
以上是生活随笔為你收集整理的flac文件提取专辑封面手记的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ARM开发板系统移植-----rootf
- 下一篇: Ionic快速安装教程