This commit is contained in:
Rene Zeldenthuis
2024-02-11 00:42:55 +01:00
parent 9ba11162d8
commit 97fc4ceb33
26 changed files with 590 additions and 30 deletions

View File

@@ -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",

View File

@@ -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"
}
}

View File

@@ -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"
}
}

View File

@@ -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",

View File

@@ -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",

View File

@@ -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"
}
}

View File

@@ -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",

View File

@@ -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"
}
}

View File

@@ -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'"
],

View File

@@ -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"
}
}

View File

@@ -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"
}
}

View File

@@ -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"
}
}

View File

@@ -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"
}
}

View File

@@ -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"
}
}

View File

@@ -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'",

View File

@@ -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",

View 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_;
};

View 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;
};

View 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;
};

View 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"
}
}

View 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;
}

View 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 *);
};

View 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;
}

View 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();
}

View File

@@ -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

View File

@@ -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()