QT 读取mp3ID3V2 获取mp3专辑图片、专辑名称、标题、作者(二)

 

这篇承接上篇,主要记录的是代码,关于mp3ID3V2的简要介绍可以跳转到上一篇:

QT 读取mp3ID3V2 获取mp3专辑图片、专辑名称、标题、作者(一)

前提说明:没有使用任何的外部库,纯代码实现的,根据网上主流的c语言代码提取mp3id3v2标签信息,结合QT修改的

一、头文件

1.1、申明两个结构体,存放相关信息

#ifndef MP3TAGLIB_H
#define MP3TAGLIB_H

#include 
#include "stdlib.h"
#include "stdio.h"
#include 
#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;
class MP3Header;

typedef struct ID3V2FrameH        
{
   char frameID[4];               //存放标签格式,是否为id3v2
   unsigned char size[4];         //存放标签数据的大小
   char flags[2];
}ID3V2FH;

typedef struct MP3INFO
{
    QString Url;            //存放这首歌的详细地址
    QString Name;            //歌名  TIT2
    QString Album;           //专辑  TALB
    QString Singer;          //歌手  TPE1
    QString Picture_url;      //歌曲图片存放路径
    QString Picture_type;     //图片类型 jpg,png
    int     number;          //歌曲编号
    int     beginNum;         //图片起始位置
    int     lenth;            //图片数据长度
    bool     pic_flag;         //是否有图片


}MP3INFO;
typedef struct frameIDStruct
{
    int beginNum;
    int endNum;
    QString FrameId;

}frameIDStruct;

1.2、MP3Header类

class MP3Header
{
public:
    MP3Header();
    FILE *fp;
    QString m_url;
    unsigned char Header[3];
    unsigned char FrameId[4];     //存放帧标识
    unsigned char Header_size[4];
    unsigned int mp3_TagSize;
    unsigned char frameSize[4];      //存放该帧内容的大小 
    unsigned int framecount;          //计算出帧内容的大小
    void GetMp3IDV2(const wchar_t *url);
    MP3INFO GetAllInfo(const wchar_t *url, int songNumber);
    void GetPicture(MP3INFO *mp3info);
    void GetFrameId();
    QString GetInfo(QString fId);
    QMap m_IDmap;

};


#endif // MP3TAGLIB_H

二、源文件

这里只展示GetAllInfo函数的实现,所有的功能都在这个函数内实现了,其他函数是我做音乐播放器用到的,这里不重要

2.1、打开mp3文件,得到标签的类型

MP3INFO MP3Header::GetAllInfo(const wchar_t *url,int songNumber)
{
    m_url = QString::fromWCharArray(url);
    fp = _wfopen(url,L"rb");
    if (NULL==fp)
    {
     printf("open read file error!!");
     MP3INFO falseInfo;
     falseInfo.pic_flag = false;
     return falseInfo;                 
    }
    fseek(fp,0,SEEK_SET);
    fread(&Header,1,3,fp);
    if(Header[0]=='I'&&Header[1]=='D'&&Header[2]=='3')
    {
        qDebug()<<"open ID3 correct!";
    }

说明:

由于歌曲名字有中文也有英文,因此使用wchar_t类型存储,wchar_t宽字符类型一般为16位或32位,在申明一个wchar_t类型的常量时可以使用一下语句:

QString filePath = "C:/Users/Admin/Desktop/song.mp3"
const wchar_t * url = reinterpret_cast(filePath.utf16());

也可以:

const wchar_t *url = L"C:/Users/Admin/Desktop/song.mp3";

fp = _wfopen(url,L"rb");    为c语言的文件读取函数,因为url是宽字节,所以要使用_wfopen而不使用fopen,关于这两个函数的有关内容,可以参考博文:

https://blog.csdn.net/zmq5411/article/details/21003831

    if (NULL==fp)
    {
     printf("open read file error!!");
     MP3INFO falseInfo;
     falseInfo.pic_flag = false;
     return falseInfo;                 
    }

这一小部分的意思是,在函数调用时,可以根据返回的MP3INFO结构体的pic_flag参数判断mp3文件是否被打开成

其中fint fseek(FILE * stream, long offset, int whence);用于移动文件流读取的位置

参数1、FILE * stream为文件的指针;

参数2、long offset为以起始位置为基准向前移动的字节数,这里设置为0,为从第0个字节开始读;

参数3、SEEK_SET表示设置起始位置为文件开头

             SEEK_COR表示文件当前位置

             SEEK_END表示文件末尾

其中size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );用于读取二进制数据

参数1、读取的数据在内存中存放的位置,这里是结构体的指针

参数2、每次要读取的字节数

参数3、读取的此时

参数4、文件指针

2.2、解析并且获取mp3文件信息

    unsigned int i = 10;
    MP3INFO mp3info_struct;
    mp3info_struct.Url = m_url;
    mp3info_struct.number = songNumber;
    while(i<(mp3_TagSize-10))
    {
        frameIDStruct m_struct;

        fseek(fp,i,SEEK_SET);
        fread(&FrameId,1,4,fp);
        fseek(fp,4+i,SEEK_SET);
        fread(&frameSize,1,4,fp);
        framecount = frameSize[0]*0x1000000+frameSize[1]*0x10000+frameSize[2]*0x100+frameSize[3];
        //qDebug()<<"framecount:"<toUnicode(all.toLocal8Bit().data());
             QString unser = ua.mid(0,(int)(lenth/2-1));
             mp3info_struct.Name = unser;
             //mp3info_struct.beginNum = m_struct.beginNum;
             //mp3info_struct.lenth = lenth;
             file.close();
        }else if(m_struct.FrameId=="TPE1")       //歌手
        {
            QFile file(m_url);
             if(!file.open(QIODevice::ReadWrite | QIODevice::Text)) {
                 qDebug()<<"Can't open the file!"<toUnicode(all.toLocal8Bit().data());
             QString unser = ua.mid(0,(int)(lenth/2-1));
             mp3info_struct.Singer = unser;
             //mp3info_struct.beginNum = m_struct.beginNum;
             //mp3info_struct.lenth = lenth;
             file.close();
        }else if(m_struct.FrameId=="TALB")       //专辑
        {
            QFile file(m_url);
             if(!file.open(QIODevice::ReadWrite | QIODevice::Text)) {
                 qDebug()<<"Can't open the file!"<toUnicode(all.toLocal8Bit().data());
             QString unser = ua.mid(0,(int)(lenth/2-1));
             mp3info_struct.Album = unser;
             //mp3info_struct.beginNum = m_struct.beginNum;
             //mp3info_struct.lenth = lenth;
             file.close();
        }
        //qDebug()<<"frameSize:"<

         

代码解释说明:

第一段:

    unsigned int i = 10;
    MP3INFO mp3info_struct;
    mp3info_struct.Url = m_url;
    mp3info_struct.number = songNumber;

变量i用于表示将要在文件的第11个字节开始读取数据(前10个字节为id3v2的标签头)

申明一个结构体 mp3info_struct

第二段:

进入while循环  while(i<(mp3_TagSize-10))  mp3_tagsize记录的是整个标签的字节大小,因此需要将前10个字节跳过,即-10,可以跳转到上一篇博文参考:

QT 读取mp3ID3V2 获取mp3专辑图片、专辑名称、标题、作者(一)

进入循环内:

        frameIDStruct m_struct;

        fseek(fp,i,SEEK_SET);
        fread(&FrameId,1,4,fp);
        fseek(fp,4+i,SEEK_SET);
        fread(&frameSize,1,4,fp);
        framecount = frameSize[0]*0x1000000+frameSize[1]*0x10000+frameSize[2]*0x100+frameSize[3];
        //qDebug()<<"framecount:"<

文件读取帧标识存入FrameId,将其转化为QString格式存入aa;

读取帧内容大小存入framesize;

计算得到帧内容的字节数framecount;

得到帧数据的起始字节位置 m_struct.beginNum = i+10;

得到帧数据结束字节位置,并且i的值变为这个帧数据的结束位置,也就是下一个标签帧的起始位置,那么到下一个循环时,就从下一个标签帧开始读取了

 i =10+ i+framecount;

m_struct.endNum = i;

得到数据长度lenth

第三段:判断图片文件类型

如果 :if(m_struct.FrameId=="APIC") 帧标识为APIC则为图片

进入条件语句内部:

            unsigned char temp[20] = {0};
            fseek(fp,m_struct.beginNum,SEEK_SET);
            fread(&temp,1,20,fp);
            int tank=0;
            int j = 0;
            int pictureFlag=0;

读取图片数据时要跳过前方14个字节,然后判断图片类型的标志位才会出现,由于当时我不知道,因此采用了循环知道出现标志位才跳出循环的办法,即:

            while(1)
            {
                if((temp[j] == 0xff)&&(temp[j+1] == 0xd8))              //jpeg
                {
                    tank = j;
                    pictureFlag=1;
                    mp3info_struct.Picture_type = ".jpg";
                    qDebug()<<"jpeg";
                    qDebug()<<"j:"<

后来发现,这个tank的输出值永远都是14,因此这一步就显得没有必要了

第四段:读取图片数据

相关解释请看注释

            fseek(fp,m_struct.beginNum+tank,SEEK_SET);
            mp3info_struct.beginNum = m_struct.beginNum+tank;
            mp3info_struct.lenth = lenth;
            fread(&t,1,lenth,fp);
            if(pictureFlag==1)          //jpeg
            {
                QString temp_1 = "C:/Users/Admin/Desktop/new_text";
                QString temp_2 = QString::number(songNumber,10);
                temp_1+=temp_2;
                temp_1+=".jpg";                             //以上为生成图片名称
                mp3info_struct.Picture_url = temp_1;
                std::string str_temp = temp_1.toStdString();   //将QString转为string类型
                const char *ch = str_temp.c_str();             //再转为静态
                FILE *fpw = fopen( ch, "wb" );
                fwrite(&t,lenth,1,fpw);                        //生成图片
                fclose(fpw);     //是否也需要关掉fp
                fclose(fp);

            }else if(pictureFlag==2)        //png
            {
                QString temp_1 = "C:/Users/Admin/Desktop/new_text";
                QString temp_2 = QString::number(songNumber,10);
                temp_1+=temp_2;
                temp_1+=".png";
                mp3info_struct.Picture_url = temp_1;
                std::string str_temp = temp_1.toStdString();
                const char *ch = str_temp.c_str();
                FILE *fpw = fopen( ch, "wb" );
                fwrite(&t,lenth,1,fpw);
                fclose(fpw);     //是否也需要关掉fp
                fclose(fp);

            }

第五段:读取标题,歌手等其他数据

else if(m_struct.FrameId=="TIT2")       //标题
        {
            QFile file(m_url);
             if(!file.open(QIODevice::ReadWrite | QIODevice::Text)) {  //如果打开文件失败,处理
                 qDebug()<<"Can't open the file!"<toUnicode(all.toLocal8Bit().data());
             QString unser = ua.mid(0,(int)(lenth/2-1));
             mp3info_struct.Name = unser;
             //mp3info_struct.beginNum = m_struct.beginNum;
             //mp3info_struct.lenth = lenth;
             file.close();
        }

其中:

             QTextCodec *codec = QTextCodec::codecForName("GBK");
             QString ua = codec->toUnicode(all.toLocal8Bit().data());

两行代码是将编码格式转换为GBK

由于GBK格式是两个字节表示一个数据(要表示中文),因此转换之后,后面会出现多余数据,我们只取前面的数据,即:

QString unser = ua.mid(0,(int)(lenth/2-1));

之后的其他数据,如歌手,专辑名称也是同样的方法,最后得到图片的输出路径就会看到图片:

QT 读取mp3ID3V2 获取mp3专辑图片、专辑名称、标题、作者(二)_第1张图片

end

写的很菜,谢谢观看,欢迎批评指正

 

你可能感兴趣的:(QT,QT音乐播放器)