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是一樣的所以那部分直接照搬了,注釋也沒改。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM