最近在研究MP3中图片提取,发现这篇文章,转到自己博客,留用。原文出处http://blog.csdn.net/studywithallofyou/article/details/7738785
看到标题估计有人懵了,不过对于用到的人可以说是眼前一亮啊。做了点这个小东西,断断续续的花了我好长时间。本来不想写,但是网上的资料都是坑爹呢。
中国的抄袭果然是根深蒂固啊。上到机构、砖家、公司,下到各种各种技术博客的copy paste.有好多弄出来的东西都是错误的。自己都没实践,哪来的优越感放网上。
废话少说,开正题。
1.mp3标签简介
我们得到一个mp3文件后,通过播放器,或是右击看其属性,我们会发现除了文件大小和名字,还会有一些其他信息。比如作者,类型,年月日,有的还有一个小图片。
这些信息都是在mp3的源文件内。源文件内除了音乐的数据,还会有一些标识性信息。在计算机的世界,几乎所有的文件都是这样,不然系统怎么直到你这一堆二进制代码是什么东西,我要对它做什么。
在mp3的数据内,有多种标签标识它的信息,最常见的就是ID3。一开始是ID3V1,这个标签在mp3的最后128个字节。前3个字节是TAG,用来标识标签的开始。在linux下,你可以用vim直接打开一个mp3文件,移到最后看一下。在windows下可以用UItraEdit。后面不同的标签会有不同的标识符,每个标识符后面就是相应的数据。ID3V1的各标识符的大小都是固定的,很好读取。不过有些限制,就是不能存中文,没有图片,表示的内容也有限。
图:ID3V1标签结构
后来ID3升级到ID3V2。ID3V2有4个版本,现在常见的应该是ID3V2.4,也就是第4个版本。ID3V2的标签在mp3的开头。前10个字节是整个标签的标签头,记录了这个标签的标识,版本,整个标签的大小。前3个是标识位,其值为ID3,表示标签存在,如果前三个字节不是ID3,那就表示没有ID3V2的标签。然后一个字节是版本号,下一个字节为副版本号,再下一个是标志位。这三个字节都没太大用。最后4个字节是标签的大小,这个挺重要。
图:ID3V2标签结构
图:ID3V2头结构
说一下这四个字节是如何计算标签大小的。你最好用BYTE类型或是unsigned char类型类保存这四个字节,如果高位为1,会被认为负数。不过这里不用怕这个,因为这四个字节的每一个字节的最高位都是0,况且计算时去掉这四位。什么意思呢?就是本来是用4个字节,也就是32位的二进制数来表示标签大小,在这里把最高位都制为0,并且去掉,不加入计算。也就相当于是28位2进制数来表示标签大小。具体原因不明,反正成为了规定,就遵循吧。计算公式:
int Len = (size[0]&0x7f)*0x200000
+(size[1]&0x7f)*0x4000
+(size[2]&0x7f)*0x80
+(size[3]&0x7f);
得到的数就是标签的大小,也就是多少个字节。但是这个大小是不包括标签头的10个字节的。
读完这10个标签头的字节,再往下读10个字节就是标签帧的帧头了,和标签头的10个字节一样,它也记录了标签帧的大小,标签的内容和标志。
char FrameID[4]; /*用四个字符标识一个帧,说明其内容*/
+size[1]*0x10000
+size[2]*0x100
+size[3];
1代表字符使用UTF-16编码方式;
2代表字符使用 UTF-16BE编码方式;
3代表字符使用UTF-8编码方式。
目前只有4中编码方式,你可以计算第一个字节的大小,然后和这个表对应,就能直到使用什么编码方式保存的数据了。
如果是0或是3这两种编码方式的话,你就用一个char的数组去读这个数据,然后输出,就是你想要的内容。
如果是1或2这两种编码的话,那就要用宽字节数组去读,就是wchar_t声明的数组读取。
记住要跳过第一个字节。
3.读取图片
如果你想读取mp3里面的专辑图片的话,就要找到APIC标签,然后计算出标签大小。APIC标签的数据部分也比较特殊,首先在数据的前几个字节里面,并没有图片的数据,而是告诉你这里保存的图片数据是什么格式的,大部分都是jpeg的,因为占地小。
在数据部分前几个字节会出现一个特殊标志 image/jpeg 来标明下面的数据是jpeg格式的,如果是其他格式则为image/png image/bmp,jpeg的格式可能会有jpg peg等格式来表示。也就是image/jpg 也表示是jpeg的。
然后开始读取图片数据,在image/jpeg这个标志后面就保存了图片的数据,不过并不是image/jpeg结束后第一个字节就是图片数据,在这里image/jpeg出现的位置和图片的数据开始的位置都是不固定的,就是在它们前后可能都有一些空字节,所以要判断一下。
我们查询jpeg的图片格式可以知道当连着的两个字节是16进制0xFFD8时,就表示图片的数据开始的位置,所以你要从这里开始读,包括0xFFd8也要读进去,也就是FF是第一个字节,D8是第二个字节,然后往下读,直到读到连续两个字节是0xFFD9,这就表示图片数据读完了,记住也要把这两个结束字符读进去。
前面我们已经计算出这个APIC的大小,你可以用这个大小减去不是图片数据的字节数,从0xFFD8开始读剩下的字节数就可以了。
我们把读到的数据放到数组里,然后写到一个空文件里,就是一幅jpeg的图片了
4.图片转换
得到jpeg图片后,如果我们想转换成其它图片,那么就需要进行图片转码了。jpeg图像之所以很小,是因为里面进行了多次压缩。先要把计算机上常用的RGB图像源数据转换成YCrCb,然后进行DCT变换,重排DCT结果,量化,这样就会是图像数组的左上角是相似的数字,剩下的大部分是0。然后是RLE编码,huffman编码。最后编出来一堆你不认识的值,加上各种段,标志,保存成jpeg。所以如果我们想转换成其它图片的话,就要反着来一边。这个工作比较麻烦,也比较复杂。所以我们可以用第三方的库来实现。我这里用的是jpeglib。
然后下载jpegsr8d.zip包。虽然它写着是for windows的,其实里面是一个linux下的工程,你需要的东西必须编译一下,才能生成。
在包的里面有一个install.txt的文档,里面告诉你在不同的环境下如何编译,我截取一段在windows下用vs2010编译的部分:
Microsoft Windows, Microsoft Visual C++ 2010 Developer Studio (v10):
We include makefiles that should work as project files in Visual Studio
2010 or later. There is a library makefile that builds the IJG library
as a static Win32 library, and application makefiles that build the sample
applications as Win32 console applications. (Even if you only want the
library, we recommend building the applications so that you can run the
self-test.)
To use:
1. Open the command prompt, change to the main directory and execute the
command line
NMAKE /f makefile.vc setup-v10
This will move jconfig.vc to jconfig.h and makefiles to project files.
(Note that the renaming is critical!)
2. Open the solution file jpeg.sln, build the library project.
(If you are using Visual Studio more recent than 2010 (v10), you'll
probably get a message saying that the project files are being updated.)
3. Open the solution file apps.sln, build the application projects.
4. To perform the self-test, execute the command line
NMAKE /f makefile.vc test-build
5. Move the application .exe files from `app`\Release to an
appropriate location on your path.
Note:
There seems to be an optimization bug in the compiler which causes the
self-test to fail with the color quantization option.
We have disabled optimization for the file jquant2.c in the library
project file which causes the self-test to pass properly.
也就是先执行NMAKE /f makefile.vc setup-v10,然后用vs打开jpeg.sln工程,编译一下,然后打开apps.sln工程,里面有好几个工程,每一个都编译一遍,这时候你得到这四个文件:jconfig.h jmorecfg.h jpeg.lib jpeglib.h,把它们放到你的工程目录下,然后在程序开始包含头文件的时候包含进去就可以了。然后上网搜一下copy 粘贴最多的那片文章,把里面代码稍微改一下就可以用了。后面的命令是测试用的,不用管。
如何执行NMAKE /f makefile.vc setup-v10?先打开开始菜单,找到visual studio2010的文件夹,找到visual studio tools,然后在里面打开x64 cross tools command,进入到makefile.vc目录下,也就是你解压的目录下,执行就可以了。在这个command下切换目录是,先输入文件所在盘符名,比如C:,回车,然后cd 加文件名进入即可。
这个第三方库帮我们把jpeg的解码都做了,我们获得其中的图像数据RGB,然后上网查一下bmp的格式,然后把那篇拷贝粘贴最多的程序,改一下把数据写进去就可以了。
bmp格式很简单,只有两个头标签,然后是把RGB数据写到标签后面就行了,没有压缩,所以比jpeg大十几倍或几十倍。
1.语言
在这里我是用的c c++来实现的,在windows下。
2.思路
这里必须先读取mp3源文件,然后对每段数据读取处理
3.具体细节
主要用到了文件操作,用fopen打开mp3文件,用fread读取,记住fread读取是按字节的,也就是每次读取一个字节保存到数组。你可能还需要fseek来甚至从哪里读取信息。还有写函数fwrite。
还需要字符处理函数memcpy,strncmp,strcmp,ZeroMemory,strcpy等,记住声明的变量一定要初始化,尤其是数组和字符串变量。
4.其他
音频除了mp3,还有aac wma等格式,这些里面也有变迁,但是和mp3的不一样。mp3里面除了ID3还有APE等标签,其格式也不同。不过大多数音频是mp3,用的是ID3
5.代码:
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <math.h>
#define XMD_H
#include "jpeglib.h"
#include <setjmp.h>
#include <afxwin.h>
#include <Windows.h>
#include <initguid.h>
#include <ShlObj.h>
#pragma comment(lib, "jpeg.lib")
//id3v2 标签头结构体
typedef struct
{
char identi[3];//必须为ID3,否则认为标签不存在
byte major; //版本号
byte revsion; //副版本号
byte flags; //标志位
byte size[4]; //标签大小,不包括标签头的10个字节
}ID3V2Header;
//标签帧头结构体
typedef struct
{
char FrameId[4];//标签帧标识符,用来表明这个标签帧的内容
byte size[4]; //此标签帧的大小,不包括此标签头的10个字节
byte flags[2]; //标志位
}ID3V2Frameheader;
char *MusicName="Skylar Grey - I Love the way u lie.mp3";
//char *MusicName="home.mp3";
long width = 0;
long height = 0;
FILE *OpenMusic(int &Len)
{
FILE *fp=NULL;
fp=fopen(MusicName,"rb");
if(!fp)
{
printf("无法打开文件\n");
fp=NULL;
return fp;
}
//把打开的音乐文件流的内部指针重置到文件开头
fseek(fp,0,SEEK_SET);
//读取标签头
ID3V2Header mp3ID3V2;
ZeroMemory(&mp3ID3V2,10);
fread(&mp3ID3V2,10,1,fp);
//判断有没有id3v2的标签头
if(0!=strncmp(mp3ID3V2.identi,"ID3",3))
{
printf("没有ID2V2标签\n");
fclose(fp);
fp=NULL;
return fp;
}
//计算处整个标签的大小
Len = (mp3ID3V2.size[0]&0x7f)*0x200000
+(mp3ID3V2.size[1]&0x7f)*0x4000
+(mp3ID3V2.size[2]&0x7f)*0x80
+(mp3ID3V2.size[3]&0x7f);
return fp;
}
BOOL GetPicInfo(FILE *fp, int &dwFrame, int Len, int &tempi)
{
ID3V2Frameheader pFrameBuf;
ZeroMemory(&pFrameBuf,10);
fread(&pFrameBuf,10,1,fp);
int i=0;
//找到图片标签的所在位置
while((strncmp(pFrameBuf.FrameId,"APIC",4) != 0))
{
//判断是否有图片
if(i>Len)
{
printf("没有找到标识图像帧的标签\n");
return FALSE;
}
dwFrame= pFrameBuf.size[0]*0x1000000
+pFrameBuf.size[1]*0x10000
+pFrameBuf.size[2]*0x100
+pFrameBuf.size[3];
fseek(fp,dwFrame,SEEK_CUR);
ZeroMemory(&pFrameBuf,10);
fread(&pFrameBuf,10,1,fp);
i++;
}
//计算出图片标签的大小
dwFrame= pFrameBuf.size[0]*0x1000000
+pFrameBuf.size[1]*0x10000
+pFrameBuf.size[2]*0x100
+pFrameBuf.size[3];
char image_tag[7]={"0"};
char pic_type[5]={"0"};
fread(image_tag,6,1,fp);
//判断图片格式
i=0;
while(true)
{
if(i>dwFrame)
{
printf("没有找到标识图像类型的标签\n");
fclose(fp);
return FALSE;
}
if(0==(strcmp(image_tag,"image/")))
{
tempi+=6;
fread(pic_type,4,1,fp);
//mp3里面大多图片都是jpeg,也是以jpeg作为标志的
//也有以jpe,jpg,peg作为标志的
//不过也有png等格式的。
if(0==strncmp(pic_type,"jpeg",4))
{
tempi+=4;
break;
}
else if(0==strncmp(pic_type,"jpg",3))
{
tempi+=3;
fseek(fp,-1,SEEK_CUR);
break;
}
else if(0==strncmp(pic_type,"peg",3))
{
tempi+=3;
fseek(fp,-1,SEEK_CUR);
break;
}
else
{
printf("图片格式不是jpeg\n");
fclose(fp);
return FALSE;
}
}
else
{
i++;
fseek(fp,-5,SEEK_CUR);
fread(image_tag,6,1,fp);
tempi=tempi+1;
continue;
}
}
return TRUE;
}
void GetPicRGB(FILE *fp, int dwFrame, int tempi)
{
TCHAR lpTempPathBuffer[MAX_PATH];
TCHAR szTempFileName[MAX_PATH];
DWORD dwRetVal=0;
UINT uRetVal=0;
BYTE *pPicData;
unsigned char * bmpDataBuffer=NULL;
//这两个tag的是表明图片数据的开始
//jpeg图片开始的标志是0xFFD8
BYTE jpeg_header_tag1;
BYTE jpeg_header_tag2;
fseek(fp,0,SEEK_CUR);
fread(&jpeg_header_tag1,1,1,fp);
fseek(fp,0,SEEK_CUR);
fread(&jpeg_header_tag2,1,1,fp);
//计算出图片数据开始的地方
int i=0;
while(true)
{
if(i>dwFrame)
{
printf("没有找到图像数据\n");
fclose(fp);
bmpDataBuffer=NULL;
}
i++;
if((255==jpeg_header_tag1) && (216==jpeg_header_tag2))
{
pPicData = new BYTE[dwFrame-tempi];
ZeroMemory(pPicData,dwFrame-tempi);
//设定文件流的指针位置,并把图片的数据读入pPicData
fseek(fp,-2,SEEK_CUR);
fread(pPicData,dwFrame-tempi,1,fp);
fclose(fp);
fp=fopen("temp.jpeg","w+b");
fwrite(pPicData,dwFrame-tempi,1,fp);
delete []pPicData;
DeleteFile(szTempFileName);
break;
}
else
{
fseek(fp,-1,SEEK_CUR);
fread(&jpeg_header_tag1,1,1,fp);
fseek(fp,0,SEEK_CUR);
fread(&jpeg_header_tag2,1,1,fp);
tempi++;
continue;
}
}
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_decompress(&cinfo);
fseek(fp,0,SEEK_SET);
jpeg_stdio_src(&cinfo,fp);
jpeg_read_header(&cinfo,1);
width = cinfo.image_width;
height = cinfo.image_height;
JSAMPARRAY buffer;
jpeg_start_decompress(&cinfo);
bmpDataBuffer=new unsigned char[width*height*3];
buffer=(*cinfo.mem->alloc_sarray)
((j_common_ptr) &cinfo, JPOOL_IMAGE, (width*3), 1);
//获得图像rgb源数据,存入数组bmpDataBuffer
while(cinfo.output_scanline < cinfo.output_height)
{
int j=0;
jpeg_read_scanlines(&cinfo, buffer, 1);
if(cinfo.num_components==1)
{
//这个地方是把256转成24.我们得到的256的bmp图片,是在头信息的地方加了一个调色板
//调色板的大小是1024,是四个字节表示一种颜色,一共256种颜色
//256bmp下面不是rgb数据,是针对于调色板的一个索引,一个字节,8位,共可以表示2的8次方,也就是256种颜色
//如果num_componets=1也就表示是一种黑白的图片,黑白的图片的调色板的每种颜色的rgb是相等的,就是如果rgb相等
//显示出来的颜色是黑白的。这时我们可以用两种bmp来显示这个图片
//一个是256,调色板加索引数据
//另一种是24位图,24的bmp是没有调色板,后面3位表示一个颜色的rgb,所以是2^8*2^8*2^8,共可以表示2^24种颜色
//基于上面256灰度图的索引值就是rgb值,因为3个相等,所以可以把上面的256灰度的bmp去掉调色板,把每个索引用
//3位表示,值和索引一样,3个值也一样,这样就转成了一个24位的灰度图
for(j=0;j<width;j++)
{
bmpDataBuffer[(height - cinfo.output_scanline)*(width*3)+3*j] = buffer[0][j]; //R
j++;
bmpDataBuffer[(height - cinfo.output_scanline)*(width*3)+3*j+1] = buffer[0][j]; //G
j++;
bmpDataBuffer[(height - cinfo.output_scanline)*(width*3)+3*j+2] = buffer[0][j]; //B
}
}
if(cinfo.num_components==3)
{
for(j=0;j<(width*3);j++)
{
bmpDataBuffer[(height - cinfo.output_scanline)*(width*3)+j] = buffer[0][j+2]; //R
j++;
bmpDataBuffer[(height - cinfo.output_scanline)*(width*3)+j] = buffer[0][j]; //G
j++;
bmpDataBuffer[(height - cinfo.output_scanline)*(width*3)+j] = buffer[0][j-2]; //B
}
}
}
jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
//关闭临时文件指针,删除临时文件
fclose(fp);
remove(szTempFileName);
BITMAPFILEHEADER fileHeader;
BITMAPINFOHEADER infoHeader;
//bmp header(fileHeader)
ZeroMemory(&fileHeader, sizeof(BITMAPFILEHEADER));
fileHeader.bfType = 'MB';
fileHeader.bfSize = sizeof(BITMAPFILEHEADER)+ sizeof(BITMAPINFOHEADER) + (height * width * 3);
fileHeader.bfOffBits = sizeof(BITMAPFILEHEADER)+ sizeof(BITMAPINFOHEADER) ;
//bmp infoheader(infoHeader)
ZeroMemory(&infoHeader, sizeof(infoHeader));
infoHeader.biSize = sizeof(infoHeader);
infoHeader.biWidth = width;
infoHeader.biHeight = height;
infoHeader.biPlanes = 1;
infoHeader.biBitCount = 24;
infoHeader.biCompression = BI_RGB;
infoHeader.biSizeImage = width * height;
infoHeader.biXPelsPerMeter = 0;
infoHeader.biYPelsPerMeter = 0;
infoHeader.biClrUsed = 0;
infoHeader.biClrImportant = 0;
FILE *fpbmp=fopen("a.bmp","w+b");
fwrite(&fileHeader,sizeof(fileHeader),1,fpbmp);
fwrite(&infoHeader,sizeof(infoHeader),1,fpbmp);
fwrite(bmpDataBuffer,width*height*3,1,fpbmp);
fclose(fpbmp);
}
void jpgtobmp(const char *strSourceFileName, const char *strDestFileName)
{
BITMAPFILEHEADER bfh; // bmp文件头
BITMAPINFOHEADER bih; // bmp头信息
RGBQUAD rq[256]; // 调色板
int nAdjust; // 用于字节对齐
char indata[1000000]; // 用于存放解压缩前的图像数据,该数据直接从jpg文件读取
BYTE *data= NULL;
int nComponent = 0;
// 声明解压缩对象及错误信息管理器
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_decompress(&cinfo);
FILE *f = fopen(strSourceFileName,"rb");
if (f==NULL)
{
printf("Open file error!\n");
return;
}
// 下面代码用于解压缩,从本行开始解压缩
jpeg_stdio_src(&cinfo, f );
jpeg_read_header(&cinfo, TRUE);
nAdjust = cinfo.image_width*cinfo.num_components%4;
if (nAdjust) nAdjust = 4-nAdjust;
data = new BYTE[(cinfo.image_width*cinfo.num_components+nAdjust)*cinfo.image_height];
jpeg_start_decompress(&cinfo);
JSAMPROW row_pointer[1];
while (cinfo.output_scanline < cinfo.output_height)
{
row_pointer[0] = &data[(cinfo.output_height - cinfo.output_scanline-1)*(cinfo.image_width*cinfo.num_components+nAdjust)];
jpeg_read_scanlines(&cinfo,row_pointer ,
1);
}
jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
// 上面代码用于解压缩,到本行为止解压缩完成
fclose(f);
// 以下代码讲解压缩后的图像存入文件,可以根据实际应用做其他处理,如传输
f=fopen(strDestFileName,"wb");
if (f==NULL)
{
delete [] data;
//delete [] pDataConv;
return;
}
// 写文件头
memset(&bfh,0,sizeof(bfh));
bfh.bfSize = sizeof(bfh)+sizeof(bih);
bfh.bfOffBits = sizeof(bfh)+sizeof(bih);
if (cinfo.num_components==1)
{
bfh.bfOffBits += 1024;
bfh.bfSize += 1024;
}
bfh.bfSize += (cinfo.image_width*cinfo.num_components+nAdjust)*cinfo.image_height;
bfh.bfType = 0x4d42;
fwrite(&bfh,sizeof(bfh),1,f);
// 写图像信息
bih.biBitCount = cinfo.num_components*8;
bih.biSize = sizeof(bih);
bih.biWidth = cinfo.image_width;
bih.biHeight = cinfo.image_height;
bih.biPlanes = 1;
bih.biCompression = 0;
bih.biSizeImage = (cinfo.image_width*cinfo.num_components+nAdjust)*cinfo.image_height;
bih.biXPelsPerMeter = 0;
bih.biYPelsPerMeter = 0;
bih.biClrUsed = 0;
bih.biClrImportant = 0;
fwrite(&bih,sizeof(bih),1,f);
// 写调色板
if (cinfo.num_components ==1)
{
for (int i=0;i<256;i++)
{
rq[i].rgbBlue =i;
rq[i].rgbGreen = i;
rq[i].rgbRed = i;
rq[i].rgbReserved = 0;
}
fwrite(rq,1024,1,f);
}
if (cinfo.num_components==3)
{
// 调整rgb顺序
for (int j=0;j<bih.biHeight;j++)
for (int i = 0;i<bih.biWidth;i++)
{
BYTE red = data[j*(cinfo.image_width*cinfo.num_components+nAdjust)+i*3];
data[j*(cinfo.image_width*cinfo.num_components+nAdjust)+i*3] = data[j*(cinfo.image_width*cinfo.num_components+nAdjust)+i*3+2];
data[j*(cinfo.image_width*cinfo.num_components+nAdjust)+i*3+2] = red;
}
}
fwrite(data,(cinfo.image_width*cinfo.num_components+nAdjust)*cinfo.image_height,1,f);
fclose(f);
delete [] data;
}
void jpgtobmp1(const char *strSourceFileName, const char *strDestFileName)
{
BITMAPFILEHEADER bfh; // bmp文件头
BITMAPINFOHEADER bih; // bmp头信息
RGBQUAD rq[256]; // 调色板
int nAdjust; // 用于字节对齐
char indata[1000000]; // 用于存放解压缩前的图像数据,该数据直接从jpg文件读取
BYTE *data= NULL;
BYTE *data1=NULL;
int nComponent = 3;
int just;
// 声明解压缩对象及错误信息管理器
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_decompress(&cinfo);
FILE *f = fopen(strSourceFileName,"rb");
if (f==NULL)
{
printf("Open file error!\n");
return;
}
// 下面代码用于解压缩,从本行开始解压缩
jpeg_stdio_src(&cinfo, f );
jpeg_read_header(&cinfo, TRUE);
nAdjust = cinfo.image_width*cinfo.num_components%4;
if (nAdjust) nAdjust = 4-nAdjust;
just = cinfo.image_width*nComponent%4;
if (just) just = 4-just;
data = new BYTE[(cinfo.image_width*cinfo.num_components+nAdjust)*cinfo.image_height];
data1=new BYTE[(cinfo.image_width*nComponent+just)*cinfo.image_height];
jpeg_start_decompress(&cinfo);
JSAMPROW row_pointer[1];
while (cinfo.output_scanline < cinfo.output_height)
{
row_pointer[0] = &data[(cinfo.output_height - cinfo.output_scanline-1)*(cinfo.image_width*cinfo.num_components+nAdjust)];
jpeg_read_scanlines(&cinfo,row_pointer ,
1);
}
jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
// 上面代码用于解压缩,到本行为止解压缩完成
fclose(f);
// 以下代码讲解压缩后的图像存入文件,可以根据实际应用做其他处理,如传输
f=fopen(strDestFileName,"wb");
if (f==NULL)
{
delete [] data;
//delete [] pDataConv;
return;
}
// 写文件头
memset(&bfh,0,sizeof(bfh));
bfh.bfSize = sizeof(bfh)+sizeof(bih);
bfh.bfOffBits = sizeof(bfh)+sizeof(bih);
bfh.bfSize += (cinfo.image_width*nComponent+just)*cinfo.image_height;
bfh.bfType = 0x4d42;
fwrite(&bfh,sizeof(bfh),1,f);
// 写图像信息
bih.biBitCount = nComponent*8;
bih.biSize = sizeof(bih);
bih.biWidth = cinfo.image_width;
bih.biHeight = cinfo.image_height;
bih.biPlanes = 1;
bih.biCompression = 0;
bih.biSizeImage = (cinfo.image_width*nComponent+just)*cinfo.image_height;
bih.biXPelsPerMeter = 0;
bih.biYPelsPerMeter = 0;
bih.biClrUsed = 0;
bih.biClrImportant = 0;
fwrite(&bih,sizeof(bih),1,f);
for(int testi=0; testi<((cinfo.image_width*cinfo.num_components+nAdjust)*cinfo.image_height); testi++)
{
data1[testi*3]=data[testi];
data1[testi*3+1]=data[testi];
data1[testi*3+2]=data[testi];
}
fwrite(data1,(cinfo.image_width*nComponent+just)*cinfo.image_height,1,f);
fclose(f);
delete [] data;
}
int main()
{
FILE *fp=NULL;
//ID3大小
int Len=0;
fp=OpenMusic(Len);
if(NULL==fp)
{
return 0;
}
//图片帧大小
int dwFrame=0;
//记录图片标签数据中不是图片数据的字节数
int tempi=0;
if(FALSE==GetPicInfo(fp,dwFrame,Len,tempi))
{
return 0;
}
//获取图片数据
GetPicRGB(fp,dwFrame,tempi);
jpgtobmp("temp.jpeg","b.bmp");
jpgtobmp1("temp.jpeg","c.bmp");
return 1;
}