293 lines
12 KiB
C++
293 lines
12 KiB
C++
#ifndef SRCCTL_RTSP_DATA_CHANNEL_H
|
||
#define SRCCTL_RTSP_DATA_CHANNEL_H
|
||
|
||
#include "Socket/CCServerSocket.h"
|
||
#include "Socket/CCDatagramSocket.h"
|
||
#include "CCSDP.h"
|
||
|
||
#define CC_R_RTSP_BUFFER_SIZE 1
|
||
|
||
namespace CTL{
|
||
typedef struct RTSP_Message{
|
||
int statusCode = 0; // 状态码 (响应时使用)
|
||
int port = 0; // 客户端端口号
|
||
bool isRequest = true; // 是否为请求消息(true)还是响应消息(false)
|
||
String client_ip; // 客户端IP
|
||
String url_data; // 请求URL
|
||
String method; // RTSP方法 (OPTIONS, DESCRIBE, SETUP, PLAY等)
|
||
String uri; // 请求URI
|
||
String uri_path; // 请求URI路径
|
||
String version; // RTSP版本 (如"RTSP/1.0")
|
||
String reasonPhrase; // 原因短语 (响应时使用)
|
||
std::map<String, String> headers; // 头部字段
|
||
String body; // 消息体
|
||
String sdp_content; // SDP内容
|
||
} RTSP_Request;
|
||
class RTSPDataChannel{
|
||
ClientSocket* socket_t = nullptr;
|
||
public:
|
||
explicit RTSPDataChannel(ClientSocket* socket){
|
||
socket_t = socket;
|
||
}
|
||
~RTSPDataChannel(){
|
||
if (socket_t) {
|
||
socket_t->close();
|
||
socket_t = nullptr;
|
||
}
|
||
}
|
||
static void MessagePrint(const String& message,...){
|
||
System::Println(message);
|
||
}
|
||
int receive(char *buffer, const size_t size) const {
|
||
if (socket_t && socket_t->isConnect()) {
|
||
const auto input = socket_t->getInputStream();
|
||
if (input->available()) {
|
||
return input->read(buffer, size);
|
||
}
|
||
return -2;
|
||
}
|
||
return -1;
|
||
}
|
||
int write(const char *buffer, const size_t size) const {
|
||
if (socket_t && socket_t->isConnect()) {
|
||
const auto output = socket_t->getOutputStream();
|
||
return output->write(buffer,size);
|
||
}
|
||
return 0;
|
||
}
|
||
String readHeader() const{
|
||
if (this->socket_t) {
|
||
String data;
|
||
data.reserve(4096); // 预分配内存
|
||
int ET = 1;
|
||
while (true){
|
||
char buffer[CC_R_RTSP_BUFFER_SIZE] = {0};
|
||
if(const auto length = receive(buffer,CC_R_RTSP_BUFFER_SIZE); length > 0){
|
||
data.append(buffer, length); // 块追加代替逐个字符处理
|
||
if (const auto pos = data.find("\r\n\r\n"); pos != CTL::String::npos) {
|
||
data.resize(pos + 4); // 截断到header结束位置
|
||
break;
|
||
}
|
||
}
|
||
else if (length == -2) {
|
||
if (ET >= 1000 * 1000) {
|
||
this->MessagePrint("RTSPDataChannel readHeader -> Timeout");
|
||
break;
|
||
}
|
||
ET++;
|
||
Thread::Sleep(1);
|
||
}
|
||
else{
|
||
return "close";
|
||
}
|
||
}
|
||
return data;
|
||
}
|
||
return "Error_10030";
|
||
}
|
||
String readData(const uint64_t length) const{
|
||
if (this->socket_t) {
|
||
String data;
|
||
data.reserve(length); // 预分配内存
|
||
int ET = 1;
|
||
while (true){
|
||
char buffer[CC_R_RTSP_BUFFER_SIZE] = {0};
|
||
if(const auto len = receive(buffer,CC_R_RTSP_BUFFER_SIZE); len > 0){
|
||
data.append(buffer, len);
|
||
if (data.length() >= length) {
|
||
break;
|
||
}
|
||
}
|
||
else if (len == -2) {
|
||
if (ET >= 1000 * 300) {
|
||
this->MessagePrint("RTSPDataChannel readData -> Timeout");
|
||
break;
|
||
}
|
||
ET++;
|
||
Thread::Sleep(1);
|
||
}
|
||
else{
|
||
return "close";
|
||
}
|
||
}
|
||
return data;
|
||
}
|
||
return "Error_10030";
|
||
}
|
||
static RTSP_Message ParseRTSP(const String& data){
|
||
RTSP_Message msg;
|
||
if (data.empty()) {
|
||
return msg;
|
||
}
|
||
msg.url_data = data;
|
||
// 查找空行(\r\n\r\n)来分离头部和消息体
|
||
size_t headerEndPos = data.find("\r\n\r\n");
|
||
String headersPart;
|
||
String bodyPart;
|
||
if (headerEndPos != CTL::String::npos) {
|
||
headersPart = data.substr(0, headerEndPos);
|
||
bodyPart = data.substr(headerEndPos + 4); // 跳过"\r\n\r\n"
|
||
}
|
||
else {
|
||
headersPart = data;
|
||
}
|
||
// 分割头部行
|
||
std::vector<String> lines;
|
||
String currentLine;
|
||
for (size_t i = 0; i < headersPart.length(); ++i) {
|
||
if (headersPart[i] == '\r' && i + 1 < headersPart.length() && headersPart[i + 1] == '\n') {
|
||
lines.push_back(currentLine);
|
||
currentLine.clear();
|
||
i++; // 跳过'\n'
|
||
} else {
|
||
currentLine += headersPart[i];
|
||
}
|
||
}
|
||
if (!currentLine.empty()) {
|
||
lines.push_back(currentLine);
|
||
}
|
||
if (!lines.empty()) {
|
||
// 解析第一行 (请求行或状态行)
|
||
const String& firstLine = lines[0];
|
||
|
||
// 检查是请求还是响应
|
||
if (firstLine.find("RTSP/") == 0) {
|
||
// 这是响应行: "RTSP/1.0 200 OK"
|
||
msg.isRequest = false;
|
||
|
||
size_t firstSpace = firstLine.find(' ');
|
||
if (firstSpace != String::npos) {
|
||
size_t secondSpace = firstLine.find(' ', firstSpace + 1);
|
||
if (secondSpace != String::npos) {
|
||
msg.version = firstLine.substr(0, firstSpace);
|
||
try {
|
||
msg.statusCode = std::stoi(firstLine.substr(firstSpace + 1,
|
||
secondSpace - firstSpace - 1));
|
||
}
|
||
catch (...) {
|
||
msg.statusCode = 500; // 默认错误状态
|
||
}
|
||
msg.reasonPhrase = firstLine.substr(secondSpace + 1);
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
// 这是请求行: "OPTIONS rtsp://... RTSP/1.0"
|
||
size_t firstSpace = firstLine.find(' ');
|
||
size_t lastSpace = firstLine.rfind(' ');
|
||
|
||
if (firstSpace != String::npos && lastSpace != String::npos && firstSpace < lastSpace) {
|
||
msg.method = firstLine.substr(0, firstSpace);
|
||
msg.uri = firstLine.substr(firstSpace + 1, lastSpace - firstSpace - 1);
|
||
msg.version = firstLine.substr(lastSpace + 1);
|
||
|
||
// 从URI中解析路径部分
|
||
size_t schemeEnd = msg.uri.find("://");
|
||
if (schemeEnd != String::npos) {
|
||
// 跳过协议和主机部分,找到路径
|
||
size_t hostStart = schemeEnd + 3;
|
||
size_t pathStart = msg.uri.find('/', hostStart);
|
||
if (pathStart != String::npos) {
|
||
msg.uri_path = msg.uri.substr(pathStart);
|
||
} else {
|
||
// 如果没有路径,则设置为根路径
|
||
msg.uri_path = "/";
|
||
}
|
||
} else {
|
||
// 如果没有协议前缀,整个URI可能是路径
|
||
if (msg.uri.empty() || msg.uri[0] != '/') {
|
||
msg.uri_path = "/" + msg.uri;
|
||
} else {
|
||
msg.uri_path = msg.uri;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 解析头部字段
|
||
for (size_t i = 1; i < lines.size(); ++i) {
|
||
const String& line = lines[i];
|
||
size_t colonPos = line.find(':');
|
||
if (colonPos != String::npos) {
|
||
String key = line.substr(0, colonPos);
|
||
String value = line.substr(colonPos + 1);
|
||
|
||
// 移除键和值前后的空白字符
|
||
size_t startKey = key.find_first_not_of(" \t");
|
||
size_t endKey = key.find_last_not_of(" \t");
|
||
if (startKey != String::npos && endKey != String::npos) {
|
||
key = key.substr(startKey, endKey - startKey + 1);
|
||
} else if (startKey == String::npos) {
|
||
continue; // 跳过无效行
|
||
}
|
||
|
||
size_t startPos = value.find_first_not_of(" \t");
|
||
size_t endPos = value.find_last_not_of(" \t");
|
||
if (startPos != String::npos && endPos != String::npos) {
|
||
value = value.substr(startPos, endPos - startPos + 1);
|
||
} else if (startPos == String::npos) {
|
||
value.clear();
|
||
}
|
||
|
||
// 将头部字段转换为小写作为键
|
||
String lowerKey = key;
|
||
std::transform(lowerKey.begin(), lowerKey.end(), lowerKey.begin(), ::tolower);
|
||
|
||
msg.headers[lowerKey] = value;
|
||
}
|
||
}
|
||
}
|
||
// 设置消息体
|
||
msg.body = bodyPart;
|
||
return msg;
|
||
}
|
||
};
|
||
class RTSP_Response{
|
||
RTSPDataChannel* socket_t = nullptr;
|
||
std::map<String, String> headers;
|
||
SDPBuilder sdp_builder;
|
||
public:
|
||
//-----------------------------------------------------------------------------------------
|
||
int statusCode = 200;
|
||
String reasonPhrase = "OK";
|
||
bool auto_send = true;
|
||
//-----------------------------------------------------------------------------------------
|
||
void setSocket(RTSPDataChannel* socket){
|
||
this->socket_t = socket;
|
||
}
|
||
RTSPDataChannel* getSocket() const{
|
||
return socket_t;
|
||
}
|
||
String getHeader(const String& key) const{
|
||
auto it = headers.find(key);
|
||
if (it != headers.end()) {
|
||
return it->second;
|
||
}
|
||
return "";
|
||
}
|
||
void setHeader(const String& key, const String& value){
|
||
headers[key] = value;
|
||
}
|
||
String getBuild() const{
|
||
String response = String::format("RTSP/1.0 {} {}\r\n", statusCode, reasonPhrase.c_str());
|
||
for (const auto& header : headers) {
|
||
response.append(String::format("{}: {}\r\n",header.first.c_str(),header.second.c_str()));
|
||
}
|
||
response.append("\r\n");
|
||
return response;
|
||
}
|
||
bool send(){
|
||
if(socket_t != nullptr){
|
||
int ret = socket_t->write(getBuild().c_str(), getBuild().length());
|
||
if (ret > 0) {
|
||
auto_send = false;
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
//-----------------------------------------------------------------------------------------
|
||
};
|
||
}
|
||
|
||
#endif |