4.创建线程
本文使用 POSIX 线程库(pthreads)来创建线程。比如,本文需要两个线程,一个是数据接收线程,另一个是音乐播放线程。创建线程的程序如下所示:
清单 10:创建线程
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 接收数据。
更多内容请看PCdog.com--流媒体文件格式播放技巧 网吧流媒体服务器专题
