简介
最近搞了块Seeed Studio的XIAO ESP32-S3 Sense开发板,这小东西麻雀虽小五脏俱全——自带摄像头、麦克风、SD卡槽,还支持WiFi。今天就来折腾一下,用ESP-IDF原生框架实现MJPEG视频流通过HTTP推流,让浏览器能直接看到实时画面。
为什么选择ESP-IDF?
之前玩ESP32系列的时候,用Arduino框架居多,简单上手快。但这次想深入一点,直接用乐鑫官方的ESP-IDF框架开发。原因有三:
1-性能更好:ESP-IDF底层优化更到位,对多任务、内存管理更精细
2-功能更全:很多底层API只有ESP-IDF才有
3-生产可用:如果想做产品,ESP-IDF是更靠谱的选择
其实最重要的原因是因为:虽然这个开发板带有WebCam的demo(Arduino环境)但是仅仅具有拍照的功能,使用Arduino开发的话并不知道怎么通过MJPEG编码进行推流播放。而ESP-IDF具有成熟的摄像头驱动库 ESP- Camera也可以加速我们的开发。
根据官方原理图,摄像头和音频的引脚分配如下:
// 摄像头引脚 #define XCLK_GPIO_NUM 10 #define SIOD_GPIO_NUM 40 #define SIOC_GPIO_NUM 39 #define Y9_GPIO_NUM 48 #define Y8_GPIO_NUM 11 #define Y7_GPIO_NUM 12 #define Y6_GPIO_NUM 14 #define Y5_GPIO_NUM 16 #define Y4_GPIO_NUM 18 #define Y3_GPIO_NUM 17 #define Y2_GPIO_NUM 15 #define VSYNC_GPIO_NUM 38 #define HREF_GPIO_NUM 47 #define PCLK_GPIO_NUM 13 // 麦克风引脚 #define I2S_SCK_GPIO 42 // BCLK #define I2S_DIN_GPIO 41 // DATA
整个项目的核心思路很简单:
1-初始化WiFi,工作在AP模式(也可以改成STA模式连接现有WiFi,不建议、因为延迟较大)
2-初始化摄像头,配置为JPEG输出
3-初始化I2S音频(这次先不用,没有研究明白怎么一起推流出去)
4-启动HTTP服务器,提供两个端点:/:返回HTML页面,显示视频流 。 /stream:MJPEG视频流,用multipart/x-mixed-replace格式
具体实现步骤如下
一、首先初始化项目,在组件管理器中增加ESP-Camera的支持
## IDF Component Manager Manifest File dependencies: ## Required IDF version idf: version: '>=4.1.0' # # Put list of dependencies here # # For components maintained by Espressif: # component: "~1.0.0" # # For 3rd party components: # username/component: ">=1.0.0,<2.0.0" espressif/esp32-camera: ^2.1.5
MJPEG流原理
MJPEG其实就是一帧一帧的JPEG图片连续传输,用特殊的boundary分隔。浏览器收到后会自动连续播放,实现视频效果。代码里用的是AP模式,开发板自己创建一个WiFi热点,手机或电脑直接连上来访问。这样最省事,不用配置路由器而且延迟还低。
static void wifi_init()
{
esp_netif_init();
esp_event_loop_create_default();
esp_netif_create_default_wifi_ap();
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
esp_wifi_init(&cfg);
wifi_config_t wifi_config = {
.ap = {
.ssid = "99999999999",
.password = "88888888",
.ssid_len = 11,
.channel = 6,
.authmode = WIFI_AUTH_WPA2_PSK,
.max_connection = 4,
},
};
esp_wifi_set_mode(WIFI_MODE_AP);
esp_wifi_set_config(WIFI_IF_AP, &wifi_config);
esp_wifi_set_ps(WIFI_PS_NONE); // 禁用省电模式保证流畅度
esp_wifi_start();
ESP_LOGI(TAG, "WiFi AP started. SSID: %s", WIFI_SSID);
}二、 摄像头初始化
XIAO Sense用的是OV2640传感器(最新版和我现在用的版本是OV3660),最高支持UXGA(1600x1200)。但视频流用不了那么高,我选SVGA(800x600)平衡画质和流畅度。
static void camera_init()
{
camera_config_t config = {
.xclk_freq_hz = 20000000,
.pixel_format = PIXFORMAT_JPEG,
.frame_size = FRAMESIZE_SVGA, // 800x600
.jpeg_quality = 15, // 质量越低,帧率越高
.fb_count = 4, // 4个帧缓冲区,平滑采集
.grab_mode = CAMERA_GRAB_LATEST, // 只取最新帧
// ... 引脚配置省略
};
esp_err_t err = esp_camera_init(&config);
}三、I2S音频初始化(预留功能)
XIAO Sense板载的MSM261麦克风是I2S接口的数字麦克风。虽然这次没用到音频,但先初始化好,方便后续扩展。
static void audio_init()
{
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_PORT, I2S_ROLE_MASTER);
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, NULL, &i2s_chan_handle));
i2s_std_config_t std_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(16000),
.slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(16, 1),
.gpio_cfg = {
.bclk = I2S_SCK_GPIO, // 42
.din = I2S_DIN_GPIO, // 41
// ws引脚留空,数字麦克风不需要
},
};
i2s_channel_init_std_mode(i2s_chan_handle, &std_cfg);
i2s_channel_enable(i2s_chan_handle);
}四、HTTP服务器和MJPEG流处理
这是核心部分。使用ESP-IDF的esp_http_server组件,比Arduino的WebServer更底层,也更灵活。
static esp_err_t http_handler(httpd_req_t *req)
{
if (strcmp(req->uri, "/") == 0) {
// 返回HTML页面
httpd_resp_set_type(req, "text/html; charset=utf-8");
return httpd_resp_send(req, HTML_PAGE, strlen(HTML_PAGE));
}
if (strcmp(req->uri, "/stream") == 0) {
// 设置MJPEG流响应头
httpd_resp_set_type(req, "multipart/x-mixed-replace; boundary=frame");
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
while (1) {
camera_fb_t *fb = esp_camera_fb_get();
if (!fb) {
vTaskDelay(pdMS_TO_TICKS(50));
continue;
}
// 发送boundary
char buf[64];
size_t hlen = snprintf(buf, sizeof(buf),
"--frame\r\nContent-Type: image/jpeg\r\nContent-Length: %u\r\n\r\n",
fb->len);
if (httpd_resp_send_chunk(req, buf, hlen) != ESP_OK) break;
if (httpd_resp_send_chunk(req, (char*)fb->buf, fb->len) != ESP_OK) break;
if (httpd_resp_send_chunk(req, "\r\n", 2) != ESP_OK) break;
esp_camera_fb_return(fb);
vTaskDelay(pdMS_TO_TICKS(50)); // 限制约20fps
}
}
return ESP_OK;
}五、前端页面
前端页面是借助AI生成的,因为原本的前端页面比较简洁,使用AI增加了对移动端的适配。代码较多这里就不贴了。见附件即可
效果如下
首先连接WIFI

然后访问对应的IP地址

实际上帧率还可以,但是有一定的延迟。目前是采用的直连的方式。不是很建议使用路由器、因为使用路由器的话还需要路由器进行中转一次、帧率会更低。
附件如下
切勿用于违法犯罪!
我要赚赏金
