日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

基于libmad库的MP3解码简析

發布時間:2023/11/27 生活经验 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 基于libmad库的MP3解码简析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
基于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庫源碼 ? ???? 可以去相關網站下載,給出鏈接: ?????http://download.chinaunix.net/download.php?id=11891&ResourceID=5910 可以根據不同的平臺自行編譯或者移植,略述。 ? ?????二、相關數據結構及函數接口簡介 ? ???? 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

posted on 2012-07-15 11:19 小小程序員001 閱讀(...) 評論(...) 編輯 收藏

轉載于:https://www.cnblogs.com/musicfans/archive/2012/07/15/2819301.html

總結

以上是生活随笔為你收集整理的基于libmad库的MP3解码简析的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。