forked from external-repos/esp32cam-rtsp
Work in progress
This commit is contained in:
@@ -13,9 +13,36 @@ typedef struct camera_config_entry
|
|||||||
|
|
||||||
constexpr const camera_config_entry_t camera_configs[] = {
|
constexpr const camera_config_entry_t camera_configs[] = {
|
||||||
|
|
||||||
{"ESP32CAM", {.pin_pwdn = -1, .pin_reset = 15, .pin_xclk = 27, .pin_sscb_sda = 25, .pin_sscb_scl = 23, .pin_d7 = 19, .pin_d6 = 36, .pin_d5 = 18, .pin_d4 = 39, .pin_d3 = 5, .pin_d2 = 34, .pin_d1 = 35, .pin_d0 = 17, .pin_vsync = 22, .pin_href = 26, .pin_pclk = 21, .xclk_freq_hz = 20000000, .ledc_timer = LEDC_TIMER_0, .ledc_channel = LEDC_CHANNEL_0, .pixel_format = PIXFORMAT_JPEG, .frame_size = FRAMESIZE_SVGA, .jpeg_quality = 12, .fb_count = 2}},
|
{"ESP32CAM",
|
||||||
{"AI THINKER", {.pin_pwdn = 32, .pin_reset = -1, .pin_xclk = 0, .pin_sscb_sda = 26, .pin_sscb_scl = 27, .pin_d7 = 35, .pin_d6 = 34, .pin_d5 = 39, .pin_d4 = 36, .pin_d3 = 21, .pin_d2 = 19, .pin_d1 = 18, .pin_d0 = 5, .pin_vsync = 25, .pin_href = 23, .pin_pclk = 22, .xclk_freq_hz = 20000000, .ledc_timer = LEDC_TIMER_1, .ledc_channel = LEDC_CHANNEL_1, .pixel_format = PIXFORMAT_JPEG, .frame_size = FRAMESIZE_SVGA, .jpeg_quality = 12, .fb_count = 2}},
|
{.pin_pwdn = -1,
|
||||||
{"TTGO T-CAM", {.pin_pwdn = 26, .pin_reset = -1, .pin_xclk = 32, .pin_sscb_sda = 13, .pin_sscb_scl = 12, .pin_d7 = 39, .pin_d6 = 36, .pin_d5 = 23, .pin_d4 = 18, .pin_d3 = 15, .pin_d2 = 4, .pin_d1 = 14, .pin_d0 = 5, .pin_vsync = 27, .pin_href = 25, .pin_pclk = 19, .xclk_freq_hz = 20000000, .ledc_timer = LEDC_TIMER_0, .ledc_channel = LEDC_CHANNEL_0, .pixel_format = PIXFORMAT_JPEG, .frame_size = FRAMESIZE_SVGA, .jpeg_quality = 12, .fb_count = 2}}};
|
.pin_reset = 15,
|
||||||
|
.pin_xclk = 27,
|
||||||
|
.pin_sscb_sda = 25,
|
||||||
|
.pin_sscb_scl = 23,
|
||||||
|
.pin_d7 = 19,
|
||||||
|
.pin_d6 = 36,
|
||||||
|
.pin_d5 = 18,
|
||||||
|
.pin_d4 = 39,
|
||||||
|
.pin_d3 = 5,
|
||||||
|
.pin_d2 = 34,
|
||||||
|
.pin_d1 = 35,
|
||||||
|
.pin_d0 = 17,
|
||||||
|
.pin_vsync = 22,
|
||||||
|
.pin_href = 26,
|
||||||
|
.pin_pclk = 21,
|
||||||
|
.xclk_freq_hz = 20000000,
|
||||||
|
.ledc_timer = LEDC_TIMER_0,
|
||||||
|
.ledc_channel = LEDC_CHANNEL_0,
|
||||||
|
.pixel_format = PIXFORMAT_JPEG,
|
||||||
|
.frame_size = FRAMESIZE_SVGA,
|
||||||
|
.jpeg_quality = 12,
|
||||||
|
.fb_count = 2}},
|
||||||
|
{"AI THINKER",
|
||||||
|
{.pin_pwdn = 32,
|
||||||
|
.pin_reset = -1, .pin_xclk = 0, .pin_sscb_sda = 26, .pin_sscb_scl = 27, .pin_d7 = 35, .pin_d6 = 34, .pin_d5 = 39, .pin_d4 = 36, .pin_d3 = 21, .pin_d2 = 19, .pin_d1 = 18, .pin_d0 = 5, .pin_vsync = 25, .pin_href = 23, .pin_pclk = 22, .xclk_freq_hz = 20000000, .ledc_timer = LEDC_TIMER_1, .ledc_channel = LEDC_CHANNEL_1, .pixel_format = PIXFORMAT_JPEG, .frame_size = FRAMESIZE_SVGA, .jpeg_quality = 12, .fb_count = 2}},
|
||||||
|
{"TTGO T-CAM",
|
||||||
|
{.pin_pwdn = 26,
|
||||||
|
.pin_reset = -1, .pin_xclk = 32, .pin_sscb_sda = 13, .pin_sscb_scl = 12, .pin_d7 = 39, .pin_d6 = 36, .pin_d5 = 23, .pin_d4 = 18, .pin_d3 = 15, .pin_d2 = 4, .pin_d1 = 14, .pin_d0 = 5, .pin_vsync = 27, .pin_href = 25, .pin_pclk = 19, .xclk_freq_hz = 20000000, .ledc_timer = LEDC_TIMER_0, .ledc_channel = LEDC_CHANNEL_0, .pixel_format = PIXFORMAT_JPEG, .frame_size = FRAMESIZE_SVGA, .jpeg_quality = 12, .fb_count = 2}}};
|
||||||
|
|
||||||
const camera_config_t lookup_camera_config(const char *pin)
|
const camera_config_t lookup_camera_config(const char *pin)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -9,6 +9,6 @@
|
|||||||
|
|
||||||
#define RTSP_PORT 554
|
#define RTSP_PORT 554
|
||||||
#define DEFAULT_CAMERA_CONFIG "AI THINKER"
|
#define DEFAULT_CAMERA_CONFIG "AI THINKER"
|
||||||
#define DEFAULT_FRAMERATE "20"
|
#define DEFAULT_FRAMEDURATION "20"
|
||||||
#define DEFAULT_FRAMESIZE "SVGA (800x600)"
|
#define DEFAULT_FRAMESIZE "SVGA (800x600)"
|
||||||
#define DEFAULT_JPEG_QUALITY "12"
|
#define DEFAULT_JPEG_QUALITY "12"
|
||||||
5
lib/template_render/library.json
Normal file
5
lib/template_render/library.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"name": "template_render",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "A mini template renderer"
|
||||||
|
}
|
||||||
19
lib/template_render/template_render.h
Normal file
19
lib/template_render/template_render.h
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Wstring.h>
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
const char *key;
|
||||||
|
const String value;
|
||||||
|
} template_substitution_t;
|
||||||
|
|
||||||
|
template<typename T, size_t n>
|
||||||
|
inline String template_render(const char *format, T (&values)[n])
|
||||||
|
{
|
||||||
|
auto s = String(format);
|
||||||
|
for (size_t i=0; i<n; i++)
|
||||||
|
s.replace("{{" + String(values[n].key) + "}}", values[n].value);
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
97
src/main.cpp
97
src/main.cpp
@@ -9,16 +9,17 @@
|
|||||||
#include <camera_config.h>
|
#include <camera_config.h>
|
||||||
#include <format_duration.h>
|
#include <format_duration.h>
|
||||||
#include <format_si.h>
|
#include <format_si.h>
|
||||||
|
#include <template_render.h>
|
||||||
#include <settings.h>
|
#include <settings.h>
|
||||||
|
|
||||||
char camera_config_val[sizeof(camera_config_entry)];
|
char camera_config_val[sizeof(camera_config_entry)];
|
||||||
char frame_rate_val[6];
|
char frame_duration_val[6];
|
||||||
char frame_size_val[sizeof(frame_size_entry_t)];
|
char frame_size_val[sizeof(frame_size_entry_t)];
|
||||||
char jpeg_quality_val[4];
|
char jpeg_quality_val[4];
|
||||||
|
|
||||||
auto config_group_stream_settings = iotwebconf::ParameterGroup("settings", "Streaming settings");
|
auto config_group_stream_settings = iotwebconf::ParameterGroup("settings", "Streaming settings");
|
||||||
auto config_camera_config = iotwebconf::SelectParameter("Camera config", "config", camera_config_val, sizeof(camera_config_val), (const char *)camera_configs, (const char *)camera_configs, sizeof(camera_configs) / sizeof(camera_configs[0]), sizeof(camera_configs[0]), DEFAULT_CAMERA_CONFIG);
|
auto config_camera_config = iotwebconf::SelectParameter("Camera config", "config", camera_config_val, sizeof(camera_config_val), (const char *)camera_configs, (const char *)camera_configs, sizeof(camera_configs) / sizeof(camera_configs[0]), sizeof(camera_configs[0]), DEFAULT_CAMERA_CONFIG);
|
||||||
auto config_frame_rate = iotwebconf::NumberParameter("Frame rate (ms)", "fr", frame_rate_val, sizeof(frame_rate_val), DEFAULT_FRAMERATE, nullptr, "min=\"10\"");
|
auto config_frame_rate = iotwebconf::NumberParameter("Frame duration (ms)", "fd", frame_duration_val, sizeof(frame_duration_val), DEFAULT_FRAMEDURATION, nullptr, "min=\"10\"");
|
||||||
auto config_frame_size = iotwebconf::SelectParameter("Frame size", "fs", frame_size_val, sizeof(frame_size_val), (const char *)frame_sizes, (const char *)frame_sizes, sizeof(frame_sizes) / sizeof(frame_sizes[0]), sizeof(frame_sizes[0]), DEFAULT_FRAMESIZE);
|
auto config_frame_size = iotwebconf::SelectParameter("Frame size", "fs", frame_size_val, sizeof(frame_size_val), (const char *)frame_sizes, (const char *)frame_sizes, sizeof(frame_sizes) / sizeof(frame_sizes[0]), sizeof(frame_sizes[0]), DEFAULT_FRAMESIZE);
|
||||||
auto config_jpg_quality = iotwebconf::NumberParameter("JPEG quality", "q", jpeg_quality_val, sizeof(jpeg_quality_val), DEFAULT_JPEG_QUALITY, nullptr, "min=\"1\" max=\"100\"");
|
auto config_jpg_quality = iotwebconf::NumberParameter("JPEG quality", "q", jpeg_quality_val, sizeof(jpeg_quality_val), DEFAULT_JPEG_QUALITY, nullptr, "min=\"1\" max=\"100\"");
|
||||||
|
|
||||||
@@ -42,48 +43,66 @@ void handle_root()
|
|||||||
if (iotWebConf.handleCaptivePortal())
|
if (iotWebConf.handleCaptivePortal())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto url = "rtsp://" + String(iotWebConf.getThingName()) + ".local:" + String(RTSP_PORT) + "/mjpeg/1";
|
const char *root_page_template =
|
||||||
|
"<!DOCTYPE html><html lang=\"en\">"
|
||||||
|
"<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1, user-scalable=no\"/>"
|
||||||
|
"<head><title>{{Title}}" APP_TITLE " v" APP_VERSION "</title></head>"
|
||||||
|
"<body>"
|
||||||
|
"<h2>Status page for {{ThingName}}</h2><hr />"
|
||||||
|
|
||||||
String html;
|
"<h3>ESP32</h3>"
|
||||||
html += "<!DOCTYPE html><html lang=\"en\">"
|
"<ul>"
|
||||||
"<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1, user-scalable=no\"/>"
|
"<li>CPU model: {{ChipModel}}</li>"
|
||||||
"<head><title>" APP_TITLE " v" APP_VERSION "</title></head>"
|
"<li>CPU speed: {{CpuFreqMHz}}Mhz</li>"
|
||||||
"<body>";
|
"<li>Mac address: {{MacAddress}}</li>"
|
||||||
|
"<li>IPv4 address: {{IpV4}}</li>"
|
||||||
|
"<li>IPv6 address: {{IpV6}}</li>"
|
||||||
|
"</ul>"
|
||||||
|
|
||||||
html += "<h2>Status page for " + String(iotWebConf.getThingName()) + "</h2><hr />";
|
"<h3>Settings</h3>"
|
||||||
|
"<ul>"
|
||||||
|
"<li>Camera type: {{CameraType}}</li>"
|
||||||
|
"<li>Frame size: {{FrameSize}}</li>"
|
||||||
|
"<li>Frame rate: {{FrameDuration}} ms ({{FrameFrequency}} f/s)</li>"
|
||||||
|
"<li>JPEG quality: {{JpegQuality}} (0-100)</li>"
|
||||||
|
"</ul>"
|
||||||
|
|
||||||
html += "<h3>ESP32</h3>";
|
"<h3>Diagnostics</h3>"
|
||||||
html += "<ul>";
|
"<ul>"
|
||||||
html += "<li>CPU model: " + String(ESP.getChipModel()) + "</li>";
|
"<li>Uptime: {{Uptime}}</li>"
|
||||||
html += "<li>CPU speed: " + String(ESP.getCpuFreqMHz()) + "Mhz</li>";
|
"<li>Free heap: {{FreeHeap}}b</li>"
|
||||||
html += "<li>Mac address: " + WiFi.macAddress() + "</li>";
|
"<li>Max free block: {{MaxAllocHeap}}b</li>"
|
||||||
html += "<li>IPv4 address: " + WiFi.localIP().toString() + "</li>";
|
"</ul>"
|
||||||
html += "<li>IPv6 address: " + WiFi.localIPv6().toString() + "</li>";
|
|
||||||
html += "</ul>";
|
|
||||||
|
|
||||||
html += "<h3>Settings</h3>";
|
"<br/>camera stream: <a href=\"rtsp://{{ThingName}}.local:{{RtspPort}}/mjpeg/1\">rtsp://{{ThingName}}.local:{{RtspPort}}/mjpeg/1</a>"
|
||||||
html += "<ul>";
|
"<br />"
|
||||||
html += "<li>Camera type: " + String(camera_config_val) + "</li>";
|
"<br/>Go to <a href=\"config\">configure page</a> to change settings.";
|
||||||
html += "<li>Frame size: " + String(frame_size_val) + "</li>";
|
|
||||||
html += "<li>Frame rate: " + String(frame_rate_val) + " ms (" + String(1000.0 / atol(frame_rate_val), 1) + " f/s)</li>";
|
|
||||||
html += "<li>JPEG quality: " + String(jpeg_quality_val) + " (0-100)</li>";
|
|
||||||
html += "</ul>";
|
|
||||||
|
|
||||||
html += "<h3>Diagnostics</h3>";
|
const template_substitution_t root_page_substitutions[] = {
|
||||||
html += "<ul>";
|
{"Title", APP_TITLE},
|
||||||
html += "<li>Uptime: " + String(format_duration(millis() / 1000)) + "</li>";
|
{"Version", APP_VERSION},
|
||||||
html += "<li>Free heap: " + format_si(ESP.getFreeHeap()) + "b</li>";
|
{"ThingName", iotWebConf.getThingName()},
|
||||||
html += "<li>Max free block: " + format_si(ESP.getMaxAllocHeap()) + "b</li>";
|
{"ChipModel", ESP.getChipModel()},
|
||||||
html += "</ul>";
|
{"CpuFreqMHz", String(ESP.getCpuFreqMHz())},
|
||||||
|
{"MacAddress", WiFi.macAddress()},
|
||||||
|
{"IpV4", WiFi.localIP().toString()},
|
||||||
|
{"IpV6", WiFi.localIPv6().toString()},
|
||||||
|
{"CameraType", camera_config_val},
|
||||||
|
{"FrameSize", frame_size_val},
|
||||||
|
{"FrameDuration", frame_duration_val},
|
||||||
|
{"FrameFrequency", String(1000.0 / atol(frame_duration_val), 1)},
|
||||||
|
{"JpegQuality", jpeg_quality_val},
|
||||||
|
{"Uptime", String(format_duration(millis() / 1000))},
|
||||||
|
{"FreeHeap", format_si(ESP.getFreeHeap())},
|
||||||
|
{"MaxAllocHeap", format_si(ESP.getMaxAllocHeap())},
|
||||||
|
{"RtspPort", String(RTSP_PORT)}};
|
||||||
|
|
||||||
html += "<br/>camera stream: <a href=\"" + url + "\">" + url + "</a>";
|
auto html = template_render(root_page_template, root_page_substitutions);
|
||||||
html += "<br />";
|
|
||||||
html += "<br/>Go to <a href=\"config\">configure page</a> to change settings.";
|
|
||||||
|
|
||||||
if (config_changed)
|
if (config_changed)
|
||||||
{
|
{
|
||||||
html += "<br />";
|
html += "<br />"
|
||||||
html += "<br/><h3 style=\"color:red\">Configuration has changed. Please <a href=\"restart\">restart</a> the device.</h3>";
|
"<br/><h3 style=\"color:red\">Configuration has changed. Please <a href=\"restart\">restart</a> the device.</h3>";
|
||||||
}
|
}
|
||||||
|
|
||||||
html += "</body></html>";
|
html += "</body></html>";
|
||||||
@@ -127,7 +146,7 @@ bool initialize_camera()
|
|||||||
auto frame_size = lookup_frame_size(frame_size_val);
|
auto frame_size = lookup_frame_size(frame_size_val);
|
||||||
log_i("JPEG quality: %s", jpeg_quality_val);
|
log_i("JPEG quality: %s", jpeg_quality_val);
|
||||||
auto jpeg_quality = atoi(jpeg_quality_val);
|
auto jpeg_quality = atoi(jpeg_quality_val);
|
||||||
log_i("Framerate: %s ms", frame_rate_val);
|
log_i("Frame rate: %s ms", frame_duration_val);
|
||||||
|
|
||||||
camera_config.frame_size = frame_size;
|
camera_config.frame_size = frame_size;
|
||||||
camera_config.jpeg_quality = jpeg_quality;
|
camera_config.jpeg_quality = jpeg_quality;
|
||||||
@@ -139,11 +158,11 @@ void start_rtsp_server()
|
|||||||
log_v("start_rtsp_server");
|
log_v("start_rtsp_server");
|
||||||
if (!initialize_camera())
|
if (!initialize_camera())
|
||||||
{
|
{
|
||||||
log_e("Failed to initialize camera. Type: %s, frame size: %s, frame rate: %s ms, jpeg quality: %s", camera_config_val, frame_size_val, frame_rate_val, jpeg_quality_val);
|
log_e("Failed to initialize camera. Type: %s, frame size: %s, frame rate: %s ms, jpeg quality: %s", camera_config_val, frame_size_val, frame_duration_val, jpeg_quality_val);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto frame_rate = atol(frame_rate_val);
|
auto frame_rate = atol(frame_duration_val);
|
||||||
camera_server = std::unique_ptr<rtsp_server>(new rtsp_server(cam, frame_rate, RTSP_PORT));
|
camera_server = std::unique_ptr<rtsp_server>(new rtsp_server(cam, frame_rate, RTSP_PORT));
|
||||||
// Add service to mDNS - rtsp
|
// Add service to mDNS - rtsp
|
||||||
MDNS.addService("rtsp", "tcp", 554);
|
MDNS.addService("rtsp", "tcp", 554);
|
||||||
@@ -168,7 +187,7 @@ void setup()
|
|||||||
Serial.setDebugOutput(true);
|
Serial.setDebugOutput(true);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
log_i("CPU Freq = %d Mhz", getCpuFrequencyMhz());
|
log_i("CPU Freq: %d Mhz", getCpuFrequencyMhz());
|
||||||
log_i("Free heap: %d bytes", ESP.getFreeHeap());
|
log_i("Free heap: %d bytes", ESP.getFreeHeap());
|
||||||
log_i("Starting " APP_TITLE "...");
|
log_i("Starting " APP_TITLE "...");
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user