From 604f4cc57bc881f69a60ea484dca312a99657995 Mon Sep 17 00:00:00 2001 From: winlin Date: Mon, 16 Feb 2015 22:15:59 +0800 Subject: [PATCH] for #133, support the rtsp options request and response. --- trunk/src/app/srs_app_http.cpp | 14 +- trunk/src/app/srs_app_http.hpp | 13 +- trunk/src/app/srs_app_http_client.cpp | 14 +- trunk/src/app/srs_app_rtsp.cpp | 23 ++ trunk/src/kernel/srs_kernel_consts.hpp | 120 ++++++- trunk/src/kernel/srs_kernel_error.hpp | 2 + trunk/src/protocol/srs_rtsp_stack.cpp | 448 +++++++++++++++++++++++++ trunk/src/protocol/srs_rtsp_stack.hpp | 248 ++++++++++++++ 8 files changed, 849 insertions(+), 33 deletions(-) diff --git a/trunk/src/app/srs_app_http.cpp b/trunk/src/app/srs_app_http.cpp index ea3158c297..40981d0fd8 100644 --- a/trunk/src/app/srs_app_http.cpp +++ b/trunk/src/app/srs_app_http.cpp @@ -62,7 +62,7 @@ int srs_go_http_response_json(ISrsGoHttpResponseWriter* w, string data) } // get the status text of code. -string srs_generate_status_text(int status) +string srs_generate_http_status_text(int status) { static std::map _status_map; if (_status_map.empty()) { @@ -212,7 +212,7 @@ void SrsGoHttpHeader::write(stringstream& ss) { std::map::iterator it; for (it = headers.begin(); it != headers.end(); ++it) { - ss << it->first << ": " << it->second << __SRS_CRLF; + ss << it->first << ": " << it->second << __SRS_HTTP_CRLF; } } @@ -711,7 +711,7 @@ int SrsGoHttpResponseWriter::final_request() // complete the chunked encoding. if (content_length == -1) { std::stringstream ss; - ss << 0 << __SRS_CRLF << __SRS_CRLF; + ss << 0 << __SRS_HTTP_CRLF << __SRS_HTTP_CRLF; std::string ch = ss.str(); return skt->write((void*)ch.data(), (int)ch.length(), NULL); } @@ -752,7 +752,7 @@ int SrsGoHttpResponseWriter::write(char* data, int size) // send in chunked encoding. std::stringstream ss; - ss << hex << size << __SRS_CRLF; + ss << hex << size << __SRS_HTTP_CRLF; std::string ch = ss.str(); if ((ret = skt->write((void*)ch.data(), (int)ch.length(), NULL)) != ERROR_SUCCESS) { return ret; @@ -760,7 +760,7 @@ int SrsGoHttpResponseWriter::write(char* data, int size) if ((ret = skt->write((void*)data, size, NULL)) != ERROR_SUCCESS) { return ret; } - if ((ret = skt->write((void*)__SRS_CRLF, 2, NULL)) != ERROR_SUCCESS) { + if ((ret = skt->write((void*)__SRS_HTTP_CRLF, 2, NULL)) != ERROR_SUCCESS) { return ret; } @@ -794,7 +794,7 @@ int SrsGoHttpResponseWriter::send_header(char* data, int size) // status_line ss << "HTTP/1.1 " << status << " " - << srs_generate_status_text(status) << __SRS_CRLF; + << srs_generate_http_status_text(status) << __SRS_HTTP_CRLF; // detect content type if (srs_go_http_body_allowd(status)) { @@ -820,7 +820,7 @@ int SrsGoHttpResponseWriter::send_header(char* data, int size) hdr->write(ss); // header_eof - ss << __SRS_CRLF; + ss << __SRS_HTTP_CRLF; std::string buf = ss.str(); return skt->write((void*)buf.c_str(), buf.length(), NULL); diff --git a/trunk/src/app/srs_app_http.hpp b/trunk/src/app/srs_app_http.hpp index 9d85116d22..abc4f29f36 100644 --- a/trunk/src/app/srs_app_http.hpp +++ b/trunk/src/app/srs_app_http.hpp @@ -39,6 +39,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include +#include class SrsRequest; class SrsStSocket; @@ -51,19 +52,19 @@ class ISrsGoHttpResponseWriter; // http specification // CR = -#define __SRS_CR SRS_CONSTS_CR // 0x0D +#define __SRS_HTTP_CR SRS_CONSTS_CR // 0x0D // LF = -#define __SRS_LF SRS_CONSTS_LF // 0x0A +#define __SRS_HTTP_LF SRS_CONSTS_LF // 0x0A // SP = -#define __SRS_SP ' ' // 0x20 +#define __SRS_HTTP_SP ' ' // 0x20 // HT = -#define __SRS_HT '\x09' // 0x09 +#define __SRS_HTTP_HT '\x09' // 0x09 // HTTP/1.1 defines the sequence CR LF as the end-of-line marker for all // protocol elements except the entity-body (see appendix 19.3 for // tolerant applications). -#define __SRS_CRLF "\r\n" // 0x0D0A -#define __SRS_CRLFCRLF "\r\n\r\n" // 0x0D0A0D0A +#define __SRS_HTTP_CRLF "\r\n" // 0x0D0A +#define __SRS_HTTP_CRLFCRLF "\r\n\r\n" // 0x0D0A0D0A // @see SrsHttpMessage._http_ts_send_buffer #define __SRS_HTTP_TS_SEND_BUFFER_SIZE 4096 diff --git a/trunk/src/app/srs_app_http_client.cpp b/trunk/src/app/srs_app_http_client.cpp index d3d57ac306..aeaebdc1c4 100644 --- a/trunk/src/app/srs_app_http_client.cpp +++ b/trunk/src/app/srs_app_http_client.cpp @@ -76,13 +76,13 @@ int SrsHttpClient::post(SrsHttpUri* uri, string req, int& status_code, string& r // POST %s HTTP/1.1\r\nHost: %s\r\nContent-Length: %d\r\n\r\n%s std::stringstream ss; ss << "POST " << uri->get_path() << " " - << "HTTP/1.1" << __SRS_CRLF - << "Host: " << uri->get_host() << __SRS_CRLF - << "Connection: Keep-Alive" << __SRS_CRLF - << "Content-Length: " << std::dec << req.length() << __SRS_CRLF - << "User-Agent: " << RTMP_SIG_SRS_NAME << RTMP_SIG_SRS_VERSION << __SRS_CRLF - << "Content-Type: application/json" << __SRS_CRLF - << __SRS_CRLF + << "HTTP/1.1" << __SRS_HTTP_CRLF + << "Host: " << uri->get_host() << __SRS_HTTP_CRLF + << "Connection: Keep-Alive" << __SRS_HTTP_CRLF + << "Content-Length: " << std::dec << req.length() << __SRS_HTTP_CRLF + << "User-Agent: " << RTMP_SIG_SRS_NAME << RTMP_SIG_SRS_VERSION << __SRS_HTTP_CRLF + << "Content-Type: application/json" << __SRS_HTTP_CRLF + << __SRS_HTTP_CRLF << req; SrsStSocket skt(stfd); diff --git a/trunk/src/app/srs_app_rtsp.cpp b/trunk/src/app/srs_app_rtsp.cpp index 1763363804..7979524043 100644 --- a/trunk/src/app/srs_app_rtsp.cpp +++ b/trunk/src/app/srs_app_rtsp.cpp @@ -32,6 +32,7 @@ using namespace std; #include #include #include +#include #ifdef SRS_AUTO_STREAM_CASTER @@ -76,6 +77,28 @@ int SrsRtspConn::do_cycle() std::string ip = srs_get_peer_ip(st_netfd_fileno(stfd)); srs_trace("rtsp: serve %s", ip.c_str()); + // consume all rtsp messages. + for (;;) { + SrsRtspRequest* req = NULL; + if ((ret = rtsp->recv_message(&req)) != ERROR_SUCCESS) { + if (!srs_is_client_gracefully_close(ret)) { + srs_error("rtsp: recv request failed. ret=%d", ret); + } + return ret; + } + SrsAutoFree(SrsRtspRequest, req); + srs_info("rtsp: got rtsp request"); + + if (req->is_options()) { + if ((ret = rtsp->send_message(new SrsRtspOptionsResponse(req->seq))) != ERROR_SUCCESS) { + if (!srs_is_client_gracefully_close(ret)) { + srs_error("rtsp: send response failed. ret=%d", ret); + } + return ret; + } + } + } + return ret; } diff --git a/trunk/src/kernel/srs_kernel_consts.hpp b/trunk/src/kernel/srs_kernel_consts.hpp index 0f6bb4c85a..39c07b13b1 100644 --- a/trunk/src/kernel/srs_kernel_consts.hpp +++ b/trunk/src/kernel/srs_kernel_consts.hpp @@ -265,43 +265,137 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define SRS_CONSTS_HTTP_Continue_str "Continue" #define SRS_CONSTS_HTTP_SwitchingProtocols_str "Switching Protocols" #define SRS_CONSTS_HTTP_OK_str "OK" -#define SRS_CONSTS_HTTP_Created_str "Created " +#define SRS_CONSTS_HTTP_Created_str "Created" #define SRS_CONSTS_HTTP_Accepted_str "Accepted" -#define SRS_CONSTS_HTTP_NonAuthoritativeInformation_str "Non Authoritative Information " -#define SRS_CONSTS_HTTP_NoContent_str "No Content " +#define SRS_CONSTS_HTTP_NonAuthoritativeInformation_str "Non Authoritative Information" +#define SRS_CONSTS_HTTP_NoContent_str "No Content" #define SRS_CONSTS_HTTP_ResetContent_str "Reset Content" #define SRS_CONSTS_HTTP_PartialContent_str "Partial Content" -#define SRS_CONSTS_HTTP_MultipleChoices_str "Multiple Choices " +#define SRS_CONSTS_HTTP_MultipleChoices_str "Multiple Choices" #define SRS_CONSTS_HTTP_MovedPermanently_str "Moved Permanently" #define SRS_CONSTS_HTTP_Found_str "Found" #define SRS_CONSTS_HTTP_SeeOther_str "See Other" -#define SRS_CONSTS_HTTP_NotModified_str "Not Modified " +#define SRS_CONSTS_HTTP_NotModified_str "Not Modified" #define SRS_CONSTS_HTTP_UseProxy_str "Use Proxy" -#define SRS_CONSTS_HTTP_TemporaryRedirect_str "Temporary Redirect " +#define SRS_CONSTS_HTTP_TemporaryRedirect_str "Temporary Redirect" #define SRS_CONSTS_HTTP_BadRequest_str "Bad Request" #define SRS_CONSTS_HTTP_Unauthorized_str "Unauthorized" -#define SRS_CONSTS_HTTP_PaymentRequired_str "Payment Required " -#define SRS_CONSTS_HTTP_Forbidden_str "Forbidden " +#define SRS_CONSTS_HTTP_PaymentRequired_str "Payment Required" +#define SRS_CONSTS_HTTP_Forbidden_str "Forbidden" #define SRS_CONSTS_HTTP_NotFound_str "Not Found" #define SRS_CONSTS_HTTP_MethodNotAllowed_str "Method Not Allowed" -#define SRS_CONSTS_HTTP_NotAcceptable_str "Not Acceptable " -#define SRS_CONSTS_HTTP_ProxyAuthenticationRequired_str "Proxy Authentication Required " +#define SRS_CONSTS_HTTP_NotAcceptable_str "Not Acceptable" +#define SRS_CONSTS_HTTP_ProxyAuthenticationRequired_str "Proxy Authentication Required" #define SRS_CONSTS_HTTP_RequestTimeout_str "Request Timeout" #define SRS_CONSTS_HTTP_Conflict_str "Conflict" #define SRS_CONSTS_HTTP_Gone_str "Gone" #define SRS_CONSTS_HTTP_LengthRequired_str "Length Required" #define SRS_CONSTS_HTTP_PreconditionFailed_str "Precondition Failed" -#define SRS_CONSTS_HTTP_RequestEntityTooLarge_str "Request Entity Too Large " +#define SRS_CONSTS_HTTP_RequestEntityTooLarge_str "Request Entity Too Large" #define SRS_CONSTS_HTTP_RequestURITooLarge_str "Request URI Too Large" #define SRS_CONSTS_HTTP_UnsupportedMediaType_str "Unsupported Media Type" #define SRS_CONSTS_HTTP_RequestedRangeNotSatisfiable_str "Requested Range Not Satisfiable" -#define SRS_CONSTS_HTTP_ExpectationFailed_str "Expectation Failed " -#define SRS_CONSTS_HTTP_InternalServerError_str "Internal Server Error " +#define SRS_CONSTS_HTTP_ExpectationFailed_str "Expectation Failed" +#define SRS_CONSTS_HTTP_InternalServerError_str "Internal Server Error" #define SRS_CONSTS_HTTP_NotImplemented_str "Not Implemented" #define SRS_CONSTS_HTTP_BadGateway_str "Bad Gateway" #define SRS_CONSTS_HTTP_ServiceUnavailable_str "Service Unavailable" #define SRS_CONSTS_HTTP_GatewayTimeout_str "Gateway Timeout" #define SRS_CONSTS_HTTP_HTTPVersionNotSupported_str "HTTP Version Not Supported" +/////////////////////////////////////////////////////////// +// RTSP consts values +/////////////////////////////////////////////////////////// +// 7.1.1 Status Code and Reason Phrase +#define SRS_CONSTS_RTSP_Continue 100 +#define SRS_CONSTS_RTSP_OK 200 +#define SRS_CONSTS_RTSP_Created 201 +#define SRS_CONSTS_RTSP_LowOnStorageSpace 250 +#define SRS_CONSTS_RTSP_MultipleChoices 300 +#define SRS_CONSTS_RTSP_MovedPermanently 301 +#define SRS_CONSTS_RTSP_MovedTemporarily 302 +#define SRS_CONSTS_RTSP_SeeOther 303 +#define SRS_CONSTS_RTSP_NotModified 304 +#define SRS_CONSTS_RTSP_UseProxy 305 +#define SRS_CONSTS_RTSP_BadRequest 400 +#define SRS_CONSTS_RTSP_Unauthorized 401 +#define SRS_CONSTS_RTSP_PaymentRequired 402 +#define SRS_CONSTS_RTSP_Forbidden 403 +#define SRS_CONSTS_RTSP_NotFound 404 +#define SRS_CONSTS_RTSP_MethodNotAllowed 405 +#define SRS_CONSTS_RTSP_NotAcceptable 406 +#define SRS_CONSTS_RTSP_ProxyAuthenticationRequired 407 +#define SRS_CONSTS_RTSP_RequestTimeout 408 +#define SRS_CONSTS_RTSP_Gone 410 +#define SRS_CONSTS_RTSP_LengthRequired 411 +#define SRS_CONSTS_RTSP_PreconditionFailed 412 +#define SRS_CONSTS_RTSP_RequestEntityTooLarge 413 +#define SRS_CONSTS_RTSP_RequestURITooLarge 414 +#define SRS_CONSTS_RTSP_UnsupportedMediaType 415 +#define SRS_CONSTS_RTSP_ParameterNotUnderstood 451 +#define SRS_CONSTS_RTSP_ConferenceNotFound 452 +#define SRS_CONSTS_RTSP_NotEnoughBandwidth 453 +#define SRS_CONSTS_RTSP_SessionNotFound 454 +#define SRS_CONSTS_RTSP_MethodNotValidInThisState 455 +#define SRS_CONSTS_RTSP_HeaderFieldNotValidForResource 456 +#define SRS_CONSTS_RTSP_InvalidRange 457 +#define SRS_CONSTS_RTSP_ParameterIsReadOnly 458 +#define SRS_CONSTS_RTSP_AggregateOperationNotAllowed 459 +#define SRS_CONSTS_RTSP_OnlyAggregateOperationAllowed 460 +#define SRS_CONSTS_RTSP_UnsupportedTransport 461 +#define SRS_CONSTS_RTSP_DestinationUnreachable 462 +#define SRS_CONSTS_RTSP_InternalServerError 500 +#define SRS_CONSTS_RTSP_NotImplemented 501 +#define SRS_CONSTS_RTSP_BadGateway 502 +#define SRS_CONSTS_RTSP_ServiceUnavailable 503 +#define SRS_CONSTS_RTSP_GatewayTimeout 504 +#define SRS_CONSTS_RTSP_RTSPVersionNotSupported 505 +#define SRS_CONSTS_RTSP_OptionNotSupported 551 + +#define SRS_CONSTS_RTSP_Continue_str "Continue" +#define SRS_CONSTS_RTSP_OK_str "OK" +#define SRS_CONSTS_RTSP_Created_str "Created" +#define SRS_CONSTS_RTSP_LowOnStorageSpace_str "Low on Storage Space" +#define SRS_CONSTS_RTSP_MultipleChoices_str "Multiple Choices" +#define SRS_CONSTS_RTSP_MovedPermanently_str "Moved Permanently" +#define SRS_CONSTS_RTSP_MovedTemporarily_str "Moved Temporarily" +#define SRS_CONSTS_RTSP_SeeOther_str "See Other" +#define SRS_CONSTS_RTSP_NotModified_str "Not Modified" +#define SRS_CONSTS_RTSP_UseProxy_str "Use Proxy" +#define SRS_CONSTS_RTSP_BadRequest_str "Bad Request" +#define SRS_CONSTS_RTSP_Unauthorized_str "Unauthorized" +#define SRS_CONSTS_RTSP_PaymentRequired_str "Payment Required" +#define SRS_CONSTS_RTSP_Forbidden_str "Forbidden" +#define SRS_CONSTS_RTSP_NotFound_str "Not Found" +#define SRS_CONSTS_RTSP_MethodNotAllowed_str "Method Not Allowed" +#define SRS_CONSTS_RTSP_NotAcceptable_str "Not Acceptable" +#define SRS_CONSTS_RTSP_ProxyAuthenticationRequired_str "Proxy Authentication Required" +#define SRS_CONSTS_RTSP_RequestTimeout_str "Request Timeout" +#define SRS_CONSTS_RTSP_Gone_str "Gone" +#define SRS_CONSTS_RTSP_LengthRequired_str "Length Required" +#define SRS_CONSTS_RTSP_PreconditionFailed_str "Precondition Failed" +#define SRS_CONSTS_RTSP_RequestEntityTooLarge_str "Request Entity Too Large" +#define SRS_CONSTS_RTSP_RequestURITooLarge_str "Request URI Too Large" +#define SRS_CONSTS_RTSP_UnsupportedMediaType_str "Unsupported Media Type" +#define SRS_CONSTS_RTSP_ParameterNotUnderstood_str "Invalid parameter" +#define SRS_CONSTS_RTSP_ConferenceNotFound_str "Illegal Conference Identifier" +#define SRS_CONSTS_RTSP_NotEnoughBandwidth_str "Not Enough Bandwidth" +#define SRS_CONSTS_RTSP_SessionNotFound_str "Session Not Found" +#define SRS_CONSTS_RTSP_MethodNotValidInThisState_str "Method Not Valid In This State" +#define SRS_CONSTS_RTSP_HeaderFieldNotValidForResource_str "Header Field Not Valid" +#define SRS_CONSTS_RTSP_InvalidRange_str "Invalid Range" +#define SRS_CONSTS_RTSP_ParameterIsReadOnly_str "Parameter Is Read-Only" +#define SRS_CONSTS_RTSP_AggregateOperationNotAllowed_str "Aggregate Operation Not Allowed" +#define SRS_CONSTS_RTSP_OnlyAggregateOperationAllowed_str "Only Aggregate Operation Allowed" +#define SRS_CONSTS_RTSP_UnsupportedTransport_str "Unsupported Transport" +#define SRS_CONSTS_RTSP_DestinationUnreachable_str "Destination Unreachable" +#define SRS_CONSTS_RTSP_InternalServerError_str "Internal Server Error" +#define SRS_CONSTS_RTSP_NotImplemented_str "Not Implemented" +#define SRS_CONSTS_RTSP_BadGateway_str "Bad Gateway" +#define SRS_CONSTS_RTSP_ServiceUnavailable_str "Service Unavailable" +#define SRS_CONSTS_RTSP_GatewayTimeout_str "Gateway Timeout" +#define SRS_CONSTS_RTSP_RTSPVersionNotSupported_str "RTSP Version Not Supported" +#define SRS_CONSTS_RTSP_OptionNotSupported_str "Option not support" + #endif diff --git a/trunk/src/kernel/srs_kernel_error.hpp b/trunk/src/kernel/srs_kernel_error.hpp index 32b9202ff1..ce2a42bbf4 100644 --- a/trunk/src/kernel/srs_kernel_error.hpp +++ b/trunk/src/kernel/srs_kernel_error.hpp @@ -142,6 +142,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define ERROR_OpenSslComputeSharedKey 2039 #define ERROR_RTMP_MIC_CHUNKSIZE_CHANGED 2040 #define ERROR_RTMP_MIC_CACHE_OVERFLOW 2041 +#define ERROR_RTSP_TOKEN_NOT_NORMAL 2042 +#define ERROR_RTSP_REQUEST_HEADER_EOF 2043 // // system control message, // not an error, but special control logic. diff --git a/trunk/src/protocol/srs_rtsp_stack.cpp b/trunk/src/protocol/srs_rtsp_stack.cpp index 220e9a3931..c1805a23a2 100644 --- a/trunk/src/protocol/srs_rtsp_stack.cpp +++ b/trunk/src/protocol/srs_rtsp_stack.cpp @@ -23,17 +23,465 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include +#include +#include +using namespace std; + #include +#include +#include +#include +#include #ifdef SRS_AUTO_STREAM_CASTER +#define __SRS_RTSP_BUFFER 4096 + +// get the status text of code. +string srs_generate_rtsp_status_text(int status) +{ + static std::map _status_map; + if (_status_map.empty()) { + _status_map[SRS_CONSTS_RTSP_Continue ] = SRS_CONSTS_RTSP_Continue_str ; + _status_map[SRS_CONSTS_RTSP_OK ] = SRS_CONSTS_RTSP_OK_str ; + _status_map[SRS_CONSTS_RTSP_Created ] = SRS_CONSTS_RTSP_Created_str ; + _status_map[SRS_CONSTS_RTSP_LowOnStorageSpace ] = SRS_CONSTS_RTSP_LowOnStorageSpace_str ; + _status_map[SRS_CONSTS_RTSP_MultipleChoices ] = SRS_CONSTS_RTSP_MultipleChoices_str ; + _status_map[SRS_CONSTS_RTSP_MovedPermanently ] = SRS_CONSTS_RTSP_MovedPermanently_str ; + _status_map[SRS_CONSTS_RTSP_MovedTemporarily ] = SRS_CONSTS_RTSP_MovedTemporarily_str ; + _status_map[SRS_CONSTS_RTSP_SeeOther ] = SRS_CONSTS_RTSP_SeeOther_str ; + _status_map[SRS_CONSTS_RTSP_NotModified ] = SRS_CONSTS_RTSP_NotModified_str ; + _status_map[SRS_CONSTS_RTSP_UseProxy ] = SRS_CONSTS_RTSP_UseProxy_str ; + _status_map[SRS_CONSTS_RTSP_BadRequest ] = SRS_CONSTS_RTSP_BadRequest_str ; + _status_map[SRS_CONSTS_RTSP_Unauthorized ] = SRS_CONSTS_RTSP_Unauthorized_str ; + _status_map[SRS_CONSTS_RTSP_PaymentRequired ] = SRS_CONSTS_RTSP_PaymentRequired_str ; + _status_map[SRS_CONSTS_RTSP_Forbidden ] = SRS_CONSTS_RTSP_Forbidden_str ; + _status_map[SRS_CONSTS_RTSP_NotFound ] = SRS_CONSTS_RTSP_NotFound_str ; + _status_map[SRS_CONSTS_RTSP_MethodNotAllowed ] = SRS_CONSTS_RTSP_MethodNotAllowed_str ; + _status_map[SRS_CONSTS_RTSP_NotAcceptable ] = SRS_CONSTS_RTSP_NotAcceptable_str ; + _status_map[SRS_CONSTS_RTSP_ProxyAuthenticationRequired ] = SRS_CONSTS_RTSP_ProxyAuthenticationRequired_str ; + _status_map[SRS_CONSTS_RTSP_RequestTimeout ] = SRS_CONSTS_RTSP_RequestTimeout_str ; + _status_map[SRS_CONSTS_RTSP_Gone ] = SRS_CONSTS_RTSP_Gone_str ; + _status_map[SRS_CONSTS_RTSP_LengthRequired ] = SRS_CONSTS_RTSP_LengthRequired_str ; + _status_map[SRS_CONSTS_RTSP_PreconditionFailed ] = SRS_CONSTS_RTSP_PreconditionFailed_str ; + _status_map[SRS_CONSTS_RTSP_RequestEntityTooLarge ] = SRS_CONSTS_RTSP_RequestEntityTooLarge_str ; + _status_map[SRS_CONSTS_RTSP_RequestURITooLarge ] = SRS_CONSTS_RTSP_RequestURITooLarge_str ; + _status_map[SRS_CONSTS_RTSP_UnsupportedMediaType ] = SRS_CONSTS_RTSP_UnsupportedMediaType_str ; + _status_map[SRS_CONSTS_RTSP_ParameterNotUnderstood ] = SRS_CONSTS_RTSP_ParameterNotUnderstood_str ; + _status_map[SRS_CONSTS_RTSP_ConferenceNotFound ] = SRS_CONSTS_RTSP_ConferenceNotFound_str ; + _status_map[SRS_CONSTS_RTSP_NotEnoughBandwidth ] = SRS_CONSTS_RTSP_NotEnoughBandwidth_str ; + _status_map[SRS_CONSTS_RTSP_SessionNotFound ] = SRS_CONSTS_RTSP_SessionNotFound_str ; + _status_map[SRS_CONSTS_RTSP_MethodNotValidInThisState ] = SRS_CONSTS_RTSP_MethodNotValidInThisState_str ; + _status_map[SRS_CONSTS_RTSP_HeaderFieldNotValidForResource ] = SRS_CONSTS_RTSP_HeaderFieldNotValidForResource_str ; + _status_map[SRS_CONSTS_RTSP_InvalidRange ] = SRS_CONSTS_RTSP_InvalidRange_str ; + _status_map[SRS_CONSTS_RTSP_ParameterIsReadOnly ] = SRS_CONSTS_RTSP_ParameterIsReadOnly_str ; + _status_map[SRS_CONSTS_RTSP_AggregateOperationNotAllowed ] = SRS_CONSTS_RTSP_AggregateOperationNotAllowed_str ; + _status_map[SRS_CONSTS_RTSP_OnlyAggregateOperationAllowed ] = SRS_CONSTS_RTSP_OnlyAggregateOperationAllowed_str ; + _status_map[SRS_CONSTS_RTSP_UnsupportedTransport ] = SRS_CONSTS_RTSP_UnsupportedTransport_str ; + _status_map[SRS_CONSTS_RTSP_DestinationUnreachable ] = SRS_CONSTS_RTSP_DestinationUnreachable_str ; + _status_map[SRS_CONSTS_RTSP_InternalServerError ] = SRS_CONSTS_RTSP_InternalServerError_str ; + _status_map[SRS_CONSTS_RTSP_NotImplemented ] = SRS_CONSTS_RTSP_NotImplemented_str ; + _status_map[SRS_CONSTS_RTSP_BadGateway ] = SRS_CONSTS_RTSP_BadGateway_str ; + _status_map[SRS_CONSTS_RTSP_ServiceUnavailable ] = SRS_CONSTS_RTSP_ServiceUnavailable_str ; + _status_map[SRS_CONSTS_RTSP_GatewayTimeout ] = SRS_CONSTS_RTSP_GatewayTimeout_str ; + _status_map[SRS_CONSTS_RTSP_RTSPVersionNotSupported ] = SRS_CONSTS_RTSP_RTSPVersionNotSupported_str ; + _status_map[SRS_CONSTS_RTSP_OptionNotSupported ] = SRS_CONSTS_RTSP_OptionNotSupported_str ; + } + + std::string status_text; + if (_status_map.find(status) == _status_map.end()) { + status_text = "Status Unknown"; + } else { + status_text = _status_map[status]; + } + + return status_text; +} + +std::string srs_generate_rtsp_method_str(SrsRtspMethod method) +{ + switch (method) { + case SrsRtspMethodDescribe: return __SRS_METHOD_DESCRIBE; + case SrsRtspMethodAnnounce: return __SRS_METHOD_ANNOUNCE; + case SrsRtspMethodGetParameter: return __SRS_METHOD_GET_PARAMETER; + case SrsRtspMethodOptions: return __SRS_METHOD_OPTIONS; + case SrsRtspMethodPause: return __SRS_METHOD_PAUSE; + case SrsRtspMethodPlay: return __SRS_METHOD_PLAY; + case SrsRtspMethodRecord: return __SRS_METHOD_RECORD; + case SrsRtspMethodRedirect: return __SRS_METHOD_REDIRECT; + case SrsRtspMethodSetup: return __SRS_METHOD_SETUP; + case SrsRtspMethodSetParameter: return __SRS_METHOD_SET_PARAMETER; + case SrsRtspMethodTeardown: return __SRS_METHOD_TEARDOWN; + default: return "Unknown"; + } +} + +SrsRtspRequest::SrsRtspRequest() +{ + seq = 0; +} + +SrsRtspRequest::~SrsRtspRequest() +{ +} + +bool SrsRtspRequest::is_options() +{ + return method == __SRS_METHOD_OPTIONS; +} + +SrsRtspResponse::SrsRtspResponse(int cseq) +{ + seq = cseq; + status = SRS_CONSTS_RTSP_OK; +} + +SrsRtspResponse::~SrsRtspResponse() +{ +} + +stringstream& SrsRtspResponse::encode(stringstream& ss) +{ + // status line + ss << __SRS_VERSION << __SRS_RTSP_SP + << status << __SRS_RTSP_SP + << srs_generate_rtsp_status_text(status) << __SRS_RTSP_CRLF; + + // cseq + ss << __SRS_TOKEN_CSEQ << ":" << __SRS_RTSP_SP << seq << __SRS_RTSP_CRLF; + + // others. + ss << "Cache-Control: no-store" << __SRS_RTSP_CRLF + << "Pragma: no-cache" << __SRS_RTSP_CRLF + << "Server: " << RTMP_SIG_SRS_SERVER << __SRS_RTSP_CRLF; + + return ss; +} + +SrsRtspOptionsResponse::SrsRtspOptionsResponse(int cseq) : SrsRtspResponse(cseq) +{ + methods = (SrsRtspMethod)(SrsRtspMethodDescribe | SrsRtspMethodOptions + | SrsRtspMethodPause | SrsRtspMethodPlay | SrsRtspMethodSetup | SrsRtspMethodTeardown + | SrsRtspMethodAnnounce | SrsRtspMethodRecord); +} + +SrsRtspOptionsResponse::~SrsRtspOptionsResponse() +{ +} + +stringstream& SrsRtspOptionsResponse::encode(stringstream& ss) +{ + SrsRtspResponse::encode(ss); + + SrsRtspMethod __methods[] = { + SrsRtspMethodDescribe, + SrsRtspMethodAnnounce, + SrsRtspMethodGetParameter, + SrsRtspMethodOptions, + SrsRtspMethodPause, + SrsRtspMethodPlay, + SrsRtspMethodRecord, + SrsRtspMethodRedirect, + SrsRtspMethodSetup, + SrsRtspMethodSetParameter, + SrsRtspMethodTeardown, + }; + + ss << __SRS_TOKEN_PUBLIC << ":" << __SRS_RTSP_SP; + + bool appended = false; + int nb_methods = (int)(sizeof(__methods) / sizeof(SrsRtspMethod)); + for (int i = 0; i < nb_methods; i++) { + SrsRtspMethod method = __methods[i]; + if (((int)methods & (int)method) != (int)method) { + continue; + } + + if (appended) { + ss << ", "; + } + ss << srs_generate_rtsp_method_str(method); + appended = true; + } + ss << __SRS_RTSP_CRLF; + + // eof header. + ss << __SRS_RTSP_CRLF; + + return ss; +} + SrsRtspStack::SrsRtspStack(ISrsProtocolReaderWriter* s) { + buf = new SrsSimpleBuffer(); skt = s; } SrsRtspStack::~SrsRtspStack() { + srs_freep(buf); +} + +int SrsRtspStack::recv_message(SrsRtspRequest** preq) +{ + int ret = ERROR_SUCCESS; + + SrsRtspRequest* req = new SrsRtspRequest(); + if ((ret = do_recv_message(req)) != ERROR_SUCCESS) { + srs_freep(req); + return ret; + } + + *preq = req; + + return ret; +} + +int SrsRtspStack::send_message(SrsRtspResponse* res) +{ + int ret = ERROR_SUCCESS; + + std::stringstream ss; + // encode the message to string. + res->encode(ss); + + std::string str = ss.str(); + srs_assert(!str.empty()); + + if ((ret = skt->write((char*)str.c_str(), (int)str.length(), NULL)) != ERROR_SUCCESS) { + if (!srs_is_client_gracefully_close(ret)) { + srs_error("rtsp: send response failed. ret=%d", ret); + } + return ret; + } + srs_info("rtsp: send response ok"); + + return ret; +} + +int SrsRtspStack::do_recv_message(SrsRtspRequest* req) +{ + int ret = ERROR_SUCCESS; + + // parse request line. + if ((ret = recv_token_normal(req->method)) != ERROR_SUCCESS) { + if (!srs_is_client_gracefully_close(ret)) { + srs_error("rtsp: parse method failed. ret=%d", ret); + } + return ret; + } + + if ((ret = recv_token_normal(req->uri)) != ERROR_SUCCESS) { + if (!srs_is_client_gracefully_close(ret)) { + srs_error("rtsp: parse uri failed. ret=%d", ret); + } + return ret; + } + + if ((ret = recv_token_eof(req->version)) != ERROR_SUCCESS) { + if (!srs_is_client_gracefully_close(ret)) { + srs_error("rtsp: parse version failed. ret=%d", ret); + } + return ret; + } + + // parse headers. + for (;;) { + // parse the header name + std::string token; + if ((ret = recv_token_normal(token)) != ERROR_SUCCESS) { + if (ret == ERROR_RTSP_REQUEST_HEADER_EOF) { + ret = ERROR_SUCCESS; + srs_info("rtsp: message header parsed"); + break; + } + if (!srs_is_client_gracefully_close(ret)) { + srs_error("rtsp: parse token failed. ret=%d", ret); + } + return ret; + } + + // parse the header value according by header name + if (token == __SRS_TOKEN_CSEQ) { + std::string seq; + if ((ret = recv_token_eof(seq)) != ERROR_SUCCESS) { + if (!srs_is_client_gracefully_close(ret)) { + srs_error("rtsp: parse seq failed. ret=%d", ret); + } + return ret; + } + req->seq = ::atoi(seq.c_str()); + } else { + // unknown header name, parse util EOF. + SrsRtspTokenState state = SrsRtspTokenStateNormal; + while (state == SrsRtspTokenStateNormal) { + std::string value; + if ((ret = recv_token(value, state)) != ERROR_SUCCESS) { + if (!srs_is_client_gracefully_close(ret)) { + srs_error("rtsp: parse token failed. ret=%d", ret); + } + return ret; + } + srs_trace("rtsp: ignore header %s=%s", token.c_str(), value.c_str()); + } + } + } + + // parse body. + + return ret; +} + +int SrsRtspStack::recv_token_normal(std::string& token) +{ + int ret = ERROR_SUCCESS; + + SrsRtspTokenState state; + + if ((ret = recv_token(token, state)) != ERROR_SUCCESS) { + if (ret == ERROR_RTSP_REQUEST_HEADER_EOF) { + return ret; + } + if (!srs_is_client_gracefully_close(ret)) { + srs_error("rtsp: parse token failed. ret=%d", ret); + } + return ret; + } + + if (state != SrsRtspTokenStateNormal) { + ret = ERROR_RTSP_TOKEN_NOT_NORMAL; + srs_error("rtsp: parse normal token failed, state=%d. ret=%d", state, ret); + return ret; + } + + return ret; +} + +int SrsRtspStack::recv_token_eof(std::string& token) +{ + int ret = ERROR_SUCCESS; + + SrsRtspTokenState state; + + if ((ret = recv_token(token, state)) != ERROR_SUCCESS) { + if (ret == ERROR_RTSP_REQUEST_HEADER_EOF) { + return ret; + } + if (!srs_is_client_gracefully_close(ret)) { + srs_error("rtsp: parse token failed. ret=%d", ret); + } + return ret; + } + + if (state != SrsRtspTokenStateEOF) { + ret = ERROR_RTSP_TOKEN_NOT_NORMAL; + srs_error("rtsp: parse eof token failed, state=%d. ret=%d", state, ret); + return ret; + } + + return ret; +} + +int SrsRtspStack::recv_token_util_eof(std::string& token) +{ + int ret = ERROR_SUCCESS; + + SrsRtspTokenState state; + + // use 0x00 as ignore the normal token flag. + if ((ret = recv_token(token, state, 0x00)) != ERROR_SUCCESS) { + if (ret == ERROR_RTSP_REQUEST_HEADER_EOF) { + return ret; + } + if (!srs_is_client_gracefully_close(ret)) { + srs_error("rtsp: parse token failed. ret=%d", ret); + } + return ret; + } + + if (state != SrsRtspTokenStateEOF) { + ret = ERROR_RTSP_TOKEN_NOT_NORMAL; + srs_error("rtsp: parse eof token failed, state=%d. ret=%d", state, ret); + return ret; + } + + return ret; +} + +int SrsRtspStack::recv_token(std::string& token, SrsRtspTokenState& state, char normal_ch) +{ + int ret = ERROR_SUCCESS; + + // whatever, default to error state. + state = SrsRtspTokenStateError; + + // when buffer is empty, append bytes first. + bool append_bytes = buf->length() == 0; + + // parse util token. + for (;;) { + // append bytes if required. + if (append_bytes) { + append_bytes = false; + + char buffer[__SRS_RTSP_BUFFER]; + ssize_t nb_read = 0; + if ((ret = skt->read(buffer, __SRS_RTSP_BUFFER, &nb_read)) != ERROR_SUCCESS) { + if (!srs_is_client_gracefully_close(ret)) { + srs_error("rtsp: io read failed. ret=%d", ret); + } + return ret; + } + srs_info("rtsp: io read %d bytes", nb_read); + + buf->append(buffer, nb_read); + } + + // parse one by one. + char* start = buf->bytes(); + char* end = start + buf->length(); + char* p = start; + + // find util SP/CR/LF, max 2 EOF, to finger out the EOF of message. + for (; p < end && p[0] != normal_ch && p[0] != __SRS_RTSP_CR && p[0] != __SRS_RTSP_LF; p++) { + } + + // matched. + if (p < end) { + // finger out the state. + if (p[0] == normal_ch) { + state = SrsRtspTokenStateNormal; + } else { + state = SrsRtspTokenStateEOF; + } + + // got the token. + int nb_token = p - start; + // trim last ':' character. + if (nb_token && p[-1] == ':') { + nb_token--; + } + if (nb_token) { + token.append(start, nb_token); + } else { + ret = ERROR_RTSP_REQUEST_HEADER_EOF; + } + + // ignore SP/CR/LF + for (int i = 0; i < 2 && p < end && (p[0] == normal_ch || p[0] == __SRS_RTSP_CR || p[0] == __SRS_RTSP_LF); p++, i++) { + } + + // consume the token bytes. + srs_assert(p - start); + buf->erase(p - start); + break; + } + + // append more and parse again. + append_bytes = true; + } + + return ret; } #endif diff --git a/trunk/src/protocol/srs_rtsp_stack.hpp b/trunk/src/protocol/srs_rtsp_stack.hpp index a011e7c699..a5184f95cc 100644 --- a/trunk/src/protocol/srs_rtsp_stack.hpp +++ b/trunk/src/protocol/srs_rtsp_stack.hpp @@ -30,16 +30,223 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include +#include +#include + +#include + #ifdef SRS_AUTO_STREAM_CASTER +class SrsSimpleBuffer; class ISrsProtocolReaderWriter; +// rtsp specification +// CR = +#define __SRS_RTSP_CR SRS_CONSTS_CR // 0x0D +// LF = +#define __SRS_RTSP_LF SRS_CONSTS_LF // 0x0A +// SP = +#define __SRS_RTSP_SP ' ' // 0x20 + +// 4 RTSP Message +// Lines are terminated by CRLF, but +// receivers should be prepared to also interpret CR and LF by +// themselves as line terminators. +#define __SRS_RTSP_CRLF "\r\n" // 0x0D0A +#define __SRS_RTSP_CRLFCRLF "\r\n\r\n" // 0x0D0A0D0A + +// RTSP token +#define __SRS_TOKEN_CSEQ "CSeq" +#define __SRS_TOKEN_PUBLIC "Public" + +// RTSP methods +#define __SRS_METHOD_OPTIONS "OPTIONS" +#define __SRS_METHOD_DESCRIBE "DESCRIBE" +#define __SRS_METHOD_ANNOUNCE "ANNOUNCE" +#define __SRS_METHOD_SETUP "SETUP" +#define __SRS_METHOD_PLAY "PLAY" +#define __SRS_METHOD_PAUSE "PAUSE" +#define __SRS_METHOD_TEARDOWN "TEARDOWN" +#define __SRS_METHOD_GET_PARAMETER "GET_PARAMETER" +#define __SRS_METHOD_SET_PARAMETER "SET_PARAMETER" +#define __SRS_METHOD_REDIRECT "REDIRECT" +#define __SRS_METHOD_RECORD "RECORD" +// Embedded (Interleaved) Binary Data + +// RTSP-Version +#define __SRS_VERSION "RTSP/1.0" + +/** +* the rtsp request message. +* 6 Request +* A request message from a client to a server or vice versa includes, +* within the first line of that message, the method to be applied to +* the resource, the identifier of the resource, and the protocol +* version in use. +* Request = Request-Line ; Section 6.1 +* *( general-header ; Section 5 +* | request-header ; Section 6.2 +* | entity-header ) ; Section 8.1 +* CRLF +* [ message-body ] ; Section 4.3 +*/ +class SrsRtspRequest +{ +public: + /** + * 6.1 Request Line + * Request-Line = Method SP Request-URI SP RTSP-Version CRLF + */ + std::string method; + std::string uri; + std::string version; + /** + * 12.17 CSeq + * The CSeq field specifies the sequence number for an RTSP requestresponse + * pair. This field MUST be present in all requests and + * responses. For every RTSP request containing the given sequence + * number, there will be a corresponding response having the same + * number. Any retransmitted request must contain the same sequence + * number as the original (i.e. the sequence number is not incremented + * for retransmissions of the same request). + */ + int seq; +public: + SrsRtspRequest(); + virtual ~SrsRtspRequest(); +public: + virtual bool is_options(); +}; + +/** +* the rtsp response message. +* 7 Response +* [H6] applies except that HTTP-Version is replaced by RTSP-Version. +* Also, RTSP defines additional status codes and does not define some +* HTTP codes. The valid response codes and the methods they can be used +* with are defined in Table 1. +* After receiving and interpreting a request message, the recipient +* responds with an RTSP response message. +* Response = Status-Line ; Section 7.1 +* *( general-header ; Section 5 +* | response-header ; Section 7.1.2 +* | entity-header ) ; Section 8.1 +* CRLF +* [ message-body ] ; Section 4.3 +*/ +class SrsRtspResponse +{ +public: + /** + * 7.1 Status-Line + * The first line of a Response message is the Status-Line, consisting + * of the protocol version followed by a numeric status code, and the + * textual phrase associated with the status code, with each element + * separated by SP characters. No CR or LF is allowed except in the + * final CRLF sequence. + * Status-Line = RTSP-Version SP Status-Code SP Reason-Phrase CRLF + */ + // @see about the version of rtsp, see __SRS_VERSION + // @see about the status of rtsp, see SRS_CONSTS_RTSP_OK + int status; + /** + * 12.17 CSeq + * The CSeq field specifies the sequence number for an RTSP requestresponse + * pair. This field MUST be present in all requests and + * responses. For every RTSP request containing the given sequence + * number, there will be a corresponding response having the same + * number. Any retransmitted request must contain the same sequence + * number as the original (i.e. the sequence number is not incremented + * for retransmissions of the same request). + */ + int seq; +public: + SrsRtspResponse(int cseq); + virtual ~SrsRtspResponse(); +public: + /** + * encode message to string. + */ + virtual std::stringstream& encode(std::stringstream& ss); +}; + +/** +* 10 Method Definitions +* The method token indicates the method to be performed on the resource +* identified by the Request-URI. The method is case-sensitive. New +* methods may be defined in the future. Method names may not start with +* a $ character (decimal 24) and must be a token. Methods are +* summarized in Table 2. +* Notes on Table 2: PAUSE is recommended, but not required in that a +* fully functional server can be built that does not support this +* method, for example, for live feeds. If a server does not support a +* particular method, it MUST return "501 Not Implemented" and a client +* SHOULD not try this method again for this server. +*/ +enum SrsRtspMethod +{ + SrsRtspMethodDescribe = 0x0001, + SrsRtspMethodAnnounce = 0x0002, + SrsRtspMethodGetParameter = 0x0004, + SrsRtspMethodOptions = 0x0008, + SrsRtspMethodPause = 0x0010, + SrsRtspMethodPlay = 0x0020, + SrsRtspMethodRecord = 0x0040, + SrsRtspMethodRedirect = 0x0080, + SrsRtspMethodSetup = 0x0100, + SrsRtspMethodSetParameter = 0x0200, + SrsRtspMethodTeardown = 0x0400, +}; + +/** +* 10.1 OPTIONS +* The behavior is equivalent to that described in [H9.2]. An OPTIONS +* request may be issued at any time, e.g., if the client is about to +* try a nonstandard request. It does not influence server state. +*/ +class SrsRtspOptionsResponse : public SrsRtspResponse +{ +public: + /** + * join of SrsRtspMethod + */ + SrsRtspMethod methods; +public: + SrsRtspOptionsResponse(int cseq); + virtual ~SrsRtspOptionsResponse(); +public: + virtual std::stringstream& encode(std::stringstream& ss); +}; + +/** +* the state of rtsp token. +*/ +enum SrsRtspTokenState +{ + /** + * parse token failed, default state. + */ + SrsRtspTokenStateError = 100, + /** + * when SP follow the token. + */ + SrsRtspTokenStateNormal = 101, + /** + * when CRLF follow the token. + */ + SrsRtspTokenStateEOF = 102, +}; + /** * the rtsp protocol stack to parse the rtsp packets. */ class SrsRtspStack { private: + /** + * cached bytes buffer. + */ + SrsSimpleBuffer* buf; /** * underlayer socket object, send/recv bytes. */ @@ -47,6 +254,47 @@ class SrsRtspStack public: SrsRtspStack(ISrsProtocolReaderWriter* s); virtual ~SrsRtspStack(); +public: + /** + * recv rtsp message from underlayer io. + * @param preq the output rtsp request message, which user must free it. + * @return an int error code. + * ERROR_RTSP_REQUEST_HEADER_EOF indicates request header EOF. + */ + virtual int recv_message(SrsRtspRequest** preq); + /** + * send rtsp message over underlayer io. + * @param res the rtsp response message, which user should never free it. + * @return an int error code. + */ + virtual int send_message(SrsRtspResponse* res); +private: + /** + * recv the rtsp message. + */ + virtual int do_recv_message(SrsRtspRequest* req); + /** + * read a normal token from io, error when token state is not normal. + */ + virtual int recv_token_normal(std::string& token); + /** + * read a normal token from io, error when token state is not eof. + */ + virtual int recv_token_eof(std::string& token); + /** + * read the token util got eof, for example, to read the response status Reason-Phrase + */ + virtual int recv_token_util_eof(std::string& token); + /** + * read a token from io, split by SP, endswith CRLF: + * token1 SP token2 SP ... tokenN CRLF + * @param normal_ch, the char to indicates the normal token. + * the SP use to indicates the normal token, @see __SRS_RTSP_SP + * the 0x00 use to ignore normal token flag. @see recv_token_util_eof + * @param token, output the read token. + * @param state, output the token parse state. + */ + virtual int recv_token(std::string& token, SrsRtspTokenState& state, char normal_ch = __SRS_RTSP_SP); }; #endif