基于libmad库的MP3解码简析
- 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解碼流程簡介 MP3解碼有同步方式和異步方式兩種,libmad是以楨為單位對MP3進行解碼的,所謂同步方式是指解碼函數(shù)在解碼完一幀后才返回并帶回出錯信息,異步方式是指解碼函數(shù)在調(diào)用后立即返回,通過消息傳遞解碼狀態(tài)信息。 1、首先創(chuàng)建一個解碼器 struct mad_decoder decoder,緊接著調(diào)用函數(shù)? mad_decoder_init(...)函數(shù),給出這個函數(shù)的原型及定義
- /*
- ?*?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?*/);
?????第六個參數(shù),output_func函數(shù),這個是用來將解碼之后的數(shù)據(jù)寫入輸出緩沖區(qū)或者音頻設(shè)備節(jié)點的; ???? 第七個參數(shù),error_func函數(shù),是用來打印返回的解碼出錯信息的; ?????第八個參數(shù),message_func可以不必實現(xiàn)。
???? 2、調(diào)用mad_decoder_run(&decoder, MAD_DECODER_MODE_SYNC)函數(shù)啟動解碼,查看Libmad庫源碼可知,這個函數(shù)里面會注冊一個函數(shù)指針
- /*
- ?*?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.
- ?*/
-
- //輸出函數(shù)做相應(yīng)的修改,目的是解決播放音樂時聲音卡的問題。
- 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框架的,當然,在嵌入式領(lǐng)域,ALSA也是兼容OSS接口的; ????????2、為了在ubuntu上調(diào)試方便,并沒有直接往音頻接口,而是創(chuàng)建了一個文件,直接往文件里面寫; ??????? 3、上述代碼中的紅色區(qū)域重點講解一下
- 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;
-
- }
???????我們設(shè)置的輸入buff緩沖區(qū)的大小是8192字節(jié),但是對于mp3文件來講,不一定這8192個字節(jié)就剛好是若干個完整的幀,有可能會有若干字節(jié)是輸入下一個幀的,所有要根據(jù)struct mad_stream中的兩個指針,標示了緩沖區(qū)中的完整幀的起始地址:
- 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;
得到剩余的下一個幀的數(shù)據(jù),并且需要將其從buff數(shù)組的尾部拷貝到頭部,再從mp3文件中讀取一部分字節(jié)拼湊成下一個8192字節(jié),提交給庫去解碼,如此周而復(fù)始。
?????? 4、此代碼解碼出來的pcm可以加上44字節(jié)的wav頭文件,則可以用播放器正常播放。
?
???? 五、如何從網(wǎng)絡(luò)socket獲取相應(yīng)數(shù)據(jù),邊解碼邊播放
???? 由于我的項目是要實現(xiàn)一個遠程播放器的功能,即手機端的mp3源文件通過wifi傳輸?shù)介_發(fā)板上解碼播放,所以,對于輸入緩沖區(qū)的控制就不像操作文件那個,可以通過file結(jié)構(gòu)體精確控制好讀取的數(shù)據(jù)位置了,為此,做了些許修改。
???? 可以開兩個線程,一個線程用于接收socket數(shù)據(jù),一個用于解碼播放。主要是緩沖區(qū)的控制,可以如此實現(xiàn):將接收buff[]大小設(shè)置為8192*10字節(jié),然后,解碼input函數(shù)里面的buff[]的大小設(shè)置為8192*11字節(jié),也就是說,多余了8192用來緩沖多余的下一幀字節(jié)的數(shù)據(jù)(因為mp3文件的幀不會超過8192字節(jié)),那么,區(qū)別于上面的思路,我們可以固定的讓socket的buff[]接收8192*10字節(jié)的數(shù)據(jù),如果解碼的buff[]里面初次解碼后有剩余的數(shù)據(jù),仍然將其復(fù)制到解碼buff[]的頭部,只是這時候還是將socket的buff[]的8192*10字節(jié)的數(shù)據(jù)加到解碼buff[]的剛剛拷貝的數(shù)據(jù)后面,所以,這里調(diào)用mad_stream_buffer(stream, buf, bsize)中的bsize就是8192*10+剩余的幀數(shù)據(jù)大小了。
?
相關(guān)參考:
1、作者:cqulpj? 網(wǎng)址:?http://cqulpj.blogbus.com/logs/68406670.html posted on 2012-07-15 11:19 小小程序員001 閱讀(...) 評論(...) 編輯 收藏轉(zhuǎn)載于:https://www.cnblogs.com/musicfans/archive/2012/07/15/2819301.html
總結(jié)
以上是生活随笔為你收集整理的基于libmad库的MP3解码简析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 请问这个是什么电影?
- 下一篇: 怎么添加字幕?