ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
rtsp_server.cpp
Go to the documentation of this file.
1#include "rtsp_server.h"
2
3#ifdef USE_CAMERA
4
5#include <cerrno>
6#include <cinttypes>
7#include <utility>
8
10#include "esphome/core/log.h"
11
13
14ESPHOME_LOG_TAG(TAG, "rtsp_server");
15
16void RTSPServer::socket_failed_(const char *what) {
17 ESP_LOGE(TAG, "Socket %s failed: errno %d", what, errno);
18 delete this->socket_;
19 this->socket_ = nullptr;
20 this->mark_failed();
21}
22
24 this->socket_ = socket::socket_ip_loop_monitored(SOCK_STREAM, 0).release();
25 if (this->socket_ == nullptr) {
26 ESP_LOGE(TAG, "Socket creation failed: errno %d", errno);
27 this->mark_failed();
28 return;
29 }
30
31 int enable = 1;
32 this->socket_->setsockopt(SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable));
33
34 if (this->socket_->setblocking(false) != 0) {
35 this->socket_failed_("nonblocking");
36 return;
37 }
38
39 struct sockaddr_storage server{};
40 socklen_t sl = socket::set_sockaddr_any((struct sockaddr *) &server, sizeof(server), this->port_);
41 if (sl == 0 || this->socket_->bind((struct sockaddr *) &server, sl) != 0) {
42 this->socket_failed_("bind");
43 return;
44 }
45
46 if (this->socket_->listen(4) != 0) {
47 this->socket_failed_("listen");
48 return;
49 }
50
52}
53
56 if (this->socket_ != nullptr && this->socket_->ready()) {
57 this->accept_new_connections_(now);
58 }
59
60 bool playing = false;
61 for (auto &session : this->sessions_) {
62 session.loop(now, this->port_);
63 playing |= session.is_playing();
64 }
65
66 if (playing != this->streaming_) {
67 this->streaming_ = playing;
68 if (playing) {
70 } else {
72 }
73 }
74}
75
77 while (true) {
78 struct sockaddr_storage source_addr;
79 socklen_t addr_len = sizeof(source_addr);
80
81 auto sock = this->socket_->accept_loop_monitored((struct sockaddr *) &source_addr, &addr_len);
82 if (!sock)
83 break;
84#ifdef USE_SOCKET_IPV4_ALLOW
85 if (!this->allow_.admit(reinterpret_cast<const struct sockaddr *>(&source_addr), addr_len, TAG, now))
86 continue;
87#endif
88
89 RTSPSession *free_slot = nullptr;
90 for (auto &session : this->sessions_) {
91 if (!session.is_active()) {
92 free_slot = &session;
93 break;
94 }
95 }
96 if (free_slot == nullptr) {
97 ESP_LOGW(TAG, "Max connections (%d) reached, rejecting connection", MAX_RTSP_CONNECTIONS);
98 continue;
99 }
100
101 ESP_LOGD(TAG, "Accepted RTSP connection");
102 free_slot->open(std::move(sock), now);
103 }
104}
105
106void RTSPServer::on_camera_image(const std::shared_ptr<camera::CameraImage> &image) {
107 if (!this->streaming_ || !image->was_requested_by(camera::RTSP_REQUESTER))
108 return;
109
110 JpegInfo jpeg;
111 if (!parse_jpeg_info(image->get_data_buffer(), image->get_data_length(), jpeg) ||
112 jpeg.width > RTP_JPEG_MAX_DIMENSION || jpeg.height > RTP_JPEG_MAX_DIMENSION) {
113 if (!this->frame_warned_) {
114 this->frame_warned_ = true;
115 ESP_LOGW(TAG,
116 "Dropping frame %" PRIu16 "x%" PRIu16 ": not a baseline 4:2:2/4:2:0 JPEG without restart markers"
117 " of at most %" PRIu16 " px per side",
118 jpeg.width, jpeg.height, RTP_JPEG_MAX_DIMENSION);
119 }
120 return;
121 }
122
124 for (auto &session : this->sessions_) {
125 session.on_new_frame(image, jpeg, now);
126 }
127}
128
130 ESP_LOGCONFIG(TAG,
131 "RTSP Server:\n"
132 " Port: %u\n"
133 " Max connections: %u",
134 this->port_, MAX_RTSP_CONNECTIONS);
135#ifdef USE_SOCKET_IPV4_ALLOW
136 this->allow_.dump_config(TAG);
137#endif
138}
139
141
142} // namespace esphome::rtsp_server
143
144#endif
uint32_t IRAM_ATTR HOT get_loop_component_start_time() const
Get the cached time in milliseconds from when the current component started its loop execution.
void mark_failed()
Mark this component as failed.
virtual void start_stream(CameraRequester requester)=0
virtual void stop_stream(CameraRequester requester)=0
virtual void add_listener(CameraListener *listener)=0
Add a listener to receive camera events.
static Camera * instance()
The singleton instance of the camera implementation.
Definition camera.cpp:18
void on_camera_image(const std::shared_ptr< camera::CameraImage > &image) override
socket::ListenSocket * socket_
Definition rtsp_server.h:38
float get_setup_priority() const override
void socket_failed_(const char *what)
std::array< RTSPSession, MAX_RTSP_CONNECTIONS > sessions_
Definition rtsp_server.h:39
void accept_new_connections_(uint32_t now)
A single RTSP client: one TCP connection carrying both the RTSP control channel and,...
void open(std::unique_ptr< socket::Socket > socket, uint32_t now)
bool ready() const
Check if the socket has buffered data ready to read.
Definition socket.h:106
int bind(const struct sockaddr *addr, socklen_t addrlen)
int setsockopt(int level, int optname, const void *optval, socklen_t optlen)
std::unique_ptr< BSDSocketImpl > accept_loop_monitored(struct sockaddr *addr, socklen_t *addrlen)
void dump_config(const char *tag) const
One config line per allowed network.
bool admit(const struct sockaddr *peer, socklen_t len, const char *tag, uint32_t now)
allows(peer), logging a refused peer at most once every REJECT_LOG_INTERVAL_MS.
uint16_t addr_len
uint32_t socklen_t
Definition headers.h:109
ESPHOME_LOG_TAG(TAG, "rtsp_server")
bool parse_jpeg_info(const uint8_t *data, size_t len, JpegInfo &out)
Scans JPEG marker segments (SOF0/1, DQT, SOS) for the dimensions, quantization tables and the start o...
constexpr float LATE
For components that should be initialized at the very end of the setup process.
Definition component.h:59
socklen_t set_sockaddr_any(struct sockaddr *addr, socklen_t addrlen, uint16_t port)
Set a sockaddr to the any address and specified port for the IP version used by socket_ip().
std::unique_ptr< ListenSocket > socket_ip_loop_monitored(int type, int protocol)
Definition socket.h:175
Application App
Global storage of Application pointer - only one Application can exist.
static void uint32_t
The fields of a JPEG frame that an RFC 2435 RTP/JPEG payload needs. Pointers refer into the frame.