一个基于JRTPLIB的轻量级RTSP客户端(myRTSPClient)——实现篇:(三)用户接口层之RTSP命令
2017-07-10 23:32 Ansersion 阅读(1311) 评论(0) 编辑 收藏 举报截至版本1.2.3,myRtspClient函数库共支持以下6个RTSP命令(RFC 2326):
(1)OPTIONS
(2)DESCRIBE
(3)SETUP
(4)PLAY
(5)PAUSE
(6)TEARDOWN
对应的接口函数都以“Do”开头,如“DoOPTIONS”。各个接口函数写法相似,大同小异,差异部分会在后续章节做说明,现以DoOPTIONS()和DoPLAY()举例。
一、ErrorType RtspClient::DoOPTIONS(string uri)
1 ErrorType RtspClient::DoOPTIONS(string uri) 2 { 3 string RtspUri(""); 4 int Sockfd = -1; 5 6 if(uri.length() != 0) { 7 RtspUri.assign(uri); 8 RtspURI.assign(uri); 9 } 10 else if(RtspURI.length() != 0) RtspUri.assign(RtspURI); 11 else return RTSP_INVALID_URI; 12 13 Sockfd = CreateTcpSockfd(RtspUri); 14 if(Sockfd < 0) return RTSP_INVALID_URI; 15 16 string Cmd("OPTIONS"); 17 stringstream Msg(""); 18 Msg << Cmd << " " << RtspUri << " " << "RTSP/" << VERSION_RTSP << "\r\n"; 19 Msg << "CSeq: " << ++RtspCSeq << "\r\n"; 20 Msg << "\r\n"; 21 22 if(!SendRTSP(Sockfd, Msg.str())) { 23 Close(Sockfd); 24 return RTSP_SEND_ERROR; 25 } 26 if(!RecvRTSP(Sockfd, &RtspResponse)) { 27 Close(Sockfd); 28 return RTSP_RECV_ERROR; 29 } 30 return RTSP_NO_ERROR; 31 }
第6-11行,获取RTSP的URI(如rtsp://127.0.0.1/ansersion)。先检查DoOPTIONS参数传入的URI,如果不存在就使用类成员变量RtspURI(更多说明见“实现篇(二)二、RtspClient::RtspClient(string uri)”),若RtspURI也不存在则返回失败。如果参数传入URI,该URI同时会保存至类成员变量RtspURI从而记忆住该URI,以便后续调用“DoXXX”RTSP命令函数时,不必再重复传入URI。
第13-14行,根据URI创建socket,用于和RTSP服务端通信(注:CreateTcpSockfd会根据URI中的IP创建socket,如果以前创建过socket且未关闭,则继续沿用旧有socket。截至版本1.2.3,仅支持使用IPv4的URI)。
第16-20行,组建OPTIONS报文(参见RFC2326 10.1)。
第22-25行,向服务端发送OPTIONS报文。
第26-29行,接收服务端返回报文,并将报文赋值给类成员变量RtspResponse(可以通过GetResponse()获取该值)。
第30行,返回成功。
二、ErrorType RtspClient::DoPLAY(MediaSession * media_session)
1 ErrorType RtspClient::DoPLAY(MediaSession * media_session) 2 { 3 if(!media_session) { 4 return RTSP_INVALID_MEDIA_SESSION; 5 } 6 7 ErrorType Err = RTSP_NO_ERROR; 8 int Sockfd = -1; 9 Sockfd = CreateTcpSockfd(); 10 if(Sockfd < 0) return RTSP_INVALID_URI; 11 12 string Cmd("PLAY"); 13 stringstream Msg(""); 14 Msg << Cmd << " " << RtspURI << " " << "RTSP/" << VERSION_RTSP << "\r\n"; 15 Msg << "CSeq: " << ++RtspCSeq << "\r\n"; 16 Msg << "Session: " << media_session->SessionID << "\r\n"; 17 if(Realm.length() > 0 && Nonce.length() > 0) { 18 string RealmTmp = Realm; 19 string NonceTmp = Nonce; 20 string Md5Response = MakeMd5DigestResp(RealmTmp, Cmd, RtspURI, NonceTmp); 21 if(Md5Response.length() != MD5_SIZE) { 22 cout << "Make MD5 digest response error" << endl; 23 return RTSP_RESPONSE_401; 24 } 25 Msg << "Authorization: Digest username=\"" << Username << "\", realm=\"" 26 << RealmTmp << "\", nonce=\"" << NonceTmp << "\", uri=\"" << RtspURI 27 << "\", response=\"" << Md5Response << "\"\r\n"; 28 } 29 Msg << "\r\n"; 30 31 if(RTSP_NO_ERROR == Err && !SendRTSP(Sockfd, Msg.str())) { 32 Close(Sockfd); 33 Sockfd = -1; 34 Err = RTSP_SEND_ERROR; 35 } 36 if(RTSP_NO_ERROR == Err && !RecvRTSP(Sockfd, &RtspResponse)) { 37 Close(Sockfd); 38 Sockfd = -1; 39 Err = RTSP_RECV_ERROR; 40 } 41 return RTSP_NO_ERROR; 42 }
第3-5行,判断media_session是否有效(MediaSession类存有音频或视频相关信息,后续章节将做说明)。
第9-10行,创建socket。
第12-29行,组建PLAY报文(参见RFC2326 10.5),其中第17-28行在报文中加入鉴权信息(参见RFC2069),如果不需要鉴权则可略去。
第31-35行,向服务端发送PLAY报文。
第36-40行,接收服务端返回报文。
example代码中使用的是ErrorType RtspClient::DoPLAY(string media_type),是该函数的封装,参数media_type取“video”或“audio”。