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


     

    版权声明:本文版权归作者所有,如需转载,请标明出处

  • 相关阅读:
    搭建企业级Docker Registry -- Harbor
    搭建私服-docker registry
    CentOS 7.2修改网卡名称
    tomcat错误日志监控脚本
    Openresty+Lua+Redis灰度发布
    Jenkins权限控制-Role Strategy Plugin插件使用
    Rsyslog日志服务搭建
    awk4.0对数组value排序
    Spring-IOC 在非 web 环境下优雅关闭容器
    Spring-IOC bean 生命周期之 Lifecycle 钩子
  • 原文地址:https://www.cnblogs.com/studywithallofyou/p/9020974.html
Copyright © 2020-2023  润新知