基于libmad 的简单MP3流媒体播放器的实现
作者:李 素科 来源:linux.chinaunix.net
点击:
日期:2007-04-22
[收藏] [投稿]
IE是否经常中毒?推荐您
|
| ret_val = pthread_create(&thread[0],
NULL,
get_http_content,
&read_val);
if (ret_val != 0) {
printf("Cannot create get_http_content thread!\n");
return 1;
}
ret_val = pthread_create(&thread[1],
NULL,
play_http_content,
&read_val);
if (ret_val != 0) {
printf("Cannot create play_http_content thread!\n");
return 1;
}
pthread_join(thread[0], NULL);
pthread_join(thread[1], NULL);
|
可以看到,数据接收线程的线程主函数是 get_http_content, 而播放音乐的线程主函数是 play_http_content。创建子线程后,主线程调用 pthread_join() 等待子结束,并释放线程相关资源。
5.接收 MP3 流媒体数据
由于 MP3 流媒体数据是在 HTTP 服务器的文件目录中,所以,必须由客户端发送 HTTP 请求,然后得到相关 URL 的 HTTP 响应。HTTP 的请求格式如下:
<Method> <Request-URI> <HTTP-1.x> CRLF
*(( general-header
| request-header
| entity-header ) CRLF)
CRLF
[ message-body ]
|
这里 CR(13) 表示回车,LF 表示换行。
根据 HTTP 请求格式,可以构建发送到 HTTP 服务器请求。比如,想要往 192.168.0.123 HTTP 发送获得文件 http://192.168.0.123/45.MP3 那么构建的请求是:
GET /45.MP3 HTTP/1.1\r\n
HOST: 192.168.0.123\r\n\r\n
|
发送请求后,HTTP 服务器会就请求做出响应。如果请求合法,那么响应包括响应的媒体信息,包括 HTTP/1.1 200 OK,表示请求成功。最简单验证请求是否有效的方法是使用 telnet。 例如:
[root@localhost netmad]# telnet 192.168.0.123 80
Trying 192.168.0.123...
Connected to 192.168.0.123(192.168.0.123).
Escape character is '^]'.
HEAD /45.MP3 HTTP/1.1
HOST:192.168.0.123
HTTP/1.1 200 OK
Date: Tue, 14 Nov 2006 10:11:43 GMT
Server: Apache/2.2.0 (Fedora)
Last-Modified: Tue, 17 Oct 2006 15:08:16 GMT
ETag: "3147c9-32e080-1fb83800"
Accept-Ranges: bytes
Content-Length: 3334272
Connection: close
Content-Type: audio/mpeg
X-Pad: avoid browser bug
|
这里可以看到在 HTTP 请求的响应中,有关于 45.MP3 的简单信息,包括文件类型 Content-Type: audio/mpeg,以及文件的长度 Content-Length: 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 <sys/types.h>
#include <sys/socket.h>
int socket (int family, int 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.数据接收线程和音乐播放线程
由于是两个线程并发运行,且音乐播放线程线程运行速度较慢。如果网络速度较快,数据接收线程的接收缓冲区满后,如果当前音乐播放线程正在播放音乐,那么数据接收线程必须停止接收数据。如果不让数据接收线程进入等待状态,它会一直轮训音乐播放线程观察其是否需要数据,简单的轮询会浪费 CPU 资源,所以在这种情况下,有必要让数据接收线程进入等待状态。本文使用信号量机制,来动态控制线程的运行。数据接收缓冲区必须留出一定的空间,存放解码缓冲区中没有被解码的数据。那么要留出多少数据空间呢?至少应该留出一帧数据的空间。这里 8192 字节空间存放剩余的一帧 MPEG 数据,一般情况下应该够用。因此定义:
|