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:
parent
604f4cc57b
commit
4807f7850d
5 changed files with 729 additions and 13 deletions
|
@ -92,7 +92,14 @@ int SrsRtspConn::do_cycle()
|
||||||
if (req->is_options()) {
|
if (req->is_options()) {
|
||||||
if ((ret = rtsp->send_message(new SrsRtspOptionsResponse(req->seq))) != ERROR_SUCCESS) {
|
if ((ret = rtsp->send_message(new SrsRtspOptionsResponse(req->seq))) != ERROR_SUCCESS) {
|
||||||
if (!srs_is_client_gracefully_close(ret)) {
|
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;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -407,3 +407,217 @@ u_int32_t srs_crc32(const void* buf, int size)
|
||||||
return mpegts_crc32((const u_int8_t*)buf, 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 <rdm4@martellventures.com> (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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -85,5 +85,35 @@ extern bool srs_aac_startswith_adts(SrsStream* stream);
|
||||||
*/
|
*/
|
||||||
extern u_int32_t srs_crc32(const void* buf, int size);
|
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
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,8 @@ using namespace std;
|
||||||
#include <srs_kernel_error.hpp>
|
#include <srs_kernel_error.hpp>
|
||||||
#include <srs_kernel_log.hpp>
|
#include <srs_kernel_log.hpp>
|
||||||
#include <srs_kernel_consts.hpp>
|
#include <srs_kernel_consts.hpp>
|
||||||
|
#include <srs_core_autofree.hpp>
|
||||||
|
#include <srs_kernel_utility.hpp>
|
||||||
|
|
||||||
#ifdef SRS_AUTO_STREAM_CASTER
|
#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()
|
SrsRtspRequest::SrsRtspRequest()
|
||||||
{
|
{
|
||||||
seq = 0;
|
seq = 0;
|
||||||
|
content_length = 0;
|
||||||
|
sdp = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsRtspRequest::~SrsRtspRequest()
|
SrsRtspRequest::~SrsRtspRequest()
|
||||||
{
|
{
|
||||||
|
srs_freep(sdp);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SrsRtspRequest::is_options()
|
bool SrsRtspRequest::is_options()
|
||||||
|
@ -130,6 +409,11 @@ bool SrsRtspRequest::is_options()
|
||||||
return method == __SRS_METHOD_OPTIONS;
|
return method == __SRS_METHOD_OPTIONS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SrsRtspRequest::is_announce()
|
||||||
|
{
|
||||||
|
return method == __SRS_METHOD_ANNOUNCE;
|
||||||
|
}
|
||||||
|
|
||||||
SrsRtspResponse::SrsRtspResponse(int cseq)
|
SrsRtspResponse::SrsRtspResponse(int cseq)
|
||||||
{
|
{
|
||||||
seq = cseq;
|
seq = cseq;
|
||||||
|
@ -306,11 +590,29 @@ int SrsRtspStack::do_recv_message(SrsRtspRequest* req)
|
||||||
std::string seq;
|
std::string seq;
|
||||||
if ((ret = recv_token_eof(seq)) != ERROR_SUCCESS) {
|
if ((ret = recv_token_eof(seq)) != ERROR_SUCCESS) {
|
||||||
if (!srs_is_client_gracefully_close(ret)) {
|
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;
|
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 {
|
} else {
|
||||||
// unknown header name, parse util EOF.
|
// unknown header name, parse util EOF.
|
||||||
SrsRtspTokenState state = SrsRtspTokenStateNormal;
|
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;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -382,14 +707,14 @@ int SrsRtspStack::recv_token_eof(std::string& token)
|
||||||
return ret;
|
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;
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
SrsRtspTokenState state;
|
SrsRtspTokenState state;
|
||||||
|
|
||||||
// use 0x00 as ignore the normal token flag.
|
// 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) {
|
if (ret == ERROR_RTSP_REQUEST_HEADER_EOF) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -408,7 +733,7 @@ int SrsRtspStack::recv_token_util_eof(std::string& token)
|
||||||
return ret;
|
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;
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
@ -474,6 +799,9 @@ int SrsRtspStack::recv_token(std::string& token, SrsRtspTokenState& state, char
|
||||||
// consume the token bytes.
|
// consume the token bytes.
|
||||||
srs_assert(p - start);
|
srs_assert(p - start);
|
||||||
buf->erase(p - start);
|
buf->erase(p - start);
|
||||||
|
if (pconsumed) {
|
||||||
|
*pconsumed = p - start;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -58,6 +58,8 @@ class ISrsProtocolReaderWriter;
|
||||||
// RTSP token
|
// RTSP token
|
||||||
#define __SRS_TOKEN_CSEQ "CSeq"
|
#define __SRS_TOKEN_CSEQ "CSeq"
|
||||||
#define __SRS_TOKEN_PUBLIC "Public"
|
#define __SRS_TOKEN_PUBLIC "Public"
|
||||||
|
#define __SRS_TOKEN_CONTENT_TYPE "Content-Type"
|
||||||
|
#define __SRS_TOKEN_CONTENT_LENGTH "Content-Length"
|
||||||
|
|
||||||
// RTSP methods
|
// RTSP methods
|
||||||
#define __SRS_METHOD_OPTIONS "OPTIONS"
|
#define __SRS_METHOD_OPTIONS "OPTIONS"
|
||||||
|
@ -76,6 +78,118 @@ class ISrsProtocolReaderWriter;
|
||||||
// RTSP-Version
|
// RTSP-Version
|
||||||
#define __SRS_VERSION "RTSP/1.0"
|
#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.
|
* the rtsp request message.
|
||||||
* 6 Request
|
* 6 Request
|
||||||
|
@ -110,12 +224,33 @@ public:
|
||||||
* number as the original (i.e. the sequence number is not incremented
|
* number as the original (i.e. the sequence number is not incremented
|
||||||
* for retransmissions of the same request).
|
* 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:
|
public:
|
||||||
SrsRtspRequest();
|
SrsRtspRequest();
|
||||||
virtual ~SrsRtspRequest();
|
virtual ~SrsRtspRequest();
|
||||||
public:
|
public:
|
||||||
virtual bool is_options();
|
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
|
* number as the original (i.e. the sequence number is not incremented
|
||||||
* for retransmissions of the same request).
|
* for retransmissions of the same request).
|
||||||
*/
|
*/
|
||||||
int seq;
|
long seq;
|
||||||
public:
|
public:
|
||||||
SrsRtspResponse(int cseq);
|
SrsRtspResponse(int cseq);
|
||||||
virtual ~SrsRtspResponse();
|
virtual ~SrsRtspResponse();
|
||||||
|
@ -283,18 +418,20 @@ private:
|
||||||
virtual int recv_token_eof(std::string& token);
|
virtual int recv_token_eof(std::string& token);
|
||||||
/**
|
/**
|
||||||
* read the token util got eof, for example, to read the response status Reason-Phrase
|
* 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:
|
* read a token from io, split by SP, endswith CRLF:
|
||||||
* token1 SP token2 SP ... tokenN 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.
|
* @param normal_ch, the char to indicates the normal token.
|
||||||
* the SP use to indicates the normal token, @see __SRS_RTSP_SP
|
* 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
|
* the 0x00 use to ignore normal token flag. @see recv_token_util_eof
|
||||||
* @param token, output the read token.
|
* @param pconsumed, output the token parsed length. NULL to ignore.
|
||||||
* @param state, output the token parse state.
|
|
||||||
*/
|
*/
|
||||||
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
|
#endif
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue