C语言解析MP3数据结构

C语言解析MP3数据结构
本章博客编写的初始原因:MP3一般有CBR和VBR两种格式,其中,正常的VBR会把总帧数记录在MP3数据帧的第一帧中,从而利用总帧数计算MP3的总时间。但是,偶尔会遇到一些mp3文件,它是VBR格式,却没有在第一个数据帧记录总帧数。这一类mp3文件是获取不了正确的总时间的,比如Windows Media Player,Kugou等播放器都是无法获取正确总时间的;为了获取总帧数,我决定去遍历整个数据,统计总帧数;

这篇博客的代码,只需稍作修改,就可以计算总帧数和平均bitrate;

在看这边文章之后,希望你已经对MP3的数据结构有一定了解,可以先搜索《MP3详细解析》,看一下其他人的博客,熟悉MP3结构之后再来看这边文章;

以下是MP3数据帧Header的结构,在其他博客中都会看到这个表格,我的代码就是研究这个表格之后,整理编写;
1.mp3数据帧的头

2.根据Layer和Mpeg获得数据帧长度


先大概说明一下以下代码的功能:先解析mp3数据的前3帧,如果前3帧的bitrate相同,说明这确实是一个CBR格式MP3,退出此函数;如果前3帧的bitrate不同,则解码前100帧数据,统计总帧数,计算平均bitrate;

代码中会使用到的定义:

static const long table_BitRate_V1L1[15]	= {0,32,64,96,128,160,192,224,256,288,320,352,384,416,448};
static const long table_BitRate_V1L2[15]	= {0,32,48,56,64,80,96,112,128,160,192,224,256,320,384};
static const long table_BitRate_V1L3[15]	= {0,32,40,48,56,64,80,96,112,128,160,192,224,256,320};
static const long table_BitRate_V2L1[15]	= {0,32,48,56,64,80,96,112,128,144,160,176,192,224,256};
static const long table_BitRate_V2L2L3[15]	= {0,8,16,24,32,40,48,56,64,80,96,112,128,144,160};
static const long *table_BitRate_V1[4] = {0, table_BitRate_V1L3, table_BitRate_V1L2, table_BitRate_V1L1};
static const long *table_BitRate_V2[4] = {0, table_BitRate_V2L2L3, table_BitRate_V2L2L3, table_BitRate_V2L1};
static const long **table_BitRate[4] = {table_BitRate_V2, 0, table_BitRate_V2, table_BitRate_V1};


static const long table_SampleRate_V1[3]	= {44100,48000,32000};
static const long table_SampleRate_V2[3]	= {22050,24000,16000};
static const long table_SampleRate_V25[3]	= {11025,12000,8000};
static const long *table_SampleRate[4] = {table_SampleRate_V25, 0, table_SampleRate_V2, table_SampleRate_V1};

static const INT32U table_MpegVersion[4]	= {25,0,2,1};

#define EQUAL_MASK	0xFFFE0CC0
#define LAYER_III	1
#define LAYER_II	2
#define LAYER_I		3

#define MPEG_V25	0
#define MPEG_V2		2
#define MPEG_V1		3

typedef struct
{
	INT8U emphasis		:	 2;
	INT8U original		:	 1;
	INT8U copyright		:	 1;
	INT8U ext			:	 2;
	INT8U channel		:    2;
	INT8U priv			:	 1;
	INT8U padding		:	 1;
	INT8U samplerate	:    2;
	INT8U bitrate		:	 4;
	INT8U protection	:	 1;
	INT8U layer			:    2;
	INT8U mpeg			:    2;
	INT16U sync			:    11;
}MP3_SYNC_WORD;

typedef union//struct
{
	MP3_SYNC_WORD sync_word;
	INT32U  word;
	INT8U   ch[4];
} MP3_SYNC_UNION;


代码主体:
说明:函数有两个输入参数,一个是MP3的文件句柄;一个是数据帧在文件中的偏移位置,即ID3V2之后的位置;

#define     CHECK_BUF_SIZE          2048		//ring buf size
#define     VBR_STATISTICS_NUM      100         //if the bitrate of front 3 frame is not same,statistics next (100-3) frame and get average bitrate
#define     STATISTICS_BUF_SIZE     2048*10     //the bigger size of read file each time
//function name  :  audio_mp3_VBR_check_again
//input parameter:  fin             the mp3 file handle
//                  ID3V2_length    the len of mp3 ID3V2
//return value   :  0               CBR
//                  >0              the average bitrate of front 100 frame
//                  -1              error
static INT32U audio_mp3_VBR_check_again(INT16S fin,INT32U ID3V2_length)
{
    INT32U  total_frame=0;
    INT32U  bitrate_average = 0;
    INT32U  buf=0;
    INT32U  buf_size=0;
    INT32U  position=0;
    INT8U   ret=0;
    INT8U   check_num = 3;
    INT32U  VBR_flag = 0;
    INT32S  real_read = 0;
    INT32U  offset;
    INT8U   i;

    MP3_SYNC_UNION  mp3_file_head[2];
	INT8U   *p_audio_file_head;
	INT32U  BitRate;
	INT32U  BitRate2[check_num];
	INT32U  SampleRate;
	INT32U  Padding;
	INT32U  FrameLengthInBytes=0;

    buf_size = CHECK_BUF_SIZE;
    buf = (INT32U)gp_malloc_align(buf_size,4);
    if(!buf)
    {
        gp_free((void*)buf);
        return -1;
    }

    p_audio_file_head = (INT8U*)buf;

    mp3_file_head[0].word = 0;
    position = 0;//effect : Position to each frame header position in file and buf
    //Parse the header of 3 frames,whether the bitrate of each frame is same
    while(check_num--)
    {
        p_audio_file_head = (INT8U*)buf;
        lseek(fin, ID3V2_length+position, SEEK_SET);
        buf_size = CHECK_BUF_SIZE;
        real_read = fs_read(fin,buf,buf_size);
        mp3_file_head[0].ch[0] = *(p_audio_file_head);
        //此处先统计前3帧数据的bitrate
        while (real_read > 4)
        {
			//while循环就是根据以上表格编写的,主要是解析前每帧的前4个Bytes
            if(	mp3_file_head[0].sync_word.sync		    == 0x07FF	&&
                    mp3_file_head[0].sync_word.mpeg		!= 1		&&
                    mp3_file_head[0].sync_word.layer	!= 0		&&
                    mp3_file_head[0].sync_word.layer	!= 3		&&
                    mp3_file_head[0].sync_word.bitrate	!= 0		&&
                    mp3_file_head[0].sync_word.bitrate	!= 15		&&
                    mp3_file_head[0].sync_word.samplerate	!= 3)
            {
				BitRate = table_BitRate[mp3_file_head[0].sync_word.mpeg][mp3_file_head[0].sync_word.layer][mp3_file_head[0].sync_word.bitrate] * 1000;
				SampleRate = table_SampleRate[mp3_file_head[0].sync_word.mpeg][mp3_file_head[0].sync_word.samplerate];
				Padding = mp3_file_head[0].sync_word.padding;
				if(mp3_file_head[0].sync_word.mpeg == MPEG_V1)
					FrameLengthInBytes = 144 * BitRate / SampleRate + Padding; // 144 = 1152/8
				else
					FrameLengthInBytes = 72 * BitRate / SampleRate + Padding;

                //根据计算得到的帧长度FrameLengthInBytes,计算本帧长度,之后偏移到下一帧帧头
                position = position + FrameLengthInBytes - 4;
                BitRate2[check_num] = BitRate;
                mp3_file_head[0].word = 0;
                bitrate_average += BitRate;
                total_frame++;
                break;
            }
            p_audio_file_head ++;
            position ++;
            real_read -- ;
            mp3_file_head[0].word <<= 8;
            mp3_file_head[0].ch[0] = *(p_audio_file_head);
        }
    }
    //Whether the bitrate of each frame is same
    if(BitRate2[0]==BitRate2[1] && BitRate2[1]==BitRate2[2])
        VBR_flag = 0;
    else
        VBR_flag = 1;
    //If it's in VBR format, parse each frame to get the total number of frames
    if(VBR_flag)
    {
        gp_free((void*)buf);
        buf_size = STATISTICS_BUF_SIZE;
        buf = (INT32U)gp_malloc_align(buf_size,4);
        p_audio_file_head = (INT8U*)buf;
        lseek(fin, ID3V2_length+position, SEEK_SET);
        real_read = fs_read(fin,buf,buf_size);
        mp3_file_head[0].ch[0] = *(p_audio_file_head);
        ret = 1;
        //开始统计前100帧的bitrate
        //此处我写也两个while(),外循环是我备用做其他功能的,统计数据看内循环while
        while(ret)
        {
            while (real_read > 4)
            {

                if(	mp3_file_head[0].sync_word.sync		    == 0x07FF	&&
                    mp3_file_head[0].sync_word.mpeg		!= 1		&&
                    mp3_file_head[0].sync_word.layer	!= 0		&&
                    mp3_file_head[0].sync_word.layer	!= 3		&&
                    mp3_file_head[0].sync_word.bitrate	!= 0		&&
                    mp3_file_head[0].sync_word.bitrate	!= 15		&&
                    mp3_file_head[0].sync_word.samplerate	!= 3)
                {
                    BitRate = table_BitRate[mp3_file_head[0].sync_word.mpeg][mp3_file_head[0].sync_word.layer][mp3_file_head[0].sync_word.bitrate] * 1000;
                    SampleRate = table_SampleRate[mp3_file_head[0].sync_word.mpeg][mp3_file_head[0].sync_word.samplerate];
                    Padding = mp3_file_head[0].sync_word.padding;
                    if(mp3_file_head[0].sync_word.mpeg == MPEG_V1)
                        FrameLengthInBytes = 144 * BitRate / SampleRate + Padding; // 144 = 1152/8
                    else
                        FrameLengthInBytes = 72 * BitRate / SampleRate + Padding;

                    position = position + FrameLengthInBytes - 4;
                    real_read = real_read - FrameLengthInBytes + 4;
                    p_audio_file_head = p_audio_file_head + FrameLengthInBytes - 4;
                    bitrate_average += BitRate;
                    mp3_file_head[0].word = 0;
                    total_frame++;
                    if(total_frame>=100)	//将此处if注释掉的话,就可以遍历所有数据帧
                        break;
                }

                if(real_read < 5)      //Read the file, and Protect the last four bytes of buf
                {
                    offset = 0;
                    buf_size = STATISTICS_BUF_SIZE;
                    p_audio_file_head = (INT8U*)buf;
                    if(real_read > 0)   //Protect the last four bytes of buf
                    {
                        offset = real_read;
                        buf_size = STATISTICS_BUF_SIZE - real_read;
                        for(i=0;i<offset;i++)     //deal the last four bytes
                        {
                            *(p_audio_file_head+i) = *(p_audio_file_head+STATISTICS_BUF_SIZE-offset+i);
                        }
                    }
                    lseek(fin, ID3V2_length+position, SEEK_SET);
                    real_read = fs_read(fin,buf+offset,buf_size);
                    if(real_read < 1024)
                        break;
                }

                p_audio_file_head ++;
                real_read --;
                position ++;
                mp3_file_head[0].word <<= 8;
                mp3_file_head[0].ch[0] = *(p_audio_file_head);
            }
            ret = 0;
        }
    }
    else    //CBR
    {
        total_frame = 0;
        bitrate_average = 0;
    }

    lseek(fin, ID3V2_length+RING_BUF_SIZE/2, SEEK_SET);
    gp_free((void*)buf);

    if(total_frame==0)
        return -1;

    return bitrate_average/total_frame;
}

不知道你是否有耐心把上面的代码研究看懂,看不懂我也没办法了哈!!!
其实就是不断的解析数据帧的前4个bytes,得到bitrate和帧长之后,就往后偏移帧长,之后解析下一帧帧头,一直到整个文件读完,或者读够100帧;

以上代码,做适当修改,就可以统计MP3文件的总帧数,也可统计整个文件的bitrate之和,就可以按CBR或VBR的方式计算总时间了;
修改如下:
1.将第二次统计中的 if(total_frame>=100) 判断注释掉,即可统计所有数据帧,得到总帧数;
2.在第一次判断前三帧Bitrate是否相同时,如果想多判断几帧,修改check_num的值就可以了;

看完代码之后会不会豁然开朗呢?网上的MP3解析各式各样,没有代码看解析,都是懵懵懂懂,结合解析看代码,是不是发现MP3的数据结构就清楚?
编写代码不易,请给个赞!!!
————————————————
版权声明:本文为CSDN博主「水心123」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_37981492/article/details/103380722

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值