mirror of
https://github.com/rzeldent/esp32cam-rtsp.git
synced 2025-11-12 03:06:22 +00:00
WIP
This commit is contained in:
@@ -31,7 +31,8 @@
|
||||
"'-D CAMERA_CONFIG_LEDC_TIMER=LEDC_TIMER_0'",
|
||||
"'-D CAMERA_CONFIG_LEDC_CHANNEL=LEDC_CHANNEL_0'",
|
||||
"'-D CAMERA_CONFIG_FB_COUNT=2'",
|
||||
"'-D CAMERA_CONFIG_FB_LOCATION=CAMERA_FB_IN_PSRAM'"
|
||||
"'-D CAMERA_CONFIG_FB_LOCATION=CAMERA_FB_IN_PSRAM'",
|
||||
"'-D CAMERA_CONFIG_SCCB_I2C_PORT=I2C_NUM_0'"
|
||||
],
|
||||
"f_cpu": "240000000L",
|
||||
"f_flash": "40000000L",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino":{
|
||||
"arduino": {
|
||||
"ldscript": "esp32s2_out.ld"
|
||||
},
|
||||
"core": "esp32",
|
||||
@@ -31,7 +31,9 @@
|
||||
"'-D CAMERA_CONFIG_LEDC_TIMER=LEDC_TIMER_0'",
|
||||
"'-D CAMERA_CONFIG_LEDC_CHANNEL=LEDC_CHANNEL_0'",
|
||||
"'-D CAMERA_CONFIG_FB_COUNT=2'",
|
||||
"'-D CAMERA_CONFIG_FB_LOCATION=CAMERA_FB_IN_PSRAM'" ],
|
||||
"'-D CAMERA_CONFIG_FB_LOCATION=CAMERA_FB_IN_PSRAM'",
|
||||
"'-D CAMERA_CONFIG_SCCB_I2C_PORT=I2C_NUM_0'"
|
||||
],
|
||||
"f_cpu": "240000000L",
|
||||
"f_flash": "80000000L",
|
||||
"flash_mode": "qio",
|
||||
@@ -58,4 +60,4 @@
|
||||
},
|
||||
"url": "https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/hw-reference/esp32s2/user-guide-esp-lyrap-cam-v1.1.html",
|
||||
"vendor": "Espressif"
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino":{
|
||||
"arduino": {
|
||||
"ldscript": "esp32s2_out.ld"
|
||||
},
|
||||
"core": "esp32",
|
||||
@@ -31,7 +31,9 @@
|
||||
"'-D CAMERA_CONFIG_LEDC_TIMER=LEDC_TIMER_0'",
|
||||
"'-D CAMERA_CONFIG_LEDC_CHANNEL=LEDC_CHANNEL_0'",
|
||||
"'-D CAMERA_CONFIG_FB_COUNT=2'",
|
||||
"'-D CAMERA_CONFIG_FB_LOCATION=CAMERA_FB_IN_PSRAM'" ],
|
||||
"'-D CAMERA_CONFIG_FB_LOCATION=CAMERA_FB_IN_PSRAM'",
|
||||
"'-D CAMERA_CONFIG_SCCB_I2C_PORT=I2C_NUM_0'"
|
||||
],
|
||||
"f_cpu": "240000000L",
|
||||
"f_flash": "80000000L",
|
||||
"flash_mode": "qio",
|
||||
@@ -58,4 +60,4 @@
|
||||
},
|
||||
"url": "https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/hw-reference/esp32s2/user-guide-esp-lyrap-cam-v1.1.html",
|
||||
"vendor": "Espressif"
|
||||
}
|
||||
}
|
||||
@@ -33,7 +33,8 @@
|
||||
"'-D CAMERA_CONFIG_LEDC_TIMER=LEDC_TIMER_0'",
|
||||
"'-D CAMERA_CONFIG_LEDC_CHANNEL=LEDC_CHANNEL_0'",
|
||||
"'-D CAMERA_CONFIG_FB_COUNT=2'",
|
||||
"'-D CAMERA_CONFIG_FB_LOCATION=CAMERA_FB_IN_PSRAM'"
|
||||
"'-D CAMERA_CONFIG_FB_LOCATION=CAMERA_FB_IN_PSRAM'",
|
||||
"'-D CAMERA_CONFIG_SCCB_I2C_PORT=I2C_NUM_0'"
|
||||
],
|
||||
"f_cpu": "240000000L",
|
||||
"f_flash": "80000000L",
|
||||
|
||||
@@ -33,7 +33,8 @@
|
||||
"'-D CAMERA_CONFIG_LEDC_TIMER=LEDC_TIMER_0'",
|
||||
"'-D CAMERA_CONFIG_LEDC_CHANNEL=LEDC_CHANNEL_0'",
|
||||
"'-D CAMERA_CONFIG_FB_COUNT=2'",
|
||||
"'-D CAMERA_CONFIG_FB_LOCATION=CAMERA_FB_IN_PSRAM'"
|
||||
"'-D CAMERA_CONFIG_FB_LOCATION=CAMERA_FB_IN_PSRAM'",
|
||||
"'-D CAMERA_CONFIG_SCCB_I2C_PORT=I2C_NUM_0'"
|
||||
],
|
||||
"f_cpu": "240000000L",
|
||||
"f_flash": "80000000L",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino":{
|
||||
"arduino": {
|
||||
"ldscript": "esp32_out.ld",
|
||||
"partitions": "huge_app.csv"
|
||||
},
|
||||
@@ -31,7 +31,8 @@
|
||||
"'-D CAMERA_CONFIG_LEDC_TIMER=LEDC_TIMER_0'",
|
||||
"'-D CAMERA_CONFIG_LEDC_CHANNEL=LEDC_CHANNEL_0'",
|
||||
"'-D CAMERA_CONFIG_FB_COUNT=1'",
|
||||
"'-D CAMERA_CONFIG_FB_LOCATION=CAMERA_FB_IN_PSRAM'"
|
||||
"'-D CAMERA_CONFIG_FB_LOCATION=CAMERA_FB_IN_PSRAM'",
|
||||
"'-D CAMERA_CONFIG_SCCB_I2C_PORT=I2C_NUM_0'"
|
||||
],
|
||||
"f_cpu": "240000000L",
|
||||
"f_flash": "40000000L",
|
||||
@@ -62,4 +63,4 @@
|
||||
},
|
||||
"url": "https://www.espressif.com/en/products/devkits/esp-eye/overview",
|
||||
"vendor": "Espressif"
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,8 @@
|
||||
"'-D CAMERA_CONFIG_LEDC_TIMER=LEDC_TIMER_0'",
|
||||
"'-D CAMERA_CONFIG_LEDC_CHANNEL=LEDC_CHANNEL_0'",
|
||||
"'-D CAMERA_CONFIG_FB_COUNT=2'",
|
||||
"'-D CAMERA_CONFIG_FB_LOCATION=CAMERA_FB_IN_PSRAM'"
|
||||
"'-D CAMERA_CONFIG_FB_LOCATION=CAMERA_FB_IN_PSRAM'",
|
||||
"'-D CAMERA_CONFIG_SCCB_I2C_PORT=I2C_NUM_0'"
|
||||
],
|
||||
"f_cpu": "240000000L",
|
||||
"f_flash": "40000000L",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino":{
|
||||
"arduino": {
|
||||
"ldscript": "esp32_out.ld",
|
||||
"partitions": "huge_app.csv"
|
||||
},
|
||||
@@ -28,6 +28,7 @@
|
||||
"'-D CAMERA_CONFIG_LEDC_CHANNEL=LEDC_CHANNEL_0'",
|
||||
"'-D CAMERA_CONFIG_FB_COUNT=1'",
|
||||
"'-D CAMERA_CONFIG_FB_LOCATION=CAMERA_FB_IN_DRAM'",
|
||||
"'-D CAMERA_CONFIG_SCCB_I2C_PORT=I2C_NUM_0'",
|
||||
"'-D GROVE_SDA=13'",
|
||||
"'-D GROVE_SCL=4'"
|
||||
],
|
||||
@@ -60,4 +61,4 @@
|
||||
},
|
||||
"url": "https://docs.m5stack.com/en/unit/m5camera",
|
||||
"vendor": "M5STACK"
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,7 @@
|
||||
"'-D CAMERA_CONFIG_LEDC_CHANNEL=LEDC_CHANNEL_0'",
|
||||
"'-D CAMERA_CONFIG_FB_COUNT=2'",
|
||||
"'-D CAMERA_CONFIG_FB_LOCATION=CAMERA_FB_IN_PSRAM'",
|
||||
"'-D CAMERA_CONFIG_SCCB_I2C_PORT=I2C_NUM_0'",
|
||||
"'-D GROVE_SDA=13'",
|
||||
"'-D GROVE_SCL=4'"
|
||||
],
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino":{
|
||||
"arduino": {
|
||||
"ldscript": "esp32_out.ld",
|
||||
"partitions": "huge_app.csv"
|
||||
},
|
||||
@@ -32,6 +32,7 @@
|
||||
"'-D CAMERA_CONFIG_LEDC_CHANNEL=LEDC_CHANNEL_0'",
|
||||
"'-D CAMERA_CONFIG_FB_COUNT=2'",
|
||||
"'-D CAMERA_CONFIG_FB_LOCATION=CAMERA_FB_IN_PSRAM'",
|
||||
"'-D CAMERA_CONFIG_SCCB_I2C_PORT=I2C_NUM_0'",
|
||||
"'-D MICROPHONE_GPIO=32'",
|
||||
"'-D GROVE_SDA=13'",
|
||||
"'-D GROVE_SCL=4'"
|
||||
@@ -65,4 +66,4 @@
|
||||
},
|
||||
"url": "https://docs.m5stack.com/en/unit/esp32cam",
|
||||
"vendor": "M5STACK"
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino":{
|
||||
"arduino": {
|
||||
"ldscript": "esp32_out.ld",
|
||||
"partitions": "huge_app.csv"
|
||||
},
|
||||
@@ -29,7 +29,8 @@
|
||||
"'-D CAMERA_CONFIG_LEDC_TIMER=LEDC_TIMER_0'",
|
||||
"'-D CAMERA_CONFIG_LEDC_CHANNEL=LEDC_CHANNEL_0'",
|
||||
"'-D CAMERA_CONFIG_FB_COUNT=1'",
|
||||
"'-D CAMERA_CONFIG_FB_LOCATION=CAMERA_FB_IN_DRAM'"
|
||||
"'-D CAMERA_CONFIG_FB_LOCATION=CAMERA_FB_IN_DRAM'",
|
||||
"'-D CAMERA_CONFIG_SCCB_I2C_PORT=I2C_NUM_0'"
|
||||
],
|
||||
"f_cpu": "240000000L",
|
||||
"f_flash": "40000000L",
|
||||
@@ -60,4 +61,4 @@
|
||||
},
|
||||
"url": "https://docs.m5stack.com/en/unit/unit_cam",
|
||||
"vendor": "M5STACK"
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,7 @@
|
||||
"'-D CAMERA_CONFIG_LEDC_CHANNEL=LEDC_CHANNEL_0'",
|
||||
"'-D CAMERA_CONFIG_FB_COUNT=2'",
|
||||
"'-D CAMERA_CONFIG_FB_LOCATION=CAMERA_FB_IN_DRAM'",
|
||||
"'-D CAMERA_CONFIG_SCCB_I2C_PORT=I2C_NUM_0'",
|
||||
"'-D I2C_MEMS_SDA=48'",
|
||||
"'-D I2C_MEMS_SCL=47'",
|
||||
"'-D TF_CS=9'",
|
||||
@@ -80,4 +81,4 @@
|
||||
},
|
||||
"url": "https://docs.m5stack.com/en/unit/Unit-CamS3",
|
||||
"vendor": "M5STACK"
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino":{
|
||||
"arduino": {
|
||||
"ldscript": "esp32_out.ld",
|
||||
"partitions": "huge_app.csv"
|
||||
},
|
||||
@@ -29,7 +29,8 @@
|
||||
"'-D CAMERA_CONFIG_LEDC_TIMER=LEDC_TIMER_0'",
|
||||
"'-D CAMERA_CONFIG_LEDC_CHANNEL=LEDC_CHANNEL_0'",
|
||||
"'-D CAMERA_CONFIG_FB_COUNT=2'",
|
||||
"'-D CAMERA_CONFIG_FB_LOCATION=CAMERA_FB_IN_PSRAM'"
|
||||
"'-D CAMERA_CONFIG_FB_LOCATION=CAMERA_FB_IN_PSRAM'",
|
||||
"'-D CAMERA_CONFIG_SCCB_I2C_PORT=I2C_NUM_0'"
|
||||
],
|
||||
"f_cpu": "240000000L",
|
||||
"f_flash": "40000000L",
|
||||
@@ -60,4 +61,4 @@
|
||||
},
|
||||
"url": "https://shop.m5stack.com/collections/m5-cameras",
|
||||
"vendor": "M5STACK"
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,7 @@
|
||||
"'-D CAMERA_CONFIG_LEDC_CHANNEL=LEDC_CHANNEL_0'",
|
||||
"'-D CAMERA_CONFIG_FB_COUNT=2'",
|
||||
"'-D CAMERA_CONFIG_FB_LOCATION=CAMERA_FB_IN_PSRAM'",
|
||||
"'-D CAMERA_CONFIG_SCCB_I2C_PORT=I2C_NUM_0'",
|
||||
"'-D I2C_MEMS_SDA=41'",
|
||||
"'-D I2C_MEMS_SCL=42'",
|
||||
"'-D TF_CS=21'",
|
||||
@@ -80,4 +81,4 @@
|
||||
},
|
||||
"url": "https://www.seeedstudio.com/XIAO-ESP32S3-p-5627.html",
|
||||
"vendor": "Seeed Studio"
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,7 @@
|
||||
"'-D CAMERA_CONFIG_LEDC_CHANNEL=LEDC_CHANNEL_0'",
|
||||
"'-D CAMERA_CONFIG_FB_COUNT=1'",
|
||||
"'-D CAMERA_CONFIG_FB_LOCATION=CAMERA_FB_IN_DRAM'",
|
||||
"'-D CAMERA_CONFIG_SCCB_I2C_PORT=I2C_NUM_0'",
|
||||
"'-D LCD_SSD1306_PIN_SDA=21'",
|
||||
"'-D LCD_SSD1306_PIN_SCL=22'",
|
||||
"'-D BUTTON_RIGHT_PIN=34'",
|
||||
|
||||
@@ -27,7 +27,8 @@
|
||||
"'-D CAMERA_CONFIG_LEDC_TIMER=LEDC_TIMER_0'",
|
||||
"'-D CAMERA_CONFIG_LEDC_CHANNEL=LEDC_CHANNEL_0'",
|
||||
"'-D CAMERA_CONFIG_FB_COUNT=1'",
|
||||
"'-D CAMERA_CONFIG_FB_LOCATION=CAMERA_FB_IN_DRAM'"
|
||||
"'-D CAMERA_CONFIG_FB_LOCATION=CAMERA_FB_IN_DRAM'",
|
||||
"'-D CAMERA_CONFIG_SCCB_I2C_PORT=I2C_NUM_0'"
|
||||
],
|
||||
"f_cpu": "240000000L",
|
||||
"f_flash": "40000000L",
|
||||
|
||||
74
lib/micro-rtsp-server/include/micro_rtsp_server.h
Normal file
74
lib/micro-rtsp-server/include/micro_rtsp_server.h
Normal file
@@ -0,0 +1,74 @@
|
||||
#pragma once
|
||||
|
||||
#include <WiFiServer.h>
|
||||
#include <micro_rtsp_source.h>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
|
||||
class micro_rtsp_server : public WiFiServer
|
||||
{
|
||||
public:
|
||||
micro_rtsp_server(micro_rtsp_source *source, unsigned frame_interval = 100, unsigned short port = 554);
|
||||
~micro_rtsp_server();
|
||||
|
||||
void loop();
|
||||
|
||||
unsigned get_frame_interval() { return frame_interval_; }
|
||||
unsigned set_frame_interval(unsigned value) { return frame_interval_ = value; }
|
||||
|
||||
size_t clients() { return clients_.size(); }
|
||||
|
||||
class rtsp_client
|
||||
{
|
||||
public:
|
||||
rtsp_client(const WiFiClient &wifi_client);
|
||||
void handle_request();
|
||||
|
||||
private:
|
||||
enum rtsp_command
|
||||
{
|
||||
rtsp_command_unknown,
|
||||
rtsp_command_option, // OPTIONS
|
||||
rtsp_command_describe, // DESCRIBE
|
||||
rtsp_command_setup, // SETUP
|
||||
rtsp_command_play, // PLAY
|
||||
rtsp_command_teardown // TEARDOWN
|
||||
};
|
||||
|
||||
rtsp_command parse_command(const String& request);
|
||||
unsigned long parse_cseq(const String& request);
|
||||
bool parse_stream_url(const String& request);
|
||||
|
||||
int parse_client_port(const String& request);
|
||||
|
||||
|
||||
WiFiClient wifi_client_;
|
||||
|
||||
bool tcp_transport_;
|
||||
|
||||
String host_url_;
|
||||
unsigned short host_port_;
|
||||
String stream_name_;
|
||||
uint stream_id_;
|
||||
|
||||
unsigned short rtp_port_;
|
||||
// enum rtsp_state state_;
|
||||
|
||||
String date_header();
|
||||
|
||||
void handle_option(unsigned long cseq);
|
||||
void handle_describe(unsigned long cseq, const String& stream_url);
|
||||
void handle_setup(unsigned long cseq, const String& stream_url);
|
||||
void handle_play();
|
||||
void handle_teardown();
|
||||
};
|
||||
|
||||
private:
|
||||
micro_rtsp_source *source_;
|
||||
|
||||
unsigned frame_interval_;
|
||||
unsigned long next_frame_update_;
|
||||
|
||||
unsigned long next_check_client_;
|
||||
std::list<rtsp_client *> clients_;
|
||||
};
|
||||
15
lib/micro-rtsp-server/include/micro_rtsp_source.h
Normal file
15
lib/micro-rtsp-server/include/micro_rtsp_source.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
class micro_rtsp_source
|
||||
{
|
||||
public:
|
||||
virtual void update_frame();
|
||||
|
||||
virtual uint8_t *data() const = 0;
|
||||
virtual size_t width() const = 0;
|
||||
virtual size_t height() const = 0;
|
||||
virtual size_t size() const = 0;
|
||||
};
|
||||
27
lib/micro-rtsp-server/include/micro_rtsp_source_camera.h
Normal file
27
lib/micro-rtsp-server/include/micro_rtsp_source_camera.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <micro_rtsp_source.h>
|
||||
|
||||
#include <esp_camera.h>
|
||||
|
||||
class micro_rtsp_source_camera : public micro_rtsp_source
|
||||
{
|
||||
public:
|
||||
micro_rtsp_source_camera();
|
||||
virtual ~micro_rtsp_source_camera();
|
||||
|
||||
esp_err_t initialize(camera_config_t *camera_config);
|
||||
esp_err_t deinitialize();
|
||||
// sensor_t* esp_camera_sensor_get();
|
||||
|
||||
void update_frame();
|
||||
|
||||
uint8_t *data() const { return fb->buf; }
|
||||
size_t width() const { return fb->width; }
|
||||
size_t height() const { return fb->height; }
|
||||
size_t size() const { return fb->len; }
|
||||
|
||||
private:
|
||||
esp_err_t init_result;
|
||||
camera_fb_t *fb;
|
||||
};
|
||||
19
lib/micro-rtsp-server/library.json
Normal file
19
lib/micro-rtsp-server/library.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "micro-rtsp-streamer",
|
||||
"version": "1.0.0",
|
||||
"description": "RTSP Server",
|
||||
"keywords": "",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/rzeldent/micro-rtsp-streamer"
|
||||
},
|
||||
|
||||
"build": {
|
||||
"srcDir": "src/",
|
||||
"includeDir": "include/"
|
||||
},
|
||||
|
||||
"dependencies": {
|
||||
"espressif/esp32-camera": "^2.0.4"
|
||||
}
|
||||
}
|
||||
53
lib/micro-rtsp-server/rtsp_server.cpp
Normal file
53
lib/micro-rtsp-server/rtsp_server.cpp
Normal file
@@ -0,0 +1,53 @@
|
||||
#include "rtsp_server.h"
|
||||
#include <esp32-hal-log.h>
|
||||
|
||||
|
||||
// URI: e.g. rtsp://192.168.178.27:554/mjpeg/1
|
||||
rtsp_server::rtsp_server(OV2640 &cam, unsigned long interval, int port /*= 554*/)
|
||||
: WiFiServer(port), cam_(cam)
|
||||
{
|
||||
log_i("Starting RTSP server");
|
||||
_streamer = new OV2640Streamer()
|
||||
WiFiServer::begin();
|
||||
timer_.every(interval, client_handler, this);
|
||||
}
|
||||
|
||||
size_t rtsp_server::num_connected()
|
||||
{
|
||||
return clients_.size();
|
||||
}
|
||||
|
||||
void rtsp_server::doLoop()
|
||||
{
|
||||
timer_.tick();
|
||||
}
|
||||
|
||||
rtsp_server::rtsp_client::rtsp_client(const WiFiClient &client, OV2640 &cam)
|
||||
{
|
||||
wifi_client = client;
|
||||
streamer = std::shared_ptr<OV2640Streamer>(new OV2640Streamer(&wifi_client, cam));
|
||||
session = std::shared_ptr<CRtspSession>(new CRtspSession(&wifi_client, streamer.get()));
|
||||
}
|
||||
|
||||
bool rtsp_server::client_handler(void *arg)
|
||||
{
|
||||
auto self = static_cast<rtsp_server *>(arg);
|
||||
// Check if a client wants to connect
|
||||
auto new_client = self->accept();
|
||||
if (new_client)
|
||||
self->clients_.push_back(std::unique_ptr<rtsp_client>(new rtsp_client(new_client, self->cam_)));
|
||||
|
||||
auto now = millis();
|
||||
for (const auto &client : self->clients_)
|
||||
{
|
||||
// Handle requests
|
||||
client->session->handleRequests(0);
|
||||
// Send the frame. For now return the uptime as time marker, currMs
|
||||
client->session->broadcastCurrentFrame(now);
|
||||
}
|
||||
|
||||
self->clients_.remove_if([](std::unique_ptr<rtsp_client> const &c)
|
||||
{ return c->session->m_stopped; });
|
||||
|
||||
return true;
|
||||
}
|
||||
39
lib/micro-rtsp-server/rtsp_server.h
Normal file
39
lib/micro-rtsp-server/rtsp_server.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include <list>
|
||||
#include <WiFiServer.h>
|
||||
#include <ESPmDNS.h>
|
||||
#include <OV2640.h>
|
||||
#include <CRtspSession.h>
|
||||
#include <arduino-timer.h>
|
||||
#include <OV2640Streamer.h>
|
||||
|
||||
class rtsp_server : public WiFiServer
|
||||
{
|
||||
public:
|
||||
rtsp_server(OV2640 &cam, unsigned long interval, int port = 554);
|
||||
|
||||
void doLoop();
|
||||
|
||||
size_t num_connected();
|
||||
|
||||
private:
|
||||
struct rtsp_client
|
||||
{
|
||||
public:
|
||||
rtsp_client(const WiFiClient &client, OV2640 &cam);
|
||||
|
||||
WiFiClient wifi_client;
|
||||
// RTSP session and state
|
||||
std::shared_ptr<CRtspSession> session;
|
||||
};
|
||||
|
||||
OV2640 cam_;
|
||||
OV2640Streamer _streamer;
|
||||
|
||||
std::list<std::unique_ptr<rtsp_client>> clients_;
|
||||
uintptr_t task_;
|
||||
Timer<> timer_;
|
||||
|
||||
static bool client_handler(void *);
|
||||
};
|
||||
269
lib/micro-rtsp-server/src/micro_rtsp_server.cpp
Normal file
269
lib/micro-rtsp-server/src/micro_rtsp_server.cpp
Normal file
@@ -0,0 +1,269 @@
|
||||
#include <micro_rtsp_server.h>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
// Check client connections every 100 milliseconds
|
||||
#define CHECK_CLIENT_INTERVAL 100
|
||||
|
||||
micro_rtsp_server::micro_rtsp_server(micro_rtsp_source *source, unsigned frame_interval /*= 100*/, unsigned short port /*= 554*/)
|
||||
{
|
||||
log_i("starting RTSP server");
|
||||
|
||||
frame_interval_ = frame_interval;
|
||||
source_ = source;
|
||||
WiFiServer::begin();
|
||||
}
|
||||
|
||||
micro_rtsp_server::~micro_rtsp_server()
|
||||
{
|
||||
}
|
||||
|
||||
void micro_rtsp_server::loop()
|
||||
{
|
||||
auto now = millis();
|
||||
|
||||
if (next_check_client_ < now)
|
||||
{
|
||||
next_check_client_ = now + CHECK_CLIENT_INTERVAL;
|
||||
|
||||
// Check if a client wants to connect
|
||||
auto client = accept();
|
||||
if (client)
|
||||
clients_.push_back(new rtsp_client(client));
|
||||
|
||||
// Check for idle clients
|
||||
// clients_.remove_if([](std::unique_ptr<rtsp_client> const &c)
|
||||
// { return c->session->m_stopped; });
|
||||
}
|
||||
|
||||
if (next_frame_update_ < now)
|
||||
{
|
||||
next_frame_update_ = now + frame_interval_;
|
||||
|
||||
for (const auto &client : clients_)
|
||||
{
|
||||
// Handle requests
|
||||
client->handle_request();
|
||||
// Send the frame. For now return the uptime as time marker, currMs
|
||||
// client->session->broadcastCurrentFrame(now);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
micro_rtsp_server::rtsp_client::rtsp_client(const WiFiClient &wifi_client)
|
||||
{
|
||||
wifi_client_ = wifi_client;
|
||||
// state_ = rtsp_state_unknown;
|
||||
}
|
||||
|
||||
micro_rtsp_server::rtsp_client::rtsp_command micro_rtsp_server::rtsp_client::parse_command(const String &request)
|
||||
{
|
||||
log_i("parse_command");
|
||||
// Check for RTSP commands: Option, Describe, Setup, Play, Teardown
|
||||
if (request.startsWith("option"))
|
||||
return rtsp_command::rtsp_command_option;
|
||||
|
||||
if (request.startsWith("describe"))
|
||||
return rtsp_command::rtsp_command_describe;
|
||||
|
||||
if (request.startsWith("setup"))
|
||||
return rtsp_command::rtsp_command_setup;
|
||||
|
||||
if (request.startsWith("play"))
|
||||
return rtsp_command::rtsp_command_play;
|
||||
|
||||
if (request.startsWith("teardown"))
|
||||
return rtsp_command::rtsp_command_teardown;
|
||||
|
||||
return rtsp_command::rtsp_command_unknown;
|
||||
}
|
||||
|
||||
int micro_rtsp_server::rtsp_client::parse_client_port(const String &request)
|
||||
{
|
||||
log_i("parse_client_port");
|
||||
auto pos = request.indexOf("client_port=");
|
||||
if (pos < 0)
|
||||
{
|
||||
log_e("client_port not found");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return strtoul(&request.c_str()[pos + 12], nullptr, 10);
|
||||
}
|
||||
|
||||
unsigned long micro_rtsp_server::rtsp_client::parse_cseq(const String &request)
|
||||
{
|
||||
log_i("parse_cseq");
|
||||
auto pos = request.indexOf("cseq: ");
|
||||
if (pos < 0)
|
||||
{
|
||||
log_e("CSeq not found");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return strtoul(&request.c_str()[pos + 6], nullptr, 10);
|
||||
}
|
||||
|
||||
bool micro_rtsp_server::rtsp_client::parse_stream_url(const String &request)
|
||||
{
|
||||
log_i("parse_host_url");
|
||||
// SETUP rtsp://192.168.10.93:1234/mjpeg/1 RTSP/1.0
|
||||
auto pos = request.indexOf("rtsp://");
|
||||
if (pos < 0)
|
||||
{
|
||||
log_e("rtsp:// not found");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Look for :
|
||||
auto start = pos + 7;
|
||||
pos = request.indexOf(':', start);
|
||||
if (pos < 0)
|
||||
{
|
||||
log_e("parse stream: no host url/post found (: missing)");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Should be 192.168.10.93
|
||||
host_url_ = request.substring(start, pos);
|
||||
// Should be 1234
|
||||
host_port_ = (unsigned short)(&request.c_str()[pos + 1], nullptr, 10);
|
||||
|
||||
start = pos + 1;
|
||||
pos = request.indexOf('/', start);
|
||||
if (pos < 0)
|
||||
{
|
||||
log_e("parse stream: no host port found (/ missing)");
|
||||
return false;
|
||||
}
|
||||
|
||||
start = pos + 1;
|
||||
pos = request.indexOf(' ', start);
|
||||
if (pos < 0)
|
||||
{
|
||||
log_e("parse stream: no stream found (' ' missing)");
|
||||
return false;
|
||||
}
|
||||
|
||||
// should be mjpeg/1
|
||||
stream_name_ = request.substring(start, pos);
|
||||
return true;
|
||||
}
|
||||
|
||||
void micro_rtsp_server::rtsp_client::handle_request()
|
||||
{
|
||||
log_i("handle_request");
|
||||
// Read if data available
|
||||
auto available = wifi_client_.available();
|
||||
if (available > 0)
|
||||
{
|
||||
auto request = wifi_client_.readString();
|
||||
// Make response lowercase
|
||||
request.toLowerCase();
|
||||
|
||||
auto command = parse_command(request);
|
||||
if (command == rtsp_command_unknown)
|
||||
{
|
||||
log_w("Invalid RTSP command received: %s", request.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned long cseq = parse_cseq(request);
|
||||
if (cseq == 0)
|
||||
{
|
||||
log_w("Invalid sequence: %s", request.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
switch (command)
|
||||
{
|
||||
case rtsp_command_option:
|
||||
handle_option(cseq);
|
||||
break;
|
||||
case rtsp_command_describe:
|
||||
handle_describe(cseq, request);
|
||||
break;
|
||||
case rtsp_command_setup:
|
||||
handle_setup(cseq, request);
|
||||
break;
|
||||
case rtsp_command_play:
|
||||
handle_play();
|
||||
break;
|
||||
case rtsp_command_teardown:
|
||||
handle_teardown();
|
||||
break;
|
||||
default:
|
||||
log_e("unknown rtsp_command");
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String micro_rtsp_server::rtsp_client::date_header()
|
||||
{
|
||||
char buffer[200];
|
||||
auto now = time(nullptr);
|
||||
strftime(buffer, sizeof(buffer), "Date: %a, %b %d %Y %H:%M:%S GMT", gmtime(&now));
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void micro_rtsp_server::rtsp_client::handle_option(unsigned long cseq)
|
||||
{
|
||||
log_i("handle_option");
|
||||
auto response = String("RTSP/1.0 200 OK\r\n") +
|
||||
String("CSeq: ") + String(cseq) + String("\r\n") +
|
||||
String("Content-Length: 0\r\n") +
|
||||
String("Public: DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE\r\n") +
|
||||
String("\r\n");
|
||||
wifi_client_.write(response.c_str());
|
||||
}
|
||||
|
||||
void micro_rtsp_server::rtsp_client::handle_describe(unsigned long cseq, const String &request)
|
||||
{
|
||||
log_i("handle_describe");
|
||||
if (!parse_stream_url(request))
|
||||
{
|
||||
log_w("Unable to parse stream url", request.c_str());
|
||||
auto response = String("RTSP/1.0 404 Stream Not Found\r\n") +
|
||||
String("CSeq: ") + String(cseq) + String("\r\n") +
|
||||
date_header() + String("\r\n");
|
||||
wifi_client_.write(response.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (stream_name_ != "mjpeg/1")
|
||||
{
|
||||
log_w("stream %s was requested but is not available", stream_name_.c_str());
|
||||
auto response = String("RTSP/1.0 404 Stream Not Found\r\n") +
|
||||
String("CSeq: ") + String(cseq) + String("\r\n") +
|
||||
date_header() +
|
||||
String("\r\n");
|
||||
wifi_client_.write(response.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
auto sdp = String("v=0\r\n") +
|
||||
String("o=- ") + String(host_port_) + String(" 1 IN IP4 ") + String(rand()) + String("\r\n") +
|
||||
String("s=\r\n") +
|
||||
String("t=0 0\r\n") + // start / stop - 0 -> unbounded and permanent session
|
||||
String("m=video 0 RTP/AVP 26\r\n") + // currently we just handle UDP sessions
|
||||
String("c=IN IP4 0.0.0.0\r\n");
|
||||
|
||||
auto response =
|
||||
String("RTSP/1.0 200 OK\r\n") +
|
||||
String("CSeq: ") + String(cseq) + String("\r\n") +
|
||||
date_header() + String("\r\n") +
|
||||
String("Content-Base: ") + stream_name_ + ("/\r\n") +
|
||||
String("Content-Type: application/sdp\r\n") +
|
||||
String("Content-Length: ") + String(sdp.length()) + String("\r\n") +
|
||||
String("\r\n") +
|
||||
sdp;
|
||||
|
||||
wifi_client_.write(response.c_str());
|
||||
}
|
||||
|
||||
void micro_rtsp_server::rtsp_client::handle_setup(unsigned long cseq, const String &request)
|
||||
{
|
||||
tcp_transport_ = request.indexOf("rtp/avp/tcp") >= 0;
|
||||
|
||||
}
|
||||
36
lib/micro-rtsp-server/src/micro_rtsp_source_camera.cpp
Normal file
36
lib/micro-rtsp-server/src/micro_rtsp_source_camera.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "micro_rtsp_source_camera.h"
|
||||
|
||||
micro_rtsp_source_camera::micro_rtsp_source_camera()
|
||||
{
|
||||
init_result == ESP_FAIL;
|
||||
}
|
||||
|
||||
micro_rtsp_source_camera::~micro_rtsp_source_camera()
|
||||
{
|
||||
deinitialize();
|
||||
}
|
||||
|
||||
esp_err_t micro_rtsp_source_camera::initialize(camera_config_t *camera_config)
|
||||
{
|
||||
|
||||
init_result = esp_camera_init(camera_config);
|
||||
if (init_result == ESP_OK)
|
||||
update_frame();
|
||||
else
|
||||
log_e("Camera initialization failed: 0x%x", init_result);
|
||||
}
|
||||
|
||||
esp_err_t micro_rtsp_source_camera::deinitialize()
|
||||
{
|
||||
return init_result == ESP_OK ? esp_camera_deinit() : ESP_OK;
|
||||
}
|
||||
|
||||
void micro_rtsp_source_camera::update_frame()
|
||||
{
|
||||
if (fb)
|
||||
esp_camera_fb_return(fb);
|
||||
|
||||
fb = esp_camera_fb_get();
|
||||
}
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
###############################################################################
|
||||
[platformio]
|
||||
#default_envs = esp32cam_ai_thinker
|
||||
default_envs = esp32cam_ai_thinker
|
||||
#default_envs = esp32cam_espressif_esp_eye
|
||||
#default_envs = esp32cam_espressif_esp32s2_cam_board
|
||||
#default_envs = esp32cam_espressif_esp32s2_cam_header
|
||||
|
||||
16
src/main.cpp
16
src/main.cpp
@@ -16,6 +16,9 @@
|
||||
#include <moustache.h>
|
||||
#include <settings.h>
|
||||
|
||||
#include <micro_rtsp_server.h>
|
||||
#include <micro_rtsp_source_camera.h>
|
||||
|
||||
// HTML files
|
||||
extern const char index_html_min_start[] asm("_binary_html_index_min_html_start");
|
||||
|
||||
@@ -48,10 +51,13 @@ auto param_colorbar = iotwebconf::Builder<iotwebconf::CheckboxTParameter>("cb").
|
||||
|
||||
// Camera
|
||||
OV2640 cam;
|
||||
micro_rtsp_source_camera source_camera;
|
||||
// DNS Server
|
||||
DNSServer dnsServer;
|
||||
// RTSP Server
|
||||
std::unique_ptr<rtsp_server> camera_server;
|
||||
|
||||
micro_rtsp_server micro_rtsp_server(&source_camera, RTSP_PORT);
|
||||
// Web server
|
||||
WebServer web_server(80);
|
||||
|
||||
@@ -219,7 +225,9 @@ esp_err_t initialize_camera()
|
||||
log_i("JPEG quality: %d", param_jpg_quality.value());
|
||||
auto jpeg_quality = param_jpg_quality.value();
|
||||
log_i("Frame duration: %d ms", param_frame_duration.value());
|
||||
constexpr auto i2c_port = I2C_NUM_0;
|
||||
|
||||
// Set frame duration
|
||||
micro_rtsp_server.set_frame_interval(param_frame_duration.value());
|
||||
|
||||
camera_config_t camera_config = {
|
||||
.pin_pwdn = CAMERA_CONFIG_PIN_PWDN, // GPIO pin for camera power down line
|
||||
@@ -250,10 +258,12 @@ esp_err_t initialize_camera()
|
||||
#if CONFIG_CAMERA_CONVERTER_ENABLED
|
||||
conv_mode = CONV_DISABLE, // RGB<->YUV Conversion mode
|
||||
#endif
|
||||
.sccb_i2c_port = i2c_port // If pin_sccb_sda is -1, use the already configured I2C bus by number
|
||||
.sccb_i2c_port = CAMERA_CONFIG_SCCB_I2C_PORT // If pin_sccb_sda is -1, use the already configured I2C bus by number
|
||||
};
|
||||
|
||||
return cam.init(camera_config);
|
||||
return source_camera.initialize(&camera_config);
|
||||
|
||||
// return cam.init(camera_config);
|
||||
}
|
||||
|
||||
void update_camera_settings()
|
||||
|
||||
Reference in New Issue
Block a user