最近碰到移植rtsp库到wince下,读不到数据,为了解决问题,写了一个小的rtsp客户端协议学习程序,实现基本的会话协议,OPTIONS,DESCRIBE,SETUP,PLAY等协议,将读取的数据存入到文件中去。程序代码贴在下面,这个代码能在ce和pc上跑。供大家分享下。
#ifdef _WIN32_WCE
#include "stdafx.h"
#endif
#ifndef _WIN32_WCE
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <commctrl.h>
#include <MMSystem.h>
#include <winsock2.h>
#include <stdio.h>
#include <stdlib.h>
#include <tchar.h>
#ifdef _WIN32_WCE
#pragma comment(lib, "ws2.lib")
#else
#pragma comment(lib, "ws2_32.lib")
#endif
#define PP_NAME "User-Agent:rtsp client(v1.0)"
#define PP_CRLF "/r/n"
//测试之用
//打开连接
long InitSocket();
//关闭连接
long DeInitSocket();
//初始化tcp socket
long InitTCPSocket(int port);
//初始化udp socket
long InitUDPSocket(const char *ip, int port);
//取socket端口号
long GetSokcetPort(int sock, int *port);
//读取数据
long ReadSocket(int sock, char *buf, int len, int timeout);
//发送命令数据
long SendRTSPCmd(int sock, const char *cmd, const char *szparam);
//解析rtsp命令回应数据
long PraseRTSPCmd();
long PraseOptionCmd(const char *sz);
long PraseDescribeCmd(const char *sz);
long PraseSetupCmd(const char *sz, char *sess);
long PrasePlayCmd(const char *sz);
long GetResponseCode(const char *sz); //取返回值
//
//字符串操作函数
static char* getLine(char* startOfLine);
//生成rtsp发送命令
char * GetRTSPCmd(const char *);
char * GetOptionCmd(char *url);
char * GetDescribeCmd(char *url);
char * GetPlayCmd(char *url, char *session, char *range);
char * GetSetupCmd(char *url, int port1, int port2);
char * GetReportCmd(char *);
//
//日志函数
long logwr(void *, int len);
//全局变量定义区
fd_set rfdsock;
//日志写入文件指针
FILE *fp = NULL;
//
//
//rtsp请求解析
long PraseURL(const char *url, char *szip, int *iport);
int _tmain(int argc, _TCHAR* argv[])
{
int sockin, sc1, sc2;
sockaddr_in addr;
char *buf, *szcmd, *url;
char szip[32];
int nlen, iret, iport;
int ip1, ip2;
long lret;
//初始化变量
FD_ZERO(&rfdsock);
fp = fopen("1.txt", "w+");
//分配缓冲区
nlen = 10240;
buf = (char*)malloc(nlen);
//定义要连接的url
//url = "rtsp://192.168.1.43:2554/realmp3.mp3";
url = "rtsp://192.168.1.42/realmp3.mp3";
//url = "rtsp://192.168.1.43/1.amr";
//初始化sock
InitSocket();
//分析url请求,取出ip,端口
lret = PraseURL(url, szip, &iport);
//初始化与服务器连接的socket
sockin = InitTCPSocket(0);
//与服务器连接
addr.sin_family = AF_INET;
addr.sin_port = htons(iport);
addr.sin_addr.s_addr = inet_addr(szip);
iret = connect(sockin,(struct sockaddr*)&addr, sizeof addr);
//发送option命令
szcmd = GetOptionCmd(url);
lret = SendRTSPCmd(sockin, "OPTIONS",szcmd);
free(szcmd);
lret = ReadSocket(sockin, buf, nlen,100);
//发送DESCRIBE命令
szcmd = GetDescribeCmd(url);
lret = SendRTSPCmd(sockin, "DESCRIBE", szcmd);
free(szcmd);
lret = ReadSocket(sockin, buf, nlen, 100);
//解析Response
lret = PraseDescribeCmd((const char*)buf);
//创建客户端接收端口
sc1 = InitUDPSocket(NULL, 6544);
sc2 = InitUDPSocket(NULL, 6545);
//将sock加入到要等待的队列
FD_SET(sc1, &rfdsock );
FD_SET(sc2, &rfdsock);
lret = GetSokcetPort(sc1, &ip1);
lret = GetSokcetPort(sc2, &ip2);
//发送Setup命令,告诉服务器客户端的接受数据的端口
szcmd = GetSetupCmd(url, ip1, ip2);
//告诉服务器客户端的端口
lret = SendRTSPCmd(sockin, "SETUP", szcmd);
free(szcmd);
lret = ReadSocket(sockin, buf, nlen, 100);
//解析Response返回的命令串
lret = PraseSetupCmd(buf, szip);
char *session, *srange;
session = szip;
//发送PLAY命令
srange = "Range: npt=0.000-39.471/r/n";
szcmd = GetPlayCmd(url, session, srange);
lret = SendRTSPCmd(sockin, "PLAY", szcmd);
free(szcmd);
lret = ReadSocket(sockin, buf, nlen, 100);
timeval tv;
fd_set fr;
int i;
tv.tv_sec = 20;
tv.tv_usec = 0;
struct sockaddr_in addr2;
int addrlen;
addrlen = sizeof addr;
//将数据写到文件中去
FILE *ffp;
ffp = fopen("1.mp3", "w+");
//开始接受数据了
while(true)
{
fr = rfdsock;
lret = select(0, &fr, NULL, NULL, &tv);
if(lret == SOCKET_ERROR)
{
break;
}
else if(lret >0)
{
//判断是哪个socket可以读取数据了
for(i = 0; i< 2;i ++)
{
if(FD_ISSET(rfdsock.fd_array[i], &fr)
&& FD_ISSET(rfdsock.fd_array[i], &rfdsock))
{
lret = recvfrom(rfdsock.fd_array[i], buf, nlen,0, (struct sockaddr*)&addr2, &addrlen );
if(lret > 0 && ffp)
{
fwrite(buf, 1, lret, ffp);
}
else if(lret == SOCKET_ERROR)
{
break;
}
}
}
}
else if(lret == 0)
break;
}
fclose(ffp);
//退出后的清理工作
closesocket(sockin);
closesocket(sc1);
closesocket(sc2);
fwrite("/r/nend", 1, 5, fp);
fclose(fp);
return 0;
}
long InitSocket()
{
WSADATA ws;
long lret = -1;
lret = WSAStartup(MAKEWORD(2,2), &ws);
return 0;
}
long DeInitSocket()
{
WSACleanup();
return 0;
}
long InitTCPSocket( int port )
{
long lret ;
int sock;
sockaddr_in addr;
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
//int flag = 1;
//lret = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&flag, sizeof flag);
//addr.sin_family = AF_INET;
//addr.sin_port = 0;
//addr.sin_addr.s_addr = INADDR_ANY;
//lret = bind(sock, (struct sockaddr*)&addr, sizeof addr);
lret = sock;
return lret;
}
#define SIO_RCVALL _WSAIOW(IOC_VENDOR,1)
#define SIO_RCVALL_MCAST _WSAIOW(IOC_VENDOR,2)
#define SIO_RCVALL_IGMPMCAST _WSAIOW(IOC_VENDOR,3)
#define SIO_KEEPALIVE_VALS _WSAIOW(IOC_VENDOR,4)
#define SIO_ABSORB_RTRALERT _WSAIOW(IOC_VENDOR,5)
#define SIO_UCAST_IF _WSAIOW(IOC_VENDOR,6)
#define SIO_LIMIT_BROADCASTS _WSAIOW(IOC_VENDOR,7)
#define SIO_INDEX_BIND _WSAIOW(IOC_VENDOR,8)
#define SIO_INDEX_MCASTIF _WSAIOW(IOC_VENDOR,9)
#define SIO_INDEX_ADD_MCAST _WSAIOW(IOC_VENDOR,10)
#define SIO_INDEX_DEL_MCAST _WSAIOW(IOC_VENDOR,11)
long InitUDPSocket(const char *ip, int port )
{
long lret;
int sock;
sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
int flag = 1;
lret = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&flag, sizeof flag);
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = port;
if(ip)
addr.sin_addr.s_addr = inet_addr(ip);
else
addr.sin_addr.s_addr = INADDR_ANY;
lret = bind(sock, (struct sockaddr*)&addr, sizeof addr);
//设置非堵塞通讯
//u_long arg = 2;
//lret = ioctlsocket(sock, SIO_LIMIT_BROADCASTS, &arg);
lret = sock;
return lret;
}
long GetSokcetPort( int sock, int *port )
{
long lret = -1;
sockaddr_in addr;
int nlen;
nlen = sizeof addr;
addr.sin_port = 0;
*port = 0;
if(getsockname(sock, (struct sockaddr*)&addr, &nlen) < 0)
lret = -1;
else
{
lret = 0;
*port = addr.sin_port;
}
return lret;
}
long ReadSocket(int sock, char *buf, int len, int timeout )
{
long lret ;
int iret;
fd_set fr;
timeval tm;
tm.tv_sec = timeout;
tm.tv_usec = 0;
FD_ZERO(&fr);
fr.fd_count = 1;
fr.fd_array[0] = sock;
lret = select(sock, &fr, NULL, NULL, &tm);
if(lret > 0)
{
lret = recv(sock, buf, len, 0);
if(lret == SOCKET_ERROR)
{
}
else if(lret > 0)
{
logwr((void*)buf, lret);
}
}
return lret;
}
long SendRTSPCmd( int sock, const char *cmd, const char *szparam )
{
long lret;
int ilen;
ilen = strlen(szparam);
lret = send(sock, szparam, ilen,0);
if(lret == SOCKET_ERROR)
{
lret = WSAGetLastError();
}
logwr((void *)szparam, ilen);
return lret;
}
char * GetRTSPCmd( const char * szName)
{
char *str = NULL;
char const* cmdFmt = NULL;
if(!strcmp(szName, "OPTIONS"))
{
cmdFmt =
"OPTIONS %s RTSP/1.0/r/n"
"CSeq: %d/r/n"
"%s"
"%s"
#ifdef SUPPORT_REAL_RTSP
REAL_OPTIONS_HEADERS
#endif
"/r/n";
}
else if(!strcmp(szName, "ANNOUNCE"))
{
cmdFmt =
"ANNOUNCE %s RTSP/1.0/r/n"
"CSeq: %d/r/n"
"Content-Type: application/sdp/r/n"
"%s"
"Content-length: %d/r/n/r/n"
"%s";
}
else if(!strcmp(szName, "PLAY"))
{
cmdFmt =
"PLAY %s RTSP/1.0/r/n"
"CSeq: %d/r/n"
"Session: %s/r/n"
"%s"
"%s"
"%s"
"%s"
"/r/n";
}
else if(!strcmp(szName, "PAUSE"))
{
cmdFmt =
"PAUSE %s RTSP/1.0/r/n"
"CSeq: %d/r/n"
"Session: %s/r/n"
"%s"
"%s"
"/r/n";
}
else if(!strcmp(szName, "RECORD"))
{
cmdFmt =
"RECORD %s%s%s RTSP/1.0/r/n"
"CSeq: %d/r/n"
"Session: %s/r/n"
"Range: npt=0-/r/n"
"%s"
"%s"
"/r/n";
}
else if(!strcmp(szName, "SET_PARAMETER"))
{
cmdFmt =
"SET_PARAMETER %s RTSP/1.0/r/n"
"CSeq: %d/r/n"
"Session: %s/r/n"
"%s"
"%s"
"Content-length: %d/r/n/r/n"
"%s: %s/r/n";
}
else if(!strcmp(szName, "GET_PARAMETER"))
{
cmdFmt =
"GET_PARAMETER %s RTSP/1.0/r/n"
"CSeq: %d/r/n"
"Session: %s/r/n"
"%s"
"%s"
"Content-type: text/parameters/r/n"
"Content-length: %d/r/n/r/n"
"%s/r/n";
}
else if(!strcmp(szName, "TEARDOWN"))
{
cmdFmt =
"TEARDOWN %s RTSP/1.0/r/n"
"CSeq: %d/r/n"
"Session: %s/r/n"
"%s"
"%s"
"/r/n";
}
else if(!strcmp(szName, "DESCRIBE"))
{
cmdFmt =
"DESCRIBE %s RTSP/1.0/r/n"
"CSeq: %d/r/n"
"%s"
"%s"
"%s"
#ifdef SUPPORT_REAL_RTSP
REAL_DESCRIBE_HEADERS
#endif
"/r/n";
}
else if(!strcmp(szName, "ANNOUNCE"))
{
cmdFmt =
"ANNOUNCE %s RTSP/1.0/r/n"
"CSeq: %d/r/n"
"Content-Type: application/sdp/r/n"
"%s"
"Content-length: %d/r/n/r/n"
"%s";
}
else if(!strcmp(szName, "SETUP"))
{
cmdFmt =
"%s"
"CSeq: %d/r/n"
"%s"
"%s"
"%s"
"%s"
"/r/n";
}
str = (char*)cmdFmt;
return str;
}
char * GetOptionCmd( char *url )
{
int nlen, iret;
char *ss;
char *s = GetRTSPCmd("OPTIONS");
nlen = strlen(s);
iret = nlen + strlen(url) + strlen(PP_NAME) + 200;
ss = (char*)malloc(iret);
sprintf(ss, s, url, 1, PP_NAME, PP_CRLF);
return ss;
}
char * GetDescribeCmd( char *url )
{
int nlen, iret;
char *ss;
char *s = GetRTSPCmd("DESCRIBE");
nlen = strlen(s);
iret = nlen + strlen(url) + strlen(PP_NAME) + 200;
ss = (char*)malloc(iret);
sprintf(ss, s, url, 1, PP_NAME, PP_CRLF, PP_CRLF);
return ss;
}
char * GetPlayCmd( char *url , char *session, char *range)
{
int nlen, iret;
char *ss;
char *s = GetRTSPCmd("PLAY");
nlen = strlen(s);
iret = nlen + strlen(url) + strlen(PP_NAME) + 200;
ss = (char*)malloc(iret);
//char buf[128] = {0};
//sprintf(buf, "Session: %s/r/n", session);
sprintf(ss, s, url, 1, session, range, PP_NAME, PP_CRLF, PP_CRLF);
return ss;
}
char * GetSetupCmd( char *url , int port1, int port2)
{
int nlen, iret;
char *ss;
char *s = GetRTSPCmd("SETUP");
nlen = strlen(s);
iret = nlen + strlen(url) + strlen(PP_NAME) + 200;
ss = (char*)malloc(iret);
char buf[128] = {0};
char buf2[128] = {0};
if(port1 == 0)
strcpy(buf, "Transport: RTP/AVP/TCP;unicast;interleaved=0-1");
else
sprintf(buf, "Transport: RTP/AVP;unicast;client_port=%d-%d/r/n", ntohs(port2), ntohs(port1));
sprintf(buf2, "SETUP %s/streamid=0 RTSP/1.0/r/n", url);
sprintf(ss, s, buf2, 1, buf, PP_NAME, PP_CRLF, PP_CRLF);
return ss;
}
long PraseURL( const char *url, char *szip, int *iport )
{
long lret = -1;
if(url)
{
//找到了rtsp这个标识符
if(!_strnicmp(url, "rtsp://", 7))
{
//找ip
char *s, *ss;
s = (char*)url + strlen("rtsp://");
ss = strchr(s, '/');
strncpy(szip, s, ss- s);
szip[ss -s] = '/0';
//查找下是否是有端口设置
s = strchr(szip, ':');
//有端口设置
if(s)
{
ss = s;
s ++;
*iport = atoi(s);
//同时修正ip地址
szip[ss - szip] = '/0';
}
else
*iport = 554;
lret = 0;
}
}
return lret;
}
long logwr( void *data, int len )
{
long lret = -1;
if(fp)
lret = fwrite(data, 1, len, fp);
return lret;
}
static char* getLine(char* startOfLine) {
// returns the start of the next line, or NULL if none
for (char* ptr = startOfLine; *ptr != '/0'; ++ptr) {
// Check for the end of line: /r/n (but also accept /r or /n by itself):
if (*ptr == '/r' || *ptr == '/n') {
// We found the end of the line
if (*ptr == '/r') {
*ptr++ = '/0';
if (*ptr == '/n') ++ptr;
} else {
*ptr++ = '/0';
}
return ptr;
}
}
return NULL;
}
long GetResponseCode( const char *sz )
{
long lret = -1;
if(sz)
{
if(sscanf(sz, "%*s%u", &lret) != 1)
;
}
return lret;
}
long PraseDescribeCmd( const char *sz )
{
long lret = -1;
char *ss, *szst;
szst = (char *)sz;
int contentLength = -1;
if(GetResponseCode(sz )== 200)
{
ss= getLine(szst);
while(1)
{
ss = getLine(ss);
if(ss == NULL)
break;
if (sscanf(ss, "Content-Length: %d", &contentLength) == 1
|| sscanf(ss, "Content-length: %d", &contentLength) == 1) {
if (contentLength < 0) {
}
}
}
}
return lret;
}
long PraseSetupCmd( const char *sz , char *sess)
{
long lret = -1;
char *ss, *szst;
szst = (char *)sz;
int contentLength = -1;
if(GetResponseCode(sz )== 200)
{
ss= getLine(szst);
while(1)
{
ss = getLine(ss);
if(ss == NULL)
break;
if (sscanf(ss, "Session: %[^;]", sess) == 1)
{
lret = 1;
}
}
}
return lret;
}