Files
esp32cam-rtsp/lib/micro-rtsp-server/src/micro_rtsp_requests.cpp
Rene Zeldenthuis 2eb7a58e1c Work in progress
2024-02-18 12:47:17 +01:00

216 lines
6.6 KiB
C++

#include <esp32-hal-log.h>
#include <regex>
#include "micro_rtsp_requests.h"
// https://datatracker.ietf.org/doc/html/rfc2326
micro_rtsp_requests::rtsp_command micro_rtsp_requests::parse_command(const std::string &request)
{
log_i("parse_command");
// Check for RTSP commands: Option, Describe, Setup, Play, Teardown
if (std::regex_match(request, std::regex("^OPTION ", std::regex_constants::icase)))
return rtsp_command::rtsp_command_option;
if (std::regex_match(request, std::regex("^DESCRIBE ", std::regex_constants::icase)))
return rtsp_command::rtsp_command_describe;
if (std::regex_match(request, std::regex("^SETUP ", std::regex_constants::icase)))
return rtsp_command::rtsp_command_setup;
if (std::regex_match(request, std::regex("^PLAY ", std::regex_constants::icase)))
return rtsp_command::rtsp_command_play;
if (std::regex_match(request, std::regex("^TEARDOWN ", std::regex_constants::icase)))
return rtsp_command::rtsp_command_teardown;
log_e("Invalid rtsp command: %s", request.c_str());
return rtsp_command::rtsp_command_unknown;
}
bool micro_rtsp_requests::parse_client_port(const std::string &request)
{
log_i("parse_client_port");
std::regex regex("client_port=([0-9]+)", std::regex_constants::icase);
std::smatch match;
if (!std::regex_match(request, match, regex))
{
log_e("client_port not found");
return false;
}
client_port_ = std::stoi(match[1].str());
return true;
}
bool micro_rtsp_requests::parse_cseq(const std::string &request)
{
log_i("parse_cseq");
// CSeq: 2
std::regex regex("CSeq: (\\d+)", std::regex_constants::icase);
std::smatch match;
if (!std::regex_match(request, match, regex))
{
log_e("CSeq not found");
return false;
}
cseq_ = std::stoul(match[1].str());
return true;
}
bool micro_rtsp_requests::parse_stream_url(const std::string &request)
{
log_i("parse_host_url");
// SETUP rtsp://192.168.10.93:1234/mjpeg/1 RTSP/1.0
std::regex regex("rtsp:\\/\\/([\\w.]+):(\\d+)\\/([\\/\\w]+)\\s+RTSP/1.0", std::regex_constants::icase);
std::smatch match;
if (!std::regex_match(request, match, regex))
{
log_e("Unable to parse url");
return false;
}
host_url_ = match[1].str();
host_port_ = std::stoi(match[2].str());
stream_name_ = match[3].str();
return true;
}
std::string micro_rtsp_requests::date_header()
{
char buffer[50];
auto now = std::time(nullptr);
std::strftime(buffer, sizeof(buffer), "Date: %a, %b %d %Y %H:%M:%S GMT", std::gmtime(&now));
return buffer;
}
std::string micro_rtsp_requests::rtsp_error(unsigned short code, const std::string &message)
{
std::ostringstream oss;
oss << "RTSP/1.0 " << code << " " << message << "\r\n"
<< "CSeq: " << cseq_ << "\r\n"
<< date_header() << "\r\n"
<< "\r\n";
return oss.str();
}
std::string micro_rtsp_requests::handle_option(const std::string &request)
{
log_i("handle_option");
std::ostringstream oss;
oss << "RTSP/1.0 200 OK\r\n"
<< "CSeq: " << cseq_ << "\r\n"
<< date_header() << "\r\n"
<< "Content-Length: 0\r\n"
<< "Public: DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE\r\n"
<< "\r\n";
return oss.str();
}
std::string micro_rtsp_requests::handle_describe(const std::string &request)
{
log_i("handle_describe");
if (!parse_stream_url(request))
return rtsp_error(400, "Invalid Stream Name");
if (stream_name_ != available_stream_name_)
return rtsp_error(404, "Stream Not Found");
std::ostringstream sdpos;
sdpos << "v=0\r\n"
<< "o=- " << host_port_ << " 1 IN IP4 " << std::rand() << "\r\n"
<< "s=\r\n"
<< "t=0 0\r\n" // start / stop - 0 -> unbounded and permanent session
<< "m=video 0 RTP/AVP 26\r\n" // currently we just handle UDP sessions
<< "c=IN IP4 0.0.0.0\r\n";
auto sdp = sdpos.str();
std::ostringstream oss;
oss << "RTSP/1.0 200 OK\r\n"
<< "CSeq: " << cseq_ << "\r\n"
<< date_header() << "\r\n"
<< "Content-Base: " << stream_name_ << "/\r\n"
<< "Content-Type: application/sdp\r\n"
<< "Content-Length: " << sdp.size() << "\r\n"
<< "\r\n"
<< sdp;
return oss.str();
}
std::string micro_rtsp_requests::handle_setup(const std::string &request)
{
tcp_transport_ = request.find("rtp/avp/tcp") != std::string::npos;
if (!parse_client_port(request))
return rtsp_error(400, "Could Not Find Client Port");
std::ostringstream ostransport;
if (tcp_transport_)
ostransport << "RTP/AVP/TCP;unicast;interleaved=0-1";
else
ostransport << "RTP/AVP;unicast;destination=127.0.0.1;source=127.0.0.1;client_port=" << client_port_ << "-" << client_port_ + 1 << ";server_port=" << rtp_streamer_port_ << "-" << rtcp_streamer_port_;
std::ostringstream oss;
oss << "RTSP/1.0 200 OK\r\n"
<< "CSeq: " << cseq_ << "\r\n"
<< date_header() << "\r\n"
<< "Transport: " << ostransport.str() << "\r\n"
<< "Session: " << rtsp_session_id_ << "\r\n"
<< "\r\n";
return oss.str();
}
std::string micro_rtsp_requests::handle_play(const std::string &request)
{
stream_active_ = true;
std::ostringstream oss;
oss << "RTSP/1.0 200 OK\r\n"
<< "CSeq: " << cseq_ << "\r\n"
<< date_header() << "\r\n"
<< "Range: npt=0.000-\r\n"
<< "Session: " << rtsp_session_id_ << "\r\n"
<< "RTP-Info: url=rtsp://127.0.0.1:8554/" << available_stream_name_ << "/track1\r\n"
<< "\r\n";
return oss.str();
}
std::string micro_rtsp_requests::handle_teardown(const std::string &request)
{
stream_stopped_ = true;
std::ostringstream oss;
oss << "RTSP/1.0 200 OK\r\n"
<< "CSeq: " << cseq_ << "\r\n"
<< date_header() << "\r\n"
<< "\r\n";
return oss.str();
}
std::string micro_rtsp_requests::process_request(const std::string &request)
{
log_i("handle_request");
auto command = parse_command(request);
if (command != rtsp_command_unknown)
{
if (!parse_cseq(request))
return rtsp_error(400, "No Sequence Found");
switch (command)
{
case rtsp_command_option:
return handle_option(request);
case rtsp_command_describe:
return handle_describe(request);
case rtsp_command_setup:
return handle_setup(request);
case rtsp_command_play:
return handle_play(request);
case rtsp_command_teardown:
return handle_teardown(request);
}
}
return rtsp_error(400, "Unknown Command");
}