Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1147237
  • 博文数量: 646
  • 博客积分: 288
  • 博客等级: 二等列兵
  • 技术积分: 5375
  • 用 户 组: 普通用户
  • 注册时间: 2010-07-08 14:33
个人简介

为了技术,我不会停下学习的脚步,我相信我还能走二十年。

文章分类

全部博文(646)

文章存档

2014年(8)

2013年(134)

2012年(504)

分类:

2012-07-01 19:50:27

原文地址:基于libmad库的MP3解码简析 作者:JGFNTU

     MAD (libmad)是一个开源的高精度 MPEG 音频解码库,支持 MPEG-1(Layer I, Layer II 和 LayerIII(也就是 MP3)。LIBMAD 提供 24-bit 的 PCM 输出,完全是定点计算,非常适合没有浮点支持的平台上使用。使用 libmad 提供的一系列 API,就可以非常简单地实现 MP3 数据解码工作。在 libmad 的源代码文件目录下的 mad.h 文件中,可以看到绝大部分该库的数据结构和 API 等。
     网上有很多关于libmad的使用实例,在他们的基础上,我稍加总结、整理和衍生,文末给出相关参考链接,表示感谢!
 
     一、libmad库源码
 
     可以去相关网站下载,给出链接:
    
可以根据不同的平台自行编译或者移植,略述。
 
     二、相关数据结构及函数接口简介
 
     1、struct mad_decode
  1. struct mad_decoder {
  2.   enum mad_decoder_mode mode;

  3.   int options;

  4.   struct {
  5.     long pid;
  6.     int in;
  7.     int out;
  8.   } async;

  9.   struct {
  10.     struct mad_stream stream;
  11.     struct mad_frame frame;
  12.     struct mad_synth synth;
  13.   } *sync;

  14.   void *cb_data;

  15.   enum mad_flow (*input_func)(void *, struct mad_stream *);
  16.   enum mad_flow (*header_func)(void *, struct mad_header const *);
  17.   enum mad_flow (*filter_func)(void *,
  18.              struct mad_stream const *, struct mad_frame *);
  19.   enum mad_flow (*output_func)(void *,
  20.              struct mad_header const *, struct mad_pcm *);
  21.   enum mad_flow (*error_func)(void *, struct mad_stream *, struct mad_frame *);
  22.   enum mad_flow (*message_func)(void *, void *, unsigned int *);
  23. };
     2、struct mad_stream

  1. struct mad_stream {
  2.   unsigned char const *buffer;        /* input bitstream buffer */
  3.   unsigned char const *bufend;        /* end of buffer */
  4.   unsigned long skiplen;              /* bytes to skip before next frame */

  5.   int sync;                           /* stream sync found */
  6.   unsigned long freerate;             /* free bitrate (fixed) */

  7.   unsigned char const *this_frame;    /* start of current frame */
  8.   unsigned char const *next_frame;    /* start of next frame */
  9.   struct mad_bitptr ptr;              /* current processing bit pointer */

  10.   struct mad_bitptr anc_ptr;          /* ancillary bits pointer */
  11.   unsigned int anc_bitlen;            /* number of ancillary bits */

  12.   unsigned char (*main_data)[MAD_BUFFER_MDLEN];
  13.                                       /* Layer III main_data() */
  14.   unsigned int md_len;                /* bytes in main_data */

  15.   int options;                        /* decoding options (see below) */
  16.   enum mad_error error;               /* error code (see above) */
  17. };

     三、MP3解码流程简介
 
     MP3解码有同步方式和异步方式两种,libmad是以桢为单位对MP3进行解码的,所谓同步方式是指解码函数在解码完一帧后才返回并带回出错信息,异步方式是指解码函数在调用后立即返回,通过消息传递解码状态信息。
     1、首先创建一个解码器 struct mad_decoder decoder,紧接着调用函数  mad_decoder_init(...)函数,给出这个函数的原型及定义

  1. /*
  2.  * NAME:    decoder->init()
  3.  * DESCRIPTION:    initialize a decoder object with callback routines
  4.  */
  5. void mad_decoder_init(struct mad_decoder *decoder, void *data,
  6.          enum mad_flow (*input_func)(void *,
  7.                          struct mad_stream *),
  8.          enum mad_flow (*header_func)(void *,
  9.                          struct mad_header const *),
  10.          enum mad_flow (*filter_func)(void *,
  11.                          struct mad_stream const *,
  12.                          struct mad_frame *),
  13.          enum mad_flow (*output_func)(void *,
  14.                          struct mad_header const *,
  15.                          struct mad_pcm *),
  16.          enum mad_flow (*error_func)(void *,
  17.                          struct mad_stream *,
  18.                          struct mad_frame *),
  19.          enum mad_flow (*message_func)(void *,
  20.                          void *, unsigned int *))
  21. {
  22.   decoder->mode = -1;

  23.   decoder->options = 0;

  24.   decoder->async.pid = 0;
  25.   decoder->async.in = -1;
  26.   decoder->async.out = -1;

  27.   decoder->sync = 0;

  28.   decoder->cb_data = data;

  29.   decoder->input_func = input_func;
  30.   decoder->header_func = header_func;
  31.   decoder->filter_func = filter_func;
  32.   decoder->output_func = output_func;
  33.   decoder->error_func = error_func;
  34.   decoder->message_func = message_func;
  35. }
     用户编程可以用如下方式调用,可以看到从第三个参数开始,其实都是一些列的函数指针,这里初始化的目的其实是给创建的decoder注册下面即将要自己实现的这些函数。Libmad库会在解码过程中回调这些函数:

  1. mad_decoder_init(&decoder, &buffer,
  2.          input, 0 /* header */, 0 /* filter */, output,
  3.          error, 0 /* message */);
     第一个参数,就是定义的解码器decoder;
     第二个参数,是一个void型的函数指针,这里也就是给你的用户空间定义私有的数据结构体用的,下面会给出具体的例子来说明其用法;
     第三个参数,input_func函数,这个是用来读取你的mp3资源的函数;
     第四个参数,header_func函数,这个顾名思义是处理mp3头部信息的函数,可以根据需要取舍;
     第五个参数,filter_func函数,也没有深入理解过,可以不必实现;
     第六个参数,output_func函数,这个是用来将解码之后的数据写入输出缓冲区或者音频设备节点的;
     第七个参数,error_func函数,是用来打印返回的解码出错信息的;
     第八个参数,message_func可以不必实现。

     2、调用mad_decoder_run(&decoder, MAD_DECODER_MODE_SYNC)函数启动解码,查看Libmad库源码可知,这个函数里面会注册一个函数指针
  1. /*
  2.  * NAME:    decoder->run()
  3.  * DESCRIPTION:    run the decoder thread either synchronously or asynchronously
  4.  */
  5. int mad_decoder_run(struct mad_decoder *decoder, enum mad_decoder_mode mode)
  6. {
  7.   int result;
  8.   int (*run)(struct mad_decoder *) = 0;

  9.   switch (decoder->mode = mode) {
  10.   case MAD_DECODER_MODE_SYNC:
  11.     run = run_sync; 
  12.     break;

  13.   case MAD_DECODER_MODE_ASYNC:
  14. # if defined(USE_ASYNC)
  15.     run = run_async;
  16. # endif
  17.     break;
  18.   }

  19.   if (run == 0)
  20.     return -1;

  21.   decoder->sync = malloc(sizeof(*decoder->sync));
  22.   if (decoder->sync == 0)
  23.     return -1;

  24.   result = run(decoder);

  25.   free(decoder->sync);
  26.   decoder->sync = 0;

  27.   return result;
  28. }
     而在这个run_sync(struct mad_decoder *decoder)函数中则有一个大的while循环来依次调用
decoder->input_func(decoder->cb_data, stream)获取mp3源文件,然后交由相关库函数解码。
而后会有decoder->output_func(decoder->cb_data,  &frame->header, &synth->pcm)函数来输出解码后的数据。
     3、最后调用mad_decoder_finish(&decoder)结束解码,释放decoder资源。
     4、在input_func函数中,会调用一个很重要的函数
mad_stream_buffer(stream, buffer->start, buffer->length) ,第一个参数指向一个mad_stream变量,mad_stream结构定义在stream.h头文件里,用于记录文件的地址和当前处理的位置。第二、三个参数分别是mp3文件在内存中映像的起始地址和文件长度。mad_stream_buffer()函数将mp3文件与mad_stream结构进行关联。
 
     四、MP3解码编程实例

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <unistd.h>
  5. #include <sys/stat.h>
  6. #include <sys/mman.h>
  7. #include <fcntl.h>
  8. #include <sys/types.h>
  9. #include <sys/ioctl.h>
  10. #include <sys/soundcard.h>
  11. #include "mad.h"

  12. #define BUFSIZE 8192

  13. /*
  14.  * This is a private message structure. A generic pointer to this structure
  15.  * is passed to each of the callback functions. Put here any data you need
  16.  * to access from within the callbacks.
  17.  */
  18. struct buffer {
  19.     FILE *fp; /*file pointer*/
  20.     unsigned int flen; /*file length*/
  21.     unsigned int fpos; /*current position*/
  22.     unsigned char fbuf[BUFSIZE]; /*buffer*/
  23.     unsigned int fbsize; /*indeed size of buffer*/
  24. };
  25. typedef struct buffer mp3_file;

  26. int soundfd; /*soundcard file*/
  27. unsigned int prerate = 0; /*the pre simple rate*/

  28. int writedsp(int c)
  29. {
  30.     return write(soundfd, (char *)&c, 1);
  31. }

  32. void set_dsp()
  33. {
  34. #if 0
  35.     int format = AFMT_S16_LE;
  36.     int channels = 2;
  37.     int rate = 44100;

  38.     soundfd = open("/dev/dsp", O_WRONLY);
  39.     ioctl(soundfd, SNDCTL_DSP_SPEED,&rate);
  40.     ioctl(soundfd, SNDCTL_DSP_SETFMT, &format);
  41.     ioctl(soundfd, SNDCTL_DSP_CHANNELS, &channels);
  42. #else
  43.     if((soundfd = open("test.bin" , O_WRONLY | O_CREAT)) < 0)
  44.     {
  45.         fprintf(stderr , "can't open sound device!\n");
  46.         exit(-1);
  47.     }
  48. #endif
  49. }

  50. /*
  51.  * This is perhaps the simplest example use of the MAD high-level API.
  52.  * Standard input is mapped into memory via mmap(), then the high-level API
  53.  * is invoked with three callbacks: input, output, and error. The output
  54.  * callback converts MAD's high-resolution PCM samples to 16 bits, then
  55.  * writes them to standard output in little-endian, stereo-interleaved
  56.  * format.
  57.  */

  58. static int decode(mp3_file *mp3fp);

  59. int main(int argc, char *argv[])
  60. {
  61.     long flen, fsta, fend;
  62.     int dlen;
  63.     mp3_file *mp3fp;

  64.     if (argc != 2)
  65.         return 1;

  66.     mp3fp = (mp3_file *)malloc(sizeof(mp3_file));
  67.     if((mp3fp->fp = fopen(argv[1], "r")) == NULL)
  68.     {
  69.         printf("can't open source file.\n");
  70.         return 2;
  71.     }
  72.     fsta = ftell(mp3fp->fp);
  73.     fseek(mp3fp->fp, 0, SEEK_END);
  74.     fend = ftell(mp3fp->fp);
  75.     flen = fend - fsta;
  76.     if(flen > 0)
  77.         fseek(mp3fp->fp, 0, SEEK_SET);
  78.     fread(mp3fp->fbuf, 1, BUFSIZE, mp3fp->fp);
  79.     mp3fp->fbsize = BUFSIZE;
  80.     mp3fp->fpos = BUFSIZE;
  81.     mp3fp->flen = flen;

  82.     set_dsp();

  83.     decode(mp3fp);

  84.     close(soundfd);
  85.     fclose(mp3fp->fp);

  86.     return 0;
  87. }

  88. static enum mad_flow input(void *data, struct mad_stream *stream)
  89. {
  90.     mp3_file *mp3fp;
  91.     int ret_code;
  92.     int unproc_data_size; /*the unprocessed data's size*/
  93.     int copy_size;

  94.     mp3fp = (mp3_file *)data;
  95.     if(mp3fp->fpos < mp3fp->flen) {
  96.         unproc_data_size = stream->bufend - stream->next_frame;
  97.         //printf("%d, %d, %d\n", unproc_data_size, mp3fp->fpos, mp3fp->fbsize);
  98.         memcpy(mp3fp->fbuf, mp3fp->fbuf + mp3fp->fbsize - unproc_data_size, unproc_data_size);
  99.         copy_size = BUFSIZE - unproc_data_size;
  100.         if(mp3fp->fpos + copy_size > mp3fp->flen) {
  101.             copy_size = mp3fp->flen - mp3fp->fpos;
  102.         }
  103.         fread(mp3fp->fbuf+unproc_data_size, 1, copy_size, mp3fp->fp);
  104.         mp3fp->fbsize = unproc_data_size + copy_size;
  105.         mp3fp->fpos += copy_size;

  106.         /*Hand off the buffer to the mp3 input stream*/
  107.         mad_stream_buffer(stream, mp3fp->fbuf, mp3fp->fbsize);
  108.         ret_code = MAD_FLOW_CONTINUE;
  109.     } else {
  110.         ret_code = MAD_FLOW_STOP;
  111.     }

  112.     return ret_code;

  113. }

  114. /*
  115.  * The following utility routine performs simple rounding, clipping, and
  116.  * scaling of MAD's high-resolution samples down to 16 bits. It does not
  117.  * perform any dithering or noise shaping, which would be recommended to
  118.  * obtain any exceptional audio quality. It is therefore not recommended to
  119.  * use this routine if high-quality output is desired.
  120.  */

  121. static inline signed int scale(mad_fixed_t sample)
  122. {
  123.     /* round */
  124.     sample += (1L << (MAD_F_FRACBITS - 16));

  125.     /* clip */
  126.     if (sample >= MAD_F_ONE)
  127.         sample = MAD_F_ONE - 1;
  128.     else if (sample < -MAD_F_ONE)
  129.         sample = -MAD_F_ONE;

  130.     /* quantize */
  131.     return sample >> (MAD_F_FRACBITS + 1 - 16);
  132. }

  133. /*
  134.  * This is the output callback function. It is called after each frame of
  135.  * MPEG audio data has been completely decoded. The purpose of this callback
  136.  * is to output (or play) the decoded PCM audio.
  137.  */

  138. //输出函数做相应的修改,目的是解决播放音乐时声音卡的问题。
  139. static enum mad_flow output(void *data, struct mad_header const *header,
  140.         struct mad_pcm *pcm)
  141. {
  142.     unsigned int nchannels, nsamples;
  143.     mad_fixed_t const *left_ch, *right_ch;
  144.     // pcm->samplerate contains the sampling frequency
  145.     nchannels = pcm->channels;
  146.     nsamples = pcm->length;
  147.     left_ch = pcm->samples[0];
  148.     right_ch = pcm->samples[1];
  149.     short buf[nsamples *2];
  150.     int i = 0;
  151.     //printf(">>%d\n", nsamples);
  152.     while (nsamples--) {
  153.         signed int sample;
  154.         // output sample(s) in 16-bit signed little-endian PCM
  155.         sample = scale(*left_ch++);
  156.         buf[i++] = sample & 0xFFFF;
  157.         if (nchannels == 2) {
  158.             sample = scale(*right_ch++);
  159.             buf[i++] = sample & 0xFFFF;
  160.         }
  161.     }
  162.     //fprintf(stderr, ".");
  163.     write(soundfd, &buf[0], i * 2);
  164.     return MAD_FLOW_CONTINUE;
  165. }

  166. /*
  167.  * This is the error callback function. It is called whenever a decoding
  168.  * error occurs. The error is indicated by stream->error; the list of
  169.  * possible MAD_ERROR_* errors can be found in the mad.h (or stream.h)
  170.  * header file.
  171.  */

  172. static enum mad_flow error(void *data,
  173.         struct mad_stream *stream,
  174.         struct mad_frame *frame)
  175. {
  176.     mp3_file *mp3fp = data;

  177.     fprintf(stderr, "decoding error 0x%04x (%s) at byte offset %u\n",
  178.             stream->error, mad_stream_errorstr(stream),
  179.             stream->this_frame - mp3fp->fbuf);

  180.     /* return MAD_FLOW_BREAK here to stop decoding (and propagate an error) */

  181.     return MAD_FLOW_CONTINUE;
  182. }

  183. /*
  184.  * This is the function called by main() above to perform all the decoding.
  185.  * It instantiates a decoder object and configures it with the input,
  186.  * output, and error callback functions above. A single call to
  187.  * mad_decoder_run() continues until a callback function returns
  188.  * MAD_FLOW_STOP (to stop decoding) or MAD_FLOW_BREAK (to stop decoding and
  189.  * signal an error).
  190.  */

  191. static int decode(mp3_file *mp3fp)
  192. {
  193.     struct mad_decoder decoder;
  194.     int result;

  195.     /* configure input, output, and error functions */
  196.     mad_decoder_init(&decoder, mp3fp,
  197.             input, 0 /* header */, 0 /* filter */, output,
  198.             error, 0 /* message */);

  199.     /* start decoding */
  200.     result = mad_decoder_run(&decoder, MAD_DECODER_MODE_SYNC);

  201.     /* release the decoder */
  202.     mad_decoder_finish(&decoder);

  203.     return result;
  204. }

说明:1、实例原本是基于音频OSS框架的,当然,在嵌入式领域,ALSA也是兼容OSS接口的;
        2、为了在ubuntu上调试方便,并没有直接往音频接口,而是创建了一个文件,直接往文件里面写;
        3、上述代码中的红色区域重点讲解一下

  1. static enum mad_flow input(void *data, struct mad_stream *stream)
  2. {
  3.     mp3_file *mp3fp;
  4.     int ret_code;
  5.     int unproc_data_size; /*the unprocessed data's size*/
  6.     int copy_size;

  7.     mp3fp = (mp3_file *)data;
  8.     if(mp3fp->fpos < mp3fp->flen) {
  9.         unproc_data_size = stream->bufend - stream->next_frame;
  10.         //printf("%d, %d, %d\n", unproc_data_size, mp3fp->fpos, mp3fp->fbsize);
  11.         memcpy(mp3fp->fbuf, mp3fp->fbuf + mp3fp->fbsize - unproc_data_size, unproc_data_size);
  12.         copy_size = BUFSIZE - unproc_data_size;
  13.         if(mp3fp->fpos + copy_size > mp3fp->flen) {
  14.             copy_size = mp3fp->flen - mp3fp->fpos;
  15.         }
  16.         fread(mp3fp->fbuf+unproc_data_size, 1, copy_size, mp3fp->fp);
  17.         mp3fp->fbsize = unproc_data_size + copy_size;
  18.         mp3fp->fpos += copy_size;

  19.         /*Hand off the buffer to the mp3 input stream*/
  20.         mad_stream_buffer(stream, mp3fp->fbuf, mp3fp->fbsize);
  21.         ret_code = MAD_FLOW_CONTINUE;
  22.     } else {
  23.         ret_code = MAD_FLOW_STOP;
  24.     }

  25.     return ret_code;

  26. }

       我们设置的输入buff缓冲区的大小是8192字节,但是对于mp3文件来讲,不一定这8192个字节就刚好是若干个完整的帧,有可能会有若干字节是输入下一个帧的,所有要根据struct mad_stream中的两个指针,标示了缓冲区中的完整帧的起始地址:
  1. unsigned char const *this_frame;    /* start of current frame */
  2. unsigned char const *next_frame;    /* start of next frame */
那么
  1. 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

                               http://cqulpj.blogbus.com/logs/68406669.html

2、作者:李素科  网址: http://www.ibm.com/developerworks/cn/linux/l-cn-libmadmp3player/index.html

3、作者:liky125 网址: http://blog.chinaunix.net/uid-26073752-id-2392553.html

阅读(727) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~