#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("rtsp_log.txt", "w+"); //分配緩沖區; nlen = 10240; buf = (char*)malloc(nlen); //定義要連接的url; //url = "rtsp://192.168.1.43:2554/realmp3.mp3"; url = "rtsp://192.168.10.177/bipbop-gear1-all.ts"; //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); if(iret == SOCKET_ERROR) { int erro = WSAGetLastError(); printf("connect fail !"); Sleep(3000); closesocket(sockin); WSACleanup(); return 0; } //發送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返回的命令串; char szip2[9]; lret = PraseSetupCmd(buf, szip2); char *session, *srange; session = szip2; //發送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("bipbop-gear1-all.ts", "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*)"***Recive:\r\n", 12); 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*)"***Send:\r\n", 10); 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 ="SETUP %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, 2, 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, 4, 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, "\r\nTransport: RTP/AVP/TCP;unicast;interleaved=0-1"); else sprintf(buf, "\r\nTransport: RTP/AVP;unicast;client_port=%d-%d", ntohs(port2), ntohs(port1)); //sprintf(buf2, "SETUP %s/streamid=0 RTSP/1.0\r\n", url); //sprintf(ss, s, buf2, 3, buf, PP_NAME, PP_CRLF, PP_CRLF); sprintf(buf2, "%s/track1 RTSP/1.0\r\n", url); sprintf(ss, s, buf2, 3, PP_NAME, buf, 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: %sP4 192.168.10.177", sess) == 1) { lret = 1; return lret; } } } return lret; }