流媒体MP3播放器教程

上传人:d**** 文档编号:177467259 上传时间:2022-12-25 格式:DOCX 页数:22 大小:356.15KB
收藏 版权申诉 举报 下载
流媒体MP3播放器教程_第1页
第1页 / 共22页
流媒体MP3播放器教程_第2页
第2页 / 共22页
流媒体MP3播放器教程_第3页
第3页 / 共22页
资源描述:

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

1、基于libmad的简单MP3流媒体播放器的实现.介绍本文在 Fedora 5 Linux 下实现了一个基于 libmad 的 mp3 流媒体播放器。此流媒体播放器可 以播放基于 HTTP 1.1 协议传输的 MP3 流媒体数据。基本原理是:从 HTTP 服务器获得 MP3 媒体信息,然后通过网络传输把 MP3 数据以数据流 的形式接收到 MP3 流媒体播放器客户端,由客户端通过 libmad 解码 MP3 数据流,得到 PCM 音频数据,写入音频设备,播放音乐。本文的流媒体播放器只是实现了必要的简单功能,没有考 虑太多情况。比如,没有考虑实时播放控制,这样的话就不能随意选取播放点进行播放。本文

2、的 MP3 流媒体播放器创建两个线程,使用两个缓冲区保存 MP3 数据,可以一边下载数 据,一边播放音乐。编译运行此 MP3 流媒体播放器需要安装 libmad ( 以及 ALSA(Advanced Linux Sound Architecture) (http:/www.alsa-project.org)相关的软件。ALSA 包括 4 部分,分别是 sound driver, sound library , sound utilities以及tools。至少应该安装sound driver, sound library。编译程序时连接库的选项是: -lmad -lasound -lpthre

3、ad。本文的 MP3 流媒体播放器使用双缓冲区,一个是数据接收缓冲区,另一个是数据解码缓冲区。 主程序结构如下图所示,图中的蓝色线表示数据流向。图 1:MP3 流媒体播放器主程序结构图2libmad 简介MAD (libmad)是一个开源的高精度MPEG音频解码库,支持MPEG-1 (Layer I, Layer II和 Layerlll (也就是MP3)。LIBMAD提供24-bit的PCM输出,完全是定点计算,非常适合没 有浮点支持的平台上使用。使用libmad提供的一系列API,就可以非常简单地实现MP3数据 解码工作。在 libmad 的源代码文件目录下的 mad.h 文件中,可以看到

4、绝大部分该库的数据结构 和 API 等。本文用到的libmad中的主要数据结构有:st ruct mad一st ream, st rue t mad_syn th, struet mad一frame。它们的定义如下:清单 1:libmad 中的主要数据结构struet mad_stream unsigned ehar eonst *buffer; /* input bitstream buffer */ unsigned ehar eonst *bufend; /* end of buffer */unsigned long skiplen; frame */int sync;unsigned

5、 long freerate;unsigned charunsigned char/由于缓冲区的MPEG数据帧不一定完整,所以不完整的MPEG帧的数据必须拷贝到下一次 解码操作的缓冲区中,进行再次解码。这里我们还看到 bufend 指向缓冲区数据的最后地址, 也就是最后一字节的地址加1的位置。mad_stream.bufend - mad_stream.next_frame 就是剩余的未被解码的 MPEG 帧的数据的字节数量(假设此帧 在缓冲区中不完整)。 mad_stream 的 error 域用来记录操作 mad_stream 得到的错误 代码。错误代码在 mad.h 中有很详细的定义。

6、清单 2:错误代码在 mad.h 中的详细定义 struct mad_synth mad_fixed_t filter222168; /* polyphase filterbank outputs */ bytes to skip before next/* stream sync found */const *this_frame; /* const *next_frame; /*struct mad_bitptr ptr; pointer */struct mad_bitptr anc_ptr; unsigned int anc_bitlen;/* current processing b

7、it/* ancillary bits pointer */* number of ancillary bits */* free bitrate (fixed) */ start of current frame */ start of next frame */unsigned char (*main_data)MAD_BUFFER_MDLEN;/* Layer III main_data() */ unsigned int md_len;int options;*/enum mad_error error;/* bytes in main_data */* decoding option

8、s (see below)/* error code (see above) */;更多内容请看流媒体播放器 流媒体文件格式播放技巧专题,或/* cheopeosv */* current processing phase */unsigned int phase;如果缓冲区最后一个 MPEG 数据帧只有部分数据包括在缓冲区中,那么 struct mad_stream 中的 next_frame 域指到不完整数据的开始地址。 ;mad_synth 中的关键域 pcm 保存解码和合成后得到的 PCM 数据。清单 3:mad_synth 中的关键域struct mad_pcm unsigned i

9、nt samplerate; unsigned short channels; unsigned short length;/* sampling frequency (Hz) */* number of channels */* number of samples per channel*/mad_fixed_t samples21152; /* PCM output samples chsample */;struct mad_pcm 定义了音频的采样率、每个声道个数以及最后的 PCM 采样数据。这些参 数可用来初始化音频设备。清单 4:struct mad_pcmstruct mad_f

10、rame struct mad_header header; /* MPEG audio header */ int options; /* decoding options (from stream) */mad_fixed_t sbsample23632; /* synthesis subband filter samples */mad_fixed_t (*overlap)23218; /* Layer III block overlap data */;mad_frame 是记录 MPEG 帧解码后的数据的数据结构,其中的 mad_header 尤其重要, 其用来记录 MPEG 帧的一

11、些基本信息,比如 MPEG 层数、声道模式、流比特率、采样比特率等 等。声道模式包括单声道、双声道、联合立体混音声以及一般立体声。清单 5:mad_frameenum mad_mode MAD_MODE_SINGLE_CHANNEL = 0, MAD_MODE_DUAL_CHANNEL/* single channel */1,/* dual channel */MAD_MODE_JOINT_STEREO =2, stereo */MAD_MODE_STEREO = 3;struct mad_header enum mad_layer layer;enum mad_mode mode; int

12、 mode_extension;enum mad_emphasis emphasis; unsigned long bitrate; unsigned int samplerate; unsigned short crc_check; unsigned short crc_target; int flags;/* joint (MS/intensity)/* normal LR stereo */* audio layer (1, 2, or 3) */* channel mode */* additional mode info */* de-emphasis to use */* stre

13、am bitrate (bps) */* sampling frequency (Hz) */* frame CRC accumulator */* final target CRC checksum */* flags */int private_bits; /* private bits */ mad_timer_t duration; /* audio playing time of frame */ ;下面就本文使用的 API 的功能做简单介绍。在本文中用到的 API 包括:void mad_stream_init(struct mad_stream *) void mad_synth

14、_init(struct mad_synth *); void mad_frame_init(struct mad_frame *);以上3 个 API 初始化解码需要的数据结构。void mad_stream_buffer(struct mad_stream *, unsigned char const *, unsigned long);此函数把原始的未解码的 MPEG 数据和 mad_stream 数据结构关联,以便使用 mad_frame_decode( ) 来解码 MPEG 帧数据。int mad_frame_decode(struct mad_frame *, struct mad

15、_stream *);把 mad_stream 中的 MPEG 帧数据解码。void mad_synth_frame(struct mad_synth *, struct mad_frame const *);把解码后的音频数据合成 PCM 采样。void mad_stream_finish(struct mad_stream *); void mad_frame_finish(struct mad_frame *); mad_synth_finish(struct mad_synth);以上 3 个 API 在解码完毕后使用,释放 libmad 占用的资源等。3PCM 音频设备的操作对音频设

16、备的操作主要是初始化音频设备以及往音频设备发送 PCM (Pulse Code Modulation ) 数据。为了方便,本文使用ALSA (Advanced Linux Sound Architecture)提供的库和驱动。在编 译和运行本文中的 MP3 流媒体播放器的时候,必须先安装 ALSA 相关的文件。本文用到的主要对 PCM 设备操作的函数分为 PCM 设备初始化的函数以及 PCM 接口的一些 操作函数。PCM 硬件设备参数设置和初始化的函数有:int snd_pcm_hw_params_malloc (snd_pcm_hw_params_t int snd_pcm_open (sn

17、d_pcm_t *pcm, const char *name, snd_pcm_stream_t stream, int mode)ptr)int snd_pcm_hw_params_any (snd_pcm_t PCM接口的操作函数: int snd_pcm_hw_params (snd_pcm_t *pcm, snd_pcm_hw_params_t *params) int snd_pcm_prepare (snd_pcm_t *pcm)pcm, snd_pcm_hw_params_t *params)void snd_pcm_hw_params_free (snd_pcm_hw_para

18、ms_t *obj) int snd_pcm_hw_params_set_Access ( snd_pcm_t *pcm, snd_pcm_hw_params_t *params,snd_pcm_access_t _access) int snd_pcm_hw_params_set_format ( snd_pcm_t *pcm, snd_pcm_hw_params_t *params,snd_pcm_format_t val)int snd_pcm_hw_params_set_channels(snd_pcm_t *pcm,snd_pcm_hw_params_t*params,unsigne

19、d int val)int snd_pcm_hw_params_set_rate_near(snd_pcm_t *pcm,snd_pcm_hw_params_t*params,unsigned int *val, int *dir) int snd_pcm_close (snd_pcm_t *pcm) snd_pcm_sframes_tsnd_pcm_writei (snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size)这些函数用到了 snd_pcm_hw_params_t 结构,此结构包含用来播放 PCM 数据流的硬 件信息配置

20、。在往音频设备(声卡)写入音频数据之前,必须设置访问类型、采样格式、采样率、 声道数等。首先使用snd_pcm_open ()打开PCM设备,在ALSA中,PCM设备都有名字与之对 应。比如我们可以定义PCM设备名字为char *pcm_name = plughw:O,O。最重要 的PCM设备接口是“plughw”以及“hw”接口。使用“plughw”接口,程序员不必过多关心硬件,而 且如果设置的配置参数和实际硬件支持的参数不一致,ALSA会自动转换数据。如果使用“hw”接 口,我们就必须检测硬件是否支持设置的参数了。 Plughw 后面的两个数字分别表示设备号和次设 备(subdevice)

21、 号。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 帧中

22、有 16-bit 的左声道数据,然后 是 16-bit 右声道数据。SND_PCM_ACCESS_RW_NONINTERLEAVED非交错访问。每个PCM帧只是一个声道需要的数据,如果使用多个声道,那么第一帧是第一函数 snd_pcm_hw_params_set_format() 设置数据格式,主要控制输入的音频数据的 类型、无符号还是有符号、是Utt1 e-endian还是bit-endian。比如对于bit长度 的采样数据可以设置为: SND_PCM_F0RMAT_S16_LE SND_PCM_F0RMAT_S16_BE SND_PCM_FORMAT_U16_LE SND_PCM_FORM

23、AT_U16_BE有符号 16 bit Little Endian 有符号 16 bit Big Endian 无符号 16 bit Little Endian 无符号 16 bit Big Endian比如对于 32-bit 长度的采样数据可以设置为:SND_PCM_FORMAT_S32_LESND_PCM_FORMAT_S32_BESND_PCM_FORMAT_U32_LESND_PCM_FORMAT_U32_BE有符号 32 bit Little Endian 有符号 32 bit Big Endian 无符号 32 bit Little Endian 无符号 32 bit Big En

24、dian函数 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 设备的例程/* open a

25、PCM device */ int open_device(struct mad_header const *header)int err; snd_pcm_hw_params_t *hw_params;char *pcm_name = plughw:0,0;int rate = header-samplerate;int channels = 2;if (header-mode = 0) channels = 1; else channels = 2;if (err = snd_pcm_open (&playback_handle,pcm_name, SND_PCM_STREAM_PLAYB

26、ACK, 0) 0) printf(cannot open audio device %s (%s)n, pcm_name, snd_strerror (err);return -1;if (err = snd_pcm_hw_params_malloc (&hw_params) 0) printf(cannot allocate hardware parameter structure (%s)n,snd_strerror (err); return -1;if (err = snd_pcm_hw_params_any (playback_handle, hw_params) 0) print

27、f(cannot initialize hardware parameter structure (%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(cannot set access type (%s)n, snd_strerror (err);return -1;if (err = snd_pcm_hw_params_set_format (playback

28、_handle,hw_params, SND_PCM_FORMAT_S32_LE) 0) printf(cannot set sample format (%s)n, snd_strerror (err);return -1;if (err = snd_pcm_hw_params_set_rate_near (playback_handle, hw_params, &rate, 0) 0) printf(cannot set sample rate (%s)n, snd_strerror (err);return -1;if (err = snd_pcm_hw_params_set_chann

29、els (playback_handle, hw_params, channels) 0) printf(cannot set channel count (%s)n, snd_strerror (err);return -1;if (err = snd_pcm_hw_params (playback_handle,hw_params) 0) printf(cannot set parameters (%s)n, snd_strerror (err);return -1;snd_pcm_hw_params_free (hw_params);if (err = snd_pcm_prepare (

30、playback_handle) samples0j;*(OutputPtr+) = sample & 0xff;*(OutputPtr+) = (sample 8);*(OutputPtr+) = (sample 16);*(OutputPtr+) = (sample 24); if (nchannels = 2) sample = pcm-samples1j;*(OutputPtr+) = sample & 0xff;*(OutputPtr+) = sample 8;*(OutputPtr+) = (sample 16);*(OutputPtr+) = (sample 24);j+;if

31、(err = snd_pcm_writei (playback_handle, buf, samples) 0) err = xrun_recovery(playback_handle, err);if (err 0) printf(Write error: %sn, snd_strerror(err); return -1;这里用到了 http:/www.alsa-project.org/ 关于 ALSA 文档中的例子函数 xrun_recovery( )。详 细例子请参见 http:/www.alsa-project.org/alsa-doc/alsa-lib/_2test_2pcm_8c

32、-example.htm 1。使用此函 数的目的是避免出现由于网络原因,声卡不能及时得到音频数据而使得 snd_pcm_writei() 不能正 常连续工作。实际上在 xrun_recovery( ) 中,又调用 snd_pcm_prepare() 和 snd_pcm_resume() 以xrun_recovery() 函数如下所示:清单 8: xrun_recovery()函数 int xrun_recovery(snd_pcm_t *handle, int err) if (err = -EPIPE) /* under-run */ err = snd_pcm_prepare(handle

33、); if (err 0) printf(Cantrecoveryfromunderrun,preparefailed:%sn, snd_strerror(err); return 0; else if (err = -ESTRPIPE) while (err = snd_pcm_resume(handle) = -EAGAIN) sleep(1);/* wait until the suspend flag isreleased */ if (err 0) err = snd_pcm_prepare(handle); if (err 0) printf(Cant recovery from

34、suspend, prepare failed: %sn, snd_strerror(err); return 0; return err;知道了具体的音频设备操作方法,就该使用 MAD 提供的函数具体实现解码了。函数 mp3_decode_buf( ) 提供了使用 libmad 解码的方法。首先调用 mad_streambufferO函数把mp3流数据和decode_stream关联,然后开始循环 解码数据。如果在解码数据过程中,有不完整 PCM 数据帧,那么 decode_stream.error 的值就是 MAD_ERROR_BUFLEN,且 decode_stream.next_fra

35、me 不为 NULL。这时候,把剩余的未解码的数据再拷贝到数据解码缓冲区里。 mad_frame_decode( ) 函数从 decode_stream 中得到 PCM 数据。清单 9:mad_frame_decode( ) 函数从 decode_stream 中得到 PCM 数据int mp3_decode_buf(char *input_buf, int size)int decode_over_flag = 0;int remain_bytes = 0;int ret_val = 0;mad_stream_buffer(&decode_stream, input_buf, size);

36、decode_stream.error = MAD_ERROR_NONE;while (1)if (decode_stream.error = MAD_ERROR_BUFLEN) if (decode_stream.next_frame != NULL) remain_bytes = decode_stream.bufend - decode_stream.next_frame;memcpy(input_buf, decode_stream.next_frame, remain_bytes);return remain_bytes;ret_val = mad_frame_decode(&dec

37、ode_frame, &decode_stream);/* 省略部分代码 */if (ret_val = 0) if (play_frame(&decode_frame) = -1) return -1;/* 后面代码省略 */return 0;4创建线程本文使用POSIX线程库(pthreads)来创建线程。比如,本文需要两个线程,一个是数据接收线程,另一个是音乐播放线程。创建线程的程序如下所示:清单 10:创建线程ret_val = pthread_create(&thread0,NULL,get_http_content,&read_val);if (ret_val != 0) prin

38、tf(Cannot create get_http_content thread!n);return 1;ret_val = pthread_create(&thread1,NULL,play_http_content,&read_val);if (ret_val != 0) printf(Cannot create play_http_content thread!n);return 1;pthread_join(thread0, NULL);pthread_join(thread1, NULL);可以看到,数据接收线程的线程主函数是 get_http_content, 而播放音乐的线程主函

39、数 是play_http_content。创建子线程后,主线程调用pthread_join()等待子结束, 并释放线程相关资源。5接收 MP3 流媒体数据由于 MP3 流媒体数据是在 HTTP 服务器的文件目录中,所以,必须由客户端发送 HTTP 请 求,然后得到相关 URL 的 HTTP 响应。 HTTP 的请求格式如下: CRLF *( general-headerrequest-headerentity-header ) CRLF)CRLF message-body GET /45.MP3 HTTP/l.lrn HOST: 192.168.0.123rnrn发送请求后,HTTP服务器会就

40、请求做出响应。如果请求合法,那么响应包括响应的媒体信息, 包括HTTP/1.1 200 K,表示请求成功。最简单验证请求是否有效的方法是使用telnet。例 如:rootlocalhost netmad# telnet 192.168.0.12380Trying 192.168.0.123.Connected to 192.168.0.123(192.168.0.123). Escape character is .HEAD /45.MP3 HTTP/1.1HOST:192.168.0.123HTTP/1.1 200 OKDate: Tue, 14 Nov 2006 10:11:43 GMTS

41、erver: Apache/2.2.0 (Fedora)Last-Modified: Tue, 17 Oct 2006 15:08:16 GMTETag: 3147c9-32e080-1fb83800Accept-Ranges: bytes Content-Length: 3334272Connection: close Content-Type: audio/mpegX-Pad: avoid browser bug这里可以看到在 HTTP 请求的响应中,有关于 45.MP3 的简单信息,包括文件类型ContentType: audio/mpeg,以及文件的长度 ContentLeng th:

42、 3334272。通过解析 HTTP 响应,很容易从 Content-Length 项得到 MP3 数据总的长度。为了发送HTTP请求,首先从播放器程序传递的参数解析出请求的资源的URI,比如程序传递参数为 http:/192.168.0.123/45.MP3那么解析此url,得到http请求的资源uri是 /45.MP3。get_address 函数简单地解析了 url,用 gethostbyname()获得域名 以及操作 socket 需要的地址信息。本文用于网络通信的一些 socket 相关的函数如下:#include #include int socket (int family, i

43、nt type, int protocol)此函数创建 socket 。int connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen);和目标地址服务程序连接,完成 3 次握手。int recv(int s, void *buf, size_t len, int flags);此函数从创建的 socket 接收数据。更多内容请看流媒体播放器 流媒体文件格式播放技巧专题,或6数据接收线程和音乐播放线程由于是两个线程并发运行,且音乐播放线程线程运行速度较慢。如果网络速度较快,数据接收 线程的接收缓冲区满后,如

44、果当前音乐播放线程正在播放音乐,那么数据接收线程必须停止接收 数据。如果不让数据接收线程进入等待状态,它会一直轮训音乐播放线程观察其是否需要数据, 简单的轮询会浪费 CPu 资源,所以在这种情况下,有必要让数据接收线程进入等待状态。本文 使用信号量机制,来动态控制线程的运行。数据接收缓冲区必须留出一定的空间,存放解码缓冲 区中没有被解码的数据。那么要留出多少数据空间呢?至少应该留出一帧数据的空间。这里 8192 字节空间存放剩余的一帧 MPEG 数据,一般情况下应该够用。因此定义:#define DECODE_BUF_SIZE(8192*11)#define GARD_SIZE(8192*10

45、)static char decode_bufDECODE_BUF_SIZE; static char recv_bufDECODE_BUF_SIZE;GARD_SIZE 是一次从 socket 读取数据字节数的最大值,而解码缓冲区的大小应该是比GARD_SIZE 大 8192 字节,因此定义 DECODE_BUF_SIZE 为(8192*11)。recv_buf 是数据接收缓冲区, decode_buf 是数据解码缓冲区。在拷贝数据到解码缓冲区的时候,上次 未解码的数据,还被保存在解码缓冲区的开始部分,故拷贝数据的时候,必须拷贝到剩余数据的 后面,程序例子如下:memcpy(decode_b

46、uf + current_remain, recv_buf, current_read); current_read += current_remain;这里的 current_remain 表示上次解码线程中未解码的不完整 MP3 帧的数据字节数, current_read 表示当前接收线程接收到的实际数据字节数。两个缓冲区之间的数据拷贝操作 如下图所示。图 2:缓冲区之间的数据拷贝操作接收数据完整帧数据It数据解码缓冲区拷贝上次不解码的不完整帧数据解码缓冲区数据接收线程和音乐播放线程之间的同步由于使用了双缓冲区保存数据,所以,在音乐播放线程播放音乐的时候,数据接收线程不能把数据拷贝到数据解

47、码缓冲区,而是需要等待。当数据接收缓冲区满的时候,接收线程自己也需要等待。本文用到了 POSIX 信号量处理函数,实现了线程之间的同步。它们分别是: #include int sem_init(sem_t *sem, int pshared, unsigned int value);初始化信号量,第三个参数表示初始的信号量的计数。int sem_wait(sem_t * sem);sem_wait阻塞当前线程的执行,直到信号量的计数非0;然后,它会把信号量计数减1,然 后程序继续执行。相当于 P 操作。int sem_post(sem_t * sem);把sem指向的信号量计数加1。相当于v操

48、作。int sem_destroy(sem_t * sem);释放信号量对象。在程序中,信号量定义及初始化为:static sem_t empty_sem;static sem_t decode_sem; static sem_t copy_sem;sem_init(&empty_sem, 0, 1); sem_init(&decode_sem, 0, 0);sem_init(_sem, 0, 1);更多内容请看流媒体播放器流媒体文件格式播放技巧专题,或empty_sem 信号量的计数表示接收缓冲是否为空,其中如果是 1,表示 为空;如果为0表示不为空。decode_sem信号量的计数表示音乐

49、播放线 程是否正在对数据解码缓冲区的数据进行解码,如果是 1 表示正在进行 解码,如果是0表示没有解码;copy_sem信号量的计数表示是否可以从数据接收缓冲区拷贝数据到数据解码缓冲区,如果是 1 表示可以,如果 是 0 表示不能。两个线程的同步操作或者说是 PV 操作流程如下图所示:图3: PV操作流程示意图流媒体播放器大对比PPS tream流媒体播放器最新版发布7程序运行实例图 4:程序运行实例rootkjciFhast:/hoad - Shell - KonsofeSession Edit View Baokmarks Settings Helprootfilccalhost neti

50、iad # . /netnad http:/192_ 168_0.123/45_mp3Playing irusic froi http:/192.168. . 123/45_np3File nane is 45.up3Hie requested MP3 file1s size is 3334272 bytes.*士*士士士士士*扛 mini toy WPg Playw *rp3 strean infcxmatiaa 2-Bit rate: 128000 kb/sMPEG layer IIINomaZL LR stereo sanple rate: 44100 HzPl jytr, *|10.C (10.C)8小结在实现基于libmad的MP3流媒体播放器中,我们用到了 libmad的API、网络socket编程 技术、在音频设备上播放 PCM 数据技术、 POSIX 信号量以及 POSIX 线程。数据接收线程和音 乐播放线程通过信号量和共享数据通信,相比单缓冲操作,通过双缓冲数据操作有效地提高了程 序执行效率。同时,通过简单的信号量操作,线程不必使用轮询的方法来处理数据,也进一步减 少了对 CPU 资源的浪费。

展开阅读全文
温馨提示:
1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
2: 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
3.本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
关于我们 - 网站声明 - 网站地图 - 资源地图 - 友情链接 - 网站客服 - 联系我们

copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!