流媒体MP3播放器教程.docx

上传人:b****3 文档编号:4942137 上传时间:2022-12-11 格式:DOCX 页数:20 大小:87.48KB
下载 相关 举报
流媒体MP3播放器教程.docx_第1页
第1页 / 共20页
流媒体MP3播放器教程.docx_第2页
第2页 / 共20页
流媒体MP3播放器教程.docx_第3页
第3页 / 共20页
流媒体MP3播放器教程.docx_第4页
第4页 / 共20页
流媒体MP3播放器教程.docx_第5页
第5页 / 共20页
点击查看更多>>
下载资源
资源描述

流媒体MP3播放器教程.docx

《流媒体MP3播放器教程.docx》由会员分享,可在线阅读,更多相关《流媒体MP3播放器教程.docx(20页珍藏版)》请在冰豆网上搜索。

流媒体MP3播放器教程.docx

流媒体MP3播放器教程

基于libmad的简单MP3流媒体播放器的实现.介绍

   本文在Fedora5Linux下实现了一个基于libmad的mp3流媒体播放器。

此流媒体播放器可以播放基于HTTP1.1协议传输的MP3流媒体数据。

 

   基本原理是:

从HTTP服务器获得MP3媒体信息,然后通过网络传输把MP3数据以数据流的形式接收到MP3流媒体播放器客户端,由客户端通过libmad解码MP3数据流,得到PCM音频数据,写入音频设备,播放音乐。

本文的流媒体播放器只是实现了必要的简单功能,没有考虑太多情况。

比如,没有考虑实时播放控制,这样的话就不能随意选取播放点进行播放。

   本文的MP3流媒体播放器创建两个线程,使用两个缓冲区保存MP3数据,可以一边下载数据,一边播放音乐。

编译运行此MP3流媒体播放器需要安装libmad(以及ALSA(AdvancedLinuxSoundArchitecture)(http:

//www.alsa-project.org)相关的软件。

ALSA包括4部分,分别是sounddriver,soundlibrary,soundutilities以及tools。

至少应该安装sounddriver,soundlibrary。

编译程序时连接库的选项是:

-lmad-lasound-lpthread。

   本文的MP3流媒体播放器使用双缓冲区,一个是数据接收缓冲区,另一个是数据解码缓冲区。

主程序结构如下图所示,图中的蓝色线表示数据流向。

图1:

MP3流媒体播放器主程序结构图

2.libmad简介

   MAD(libmad)是一个开源的高精度MPEG音频解码库,支持MPEG-1(LayerI,LayerII和LayerIII(也就是MP3)。

LIBMAD提供24-bit的PCM输出,完全是定点计算,非常适合没有浮点支持的平台上使用。

使用libmad提供的一系列API,就可以非常简单地实现MP3数据解码工作。

在libmad的源代码文件目录下的mad.h文件中,可以看到绝大部分该库的数据结构和API等。

∙本文用到的libmad中的主要数据结构有:

structmad_stream,structmad_synth,structmad_frame。

它们的定义如下:

清单1:

libmad中的主要数据结构

structmad_stream{

unsignedcharconst*buffer;/*inputbitstreambuffer*/

unsignedcharconst*bufend;/*endofbuffer*/

unsignedlongskiplen;/*bytestoskipbeforenextframe*/

intsync;/*streamsyncfound*/

unsignedlongfreerate;/*freebitrate(fixed)*/

unsignedcharconst*this_frame;/*startofcurrentframe*/

unsignedcharconst*next_frame;/*startofnextframe*/

structmad_bitptrptr;/*currentprocessingbitpointer*/

structmad_bitptranc_ptr;/*ancillarybitspointer*/

unsignedintanc_bitlen;/*numberofancillarybits*/

unsignedchar(*main_data)[MAD_BUFFER_MDLEN];

/*LayerIIImain_data()*/

unsignedintmd_len;/*bytesinmain_data*/

intoptions;/*decodingoptions(seebelow)*/

enummad_errorerror;/*errorcode(seeabove)*/

};

更多内容请看流媒体播放器  流媒体文件格式播放技巧专题,或

   如果缓冲区最后一个MPEG数据帧只有部分数据包括在缓冲区中,那么structmad_stream中的next_frame域指到不完整数据的开始地址。

∙由于缓冲区的MPEG数据帧不一定完整,所以不完整的MPEG帧的数据必须拷贝到下一次解码操作的缓冲区中,进行再次解码。

这里我们还看到bufend指向缓冲区数据的最后地址,也就是最后一字节的地址加1的位置。

mad_stream.bufend–mad_stream.next_frame就是剩余的未被解码的MPEG帧的数据的字节数量(假设此帧在缓冲区中不完整)。

mad_stream的error域用来记录操作mad_stream得到的错误代码。

错误代码在mad.h中有很详细的定义。

∙清单2:

错误代码在mad.h中的详细定义

∙structmad_synth{

∙mad_fixed_tfilter[2][2][2][16][8];/*polyphasefilterbankoutputs*/

∙/*[ch][eo][peo][s][v]*/

∙unsignedintphase;/*currentprocessingphase*/

∙structmad_pcmpcm;/*PCMoutput*/

∙};

mad_synth中的关键域pcm保存解码和合成后得到的PCM数据。

清单3:

mad_synth中的关键域

structmad_pcm{

unsignedintsamplerate;/*samplingfrequency(Hz)*/

unsignedshortchannels;/*numberofchannels*/

unsignedshortlength;/*numberofsamplesperchannel*/

mad_fixed_tsamples[2][1152];/*PCMoutputsamples[ch][sample]*/

};

structmad_pcm定义了音频的采样率、每个声道个数以及最后的PCM采样数据。

这些参数可用来初始化音频设备。

清单4:

structmad_pcm

structmad_frame{

structmad_headerheader;/*MPEGaudioheader*/

intoptions;/*decodingoptions(fromstream)*/

mad_fixed_tsbsample[2][36][32];/*synthesissubbandfiltersamples*/

mad_fixed_t(*overlap)[2][32][18];/*LayerIIIblockoverlapdata*/

};

mad_frame是记录MPEG帧解码后的数据的数据结构,其中的mad_header尤其重要,其用来记录MPEG帧的一些基本信息,比如MPEG层数、声道模式、流比特率、采样比特率等等。

声道模式包括单声道、双声道、联合立体混音声以及一般立体声。

清单5:

mad_frame

enummad_mode{

MAD_MODE_SINGLE_CHANNEL=0,/*singlechannel*/

MAD_MODE_DUAL_CHANNEL=1,/*dualchannel*/

MAD_MODE_JOINT_STEREO=2,/*joint(MS/intensity)stereo*/

MAD_MODE_STEREO=3/*normalLRstereo*/

};

structmad_header{

enummad_layerlayer;/*audiolayer(1,2,or3)*/

enummad_modemode;/*channelmode*/

intmode_extension;/*additionalmodeinfo*/

enummad_emphasisemphasis;/*de-emphasistouse*/

unsignedlongbitrate;/*streambitrate(bps)*/

unsignedintsamplerate;/*samplingfrequency(Hz)*/

unsignedshortcrc_check;/*frameCRCaccumulator*/

unsignedshortcrc_target;/*finaltargetCRCchecksum*/

intflags;/*flags*/

intprivate_bits;/*privatebits*/

mad_timer_tduration;/*audioplayingtimeofframe*/

};

∙下面就本文使用的API的功能做简单介绍。

在本文中用到的API包括:

voidmad_stream_init(structmad_stream*)

voidmad_synth_init(structmad_synth*);

voidmad_frame_init(structmad_frame*);

以上3个API初始化解码需要的数据结构。

voidmad_stream_buffer(structmad_stream*,unsignedcharconst*,unsignedlong);

此函数把原始的未解码的MPEG数据和mad_stream数据结构关联,以便使用mad_frame_decode()来解码MPEG帧数据。

intmad_frame_decode(structmad_frame*,structmad_stream*);

把mad_stream中的MPEG帧数据解码。

voidmad_synth_frame(structmad_synth*,structmad_frameconst*);

把解码后的音频数据合成PCM采样。

voidmad_stream_finish(structmad_stream*);

voidmad_frame_finish(structmad_frame*);

mad_synth_finish(structmad_synth);

以上3个API在解码完毕后使用,释放libmad占用的资源等。

 

更多内容请看流媒体播放器  流媒体文件格式播放技巧专题,或

3.PCM音频设备的操作

   对音频设备的操作主要是初始化音频设备以及往音频设备发送PCM(PulseCodeModulation)数据。

为了方便,本文使用ALSA(AdvancedLinuxSoundArchitecture)提供的库和驱动。

在编译和运行本文中的MP3流媒体播放器的时候,必须先安装ALSA相关的文件。

本文用到的主要对PCM设备操作的函数分为PCM设备初始化的函数以及PCM接口的一些操作函数。

PCM硬件设备参数设置和初始化的函数有:

intsnd_pcm_hw_params_malloc(snd_pcm_hw_params_t**ptr)

intsnd_pcm_hw_params_any(snd_pcm_t*pcm,snd_pcm_hw_params_t*params)

voidsnd_pcm_hw_params_free(snd_pcm_hw_params_t*obj)

intsnd_pcm_hw_params_set_Access(snd_pcm_t*pcm,

snd_pcm_hw_params_t*params,

snd_pcm_access_t_access)

intsnd_pcm_hw_params_set_format(snd_pcm_t*pcm,

snd_pcm_hw_params_t*params,

snd_pcm_format_tval)

intsnd_pcm_hw_params_set_channels(snd_pcm_t*pcm,

snd_pcm_hw_params_t*params,

unsignedintval)

intsnd_pcm_hw_params_set_rate_near(snd_pcm_t*pcm,

snd_pcm_hw_params_t*params,

unsignedint*val,int*dir)

∙PCM接口的操作函数:

∙intsnd_pcm_hw_params(snd_pcm_t*pcm,snd_pcm_hw_params_t*params)

∙intsnd_pcm_prepare(snd_pcm_t*pcm)

∙intsnd_pcm_open(snd_pcm_t**pcm,constchar*name,

∙snd_pcm_stream_tstream,intmode)

∙intsnd_pcm_close(snd_pcm_t*pcm)

∙snd_pcm_sframes_tsnd_pcm_writei(snd_pcm_t*pcm,

∙constvoid*buffer,snd_pcm_uframes_tsize)

   这些函数用到了snd_pcm_hw_params_t结构,此结构包含用来播放PCM数据流的硬件信息配置。

在往音频设备(声卡)写入音频数据之前,必须设置访问类型、采样格式、采样率、声道数等。

   首先使用snd_pcm_open()打开PCM设备,在ALSA中,PCM设备都有名字与之对应。

比如我们可以定义PCM设备名字为char*pcm_name="plughw:

0,0"。

最重要的PCM设备接口是“plughw”以及“hw”接口。

使用“plughw”接口,程序员不必过多关心硬件,而且如果设置的配置参数和实际硬件支持的参数不一致,ALSA会自动转换数据。

如果使用“hw”接口,我们就必须检测硬件是否支持设置的参数了。

Plughw后面的两个数字分别表示设备号和次设备(subdevice)号。

snd_pcm_hw_params_malloc()在栈中分配snd_pcm_hw_params_t结构的空间,然后使用snd_pcm_hw_params_any()函数用声卡的全配置空间参数初始化已经分配的snd_pcm_hw_params_t结构。

snd_pcm_hw_params_set_access()设置访问类型,常用访问类型的宏定义有:

SND_PCM_ACCESS_RW_INTERLEAVED

   交错访问。

在缓冲区的每个PCM帧都包含所有设置的声道的连续的采样数据。

比如声卡要播放采样长度是16-bit的PCM立体声数据,表示每个PCM帧中有16-bit的左声道数据,然后是16-bit右声道数据。

SND_PCM_ACCESS_RW_NONINTERLEAVED

   非交错访问。

每个PCM帧只是一个声道需要的数据,如果使用多个声道,那么第一帧是第一个声道的数据,第二帧是第二个声道的数据,依此类推。

函数snd_pcm_hw_params_set_format()设置数据格式,主要控制输入的音频数据的类型、无符号还是有符号、是little-endian还是bit-endian。

比如对于16-bit长度的采样数据可以设置为:

∙SND_PCM_FORMAT_S16_LE有符号16bitLittleEndian

∙SND_PCM_FORMAT_S16_BE有符号16bitBigEndian

∙SND_PCM_FORMAT_U16_LE无符号16bitLittleEndian

∙SND_PCM_FORMAT_U16_BE无符号16bitBigEndian

比如对于32-bit长度的采样数据可以设置为:

SND_PCM_FORMAT_S32_LE有符号32bitLittleEndian

SND_PCM_FORMAT_S32_BE有符号32bitBigEndian

SND_PCM_FORMAT_U32_LE无符号32bitLittleEndian

SND_PCM_FORMAT_U32_BE无符号32bitBigEndian

   函数snd_pcm_hw_params_set_channels()设置音频设备的声道,常见的就是单声道和立体声,如果是立体声,设置最后一个参数为2。

snd_pcm_hw_params_set_rate_near()函数设置音频数据的最接近目标的采样率。

snd_pcm_hw_params()从设备配置空间选择一个配置,让函数snd_pcm_prepare()准备好PCM设备,以便写入PCM数据。

snd_pcm_writei()用来把交错的音频数据写入到音频设备。

初始化PCM设备的例程如下:

清单6:

初始化PCM设备的例程

/*openaPCMdevice*/

intopen_device(structmad_headerconst*header)

{

interr;

snd_pcm_hw_params_t*hw_params;

char*pcm_name="plughw:

0,0";

intrate=header->samplerate;

intchannels=2;

if(header->mode==0){

channels=1;

}else{

channels=2;

}

if((err=snd_pcm_open(&playback_handle,

pcm_name,SND_PCM_STREAM_PLAYBACK,0))<0){

printf("cannotopenaudiodevice%s(%s)\n",

pcm_name,

snd_strerror(err));

return-1;

}

if((err=snd_pcm_hw_params_malloc(&hw_params))<0){

printf("cannotallocatehardwareparameterstructure(%s)\n",

snd_strerror(err));

return-1;

}

if((err=snd_pcm_hw_params_any(playback_handle,hw_params))<0){

printf("cannotinitializehardwareparameterstructure(%s)\n",

snd_strerror(err));

return-1;

}

if((err=snd_pcm_hw_params_set_access(playback_handle,hw_params,

SND_PCM_ACCESS_RW_INTERLEAVED))<0){

printf("cannotsetaccesstype(%s)\n",

snd_strerror(err));

return-1;

}

if((err=snd_pcm_hw_params_set_format(playback_handle,

hw_params,SND_PCM_FORMAT_S32_LE))<0){

printf("cannotsetsampleformat(%s)\n",

snd_strerror(err));

return-1;

}

if((err=snd_pcm_hw_params_set_rate_near(playback_handle,

hw_params,&rate,0))<0){

printf("cannotsetsamplerate(%s)\n",

snd_strerror(err));

return-1;

}

if((err=snd_pcm_hw_params_set_channels(playback_handle,

hw_params,channels))<0){

printf("cannotsetchannelcount(%s)\n",

snd_strerror(err));

return-1;

}

if((err=snd_pcm_hw_params(playback_handle,

hw_params))<0){

printf("cannotsetparameters(%s)\n",

snd_strerror(err));

return-1;

}

snd_pcm_hw_params_free(hw_params);

if((err=snd_pcm_prepare(playback_handle))<0){

printf("cannotprepareaudiointerfaceforuse(%s)\n",

snd_strerror(err));

return-1;

}

return0;

}

∙这里配置的PCM格式是SND_PCM_FORMAT_S32_LE,采样的格式是每个采样有32-bit的数据,数据按照little-endian存放。

如果通过mad_frame_decode()函数得到PCM数据后,要求每个采样数据只占16-bit,需要把数据进行MAD的定点类型到signedshort

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 法律文书 > 调解书

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1