From 4807f7850d8ce942d86f2e15df088a8a1b54ee18 Mon Sep 17 00:00:00 2001 From: winlin Date: Tue, 17 Feb 2015 14:16:02 +0800 Subject: [PATCH] for #133, rtsp parse the announce packet, parse the sps/pps and audio sequence header by base64. --- trunk/src/app/srs_app_rtsp.cpp | 9 +- trunk/src/kernel/srs_kernel_utility.cpp | 214 +++++++++++++++ trunk/src/kernel/srs_kernel_utility.hpp | 30 +++ trunk/src/protocol/srs_rtsp_stack.cpp | 340 +++++++++++++++++++++++- trunk/src/protocol/srs_rtsp_stack.hpp | 149 ++++++++++- 5 files changed, 729 insertions(+), 13 deletions(-) diff --git a/trunk/src/app/srs_app_rtsp.cpp b/trunk/src/app/srs_app_rtsp.cpp index 7979524043..5402be91f2 100644 --- a/trunk/src/app/srs_app_rtsp.cpp +++ b/trunk/src/app/srs_app_rtsp.cpp @@ -92,7 +92,14 @@ int SrsRtspConn::do_cycle() 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); + srs_error("rtsp: send OPTIONS response failed. ret=%d", ret); + } + return ret; + } + } else if (req->is_announce()) { + if ((ret = rtsp->send_message(new SrsRtspResponse(req->seq))) != ERROR_SUCCESS) { + if (!srs_is_client_gracefully_close(ret)) { + srs_error("rtsp: send ANNOUNCE response failed. ret=%d", ret); } return ret; } diff --git a/trunk/src/kernel/srs_kernel_utility.cpp b/trunk/src/kernel/srs_kernel_utility.cpp index 1c25a37da3..2ebc47abbb 100644 --- a/trunk/src/kernel/srs_kernel_utility.cpp +++ b/trunk/src/kernel/srs_kernel_utility.cpp @@ -407,3 +407,217 @@ u_int32_t srs_crc32(const void* buf, int size) return mpegts_crc32((const u_int8_t*)buf, size); } +/* + * Copyright (c) 2006 Ryan Martell. (rdm4@martellventures.com) + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#define UINT_MAX 0xffffffff + +#ifndef AV_RB32 +# define AV_RB32(x) \ + (((uint32_t)((const u_int8_t*)(x))[0] << 24) | \ + (((const u_int8_t*)(x))[1] << 16) | \ + (((const u_int8_t*)(x))[2] << 8) | \ + ((const u_int8_t*)(x))[3]) +#endif + +#ifndef AV_WL32 +# define AV_WL32(p, darg) do { \ + unsigned d = (darg); \ + ((u_int8_t*)(p))[0] = (d); \ + ((u_int8_t*)(p))[1] = (d)>>8; \ + ((u_int8_t*)(p))[2] = (d)>>16; \ + ((u_int8_t*)(p))[3] = (d)>>24; \ + } while(0) +#endif + +# define AV_WN(s, p, v) AV_WL##s(p, v) + +# if defined(AV_WN32) && !defined(AV_WL32) +# define AV_WL32(p, v) AV_WN32(p, v) +# elif !defined(AV_WN32) && defined(AV_WL32) +# define AV_WN32(p, v) AV_WL32(p, v) +# endif + +#ifndef AV_WN32 +# define AV_WN32(p, v) AV_WN(32, p, v) +#endif + +#define AV_BSWAP16C(x) (((x) << 8 & 0xff00) | ((x) >> 8 & 0x00ff)) +#define AV_BSWAP32C(x) (AV_BSWAP16C(x) << 16 | AV_BSWAP16C((x) >> 16)) + +#ifndef av_bswap32 +static const u_int32_t av_bswap32(u_int32_t x) +{ + return AV_BSWAP32C(x); +} +#endif + +#define av_be2ne32(x) av_bswap32(x) + +/** + * @file + * @brief Base64 encode/decode + * @author Ryan Martell (with lots of Michael) + */ + +/* ---------------- private code */ +static const u_int8_t map2[256] = +{ + 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, + + 0x3e, 0xff, 0xff, 0xff, 0x3f, 0x34, 0x35, 0x36, + 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0xff, + 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x01, + 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, + 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1a, 0x1b, + 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, + 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, + 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, + + 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +}; + +#define BASE64_DEC_STEP(i) do { \ + bits = map2[in[i]]; \ + if (bits & 0x80) \ + goto out ## i; \ + v = i ? (v << 6) + bits : bits; \ +} while(0) + +int srs_av_base64_decode(u_int8_t* out, const char* in_str, int out_size) +{ + u_int8_t *dst = out; + u_int8_t *end = out + out_size; + // no sign extension + const u_int8_t *in = (const u_int8_t*)in_str; + unsigned bits = 0xff; + unsigned v; + + while (end - dst > 3) { + BASE64_DEC_STEP(0); + BASE64_DEC_STEP(1); + BASE64_DEC_STEP(2); + BASE64_DEC_STEP(3); + // Using AV_WB32 directly confuses compiler + v = av_be2ne32(v << 8); + AV_WN32(dst, v); + dst += 3; + in += 4; + } + if (end - dst) { + BASE64_DEC_STEP(0); + BASE64_DEC_STEP(1); + BASE64_DEC_STEP(2); + BASE64_DEC_STEP(3); + *dst++ = v >> 16; + if (end - dst) + *dst++ = v >> 8; + if (end - dst) + *dst++ = v; + in += 4; + } + while (1) { + BASE64_DEC_STEP(0); + in++; + BASE64_DEC_STEP(0); + in++; + BASE64_DEC_STEP(0); + in++; + BASE64_DEC_STEP(0); + in++; + } + +out3: + *dst++ = v >> 10; + v <<= 2; +out2: + *dst++ = v >> 4; +out1: +out0: + return bits & 1 ? -1 : dst - out; +} + +/***************************************************************************** +* b64_encode: Stolen from VLC's http.c. +* Simplified by Michael. +* Fixed edge cases and made it work from data (vs. strings) by Ryan. +*****************************************************************************/ + +char* srs_av_base64_encode(char* out, int out_size, const u_int8_t* in, int in_size) +{ + static const char b64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + char *ret, *dst; + unsigned i_bits = 0; + int i_shift = 0; + int bytes_remaining = in_size; + + if (in_size >= (int)(UINT_MAX / 4) || + out_size < SRS_AV_BASE64_SIZE(in_size)) + return NULL; + ret = dst = out; + while (bytes_remaining > 3) { + i_bits = AV_RB32(in); + in += 3; bytes_remaining -= 3; + *dst++ = b64[ i_bits>>26 ]; + *dst++ = b64[(i_bits>>20) & 0x3F]; + *dst++ = b64[(i_bits>>14) & 0x3F]; + *dst++ = b64[(i_bits>>8 ) & 0x3F]; + } + i_bits = 0; + while (bytes_remaining) { + i_bits = (i_bits << 8) + *in++; + bytes_remaining--; + i_shift += 8; + } + while (i_shift > 0) { + *dst++ = b64[(i_bits << 6 >> i_shift) & 0x3f]; + i_shift -= 6; + } + while ((dst - ret) & 3) + *dst++ = '='; + *dst = '\0'; + + return ret; +} + diff --git a/trunk/src/kernel/srs_kernel_utility.hpp b/trunk/src/kernel/srs_kernel_utility.hpp index 6e0528273b..dea266c093 100644 --- a/trunk/src/kernel/srs_kernel_utility.hpp +++ b/trunk/src/kernel/srs_kernel_utility.hpp @@ -85,5 +85,35 @@ extern bool srs_aac_startswith_adts(SrsStream* stream); */ extern u_int32_t srs_crc32(const void* buf, int size); +/** +* Decode a base64-encoded string. +* +* @param out buffer for decoded data +* @param in null-terminated input string +* @param out_size size in bytes of the out buffer, must be at +* least 3/4 of the length of in +* @return number of bytes written, or a negative value in case of +* invalid input +*/ +extern int srs_av_base64_decode(u_int8_t* out, const char* in, int out_size); + +/** +* Encode data to base64 and null-terminate. +* +* @param out buffer for encoded data +* @param out_size size in bytes of the out buffer (including the +* null terminator), must be at least AV_BASE64_SIZE(in_size) +* @param in input buffer containing the data to encode +* @param in_size size in bytes of the in buffer +* @return out or NULL in case of error +*/ +extern char* srs_av_base64_encode(char* out, int out_size, const u_int8_t* in, int in_size); + +/** + * Calculate the output size needed to base64-encode x bytes to a + * null-terminated string. + */ +#define SRS_AV_BASE64_SIZE(x) (((x)+2) / 3 * 4 + 1) + #endif diff --git a/trunk/src/protocol/srs_rtsp_stack.cpp b/trunk/src/protocol/srs_rtsp_stack.cpp index c1805a23a2..078e45159a 100644 --- a/trunk/src/protocol/srs_rtsp_stack.cpp +++ b/trunk/src/protocol/srs_rtsp_stack.cpp @@ -32,6 +32,8 @@ using namespace std; #include #include #include +#include +#include #ifdef SRS_AUTO_STREAM_CASTER @@ -116,13 +118,290 @@ std::string srs_generate_rtsp_method_str(SrsRtspMethod method) } } +SrsRtspSdp::SrsRtspSdp() +{ + state = SrsRtspSdpStateOthers; +} + +SrsRtspSdp::~SrsRtspSdp() +{ +} + +int SrsRtspSdp::parse(string token) +{ + int ret = ERROR_SUCCESS; + + if (token.empty()) { + srs_info("rtsp: ignore empty token."); + return ret; + } + + size_t pos = string::npos; + + char* start = (char*)token.data(); + char* end = start + (int)token.length(); + char* p = start; + + // key, first 2bytes. + // v=0 + // o=- 0 0 IN IP4 127.0.0.1 + // s=No Name + // c=IN IP4 192.168.43.23 + // t=0 0 + // a=tool:libavformat 53.9.0 + // m=video 0 RTP/AVP 96 + // b=AS:850 + // a=rtpmap:96 H264/90000 + // a=fmtp:96 packetization-mode=1; sprop-parameter-sets=Z2QAKKzRwFAFu/8ALQAiEAAAAwAQAAADAwjxgxHg,aOmrLIs= + // a=control:streamid=0 + // m=audio 0 RTP/AVP 97 + // b=AS:49 + // a=rtpmap:97 MPEG4-GENERIC/44100/2 + // a=fmtp:97 profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3; config=139056E5A0 + // a=control:streamid=1 + char key = p[0]; + p += 2; + + // left bytes as attr string. + std::string attr_str; + if (end - p) { + attr_str.append(p, end - p); + } + + // parse the attributes from left bytes. + std::vector attrs; + while (p < end) { + // parse an attribute, split by SP. + char* pa = p; + for (; p < end && p[0] != __SRS_RTSP_SP; p++) { + } + std::string attr; + if (p > pa) { + attr.append(pa, p - pa); + attrs.push_back(attr); + } + p++; + } + + // parse the first attr as desc, update the first elem for desc. + // for example, the value can be "tool", "AS", "rtpmap", "fmtp", "control" + std::string desc_key; + if (attrs.size() > 0) { + std::string attr = attrs.at(0); + if ((pos = attr.find(":")) != string::npos) { + desc_key = attr.substr(0, pos); + attr = attr.substr(pos + 1); + attr_str = attr_str.substr(pos + 1); + attrs[0] = attr; + } else { + desc_key = attr; + } + } + + // interpret the attribute according by key. + switch (key) { + case 'v': version = attr_str; break; + case 'o': + owner_username = (attrs.size() > 0)? attrs[0]:""; + owner_session_id = (attrs.size() > 1)? attrs[1]:""; + owner_session_version = (attrs.size() > 2)? attrs[2]:""; + owner_network_type = (attrs.size() > 3)? attrs[3]:""; + owner_address_type = (attrs.size() > 4)? attrs[4]:""; + owner_address = (attrs.size() > 5)? attrs[5]:""; + break; + case 's': session_name = attr_str; break; + case 'c': + connection_network_type = (attrs.size() > 0)? attrs[0]:""; + connection_address_type = (attrs.size() > 0)? attrs[0]:""; + connection_address = (attrs.size() > 0)? attrs[0]:""; + break; + case 'a': + if (desc_key == "tool") { + tool = attr_str; + } else if (desc_key == "rtpmap") { + if (state == SrsRtspSdpStateVideo) { + video_codec = (attrs.size() > 1)? attrs[1]:""; + if ((pos = video_codec.find("/")) != string::npos) { + video_sample_rate = video_codec.substr(pos + 1); + video_codec = video_codec.substr(0, pos); + } + } else if (state == SrsRtspSdpStateAudio) { + audio_codec = (attrs.size() > 1)? attrs[1]:""; + if ((pos = audio_codec.find("/")) != string::npos) { + audio_sample_rate = audio_codec.substr(pos + 1); + audio_codec = audio_codec.substr(0, pos); + } + if ((pos = audio_codec.find("/")) != string::npos) { + audio_channel = audio_codec.substr(pos + 1); + audio_codec = audio_codec.substr(0, pos); + } + } + } else if (desc_key == "fmtp") { + for (int i = 1; i < (int)attrs.size(); i++) { + std::string attr = attrs.at(i); + if ((ret = parse_fmtp_attribute(attr)) != ERROR_SUCCESS) { + srs_error("rtsp: parse fmtp failed, attr=%s. ret=%d", attr.c_str(), ret); + return ret; + } + } + } else if (desc_key == "control") { + for (int i = 0; i < (int)attrs.size(); i++) { + std::string attr = attrs.at(i); + if ((ret = parse_control_attribute(attr)) != ERROR_SUCCESS) { + srs_error("rtsp: parse control failed, attr=%s. ret=%d", attr.c_str(), ret); + return ret; + } + } + } + break; + case 'm': + if (desc_key == "video") { + state = SrsRtspSdpStateVideo; + video_port = (attrs.size() > 1)? attrs[1]:""; + video_protocol = (attrs.size() > 2)? attrs[2]:""; + video_transport_format = (attrs.size() > 3)? attrs[3]:""; + } else if (desc_key == "audio") { + state = SrsRtspSdpStateAudio; + audio_port = (attrs.size() > 1)? attrs[1]:""; + audio_protocol = (attrs.size() > 2)? attrs[2]:""; + audio_transport_format = (attrs.size() > 3)? attrs[3]:""; + } + break; + case 'b': + if (desc_key == "AS") { + if (state == SrsRtspSdpStateVideo) { + video_bandwidth_kbps = (attrs.size() > 0)? attrs[0]:""; + } else if (state == SrsRtspSdpStateAudio) { + audio_bandwidth_kbps = (attrs.size() > 0)? attrs[0]:""; + } + } + break; + case 't': + default: break; + } + + return ret; +} + +int SrsRtspSdp::parse_fmtp_attribute(string& attr) +{ + int ret = ERROR_SUCCESS; + + size_t pos = string::npos; + + while (!attr.empty()) { + std::string item = attr; + if ((pos = item.find(";")) != string::npos) { + item = attr.substr(0, pos); + attr = attr.substr(pos + 1); + } else { + attr = ""; + } + + std::string item_key = item, item_value; + if ((pos = item.find("=")) != string::npos) { + item_key = item.substr(0, pos); + item_value = item.substr(pos + 1); + } + + if (state == SrsRtspSdpStateVideo) { + if (item_key == "packetization-mode") { + video_packetization_mode = item_value; + } else if (item_key == "sprop-parameter-sets") { + video_sps = item_value; + if ((pos = video_sps.find(",")) != string::npos) { + video_pps = video_sps.substr(pos + 1); + video_sps = video_sps.substr(0, pos); + } + // decode the sps/pps by base64 + video_sps = base64_decode(video_sps); + video_pps = base64_decode(video_pps); + } + } else if (state == SrsRtspSdpStateAudio) { + if (item_key == "profile-level-id") { + audio_profile_level_id = item_value; + } else if (item_key == "mode") { + audio_mode = item_value; + } else if (item_key == "sizelength") { + audio_size_length = item_value; + } else if (item_key == "indexlength") { + audio_index_length = item_value; + } else if (item_key == "indexdeltalength") { + audio_index_delta_length = item_value; + } else if (item_key == "config") { + audio_sh = base64_decode(item_value); + } + } + } + + return ret; +} + +int SrsRtspSdp::parse_control_attribute(string& attr) +{ + int ret = ERROR_SUCCESS; + + size_t pos = string::npos; + + while (!attr.empty()) { + std::string item = attr; + if ((pos = item.find(";")) != string::npos) { + item = attr.substr(0, pos); + attr = attr.substr(pos + 1); + } else { + attr = ""; + } + + std::string item_key = item, item_value; + if ((pos = item.find("=")) != string::npos) { + item_key = item.substr(0, pos); + item_value = item.substr(pos + 1); + } + + if (state == SrsRtspSdpStateVideo) { + if (item_key == "streamid") { + video_stream_id = item_value; + } + } else if (state == SrsRtspSdpStateAudio) { + if (item_key == "streamid") { + audio_stream_id = item_value; + } + } + } + + return ret; +} + +string SrsRtspSdp::base64_decode(string value) +{ + if (value.empty()) { + return ""; + } + + int nb_output = (int)(value.length() * 2); + u_int8_t* output = new u_int8_t[nb_output]; + SrsAutoFree(u_int8_t, output); + + int ret = srs_av_base64_decode(output, (char*)value.c_str(), nb_output); + if (ret <= 0) { + return ""; + } + + std::string plaintext; + plaintext.append((char*)output, ret); + return plaintext; +} + SrsRtspRequest::SrsRtspRequest() { seq = 0; + content_length = 0; + sdp = NULL; } SrsRtspRequest::~SrsRtspRequest() { + srs_freep(sdp); } bool SrsRtspRequest::is_options() @@ -130,6 +409,11 @@ bool SrsRtspRequest::is_options() return method == __SRS_METHOD_OPTIONS; } +bool SrsRtspRequest::is_announce() +{ + return method == __SRS_METHOD_ANNOUNCE; +} + SrsRtspResponse::SrsRtspResponse(int cseq) { seq = cseq; @@ -306,11 +590,29 @@ int SrsRtspStack::do_recv_message(SrsRtspRequest* req) 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); + srs_error("rtsp: parse %s failed. ret=%d", __SRS_TOKEN_CSEQ, ret); } return ret; } - req->seq = ::atoi(seq.c_str()); + req->seq = ::atol(seq.c_str()); + } else if (token == __SRS_TOKEN_CONTENT_TYPE) { + std::string ct; + if ((ret = recv_token_eof(ct)) != ERROR_SUCCESS) { + if (!srs_is_client_gracefully_close(ret)) { + srs_error("rtsp: parse %s failed. ret=%d", __SRS_TOKEN_CONTENT_TYPE, ret); + } + return ret; + } + req->content_type = ct; + } else if (token == __SRS_TOKEN_CONTENT_LENGTH) { + std::string cl; + if ((ret = recv_token_eof(cl)) != ERROR_SUCCESS) { + if (!srs_is_client_gracefully_close(ret)) { + srs_error("rtsp: parse %s failed. ret=%d", __SRS_TOKEN_CONTENT_LENGTH, ret); + } + return ret; + } + req->content_length = ::atol(cl.c_str()); } else { // unknown header name, parse util EOF. SrsRtspTokenState state = SrsRtspTokenStateNormal; @@ -327,7 +629,30 @@ int SrsRtspStack::do_recv_message(SrsRtspRequest* req) } } - // parse body. + // parse rdp body. + long consumed = 0; + while (consumed < req->content_length) { + if (!req->sdp) { + req->sdp = new SrsRtspSdp(); + } + + int nb_token = 0; + std::string token; + if ((ret = recv_token_util_eof(token, &nb_token)) != ERROR_SUCCESS) { + if (!srs_is_client_gracefully_close(ret)) { + srs_error("rtsp: parse sdp token failed. ret=%d", ret); + } + return ret; + } + consumed += nb_token; + + if ((ret = req->sdp->parse(token)) != ERROR_SUCCESS) { + srs_error("rtsp: sdp parse token failed, token=%s. ret=%d", token.c_str(), ret); + return ret; + } + srs_info("rtsp: %s", token.c_str()); + } + srs_info("rtsp: sdp parsed, size=%d", consumed); return ret; } @@ -382,14 +707,14 @@ int SrsRtspStack::recv_token_eof(std::string& token) return ret; } -int SrsRtspStack::recv_token_util_eof(std::string& token) +int SrsRtspStack::recv_token_util_eof(std::string& token, int* pconsumed) { 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 = recv_token(token, state, 0x00, pconsumed)) != ERROR_SUCCESS) { if (ret == ERROR_RTSP_REQUEST_HEADER_EOF) { return ret; } @@ -408,7 +733,7 @@ int SrsRtspStack::recv_token_util_eof(std::string& token) return ret; } -int SrsRtspStack::recv_token(std::string& token, SrsRtspTokenState& state, char normal_ch) +int SrsRtspStack::recv_token(std::string& token, SrsRtspTokenState& state, char normal_ch, int* pconsumed) { int ret = ERROR_SUCCESS; @@ -474,6 +799,9 @@ int SrsRtspStack::recv_token(std::string& token, SrsRtspTokenState& state, char // consume the token bytes. srs_assert(p - start); buf->erase(p - start); + if (pconsumed) { + *pconsumed = p - start; + } break; } diff --git a/trunk/src/protocol/srs_rtsp_stack.hpp b/trunk/src/protocol/srs_rtsp_stack.hpp index a5184f95cc..5c79c0669d 100644 --- a/trunk/src/protocol/srs_rtsp_stack.hpp +++ b/trunk/src/protocol/srs_rtsp_stack.hpp @@ -58,6 +58,8 @@ class ISrsProtocolReaderWriter; // RTSP token #define __SRS_TOKEN_CSEQ "CSeq" #define __SRS_TOKEN_PUBLIC "Public" +#define __SRS_TOKEN_CONTENT_TYPE "Content-Type" +#define __SRS_TOKEN_CONTENT_LENGTH "Content-Length" // RTSP methods #define __SRS_METHOD_OPTIONS "OPTIONS" @@ -76,6 +78,118 @@ class ISrsProtocolReaderWriter; // RTSP-Version #define __SRS_VERSION "RTSP/1.0" +/** +* the rtsp sdp parse state. +*/ +enum SrsRtspSdpState +{ + /** + * other sdp properties. + */ + SrsRtspSdpStateOthers, + /** + * parse sdp audio state. + */ + SrsRtspSdpStateAudio, + /** + * parse sdp video state. + */ + SrsRtspSdpStateVideo, +}; + +/** +* the sdp in announce. +* Appendix C: Use of SDP for RTSP Session Descriptions +* The Session Description Protocol (SDP, RFC 2327 [6]) may be used to +* describe streams or presentations in RTSP. +*/ +class SrsRtspSdp +{ +private: + SrsRtspSdpState state; +public: + /** + * the version of sdp. + */ + std::string version; + /** + * the owner/creator of sdp. + */ + std::string owner_username; + std::string owner_session_id; + std::string owner_session_version; + std::string owner_network_type; + std::string owner_address_type; + std::string owner_address; + /** + * the session name of sdp. + */ + std::string session_name; + /** + * the connection info of sdp. + */ + std::string connection_network_type; + std::string connection_address_type; + std::string connection_address; + /** + * the tool attribute of sdp. + */ + std::string tool; + /** + * the video attribute of sdp. + */ + std::string video_port; + std::string video_protocol; + std::string video_transport_format; + std::string video_bandwidth_kbps; + std::string video_codec; + std::string video_sample_rate; + std::string video_stream_id; + // fmtp + std::string video_packetization_mode; + std::string video_sps; // sequence header: sps. + std::string video_pps; // sequence header: pps. + /** + * the audio attribute of sdp. + */ + std::string audio_port; + std::string audio_protocol; + std::string audio_transport_format; + std::string audio_bandwidth_kbps; + std::string audio_codec; + std::string audio_sample_rate; + std::string audio_channel; + std::string audio_stream_id; + // fmtp + std::string audio_profile_level_id; + std::string audio_mode; + std::string audio_size_length; + std::string audio_index_length; + std::string audio_index_delta_length; + std::string audio_sh; // sequence header. +public: + SrsRtspSdp(); + virtual ~SrsRtspSdp(); +public: + /** + * parse a line of token for sdp. + */ + virtual int parse(std::string token); +private: + /** + * generally, the fmtp is the sequence header for video or audio. + */ + virtual int parse_fmtp_attribute(std::string& attr); + /** + * generally, the control is the stream info for video or audio. + */ + virtual int parse_control_attribute(std::string& attr); + /** + * decode the string by base64. + */ + virtual std::string base64_decode(std::string value); +}; + /** * the rtsp request message. * 6 Request @@ -110,12 +224,33 @@ class SrsRtspRequest * number as the original (i.e. the sequence number is not incremented * for retransmissions of the same request). */ - int seq; + long seq; + /** + * 12.16 Content-Type + * See [H14.18]. Note that the content types suitable for RTSP are + * likely to be restricted in practice to presentation descriptions and + * parameter-value types. + */ + std::string content_type; + /** + * 12.14 Content-Length + * This field contains the length of the content of the method (i.e. + * after the double CRLF following the last header). Unlike HTTP, it + * MUST be included in all messages that carry content beyond the header + * portion of the message. If it is missing, a default value of zero is + * assumed. It is interpreted according to [H14.14]. + */ + long content_length; + /** + * the sdp in announce, NULL for no sdp. + */ + SrsRtspSdp* sdp; public: SrsRtspRequest(); virtual ~SrsRtspRequest(); public: virtual bool is_options(); + virtual bool is_announce(); }; /** @@ -159,7 +294,7 @@ class SrsRtspResponse * number as the original (i.e. the sequence number is not incremented * for retransmissions of the same request). */ - int seq; + long seq; public: SrsRtspResponse(int cseq); virtual ~SrsRtspResponse(); @@ -283,18 +418,20 @@ class SrsRtspStack virtual int recv_token_eof(std::string& token); /** * read the token util got eof, for example, to read the response status Reason-Phrase + * @param pconsumed, output the token parsed length. NULL to ignore. */ - virtual int recv_token_util_eof(std::string& token); + virtual int recv_token_util_eof(std::string& token, int* pconsumed = NULL); /** * read a token from io, split by SP, endswith CRLF: * token1 SP token2 SP ... tokenN CRLF + * @param token, output the read token. + * @param state, output the token parse state. * @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. + * @param pconsumed, output the token parsed length. NULL to ignore. */ - virtual int recv_token(std::string& token, SrsRtspTokenState& state, char normal_ch = __SRS_RTSP_SP); + virtual int recv_token(std::string& token, SrsRtspTokenState& state, char normal_ch = __SRS_RTSP_SP, int* pconsumed = NULL); }; #endif