Video streaming 在网站esp32 cam中实现视频流

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

我想用ESP32摄像头显示实时视频流,并且在同一个网站上还有一些其他控件的按钮。到目前为止,我已经能够显示9个工作按钮,但我找不到任何关于如何同时显示视频流的内容。如果视频流在一个单独的浏览器选项卡中工作,它也会对我起作用。在这里,我尝试使用Rui Santos的代码进行视频流 并结合我的工作代码建立一个网站。代码没有抛出任何错误,但它也只显示视频流,没有我的网站。任何关于如何以不同方式进行的帮助或提示都将不胜感激

#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();//侦听传入的客户端
如果(客户端){//如果新客户端连接,