mp3 音频 音乐 tag ID3 ID3V1 ID3V2 标签 读取信息 获得图片 jpeg bmp 图片转换(下)

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;
}

 


 

 

posted @ 2012-07-12 14:44  秋来叶黄  阅读(469)  评论(0编辑  收藏  举报