之所以要加上引号的原因是这个函数从名字上看是用于处理MP3帧头的,实际情况是真正的帧头解码是在mad_header_decode函数中进行的,mad_header_decode函数负责填充struct mad_header结构:
//file:libmad-1.05b/frame.c;line:364~375 /* begin processing */ stream->this_frame = ptr; stream->next_frame = ptr + 1; /* possibly bogus sync word */ mad_bit_init(&stream->ptr, stream->this_frame); if (decode_header(header, stream) == -1) goto fail; /* calculate frame duration */ mad_timer_set(&header->duration,0,32 * MAD_NSBSAMPLES(header), header->samplerate);
decode_header函数内部是大堆的if…else,将MP3头部的数据位表示的含义填充进header结构,而mad_timer_set函数精确地计算帧的播放持续时间(duration),这点对统计MP3文件的总播放时间很有帮助,因为不论MP3文件是恒定编码率(CBR)还是可变编码率(VBR),如果逐帧计算duration的话会精确得多。
在前面的关键数据结构的章节里我们介绍过libmad帧头结构struct mad_header,下面再回过头来看看以该结构作为参数的回调函数header(void* , struct mad_header const *)可以做什么:
//file: libmad-1.05b/frame.h;line:49~65 struct mad_header { enum mad_layer layer; /* audio layer (1, 2, or 3) */ enum mad_mode mode; /* channel mode (see above) */ int mode_extension; /* additional mode info */ enum mad_emphasis emphasis; /* de-emphasis to use (see above) */ unsigned long bitrate; /* stream bitrate (bps) */ unsigned int samplerate; /* sampling frequency (Hz) */ unsigned short crc_check; /* frame CRC accumulator */ unsigned short crc_target; /* final target CRC checksum */ int flags; /* flags (see below) */ int private_bits; /* private bits (see below) */ mad_timer_t duration; /* audio playing time of frame */ };
重写该函数并初始化到decoder结构后,我们可能做的事情有:
1、 获得音频压缩标准的层信息(layerI、II、III)
2、 获得音频文件的声道信息(单声道、立体声等)
3、 获得频文件的播放码率(bps)
4、 获得音频文件的波特率(Hz,典型的是44.1KHz)
5、 获得该帧的CRC校验计算值(如果启用了CRC的话)
6、 获得该帧的CRC校验目标值(如果有的话)
7、 最重要的是,获得该帧的播放时常!
以上的数据可以获得但不能修改,原因很简单,指针以const形式进行传递,还记得const * 与*const、const * const的区别吗?
header()回调函数如果存在(这个函数不是必须的,可以被初始化成0或NULL)那么它是紧随着input()回调函数之后被调用的,如果header()回调函数不存在,说明用户对header信息不感兴趣,那么连mad_header_decode()函数都不会被调用:
//file:libmad-1.05b/decoder.c;line:346~402 mad_stream_options(stream, decoder->options); do { switch (decoder->input_func(decoder->cb_data, stream)) { case MAD_FLOW_STOP: goto done; case MAD_FLOW_BREAK: goto fail; case MAD_FLOW_IGNORE: continue; case MAD_FLOW_CONTINUE: break; } 。。。 。。。 if (decoder->header_func) { if (mad_header_decode(&frame->header, stream) == -1) { if (!MAD_RECOVERABLE(stream->error)) break; switch (error_func(error_data, stream, frame)) { case MAD_FLOW_STOP: goto done; case MAD_FLOW_BREAK: goto fail; case MAD_FLOW_IGNORE: case MAD_FLOW_CONTINUE: default: continue; } } switch (decoder->header_func(decoder->cb_data, &frame->header)) { case MAD_FLOW_STOP: goto done; case MAD_FLOW_BREAK: goto fail; case MAD_FLOW_IGNORE: continue; case MAD_FLOW_CONTINUE: break; } }