基於libmad庫的MP3解碼簡析


 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.(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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM