1
0
Fork 0
mirror of https://github.com/ossrs/srs.git synced 2025-03-09 15:49:59 +00:00

for #133, rtsp parse the announce packet, parse the sps/pps and audio sequence header by base64.

This commit is contained in:
winlin 2015-02-17 14:16:02 +08:00
parent 604f4cc57b
commit 4807f7850d
5 changed files with 729 additions and 13 deletions

View file

@ -32,6 +32,8 @@ using namespace std;
#include <srs_kernel_error.hpp>
#include <srs_kernel_log.hpp>
#include <srs_kernel_consts.hpp>
#include <srs_core_autofree.hpp>
#include <srs_kernel_utility.hpp>
#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<std::string> 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;
}

View file

@ -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 @@ public:
* 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 @@ public:
* 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 @@ private:
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