- struct mad_decoder {
- enum mad_decoder_mode mode;
-
- int options;
-
- struct {
- long pid;
- int in;
- int out;
- } async;
-
- struct {
- struct mad_stream stream;
- struct mad_frame frame;
- struct mad_synth synth;
- } *sync;
-
- void *cb_data;
-
- enum mad_flow (*input_func)(void *, struct mad_stream *);
- enum mad_flow (*header_func)(void *, struct mad_header const *);
- enum mad_flow (*filter_func)(void *,
- struct mad_stream const *, struct mad_frame *);
- enum mad_flow (*output_func)(void *,
- struct mad_header const *, struct mad_pcm *);
- enum mad_flow (*error_func)(void *, struct mad_stream *, struct mad_frame *);
- enum mad_flow (*message_func)(void *, void *, unsigned int *);
- };
- struct mad_stream {
- unsigned char const *buffer; /* input bitstream buffer */
- unsigned char const *bufend; /* end of buffer */
- unsigned long skiplen; /* bytes to skip before next frame */
-
- int sync; /* stream sync found */
- unsigned long freerate; /* free bitrate (fixed) */
-
- unsigned char const *this_frame; /* start of current frame */
- unsigned char const *next_frame; /* start of next frame */
- struct mad_bitptr ptr; /* current processing bit pointer */
-
- struct mad_bitptr anc_ptr; /* ancillary bits pointer */
- unsigned int anc_bitlen; /* number of ancillary bits */
-
- unsigned char (*main_data)[MAD_BUFFER_MDLEN];
- /* Layer III main_data() */
- unsigned int md_len; /* bytes in main_data */
-
- int options; /* decoding options (see below) */
- enum mad_error error; /* error code (see above) */
- };
三、MP3解码流程简介
- /*
- * NAME: decoder->init()
- * DESCRIPTION: initialize a decoder object with callback routines
- */
- void mad_decoder_init(struct mad_decoder *decoder, void *data,
- enum mad_flow (*input_func)(void *,
- struct mad_stream *),
- enum mad_flow (*header_func)(void *,
- struct mad_header const *),
- enum mad_flow (*filter_func)(void *,
- struct mad_stream const *,
- struct mad_frame *),
- enum mad_flow (*output_func)(void *,
- struct mad_header const *,
- struct mad_pcm *),
- enum mad_flow (*error_func)(void *,
- struct mad_stream *,
- struct mad_frame *),
- enum mad_flow (*message_func)(void *,
- void *, unsigned int *))
- {
- decoder->mode = -1;
-
- decoder->options = 0;
-
- decoder->async.pid = 0;
- decoder->async.in = -1;
- decoder->async.out = -1;
-
- decoder->sync = 0;
-
- decoder->cb_data = data;
-
- decoder->input_func = input_func;
- decoder->header_func = header_func;
- decoder->filter_func = filter_func;
- decoder->output_func = output_func;
- decoder->error_func = error_func;
- decoder->message_func = message_func;
- }
- mad_decoder_init(&decoder, &buffer,
- input, 0 /* header */, 0 /* filter */, output,
- error, 0 /* message */);
第六个参数,output_func函数,这个是用来将解码之后的数据写入输出缓冲区或者音频设备节点的;
2、调用mad_decoder_run(&decoder, MAD_DECODER_MODE_SYNC)函数启动解码,查看Libmad库源码可知,这个函数里面会注册一个函数指针
- /*
- * NAME: decoder->run()
- * DESCRIPTION: run the decoder thread either synchronously or asynchronously
- */
- int mad_decoder_run(struct mad_decoder *decoder, enum mad_decoder_mode mode)
- {
- int result;
- int (*run)(struct mad_decoder *) = 0;
-
- switch (decoder->mode = mode) {
- case MAD_DECODER_MODE_SYNC:
- run = run_sync;
- break;
-
- case MAD_DECODER_MODE_ASYNC:
- # if defined(USE_ASYNC)
- run = run_async;
- # endif
- break;
- }
-
- if (run == 0)
- return -1;
-
- decoder->sync = malloc(sizeof(*decoder->sync));
- if (decoder->sync == 0)
- return -1;
-
- result = run(decoder);
-
- free(decoder->sync);
- decoder->sync = 0;
-
- return result;
- }
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <unistd.h>
- #include <sys/stat.h>
- #include <sys/mman.h>
- #include <fcntl.h>
- #include <sys/types.h>
- #include <sys/ioctl.h>
- #include <sys/soundcard.h>
- #include "mad.h"
-
- #define BUFSIZE 8192
-
- /*
- * This is a private message structure. A generic pointer to this structure
- * is passed to each of the callback functions. Put here any data you need
- * to access from within the callbacks.
- */
- struct buffer {
- FILE *fp; /*file pointer*/
- unsigned int flen; /*file length*/
- unsigned int fpos; /*current position*/
- unsigned char fbuf[BUFSIZE]; /*buffer*/
- unsigned int fbsize; /*indeed size of buffer*/
- };
- typedef struct buffer mp3_file;
-
- int soundfd; /*soundcard file*/
- unsigned int prerate = 0; /*the pre simple rate*/
-
- int writedsp(int c)
- {
- return write(soundfd, (char *)&c, 1);
- }
-
- void set_dsp()
- {
- #if 0
- int format = AFMT_S16_LE;
- int channels = 2;
- int rate = 44100;
-
- soundfd = open("/dev/dsp", O_WRONLY);
- ioctl(soundfd, SNDCTL_DSP_SPEED,&rate);
- ioctl(soundfd, SNDCTL_DSP_SETFMT, &format);
- ioctl(soundfd, SNDCTL_DSP_CHANNELS, &channels);
- #else
- if((soundfd = open("test.bin" , O_WRONLY | O_CREAT)) < 0)
- {
- fprintf(stderr , "can't open sound device!\n");
- exit(-1);
- }
- #endif
- }
-
- /*
- * This is perhaps the simplest example use of the MAD high-level API.
- * Standard input is mapped into memory via mmap(), then the high-level API
- * is invoked with three callbacks: input, output, and error. The output
- * callback converts MAD's high-resolution PCM samples to 16 bits, then
- * writes them to standard output in little-endian, stereo-interleaved
- * format.
- */
-
- static int decode(mp3_file *mp3fp);
-
- int main(int argc, char *argv[])
- {
- long flen, fsta, fend;
- int dlen;
- mp3_file *mp3fp;
-
- if (argc != 2)
- return 1;
-
- mp3fp = (mp3_file *)malloc(sizeof(mp3_file));
- if((mp3fp->fp = fopen(argv[1], "r")) == NULL)
- {
- printf("can't open source file.\n");
- return 2;
- }
- fsta = ftell(mp3fp->fp);
- fseek(mp3fp->fp, 0, SEEK_END);
- fend = ftell(mp3fp->fp);
- flen = fend - fsta;
- if(flen > 0)
- fseek(mp3fp->fp, 0, SEEK_SET);
- fread(mp3fp->fbuf, 1, BUFSIZE, mp3fp->fp);
- mp3fp->fbsize = BUFSIZE;
- mp3fp->fpos = BUFSIZE;
- mp3fp->flen = flen;
-
- set_dsp();
-
- decode(mp3fp);
-
- close(soundfd);
- fclose(mp3fp->fp);
-
- return 0;
- }
-
- static enum mad_flow input(void *data, struct mad_stream *stream)
- {
- mp3_file *mp3fp;
- int ret_code;
- int unproc_data_size; /*the unprocessed data's size*/
- int copy_size;
-
- mp3fp = (mp3_file *)data;
- if(mp3fp->fpos < mp3fp->flen) {
- unproc_data_size = stream->bufend - stream->next_frame;
- //printf("%d, %d, %d\n", unproc_data_size, mp3fp->fpos, mp3fp->fbsize);
- memcpy(mp3fp->fbuf, mp3fp->fbuf + mp3fp->fbsize - unproc_data_size, unproc_data_size);
- copy_size = BUFSIZE - unproc_data_size;
- if(mp3fp->fpos + copy_size > mp3fp->flen) {
- copy_size = mp3fp->flen - mp3fp->fpos;
- }
- fread(mp3fp->fbuf+unproc_data_size, 1, copy_size, mp3fp->fp);
- mp3fp->fbsize = unproc_data_size + copy_size;
- mp3fp->fpos += copy_size;
-
- /*Hand off the buffer to the mp3 input stream*/
- mad_stream_buffer(stream, mp3fp->fbuf, mp3fp->fbsize);
- ret_code = MAD_FLOW_CONTINUE;
- } else {
- ret_code = MAD_FLOW_STOP;
- }
-
- return ret_code;
-
- }
-
- /*
- * The following utility routine performs simple rounding, clipping, and
- * scaling of MAD's high-resolution samples down to 16 bits. It does not
- * perform any dithering or noise shaping, which would be recommended to
- * obtain any exceptional audio quality. It is therefore not recommended to
- * use this routine if high-quality output is desired.
- */
-
- static inline signed int scale(mad_fixed_t sample)
- {
- /* round */
- sample += (1L << (MAD_F_FRACBITS - 16));
-
- /* clip */
- if (sample >= MAD_F_ONE)
- sample = MAD_F_ONE - 1;
- else if (sample < -MAD_F_ONE)
- sample = -MAD_F_ONE;
-
- /* quantize */
- return sample >> (MAD_F_FRACBITS + 1 - 16);
- }
-
- /*
- * This is the output callback function. It is called after each frame of
- * MPEG audio data has been completely decoded. The purpose of this callback
- * is to output (or play) the decoded PCM audio.
- */
-
- //输出函数做相应的修改,目的是解决播放音乐时声音卡的问题。
- static enum mad_flow output(void *data, struct mad_header const *header,
- struct mad_pcm *pcm)
- {
- unsigned int nchannels, nsamples;
- mad_fixed_t const *left_ch, *right_ch;
- // pcm->samplerate contains the sampling frequency
- nchannels = pcm->channels;
- nsamples = pcm->length;
- left_ch = pcm->samples[0];
- right_ch = pcm->samples[1];
- short buf[nsamples *2];
- int i = 0;
- //printf(">>%d\n", nsamples);
- while (nsamples--) {
- signed int sample;
- // output sample(s) in 16-bit signed little-endian PCM
- sample = scale(*left_ch++);
- buf[i++] = sample & 0xFFFF;
- if (nchannels == 2) {
- sample = scale(*right_ch++);
- buf[i++] = sample & 0xFFFF;
- }
- }
- //fprintf(stderr, ".");
- write(soundfd, &buf[0], i * 2);
- return MAD_FLOW_CONTINUE;
- }
-
- /*
- * This is the error callback function. It is called whenever a decoding
- * error occurs. The error is indicated by stream->error; the list of
- * possible MAD_ERROR_* errors can be found in the mad.h (or stream.h)
- * header file.
- */
-
- static enum mad_flow error(void *data,
- struct mad_stream *stream,
- struct mad_frame *frame)
- {
- mp3_file *mp3fp = data;
-
- fprintf(stderr, "decoding error 0x%04x (%s) at byte offset %u\n",
- stream->error, mad_stream_errorstr(stream),
- stream->this_frame - mp3fp->fbuf);
-
- /* return MAD_FLOW_BREAK here to stop decoding (and propagate an error) */
-
- return MAD_FLOW_CONTINUE;
- }
-
- /*
- * This is the function called by main() above to perform all the decoding.
- * It instantiates a decoder object and configures it with the input,
- * output, and error callback functions above. A single call to
- * mad_decoder_run() continues until a callback function returns
- * MAD_FLOW_STOP (to stop decoding) or MAD_FLOW_BREAK (to stop decoding and
- * signal an error).
- */
-
- static int decode(mp3_file *mp3fp)
- {
- struct mad_decoder decoder;
- int result;
-
- /* configure input, output, and error functions */
- mad_decoder_init(&decoder, mp3fp,
- input, 0 /* header */, 0 /* filter */, output,
- error, 0 /* message */);
-
- /* start decoding */
- result = mad_decoder_run(&decoder, MAD_DECODER_MODE_SYNC);
-
- /* release the decoder */
- mad_decoder_finish(&decoder);
-
- return result;
- }
说明:1、实例原本是基于音频OSS框架的,当然,在嵌入式领域,ALSA也是兼容OSS接口的;
- static enum mad_flow input(void *data, struct mad_stream *stream)
- {
- mp3_file *mp3fp;
- int ret_code;
- int unproc_data_size; /*the unprocessed data's size*/
- int copy_size;
-
- mp3fp = (mp3_file *)data;
- if(mp3fp->fpos < mp3fp->flen) {
- unproc_data_size = stream->bufend - stream->next_frame;
- //printf("%d, %d, %d\n", unproc_data_size, mp3fp->fpos, mp3fp->fbsize);
- memcpy(mp3fp->fbuf, mp3fp->fbuf + mp3fp->fbsize - unproc_data_size, unproc_data_size);
- copy_size = BUFSIZE - unproc_data_size;
- if(mp3fp->fpos + copy_size > mp3fp->flen) {
- copy_size = mp3fp->flen - mp3fp->fpos;
- }
- fread(mp3fp->fbuf+unproc_data_size, 1, copy_size, mp3fp->fp);
- mp3fp->fbsize = unproc_data_size + copy_size;
- mp3fp->fpos += copy_size;
-
- /*Hand off the buffer to the mp3 input stream*/
- mad_stream_buffer(stream, mp3fp->fbuf, mp3fp->fbsize);
- ret_code = MAD_FLOW_CONTINUE;
- } else {
- ret_code = MAD_FLOW_STOP;
- }
-
- return ret_code;
-
- }
我们设置的输入buff缓冲区的大小是8192字节,但是对于mp3文件来讲,不一定这8192个字节就刚好是若干个完整的帧,有可能会有若干字节是输入下一个帧的,所有要根据struct mad_stream中的两个指针,标示了缓冲区中的完整帧的起始地址:
- unsigned char const *this_frame; /* start of current frame */
- unsigned char const *next_frame; /* start of next frame */
- unproc_data_size = stream->bufend - stream->next_frame;
得到剩余的下一个帧的数据,并且需要将其从buff数组的尾部拷贝到头部,再从mp3文件中读取一部分字节拼凑成下一个8192字节,提交给库去解码,如此周而复始。
4、此代码解码出来的pcm可以加上44字节的wav头文件,则可以用播放器正常播放。
五、如何从网络socket获取相应数据,边解码边播放
由于我的项目是要实现一个远程播放器的功能,即手机端的mp3源文件通过wifi传输到开发板上解码播放,所以,对于输入缓冲区的控制就不像操作文件那个,可以通过file结构体精确控制好读取的数据位置了,为此,做了些许修改。
可以开两个线程,一个线程用于接收socket数据,一个用于解码播放。主要是缓冲区的控制,可以如此实现:将接收buff[]大小设置为8192*10字节,然后,解码input函数里面的buff[]的大小设置为8192*11字节,也就是说,多余了8192用来缓冲多余的下一帧字节的数据(因为mp3文件的帧不会超过8192字节),那么,区别于上面的思路,我们可以固定的让socket的buff[]接收8192*10字节的数据,如果解码的buff[]里面初次解码后有剩余的数据,仍然将其复制到解码buff[]的头部,只是这时候还是将socket的buff[]的8192*10字节的数据加到解码buff[]的刚刚拷贝的数据后面,所以,这里调用mad_stream_buffer(stream, buf, bsize)中的bsize就是8192*10+剩余的帧数据大小了。
相关参考:
1、作者:cqulpj 网址: http://cqulpj.blogbus.com/logs/68406670.html