forked from external-repos/esp32cam-rtsp
216 lines
6.6 KiB
C++
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");
|
|
}
|