Video streaming 在网站esp32 cam中实现视频流
我想用ESP32摄像头显示实时视频流,并且在同一个网站上还有一些其他控件的按钮。到目前为止,我已经能够显示9个工作按钮,但我找不到任何关于如何同时显示视频流的内容。如果视频流在一个单独的浏览器选项卡中工作,它也会对我起作用。在这里,我尝试使用Rui Santos的代码进行视频流 并结合我的工作代码建立一个网站。代码没有抛出任何错误,但它也只显示视频流,没有我的网站。任何关于如何以不同方式进行的帮助或提示都将不胜感激Video streaming 在网站esp32 cam中实现视频流,video-streaming,webserver,live-streaming,esp32,ip-camera,Video Streaming,Webserver,Live Streaming,Esp32,Ip Camera,我想用ESP32摄像头显示实时视频流,并且在同一个网站上还有一些其他控件的按钮。到目前为止,我已经能够显示9个工作按钮,但我找不到任何关于如何同时显示视频流的内容。如果视频流在一个单独的浏览器选项卡中工作,它也会对我起作用。在这里,我尝试使用Rui Santos的代码进行视频流 并结合我的工作代码建立一个网站。代码没有抛出任何错误,但它也只显示视频流,没有我的网站。任何关于如何以不同方式进行的帮助或提示都将不胜感激 #include "esp_camera.h" #inclu
#include "esp_camera.h"
#include <WiFi.h>
#include "esp_timer.h"
#include "img_converters.h"
#include "Arduino.h"
#include "fb_gfx.h"
#include "soc/soc.h" //disable brownout problems
#include "soc/rtc_cntl_reg.h" //disable brownout problems
#include "esp_http_server.h"
//Replace with your network credentials
const char* ssid = "SSID";
const char* password = "password";
WiFiServer server(80);
// Variable to store the HTTP request
String header;
// Current time
unsigned long currentTime = millis();
// Previous time
unsigned long previousTime = 0;
// Define timeout time in milliseconds (example: 2000ms = 2s)
const long timeoutTime = 2000;
int forwards = 0; // VOR 1
#define PART_BOUNDARY "123456789000000000000987654321"
#define PWDN_GPIO_NUM 32
#define RESET_GPIO_NUM -1
#define XCLK_GPIO_NUM 0
#define SIOD_GPIO_NUM 26
#define SIOC_GPIO_NUM 27
#define Y9_GPIO_NUM 35
#define Y8_GPIO_NUM 34
#define Y7_GPIO_NUM 39
#define Y6_GPIO_NUM 36
#define Y5_GPIO_NUM 21
#define Y4_GPIO_NUM 19
#define Y3_GPIO_NUM 18
#define Y2_GPIO_NUM 5
#define VSYNC_GPIO_NUM 25
#define HREF_GPIO_NUM 23
#define PCLK_GPIO_NUM 22
static const char* _STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary=" PART_BOUNDARY;
static const char* _STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n";
static const char* _STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %u\r\n\r\n";
httpd_handle_t stream_httpd = NULL;
static esp_err_t stream_handler(httpd_req_t *req){
camera_fb_t * fb = NULL;
esp_err_t res = ESP_OK;
size_t _jpg_buf_len = 0;
uint8_t * _jpg_buf = NULL;
char * part_buf[64];
res = httpd_resp_set_type(req, _STREAM_CONTENT_TYPE);
if(res != ESP_OK){
return res;
}
while(true){
fb = esp_camera_fb_get();
if (!fb) {
Serial.println("Camera capture failed");
res = ESP_FAIL;
} else {
if(fb->width > 400){
if(fb->format != PIXFORMAT_JPEG){
bool jpeg_converted = frame2jpg(fb, 80, &_jpg_buf, &_jpg_buf_len);
esp_camera_fb_return(fb);
fb = NULL;
if(!jpeg_converted){
Serial.println("JPEG compression failed");
res = ESP_FAIL;
}
} else {
_jpg_buf_len = fb->len;
_jpg_buf = fb->buf;
}
}
}
if(res == ESP_OK){
size_t hlen = snprintf((char *)part_buf, 64, _STREAM_PART, _jpg_buf_len);
res = httpd_resp_send_chunk(req, (const char *)part_buf, hlen);
}
if(res == ESP_OK){
res = httpd_resp_send_chunk(req, (const char *)_jpg_buf, _jpg_buf_len);
}
if(res == ESP_OK){
res = httpd_resp_send_chunk(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY));
}
if(fb){
esp_camera_fb_return(fb);
fb = NULL;
_jpg_buf = NULL;
} else if(_jpg_buf){
free(_jpg_buf);
_jpg_buf = NULL;
}
if(res != ESP_OK){
break;
}
//Serial.printf("MJPG: %uB\n",(uint32_t)(_jpg_buf_len));
}
return res;
}
void startCameraServer(){
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
config.server_port = 80;
httpd_uri_t index_uri = {
.uri = "/",
.method = HTTP_GET,
.handler = stream_handler,
.user_ctx = NULL
};
//Serial.printf("Starting web server on port: '%d'\n", config.server_port);
if (httpd_start(&stream_httpd, &config) == ESP_OK) {
httpd_register_uri_handler(stream_httpd, &index_uri);
}
}
void setup() {
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable brownout detector
Serial.begin(115200);
Serial.setDebugOutput(false);
camera_config_t config;
config.ledc_channel = LEDC_CHANNEL_0;
config.ledc_timer = LEDC_TIMER_0;
config.pin_d0 = Y2_GPIO_NUM;
config.pin_d1 = Y3_GPIO_NUM;
config.pin_d2 = Y4_GPIO_NUM;
config.pin_d3 = Y5_GPIO_NUM;
config.pin_d4 = Y6_GPIO_NUM;
config.pin_d5 = Y7_GPIO_NUM;
config.pin_d6 = Y8_GPIO_NUM;
config.pin_d7 = Y9_GPIO_NUM;
config.pin_xclk = XCLK_GPIO_NUM;
config.pin_pclk = PCLK_GPIO_NUM;
config.pin_vsync = VSYNC_GPIO_NUM;
config.pin_href = HREF_GPIO_NUM;
config.pin_sscb_sda = SIOD_GPIO_NUM;
config.pin_sscb_scl = SIOC_GPIO_NUM;
config.pin_pwdn = PWDN_GPIO_NUM;
config.pin_reset = RESET_GPIO_NUM;
config.xclk_freq_hz = 20000000;
config.pixel_format = PIXFORMAT_JPEG;
if(psramFound()){
config.frame_size = FRAMESIZE_UXGA;
config.jpeg_quality = 10;
config.fb_count = 2;
} else {
config.frame_size = FRAMESIZE_SVGA;
config.jpeg_quality = 12;
config.fb_count = 1;
}
// Camera init
esp_err_t err = esp_camera_init(&config);
if (err != ESP_OK) {
Serial.printf("Camera init failed with error 0x%x", err);
return;
}
// Wi-Fi connection
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.print("Camera Stream Ready! Go to: http://");
Serial.print(WiFi.localIP());
// Start streaming web server
startCameraServer();
server.begin();
}
void loop() {
WiFiClient client = server.available(); // Listen for incoming clients
if (client) { // If a new client connects,
currentTime = millis();
previousTime = currentTime;
Serial.println("New Client."); // print a message out in the serial port
String currentLine = ""; // make a String to hold incoming data from the client
while (client.connected() && currentTime - previousTime <= timeoutTime) { // loop while the client's connected
currentTime = millis();
if (client.available()) { // if there's bytes to read from the client,
char c = client.read(); // read a byte, then
Serial.write(c); // print it out the serial monitor
header += c;
if (c == '\n') { // if the byte is a newline character
// if the current line is blank, you got two newline characters in a row.
// that's the end of the client HTTP request, so send a response:
if (currentLine.length() == 0) {
// HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
// and a content-type so the client knows what's coming, then a blank line:
client.println("HTTP/1.1 200 OK");
client.println("Content-type:text/html");
client.println("Connection: close");
client.println();
// turns the GPIOs on and off
if (header.indexOf("GET /forwards/on") >= 0) {
forwards = 1;
Serial.println(forwards);
}
// Display the HTML web page
client.println("<!DOCTYPE html><html>");
client.println("<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
client.println("<link rel=\"icon\" href=\"data:,\">");
// CSS to style the on/off buttons
// Feel free to change the background-color and font-size attributes to fit your preferences
client.println("<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: left;}");
client.println(".button_on { background-color: #4CAF50; border: none; color: black; padding: 8px 20px;");
client.println("text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}");
client.println(".button_off {background-color: #555555;}</style></head>");
// Web Page Heading
client.println("<body><h1>Rover Control Center</h1>");
// Display current state, and ON/OFF buttons for forwards
// If the forwards_state is off, it displays the ON button
if (forwards == 0) {
client.println("<p><a href=\"/forwards/on\"><button class=\"button_on\">forwards active</button></a></p>");
} else {
client.println("<p><a href=\"/forwards/off\"><button class=\"button_on button_off\">forwards off</button></a></p>");
}
client.println("</body></html>");
// The HTTP response ends with another blank line
client.println();
// Break out of the while loop
break;
} else { // if you got a newline, then clear currentLine
currentLine = "";
}
} else if (c != '\r') { // if you got anything else but a carriage return character,
currentLine += c; // add it to the end of the currentLine
}
}
}
// Clear the header variable
header = "";
// Close the connection
client.stop();
Serial.println("Client disconnected.");
Serial.println("");
}
delay(100);
}
#包括“esp_camera.h”
#包括
#包括“esp_timer.h”
#包括“img_转换器.h”
#包括“Arduino.h”
#包括“fb_gfx.h”
#包括“soc/soc.h”//禁用断电问题
#包括“soc/rtc\u cntl\u reg.h”//禁用断电问题
#包括“esp\u http\u server.h”
//替换为您的网络凭据
const char*ssid=“ssid”;
const char*password=“password”;
WiFiServer服务器(80);
//变量来存储HTTP请求
字符串头;
//当前时间
无符号长currentTime=millis();
//上次
无符号长前一时间=0;
//以毫秒为单位定义超时时间(例如:2000ms=2s)
const long timeoutTime=2000;
int forwards=0;//VOR 1
#定义零件边界“12345678000000000000987654321”
#定义PWDN\u GPIO\u NUM 32
#定义RESET\u GPIO\u NUM-1
#定义XCLK\u GPIO\u NUM 0
#定义SIOD_GPIO_NUM 26
#定义SIOC_GPIO_数量27
#定义Y9_GPIO_数量35
#定义Y8_GPIO_数量34
#定义Y7_GPIO_数量39
#定义Y6_GPIO_数量36
#定义Y5_GPIO_NUM 21
#定义Y4_GPIO_数量19
#定义Y3_GPIO_数量18
#定义Y2\u GPIO\u数量5
#定义VSYNC\u GPIO\u NUM 25
#定义HREF\u GPIO\u NUM 23
#定义PCLK\U GPIO\U编号22
静态常量char*\u STREAM\u CONTENT\u TYPE=“multipart/x-mixed-replace;boundary=“PART\u boundary;
静态常量char*\u STREAM\u BOUNDARY=“\r\n--”PART\u BOUNDARY”\r\n”;
静态常量char*\u STREAM\u PART=“内容类型:图像/jpeg\r\n内容长度:%u\r\n\r\n”;
httpd_handle_t stream_httpd=NULL;
静态esp_err_t stream_处理程序(httpd_req_t*req){
摄像头_fb_t*fb=NULL;
esp_err_t res=esp_OK;
大小为0;
uint8_t*_jpg_buf=NULL;
char*part_buf[64];
res=httpd\u resp\u set\u type(请求、流\u内容\u type);
如果(res!=ESP_OK){
返回res;
}
while(true){
fb=esp_摄像头_fb_get();
如果(!fb){
Serial.println(“摄像头捕获失败”);
res=ESP_故障;
}否则{
如果(fb->宽度>400){
如果(fb->format!=PIXFORMAT\u JPEG){
bool jpeg_converted=frame2jpg(fb,80,&u jpg_buf,&u jpg_buf_len);
esp_摄像头_fb_返回(fb);
fb=零;
如果(!jpeg_已转换){
Serial.println(“JPEG压缩失败”);
res=ESP_故障;
}
}否则{
_jpg_buf_len=fb->len;
_jpg_buf=fb->buf;
}
}
}
如果(res==ESP\u正常){
尺寸=snprintf((字符*)部分,64,_流部分,_jpg_buf_len);
res=httpd_resp_send_chunk(req,(const char*)部分(buf,hlen);
}
如果(res==ESP\u正常){
res=httpd_resp_send_chunk(req,(const char*)_jpg_buf,_jpg_buf_len);
}
如果(res==ESP\u正常){
res=httpd_resp_send_chunk(请求、流边界、strlen(_流边界));
}
如果(fb){
esp_摄像头_fb_返回(fb);
fb=零;
_jpg_buf=NULL;
}否则如果(_jpg_buf){
免费(_jpg_buf);
_jpg_buf=NULL;
}
如果(res!=ESP_OK){
打破
}
//Serial.printf(“MJPG:%uB\n”,(uint32_t)(_jpg_buf_len));
}
返回res;
}
void startCameraServer(){
httpd_config_t config=httpd_DEFAULT_config();
config.server_port=80;
httpd_uri_t index_uri={
.uri=“/”,
.method=HTTP\u GET,
.handler=stream\u handler,
.user_ctx=NULL
};
//Serial.printf(“在端口:'%d'\n',config.server\u端口上启动web服务器”);
if(httpd_启动(&stream_httpd,&config)==ESP_正常){
httpd\u寄存器\u uri\u处理程序(流\u httpd和索引\u uri);
}
}
无效设置(){
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG,0);//禁用断电检测器
序列号开始(115200);
Serial.setDebugOutput(false);
摄像机配置;
config.ledc_channel=ledc_channel_0;
config.ledc_timer=ledc_timer_0;
config.pin\u d0=Y2\u GPIO\u NUM;
config.pin_d1=Y3_GPIO_NUM;
config.pin_d2=Y4_GPIO_NUM;
config.pin_d3=Y5_GPIO_NUM;
config.pin_d4=Y6_GPIO_NUM;
config.pin_d5=Y7_GPIO_NUM;
config.pin_d6=Y8_GPIO_NUM;
config.pin_d7=Y9_GPIO_NUM;
config.pin\u xclk=xclk\u GPIO\u NUM;
config.pin\u pclk=pclk\u GPIO\u NUM;
config.pin\u vsync=vsync\u GPIO\u NUM;
config.pin_href=href_GPIO_NUM;
config.pin_sscb_sda=SIOD_GPIO_NUM;
config.pin_sscb_scl=SIOC_GPIO_NUM;
config.pin\u pwdn=pwdn\u GPIO\u NUM;
config.pin\u reset=reset\u GPIO\u NUM;
config.xclk_freq_hz=20000000;
config.pixel_format=PIXFORMAT_JPEG;
if(psramFound()){
config.frame\u size=FRAMESIZE\u UXGA;
config.jpeg_quality=10;
config.fb_count=2;
}否则{
config.frame\u size=FRAMESIZE\u SVGA;
config.jpeg_quality=12;
config.fb_count=1;
}
//照相机初始化
esp_err_t err=esp_camera_init(&config);
如果(错误!=ESP_正常){
Serial.printf(“摄影机初始化失败,错误0x%x”,错误);
返回;
}
//Wi-Fi连接
WiFi.begin(ssid,密码);
while(WiFi.status()!=WL_已连接){
延迟(500);
连续打印(“.”);
}
Serial.println(“”);
Serial.println(“WiFi连接”);
Serial.print(“摄像头流就绪!转到:http://”);
Serial.print(WiFi.localIP());
//启动流式web服务器
startCameraServer();
server.begin();
}
void循环(){
WiFiClient client=server.available();//侦听传入的客户端
如果(客户端){//如果新客户端连接,